void SkSVGDevice::AutoElement::addTextAttributes(const SkPaint& paint) { this->addAttribute("font-size", paint.getTextSize()); if (const char* textAlign = svg_text_align(paint.getTextAlign())) { this->addAttribute("text-anchor", textAlign); } SkString familyName; SkTHashSet<SkString> familySet; SkAutoTUnref<const SkTypeface> tface(paint.getTypeface() ? SkRef(paint.getTypeface()) : SkTypeface::RefDefault()); SkASSERT(tface); SkTypeface::Style style = tface->style(); if (style & SkTypeface::kItalic) { this->addAttribute("font-style", "italic"); } if (style & SkTypeface::kBold) { this->addAttribute("font-weight", "bold"); } SkAutoTUnref<SkTypeface::LocalizedStrings> familyNameIter(tface->createFamilyNameIterator()); SkTypeface::LocalizedString familyString; while (familyNameIter->next(&familyString)) { if (familySet.contains(familyString.fString)) { continue; } familySet.add(familyString.fString); familyName.appendf((familyName.isEmpty() ? "%s" : ", %s"), familyString.fString.c_str()); } if (!familyName.isEmpty()) { this->addAttribute("font-family", familyName); } }
static void drawKernText(SkCanvas* canvas, const void* text, size_t len, SkScalar x, SkScalar y, const SkPaint& paint) { SkTypeface* face = paint.getTypeface(); if (!face) { canvas->drawText(text, len, x, y, paint); return; } SkAutoSTMalloc<128, uint16_t> glyphStorage(len); uint16_t* glyphs = glyphStorage.get(); int glyphCount = paint.textToGlyphs(text, len, glyphs); if (glyphCount < 1) { return; } SkAutoSTMalloc<128, int32_t> adjustmentStorage(glyphCount - 1); int32_t* adjustments = adjustmentStorage.get(); if (!face->getKerningPairAdjustments(glyphs, glyphCount, adjustments)) { canvas->drawText(text, len, x, y, paint); return; } SkPaint glyphPaint(paint); glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); SkAutoSTMalloc<128, SkPoint> posStorage(glyphCount); SkPoint* pos = posStorage.get(); getGlyphPositions(glyphPaint, glyphs, glyphCount, x, y, pos); applyKerning(pos, adjustments, glyphCount, glyphPaint); canvas->drawPosText(glyphs, glyphCount * sizeof(uint16_t), pos, glyphPaint); }
sk_sp<SkFont> SkFont::Testing_CreateFromPaint(const SkPaint& paint) { uint32_t flags = 0; if (paint.isVerticalText()) { flags |= kVertical_Flag; } if (paint.isEmbeddedBitmapText()) { flags |= kEmbeddedBitmaps_Flag; } if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) { flags |= kGenA8FromLCD_Flag; } if (paint.isFakeBoldText()) { flags |= kEmbolden_Flag; } if (SkPaint::kFull_Hinting == paint.getHinting()) { flags |= kEnableByteCodeHints_Flag; } if (paint.isAutohinted()) { flags |= kEnableAutoHints_Flag; } if (paint.isSubpixelText() || paint.isLinearText()) { // this is our default } else { flags |= kUseNonlinearMetrics_Flag; } MaskType maskType = SkFont::kBW_MaskType; if (paint.isAntiAlias()) { maskType = paint.isLCDRenderText() ? kLCD_MaskType : kA8_MaskType; } return Make(sk_ref_sp(paint.getTypeface()), paint.getTextSize(), paint.getTextScaleX(), paint.getTextSkewX(), maskType, flags); }
static void test_cachedfont(skiatest::Reporter* reporter, const SkPaint& paint) { SkAutoTUnref<SkFont> font(SkFont::Testing_CreateFromPaint(paint)); // Currently SkFont resolves null into the default, so only test if paint's is not null if (paint.getTypeface()) { REPORTER_ASSERT(reporter, font->getTypeface() == paint.getTypeface()); } REPORTER_ASSERT(reporter, font->getSize() == paint.getTextSize()); REPORTER_ASSERT(reporter, font->getScaleX() == paint.getTextScaleX()); REPORTER_ASSERT(reporter, font->getSkewX() == paint.getTextSkewX()); REPORTER_ASSERT(reporter, font->isVertical() == paint.isVerticalText()); REPORTER_ASSERT(reporter, font->isEmbolden() == paint.isFakeBoldText()); REPORTER_ASSERT(reporter, font->isUseNonLinearMetrics() == is_use_nonlinear_metrics(paint)); REPORTER_ASSERT(reporter, font->isEnableAutoHints() == is_enable_auto_hints(paint)); REPORTER_ASSERT(reporter, font->isEnableByteCodeHints() == is_enable_bytecode_hints(paint)); }
static void charsToGlyphsNull_proc(int loops, const SkPaint& paint, const void* text, size_t len, int glyphCount) { SkTypeface::Encoding encoding = paint2Encoding(paint); SkTypeface* face = paint.getTypeface(); for (int i = 0; i < loops; ++i) { face->charsToGlyphs(text, encoding, NULL, glyphCount); } }
static void applyKerning(SkPoint pos[], const int32_t adjustments[], int count, const SkPaint& paint) { SkScalar scale = paint.getTextSize() / paint.getTypeface()->getUnitsPerEm(); SkScalar globalAdj = 0; for (int i = 0; i < count - 1; ++i) { globalAdj += adjustments[i] * scale; pos[i + 1].fX += globalAdj; } }
DEF_TEST(Paint_cmap, reporter) { // need to implement charsToGlyphs on other backends (e.g. linux, win) // before we can run this tests everywhere return; static const int NGLYPHS = 64; SkUnichar src[NGLYPHS]; SkUnichar dst[NGLYPHS]; // used for utf8, utf16, utf32 storage static const struct { size_t (*fSeedTextProc)(const SkUnichar[], void* dst, int count); SkPaint::TextEncoding fEncoding; } gRec[] = { { uni_to_utf8, SkPaint::kUTF8_TextEncoding }, { uni_to_utf16, SkPaint::kUTF16_TextEncoding }, { uni_to_utf32, SkPaint::kUTF32_TextEncoding }, }; SkRandom rand; SkPaint paint; paint.setTypeface(SkTypeface::RefDefault())->unref(); SkTypeface* face = paint.getTypeface(); for (int i = 0; i < 1000; ++i) { // generate some random text for (int j = 0; j < NGLYPHS; ++j) { src[j] = ' ' + j; } // inject some random chars, to sometimes abort early src[rand.nextU() & 63] = rand.nextU() & 0xFFF; for (size_t k = 0; k < SK_ARRAY_COUNT(gRec); ++k) { paint.setTextEncoding(gRec[k].fEncoding); size_t len = gRec[k].fSeedTextProc(src, dst, NGLYPHS); uint16_t glyphs0[NGLYPHS], glyphs1[NGLYPHS]; bool contains = paint.containsText(dst, len); int nglyphs = paint.textToGlyphs(dst, len, glyphs0); int first = face->charsToGlyphs(dst, paint2encoding(paint), glyphs1, NGLYPHS); int index = find_first_zero(glyphs1, NGLYPHS); REPORTER_ASSERT(reporter, NGLYPHS == nglyphs); REPORTER_ASSERT(reporter, index == first); REPORTER_ASSERT(reporter, 0 == memcmp(glyphs0, glyphs1, NGLYPHS * sizeof(uint16_t))); if (contains) { REPORTER_ASSERT(reporter, NGLYPHS == first); } else { REPORTER_ASSERT(reporter, NGLYPHS > first); } } } }
static void charsToGlyphs_proc(int loops, const SkPaint& paint, const void* text, size_t len, int glyphCount) { SkTypeface::Encoding encoding = paint2Encoding(paint); uint16_t glyphs[NGLYPHS]; SkASSERT(glyphCount <= NGLYPHS); SkTypeface* face = paint.getTypeface(); for (int i = 0; i < loops; ++i) { face->charsToGlyphs(text, encoding, glyphs, glyphCount); } }
DEF_TEST(FontHostStream, reporter) { { SkPaint paint; paint.setColor(SK_ColorGRAY); paint.setTextSize(SkIntToScalar(30)); SkTypeface* fTypeface = SkTypeface::CreateFromName("Georgia", SkTypeface::kNormal); SkSafeUnref(paint.setTypeface(fTypeface)); SkIRect origRect = SkIRect::MakeWH(64, 64); SkBitmap origBitmap; create(&origBitmap, origRect); SkCanvas origCanvas(origBitmap); SkIRect streamRect = SkIRect::MakeWH(64, 64); SkBitmap streamBitmap; create(&streamBitmap, streamRect); SkCanvas streamCanvas(streamBitmap); SkPoint point = SkPoint::Make(24, 32); // Test: origTypeface and streamTypeface from orig data draw the same drawBG(&origCanvas); origCanvas.drawText("A", 1, point.fX, point.fY, paint); SkTypeface* origTypeface = paint.getTypeface(); SkAutoTUnref<SkTypeface> aur; if (NULL == origTypeface) { origTypeface = aur.reset(SkTypeface::RefDefault()); } int ttcIndex; SkAutoTDelete<SkStreamAsset> fontData(origTypeface->openStream(&ttcIndex)); SkTypeface* streamTypeface = SkTypeface::CreateFromStream(fontData.detach()); SkFontDescriptor desc; bool isLocalStream = false; streamTypeface->getFontDescriptor(&desc, &isLocalStream); REPORTER_ASSERT(reporter, isLocalStream); SkSafeUnref(paint.setTypeface(streamTypeface)); drawBG(&streamCanvas); streamCanvas.drawPosText("A", 1, &point, paint); REPORTER_ASSERT(reporter, compare(origBitmap, origRect, streamBitmap, streamRect)); } //Make sure the typeface is deleted and removed. SkGraphics::PurgeFontCache(); }
static uint16_t compute_nondef(const SkPaint& paint, PaintUsage usage) { // kRespectsStroke_PaintUsage is only valid if other bits are also set SkASSERT(0 != (usage & ~kRespectsStroke_PaintUsage)); const SkScalar kTextSize_Default = 12; const SkScalar kTextScaleX_Default = 1; const SkScalar kTextSkewX_Default = 0; const SkScalar kStrokeWidth_Default = 0; const SkScalar kStrokeMiter_Default = 4; const SkColor kColor_Default = SK_ColorBLACK; unsigned bits = (paint.getColor() != kColor_Default) ? kColor_NonDef : 0; if (usage & kText_PaintUsage) { bits |= (paint.getTextSize() != kTextSize_Default ? kTextSize_NonDef : 0); bits |= (paint.getTextScaleX() != kTextScaleX_Default ? kTextScaleX_NonDef : 0); bits |= (paint.getTextSkewX() != kTextSkewX_Default ? kTextSkewX_NonDef : 0); bits |= (paint.getTypeface() ? kTypeface_NonDef : 0); } // TODO: kImage_PaintUsage only needs the shader/maskfilter IF its colortype is kAlpha_8 if (usage & (kVertices_PaintUsage | kDrawPaint_PaintUsage | kImage_PaintUsage | kText_PaintUsage | kGeometry_PaintUsage | kTextBlob_PaintUsage)) { bits |= (paint.getShader() ? kShader_NonDef : 0); } if (usage & (kText_PaintUsage | kGeometry_PaintUsage | kTextBlob_PaintUsage)) { bits |= (paint.getPathEffect() ? kPathEffect_NonDef : 0); bits |= (paint.getRasterizer() ? kRasterizer_NonDef : 0); if (paint.getStyle() != SkPaint::kFill_Style || (usage & kRespectsStroke_PaintUsage)) { bits |= (paint.getStrokeWidth() != kStrokeWidth_Default ? kStrokeWidth_NonDef : 0); bits |= (paint.getStrokeMiter() != kStrokeMiter_Default ? kStrokeMiter_NonDef : 0); } } if (usage & (kText_PaintUsage | kGeometry_PaintUsage | kImage_PaintUsage | kTextBlob_PaintUsage)) { bits |= (paint.getMaskFilter() ? kMaskFilter_NonDef : 0); } bits |= (paint.getColorFilter() ? kColorFilter_NonDef : 0); bits |= (paint.getImageFilter() ? kImageFilter_NonDef : 0); bits |= (paint.getDrawLooper() ? kDrawLooper_NonDef : 0); return SkToU16(bits); }
void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) { SkTypeface* typeface = paint.getTypeface(); if (fGraphicStack[fGraphicStackIndex].fTextSize != paint.getTextSize() || fGraphicStack[fGraphicStackIndex].fFont == NULL || fGraphicStack[fGraphicStackIndex].fFont->typeface() != typeface || !fGraphicStack[fGraphicStackIndex].fFont->hasGlyph(glyphID)) { int fontIndex = getFontResourceIndex(typeface, glyphID); fContent.writeText("/F"); fContent.writeDecAsText(fontIndex); fContent.writeText(" "); SkPDFScalar::Append(paint.getTextSize(), &fContent); fContent.writeText(" Tf\n"); fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize(); fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex]; } }
static void apply_paint_typeface(const SkPaint& paint, Json::Value* target, bool sendBinaries) { SkTypeface* typeface = paint.getTypeface(); if (typeface != nullptr) { if (sendBinaries) { Json::Value jsonTypeface; SkDynamicMemoryWStream buffer; typeface->serialize(&buffer); void* data = sk_malloc_throw(buffer.bytesWritten()); buffer.copyTo(data); Json::Value bytes; encode_data(data, buffer.bytesWritten(), &bytes); jsonTypeface[SKJSONCANVAS_ATTRIBUTE_BYTES] = bytes; free(data); (*target)[SKJSONCANVAS_ATTRIBUTE_TYPEFACE] = jsonTypeface; } } }
GlyphSet::GlyphSet(const SkPaint& paint, const UChar* lower, const UChar* upper, size_t byteLength) { SkPaint clonePaint(paint); clonePaint.setTextEncoding(SkPaint::kUTF16_TextEncoding); mTypeface = paint.getTypeface(); mCount = clonePaint.textToGlyphs(lower, byteLength, NULL); if (mCount > MAX_STORAGE_COUNT) { mLowerGlyphs = new uint16_t[2*mCount]; } else { mLowerGlyphs = &mStorage[0]; } // Use one array, and have mUpperGlyphs point to a portion of it, // so that we can reduce the number of new/deletes mUpperGlyphs = mLowerGlyphs + mCount; int count2 = clonePaint.textToGlyphs(lower, byteLength, mLowerGlyphs); SkASSERT(mCount == count2); count2 = clonePaint.textToGlyphs(upper, byteLength, mUpperGlyphs); SkASSERT(mCount == count2); }
static void cgSetPaintForText(CGContextRef cg, const SkPaint& paint) { SkColor c = paint.getColor(); CGFloat rgba[] = { SkColorGetB(c) / 255.0f, SkColorGetG(c) / 255.0f, SkColorGetR(c) / 255.0f, SkColorGetA(c) / 255.0f, }; CGContextSetRGBFillColor(cg, rgba[0], rgba[1], rgba[2], rgba[3]); CGContextSetTextDrawingMode(cg, kCGTextFill); CGContextSetFont(cg, typefaceToCGFont(paint.getTypeface())); CGContextSetFontSize(cg, SkScalarToFloat(paint.getTextSize())); CGContextSetAllowsFontSubpixelPositioning(cg, paint.isSubpixelText()); CGContextSetShouldSubpixelPositionFonts(cg, paint.isSubpixelText()); CGContextSetShouldAntialias(cg, paint.isAntiAlias()); CGContextSetShouldSmoothFonts(cg, paint.isLCDRenderText()); }
void onDraw(SkCanvas* canvas) override { SkPaint paint; paint.setAntiAlias(true); paint.setSubpixelText(true); paint.setTextSize(100); paint.setStyle(SkPaint::kStroke_Style); paint.setTextScaleX(fScaleX); paint.setTextSkewX(fSkewX); const SkColor boundsColors[2] = { SK_ColorRED, SK_ColorBLUE }; SkFontMgr* fm = fFM; int count = SkMin32(fm->countFamilies(), 32); int index = 0; SkScalar x = 0, y = 0; canvas->translate(80, 120); for (int i = 0; i < count; ++i) { SkAutoTUnref<SkFontStyleSet> set(fm->createStyleSet(i)); for (int j = 0; j < set->count(); ++j) { SkSafeUnref(paint.setTypeface(set->createTypeface(j))); if (paint.getTypeface()) { show_bounds(canvas, paint, x, y, boundsColors[index & 1]); index += 1; x += 160; if (0 == (index % 6)) { x = 0; y += 160; } if (index >= 30) { return; } } } } }
/* * Header: * paint flags : 32 * non_def bits : 16 * xfermode enum : 8 * pad zeros : 8 */ static void write_paint(SkWriteBuffer& writer, const SkPaint& paint, unsigned usage) { uint32_t packedFlags = pack_paint_flags(paint.getFlags(), paint.getHinting(), paint.getTextAlign(), paint.getFilterQuality(), paint.getStyle(), paint.getStrokeCap(), paint.getStrokeJoin(), paint.getTextEncoding()); writer.write32(packedFlags); unsigned nondef = compute_nondef(paint, (PaintUsage)usage); const uint8_t pad = 0; writer.write32((nondef << 16) | ((unsigned)paint.getBlendMode() << 8) | pad); CHECK_WRITE_SCALAR(writer, nondef, paint, TextSize); CHECK_WRITE_SCALAR(writer, nondef, paint, TextScaleX); CHECK_WRITE_SCALAR(writer, nondef, paint, TextSkewX); CHECK_WRITE_SCALAR(writer, nondef, paint, StrokeWidth); CHECK_WRITE_SCALAR(writer, nondef, paint, StrokeMiter); if (nondef & kColor_NonDef) { writer.write32(paint.getColor()); } if (nondef & kTypeface_NonDef) { // TODO: explore idea of writing bits indicating "use the prev (or prev N) face" // e.g. 1-N bits is an index into a ring buffer of typefaces SkTypeface* tf = paint.getTypeface(); SkASSERT(tf); writer.writeTypeface(tf); } CHECK_WRITE_FLATTENABLE(writer, nondef, paint, PathEffect); CHECK_WRITE_FLATTENABLE(writer, nondef, paint, Shader); CHECK_WRITE_FLATTENABLE(writer, nondef, paint, MaskFilter); CHECK_WRITE_FLATTENABLE(writer, nondef, paint, ColorFilter); CHECK_WRITE_FLATTENABLE(writer, nondef, paint, Rasterizer); CHECK_WRITE_FLATTENABLE(writer, nondef, paint, ImageFilter); CHECK_WRITE_FLATTENABLE(writer, nondef, paint, DrawLooper); }
void SimpleFontData::platformInit() { if (!m_platformData.size()) { m_fontMetrics.reset(); m_avgCharWidth = 0; m_maxCharWidth = 0; return; } SkPaint paint; SkPaint::FontMetrics metrics; m_platformData.setupPaint(&paint); paint.getFontMetrics(&metrics); const SkFontID fontID = m_platformData.uniqueID(); static const uint32_t vdmxTag = SkSetFourByteTag('V', 'D', 'M', 'X'); int pixelSize = m_platformData.size() + 0.5; int vdmxAscent, vdmxDescent; bool isVDMXValid = false; size_t vdmxSize = SkFontHost::GetTableSize(fontID, vdmxTag); if (vdmxSize && vdmxSize < maxVDMXTableSize) { uint8_t* vdmxTable = (uint8_t*) fastMalloc(vdmxSize); if (vdmxTable && SkFontHost::GetTableData(fontID, vdmxTag, 0, vdmxSize, vdmxTable) == vdmxSize && parseVDMX(&vdmxAscent, &vdmxDescent, vdmxTable, vdmxSize, pixelSize)) isVDMXValid = true; fastFree(vdmxTable); } float ascent; float descent; // Beware those who step here: This code is designed to match Win32 font // metrics *exactly* (except the adjustment of ascent/descent on Linux/Android). if (isVDMXValid) { ascent = vdmxAscent; descent = -vdmxDescent; } else { SkScalar height = -metrics.fAscent + metrics.fDescent + metrics.fLeading; ascent = SkScalarRound(-metrics.fAscent); descent = SkScalarRound(height) - ascent; #if OS(LINUX) || OS(ANDROID) // When subpixel positioning is enabled, if the descent is rounded down, the descent part // of the glyph may be truncated when displayed in a 'overflow: hidden' container. // To avoid that, borrow 1 unit from the ascent when possible. // FIXME: This can be removed if sub-pixel ascent/descent is supported. if (platformData().fontRenderStyle().useSubpixelPositioning && descent < SkScalarToFloat(metrics.fDescent) && ascent >= 1) { ++descent; --ascent; } #endif } m_fontMetrics.setAscent(ascent); m_fontMetrics.setDescent(descent); float xHeight; if (metrics.fXHeight) { xHeight = metrics.fXHeight; m_fontMetrics.setXHeight(xHeight); } else { xHeight = ascent * 0.56; // Best guess from Windows font metrics. m_fontMetrics.setXHeight(xHeight); m_fontMetrics.setHasXHeight(false); } float lineGap = SkScalarToFloat(metrics.fLeading); m_fontMetrics.setLineGap(lineGap); m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); if (platformData().orientation() == Vertical && !isTextOrientationFallback()) { static const uint32_t vheaTag = SkSetFourByteTag('v', 'h', 'e', 'a'); static const uint32_t vorgTag = SkSetFourByteTag('V', 'O', 'R', 'G'); size_t vheaSize = SkFontHost::GetTableSize(fontID, vheaTag); size_t vorgSize = SkFontHost::GetTableSize(fontID, vorgTag); if ((vheaSize > 0) || (vorgSize > 0)) m_hasVerticalGlyphs = true; } // In WebKit/WebCore/platform/graphics/SimpleFontData.cpp, m_spaceWidth is // calculated for us, but we need to calculate m_maxCharWidth and // m_avgCharWidth in order for text entry widgets to be sized correctly. SkScalar xRange = metrics.fXMax - metrics.fXMin; m_maxCharWidth = SkScalarRound(xRange * SkScalarRound(m_platformData.size())); if (metrics.fAvgCharWidth) m_avgCharWidth = SkScalarRound(metrics.fAvgCharWidth); else { m_avgCharWidth = xHeight; GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); if (glyphPageZero) { static const UChar32 xChar = 'x'; const Glyph xGlyph = glyphPageZero->glyphDataForCharacter(xChar).glyph; if (xGlyph) { // In widthForGlyph(), xGlyph will be compared with // m_zeroWidthSpaceGlyph, which isn't initialized yet here. // Initialize it with zero to make sure widthForGlyph() returns // the right width. m_zeroWidthSpaceGlyph = 0; m_avgCharWidth = widthForGlyph(xGlyph); } } } if (int unitsPerEm = paint.getTypeface()->getUnitsPerEm()) m_fontMetrics.setUnitsPerEm(unitsPerEm); }
void SimpleFontData::platformInit() { if (!m_platformData.size()) { m_fontMetrics.reset(); m_avgCharWidth = 0; m_maxCharWidth = 0; return; } SkPaint paint; SkPaint::FontMetrics metrics; m_platformData.setupPaint(&paint); paint.getFontMetrics(&metrics); SkTypeface* face = paint.getTypeface(); ASSERT(face); int vdmxAscent = 0, vdmxDescent = 0; bool isVDMXValid = false; #if OS(LINUX) || OS(ANDROID) // Manually digging up VDMX metrics is only applicable when bytecode hinting // using FreeType. With GDI, the metrics will already have taken this into // account (as needed). With DirectWrite or CoreText, no bytecode hinting is // ever done. This code should be pushed into FreeType (hinted font metrics). static const uint32_t vdmxTag = SkSetFourByteTag('V', 'D', 'M', 'X'); int pixelSize = m_platformData.size() + 0.5; if (!paint.isAutohinted() && (paint.getHinting() == SkPaint::kFull_Hinting || paint.getHinting() == SkPaint::kNormal_Hinting)) { size_t vdmxSize = face->getTableSize(vdmxTag); if (vdmxSize && vdmxSize < maxVDMXTableSize) { uint8_t* vdmxTable = (uint8_t*)fastMalloc(vdmxSize); if (vdmxTable && face->getTableData(vdmxTag, 0, vdmxSize, vdmxTable) == vdmxSize && parseVDMX(&vdmxAscent, &vdmxDescent, vdmxTable, vdmxSize, pixelSize)) isVDMXValid = true; fastFree(vdmxTable); } } #endif float ascent; float descent; // Beware those who step here: This code is designed to match Win32 font // metrics *exactly* (except the adjustment of ascent/descent on // Linux/Android). if (isVDMXValid) { ascent = vdmxAscent; descent = -vdmxDescent; } else { ascent = SkScalarRoundToInt(-metrics.fAscent); descent = SkScalarRoundToInt(metrics.fDescent); #if OS(LINUX) || OS(ANDROID) // When subpixel positioning is enabled, if the descent is rounded down, the // descent part of the glyph may be truncated when displayed in a 'overflow: // hidden' container. To avoid that, borrow 1 unit from the ascent when // possible. // FIXME: This can be removed if sub-pixel ascent/descent is supported. if (platformData().fontRenderStyle().useSubpixelPositioning && descent < SkScalarToFloat(metrics.fDescent) && ascent >= 1) { ++descent; --ascent; } #endif } m_fontMetrics.setAscent(ascent); m_fontMetrics.setDescent(descent); float xHeight; if (metrics.fXHeight) { xHeight = metrics.fXHeight; m_fontMetrics.setXHeight(xHeight); } else { xHeight = ascent * 0.56; // Best guess from Windows font metrics. m_fontMetrics.setXHeight(xHeight); m_fontMetrics.setHasXHeight(false); } float lineGap = SkScalarToFloat(metrics.fLeading); m_fontMetrics.setLineGap(lineGap); m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); SkScalar underlineThickness, underlinePosition; if (metrics.hasUnderlineThickness(&underlineThickness) && metrics.hasUnderlinePosition(&underlinePosition)) { m_fontMetrics.setUnderlineThickness(SkScalarToFloat(underlineThickness)); m_fontMetrics.setUnderlinePosition(SkScalarToFloat(-underlinePosition)); } if (platformData().orientation() == Vertical && !isTextOrientationFallback()) { static const uint32_t vheaTag = SkSetFourByteTag('v', 'h', 'e', 'a'); static const uint32_t vorgTag = SkSetFourByteTag('V', 'O', 'R', 'G'); size_t vheaSize = face->getTableSize(vheaTag); size_t vorgSize = face->getTableSize(vorgTag); if ((vheaSize > 0) || (vorgSize > 0)) m_hasVerticalGlyphs = true; } // In WebKit/WebCore/platform/graphics/SimpleFontData.cpp, m_spaceWidth is // calculated for us, but we need to calculate m_maxCharWidth and // m_avgCharWidth in order for text entry widgets to be sized correctly. // FIXME: This seems incorrect and should probably use fMaxCharWidth as // the code path above. SkScalar xRange = metrics.fXMax - metrics.fXMin; m_maxCharWidth = SkScalarRoundToInt(xRange * SkScalarRoundToInt(m_platformData.size())); if (metrics.fAvgCharWidth) m_avgCharWidth = SkScalarRoundToInt(metrics.fAvgCharWidth); else { m_avgCharWidth = xHeight; GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); if (glyphPageZero) { static const UChar32 xChar = 'x'; const Glyph xGlyph = glyphPageZero->glyphForCharacter(xChar); if (xGlyph) { // In widthForGlyph(), xGlyph will be compared with // m_zeroWidthSpaceGlyph, which isn't initialized yet here. // Initialize it with zero to make sure widthForGlyph() returns // the right width. m_zeroWidthSpaceGlyph = 0; m_avgCharWidth = widthForGlyph(xGlyph); } } } if (int unitsPerEm = face->getUnitsPerEm()) m_fontMetrics.setUnitsPerEm(unitsPerEm); }
// Even with kEntirePaint_Bits, we always ensure that the master paint's // text-encoding is respected, since that controls how we interpret the // text/length parameters of a draw[Pos]Text call. void SkLayerDrawLooper::LayerDrawLooperContext::ApplyInfo( SkPaint* dst, const SkPaint& src, const LayerInfo& info) { SkColor4f srcColor = src.getColor4f(); #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK // The framework may respect the alpha value on the original paint. // Match this legacy behavior. if (src.getAlpha() == 255) { srcColor.fA = dst->getColor4f().fA; } #endif dst->setColor4f(xferColor(srcColor, dst->getColor4f(), (SkBlendMode)info.fColorMode), sk_srgb_singleton()); BitFlags bits = info.fPaintBits; SkPaint::TextEncoding encoding = dst->getTextEncoding(); if (0 == bits) { return; } if (kEntirePaint_Bits == bits) { // we've already computed these, so save it from the assignment uint32_t f = dst->getFlags(); SkColor4f c = dst->getColor4f(); *dst = src; dst->setFlags(f); dst->setColor4f(c, sk_srgb_singleton()); dst->setTextEncoding(encoding); return; } if (bits & kStyle_Bit) { dst->setStyle(src.getStyle()); dst->setStrokeWidth(src.getStrokeWidth()); dst->setStrokeMiter(src.getStrokeMiter()); dst->setStrokeCap(src.getStrokeCap()); dst->setStrokeJoin(src.getStrokeJoin()); } if (bits & kTextSkewX_Bit) { dst->setTextSkewX(src.getTextSkewX()); } if (bits & kPathEffect_Bit) { dst->setPathEffect(src.refPathEffect()); } if (bits & kMaskFilter_Bit) { dst->setMaskFilter(src.refMaskFilter()); } if (bits & kShader_Bit) { dst->setShader(src.refShader()); } if (bits & kColorFilter_Bit) { dst->setColorFilter(src.refColorFilter()); } if (bits & kXfermode_Bit) { dst->setBlendMode(src.getBlendMode()); } // we don't override these #if 0 dst->setTypeface(src.getTypeface()); dst->setTextSize(src.getTextSize()); dst->setTextScaleX(src.getTextScaleX()); dst->setRasterizer(src.getRasterizer()); dst->setLooper(src.getLooper()); dst->setTextEncoding(src.getTextEncoding()); dst->setHinting(src.getHinting()); #endif }
void GrTextUtils::DrawDFText(GrAtlasTextBlob* blob, int runIndex, GrBatchFontCache* fontCache, const SkSurfaceProps& props, const SkPaint& skPaint, GrColor color, const SkMatrix& viewMatrix, const char text[], size_t byteLength, SkScalar x, SkScalar y) { SkASSERT(byteLength == 0 || text != nullptr); // nothing to draw if (text == nullptr || byteLength == 0) { return; } SkPaint::GlyphCacheProc glyphCacheProc = skPaint.getGlyphCacheProc(true); SkAutoDescriptor desc; skPaint.getScalerContextDescriptor(&desc, props, SkPaint::FakeGamma::Off, nullptr); SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(skPaint.getTypeface(), desc.getDesc()); SkTArray<SkScalar> positions; const char* textPtr = text; SkFixed stopX = 0; SkFixed stopY = 0; SkFixed origin = 0; switch (skPaint.getTextAlign()) { case SkPaint::kRight_Align: origin = SK_Fixed1; break; case SkPaint::kCenter_Align: origin = SK_FixedHalf; break; case SkPaint::kLeft_Align: origin = 0; break; } SkAutoKern autokern; const char* stop = text + byteLength; while (textPtr < stop) { // don't need x, y here, since all subpixel variants will have the // same advance const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr); SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph); positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width))); SkFixed height = glyph.fAdvanceY; positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height))); stopX += width; stopY += height; } SkASSERT(textPtr == stop); SkGlyphCache::AttachCache(origPaintCache); // now adjust starting point depending on alignment SkScalar alignX = SkFixedToScalar(stopX); SkScalar alignY = SkFixedToScalar(stopY); if (skPaint.getTextAlign() == SkPaint::kCenter_Align) { alignX = SkScalarHalf(alignX); alignY = SkScalarHalf(alignY); } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) { alignX = 0; alignY = 0; } x -= alignX; y -= alignY; SkPoint offset = SkPoint::Make(x, y); DrawDFPosText(blob, runIndex, fontCache, props, skPaint, color, viewMatrix, text, byteLength, positions.begin(), 2, offset); }
void SimpleFontData::platformInit() { if (!m_platformData.size()) { m_fontMetrics.reset(); m_avgCharWidth = 0; m_maxCharWidth = 0; return; } SkPaint paint; SkPaint::FontMetrics metrics; m_platformData.setupPaint(&paint); paint.getFontMetrics(&metrics); SkTypeface* face = paint.getTypeface(); ASSERT(face); int vdmxAscent = 0, vdmxDescent = 0; bool isVDMXValid = false; #if OS(LINUX) || OS(ANDROID) // Manually digging up VDMX metrics is only applicable when bytecode hinting using FreeType. // With DirectWrite or CoreText, no bytecode hinting is ever done. // This code should be pushed into FreeType (hinted font metrics). static const uint32_t vdmxTag = SkSetFourByteTag('V', 'D', 'M', 'X'); int pixelSize = m_platformData.size() + 0.5; if (!paint.isAutohinted() && (paint.getHinting() == SkPaint::kFull_Hinting || paint.getHinting() == SkPaint::kNormal_Hinting)) { size_t vdmxSize = face->getTableSize(vdmxTag); if (vdmxSize && vdmxSize < maxVDMXTableSize) { uint8_t* vdmxTable = (uint8_t*)WTF::Partitions::fastMalloc(vdmxSize, WTF_HEAP_PROFILER_TYPE_NAME(SimpleFontData)); if (vdmxTable && face->getTableData(vdmxTag, 0, vdmxSize, vdmxTable) == vdmxSize && parseVDMX(&vdmxAscent, &vdmxDescent, vdmxTable, vdmxSize, pixelSize)) isVDMXValid = true; WTF::Partitions::fastFree(vdmxTable); } } #endif float ascent; float descent; // Beware those who step here: This code is designed to match Win32 font // metrics *exactly* (except the adjustment of ascent/descent on Linux/Android). if (isVDMXValid) { ascent = vdmxAscent; descent = -vdmxDescent; } else { ascent = SkScalarRoundToInt(-metrics.fAscent); descent = SkScalarRoundToInt(metrics.fDescent); #if OS(LINUX) || OS(ANDROID) // When subpixel positioning is enabled, if the descent is rounded down, the descent part // of the glyph may be truncated when displayed in a 'overflow: hidden' container. // To avoid that, borrow 1 unit from the ascent when possible. // FIXME: This can be removed if sub-pixel ascent/descent is supported. if (platformData().getFontRenderStyle().useSubpixelPositioning && descent < SkScalarToFloat(metrics.fDescent) && ascent >= 1) { ++descent; --ascent; } #endif } #if OS(MACOSX) // We are preserving this ascent hack to match Safari's ascent adjustment // in their SimpleFontDataMac.mm, for details see crbug.com/445830. // We need to adjust Times, Helvetica, and Courier to closely match the // vertical metrics of their Microsoft counterparts that are the de facto // web standard. The AppKit adjustment of 20% is too big and is // incorrectly added to line spacing, so we use a 15% adjustment instead // and add it to the ascent. DEFINE_STATIC_LOCAL(AtomicString, timesName, ("Times")); DEFINE_STATIC_LOCAL(AtomicString, helveticaName, ("Helvetica")); DEFINE_STATIC_LOCAL(AtomicString, courierName, ("Courier")); String familyName = m_platformData.fontFamilyName(); if (familyName == timesName || familyName == helveticaName || familyName == courierName) ascent += floorf(((ascent + descent) * 0.15f) + 0.5f); #endif m_fontMetrics.setAscent(ascent); m_fontMetrics.setDescent(descent); float xHeight; if (metrics.fXHeight) { xHeight = metrics.fXHeight; #if OS(MACOSX) // Mac OS CTFontGetXHeight reports the bounding box height of x, // including parts extending below the baseline and apparently no x-height // value from the OS/2 table. However, the CSS ex unit // expects only parts above the baseline, hence measuring the glyph: // http://www.w3.org/TR/css3-values/#ex-unit const Glyph xGlyph = glyphForCharacter('x'); if (xGlyph) { FloatRect glyphBounds(boundsForGlyph(xGlyph)); // SkGlyph bounds, y down, based on rendering at (0,0). xHeight = - glyphBounds.y(); } #endif m_fontMetrics.setXHeight(xHeight); } else { xHeight = ascent * 0.56; // Best guess from Windows font metrics. m_fontMetrics.setXHeight(xHeight); m_fontMetrics.setHasXHeight(false); } float lineGap = SkScalarToFloat(metrics.fLeading); m_fontMetrics.setLineGap(lineGap); m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); if (platformData().isVerticalAnyUpright() && !isTextOrientationFallback()) { static const uint32_t vheaTag = SkSetFourByteTag('v', 'h', 'e', 'a'); static const uint32_t vorgTag = SkSetFourByteTag('V', 'O', 'R', 'G'); size_t vheaSize = face->getTableSize(vheaTag); size_t vorgSize = face->getTableSize(vorgTag); if ((vheaSize > 0) || (vorgSize > 0)) m_hasVerticalGlyphs = true; } // In WebKit/WebCore/platform/graphics/SimpleFontData.cpp, m_spaceWidth is // calculated for us, but we need to calculate m_maxCharWidth and // m_avgCharWidth in order for text entry widgets to be sized correctly. #if OS(WIN) m_maxCharWidth = SkScalarRoundToInt(metrics.fMaxCharWidth); // Older version of the DirectWrite API doesn't implement support for max // char width. Fall back on a multiple of the ascent. This is entirely // arbitrary but comes pretty close to the expected value in most cases. if (m_maxCharWidth < 1) m_maxCharWidth = ascent * 2; #elif OS(MACOSX) // FIXME: The current avg/max character width calculation is not ideal, // it should check either the OS2 table or, better yet, query FontMetrics. // Sadly FontMetrics provides incorrect data on Mac at the moment. // https://crbug.com/420901 m_maxCharWidth = std::max(m_avgCharWidth, m_fontMetrics.floatAscent()); #else // Better would be to rely on either fMaxCharWidth or fAveCharWidth. // skbug.com/3087 m_maxCharWidth = SkScalarRoundToInt(metrics.fXMax - metrics.fXMin); #endif #if !OS(MACOSX) if (metrics.fAvgCharWidth) { m_avgCharWidth = SkScalarRoundToInt(metrics.fAvgCharWidth); } else { #endif m_avgCharWidth = xHeight; const Glyph xGlyph = glyphForCharacter('x'); if (xGlyph) { m_avgCharWidth = widthForGlyph(xGlyph); } #if !OS(MACOSX) } #endif if (int unitsPerEm = face->getUnitsPerEm()) m_fontMetrics.setUnitsPerEm(unitsPerEm); }
void onDraw(SkCanvas* canvas) override { canvas->drawColor(sk_tool_utils::color_to_565(SK_ColorGRAY)); SkPaint paint; paint.setTypeface(emojiFont.typeface); const char* text = emojiFont.text; // draw text at different point sizes const int textSize[] = { 10, 30, 50, }; const int textYOffset[] = { 10, 40, 100, }; SkASSERT(sizeof(textSize) == sizeof(textYOffset)); size_t y_offset = 0; for (size_t y = 0; y < sizeof(textSize) / sizeof(int); y++) { paint.setTextSize(SkIntToScalar(textSize[y])); canvas->drawText(text, strlen(text), 10, SkIntToScalar(textYOffset[y]), paint); y_offset += textYOffset[y]; } // draw with shaders and image filters for (int makeLinear = 0; makeLinear < 2; makeLinear++) { for (int makeBlur = 0; makeBlur < 2; makeBlur++) { for (int makeGray = 0; makeGray < 2; makeGray++) { SkPaint shaderPaint; shaderPaint.setTypeface(paint.getTypeface()); if (SkToBool(makeLinear)) { shaderPaint.setShader(MakeLinear())->unref(); } if (SkToBool(makeBlur) && SkToBool(makeGray)) { SkAutoTUnref<SkImageFilter> grayScale(make_grayscale(nullptr)); SkAutoTUnref<SkImageFilter> blur(make_blur(3.0f, grayScale)); shaderPaint.setImageFilter(blur); } else if (SkToBool(makeBlur)) { SkAutoTUnref<SkImageFilter> blur(make_blur(3.0f, nullptr)); shaderPaint.setImageFilter(blur); } else if (SkToBool(makeGray)) { SkAutoTUnref<SkImageFilter> grayScale(make_grayscale(nullptr)); shaderPaint.setImageFilter(grayScale); } shaderPaint.setTextSize(30); canvas->drawText(text, strlen(text), 380, SkIntToScalar(y_offset), shaderPaint); y_offset += 32; } } } // setup work needed to draw text with different clips canvas->translate(10, 160); paint.setTextSize(40); // compute the bounds of the text SkRect bounds; paint.measureText(text, strlen(text), &bounds); const SkScalar boundsHalfWidth = bounds.width() * SK_ScalarHalf; const SkScalar boundsHalfHeight = bounds.height() * SK_ScalarHalf; const SkScalar boundsQuarterWidth = boundsHalfWidth * SK_ScalarHalf; const SkScalar boundsQuarterHeight = boundsHalfHeight * SK_ScalarHalf; SkRect upperLeftClip = SkRect::MakeXYWH(bounds.left(), bounds.top(), boundsHalfWidth, boundsHalfHeight); SkRect lowerRightClip = SkRect::MakeXYWH(bounds.centerX(), bounds.centerY(), boundsHalfWidth, boundsHalfHeight); SkRect interiorClip = bounds; interiorClip.inset(boundsQuarterWidth, boundsQuarterHeight); const SkRect clipRects[] = { bounds, upperLeftClip, lowerRightClip, interiorClip }; SkPaint clipHairline; clipHairline.setColor(SK_ColorWHITE); clipHairline.setStyle(SkPaint::kStroke_Style); for (size_t x = 0; x < sizeof(clipRects) / sizeof(SkRect); ++x) { canvas->save(); canvas->drawRect(clipRects[x], clipHairline); paint.setAlpha(0x20); canvas->drawText(text, strlen(text), 0, 0, paint); canvas->clipRect(clipRects[x]); paint.setAlpha(0xFF); canvas->drawText(text, strlen(text), 0, 0, paint); canvas->restore(); canvas->translate(0, bounds.height() + SkIntToScalar(25)); } }
// Even with kEntirePaint_Bits, we always ensure that the master paint's // text-encoding is respected, since that controls how we interpret the // text/length parameters of a draw[Pos]Text call. void SkLayerDrawLooper::ApplyInfo(SkPaint* dst, const SkPaint& src, const LayerInfo& info) { uint32_t mask = info.fFlagsMask; dst->setFlags((dst->getFlags() & ~mask) | (src.getFlags() & mask)); dst->setColor(xferColor(src.getColor(), dst->getColor(), info.fColorMode)); BitFlags bits = info.fPaintBits; SkPaint::TextEncoding encoding = dst->getTextEncoding(); if (0 == bits) { return; } if (kEntirePaint_Bits == bits) { // we've already computed these, so save it from the assignment uint32_t f = dst->getFlags(); SkColor c = dst->getColor(); *dst = src; dst->setFlags(f); dst->setColor(c); dst->setTextEncoding(encoding); return; } if (bits & kStyle_Bit) { dst->setStyle(src.getStyle()); dst->setStrokeWidth(src.getStrokeWidth()); dst->setStrokeMiter(src.getStrokeMiter()); dst->setStrokeCap(src.getStrokeCap()); dst->setStrokeJoin(src.getStrokeJoin()); } if (bits & kTextSkewX_Bit) { dst->setTextSkewX(src.getTextSkewX()); } if (bits & kPathEffect_Bit) { dst->setPathEffect(src.getPathEffect()); } if (bits & kMaskFilter_Bit) { dst->setMaskFilter(src.getMaskFilter()); } if (bits & kShader_Bit) { dst->setShader(src.getShader()); } if (bits & kColorFilter_Bit) { dst->setColorFilter(src.getColorFilter()); } if (bits & kXfermode_Bit) { dst->setXfermode(src.getXfermode()); } // we don't override these #if 0 dst->setTypeface(src.getTypeface()); dst->setTextSize(src.getTextSize()); dst->setTextScaleX(src.getTextScaleX()); dst->setRasterizer(src.getRasterizer()); dst->setLooper(src.getLooper()); dst->setTextEncoding(src.getTextEncoding()); dst->setHinting(src.getHinting()); #endif }
virtual void onDrawContent(SkCanvas* canvas) { canvas->translate(SkIntToScalar(10), SkIntToScalar(20)); if (false) { SkPaint paint; paint.setAntiAlias(true); paint.setTextSize(50); paint.setTypeface(SkTypeface::CreateFromName("Arial Unicode MS", SkTypeface::kNormal)); SkSafeUnref(paint.getTypeface()); char buffer[10]; size_t len = SkUTF8_FromUnichar(0x8500, buffer); canvas->drawText(buffer, len, 40, 40, paint); return; } if (false) { SkPaint paint; paint.setAntiAlias(true); SkRect r0 = { 0, 0, 10.5f, 20 }; SkRect r1 = { 10.5f, 10, 20, 30 }; paint.setColor(SK_ColorRED); canvas->drawRect(r0, paint); paint.setColor(SK_ColorBLUE); canvas->drawRect(r1, paint); return; } const struct { SkXfermode::Mode fMode; const char* fLabel; } gModes[] = { { SkXfermode::kClear_Mode, "Clear" }, { SkXfermode::kSrc_Mode, "Src" }, { SkXfermode::kDst_Mode, "Dst" }, { SkXfermode::kSrcOver_Mode, "SrcOver" }, { SkXfermode::kDstOver_Mode, "DstOver" }, { SkXfermode::kSrcIn_Mode, "SrcIn" }, { SkXfermode::kDstIn_Mode, "DstIn" }, { SkXfermode::kSrcOut_Mode, "SrcOut" }, { SkXfermode::kDstOut_Mode, "DstOut" }, { SkXfermode::kSrcATop_Mode, "SrcATop" }, { SkXfermode::kDstATop_Mode, "DstATop" }, { SkXfermode::kXor_Mode, "Xor" }, { SkXfermode::kPlus_Mode, "Plus" }, /*{ SkXfermode::kModulate_Mode, "Modulate" }, { SkXfermode::kScreen_Mode, "Screen" }, { SkXfermode::kOverlay_Mode, "Overlay" }, { SkXfermode::kDarken_Mode, "Darken" }, { SkXfermode::kLighten_Mode, "Lighten" }, { SkXfermode::kColorDodge_Mode, "ColorDodge" }, { SkXfermode::kColorBurn_Mode, "ColorBurn" }, { SkXfermode::kHardLight_Mode, "HardLight" }, { SkXfermode::kSoftLight_Mode, "SoftLight" }, { SkXfermode::kDifference_Mode, "Difference" }, { SkXfermode::kExclusion_Mode, "Exclusion" },*/ }; const SkScalar w = SkIntToScalar(W); const SkScalar h = SkIntToScalar(H); SkShader* s = SkShader::CreateBitmapShader(fBG, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); SkMatrix m; m.setScale(SkIntToScalar(6), SkIntToScalar(6)); s->setLocalMatrix(m); SkPaint labelP; labelP.setAntiAlias(true); labelP.setLCDRenderText(true); labelP.setTextAlign(SkPaint::kCenter_Align); setNamedTypeface(&labelP, "Menlo Regular"); const int W = 5; SkScalar x0 = 0; for (int twice = 0; twice < 2; twice++) { SkScalar x = x0, y = 0; for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) { SkXfermode* mode = SkXfermode::Create(gModes[i].fMode); SkAutoUnref aur(mode); SkRect r; r.set(x, y, x+w, y+h); SkPaint p; p.setStyle(SkPaint::kFill_Style); p.setShader(s); canvas->drawRect(r, p); canvas->saveLayer(&r, NULL); draw_mode(canvas, mode, twice ? 0x88 : 0xFF, r.fLeft, r.fTop); canvas->restore(); r.inset(-SK_ScalarHalf, -SK_ScalarHalf); p.setStyle(SkPaint::kStroke_Style); p.setShader(NULL); canvas->drawRect(r, p); canvas->drawText(gModes[i].fLabel, strlen(gModes[i].fLabel), x + w/2, y - labelP.getTextSize()/2, labelP); x += w + SkIntToScalar(10); if ((i % W) == W - 1) { x = x0; y += h + SkIntToScalar(30); } } x0 += SkIntToScalar(400); } s->unref(); }
void SkFlatPaint::dump() const { SkPaint defaultPaint; SkFlattenableReadBuffer buffer(fPaintData); SkTypeface* typeface = (SkTypeface*) buffer.readPtr(); char pBuffer[DUMP_BUFFER_SIZE]; char* bufferPtr = pBuffer; bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "paint: "); if (typeface != defaultPaint.getTypeface()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "typeface:%p ", typeface); SkScalar textSize = buffer.readScalar(); if (textSize != defaultPaint.getTextSize()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "textSize:%g ", SkScalarToFloat(textSize)); SkScalar textScaleX = buffer.readScalar(); if (textScaleX != defaultPaint.getTextScaleX()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "textScaleX:%g ", SkScalarToFloat(textScaleX)); SkScalar textSkewX = buffer.readScalar(); if (textSkewX != defaultPaint.getTextSkewX()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "textSkewX:%g ", SkScalarToFloat(textSkewX)); const SkPathEffect* pathEffect = (const SkPathEffect*) buffer.readFlattenable(); if (pathEffect != defaultPaint.getPathEffect()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "pathEffect:%p ", pathEffect); SkDELETE(pathEffect); const SkShader* shader = (const SkShader*) buffer.readFlattenable(); if (shader != defaultPaint.getShader()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "shader:%p ", shader); SkDELETE(shader); const SkXfermode* xfermode = (const SkXfermode*) buffer.readFlattenable(); if (xfermode != defaultPaint.getXfermode()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "xfermode:%p ", xfermode); SkDELETE(xfermode); const SkMaskFilter* maskFilter = (const SkMaskFilter*) buffer.readFlattenable(); if (maskFilter != defaultPaint.getMaskFilter()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "maskFilter:%p ", maskFilter); SkDELETE(maskFilter); const SkColorFilter* colorFilter = (const SkColorFilter*) buffer.readFlattenable(); if (colorFilter != defaultPaint.getColorFilter()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "colorFilter:%p ", colorFilter); SkDELETE(colorFilter); const SkRasterizer* rasterizer = (const SkRasterizer*) buffer.readFlattenable(); if (rasterizer != defaultPaint.getRasterizer()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "rasterizer:%p ", rasterizer); SkDELETE(rasterizer); const SkDrawLooper* drawLooper = (const SkDrawLooper*) buffer.readFlattenable(); if (drawLooper != defaultPaint.getLooper()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "drawLooper:%p ", drawLooper); SkDELETE(drawLooper); unsigned color = buffer.readU32(); if (color != defaultPaint.getColor()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "color:0x%x ", color); SkScalar strokeWidth = buffer.readScalar(); if (strokeWidth != defaultPaint.getStrokeWidth()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "strokeWidth:%g ", SkScalarToFloat(strokeWidth)); SkScalar strokeMiter = buffer.readScalar(); if (strokeMiter != defaultPaint.getStrokeMiter()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "strokeMiter:%g ", SkScalarToFloat(strokeMiter)); unsigned flags = buffer.readU16(); if (flags != defaultPaint.getFlags()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "flags:0x%x ", flags); int align = buffer.readU8(); if (align != defaultPaint.getTextAlign()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "align:0x%x ", align); int strokeCap = buffer.readU8(); if (strokeCap != defaultPaint.getStrokeCap()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "strokeCap:0x%x ", strokeCap); int strokeJoin = buffer.readU8(); if (strokeJoin != defaultPaint.getStrokeJoin()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "align:0x%x ", strokeJoin); int style = buffer.readU8(); if (style != defaultPaint.getStyle()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "style:0x%x ", style); int textEncoding = buffer.readU8(); if (textEncoding != defaultPaint.getTextEncoding()) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "textEncoding:0x%x ", textEncoding); SkDebugf("%s\n", pBuffer); }