blob: 6856d610c9de124c59dbdf925a20bba0f236de10 [file] [log] [blame]
// Copyright 2016 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef COBALT_RENDERER_RASTERIZER_SKIA_TEXT_SHAPER_H_
#define COBALT_RENDERER_RASTERIZER_SKIA_TEXT_SHAPER_H_
#include <algorithm>
#include <limits>
#include <memory>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/strings/string16.h"
#include "base/synchronization/lock.h"
#include "cobalt/render_tree/font_provider.h"
#include "cobalt/renderer/rasterizer/skia/font.h"
#include "cobalt/renderer/rasterizer/skia/glyph_buffer.h"
#include "cobalt/renderer/rasterizer/skia/harfbuzz_font.h"
#include "third_party/harfbuzz-ng/src/hb-icu.h"
#include "third_party/harfbuzz-ng/src/hb.h"
#include "third_party/icu/source/common/unicode/uscript.h"
#include "third_party/skia/include/core/SkTextBlob.h"
namespace cobalt {
namespace renderer {
namespace rasterizer {
namespace skia {
// Describes a render_tree::TextShaper using skia and HarfBuzz to shape the
// text.
class TextShaper {
public:
// A script run represents a segment of text that can be shaped using a single
// skia::Font and UScriptCode combination.
struct ScriptRun {
ScriptRun(Font* run_font, UScriptCode run_script,
unsigned int run_start_index, unsigned int run_length)
: font(run_font),
script(run_script),
start_index(run_start_index),
length(run_length) {}
Font* font;
UScriptCode script;
unsigned int start_index;
unsigned int length;
};
typedef std::vector<ScriptRun> ScriptRuns;
TextShaper();
// Shapes a utf-16 text buffer using the given font provider. The shaping
// can be simple or complex, depending on the text provided.
// |language| is used during complex shaping by HarfBuzz in order to allow it
// to make shaping decisions more likely to be correct for the locale.
// If |is_rtl| is true, then the glyphs in the text buffer will be reversed.
// Returns a newly created glyph buffer, which can be used to render the
// shaped text.
scoped_refptr<GlyphBuffer> CreateGlyphBuffer(
const base::char16* text_buffer, size_t text_length,
const std::string& language, bool is_rtl,
render_tree::FontProvider* font_provider);
// Shapes a utf-8 string using a single font. The shaping can be simple or
// complex, depending on the text provided.
// Returns a newly created glyph buffer, which can be used to render the
// shaped text.
scoped_refptr<GlyphBuffer> CreateGlyphBuffer(
const std::string& utf8_string,
const scoped_refptr<render_tree::Font>& font);
// Shapes a utf-16 text buffer using the given font provider. The shaping
// can be simple or complex, depending on the text provided. However, a glyph
// buffer is not created from the shaping data. It is instead only used to
// generate the width of the data when it is shaped.
// Returns the width of the shaped text.
float GetTextWidth(const base::char16* text_buffer, size_t text_length,
const std::string& language, bool is_rtl,
render_tree::FontProvider* font_provider,
render_tree::FontVector* maybe_used_fonts);
// Purges any caches being used by the text shaper; currently, this consists
// of the HarfBuzzFontProvider's cache.
void PurgeCaches();
private:
// Internal class used for tracking the vertical bounds of a text buffer
// during shaping when bounds are requested (i.e. the passed in |maybe_bounds|
// is non-NULL).
class VerticalBounds {
public:
VerticalBounds()
: min_y_(std::numeric_limits<float>::max()),
max_y_(std::numeric_limits<float>::min()) {}
void IncludeRange(float min_y, float max_y) {
min_y_ = std::min(min_y_, min_y);
max_y_ = std::max(max_y_, max_y);
}
float GetY() const { return IsValid() ? min_y_ : 0; }
float GetHeight() const { return IsValid() ? max_y_ - min_y_ : 0; }
private:
bool IsValid() const { return max_y_ >= min_y_; }
float min_y_;
float max_y_;
};
// Shape text relying on skia::Font and HarfBuzz.
// Returns the width of the shaped text.
// If |maybe_glyph_buffer| is non-NULL, it is populated with skia::GlyphBuffer
// shaping data.
// If |maybe_bounds| is non-NULL, it is populated with the bounds of the
// shaped text.
// If |maybe_used_fonts| is non-NULL, it is populated with all of the fonts
// used during shaping.
float ShapeText(const base::char16* text_buffer, size_t text_length,
const std::string& language, bool is_rtl,
render_tree::FontProvider* font_provider,
SkTextBlobBuilder* maybe_builder, math::RectF* maybe_bounds,
render_tree::FontVector* maybe_used_fonts);
// Populate a ScriptRuns object with all runs of text containing a single
// skia::Font and UScriptCode combination.
// Returns false if the script run collection fails.
bool CollectScriptRuns(const base::char16* text_buffer, size_t text_length,
render_tree::FontProvider* font_provider,
ScriptRuns* runs);
// Shape a complex text run using HarfBuzz.
void ShapeComplexRun(const base::char16* text_buffer,
const ScriptRun& script_run, const std::string& language,
bool is_rtl, render_tree::FontProvider* font_provider,
SkTextBlobBuilder* maybe_builder,
VerticalBounds* maybe_vertical_bounds,
render_tree::FontVector* maybe_used_fonts,
float* current_width);
// Shape a simple text run. In the case where the direction is RTL, the text
// will be reversed.
void ShapeSimpleRunWithDirection(const base::char16* text_buffer,
size_t text_length, bool is_rtl,
render_tree::FontProvider* font_provider,
SkTextBlobBuilder* maybe_builder,
VerticalBounds* maybe_vertical_bounds,
render_tree::FontVector* maybe_used_fonts,
float* current_width);
// Shape a simple text run, relying on the skia::Font objects provided by the
// FontProvider to determine the shaping data.
void ShapeSimpleRun(const base::char16* text_buffer, size_t text_length,
render_tree::FontProvider* font_provider,
SkTextBlobBuilder* maybe_builder,
VerticalBounds* maybe_vertical_bounds,
render_tree::FontVector* maybe_used_fonts,
float* current_width);
// Add the glyphs from a run of a single font within a simple text run to the
// glyph buffer.
void AddFontRunToGlyphBuffer(const render_tree::FontProvider* font_provider,
const Font* font, const int glyph_count,
SkTextBlobBuilder* builder);
// Verifies that the glyph arrays have the required size allocated. If they do
// not, then the arrays are re-allocated with the required size.
void EnsureLocalGlyphArraysHaveSize(size_t size);
// Verifies that the local text buffer has the required size allocated. If it
// does not, then the buffer is re-allocated with the required size.
void EnsureLocalTextBufferHasSize(size_t size);
// Lock used during shaping to ensure it does not occur on multiple threads at
// the same time.
base::Lock shaping_mutex_;
// Provides fonts needed by HarfBuzz during complex shaping.
HarfBuzzFontProvider harfbuzz_font_provider_;
// The allocated glyph and positions data. This is retained in between shaping
// calls to prevent constantly needing to allocate the arrays. In the case
// where a larger array is needed than the current size, larger arrays are
// allocated in their place.
size_t local_glyph_array_size_;
std::unique_ptr<render_tree::GlyphIndex[]> local_glyphs_;
std::unique_ptr<SkScalar[]> local_positions_;
// The allocated text buffer used by complex shaping when normalizing the
// text.
size_t local_text_buffer_size_;
std::unique_ptr<base::char16[]> local_text_buffer_;
};
} // namespace skia
} // namespace rasterizer
} // namespace renderer
} // namespace cobalt
#endif // COBALT_RENDERER_RASTERIZER_SKIA_TEXT_SHAPER_H_