void Transform::transform(GLfixed* point, int x, int y) const { SkPoint s; mTransform.mapXY(SkIntToScalar(x), SkIntToScalar(y), &s); point[0] = SkScalarToFixed(s.fX); point[1] = SkScalarToFixed(s.fY); }
void SkRandomScalerContext::generateAdvance(SkGlyph* glyph) { fProxy->getAdvance(glyph); SkVector advance; fMatrix.mapXY(SkFixedToScalar(glyph->fAdvanceX), SkFixedToScalar(glyph->fAdvanceY), &advance); glyph->fAdvanceX = SkScalarToFixed(advance.fX); glyph->fAdvanceY = SkScalarToFixed(advance.fY); }
void SkScalerContext_DW::generateAdvance(SkGlyph* glyph) { //Delta is the difference between the right/left side bearing metric //and where the right/left side bearing ends up after hinting. //DirectWrite does not provide this information. glyph->fRsbDelta = 0; glyph->fLsbDelta = 0; glyph->fAdvanceX = 0; glyph->fAdvanceY = 0; uint16_t glyphId = glyph->getGlyphID(); DWRITE_GLYPH_METRICS gm; if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode || DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode) { HRVM(fTypeface->fDWriteFontFace->GetGdiCompatibleGlyphMetrics( fTextSizeMeasure, 1.0f, // pixelsPerDip &fGsA, DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode, &glyphId, 1, &gm), "Could not get gdi compatible glyph metrics."); } else { HRVM(fTypeface->fDWriteFontFace->GetDesignGlyphMetrics(&glyphId, 1, &gm), "Could not get design metrics."); } DWRITE_FONT_METRICS dwfm; fTypeface->fDWriteFontFace->GetMetrics(&dwfm); SkScalar advanceX = SkScalarMulDiv(fTextSizeMeasure, SkIntToScalar(gm.advanceWidth), SkIntToScalar(dwfm.designUnitsPerEm)); SkVector vecs[1] = { { advanceX, 0 } }; if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode || DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode) { // DirectWrite produced 'compatible' metrics, but while close, // the end result is not always an integer as it would be with GDI. vecs[0].fX = SkScalarRoundToScalar(advanceX); fG_inv.mapVectors(vecs, SK_ARRAY_COUNT(vecs)); } else { fSkXform.mapVectors(vecs, SK_ARRAY_COUNT(vecs)); } glyph->fAdvanceX = SkScalarToFixed(vecs[0].fX); glyph->fAdvanceY = SkScalarToFixed(vecs[0].fY); }
SkColor SkHSVToColor(U8CPU a, const SkScalar hsv[3]) { SkASSERT(hsv); U8CPU s = SkUnitScalarClampToByte(hsv[1]); U8CPU v = SkUnitScalarClampToByte(hsv[2]); if (0 == s) { // shade of gray return SkColorSetARGB(a, v, v, v); } SkFixed hx = (hsv[0] < 0 || hsv[0] >= SkIntToScalar(360)) ? 0 : SkScalarToFixed(hsv[0]/60); SkFixed f = hx & 0xFFFF; unsigned v_scale = SkAlpha255To256(v); unsigned p = SkAlphaMul(255 - s, v_scale); unsigned q = SkAlphaMul(255 - (s * f >> 16), v_scale); unsigned t = SkAlphaMul(255 - (s * (SK_Fixed1 - f) >> 16), v_scale); unsigned r, g, b; SkASSERT((unsigned)(hx >> 16) < 6); switch (hx >> 16) { case 0: r = v; g = t; b = p; break; case 1: r = q; g = v; b = p; break; case 2: r = p; g = v; b = t; break; case 3: r = p; g = q; b = v; break; case 4: r = t; g = p; b = v; break; default: r = v; g = p; b = q; break; } return SkColorSetARGB(a, r, g, b); }
static void test_cubic2() { const char* str = "M2242 -590088L-377758 9.94099e+07L-377758 9.94099e+07L2242 -590088Z"; SkPath path; SkParsePath::FromSVGString(str, &path); { #ifdef SK_BUILD_FOR_WIN // windows doesn't have strtof float x = (float)strtod("9.94099e+07", NULL); #else float x = strtof("9.94099e+07", NULL); #endif int ix = (int)x; int fx = (int)(x * 65536); int ffx = SkScalarToFixed(x); printf("%g %x %x %x\n", x, ix, fx, ffx); SkRect r = path.getBounds(); SkIRect ir; r.round(&ir); printf("[%g %g %g %g] [%x %x %x %x]\n", SkScalarToDouble(r.fLeft), SkScalarToDouble(r.fTop), SkScalarToDouble(r.fRight), SkScalarToDouble(r.fBottom), ir.fLeft, ir.fTop, ir.fRight, ir.fBottom); } SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, 300, 200); bitmap.allocPixels(); SkCanvas canvas(bitmap); SkPaint paint; paint.setAntiAlias(true); canvas.drawPath(path, paint); }
void SkRandomScalerContext::generateMetrics(SkGlyph* glyph) { fProxy->getAdvance(glyph); SkVector advance; fMatrix.mapXY(SkFixedToScalar(glyph->fAdvanceX), SkFixedToScalar(glyph->fAdvanceY), &advance); glyph->fAdvanceX = SkScalarToFixed(advance.fX); glyph->fAdvanceY = SkScalarToFixed(advance.fY); SkPath path; fProxy->getPath(*glyph, &path); path.transform(fMatrix); SkRect storage; const SkPaint& paint = fFace->paint(); const SkRect& newBounds = paint.doComputeFastBounds(path.getBounds(), &storage, SkPaint::kFill_Style); SkIRect ibounds; newBounds.roundOut(&ibounds); glyph->fLeft = ibounds.fLeft; glyph->fTop = ibounds.fTop; glyph->fWidth = ibounds.width(); glyph->fHeight = ibounds.height(); // Here we will change the mask format of the glyph // NOTE this is being overridden by the base class SkMask::Format format; switch (glyph->getGlyphID() % 6) { case 0: format = SkMask::kLCD16_Format; break; case 1: format = SkMask::kA8_Format; break; case 2: format = SkMask::kARGB32_Format; break; default: // we will fiddle with these in generate image format = (SkMask::Format)MASK_FORMAT_UNKNOWN; } glyph->fMaskFormat = format; }
/* * Analyze filter-quality and matrix, and decide how to implement that. * * In general, we cascade down the request level [ High ... None ] * - for a given level, if we can fulfill it, fine, else * - else we downgrade to the next lower level and try again. * We can always fulfill requests for Low and None * - sometimes we will "ignore" Low and give None, but this is likely a legacy perf hack * and may be removed. */ bool SkBitmapProcState::chooseProcs() { fInvProc = SkMatrixPriv::GetMapXYProc(fInvMatrix); fInvSx = SkScalarToFixed(fInvMatrix.getScaleX()); fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX()); fInvKy = SkScalarToFixed(fInvMatrix.getSkewY()); fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY()); fAlphaScale = SkAlpha255To256(SkColorGetA(fPaintColor)); fShaderProc32 = nullptr; fShaderProc16 = nullptr; fSampleProc32 = nullptr; const bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; const bool clampClamp = SkShader::kClamp_TileMode == fTileModeX && SkShader::kClamp_TileMode == fTileModeY; return this->chooseScanlineProcs(trivialMatrix, clampClamp); }
// static void SkPDFScalar::Append(SkScalar value, SkWStream* stream) { // The range of reals in PDF/A is the same as SkFixed: +/- 32,767 and // +/- 1/65,536 (though integers can range from 2^31 - 1 to -2^31). // When using floats that are outside the whole value range, we can use // integers instead. #if defined(SK_SCALAR_IS_FIXED) stream->writeScalarAsText(value); return; #endif // SK_SCALAR_IS_FIXED #if !defined(SK_ALLOW_LARGE_PDF_SCALARS) if (value > 32767 || value < -32767) { stream->writeDecAsText(SkScalarRound(value)); return; } char buffer[SkStrAppendScalar_MaxSize]; char* end = SkStrAppendFixed(buffer, SkScalarToFixed(value)); stream->write(buffer, end - buffer); return; #endif // !SK_ALLOW_LARGE_PDF_SCALARS #if defined(SK_SCALAR_IS_FLOAT) && defined(SK_ALLOW_LARGE_PDF_SCALARS) // Floats have 24bits of significance, so anything outside that range is // no more precise than an int. (Plus PDF doesn't support scientific // notation, so this clamps to SK_Max/MinS32). if (value > (1 << 24) || value < -(1 << 24)) { stream->writeDecAsText(value); return; } // Continue to enforce the PDF limits for small floats. if (value < 1.0f/65536 && value > -1.0f/65536) { stream->writeDecAsText(0); return; } // SkStrAppendFloat might still use scientific notation, so use snprintf // directly.. static const int kFloat_MaxSize = 19; char buffer[kFloat_MaxSize]; int len = SNPRINTF(buffer, kFloat_MaxSize, "%#.8f", value); // %f always prints trailing 0s, so strip them. for (; buffer[len - 1] == '0' && len > 0; len--) { buffer[len - 1] = '\0'; } if (buffer[len - 1] == '.') { buffer[len - 1] = '\0'; } stream->writeText(buffer); return; #endif // SK_SCALAR_IS_FLOAT && SK_ALLOW_LARGE_PDF_SCALARS }
GrTexture* GrCircleBlurFragmentProcessor::CreateCircleBlurProfileTexture( GrTextureProvider* textureProvider, const SkRect& circle, float sigma, float* offset) { float halfWH = circle.width() / 2.0f; int size; compute_profile_offset_and_size(halfWH, sigma, offset, &size); GrSurfaceDesc texDesc; texDesc.fWidth = size; texDesc.fHeight = 1; texDesc.fConfig = kAlpha_8_GrPixelConfig; static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); GrUniqueKey key; GrUniqueKey::Builder builder(&key, kDomain, 2); // The profile curve varies with both the sigma of the Gaussian and the size of the // disk. Quantizing to 16.16 should be close enough though. builder[0] = SkScalarToFixed(sigma); builder[1] = SkScalarToFixed(halfWH); builder.finish(); GrTexture *blurProfile = textureProvider->findAndRefTextureByUniqueKey(key); if (!blurProfile) { SkAutoTDeleteArray<uint8_t> profile(create_profile(halfWH, sigma)); blurProfile = textureProvider->createTexture(texDesc, SkBudgeted::kYes, profile.get(), 0); if (blurProfile) { textureProvider->assignUniqueKeyToTexture(key, blurProfile); } } return blurProfile; }
bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { if (fOrigBitmap.width() == 0 || fOrigBitmap.height() == 0) { return false; } const SkMatrix* m; bool trivial_matrix = (inv.getType() & ~SkMatrix::kTranslate_Mask) == 0; bool clamp_clamp = SkShader::kClamp_TileMode == fTileModeX && SkShader::kClamp_TileMode == fTileModeY; if (clamp_clamp || trivial_matrix) { m = &inv; } else { fUnitInvMatrix = inv; fUnitInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height()); m = &fUnitInvMatrix; } fBitmap = &fOrigBitmap; if (fOrigBitmap.hasMipMap()) { int shift = fOrigBitmap.extractMipLevel(&fMipBitmap, SkScalarToFixed(m->getScaleX()), SkScalarToFixed(m->getSkewY())); if (shift > 0) { if (m != &fUnitInvMatrix) { fUnitInvMatrix = *m; m = &fUnitInvMatrix; } SkScalar scale = SkFixedToScalar(SK_Fixed1 >> shift); fUnitInvMatrix.postScale(scale, scale); // now point here instead of fOrigBitmap fBitmap = &fMipBitmap; } }
static size_t linebreak(const char text[], const char stop[], const SkPaint& paint, SkScalar margin) { const char* start = text; SkAutoGlyphCache ac(paint, NULL); SkGlyphCache* cache = ac.getCache(); SkFixed w = 0; SkFixed limit = SkScalarToFixed(margin); SkAutoKern autokern; const char* word_start = text; int prevWS = true; while (text < stop) { const char* prevText = text; SkUnichar uni = SkUTF8_NextUnichar(&text); int currWS = is_ws(uni); const SkGlyph& glyph = cache->getUnicharMetrics(uni); if (!currWS && prevWS) word_start = prevText; prevWS = currWS; w += autokern.adjust(glyph) + glyph.fAdvanceX; if (w > limit) { if (currWS) // eat the rest of the whitespace { while (text < stop && is_ws(SkUTF8_ToUnichar(text))) text += SkUTF8_CountUTF8Bytes(text); } else // backup until a whitespace (or 1 char) { if (word_start == start) { if (prevText > start) text = prevText; } else text = word_start; } break; } } return text - start; }
SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit) : INHERITED(desc.fLocalMatrix) , fPtsToUnit(ptsToUnit) { fPtsToUnit.getType(); // Precache so reads are threadsafe. SkASSERT(desc.fCount > 1); fGradFlags = SkToU8(desc.fGradFlags); SkASSERT((unsigned)desc.fTileMode < SkShader::kTileModeCount); SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs)); fTileMode = desc.fTileMode; fTileProc = gTileProcs[desc.fTileMode]; /* Note: we let the caller skip the first and/or last position. i.e. pos[0] = 0.3, pos[1] = 0.7 In these cases, we insert dummy entries to ensure that the final data will be bracketed by [0, 1]. i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1 Thus colorCount (the caller's value, and fColorCount (our value) may differ by up to 2. In the above example: colorCount = 2 fColorCount = 4 */ fColorCount = desc.fCount; // check if we need to add in dummy start and/or end position/colors bool dummyFirst = false; bool dummyLast = false; if (desc.fPos) { dummyFirst = desc.fPos[0] != 0; dummyLast = desc.fPos[desc.fCount - 1] != SK_Scalar1; fColorCount += dummyFirst + dummyLast; } if (fColorCount > kColorStorageCount) { size_t size = sizeof(SkColor) + sizeof(Rec); if (desc.fPos) { size += sizeof(SkScalar); } fOrigColors = reinterpret_cast<SkColor*>( sk_malloc_throw(size * fColorCount)); } else { fOrigColors = fStorage; } // Now copy over the colors, adding the dummies as needed { SkColor* origColors = fOrigColors; if (dummyFirst) { *origColors++ = desc.fColors[0]; } memcpy(origColors, desc.fColors, desc.fCount * sizeof(SkColor)); if (dummyLast) { origColors += desc.fCount; *origColors = desc.fColors[desc.fCount - 1]; } } if (desc.fPos && fColorCount) { fOrigPos = (SkScalar*)(fOrigColors + fColorCount); fRecs = (Rec*)(fOrigPos + fColorCount); } else { fOrigPos = nullptr; fRecs = (Rec*)(fOrigColors + fColorCount); } if (fColorCount > 2) { Rec* recs = fRecs; recs->fPos = 0; // recs->fScale = 0; // unused; recs += 1; if (desc.fPos) { SkScalar* origPosPtr = fOrigPos; *origPosPtr++ = 0; /* We need to convert the user's array of relative positions into fixed-point positions and scale factors. We need these results to be strictly monotonic (no two values equal or out of order). Hence this complex loop that just jams a zero for the scale value if it sees a segment out of order, and it assures that we start at 0 and end at 1.0 */ SkScalar prev = 0; int startIndex = dummyFirst ? 0 : 1; int count = desc.fCount + dummyLast; for (int i = startIndex; i < count; i++) { // force the last value to be 1.0 SkScalar curr; if (i == desc.fCount) { // we're really at the dummyLast curr = 1; } else { curr = SkScalarPin(desc.fPos[i], 0, 1); } *origPosPtr++ = curr; recs->fPos = SkScalarToFixed(curr); SkFixed diff = SkScalarToFixed(curr - prev); if (diff > 0) { recs->fScale = (1 << 24) / diff; } else { recs->fScale = 0; // ignore this segment } // get ready for the next value prev = curr; recs += 1; } } else { // assume even distribution fOrigPos = nullptr; SkFixed dp = SK_Fixed1 / (desc.fCount - 1); SkFixed p = dp; SkFixed scale = (desc.fCount - 1) << 8; // (1 << 24) / dp for (int i = 1; i < desc.fCount - 1; i++) { recs->fPos = p; recs->fScale = scale; recs += 1; p += dp; } recs->fPos = SK_Fixed1; recs->fScale = scale; } } else if (desc.fPos) { SkASSERT(2 == fColorCount); fOrigPos[0] = SkScalarPin(desc.fPos[0], 0, 1); fOrigPos[1] = SkScalarPin(desc.fPos[1], fOrigPos[0], 1); if (0 == fOrigPos[0] && 1 == fOrigPos[1]) { fOrigPos = nullptr; } } this->initCommon(); }
virtual void flatten(SkFlattenableWriteBuffer& b) { // this->INHERITED::flatten(b); How can we know if this is legal???? b.write32(SkScalarToFixed(fExp)); }
static hb_position_t SkiaScalarToHarfBuzzPosition(SkScalar value) { return SkScalarToFixed(value); }
SkGradientShaderBase::SkGradientShaderBase(const SkColor colors[], const SkScalar pos[], int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) { SkASSERT(colorCount > 1); fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return fMapper = mapper; SkSafeRef(mapper); SkASSERT((unsigned)mode < SkShader::kTileModeCount); SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs)); fTileMode = mode; fTileProc = gTileProcs[mode]; fCache16 = fCache16Storage = NULL; fCache32 = NULL; fCache32PixelRef = NULL; /* Note: we let the caller skip the first and/or last position. i.e. pos[0] = 0.3, pos[1] = 0.7 In these cases, we insert dummy entries to ensure that the final data will be bracketed by [0, 1]. i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1 Thus colorCount (the caller's value, and fColorCount (our value) may differ by up to 2. In the above example: colorCount = 2 fColorCount = 4 */ fColorCount = colorCount; // check if we need to add in dummy start and/or end position/colors bool dummyFirst = false; bool dummyLast = false; if (pos) { dummyFirst = pos[0] != 0; dummyLast = pos[colorCount - 1] != SK_Scalar1; fColorCount += dummyFirst + dummyLast; } if (fColorCount > kColorStorageCount) { size_t size = sizeof(SkColor) + sizeof(Rec); fOrigColors = reinterpret_cast<SkColor*>( sk_malloc_throw(size * fColorCount)); } else { fOrigColors = fStorage; } // Now copy over the colors, adding the dummies as needed { SkColor* origColors = fOrigColors; if (dummyFirst) { *origColors++ = colors[0]; } memcpy(origColors, colors, colorCount * sizeof(SkColor)); if (dummyLast) { origColors += colorCount; *origColors = colors[colorCount - 1]; } } fRecs = (Rec*)(fOrigColors + fColorCount); if (fColorCount > 2) { Rec* recs = fRecs; recs->fPos = 0; // recs->fScale = 0; // unused; recs += 1; if (pos) { /* We need to convert the user's array of relative positions into fixed-point positions and scale factors. We need these results to be strictly monotonic (no two values equal or out of order). Hence this complex loop that just jams a zero for the scale value if it sees a segment out of order, and it assures that we start at 0 and end at 1.0 */ SkFixed prev = 0; int startIndex = dummyFirst ? 0 : 1; int count = colorCount + dummyLast; for (int i = startIndex; i < count; i++) { // force the last value to be 1.0 SkFixed curr; if (i == colorCount) { // we're really at the dummyLast curr = SK_Fixed1; } else { curr = SkScalarToFixed(pos[i]); } // pin curr withing range if (curr < 0) { curr = 0; } else if (curr > SK_Fixed1) { curr = SK_Fixed1; } recs->fPos = curr; if (curr > prev) { recs->fScale = (1 << 24) / (curr - prev); } else { recs->fScale = 0; // ignore this segment } // get ready for the next value prev = curr; recs += 1; } } else { // assume even distribution SkFixed dp = SK_Fixed1 / (colorCount - 1); SkFixed p = dp; SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp for (int i = 1; i < colorCount; i++) { recs->fPos = p; recs->fScale = scale; recs += 1; p += dp; } } } this->initCommon(); }
SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc) : SkScalerContext(desc), fFTSize(NULL) { SkAutoMutexAcquire ac(gFTMutex); FT_Error err; if (gFTCount == 0) { err = FT_Init_FreeType(&gFTLibrary); // SkDEBUGF(("FT_Init_FreeType returned %d\n", err)); SkASSERT(err == 0); } ++gFTCount; // load the font file fFaceRec = ref_ft_face(fRec.fFontID); fFace = fFaceRec ? fFaceRec->fFace : NULL; // compute our factors from the record SkMatrix m; fRec.getSingleMatrix(&m); #ifdef DUMP_STRIKE_CREATION SkString keyString; SkFontHost::GetDescriptorKeyString(desc, &keyString); printf("========== strike [%g %g %g] [%g %g %g %g] hints %d format %d %s\n", SkScalarToFloat(fRec.fTextSize), SkScalarToFloat(fRec.fPreScaleX), SkScalarToFloat(fRec.fPreSkewX), SkScalarToFloat(fRec.fPost2x2[0][0]), SkScalarToFloat(fRec.fPost2x2[0][1]), SkScalarToFloat(fRec.fPost2x2[1][0]), SkScalarToFloat(fRec.fPost2x2[1][1]), fRec.fHints, fRec.fMaskFormat, keyString.c_str()); #endif // now compute our scale factors SkScalar sx = m.getScaleX(); SkScalar sy = m.getScaleY(); if (m.getSkewX() || m.getSkewY() || sx < 0 || sy < 0) { // sort of give up on hinting sx = SkMaxScalar(SkScalarAbs(sx), SkScalarAbs(m.getSkewX())); sy = SkMaxScalar(SkScalarAbs(m.getSkewY()), SkScalarAbs(sy)); sx = sy = SkScalarAve(sx, sy); SkScalar inv = SkScalarInvert(sx); // flip the skew elements to go from our Y-down system to FreeType's fMatrix22.xx = SkScalarToFixed(SkScalarMul(m.getScaleX(), inv)); fMatrix22.xy = -SkScalarToFixed(SkScalarMul(m.getSkewX(), inv)); fMatrix22.yx = -SkScalarToFixed(SkScalarMul(m.getSkewY(), inv)); fMatrix22.yy = SkScalarToFixed(SkScalarMul(m.getScaleY(), inv)); } else { fMatrix22.xx = fMatrix22.yy = SK_Fixed1; fMatrix22.xy = fMatrix22.yx = 0; } fScaleX = SkScalarToFixed(sx); fScaleY = SkScalarToFixed(sy); // compute the flags we send to Load_Glyph { uint32_t flags = FT_LOAD_DEFAULT; uint32_t render_flags = FT_LOAD_TARGET_NORMAL; // we force autohinting at the moment switch (fRec.fHints) { case kNo_Hints: flags |= FT_LOAD_NO_HINTING; break; case kSubpixel_Hints: flags |= FT_LOAD_FORCE_AUTOHINT; render_flags = FT_LOAD_TARGET_LIGHT; break; case kNormal_Hints: flags |= FT_LOAD_FORCE_AUTOHINT; #ifdef ANDROID /* Switch to light hinting (vertical only) to address some chars that behaved poorly with NORMAL. In the future we could consider making this choice exposed at runtime to the caller. */ render_flags = FT_LOAD_TARGET_LIGHT; #endif break; } if (SkMask::kBW_Format == fRec.fMaskFormat) render_flags = FT_LOAD_TARGET_MONO; else if (SkMask::kLCD_Format == fRec.fMaskFormat) render_flags = FT_LOAD_TARGET_LCD; fLoadGlyphFlags = flags | render_flags; } // now create the FT_Size { FT_Error err; err = FT_New_Size(fFace, &fFTSize); if (err != 0) { SkDEBUGF(("SkScalerContext_FreeType::FT_New_Size(%x): FT_Set_Char_Size(0x%x, 0x%x) returned 0x%x\n", fFaceRec->fFontID, fScaleX, fScaleY, err)); fFace = NULL; return; } err = FT_Activate_Size(fFTSize); if (err != 0) { SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n", fFaceRec->fFontID, fScaleX, fScaleY, err)); fFTSize = NULL; } err = FT_Set_Char_Size( fFace, SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY), 72, 72); if (err != 0) { SkDEBUGF(("SkScalerContext_FreeType::FT_Set_Char_Size(%x, 0x%x, 0x%x) returned 0x%x\n", fFaceRec->fFontID, fScaleX, fScaleY, err)); fFace = NULL; return; } FT_Set_Transform( fFace, &fMatrix22, NULL); } }
/* * Analyze filter-quality and matrix, and decide how to implement that. * * In general, we cascade down the request level [ High ... None ] * - for a given level, if we can fulfill it, fine, else * - else we downgrade to the next lower level and try again. * We can always fulfill requests for Low and None * - sometimes we will "ignore" Low and give None, but this is likely a legacy perf hack * and may be removed. */ bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { fPixmap.reset(); fInvMatrix = inv; fFilterLevel = paint.getFilterQuality(); const int origW = fProvider.info().width(); const int origH = fProvider.info().height(); bool allow_ignore_fractional_translate = true; // historical default if (kMedium_SkFilterQuality == fFilterLevel) { allow_ignore_fractional_translate = false; } SkDefaultBitmapController controller; fBMState = controller.requestBitmap(fProvider, inv, paint.getFilterQuality(), fBMStateStorage.get(), fBMStateStorage.size()); // Note : we allow the controller to return an empty (zero-dimension) result. Should we? if (nullptr == fBMState || fBMState->pixmap().info().isEmpty()) { return false; } fPixmap = fBMState->pixmap(); fInvMatrix = fBMState->invMatrix(); fFilterLevel = fBMState->quality(); SkASSERT(fPixmap.addr()); bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; bool clampClamp = SkShader::kClamp_TileMode == fTileModeX && SkShader::kClamp_TileMode == fTileModeY; // Most of the scanline procs deal with "unit" texture coordinates, as this // makes it easy to perform tiling modes (repeat = (x & 0xFFFF)). To generate // those, we divide the matrix by its dimensions here. // // We don't do this if we're either trivial (can ignore the matrix) or clamping // in both X and Y since clamping to width,height is just as easy as to 0xFFFF. if (!(clampClamp || trivialMatrix)) { fInvMatrix.postIDiv(fPixmap.width(), fPixmap.height()); } // Now that all possible changes to the matrix have taken place, check // to see if we're really close to a no-scale matrix. If so, explicitly // set it to be so. Subsequent code may inspect this matrix to choose // a faster path in this case. // This code will only execute if the matrix has some scale component; // if it's already pure translate then we won't do this inversion. if (matrix_only_scale_translate(fInvMatrix)) { SkMatrix forward; if (fInvMatrix.invert(&forward)) { if ((clampClamp && allow_ignore_fractional_translate) ? just_trans_clamp(forward, fPixmap) : just_trans_general(forward)) { fInvMatrix.setTranslate(-forward.getTranslateX(), -forward.getTranslateY()); } } } fInvProc = fInvMatrix.getMapXYProc(); fInvType = fInvMatrix.getType(); fInvSx = SkScalarToFixed(fInvMatrix.getScaleX()); fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX()); fInvKy = SkScalarToFixed(fInvMatrix.getSkewY()); fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY()); fAlphaScale = SkAlpha255To256(paint.getAlpha()); fShaderProc32 = nullptr; fShaderProc16 = nullptr; fSampleProc32 = nullptr; // recompute the triviality of the matrix here because we may have // changed it! trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; // If our target pixmap is the same as the original, then we revert back to legacy behavior // and allow the code to ignore fractional translate. // // The width/height check allows allow_ignore_fractional_translate to stay false if we // previously set it that way (e.g. we started in kMedium). // if (fPixmap.width() == origW && fPixmap.height() == origH) { allow_ignore_fractional_translate = true; } if (kLow_SkFilterQuality == fFilterLevel && allow_ignore_fractional_translate) { // Only try bilerp if the matrix is "interesting" and // the image has a suitable size. if (fInvType <= SkMatrix::kTranslate_Mask || !valid_for_filtering(fPixmap.width() | fPixmap.height())) { fFilterLevel = kNone_SkFilterQuality; } } return this->chooseScanlineProcs(trivialMatrix, clampClamp, paint); }
void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* drawContext, GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint, const SkPaint& skPaint, const SkMatrix& viewMatrix, const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkIRect& regionClipBounds) { SkASSERT(byteLength == 0 || text != NULL); if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) { return; } // This is the slow path, mainly used by Skia unit tests. The other // backends (8888, gpu, ...) use device-space dependent glyph caches. In // order to match the glyph positions that the other code paths produce, we // must also use device-space dependent glyph cache. This has the // side-effect that the glyph shape outline will be in device-space, // too. This in turn has the side-effect that NVPR can not stroke the paths, // as the stroke in NVPR is defined in object-space. // NOTE: here we have following coincidence that works at the moment: // - When using the device-space glyphs, the transforms we pass to NVPR // instanced drawing are the global transforms, and the view transform is // identity. NVPR can not use non-affine transforms in the instanced // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it // will turn off the use of device-space glyphs when perspective transforms // are in use. this->init(rt, clip, paint, skPaint, byteLength, kMaxAccuracy_RenderMode, viewMatrix, regionClipBounds); // Transform our starting point. if (fUsingDeviceSpaceGlyphs) { SkPoint loc; fContextInitialMatrix.mapXY(x, y, &loc); x = loc.fX; y = loc.fY; } SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); const char* stop = text + byteLength; // Measure first if needed. if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) { SkFixed stopX = 0; SkFixed stopY = 0; const char* textPtr = text; while (textPtr < stop) { // We don't need x, y here, since all subpixel variants will have the // same advance. const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0); stopX += glyph.fAdvanceX; stopY += glyph.fAdvanceY; } SkASSERT(textPtr == stop); SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio; SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio; if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) { alignX = SkScalarHalf(alignX); alignY = SkScalarHalf(alignY); } x -= alignX; y -= alignY; } SkAutoKern autokern; SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio); SkFixed fx = SkScalarToFixed(x); SkFixed fy = SkScalarToFixed(y); while (text < stop) { const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0); fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio); if (glyph.fWidth) { this->appendGlyph(drawContext, glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy))); } fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio); fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio); } this->finish(drawContext); }
static sk_sp<GrTextureProxy> create_profile_texture(GrProxyProvider* proxyProvider, const SkRect& circle, float sigma, float* solidRadius, float* textureRadius) { float circleR = circle.width() / 2.0f; if (circleR < SK_ScalarNearlyZero) { return nullptr; } // Profile textures are cached by the ratio of sigma to circle radius and by the size of the // profile texture (binned by powers of 2). SkScalar sigmaToCircleRRatio = sigma / circleR; // When sigma is really small this becomes a equivalent to convolving a Gaussian with a // half-plane. Similarly, in the extreme high ratio cases circle becomes a point WRT to the // Guassian and the profile texture is a just a Gaussian evaluation. However, we haven't yet // implemented this latter optimization. sigmaToCircleRRatio = SkTMin(sigmaToCircleRRatio, 8.f); SkFixed sigmaToCircleRRatioFixed; static const SkScalar kHalfPlaneThreshold = 0.1f; bool useHalfPlaneApprox = false; if (sigmaToCircleRRatio <= kHalfPlaneThreshold) { useHalfPlaneApprox = true; sigmaToCircleRRatioFixed = 0; *solidRadius = circleR - 3 * sigma; *textureRadius = 6 * sigma; } else { // Convert to fixed point for the key. sigmaToCircleRRatioFixed = SkScalarToFixed(sigmaToCircleRRatio); // We shave off some bits to reduce the number of unique entries. We could probably // shave off more than we do. sigmaToCircleRRatioFixed &= ~0xff; sigmaToCircleRRatio = SkFixedToScalar(sigmaToCircleRRatioFixed); sigma = circleR * sigmaToCircleRRatio; *solidRadius = 0; *textureRadius = circleR + 3 * sigma; } static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); GrUniqueKey key; GrUniqueKey::Builder builder(&key, kDomain, 1); builder[0] = sigmaToCircleRRatioFixed; builder.finish(); sk_sp<GrTextureProxy> blurProfile = proxyProvider->findOrCreateProxyByUniqueKey(key, kTopLeft_GrSurfaceOrigin); if (!blurProfile) { static constexpr int kProfileTextureWidth = 512; GrSurfaceDesc texDesc; texDesc.fOrigin = kTopLeft_GrSurfaceOrigin; texDesc.fWidth = kProfileTextureWidth; texDesc.fHeight = 1; texDesc.fConfig = kAlpha_8_GrPixelConfig; std::unique_ptr<uint8_t[]> profile(nullptr); if (useHalfPlaneApprox) { profile.reset(create_half_plane_profile(kProfileTextureWidth)); } else { // Rescale params to the size of the texture we're creating. SkScalar scale = kProfileTextureWidth / *textureRadius; profile.reset( create_circle_profile(sigma * scale, circleR * scale, kProfileTextureWidth)); } blurProfile = proxyProvider->createTextureProxy(texDesc, SkBudgeted::kYes, profile.get(), 0); if (!blurProfile) { return nullptr; } SkASSERT(blurProfile->origin() == kTopLeft_GrSurfaceOrigin); proxyProvider->assignUniqueKeyToProxy(key, blurProfile.get()); } return blurProfile; }
fUnitInvMatrix = *m; m = &fUnitInvMatrix; } SkScalar scale = SkFixedToScalar(SK_Fixed1 >> shift); fUnitInvMatrix.postScale(scale, scale); // now point here instead of fOrigBitmap fBitmap = &fMipBitmap; } } fInvMatrix = m; fInvProc = m->getMapXYProc(); fInvType = m->getType(); fInvSx = SkScalarToFixed(m->getScaleX()); fInvKy = SkScalarToFixed(m->getSkewY()); fAlphaScale = SkAlpha255To256(paint.getAlpha()); // pick-up filtering from the paint, but only if the matrix is // more complex than identity/translate (i.e. no need to pay the cost // of filtering if we're not scaled etc.). // note: we explicitly check inv, since m might be scaled due to unitinv // trickery, but we don't want to see that for this test fDoFilter = paint.isFilterBitmap() && (inv.getType() > SkMatrix::kTranslate_Mask && valid_for_filtering(fBitmap->width() | fBitmap->height())); fShaderProc32 = NULL; fShaderProc16 = NULL;
void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength, SkScalar x, SkScalar y) { SkASSERT(byteLength == 0 || text != nullptr); SkGlyphCache* glyphCache = this->getGlyphCache(); SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc(); fTotalGlyphCount = fFont.countText(text, byteLength); fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType, fTotalGlyphCount)); const char* stop = text + byteLength; // Measure first if needed. if (fFont.getTextAlign() != SkPaint::kLeft_Align) { SkFixed stopX = 0; SkFixed stopY = 0; const char* textPtr = text; while (textPtr < stop) { // We don't need x, y here, since all subpixel variants will have the // same advance. const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr, 0, 0); stopX += glyph.fAdvanceX; stopY += glyph.fAdvanceY; } SkASSERT(textPtr == stop); SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio; SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio; if (fFont.getTextAlign() == SkPaint::kCenter_Align) { alignX = SkScalarHalf(alignX); alignY = SkScalarHalf(alignY); } x -= alignX; y -= alignY; } SkAutoKern autokern; SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio); SkFixed fx = SkScalarToFixed(x); SkFixed fy = SkScalarToFixed(y); FallbackBlobBuilder fallback; while (text < stop) { const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0); fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio); if (glyph.fWidth) { this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)), &fallback); } fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio); fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio); } fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount)); }
bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { SkASSERT(fOrigBitmap.width() && fOrigBitmap.height()); fBitmap = NULL; fInvMatrix = inv; fFilterLevel = paint.getFilterLevel(); // possiblyScaleImage will look to see if it can rescale the image as a // preprocess; either by scaling up to the target size, or by selecting // a nearby mipmap level. If it does, it will adjust the working // matrix as well as the working bitmap. It may also adjust the filter // quality to avoid re-filtering an already perfectly scaled image. if (!this->possiblyScaleImage()) { if (!this->lockBaseBitmap()) { return false; } } // The above logic should have always assigned fBitmap, but in case it // didn't, we check for that now... // TODO(dominikg): Ask humper@ if we can just use an SkASSERT(fBitmap)? if (NULL == fBitmap) { return false; } // If we are "still" kMedium_FilterLevel, then the request was not fulfilled by possiblyScale, // so we downgrade to kLow (so the rest of the sniffing code can assume that) if (SkPaint::kMedium_FilterLevel == fFilterLevel) { fFilterLevel = SkPaint::kLow_FilterLevel; } bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; bool clampClamp = SkShader::kClamp_TileMode == fTileModeX && SkShader::kClamp_TileMode == fTileModeY; if (!(fAdjustedMatrix || clampClamp || trivialMatrix)) { fInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height()); } // Now that all possible changes to the matrix have taken place, check // to see if we're really close to a no-scale matrix. If so, explicitly // set it to be so. Subsequent code may inspect this matrix to choose // a faster path in this case. // This code will only execute if the matrix has some scale component; // if it's already pure translate then we won't do this inversion. if (matrix_only_scale_translate(fInvMatrix)) { SkMatrix forward; if (fInvMatrix.invert(&forward)) { if (clampClamp ? just_trans_clamp(forward, *fBitmap) : just_trans_general(forward)) { SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX()); SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY()); fInvMatrix.setTranslate(tx, ty); } } } fInvProc = fInvMatrix.getMapXYProc(); fInvType = fInvMatrix.getType(); fInvSx = SkScalarToFixed(fInvMatrix.getScaleX()); fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX()); fInvKy = SkScalarToFixed(fInvMatrix.getSkewY()); fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY()); fAlphaScale = SkAlpha255To256(paint.getAlpha()); fShaderProc32 = NULL; fShaderProc16 = NULL; fSampleProc32 = NULL; fSampleProc16 = NULL; // recompute the triviality of the matrix here because we may have // changed it! trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; if (SkPaint::kHigh_FilterLevel == fFilterLevel) { // If this is still set, that means we wanted HQ sampling // but couldn't do it as a preprocess. Let's try to install // the scanline version of the HQ sampler. If that process fails, // downgrade to bilerp. // NOTE: Might need to be careful here in the future when we want // to have the platform proc have a shot at this; it's possible that // the chooseBitmapFilterProc will fail to install a shader but a // platform-specific one might succeed, so it might be premature here // to fall back to bilerp. This needs thought. if (!this->setBitmapFilterProcs()) { fFilterLevel = SkPaint::kLow_FilterLevel; } } if (SkPaint::kLow_FilterLevel == fFilterLevel) { // Only try bilerp if the matrix is "interesting" and // the image has a suitable size. if (fInvType <= SkMatrix::kTranslate_Mask || !valid_for_filtering(fBitmap->width() | fBitmap->height())) { fFilterLevel = SkPaint::kNone_FilterLevel; } } // At this point, we know exactly what kind of sampling the per-scanline // shader will perform. fMatrixProc = this->chooseMatrixProc(trivialMatrix); // TODO(dominikg): SkASSERT(fMatrixProc) instead? chooseMatrixProc never returns NULL. if (NULL == fMatrixProc) { return false; } /////////////////////////////////////////////////////////////////////// const SkAlphaType at = fBitmap->alphaType(); // No need to do this if we're doing HQ sampling; if filter quality is // still set to HQ by the time we get here, then we must have installed // the shader procs above and can skip all this. if (fFilterLevel < SkPaint::kHigh_FilterLevel) { int index = 0; if (fAlphaScale < 256) { // note: this distinction is not used for D16 index |= 1; } if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) { index |= 2; } if (fFilterLevel > SkPaint::kNone_FilterLevel) { index |= 4; } // bits 3,4,5 encoding the source bitmap format switch (fBitmap->colorType()) { case kN32_SkColorType: if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) { return false; } index |= 0; break; case kRGB_565_SkColorType: index |= 8; break; case kIndex_8_SkColorType: if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) { return false; } index |= 16; break; case kARGB_4444_SkColorType: if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) { return false; } index |= 24; break; case kAlpha_8_SkColorType: index |= 32; fPaintPMColor = SkPreMultiplyColor(paint.getColor()); break; default: // TODO(dominikg): Should we ever get here? SkASSERT(false) instead? return false; } #if !SK_ARM_NEON_IS_ALWAYS static const SampleProc32 gSkBitmapProcStateSample32[] = { S32_opaque_D32_nofilter_DXDY, S32_alpha_D32_nofilter_DXDY, S32_opaque_D32_nofilter_DX, S32_alpha_D32_nofilter_DX, S32_opaque_D32_filter_DXDY, S32_alpha_D32_filter_DXDY, S32_opaque_D32_filter_DX, S32_alpha_D32_filter_DX, S16_opaque_D32_nofilter_DXDY, S16_alpha_D32_nofilter_DXDY, S16_opaque_D32_nofilter_DX, S16_alpha_D32_nofilter_DX, S16_opaque_D32_filter_DXDY, S16_alpha_D32_filter_DXDY, S16_opaque_D32_filter_DX, S16_alpha_D32_filter_DX, SI8_opaque_D32_nofilter_DXDY, SI8_alpha_D32_nofilter_DXDY, SI8_opaque_D32_nofilter_DX, SI8_alpha_D32_nofilter_DX, SI8_opaque_D32_filter_DXDY, SI8_alpha_D32_filter_DXDY, SI8_opaque_D32_filter_DX, SI8_alpha_D32_filter_DX, S4444_opaque_D32_nofilter_DXDY, S4444_alpha_D32_nofilter_DXDY, S4444_opaque_D32_nofilter_DX, S4444_alpha_D32_nofilter_DX, S4444_opaque_D32_filter_DXDY, S4444_alpha_D32_filter_DXDY, S4444_opaque_D32_filter_DX, S4444_alpha_D32_filter_DX, // A8 treats alpha/opaque the same (equally efficient) SA8_alpha_D32_nofilter_DXDY, SA8_alpha_D32_nofilter_DXDY, SA8_alpha_D32_nofilter_DX, SA8_alpha_D32_nofilter_DX, SA8_alpha_D32_filter_DXDY, SA8_alpha_D32_filter_DXDY, SA8_alpha_D32_filter_DX, SA8_alpha_D32_filter_DX }; static const SampleProc16 gSkBitmapProcStateSample16[] = { S32_D16_nofilter_DXDY, S32_D16_nofilter_DX, S32_D16_filter_DXDY, S32_D16_filter_DX, S16_D16_nofilter_DXDY, S16_D16_nofilter_DX, S16_D16_filter_DXDY, S16_D16_filter_DX, SI8_D16_nofilter_DXDY, SI8_D16_nofilter_DX, SI8_D16_filter_DXDY, SI8_D16_filter_DX, // Don't support 4444 -> 565 NULL, NULL, NULL, NULL, // Don't support A8 -> 565 NULL, NULL, NULL, NULL }; #endif fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index]; index >>= 1; // shift away any opaque/alpha distinction fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index]; // our special-case shaderprocs if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) { if (clampClamp) { fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc); } else if (SkShader::kRepeat_TileMode == fTileModeX && SkShader::kRepeat_TileMode == fTileModeY) { fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc); } } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clampClamp) { fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc); } if (NULL == fShaderProc32) { fShaderProc32 = this->chooseShaderProc32(); } }
SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc) : SkScalerContext(desc) { SkAutoMutexAcquire ac(gFTMutex); if (gFTCount == 0) { if (!InitFreetype()) { sk_throw(); } } ++gFTCount; // load the font file fFTSize = NULL; fFace = NULL; fFaceRec = ref_ft_face(fRec.fFontID); if (NULL == fFaceRec) { return; } fFace = fFaceRec->fFace; // compute our factors from the record SkMatrix m; fRec.getSingleMatrix(&m); #ifdef DUMP_STRIKE_CREATION SkString keyString; SkFontHost::GetDescriptorKeyString(desc, &keyString); printf("========== strike [%g %g %g] [%g %g %g %g] hints %d format %d %s\n", SkScalarToFloat(fRec.fTextSize), SkScalarToFloat(fRec.fPreScaleX), SkScalarToFloat(fRec.fPreSkewX), SkScalarToFloat(fRec.fPost2x2[0][0]), SkScalarToFloat(fRec.fPost2x2[0][1]), SkScalarToFloat(fRec.fPost2x2[1][0]), SkScalarToFloat(fRec.fPost2x2[1][1]), fRec.getHinting(), fRec.fMaskFormat, keyString.c_str()); #endif // now compute our scale factors SkScalar sx = m.getScaleX(); SkScalar sy = m.getScaleY(); if (m.getSkewX() || m.getSkewY() || sx < 0 || sy < 0) { // sort of give up on hinting sx = SkMaxScalar(SkScalarAbs(sx), SkScalarAbs(m.getSkewX())); sy = SkMaxScalar(SkScalarAbs(m.getSkewY()), SkScalarAbs(sy)); sx = sy = SkScalarAve(sx, sy); SkScalar inv = SkScalarInvert(sx); // flip the skew elements to go from our Y-down system to FreeType's fMatrix22.xx = SkScalarToFixed(SkScalarMul(m.getScaleX(), inv)); fMatrix22.xy = -SkScalarToFixed(SkScalarMul(m.getSkewX(), inv)); fMatrix22.yx = -SkScalarToFixed(SkScalarMul(m.getSkewY(), inv)); fMatrix22.yy = SkScalarToFixed(SkScalarMul(m.getScaleY(), inv)); } else { fMatrix22.xx = fMatrix22.yy = SK_Fixed1; fMatrix22.xy = fMatrix22.yx = 0; } fScaleX = SkScalarToFixed(sx); fScaleY = SkScalarToFixed(sy); // compute the flags we send to Load_Glyph { FT_Int32 loadFlags = FT_LOAD_DEFAULT; if (SkMask::kBW_Format == fRec.fMaskFormat) { // See http://code.google.com/p/chromium/issues/detail?id=43252#c24 loadFlags = FT_LOAD_TARGET_MONO; if (fRec.getHinting() == SkPaint::kNo_Hinting) loadFlags = FT_LOAD_NO_HINTING; } else { switch (fRec.getHinting()) { case SkPaint::kNo_Hinting: loadFlags = FT_LOAD_NO_HINTING; break; case SkPaint::kSlight_Hinting: loadFlags = FT_LOAD_TARGET_LIGHT; // This implies FORCE_AUTOHINT break; case SkPaint::kNormal_Hinting: loadFlags = FT_LOAD_TARGET_NORMAL; break; case SkPaint::kFull_Hinting: loadFlags = FT_LOAD_TARGET_NORMAL; if (SkMask::kHorizontalLCD_Format == fRec.fMaskFormat) loadFlags = FT_LOAD_TARGET_LCD; else if (SkMask::kVerticalLCD_Format == fRec.fMaskFormat) loadFlags = FT_LOAD_TARGET_LCD_V; break; default: SkDebugf("---------- UNKNOWN hinting %d\n", fRec.getHinting()); break; } } if ((fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag) == 0) loadFlags |= FT_LOAD_NO_BITMAP; fLoadGlyphFlags = loadFlags; } // now create the FT_Size { FT_Error err; err = FT_New_Size(fFace, &fFTSize); if (err != 0) { SkDEBUGF(("SkScalerContext_FreeType::FT_New_Size(%x): FT_Set_Char_Size(0x%x, 0x%x) returned 0x%x\n", fFaceRec->fFontID, fScaleX, fScaleY, err)); fFace = NULL; return; } err = FT_Activate_Size(fFTSize); if (err != 0) { SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n", fFaceRec->fFontID, fScaleX, fScaleY, err)); fFTSize = NULL; } err = FT_Set_Char_Size( fFace, SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY), 72, 72); if (err != 0) { SkDEBUGF(("SkScalerContext_FreeType::FT_Set_Char_Size(%x, 0x%x, 0x%x) returned 0x%x\n", fFaceRec->fFontID, fScaleX, fScaleY, err)); fFace = NULL; return; } FT_Set_Transform( fFace, &fMatrix22, NULL); } }