void drawTestCase(SkCanvas* canvas, const char* text, SkScalar y, const SkPaint& paint) { SkScalar widths[kMaxStringLength]; SkScalar posX[kMaxStringLength]; SkPoint pos[kMaxStringLength]; int length = SkToInt(strlen(text)); SkASSERT(length <= kMaxStringLength); paint.getTextWidths(text, length, widths); float originX; switch (paint.getTextAlign()) { case SkPaint::kRight_Align: originX = 1; break; case SkPaint::kCenter_Align: originX = 0.5f; break; case SkPaint::kLeft_Align: originX = 0; break; default: SkFAIL("Invalid paint origin"); return; } float x = kTextHeight; for (int i = 0; i < length; ++i) { posX[i] = x + originX * widths[i]; pos[i].set(posX[i], i ? pos[i - 1].y() + 3 : y + kTextHeight); x += widths[i]; } canvas->drawPosTextH(text, length, posX, y, paint); canvas->drawPosText(text, length, pos, paint); }
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); } }
void SkBBoxRecord::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { SkRect bbox; paint.measureText(text, byteLength, &bbox); SkPaint::FontMetrics metrics; paint.getFontMetrics(&metrics); // Vertical and aligned text need to be offset if (paint.isVerticalText()) { SkScalar h = bbox.fBottom - bbox.fTop; if (paint.getTextAlign() == SkPaint::kCenter_Align) { bbox.fTop -= h / 2; bbox.fBottom -= h / 2; } // Pad top and bottom with max extents from FontMetrics bbox.fBottom += metrics.fBottom; bbox.fTop += metrics.fTop; } else { SkScalar w = bbox.fRight - bbox.fLeft; if (paint.getTextAlign() == SkPaint::kCenter_Align) { bbox.fLeft -= w / 2; bbox.fRight -= w / 2; } else if (paint.getTextAlign() == SkPaint::kRight_Align) { bbox.fLeft -= w; bbox.fRight -= w; } // Set vertical bounds to max extents from font metrics bbox.fTop = metrics.fTop; bbox.fBottom = metrics.fBottom; } // Pad horizontal bounds on each side by half of max vertical extents (this is sort of // arbitrary, but seems to produce reasonable results, if there were a way of getting max // glyph X-extents to pad by, that may be better here, but FontMetrics fXMin and fXMax seem // incorrect on most platforms (too small in Linux, never even set in Windows). SkScalar pad = (metrics.fBottom - metrics.fTop) / 2; bbox.fLeft -= pad; bbox.fRight += pad; bbox.fLeft += x; bbox.fRight += x; bbox.fTop += y; bbox.fBottom += y; if (this->transformBounds(bbox, &paint)) { INHERITED::onDrawText(text, byteLength, x, y, paint); } }
void SkBaseDevice::drawTextOnPath(const SkDraw& draw, const void* text, size_t byteLength, const SkPath& follow, const SkMatrix* matrix, const SkPaint& paint) { SkASSERT(byteLength == 0 || text != nullptr); // nothing to draw if (text == nullptr || byteLength == 0 || draw.fRC->isEmpty()) { return; } SkTextToPathIter iter((const char*)text, byteLength, paint, true); SkPathMeasure meas(follow, false); SkScalar hOffset = 0; // need to measure first if (paint.getTextAlign() != SkPaint::kLeft_Align) { SkScalar pathLen = meas.getLength(); if (paint.getTextAlign() == SkPaint::kCenter_Align) { pathLen = SkScalarHalf(pathLen); } hOffset += pathLen; } const SkPath* iterPath; SkScalar xpos; SkMatrix scaledMatrix; SkScalar scale = iter.getPathScale(); scaledMatrix.setScale(scale, scale); while (iter.next(&iterPath, &xpos)) { if (iterPath) { SkPath tmp; SkMatrix m(scaledMatrix); tmp.setIsVolatile(true); m.postTranslate(xpos + hOffset, 0); if (matrix) { m.postConcat(*matrix); } morphpath(&tmp, *iterPath, meas, m); this->drawPath(draw, tmp, iter.getPaint(), nullptr, true); } } }
void SkOverdrawCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { ProcessOneGlyphBounds processBounds(this); SkSurfaceProps props(0, kUnknown_SkPixelGeometry); this->getProps(&props); auto cache = SkStrikeCache::FindOrCreateStrikeExclusive( paint, &props, SkScalerContextFlags::kNone, &this->getTotalMatrix()); SkFindAndPlaceGlyph::ProcessText(paint.getTextEncoding(), (const char*) text, byteLength, SkPoint::Make(x, y), SkMatrix(), paint.getTextAlign(), cache.get(), processBounds); }
void SkSVGDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, const SkPath& path, const SkMatrix* matrix, const SkPaint& paint) { SkString pathID = fResourceBucket->addPath(); { AutoElement defs("defs", fWriter); AutoElement pathElement("path", fWriter); pathElement.addAttribute("id", pathID); pathElement.addPathAttributes(path); } { AutoElement textElement("text", fWriter); textElement.addTextAttributes(paint); if (matrix && !matrix->isIdentity()) { textElement.addAttribute("transform", svg_transform(*matrix)); } { AutoElement textPathElement("textPath", fWriter); textPathElement.addAttribute("xlink:href", SkStringPrintf("#%s", pathID.c_str())); if (paint.getTextAlign() != SkPaint::kLeft_Align) { SkASSERT(paint.getTextAlign() == SkPaint::kCenter_Align || paint.getTextAlign() == SkPaint::kRight_Align); textPathElement.addAttribute("startOffset", paint.getTextAlign() == SkPaint::kCenter_Align ? "50%" : "100%"); } SVGTextBuilder builder(text, len, paint, SkPoint::Make(0, 0), 0); textPathElement.addText(builder.text()); } } }
static void apply_paint_textalign(const SkPaint& paint, Json::Value* target) { SkPaint::Align textAlign = paint.getTextAlign(); if (textAlign != SkPaint::kLeft_Align) { switch (textAlign) { case SkPaint::kCenter_Align: { (*target)[SKJSONCANVAS_ATTRIBUTE_TEXTALIGN] = SKJSONCANVAS_ALIGN_CENTER; break; } case SkPaint::kRight_Align: { (*target)[SKJSONCANVAS_ATTRIBUTE_TEXTALIGN] = SKJSONCANVAS_ALIGN_RIGHT; break; } default: SkASSERT(false); } } }
void GrTextUtils::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex, GrBatchFontCache* fontCache, const SkSurfaceProps& props, const SkPaint& skPaint, GrColor color, const SkMatrix& viewMatrix, const char text[], size_t byteLength, const SkScalar pos[], int scalarsPerPosition, const SkPoint& offset) { SkASSERT(byteLength == 0 || text != nullptr); SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); // nothing to draw if (text == nullptr || byteLength == 0) { return; } // Ensure the blob is set for bitmaptext blob->setHasBitmap(); GrBatchTextStrike* currStrike = nullptr; // Get GrFontScaler from cache SkGlyphCache* cache = blob->setupCache(runIndex, props, SkPaint::FakeGamma::On, skPaint, &viewMatrix); GrFontScaler* fontScaler = GrTextUtils::GetGrFontScaler(cache); SkFindAndPlaceGlyph::ProcessPosText( skPaint.getTextEncoding(), text, byteLength, offset, viewMatrix, pos, scalarsPerPosition, skPaint.getTextAlign(), cache, [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) { position += rounding; BmpAppendGlyph( blob, runIndex, fontCache, &currStrike, glyph, SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY), color, fontScaler); } ); SkGlyphCache::AttachCache(cache); }
PassRefPtr<JSONObject> LoggingCanvas::objectForSkPaint(const SkPaint& paint) { RefPtr<JSONObject> paintItem = JSONObject::create(); paintItem->setNumber("textSize", paint.getTextSize()); paintItem->setNumber("textScaleX", paint.getTextScaleX()); paintItem->setNumber("textSkewX", paint.getTextSkewX()); if (SkShader* shader = paint.getShader()) paintItem->setObject("shader", objectForSkShader(*shader)); paintItem->setString("color", stringForSkColor(paint.getColor())); paintItem->setNumber("strokeWidth", paint.getStrokeWidth()); paintItem->setNumber("strokeMiter", paint.getStrokeMiter()); paintItem->setString("flags", stringForSkPaintFlags(paint)); paintItem->setString("filterLevel", filterQualityName(paint.getFilterQuality())); paintItem->setString("textAlign", textAlignName(paint.getTextAlign())); paintItem->setString("strokeCap", strokeCapName(paint.getStrokeCap())); paintItem->setString("strokeJoin", strokeJoinName(paint.getStrokeJoin())); paintItem->setString("styleName", styleName(paint.getStyle())); paintItem->setString("textEncoding", textEncodingName(paint.getTextEncoding())); paintItem->setString("hinting", hintingName(paint.getHinting())); return paintItem.release(); }
/* * 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); }
SkPath TextArt::EnvelopeWarp::warp(const std::string& text, SkTypeface* typeface) { SkPath warpedPath; if (text.empty()) return warpedPath; //prepare paint SkPaint paint; paint.setTextSize(SkIntToScalar(64)); paint.setTypeface(typeface); paint.setTextAlign(SkPaint::kCenter_Align); //measure Bottom path to center text on it SkPathMeasure bMeasure(bSkeleton_, false); SkScalar hBOffset = 0; if (paint.getTextAlign() != SkPaint::kLeft_Align) { SkScalar pathLen = bMeasure.getLength(); if (paint.getTextAlign() == SkPaint::kCenter_Align) { pathLen = SkScalarHalf(pathLen); } hBOffset += pathLen; } //get text boundaries on normal(non-warped) state { SkMatrix scaleMartix; scaleMartix.setIdentity(); SkTextToPathIter iter(text.c_str(), text.size(), paint, true); const SkPath* glypthPath; SkScalar xpos; SkScalar scale = iter.getPathScale(); scaleMartix.setScale(scale, scale); while (iter.next(&glypthPath, &xpos)) { if (glypthPath) { //prepare resulting transformatiom Matrix SkMatrix compositeMatrix(scaleMartix); compositeMatrix.postTranslate(xpos + hBOffset, 0); SkPath p; (*glypthPath).transform(compositeMatrix, &p); //get normal(without any warps) text boundaries boundsRect_.join( p.getBounds() ); } } } //center text on Top skeleton SkPathMeasure tMeasure(tSkeleton_, false); SkScalar hTOffset = 0; { if (paint.getTextAlign() != SkPaint::kLeft_Align) { SkScalar pathLen = tMeasure.getLength(); if (paint.getTextAlign() == SkPaint::kCenter_Align) { pathLen = SkScalarHalf(pathLen); } hTOffset += pathLen; } } //warp text on Bottom and Top skeletons { SkTextToPathIter iter(text.c_str(), text.size(), paint, true); SkScalar xpos; SkMatrix scaleMartix; SkScalar scale = iter.getPathScale(); scaleMartix.setScale(scale, scale); SkPath line; line.lineTo(SkIntToScalar(100), SkIntToScalar(0)); SkPathMeasure lineMeasure(line, false); SkPathCrossing bCrossing(bSkeleton_); SkPathCrossing tCrossing(tSkeleton_); const SkPath* glypthPathOrig; while (iter.next(&glypthPathOrig, &xpos)) { if (glypthPathOrig) { SkPath glypthPath; SkRect glypthBound; glypthBound = (*glypthPathOrig).getBounds(); glypthPathOrig->offset(-glypthBound.fLeft, 0, &glypthPath); morph(bSkeleton_, bMeasure, bCrossing, tSkeleton_, tMeasure, tCrossing, glypthPath, lineMeasure, scaleMartix, xpos, hBOffset, hTOffset, warpedPath); } } } return warpedPath; }
SkScalar SkTextBox::visit(Visitor& visitor, const char text[], size_t len, const SkPaint& paint) const { SkScalar marginWidth = fBox.width(); if (marginWidth <= 0 || len == 0) { return fBox.top(); } const char* textStop = text + len; SkScalar x, y, scaledSpacing, height, fontHeight; SkPaint::FontMetrics metrics; switch (paint.getTextAlign()) { case SkPaint::kLeft_Align: x = 0; break; case SkPaint::kCenter_Align: x = SkScalarHalf(marginWidth); break; default: x = marginWidth; break; } x += fBox.fLeft; fontHeight = paint.getFontMetrics(&metrics); scaledSpacing = SkScalarMul(fontHeight, fSpacingMul) + fSpacingAdd; height = fBox.height(); // compute Y position for first line { SkScalar textHeight = fontHeight; if (fMode == kLineBreak_Mode && fSpacingAlign != kStart_SpacingAlign) { int count = SkTextLineBreaker::CountLines(text, textStop - text, paint, marginWidth); SkASSERT(count > 0); textHeight += scaledSpacing * (count - 1); } switch (fSpacingAlign) { case kStart_SpacingAlign: y = 0; break; case kCenter_SpacingAlign: y = SkScalarHalf(height - textHeight); break; default: SkASSERT(fSpacingAlign == kEnd_SpacingAlign); y = height - textHeight; break; } y += fBox.fTop - metrics.fAscent; } for (;;) { size_t trailing; len = linebreak(text, textStop, paint, marginWidth, &trailing); if (y + metrics.fDescent + metrics.fLeading > 0) { visitor(text, len - trailing, x, y, paint); } text += len; if (text >= textStop) { break; } y += scaledSpacing; if (y + metrics.fAscent >= fBox.fBottom) { break; } } return y + metrics.fDescent + metrics.fLeading; }
void GrDistanceFieldTextContext::onDrawText(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); // nothing to draw if (text == NULL || byteLength == 0) { return; } fViewMatrix = viewMatrix; SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc(); SkAutoGlyphCache autoCache(skPaint, &fDeviceProperties, NULL); SkGlyphCache* cache = autoCache.getCache(); SkTArray<SkScalar> positions; const char* textPtr = text; SkFixed stopX = 0; SkFixed stopY = 0; SkFixed origin; 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; default: SkFAIL("Invalid paint origin"); return; } 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(cache, &textPtr, 0, 0); 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); // 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); this->onDrawPosText(rt, clip, paint, skPaint, viewMatrix, text, byteLength, positions.begin(), 2, offset, regionClipBounds); }
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 SkTextBox::draw(SkCanvas* canvas, const char text[], size_t len, const SkPaint& paint) { SkASSERT(canvas && &paint && (text || len == 0)); SkScalar marginWidth = fBox.width(); if (marginWidth <= 0 || len == 0) return; const char* textStop = text + len; SkScalar x, y, scaledSpacing, height, fontHeight; SkPaint::FontMetrics metrics; switch (paint.getTextAlign()) { case SkPaint::kLeft_Align: x = 0; break; case SkPaint::kCenter_Align: x = SkScalarHalf(marginWidth); break; default: x = marginWidth; break; } x += fBox.fLeft; fontHeight = paint.getFontMetrics(&metrics); scaledSpacing = SkScalarMul(fontHeight, fSpacingMul) + fSpacingAdd; height = fBox.height(); // compute Y position for first line { SkScalar textHeight = fontHeight; if (fMode == kLineBreak_Mode && fSpacingAlign != kStart_SpacingAlign) { int count = SkTextLineBreaker::CountLines(text, textStop - text, paint, marginWidth); SkASSERT(count > 0); textHeight += scaledSpacing * (count - 1); } switch (fSpacingAlign) { case kStart_SpacingAlign: y = 0; break; case kCenter_SpacingAlign: y = SkScalarHalf(height - textHeight); break; default: SkASSERT(fSpacingAlign == kEnd_SpacingAlign); y = height - textHeight; break; } y += fBox.fTop - metrics.fAscent; } for (;;) { len = linebreak(text, textStop, paint, marginWidth); if (y + metrics.fDescent + metrics.fLeading > 0) canvas->drawText(text, len, x, y, paint); text += len; if (text >= textStop) break; y += scaledSpacing; if (y + metrics.fAscent >= height) break; } }
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); }