/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include #include "SkShaper.h" #include "SkStream.h" #include "SkTextBlob.h" #include "SkTypeface.h" static const int FONT_SIZE_SCALE = 512; namespace { struct HBFBlobDel { void operator()(hb_blob_t* b) { hb_blob_destroy(b); } }; std::unique_ptr stream_to_blob(std::unique_ptr asset) { size_t size = asset->getLength(); std::unique_ptr blob; if (const void* base = asset->getMemoryBase()) { blob.reset(hb_blob_create((char*)base, SkToUInt(size), HB_MEMORY_MODE_READONLY, asset.release(), [](void* p) { delete (SkStreamAsset*)p; })); } else { // SkDebugf("Extra SkStreamAsset copy\n"); void* ptr = size ? sk_malloc_throw(size) : nullptr; asset->read(ptr, size); blob.reset(hb_blob_create((char*)ptr, SkToUInt(size), HB_MEMORY_MODE_READONLY, ptr, sk_free)); } SkASSERT(blob); hb_blob_make_immutable(blob.get()); return blob; } } // namespace struct SkShaper::Impl { struct HBFontDel { void operator()(hb_font_t* f) { hb_font_destroy(f); } }; std::unique_ptr fHarfBuzzFont; struct HBBufDel { void operator()(hb_buffer_t* b) { hb_buffer_destroy(b); } }; std::unique_ptr fBuffer; sk_sp fTypeface; }; SkShaper::SkShaper(sk_sp tf) : fImpl(new Impl) { fImpl->fTypeface = tf ? std::move(tf) : SkTypeface::MakeDefault(); int index; std::unique_ptr blob( stream_to_blob(std::unique_ptr( fImpl->fTypeface->openStream(&index)))); struct HBFaceDel { void operator()(hb_face_t* f) { hb_face_destroy(f); } }; std::unique_ptr face( hb_face_create(blob.get(), (unsigned)index)); SkASSERT(face); if (!face) { return; } hb_face_set_index(face.get(), (unsigned)index); hb_face_set_upem(face.get(), fImpl->fTypeface->getUnitsPerEm()); fImpl->fHarfBuzzFont.reset(hb_font_create(face.get())); SkASSERT(fImpl->fHarfBuzzFont); hb_font_set_scale(fImpl->fHarfBuzzFont.get(), FONT_SIZE_SCALE, FONT_SIZE_SCALE); hb_ot_font_set_funcs(fImpl->fHarfBuzzFont.get()); fImpl->fBuffer.reset(hb_buffer_create()); } SkShaper::~SkShaper() {} bool SkShaper::good() const { return fImpl->fHarfBuzzFont != nullptr; } SkScalar SkShaper::shape(SkTextBlobBuilder* builder, const SkPaint& srcPaint, const char* utf8text, size_t textBytes, SkPoint point) const { SkPaint paint(srcPaint); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setTypeface(fImpl->fTypeface); SkASSERT(builder); hb_buffer_t* buffer = fImpl->fBuffer.get(); hb_buffer_add_utf8(buffer, utf8text, -1, 0, -1); hb_buffer_guess_segment_properties(buffer); hb_shape(fImpl->fHarfBuzzFont.get(), buffer, nullptr, 0); unsigned len = hb_buffer_get_length(buffer); if (len == 0) { hb_buffer_clear_contents(buffer); return 0; } hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, NULL); hb_glyph_position_t* pos = hb_buffer_get_glyph_positions(buffer, NULL); auto runBuffer = builder->allocRunTextPos( paint, SkToInt(len), SkToInt(textBytes), SkString()); memcpy(runBuffer.utf8text, utf8text, textBytes); double x = point.x(); double y = point.y(); double textSizeY = paint.getTextSize() / (double)FONT_SIZE_SCALE; double textSizeX = textSizeY * paint.getTextScaleX(); for (unsigned i = 0; i < len; i++) { runBuffer.glyphs[i] = info[i].codepoint; runBuffer.clusters[i] = info[i].cluster; reinterpret_cast(runBuffer.pos)[i] = SkPoint::Make(SkDoubleToScalar(x + pos[i].x_offset * textSizeX), SkDoubleToScalar(y - pos[i].y_offset * textSizeY)); x += pos[i].x_advance * textSizeX; y += pos[i].y_advance * textSizeY; } hb_buffer_clear_contents(buffer); return (SkScalar)x; }