void ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint) { #ifdef USE_CAIRO PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(aBuilder); cairo_t *ctx = cairo_create(DrawTargetCairo::GetDummySurface()); if (aTransformHint) { cairo_matrix_t mat; GfxMatrixToCairoMatrix(*aTransformHint, mat); cairo_set_matrix(ctx, &mat); } // Convert our GlyphBuffer into an array of Cairo glyphs. std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs); for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) { glyphs[i].index = aBuffer.mGlyphs[i].mIndex; glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x; glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y; } cairo_set_scaled_font(ctx, mScaledFont); cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs); RefPtr<PathCairo> cairoPath = new PathCairo(ctx); cairo_destroy(ctx); cairoPath->AppendPathToBuilder(builder); #endif }
int w_newRasterizer(lua_State *L) { Rasterizer *t = NULL; try { if (luax_istype(L, 1, IMAGE_IMAGE_DATA_T)) { love::image::ImageData *d = luax_checktype<love::image::ImageData>(L, 1, "ImageData", IMAGE_IMAGE_DATA_T); const char *g = luaL_checkstring(L, 2); std::string glyphs(g); t = instance->newRasterizer(d, glyphs); } else if (luax_istype(L, 1, DATA_T)) { Data *d = luax_checkdata(L, 1); int size = luaL_checkint(L, 2); t = instance->newRasterizer(d, size); } } catch (love::Exception &e) { return luaL_error(L, "%s", e.what()); } luax_newtype(L, "Rasterizer", FONT_RASTERIZER_T, t); return 1; }
void Canvas::drawText(const uint16_t* text, int start, int count, int contextCount, float x, float y, int bidiFlags, const Paint& origPaint, Typeface* typeface) { // minikin may modify the original paint Paint paint(origPaint); Layout layout; MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount); size_t nGlyphs = layout.nGlyphs(); std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]); std::unique_ptr<float[]> pos(new float[nGlyphs * 2]); x += MinikinUtils::xOffsetForTextAlign(&paint, layout); MinikinRect bounds; layout.getBounds(&bounds); if (!drawTextAbsolutePos()) { bounds.offset(x, y); } // Set align to left for drawing, as we don't want individual // glyphs centered or right-aligned; the offset above takes // care of all alignment. paint.setTextAlign(Paint::kLeft_Align); DrawTextFunctor f(layout, this, glyphs.get(), pos.get(), paint, x, y, bounds, layout.getAdvance()); MinikinUtils::forFontRun(layout, &paint, f); }
GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx, SkGlyphCache* glyphCache) { SkTypeface* typeface = fUsingRawGlyphPaths ? fFont.getTypeface() : glyphCache->getScalerContext()->getTypeface(); const SkDescriptor* desc = fUsingRawGlyphPaths ? nullptr : &glyphCache->getDescriptor(); static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain(); int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt(); GrUniqueKey glyphKey; GrUniqueKey::Builder builder(&glyphKey, kPathGlyphDomain, 2 + strokeDataCount); reinterpret_cast<uint32_t&>(builder[0]) = desc ? desc->getChecksum() : 0; reinterpret_cast<uint32_t&>(builder[1]) = typeface ? typeface->uniqueID() : 0; if (strokeDataCount > 0) { fStroke.asUniqueKeyFragment(&builder[2]); } builder.finish(); SkAutoTUnref<GrPathRange> glyphs( static_cast<GrPathRange*>( ctx->resourceProvider()->findAndRefResourceByUniqueKey(glyphKey))); if (nullptr == glyphs) { glyphs.reset(ctx->resourceProvider()->createGlyphs(typeface, desc, fStroke)); ctx->resourceProvider()->assignUniqueKeyToResource(glyphKey, glyphs); } else { SkASSERT(nullptr == desc || glyphs->isEqualTo(*desc)); } return glyphs.detach(); }
DEF_TEST(TextBlob_extended, reporter) { SkTextBlobBuilder textBlobBuilder; SkFont font; const char text1[] = "Foo"; const char text2[] = "Bar"; int glyphCount = font.countText(text1, strlen(text1), SkTextEncoding::kUTF8); SkAutoTMalloc<uint16_t> glyphs(glyphCount); (void)font.textToGlyphs(text1, strlen(text1), SkTextEncoding::kUTF8, glyphs.get(), glyphCount); auto run = SkTextBlobBuilderPriv::AllocRunText(&textBlobBuilder, font, glyphCount, 0, 0, SkToInt(strlen(text2)), SkString(), nullptr); memcpy(run.glyphs, glyphs.get(), sizeof(uint16_t) * glyphCount); memcpy(run.utf8text, text2, strlen(text2)); for (int i = 0; i < glyphCount; ++i) { run.clusters[i] = SkTMin(SkToU32(i), SkToU32(strlen(text2))); } sk_sp<SkTextBlob> blob(textBlobBuilder.make()); REPORTER_ASSERT(reporter, blob); for (SkTextBlobRunIterator it(blob.get()); !it.done(); it.next()) { REPORTER_ASSERT(reporter, it.glyphCount() == (uint32_t)glyphCount); for (uint32_t i = 0; i < it.glyphCount(); ++i) { REPORTER_ASSERT(reporter, it.glyphs()[i] == glyphs[i]); } REPORTER_ASSERT(reporter, SkTextBlobRunIterator::kDefault_Positioning == it.positioning()); REPORTER_ASSERT(reporter, (SkPoint{0.0f, 0.0f}) == it.offset()); REPORTER_ASSERT(reporter, it.textSize() > 0); REPORTER_ASSERT(reporter, it.clusters()); for (uint32_t i = 0; i < it.glyphCount(); ++i) { REPORTER_ASSERT(reporter, i == it.clusters()[i]); } REPORTER_ASSERT(reporter, 0 == strncmp(text2, it.text(), it.textSize())); } }
static GrPathRange* get_gr_glyphs(GrContext* ctx, const SkTypeface* typeface, const SkDescriptor* desc, const GrStrokeInfo& stroke) { static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain(); int strokeDataCount = stroke.computeUniqueKeyFragmentData32Cnt(); GrUniqueKey glyphKey; GrUniqueKey::Builder builder(&glyphKey, kPathGlyphDomain, 2 + strokeDataCount); reinterpret_cast<uint32_t&>(builder[0]) = desc ? desc->getChecksum() : 0; reinterpret_cast<uint32_t&>(builder[1]) = typeface ? typeface->uniqueID() : 0; if (strokeDataCount > 0) { stroke.asUniqueKeyFragment(&builder[2]); } builder.finish(); SkAutoTUnref<GrPathRange> glyphs( static_cast<GrPathRange*>( ctx->resourceProvider()->findAndRefResourceByUniqueKey(glyphKey))); if (NULL == glyphs) { glyphs.reset(ctx->resourceProvider()->createGlyphs(typeface, desc, stroke)); ctx->resourceProvider()->assignUniqueKeyToResource(glyphKey, glyphs); } else { SkASSERT(NULL == desc || glyphs->isEqualTo(*desc)); } return glyphs.detach(); }
gcn::Font *Engine::LoadFont(const char *infofile, const char *gfxfile) { memblock *fontinfo = resMgr.LoadTextFile((char*)infofile); if(!fontinfo) { logerror("EditorEngine::Setup: Can't load font infos (%s)", infofile); return false; } std::string glyphs((char*)(fontinfo->ptr)); logdetail("Using font glyphs for '%s':", gfxfile); logdetail("%s", glyphs.c_str()); gcn::Font *font = NULL; try { font = new gcn::ImageFont(gfxfile, glyphs); } catch(gcn::Exception ex) { delete font; font = NULL; } resMgr.Drop(fontinfo); return font; }
static GrPathRange* get_gr_glyphs(GrContext* ctx, const SkTypeface* typeface, const SkDescriptor* desc, const SkStrokeRec& stroke) { static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); GrUniqueKey key; GrUniqueKey::Builder builder(&key, kDomain, 4); struct GlyphKey { uint32_t fChecksum; uint32_t fTypeface; uint64_t fStroke; }; GlyphKey* glyphKey = reinterpret_cast<GlyphKey*>(&builder[0]); glyphKey->fChecksum = desc ? desc->getChecksum() : 0; glyphKey->fTypeface = typeface ? typeface->uniqueID() : 0; glyphKey->fStroke = GrPath::ComputeStrokeKey(stroke); builder.finish(); SkAutoTUnref<GrPathRange> glyphs( static_cast<GrPathRange*>(ctx->resourceProvider()->findAndRefResourceByUniqueKey(key))); if (NULL == glyphs || (NULL != desc && !glyphs->isEqualTo(*desc))) { glyphs.reset(ctx->getGpu()->pathRendering()->createGlyphs(typeface, desc, stroke)); ctx->resourceProvider()->assignUniqueKeyToResource(key, glyphs); } return glyphs.detach(); }
void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx, GrDrawContext* dc, GrPipelineBuilder* pipelineBuilder, GrColor color, const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const SkIRect& clipBounds, GrTextContext* fallbackTextContext, const SkPaint& originalSkPaint) const { SkASSERT(fDraw); SkASSERT(pipelineBuilder->getRenderTarget()->isStencilBufferMultisampled() || !fFont.isAntiAlias()); if (fDraw->count()) { pipelineBuilder->setState(GrPipelineBuilder::kHWAntialias_Flag, fFont.isAntiAlias()); GR_STATIC_CONST_SAME_STENCIL(kStencilPass, kZero_StencilOp, kKeep_StencilOp, kNotEqual_StencilFunc, 0xffff, 0x0000, 0xffff); *pipelineBuilder->stencil() = kStencilPass; SkAutoTUnref<GrPathRange> glyphs(this->createGlyphs(ctx)); if (fLastDrawnGlyphsID != glyphs->getUniqueID()) { // Either this is the first draw or the glyphs object was purged since last draw. glyphs->loadPathsIfNeeded(fDraw->indices(), fDraw->count()); fLastDrawnGlyphsID = glyphs->getUniqueID(); } SkMatrix drawMatrix(viewMatrix); drawMatrix.preTranslate(x, y); drawMatrix.preScale(fTextRatio, fTextRatio); SkMatrix& localMatrix = fLocalMatrixTemplate; localMatrix.setTranslateX(x); localMatrix.setTranslateY(y); dc->drawPathsFromRange(pipelineBuilder, drawMatrix, localMatrix, color, glyphs, fDraw, GrPathRendering::kWinding_FillType); } if (fFallbackTextBlob) { SkPaint fallbackSkPaint(originalSkPaint); fStroke.applyToPaint(&fallbackSkPaint); if (!fStroke.isFillStyle()) { fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio); } fallbackTextContext->drawTextBlob(dc, pipelineBuilder->getRenderTarget(), pipelineBuilder->clip(), fallbackSkPaint, viewMatrix, fFallbackTextBlob, x, y, nullptr, clipBounds); } }
void QTextEngine::shape( int item ) const { assert( item < items.size() ); QScriptItem &si = items[item]; if ( si.num_glyphs ) return; QFont::Script script = (QFont::Script)si.analysis.script; si.glyph_data_offset = used; if ( !si.fontEngine ) si.fontEngine = fnt->engineForScript( script ); si.fontEngine->ref(); si.ascent = si.fontEngine->ascent(); si.descent = si.fontEngine->descent(); si.num_glyphs = 0; if ( si.fontEngine && si.fontEngine != (QFontEngine*)-1 ) { QShaperItem shaper_item; shaper_item.script = si.analysis.script; shaper_item.string = &string; shaper_item.from = si.position; shaper_item.length = length(item); shaper_item.font = si.fontEngine; shaper_item.num_glyphs = QMAX(int(num_glyphs - used), shaper_item.length); shaper_item.flags = si.analysis.bidiLevel % 2 ? RightToLeft : 0; while (1) { // qDebug(" . num_glyphs=%d, used=%d, item.num_glyphs=%d", num_glyphs, used, shaper_item.num_glyphs); ensureSpace(shaper_item.num_glyphs); shaper_item.num_glyphs = num_glyphs - used; // qDebug(" .. num_glyphs=%d, used=%d, item.num_glyphs=%d", num_glyphs, used, shaper_item.num_glyphs); shaper_item.glyphs = glyphs(&si); shaper_item.advances = advances(&si); shaper_item.offsets = offsets(&si); shaper_item.attributes = glyphAttributes(&si); shaper_item.log_clusters = logClusters(&si); if (scriptEngines[shaper_item.script].shape(&shaper_item)) break; } si.num_glyphs = shaper_item.num_glyphs; } ((QTextEngine *)this)->used += si.num_glyphs; si.width = 0; advance_t *advances = this->advances( &si ); advance_t *end = advances + si.num_glyphs; while ( advances < end ) si.width += *(advances++); return; }
TemporaryRef<Path> ScaledFontBase::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) { #ifdef USE_SKIA if (aTarget->GetType() == BACKEND_SKIA) { SkPaint paint; paint.setTypeface(GetSkTypeface()); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setTextSize(SkFloatToScalar(mSize)); std::vector<uint16_t> indices; std::vector<SkPoint> offsets; indices.resize(aBuffer.mNumGlyphs); offsets.resize(aBuffer.mNumGlyphs); for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) { indices[i] = aBuffer.mGlyphs[i].mIndex; offsets[i].fX = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.x); offsets[i].fY = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.y); } SkPath path; paint.getPosTextPath(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), &path); return new PathSkia(path, FILL_WINDING); } #endif #ifdef USE_CAIRO if (aTarget->GetType() == BACKEND_CAIRO) { MOZ_ASSERT(mScaledFont); RefPtr<PathBuilder> builder_iface = aTarget->CreatePathBuilder(); PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(builder_iface.get()); // Manually build the path for the PathBuilder. RefPtr<CairoPathContext> context = builder->GetPathContext(); cairo_set_scaled_font(*context, mScaledFont); // Convert our GlyphBuffer into an array of Cairo glyphs. std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs); for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) { glyphs[i].index = aBuffer.mGlyphs[i].mIndex; glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x; glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y; } cairo_glyph_path(*context, &glyphs[0], aBuffer.mNumGlyphs); return builder->Finish(); } #endif return NULL; }
bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) { bool haveGlyphs = false; #ifndef BUILDING_ON_TIGER Vector<CGGlyph, 512> glyphs(bufferLength); wkGetGlyphsForCharacters(fontData->platformData().cgFont(), buffer, glyphs.data(), bufferLength); for (unsigned i = 0; i < length; ++i) { if (!glyphs[i]) setGlyphDataForIndex(offset + i, 0, 0); else { setGlyphDataForIndex(offset + i, glyphs[i], fontData); haveGlyphs = true; } } #else // Use an array of long so we get good enough alignment. long glyphVector[(GLYPH_VECTOR_SIZE + sizeof(long) - 1) / sizeof(long)]; OSStatus status = wkInitializeGlyphVector(GlyphPage::size, &glyphVector); if (status != noErr) // This should never happen, perhaps indicates a bad font! If it does the // font substitution code will find an alternate font. return false; wkConvertCharToGlyphs(fontData->m_styleGroup, buffer, bufferLength, &glyphVector); unsigned numGlyphs = wkGetGlyphVectorNumGlyphs(&glyphVector); if (numGlyphs != length) { // This should never happen, perhaps indicates a bad font? // If it does happen, the font substitution code will find an alternate font. wkClearGlyphVector(&glyphVector); return false; } ATSLayoutRecord* glyphRecord = (ATSLayoutRecord*)wkGetGlyphVectorFirstRecord(glyphVector); for (unsigned i = 0; i < length; i++) { Glyph glyph = glyphRecord->glyphID; if (!glyph) setGlyphDataForIndex(offset + i, 0, 0); else { setGlyphDataForIndex(offset + i, glyph, fontData); haveGlyphs = true; } glyphRecord = (ATSLayoutRecord *)((char *)glyphRecord + wkGetGlyphVectorRecordSize(glyphVector)); } wkClearGlyphVector(&glyphVector); #endif return haveGlyphs; }
void ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint) { BackendType backendType = aBuilder->GetBackendType(); #ifdef USE_SKIA if (backendType == BackendType::SKIA) { PathBuilderSkia *builder = static_cast<PathBuilderSkia*>(aBuilder); builder->AppendPath(GetSkiaPathForGlyphs(aBuffer)); return; } #endif #ifdef USE_CAIRO if (backendType == BackendType::CAIRO) { MOZ_ASSERT(mScaledFont); PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(aBuilder); cairo_t *ctx = cairo_create(DrawTargetCairo::GetDummySurface()); if (aTransformHint) { cairo_matrix_t mat; GfxMatrixToCairoMatrix(*aTransformHint, mat); cairo_set_matrix(ctx, &mat); } // Convert our GlyphBuffer into an array of Cairo glyphs. std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs); for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) { glyphs[i].index = aBuffer.mGlyphs[i].mIndex; glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x; glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y; } cairo_set_scaled_font(ctx, mScaledFont); cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs); RefPtr<PathCairo> cairoPath = new PathCairo(ctx); cairo_destroy(ctx); cairoPath->AppendPathToBuilder(builder); return; } if (backendType == BackendType::RECORDING) { SkPath skPath = GetSkiaPathForGlyphs(aBuffer); RefPtr<Path> path = MakeAndAddRef<PathSkia>(skPath, FillRule::FILL_WINDING); path->StreamToSink(aBuilder); return; } MOZ_ASSERT(false, "Path not being copied"); #endif }
already_AddRefed<Path> ScaledFontBase::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) { #ifdef USE_SKIA if (aTarget->GetBackendType() == BackendType::SKIA) { SkPath path = GetSkiaPathForGlyphs(aBuffer); return MakeAndAddRef<PathSkia>(path, FillRule::FILL_WINDING); } #endif #ifdef USE_CAIRO if (aTarget->GetBackendType() == BackendType::CAIRO) { MOZ_ASSERT(mScaledFont); DrawTarget *dt = const_cast<DrawTarget*>(aTarget); cairo_t *ctx = static_cast<cairo_t*>(dt->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT)); bool isNewContext = !ctx; if (!ctx) { ctx = cairo_create(DrawTargetCairo::GetDummySurface()); cairo_matrix_t mat; GfxMatrixToCairoMatrix(aTarget->GetTransform(), mat); cairo_set_matrix(ctx, &mat); } cairo_set_scaled_font(ctx, mScaledFont); // Convert our GlyphBuffer into an array of Cairo glyphs. std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs); for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) { glyphs[i].index = aBuffer.mGlyphs[i].mIndex; glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x; glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y; } cairo_new_path(ctx); cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs); RefPtr<PathCairo> newPath = new PathCairo(ctx); if (isNewContext) { cairo_destroy(ctx); } return newPath.forget(); } #endif return nullptr; }
void compile(CompileOptions& opts) { Buffer buf = opts.read(); TempAllocator4096 ta; JsonObject object(ta); JsonArray glyphs(ta); sjson::parse(buf, object); sjson::parse_array(object["glyphs"], glyphs); const u32 texture_size = sjson::parse_int(object["size"]); const u32 font_size = sjson::parse_int(object["font_size"]); const u32 num_glyphs = array::size(glyphs); Array<GlyphInfo> _glyphs(default_allocator()); array::resize(_glyphs, num_glyphs); for (u32 i = 0; i < num_glyphs; ++i) { parse_glyph(glyphs[i], _glyphs[i]); } std::sort(array::begin(_glyphs), array::end(_glyphs)); opts.write(RESOURCE_VERSION_FONT); opts.write(num_glyphs); opts.write(texture_size); opts.write(font_size); for (u32 i = 0; i < array::size(_glyphs); ++i) { opts.write(_glyphs[i].cp); } for (u32 i = 0; i < array::size(_glyphs); ++i) { opts.write(_glyphs[i].gd.x); opts.write(_glyphs[i].gd.y); opts.write(_glyphs[i].gd.width); opts.write(_glyphs[i].gd.height); opts.write(_glyphs[i].gd.x_offset); opts.write(_glyphs[i].gd.y_offset); opts.write(_glyphs[i].gd.x_advance); } }
void ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint) { #ifdef USE_SKIA if (aBackendType == BackendType::SKIA) { PathBuilderSkia *builder = static_cast<PathBuilderSkia*>(aBuilder); builder->AppendPath(GetSkiaPathForGlyphs(aBuffer)); return; } #endif #ifdef USE_CAIRO if (aBackendType == BackendType::CAIRO) { MOZ_ASSERT(mScaledFont); PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(aBuilder); cairo_t *ctx = cairo_create(DrawTargetCairo::GetDummySurface()); if (aTransformHint) { cairo_matrix_t mat; GfxMatrixToCairoMatrix(*aTransformHint, mat); cairo_set_matrix(ctx, &mat); } // Convert our GlyphBuffer into an array of Cairo glyphs. std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs); for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) { glyphs[i].index = aBuffer.mGlyphs[i].mIndex; glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x; glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y; } cairo_set_scaled_font(ctx, mScaledFont); cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs); RefPtr<PathCairo> cairoPath = new PathCairo(ctx); cairo_destroy(ctx); cairoPath->AppendPathToBuilder(builder); return; } #endif MOZ_CRASH("The specified backend type is not supported by CopyGlyphsToBuilder"); }
bool GlyphPage::fill(UChar* buffer, unsigned bufferLength, const Font* fontData) { bool haveGlyphs = false; Vector<CGGlyph, 512> glyphs(bufferLength); if (!shouldUseCoreText(buffer, bufferLength, fontData)) { // We pass in either 256 or 512 UTF-16 characters: 256 for U+FFFF and less, 512 (double character surrogates) // for U+10000 and above. It is indeed possible to get back 512 glyphs back from the API, so the glyph buffer // we pass in must be 512. If we get back more than 256 glyphs though we'll ignore all the ones after 256, // this should not happen as the only time we pass in 512 characters is when they are surrogates. CGFontGetGlyphsForUnichars(fontData->platformData().cgFont(), buffer, glyphs.data(), bufferLength); for (unsigned i = 0; i < GlyphPage::size; ++i) { if (!glyphs[i]) setGlyphDataForIndex(i, 0, 0); else { setGlyphDataForIndex(i, glyphs[i], fontData); haveGlyphs = true; } } } else { // Because we know the implementation of shouldUseCoreText(), if the font isn't for text combine and it isn't a system font, // we know it must have vertical glyphs. if (fontData->platformData().isForTextCombine() || fontData->isSystemFont()) CTFontGetGlyphsForCharacters(fontData->platformData().ctFont(), buffer, glyphs.data(), bufferLength); else CTFontGetVerticalGlyphsForCharacters(fontData->platformData().ctFont(), buffer, glyphs.data(), bufferLength); // When buffer consists of surrogate pairs, CTFontGetVerticalGlyphsForCharacters and CTFontGetGlyphsForCharacters // place the glyphs at indices corresponding to the first character of each pair. ASSERT(bufferLength == GlyphPage::size || bufferLength == 2 * GlyphPage::size); unsigned glyphStep = bufferLength / GlyphPage::size; for (unsigned i = 0; i < GlyphPage::size; ++i) { if (!glyphs[i * glyphStep]) setGlyphDataForIndex(i, 0, 0); else { setGlyphDataForIndex(i, glyphs[i * glyphStep], fontData); haveGlyphs = true; } } } return haveGlyphs; }
static GrPathRange* get_gr_glyphs(GrContext* ctx, const SkTypeface* typeface, const SkDescriptor* desc, const SkStrokeRec& stroke) { static const GrCacheID::Domain gGlyphsDomain = GrCacheID::GenerateDomain(); GrCacheID::Key key; uint64_t* keyData = key.fData64; keyData[0] = desc ? desc->getChecksum() : 0; keyData[0] = (keyData[0] << 32) | (typeface ? typeface->uniqueID() : 0); keyData[1] = GrPath::ComputeStrokeKey(stroke); GrResourceKey resourceKey = GrResourceKey(GrCacheID(gGlyphsDomain, key), GrPathRange::resourceType(), 0); SkAutoTUnref<GrPathRange> glyphs( static_cast<GrPathRange*>(ctx->findAndRefCachedResource(resourceKey))); if (NULL == glyphs || (NULL != desc && !glyphs->isEqualTo(*desc))) { glyphs.reset(ctx->getGpu()->pathRendering()->createGlyphs(typeface, desc, stroke)); ctx->addResourceToCache(resourceKey, glyphs); } return glyphs.detach(); }
void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx, GrRenderTargetContext* renderTargetContext, const GrClip& clip, const SkMatrix& viewMatrix, const SkSurfaceProps& props, SkScalar x, SkScalar y, const SkIRect& clipBounds, GrAtlasTextContext* fallbackTextContext, const SkPaint& originalSkPaint) const { SkASSERT(fInstanceData); if (fInstanceData->count()) { static constexpr GrUserStencilSettings kCoverPass( GrUserStencilSettings::StaticInit< 0x0000, GrUserStencilTest::kNotEqual, // Stencil pass accounts for clip. 0xffff, GrUserStencilOp::kZero, GrUserStencilOp::kKeep, 0xffff>() ); sk_sp<GrPathRange> glyphs(this->createGlyphs(ctx->resourceProvider())); if (fLastDrawnGlyphsID != glyphs->uniqueID()) { // Either this is the first draw or the glyphs object was purged since last draw. glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->count()); fLastDrawnGlyphsID = glyphs->uniqueID(); } GrPaint grPaint; if (!SkPaintToGrPaint(ctx, renderTargetContext, originalSkPaint, viewMatrix, &grPaint)) { return; } // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy // the entire dst. Realistically this is a moot point, because any context that supports // NV_path_rendering will also support NV_blend_equation_advanced. // For clipping we'll just skip any optimizations based on the bounds. This does, however, // hurt GrOp combining. const SkRect bounds = SkRect::MakeIWH(renderTargetContext->width(), renderTargetContext->height()); // The run's "font" overrides the anti-aliasing of the passed in SkPaint! GrAAType aaType; if (this->aa() == GrAA::kYes) { SkASSERT(renderTargetContext->isStencilBufferMultisampled()); aaType = renderTargetContext->isUnifiedMultisampled() ? GrAAType::kMSAA : GrAAType::kMixedSamples; } else { aaType = GrAAType::kNone; } std::unique_ptr<GrDrawOp> op = GrDrawPathRangeOp::Make( viewMatrix, fTextRatio, fTextInverseRatio * x, fTextInverseRatio * y, std::move(grPaint), GrPathRendering::kWinding_FillType, aaType, glyphs.get(), fInstanceData.get(), bounds); renderTargetContext->addDrawOp(clip, std::move(op)); } if (fFallbackTextBlob) { SkPaint fallbackSkPaint(originalSkPaint); fStyle.strokeRec().applyToPaint(&fallbackSkPaint); if (!fStyle.isSimpleFill()) { fallbackSkPaint.setStrokeWidth(fStyle.strokeRec().getWidth() * fTextRatio); } fallbackTextContext->drawTextBlob(ctx, renderTargetContext, clip, fallbackSkPaint, viewMatrix, props, fFallbackTextBlob.get(), x, y, nullptr, clipBounds); } }
virtual void onDraw(SkCanvas* inputCanvas) override { SkScalar textSizes[] = { 9.0f, 9.0f*2.0f, 9.0f*5.0f, 9.0f*2.0f*5.0f }; SkScalar scales[] = { 2.0f*5.0f, 5.0f, 2.0f, 1.0f }; // set up offscreen rendering with distance field text GrContext* ctx = inputCanvas->getGrContext(); SkISize size = onISize(); SkImageInfo info = SkImageInfo::MakeN32(size.width(), size.height(), kPremul_SkAlphaType, inputCanvas->imageInfo().refColorSpace()); SkSurfaceProps props(SkSurfaceProps::kUseDeviceIndependentFonts_Flag, SkSurfaceProps::kLegacyFontHost_InitType); auto surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info, 0, &props)); SkCanvas* canvas = surface ? surface->getCanvas() : inputCanvas; // init our new canvas with the old canvas's matrix canvas->setMatrix(inputCanvas->getTotalMatrix()); // apply global scale to test glyph positioning canvas->scale(1.05f, 1.05f); canvas->clear(0xffffffff); SkPaint paint; paint.setAntiAlias(true); SkFont font(ToolUtils::create_portable_typeface("serif", SkFontStyle())); font.setSubpixel(true); const char* text = "Hamburgefons"; const size_t textLen = strlen(text); // check scaling up SkScalar x = SkIntToScalar(0); SkScalar y = SkIntToScalar(78); for (size_t i = 0; i < SK_ARRAY_COUNT(textSizes); ++i) { SkAutoCanvasRestore acr(canvas, true); canvas->translate(x, y); canvas->scale(scales[i], scales[i]); font.setSize(textSizes[i]); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 0, 0, font, paint); y += font.getMetrics(nullptr)*scales[i]; } // check rotation for (size_t i = 0; i < 5; ++i) { SkScalar rotX = SkIntToScalar(10); SkScalar rotY = y; SkAutoCanvasRestore acr(canvas, true); canvas->translate(SkIntToScalar(10 + i * 200), -80); canvas->rotate(SkIntToScalar(i * 5), rotX, rotY); for (int ps = 6; ps <= 32; ps += 3) { font.setSize(SkIntToScalar(ps)); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, rotX, rotY, font, paint); rotY += font.getMetrics(nullptr); } } // check scaling down font.setEdging(SkFont::Edging::kSubpixelAntiAlias); x = SkIntToScalar(680); y = SkIntToScalar(20); size_t arraySize = SK_ARRAY_COUNT(textSizes); for (size_t i = 0; i < arraySize; ++i) { SkAutoCanvasRestore acr(canvas, true); canvas->translate(x, y); SkScalar scaleFactor = SkScalarInvert(scales[arraySize - i - 1]); canvas->scale(scaleFactor, scaleFactor); font.setSize(textSizes[i]); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 0, 0, font, paint); y += font.getMetrics(nullptr)*scaleFactor; } // check pos text { SkAutoCanvasRestore acr(canvas, true); canvas->scale(2.0f, 2.0f); SkAutoTArray<SkGlyphID> glyphs(SkToInt(textLen)); int count = font.textToGlyphs(text, textLen, SkTextEncoding::kUTF8, glyphs.get(), textLen); SkAutoTArray<SkPoint> pos(count); font.setSize(textSizes[0]); font.getPos(glyphs.get(), count, pos.get(), {340, 75}); auto blob = SkTextBlob::MakeFromPosText(glyphs.get(), count * sizeof(SkGlyphID), pos.get(), font, SkTextEncoding::kGlyphID); canvas->drawTextBlob(blob, 0, 0, paint); } // check gamma-corrected blending const SkColor fg[] = { 0xFFFFFFFF, 0xFFFFFF00, 0xFFFF00FF, 0xFF00FFFF, 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFF000000, }; paint.setColor(0xFFF7F3F7); SkRect r = SkRect::MakeLTRB(670, 215, 820, 397); canvas->drawRect(r, paint); x = SkIntToScalar(680); y = SkIntToScalar(235); font.setSize(SkIntToScalar(19)); for (size_t i = 0; i < SK_ARRAY_COUNT(fg); ++i) { paint.setColor(fg[i]); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, x, y, font, paint); y += font.getMetrics(nullptr); } paint.setColor(0xFF181C18); r = SkRect::MakeLTRB(820, 215, 970, 397); canvas->drawRect(r, paint); x = SkIntToScalar(830); y = SkIntToScalar(235); font.setSize(SkIntToScalar(19)); for (size_t i = 0; i < SK_ARRAY_COUNT(fg); ++i) { paint.setColor(fg[i]); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, x, y, font, paint); y += font.getMetrics(nullptr); } // check skew { font.setEdging(SkFont::Edging::kAntiAlias); SkAutoCanvasRestore acr(canvas, true); canvas->skew(0.0f, 0.151515f); font.setSize(SkIntToScalar(32)); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 745, 70, font, paint); } { font.setEdging(SkFont::Edging::kSubpixelAntiAlias); SkAutoCanvasRestore acr(canvas, true); canvas->skew(0.5f, 0.0f); font.setSize(SkIntToScalar(32)); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 580, 125, font, paint); } // check perspective { font.setEdging(SkFont::Edging::kAntiAlias); SkAutoCanvasRestore acr(canvas, true); SkMatrix persp; persp.setAll(0.9839f, 0, 0, 0.2246f, 0.6829f, 0, 0.0002352f, -0.0003844f, 1); canvas->concat(persp); canvas->translate(1100, -295); font.setSize(37.5f); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 0, 0, font, paint); } { font.setSubpixel(false); font.setEdging(SkFont::Edging::kAlias); SkAutoCanvasRestore acr(canvas, true); SkMatrix persp; persp.setAll(0.9839f, 0, 0, 0.2246f, 0.6829f, 0, 0.0002352f, -0.0003844f, 1); canvas->concat(persp); canvas->translate(1075, -245); canvas->scale(375, 375); font.setSize(0.1f); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 0, 0, font, paint); } // check color emoji if (fEmojiTypeface) { SkFont emoiFont; emoiFont.setSubpixel(true); emoiFont.setTypeface(fEmojiTypeface); emoiFont.setSize(SkIntToScalar(19)); canvas->drawSimpleText(fEmojiText, strlen(fEmojiText), SkTextEncoding::kUTF8, 670, 90, emoiFont, paint); } // render offscreen buffer if (surface) { SkAutoCanvasRestore acr(inputCanvas, true); // since we prepended this matrix already, we blit using identity inputCanvas->resetMatrix(); inputCanvas->drawImage(surface->makeImageSnapshot().get(), 0, 0, nullptr); } }
bool SimpleFontData::fillGlyphPage(GlyphPage* pageToFill, unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength) const { bool haveGlyphs = false; Vector<CGGlyph, 512> glyphs(bufferLength); if (!shouldUseCoreText(buffer, bufferLength, this)) { CGFontGetGlyphsForUnichars(platformData().cgFont(), buffer, glyphs.data(), bufferLength); for (unsigned i = 0; i < length; ++i) { if (glyphs[i]) { pageToFill->setGlyphDataForIndex(offset + i, glyphs[i], this); haveGlyphs = true; } } } else if (!platformData().isCompositeFontReference() && platformData().widthVariant() != RegularWidth && CTFontGetGlyphsForCharacters(platformData().ctFont(), buffer, glyphs.data(), bufferLength)) { // When buffer consists of surrogate pairs, CTFontGetGlyphsForCharacters // places the glyphs at indices corresponding to the first character of each pair. unsigned glyphStep = bufferLength / length; for (unsigned i = 0; i < length; ++i) { if (glyphs[i * glyphStep]) { pageToFill->setGlyphDataForIndex(offset + i, glyphs[i * glyphStep], this); haveGlyphs = true; } } } else { // We ask CoreText for possible vertical variant glyphs RetainPtr<CFStringRef> string(AdoptCF, CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, buffer, bufferLength, kCFAllocatorNull)); RetainPtr<CFAttributedStringRef> attributedString(AdoptCF, CFAttributedStringCreate(kCFAllocatorDefault, string.get(), getCFStringAttributes(0, hasVerticalGlyphs() ? Vertical : Horizontal))); RetainPtr<CTLineRef> line(AdoptCF, CTLineCreateWithAttributedString(attributedString.get())); CFArrayRef runArray = CTLineGetGlyphRuns(line.get()); CFIndex runCount = CFArrayGetCount(runArray); Vector<CGGlyph, 512> glyphVector; Vector<CFIndex, 512> indexVector; bool done = false; // For the CGFont comparison in the loop, use the CGFont that Core Text assigns to the CTFont. This may // be non-CFEqual to platformData().cgFont(). RetainPtr<CGFontRef> cgFont(AdoptCF, CTFontCopyGraphicsFont(platformData().ctFont(), 0)); for (CFIndex r = 0; r < runCount && !done ; ++r) { // CTLine could map characters over multiple fonts using its own font fallback list. // We need to pick runs that use the exact font we need, i.e., platformData().ctFont(). CTRunRef ctRun = static_cast<CTRunRef>(CFArrayGetValueAtIndex(runArray, r)); ASSERT(CFGetTypeID(ctRun) == CTRunGetTypeID()); CFDictionaryRef attributes = CTRunGetAttributes(ctRun); CTFontRef runFont = static_cast<CTFontRef>(CFDictionaryGetValue(attributes, kCTFontAttributeName)); RetainPtr<CGFontRef> runCGFont(AdoptCF, CTFontCopyGraphicsFont(runFont, 0)); // Use CGFont here as CFEqual for CTFont counts all attributes for font. bool gotBaseFont = CFEqual(cgFont.get(), runCGFont.get()); if (gotBaseFont || platformData().isCompositeFontReference()) { // This run uses the font we want. Extract glyphs. CFIndex glyphCount = CTRunGetGlyphCount(ctRun); const CGGlyph* glyphs = CTRunGetGlyphsPtr(ctRun); if (!glyphs) { glyphVector.resize(glyphCount); CTRunGetGlyphs(ctRun, CFRangeMake(0, 0), glyphVector.data()); glyphs = glyphVector.data(); } const CFIndex* stringIndices = CTRunGetStringIndicesPtr(ctRun); if (!stringIndices) { indexVector.resize(glyphCount); CTRunGetStringIndices(ctRun, CFRangeMake(0, 0), indexVector.data()); stringIndices = indexVector.data(); } if (gotBaseFont) { for (CFIndex i = 0; i < glyphCount; ++i) { if (stringIndices[i] >= static_cast<CFIndex>(length)) { done = true; break; } if (glyphs[i]) { pageToFill->setGlyphDataForIndex(offset + stringIndices[i], glyphs[i], this); haveGlyphs = true; } } } else { const SimpleFontData* runSimple = getCompositeFontReferenceFontData((NSFont *)runFont); if (runSimple) { for (CFIndex i = 0; i < glyphCount; ++i) { if (stringIndices[i] >= static_cast<CFIndex>(length)) { done = true; break; } if (glyphs[i]) { pageToFill->setGlyphDataForIndex(offset + stringIndices[i], glyphs[i], runSimple); haveGlyphs = true; } } } } } } } return haveGlyphs; }
template <typename T> EXPORTGTPLPLOT bool plotCurves(const std::vector<hoNDArray<T> >& x, const std::vector<hoNDArray<T> >& y, const std::string& xlabel, const std::string& ylabel, const std::string& title, const std::vector<std::string>& legend, const std::vector<std::string>& symbols, size_t xsize, size_t ysize, T xlim[2], T ylim[2], bool trueColor, bool drawLine, hoNDArray<float>& plotIm) { try { GADGET_CHECK_RETURN_FALSE(x.size()>0); GADGET_CHECK_RETURN_FALSE(y.size()>0); GADGET_CHECK_RETURN_FALSE(x.size() == y.size()); T minX = xlim[0]; T maxX = xlim[1]; T minY = ylim[0]; T maxY = ylim[1]; plsdev("mem"); hoNDArray<unsigned char> im; im.create(3, xsize, ysize); Gadgetron::clear(im); plsmem(im.get_size(1), im.get_size(2), im.begin()); plinit(); plfont(2); pladv(0); if (legend.size() == x.size()) { plvpor(0.11, 0.75, 0.1, 0.9); } else { plvpor(0.15, 0.85, 0.1, 0.9); } T spaceX = 0.01*(maxX - minX); T spaceY = 0.05*(maxY - minY); plwind(minX - spaceX, maxX + spaceX, minY - spaceY, maxY + spaceY); plcol0(15); plbox("bgcnst", 0.0, 0, "bgcnstv", 0.0, 0); // int mark[2], space[2]; //mark[0] = 4000; //space[0] = 2500; //plstyl(1, mark, space); size_t num = x.size(); size_t n; hoNDArray<double> xd, yd; // draw lines for (n = 0; n < num; n++) { size_t N = y[n].get_size(0); xd.copyFrom(x[n]); yd.copyFrom(y[n]); if (drawLine) { int c; getPlotColor(n, c); plcol0(c); pllsty(n % 8 + 1); plline(N, xd.begin(), yd.begin()); } std::string gly; if(symbols.size()>n) { gly = symbols[n]; } else getPlotGlyph(n, gly); plstring(N, xd.begin(), yd.begin(), gly.c_str()); } plcol0(15); plmtex("b", 3.2, 0.5, 0.5, xlabel.c_str()); plmtex("t", 2.0, 0.5, 0.5, title.c_str()); plmtex("l", 5.0, 0.5, 0.5, ylabel.c_str()); // draw the legend if (legend.size() == x.size()) { std::vector<PLINT> opt_array(num), text_colors(num), line_colors(num), line_styles(num), symbol_numbers(num), symbol_colors(num); std::vector<PLFLT> symbol_scales(num), line_widths(num), box_scales(num, 1); std::vector<std::string> glyphs(num); std::vector<const char*> symbols(num); PLFLT legend_width, legend_height; std::vector<const char*> legend_text(num); for (n = 0; n < num; n++) { int c; getPlotColor(n, c); getPlotGlyph(n, glyphs[n]); opt_array[n] = PL_LEGEND_SYMBOL | PL_LEGEND_LINE; text_colors[n] = 15; line_colors[n] = c; line_styles[n] = (n%8+1); line_widths[n] = 0.2; symbol_colors[n] = c; symbol_scales[n] = 0.75; symbol_numbers[n] = 1; symbols[n] = glyphs[n].c_str(); legend_text[n] = legend[n].c_str(); } pllegend(&legend_width, &legend_height, PL_LEGEND_BACKGROUND, PL_POSITION_OUTSIDE | PL_POSITION_RIGHT | PL_POSITION_TOP, 0.02, // x 0.0, // y 0.05, // plot_width 0, // bg_color 15, // bb_color 1, // bb_style 0, // nrow 0, // ncolumn num, // nlegend &opt_array[0], 0.05, // text_offset 0.35, // text_scale 1.0, // text_spacing 0.5, // text_justification &text_colors[0], (const char **)(&legend_text[0]), NULL, // box_colors NULL, // box_patterns &box_scales[0], // box_scales NULL, // box_line_widths &line_colors[0], &line_styles[0], &line_widths[0], &symbol_colors[0], &symbol_scales[0], &symbol_numbers[0], (const char **)(&symbols[0]) ); } plend(); outputPlotIm(im, trueColor, plotIm); } catch (...) { GERROR_STREAM("Errors happened in plotCurves(xlim, ylim) ... "); return false; } return true; }
void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx, GrDrawContext* dc, GrPipelineBuilder* pipelineBuilder, GrColor color, const SkMatrix& viewMatrix, const SkSurfaceProps& props, SkScalar x, SkScalar y, const SkIRect& clipBounds, GrTextContext* fallbackTextContext, const SkPaint& originalSkPaint) const { SkASSERT(fInstanceData); SkASSERT(dc->accessRenderTarget()->isStencilBufferMultisampled() || !fFont.isAntiAlias()); if (fInstanceData->count()) { pipelineBuilder->setState(GrPipelineBuilder::kHWAntialias_Flag, fFont.isAntiAlias()); GR_STATIC_CONST_SAME_STENCIL(kStencilPass, kZero_StencilOp, kKeep_StencilOp, kNotEqual_StencilFunc, 0xffff, 0x0000, 0xffff); *pipelineBuilder->stencil() = kStencilPass; SkAutoTUnref<GrPathRange> glyphs(this->createGlyphs(ctx)); if (fLastDrawnGlyphsID != glyphs->getUniqueID()) { // Either this is the first draw or the glyphs object was purged since last draw. glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->count()); fLastDrawnGlyphsID = glyphs->getUniqueID(); } // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy // the entire dst. Realistically this is a moot point, because any context that supports // NV_path_rendering will also support NV_blend_equation_advanced. // For clipping we'll just skip any optimizations based on the bounds. This does, however, // hurt batching. SkRect bounds = SkRect::MakeIWH(pipelineBuilder->getRenderTarget()->width(), pipelineBuilder->getRenderTarget()->height()); SkAutoTUnref<GrDrawPathBatchBase> batch( GrDrawPathRangeBatch::Create(viewMatrix, fTextRatio, fTextInverseRatio * x, fTextInverseRatio * y, color, GrPathRendering::kWinding_FillType, glyphs, fInstanceData, bounds)); dc->drawPathBatch(*pipelineBuilder, batch); } if (fFallbackTextBlob) { SkPaint fallbackSkPaint(originalSkPaint); fStroke.applyToPaint(&fallbackSkPaint); if (!fStroke.isFillStyle()) { fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio); } fallbackTextContext->drawTextBlob(ctx, dc, pipelineBuilder->clip(), fallbackSkPaint, viewMatrix, props, fFallbackTextBlob, x, y, nullptr, clipBounds); } }
bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) { bool haveGlyphs = false; Vector<CGGlyph, 512> glyphs(bufferLength); if (!shouldUseCoreText(buffer, bufferLength, fontData)) { // We pass in either 256 or 512 UTF-16 characters: 256 for U+FFFF and less, 512 (double character surrogates) // for U+10000 and above. It is indeed possible to get back 512 glyphs back from the API, so the glyph buffer // we pass in must be 512. If we get back more than 256 glyphs though we'll ignore all the ones after 256, // this should not happen as the only time we pass in 512 characters is when they are surrogates. CGFontGetGlyphsForUnichars(fontData->platformData().cgFont(), buffer, glyphs.data(), bufferLength); for (unsigned i = 0; i < length; ++i) { if (!glyphs[i]) setGlyphDataForIndex(offset + i, 0, 0); else { setGlyphDataForIndex(offset + i, glyphs[i], fontData); haveGlyphs = true; } } } else if (!fontData->platformData().isCompositeFontReference() && ((fontData->platformData().widthVariant() == RegularWidth) ? wkGetVerticalGlyphsForCharacters(fontData->platformData().ctFont(), buffer, glyphs.data(), bufferLength) : CTFontGetGlyphsForCharacters(fontData->platformData().ctFont(), buffer, glyphs.data(), bufferLength))) { // When buffer consists of surrogate pairs, wkGetVerticalGlyphsForCharacters and CTFontGetGlyphsForCharacters // place the glyphs at indices corresponding to the first character of each pair. unsigned glyphStep = bufferLength / length; for (unsigned i = 0; i < length; ++i) { if (!glyphs[i * glyphStep]) setGlyphDataForIndex(offset + i, 0, 0); else { setGlyphDataForIndex(offset + i, glyphs[i * glyphStep], fontData); haveGlyphs = true; } } } else { // We ask CoreText for possible vertical variant glyphs RetainPtr<CFStringRef> string = adoptCF(CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, buffer, bufferLength, kCFAllocatorNull)); RetainPtr<CFAttributedStringRef> attributedString = adoptCF(CFAttributedStringCreate(kCFAllocatorDefault, string.get(), fontData->getCFStringAttributes(0, fontData->hasVerticalGlyphs() ? Vertical : Horizontal))); RetainPtr<CTLineRef> line = adoptCF(CTLineCreateWithAttributedString(attributedString.get())); CFArrayRef runArray = CTLineGetGlyphRuns(line.get()); CFIndex runCount = CFArrayGetCount(runArray); // Initialize glyph entries for (unsigned index = 0; index < length; ++index) setGlyphDataForIndex(offset + index, 0, 0); Vector<CGGlyph, 512> glyphVector; Vector<CFIndex, 512> indexVector; bool done = false; // For the CGFont comparison in the loop, use the CGFont that Core Text assigns to the CTFont. This may // be non-CFEqual to fontData->platformData().cgFont(). RetainPtr<CGFontRef> cgFont = adoptCF(CTFontCopyGraphicsFont(fontData->platformData().ctFont(), 0)); for (CFIndex r = 0; r < runCount && !done ; ++r) { // CTLine could map characters over multiple fonts using its own font fallback list. // We need to pick runs that use the exact font we need, i.e., fontData->platformData().ctFont(). CTRunRef ctRun = static_cast<CTRunRef>(CFArrayGetValueAtIndex(runArray, r)); ASSERT(CFGetTypeID(ctRun) == CTRunGetTypeID()); CFDictionaryRef attributes = CTRunGetAttributes(ctRun); CTFontRef runFont = static_cast<CTFontRef>(CFDictionaryGetValue(attributes, kCTFontAttributeName)); RetainPtr<CGFontRef> runCGFont = adoptCF(CTFontCopyGraphicsFont(runFont, 0)); // Use CGFont here as CFEqual for CTFont counts all attributes for font. bool gotBaseFont = CFEqual(cgFont.get(), runCGFont.get()); if (gotBaseFont || fontData->platformData().isCompositeFontReference()) { // This run uses the font we want. Extract glyphs. CFIndex glyphCount = CTRunGetGlyphCount(ctRun); const CGGlyph* glyphs = CTRunGetGlyphsPtr(ctRun); if (!glyphs) { glyphVector.resize(glyphCount); CTRunGetGlyphs(ctRun, CFRangeMake(0, 0), glyphVector.data()); glyphs = glyphVector.data(); } const CFIndex* stringIndices = CTRunGetStringIndicesPtr(ctRun); if (!stringIndices) { indexVector.resize(glyphCount); CTRunGetStringIndices(ctRun, CFRangeMake(0, 0), indexVector.data()); stringIndices = indexVector.data(); } if (gotBaseFont) { for (CFIndex i = 0; i < glyphCount; ++i) { if (stringIndices[i] >= static_cast<CFIndex>(length)) { done = true; break; } if (glyphs[i]) { setGlyphDataForIndex(offset + stringIndices[i], glyphs[i], fontData); haveGlyphs = true; } } #if !PLATFORM(IOS) } else { const SimpleFontData* runSimple = fontData->getCompositeFontReferenceFontData((NSFont *)runFont); if (runSimple) { for (CFIndex i = 0; i < glyphCount; ++i) { if (stringIndices[i] >= static_cast<CFIndex>(length)) { done = true; break; } if (glyphs[i]) { setGlyphDataForIndex(offset + stringIndices[i], glyphs[i], runSimple); haveGlyphs = true; } } } #endif // !PLATFORM(IOS) } } } } return haveGlyphs; }
bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) { bool haveGlyphs = false; #ifndef BUILDING_ON_TIGER if (fontData->orientation() == Horizontal || fontData->isBrokenIdeographFont()) { Vector<CGGlyph, 512> glyphs(bufferLength); wkGetGlyphsForCharacters(fontData->platformData().cgFont(), buffer, glyphs.data(), bufferLength); for (unsigned i = 0; i < length; ++i) { if (!glyphs[i]) setGlyphDataForIndex(offset + i, 0, 0); else { setGlyphDataForIndex(offset + i, glyphs[i], fontData); haveGlyphs = true; } } } else { // We ask CoreText for possible vertical variant glyphs RetainPtr<CFStringRef> string(AdoptCF, CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, buffer, bufferLength, kCFAllocatorNull)); RetainPtr<CFAttributedStringRef> attributedString(AdoptCF, CFAttributedStringCreate(kCFAllocatorDefault, string.get(), fontData->getCFStringAttributes(0))); RetainPtr<CTLineRef> line(AdoptCF, CTLineCreateWithAttributedString(attributedString.get())); CFArrayRef runArray = CTLineGetGlyphRuns(line.get()); CFIndex runCount = CFArrayGetCount(runArray); // Initialize glyph entries for (unsigned index = 0; index < length; ++index) setGlyphDataForIndex(offset + index, 0, 0); Vector<CGGlyph, 512> glyphVector; Vector<CFIndex, 512> indexVector; bool done = false; for (CFIndex r = 0; r < runCount && !done ; ++r) { // CTLine could map characters over multiple fonts using its own font fallback list. // We need to pick runs that use the exact font we need, i.e., fontData->platformData().ctFont(). CTRunRef ctRun = static_cast<CTRunRef>(CFArrayGetValueAtIndex(runArray, r)); ASSERT(CFGetTypeID(ctRun) == CTRunGetTypeID()); CFDictionaryRef attributes = CTRunGetAttributes(ctRun); CTFontRef runFont = static_cast<CTFontRef>(CFDictionaryGetValue(attributes, kCTFontAttributeName)); RetainPtr<CGFontRef> runCGFont(AdoptCF, CTFontCopyGraphicsFont(runFont, 0)); // Use CGFont here as CFEqual for CTFont counts all attributes for font. if (CFEqual(fontData->platformData().cgFont(), runCGFont.get())) { // This run uses the font we want. Extract glyphs. CFIndex glyphCount = CTRunGetGlyphCount(ctRun); const CGGlyph* glyphs = CTRunGetGlyphsPtr(ctRun); if (!glyphs) { glyphVector.resize(glyphCount); CTRunGetGlyphs(ctRun, CFRangeMake(0, 0), glyphVector.data()); glyphs = glyphVector.data(); } const CFIndex* stringIndices = CTRunGetStringIndicesPtr(ctRun); if (!stringIndices) { indexVector.resize(glyphCount); CTRunGetStringIndices(ctRun, CFRangeMake(0, 0), indexVector.data()); stringIndices = indexVector.data(); } for (CFIndex i = 0; i < glyphCount; ++i) { if (stringIndices[i] >= static_cast<CFIndex>(length)) { done = true; break; } if (glyphs[i]) { setGlyphDataForIndex(offset + stringIndices[i], glyphs[i], fontData); haveGlyphs = true; } } } } } #else // Use an array of long so we get good enough alignment. long glyphVector[(GLYPH_VECTOR_SIZE + sizeof(long) - 1) / sizeof(long)]; OSStatus status = wkInitializeGlyphVector(GlyphPage::size, &glyphVector); if (status != noErr) // This should never happen, perhaps indicates a bad font! If it does the // font substitution code will find an alternate font. return false; wkConvertCharToGlyphs(fontData->m_styleGroup, buffer, bufferLength, &glyphVector); unsigned numGlyphs = wkGetGlyphVectorNumGlyphs(&glyphVector); if (numGlyphs != length) { // This should never happen, perhaps indicates a bad font? // If it does happen, the font substitution code will find an alternate font. wkClearGlyphVector(&glyphVector); return false; } ATSLayoutRecord* glyphRecord = (ATSLayoutRecord*)wkGetGlyphVectorFirstRecord(glyphVector); for (unsigned i = 0; i < length; i++) { Glyph glyph = glyphRecord->glyphID; if (!glyph) setGlyphDataForIndex(offset + i, 0, 0); else { setGlyphDataForIndex(offset + i, glyph, fontData); haveGlyphs = true; } glyphRecord = (ATSLayoutRecord *)((char *)glyphRecord + wkGetGlyphVectorRecordSize(glyphVector)); } wkClearGlyphVector(&glyphVector); #endif return haveGlyphs; }
bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) { bool haveGlyphs = false; if (!shouldUseCoreText(buffer, bufferLength, fontData)) { Vector<CGGlyph, 512> glyphs(bufferLength); CMFontGetGlyphsForUnichars(fontData->platformData().cgFont(), buffer, glyphs.data(), bufferLength); // wkGetGlyphsForCharacters(fontData->platformData().cgFont(), buffer, glyphs.data(), bufferLength); for (unsigned i = 0; i < length; ++i) { if (!glyphs[i]) setGlyphDataForIndex(offset + i, 0, 0); else { setGlyphDataForIndex(offset + i, glyphs[i], fontData); haveGlyphs = true; } } } else { // We ask CoreText for possible vertical variant glyphs RetainPtr<CFStringRef> string(AdoptCF, CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, buffer, bufferLength, kCFAllocatorNull)); RetainPtr<CFAttributedStringRef> attributedString(AdoptCF, CFAttributedStringCreate(kCFAllocatorDefault, string.get(), fontData->getCFStringAttributes(0, fontData->hasVerticalGlyphs() ? Vertical : Horizontal))); RetainPtr<CTLineRef> line(AdoptCF, CTLineCreateWithAttributedString(attributedString.get())); CFArrayRef runArray = CTLineGetGlyphRuns(line.get()); CFIndex runCount = CFArrayGetCount(runArray); // Initialize glyph entries for (unsigned index = 0; index < length; ++index) setGlyphDataForIndex(offset + index, 0, 0); Vector<CGGlyph, 512> glyphVector; Vector<CFIndex, 512> indexVector; bool done = false; // For the CGFont comparison in the loop, use the CGFont that Core Text assigns to the CTFont. This may // be non-CFEqual to fontData->platformData().cgFont(). RetainPtr<CGFontRef> cgFont(AdoptCF, CTFontCopyGraphicsFont(fontData->platformData().ctFont(), 0)); for (CFIndex r = 0; r < runCount && !done ; ++r) { // CTLine could map characters over multiple fonts using its own font fallback list. // We need to pick runs that use the exact font we need, i.e., fontData->platformData().ctFont(). CTRunRef ctRun = static_cast<CTRunRef>(CFArrayGetValueAtIndex(runArray, r)); ASSERT(CFGetTypeID(ctRun) == CTRunGetTypeID()); CFDictionaryRef attributes = CTRunGetAttributes(ctRun); CTFontRef runFont = static_cast<CTFontRef>(CFDictionaryGetValue(attributes, kCTFontAttributeName)); RetainPtr<CGFontRef> runCGFont(AdoptCF, CTFontCopyGraphicsFont(runFont, 0)); // Use CGFont here as CFEqual for CTFont counts all attributes for font. if (CFEqual(cgFont.get(), runCGFont.get())) { // This run uses the font we want. Extract glyphs. CFIndex glyphCount = CTRunGetGlyphCount(ctRun); const CGGlyph* glyphs = CTRunGetGlyphsPtr(ctRun); if (!glyphs) { glyphVector.resize(glyphCount); CTRunGetGlyphs(ctRun, CFRangeMake(0, 0), glyphVector.data()); glyphs = glyphVector.data(); } const CFIndex* stringIndices = CTRunGetStringIndicesPtr(ctRun); if (!stringIndices) { indexVector.resize(glyphCount); CTRunGetStringIndices(ctRun, CFRangeMake(0, 0), indexVector.data()); stringIndices = indexVector.data(); } for (CFIndex i = 0; i < glyphCount; ++i) { if (stringIndices[i] >= static_cast<CFIndex>(length)) { done = true; break; } if (glyphs[i]) { setGlyphDataForIndex(offset + stringIndices[i], glyphs[i], fontData); haveGlyphs = true; } } } } } return haveGlyphs; }
void tool_fontextract(const vdfastvector<const char *>& args, const vdfastvector<const char *>& switches) { if (args.size() < 6) help_fontextract(); printf("Asuka: Extracting font: %s -> %s.\n", args[0], args[4]); int startChar; int endChar; if (1 != sscanf(args[2], "%d", &startChar) || 1 != sscanf(args[3], "%d", &endChar)) help_fontextract(); if (startChar < 0 || endChar > 0xFFFF || endChar <= startChar) { printf("Asuka: Invalid character range %d-%d\n", startChar, endChar); exit(10); } int fontsAdded = AddFontResourceEx(args[0], FR_NOT_ENUM | FR_PRIVATE, 0); if (!fontsAdded) { printf("Asuka: Unable to load font file: %s\n", args[0]); exit(10); } HFONT hfont = CreateFontA(10, 0, 0, 0, 0, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, args[1]); if (!hfont) { printf("Asuka: Unable to instantiate font: %s\n", args[1]); exit(10); } HDC hdc = CreateDC("DISPLAY", 0, 0, 0); HGDIOBJ hfontOld = SelectObject(hdc, hfont); MAT2 m = { {0,1},{0,0},{0,0},{0,1} }; union { OUTLINETEXTMETRICW m; char buf[2048]; } metrics; if (!GetOutlineTextMetricsW(hdc, sizeof metrics, &metrics.m)) { printf("Asuka: Unable to retrieve outline text metrics.\n"); exit(10); } SelectObject(hdc, hfontOld); DeleteObject(hfont); hfont = CreateFontA(metrics.m.otmEMSquare, 0, 0, 0, 0, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, args[1]); if (!hfont) { printf("Asuka: Unable to instantiate font: %s\n", args[1]); exit(10); } hfontOld = SelectObject(hdc, hfont); if (!GetOutlineTextMetricsW(hdc, sizeof metrics, &metrics.m)) { printf("Asuka: Unable to retrieve outline text metrics.\n"); exit(10); } FILE *f = fopen(args[4], "w"); if (!f) { printf("Asuka: Unable to open output file: %s\n", args[4]); exit(10); } std::vector<GlyphInfo> glyphs(endChar - startChar + 1); sint32 minX = 0x7FFFFFFF; sint32 minY = 0x7FFFFFFF; sint32 maxX = -0x7FFFFFFF - 1; sint32 maxY = -0x7FFFFFFF - 1; vdfastvector<sint32> points; vdfastvector<uint8> commands; for(int c=startChar; c<endChar; ++c) { GlyphInfo& info = glyphs[c - startChar]; info.mPointStart = points.size() >> 1; info.mCommandStart = commands.size(); GetCharABCWidthsFloatW(hdc, c, c, &info.mWidths); // encode glyph outline GLYPHMETRICS gm; if (GDI_ERROR == GetGlyphOutlineW(hdc, c, GGO_METRICS, &gm, 0, NULL, &m)) { printf("Asuka: Unable to obtain glyph metrics for char %d.\n", c); exit(20); } DWORD dwBytes = GetGlyphOutlineW(hdc, c, GGO_NATIVE | GGO_UNHINTED, &gm, 0, NULL, &m); if (dwBytes == 0xFFFFFFFF) { printf("Asuka: Unable to obtain glyph outline for char %d.\n", c); exit(20); } // GetGlyphOutline() doesn't like providing results for spaces. if (gm.gmBlackBoxX <= 0 || gm.gmBlackBoxY <= 0) continue; void *buf = malloc(dwBytes); GetGlyphOutlineW(hdc, c, GGO_NATIVE | GGO_UNHINTED, &gm, dwBytes, buf, &m); void *limit = (char *)buf + dwBytes; TTPOLYGONHEADER *pHdr = (TTPOLYGONHEADER *)buf; sint32 lastCode = 0; while(pHdr != limit) { VDASSERT(pHdr->dwType == TT_POLYGON_TYPE); sint32 x = *(const sint32 *)&pHdr->pfxStart.x; sint32 y = *(const sint32 *)&pHdr->pfxStart.y; if (minX > x) minX = x; if (minY > y) minY = y; if (maxX < x) maxX = x; if (maxY < y) maxY = y; points.push_back(x); points.push_back(y); TTPOLYCURVE *pCurve = (TTPOLYCURVE *)(pHdr + 1); TTPOLYCURVE *pCurveEnd = (TTPOLYCURVE *)((char *)pHdr + pHdr->cb); while(pCurve != pCurveEnd) { VDASSERT(pCurve->wType == TT_PRIM_QSPLINE || pCurve->wType == TT_PRIM_LINE); POINTFX *ppfx = pCurve->apfx; for(int i=0; i<pCurve->cpfx; ++i) { sint32 x = *(const sint32 *)&ppfx->x; sint32 y = *(const sint32 *)&ppfx->y; if (minX > x) minX = x; if (minY > y) minY = y; if (maxX < x) maxX = x; if (maxY < y) maxY = y; points.push_back(x); points.push_back(y); ++ppfx; } if (pCurve->wType == TT_PRIM_LINE) { if ((lastCode & 3) != 2) { if (lastCode) commands.push_back(lastCode); lastCode = 2 - 4; } for(int i=0; i<pCurve->cpfx; ++i) { if (lastCode >= 0x7C) { commands.push_back(lastCode); lastCode = 2 - 4; } lastCode += 4; } } else { VDASSERT(pCurve->wType == TT_PRIM_QSPLINE); VDASSERT(!(pCurve->cpfx % 2)); if ((lastCode & 3) != 3) { if (lastCode) commands.push_back(lastCode); lastCode = 3 - 4; } for(int i=0; i<pCurve->cpfx; i+=2) { if (lastCode >= 0x7C) { commands.push_back(lastCode); lastCode = 3 - 4; } lastCode += 4; } } pCurve = (TTPOLYCURVE *)ppfx; } if (lastCode) { commands.push_back(lastCode | 0x80); lastCode = 0; } vdptrstep(pHdr, pHdr->cb); } free(buf); } GlyphInfo& lastInfo = glyphs.back(); lastInfo.mPointStart = points.size() >> 1; lastInfo.mCommandStart = commands.size(); // write points fprintf(f, "// Created by Asuka from %s. DO NOT EDIT!\n\n", VDFileSplitPath(args[0])); fprintf(f, "const uint16 %s_FontPointArray[]={\n", args[5]); float scaleX = (maxX > minX) ? 1.0f / (maxX - minX) : 0.0f; float scaleY = (maxY > minY) ? 1.0f / (maxY - minY) : 0.0f; int pointElementCount = points.size(); for(int i=0; i<pointElementCount; i+=2) { uint8 x = VDClampedRoundFixedToUint8Fast((float)(points[i+0] - minX) * scaleX); uint8 y = VDClampedRoundFixedToUint8Fast((float)(points[i+1] - minY) * scaleY); uint16 pt = ((uint16)y << 8) + x; fprintf(f, "0x%04x,", pt); if ((i & 30) == 30) putc('\n', f); } if (pointElementCount & 30) putc('\n', f); fprintf(f, "};\n\n"); // write commands fprintf(f, "const uint8 %s_FontCommandArray[]={\n", args[5]); int commandElementCount = commands.size(); for(int i=0; i<commandElementCount; ++i) { fprintf(f, "0x%02x,", commands[i]); if ((i & 15) == 15) putc('\n', f); } if (commandElementCount & 15) putc('\n', f); fprintf(f, "};\n\n"); // glyph data structures fprintf(f, "const VDOutlineFontGlyphInfo %s_FontGlyphArray[]={\n", args[5]); for(int i=startChar; i<=endChar; ++i) { const GlyphInfo& info = glyphs[i - startChar]; fprintf(f, "{ %d, %d, %d, %d, %d },\n", info.mPointStart, info.mCommandStart, VDRoundToInt(info.mWidths.abcfA), VDRoundToInt(info.mWidths.abcfB), VDRoundToInt(info.mWidths.abcfC)); } fprintf(f, "};\n\n"); // top-level data structure fprintf(f, "const VDOutlineFontInfo %s_FontInfo={\n", args[5]); fprintf(f, "\t%s_FontPointArray,\n", args[5]); fprintf(f, "\t%s_FontCommandArray,\n", args[5]); fprintf(f, "\t%s_FontGlyphArray,\n", args[5]); fprintf(f, "\t%d, %d,\n", startChar, endChar); fprintf(f, "\t%d, %d, %d, %d,\t// bounds (16:16)\n", minX, minY, maxX, maxY); fprintf(f, "\t%d,\t// em square\n", metrics.m.otmEMSquare); fprintf(f, "\t%d,\t// ascent\n", metrics.m.otmAscent); fprintf(f, "\t%d,\t// descent\n", metrics.m.otmDescent); fprintf(f, "\t%d,\t// line gap\n", metrics.m.otmLineGap); fprintf(f, "};\n"); printf("Asuka: %d point bytes, %d command bytes, %d glyph info bytes.\n", (int)points.size(), (int)commands.size(), (endChar - startChar + 1) * 10); SelectObject(hdc, hfontOld); DeleteDC(hdc); DeleteObject(hfont); }
void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx, GrDrawContext* drawContext, const GrPaint& grPaint, const GrClip& clip, const SkMatrix& viewMatrix, const SkSurfaceProps& props, SkScalar x, SkScalar y, const SkIRect& clipBounds, GrAtlasTextContext* fallbackTextContext, const SkPaint& originalSkPaint) const { SkASSERT(fInstanceData); SkASSERT(drawContext->isStencilBufferMultisampled() || !grPaint.isAntiAlias()); if (fInstanceData->count()) { static constexpr GrUserStencilSettings kCoverPass( GrUserStencilSettings::StaticInit< 0x0000, GrUserStencilTest::kNotEqual, // Stencil pass accounts for clip. 0xffff, GrUserStencilOp::kZero, GrUserStencilOp::kKeep, 0xffff>() ); SkAutoTUnref<GrPathRange> glyphs(this->createGlyphs(ctx)); if (fLastDrawnGlyphsID != glyphs->uniqueID()) { // Either this is the first draw or the glyphs object was purged since last draw. glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->count()); fLastDrawnGlyphsID = glyphs->uniqueID(); } // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy // the entire dst. Realistically this is a moot point, because any context that supports // NV_path_rendering will also support NV_blend_equation_advanced. // For clipping we'll just skip any optimizations based on the bounds. This does, however, // hurt batching. const SkRect bounds = SkRect::MakeIWH(drawContext->width(), drawContext->height()); SkAutoTUnref<GrDrawBatch> batch( GrDrawPathRangeBatch::Create(viewMatrix, fTextRatio, fTextInverseRatio * x, fTextInverseRatio * y, grPaint.getColor(), GrPathRendering::kWinding_FillType, glyphs, fInstanceData, bounds)); GrPipelineBuilder pipelineBuilder(grPaint); pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_Flag, grPaint.isAntiAlias()); pipelineBuilder.setUserStencil(&kCoverPass); drawContext->drawBatch(pipelineBuilder, clip, batch); } if (fFallbackTextBlob) { SkPaint fallbackSkPaint(originalSkPaint); fStyle.strokeRec().applyToPaint(&fallbackSkPaint); if (!fStyle.isSimpleFill()) { fallbackSkPaint.setStrokeWidth(fStyle.strokeRec().getWidth() * fTextRatio); } fallbackTextContext->drawTextBlob(ctx, drawContext, clip, fallbackSkPaint, viewMatrix, props, fFallbackTextBlob.get(), x, y, nullptr, clipBounds); } }
TemporaryRef<Path> ScaledFontBase::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) { #ifdef USE_SKIA if (aTarget->GetType() == BACKEND_SKIA) { SkPaint paint; paint.setTypeface(GetSkTypeface()); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setTextSize(SkFloatToScalar(mSize)); std::vector<uint16_t> indices; std::vector<SkPoint> offsets; indices.resize(aBuffer.mNumGlyphs); offsets.resize(aBuffer.mNumGlyphs); for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) { indices[i] = aBuffer.mGlyphs[i].mIndex; offsets[i].fX = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.x); offsets[i].fY = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.y); } SkPath path; paint.getPosTextPath(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), &path); return new PathSkia(path, FILL_WINDING); } #endif #ifdef USE_CAIRO if (aTarget->GetType() == BACKEND_CAIRO) { MOZ_ASSERT(mScaledFont); DrawTarget *dt = const_cast<DrawTarget*>(aTarget); cairo_t *ctx = static_cast<cairo_t*>(dt->GetNativeSurface(NATIVE_SURFACE_CAIRO_CONTEXT)); bool isNewContext = !ctx; if (!ctx) { ctx = cairo_create(DrawTargetCairo::GetDummySurface()); cairo_matrix_t mat; GfxMatrixToCairoMatrix(aTarget->GetTransform(), mat); cairo_set_matrix(ctx, &mat); } cairo_set_scaled_font(ctx, mScaledFont); // Convert our GlyphBuffer into an array of Cairo glyphs. std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs); for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) { glyphs[i].index = aBuffer.mGlyphs[i].mIndex; glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x; glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y; } cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs); RefPtr<PathCairo> newPath = new PathCairo(ctx); if (isNewContext) { cairo_destroy(ctx); } return newPath; } #endif return nullptr; }
/************************************************************************* SAX2 Handler methods *************************************************************************/ void Font_xmlHandler::elementStart(const String& element, const XMLAttributes& attributes) { // handle a Mapping element if (element == MappingElement) { if (!d_font->d_freetype) { String image_name(attributes.getValueAsString(MappingImageAttribute)); utf32 codepoint = (utf32)attributes.getValueAsInteger(MappingCodepointAttribute); int horzAdvance = attributes.getValueAsInteger(MappingHorzAdvanceAttribute, -1); Font::glyphDat mapDat; mapDat.d_image = &d_font->d_glyph_images->getImage(image_name); // calculate advance width if it was not specified if (horzAdvance == AutoGenerateHorzAdvance) { horzAdvance = (int)(mapDat.d_image->getWidth() + mapDat.d_image->getOffsetX()); } mapDat.d_horz_advance_unscaled = horzAdvance; mapDat.d_horz_advance = (uint)(((float)horzAdvance) * (d_font->d_autoScale ? d_font->d_horzScaling : 1.0f)); d_font->d_cp_map[codepoint] = mapDat; } else { Logger::getSingleton().logEvent((utf8*)"Mapping element encountered. This element is invalid for dynamic fonts.", Informative); } } // handle root Font element else if (element == FontElement) { // get name of font we are creating String font_name(attributes.getValueAsString(FontNameAttribute)); // get filename for the font String filename(attributes.getValueAsString(FontFilenameAttribute)); // get resource group for font file. String resourceGroup(attributes.getValueAsString(FontResourceGroupAttribute)); Logger::getSingleton().logEvent("Started creation of Font '" + font_name + "' via XML file.", Informative); // // load auto-scaling configuration // float hres, vres; bool auto_scale; // get native horizontal resolution hres = (float)attributes.getValueAsInteger(FontNativeHorzResAttribute, 640); // get native vertical resolution vres = (float)attributes.getValueAsInteger(FontNativeVertResAttribute, 480); // get auto-scaling setting auto_scale = attributes.getValueAsBool(FontAutoScaledAttribute, false); // // get type of font // String font_type(attributes.getValueAsString(FontTypeAttribute)); // dynamic (ttf) font if (font_type == FontTypeDynamic) { // get size of font uint size = (uint)attributes.getValueAsInteger(FontSizeAttribute, 12); // extract codepoint range utf32 first_codepoint = (utf32)attributes.getValueAsInteger(FontFirstCodepointAttribute, 32); utf32 last_codepoint = (utf32)attributes.getValueAsInteger(FontLastCodepointAttribute, 127); // build string containing the required code-points. for (; first_codepoint <= last_codepoint; ++first_codepoint) { d_glyphSet += first_codepoint; } uint flags = attributes.getValueAsBool(FontAntiAliasedAttribute, true) ? 0 : NoAntiAlias; // perform pre-initialisation d_font->setNativeResolution(Size(hres, vres)); d_font->setAutoScalingEnabled(auto_scale); // Finalise construction of font without glyphs. // Glyphs will defined after we know which ones we need. d_font->constructor_impl(font_name, filename, resourceGroup, size, flags, String("")); } // static (Imageset based) font else if (font_type == FontTypeStatic) { d_font->d_name = font_name; d_font->d_freetype = false; // load the Imageset d_font->d_glyph_images = ImagesetManager::getSingleton().createImageset(filename, resourceGroup); d_font->setNativeResolution(Size(hres, vres)); d_font->setAutoScalingEnabled(auto_scale); } // error (should never happen) else { throw FileIOException("Font::xmlHandler::startElement - The unknown Font:Type attribute value '" + font_type + "' was encountered while processing the Font file."); } d_font->d_sourceFilename = filename; } // Glyph element else if (element == GlyphElement) { if (d_font->d_freetype) { utf32 codepoint = (utf32)attributes.getValueAsInteger(GlyphCodepointAttribute); if (d_glyphSet.find(codepoint) == String::npos) { d_glyphSet.append(1, codepoint); } } else { Logger::getSingleton().logEvent((utf8*)"Glyph element encountered. This element is invalid for static fonts.", Informative); } } // GlyphRange element else if (element == GlyphRangeElement) { if (d_font->d_freetype) { utf32 start = (utf32)attributes.getValueAsInteger(GlyphRangeStartCodepointAttribute); utf32 end = (utf32)attributes.getValueAsInteger(GlyphRangeEndCodepointAttribute); for (utf32 codepoint = start; codepoint <= end; ++codepoint) { if (d_glyphSet.find(codepoint) == String::npos) { d_glyphSet.append(1, codepoint); } } } else { Logger::getSingleton().logEvent((utf8*)"GlyphRange element encountered. This element is invalid for static fonts.", Informative); } } // GlyphSet element else if (element == GlyphSetElement) { if (d_font->d_freetype) { String glyphs(attributes.getValueAsString(GlyphSetGlyphsAttribute)); for (String::size_type i = 0; i < glyphs.length(); ++i) { utf32 codepoint = glyphs[i]; if (d_glyphSet.find(codepoint) == String::npos) { d_glyphSet.append(1, codepoint); } } } else { Logger::getSingleton().logEvent((utf8*)"GlyphSet element encountered. This element is invalid for static fonts.", Informative); } } // anything else is an error which *should* have already been caught by XML validation else { throw FileIOException("Font::xmlHandler::startElement - Unexpected data was found while parsing the Font file: '" + element + "' is unknown."); } }