void SkDumpCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { SkString str; toString(text, byteLength, paint.getTextEncoding(), &str); this->dump(kDrawText_Verb, &paint, "drawText(%s [%d] %g %g)", str.c_str(), byteLength, SkScalarToFloat(x), SkScalarToFloat(y)); }
void SkDumpCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, const SkMatrix* matrix, const SkPaint& paint) { SkString str; toString(text, byteLength, paint.getTextEncoding(), &str); this->dump(kDrawText_Verb, &paint, "drawTextOnPath(%s [%d])", str.c_str(), byteLength); }
void SkOverdrawCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[], const SkRect*, const SkPaint& paint) { CountTextProc proc = nullptr; switch (paint.getTextEncoding()) { case SkPaint::kUTF8_TextEncoding: proc = SkUTF8_CountUTF8Bytes; break; case SkPaint::kUTF16_TextEncoding: proc = count_utf16; break; case SkPaint::kUTF32_TextEncoding: proc = return_4; break; case SkPaint::kGlyphID_TextEncoding: proc = return_2; break; } SkASSERT(proc); SkMatrix matrix; const void* stopText = (const char*)text + byteLength; while ((const char*)text < (const char*)stopText) { matrix.setRSXform(*xform++); matrix.setConcat(this->getTotalMatrix(), matrix); int subLen = proc((const char*)text); this->save(); this->concat(matrix); this->drawText(text, subLen, 0, 0, paint); this->restore(); text = (const char*)text + subLen; } }
void SkDumpCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[], const SkRect* cull, const SkPaint& paint) { SkString str; toString(text, byteLength, paint.getTextEncoding(), &str); this->dump(kDrawText_Verb, &paint, "drawTextRSXform(%s [%d])", str.c_str(), byteLength); }
void SkBaseDevice::drawTextRSXform(const SkDraw& draw, const void* text, size_t len, const SkRSXform xform[], const SkPaint& paint) { CountTextProc proc = nullptr; switch (paint.getTextEncoding()) { case SkPaint::kUTF8_TextEncoding: proc = SkUTF8_CountUTF8Bytes; break; case SkPaint::kUTF16_TextEncoding: proc = count_utf16; break; case SkPaint::kUTF32_TextEncoding: proc = return_4; break; case SkPaint::kGlyphID_TextEncoding: proc = return_2; break; } SkDraw localD(draw); SkMatrix localM, currM; const void* stopText = (const char*)text + len; while ((const char*)text < (const char*)stopText) { localM.setRSXform(*xform++); currM.setConcat(*draw.fMatrix, localM); localD.fMatrix = &currM; int subLen = proc((const char*)text); this->drawText(localD, text, subLen, 0, 0, paint); text = (const char*)text + subLen; } }
void SkDumpCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY, const SkPaint& paint) { SkString str; toString(text, byteLength, paint.getTextEncoding(), &str); this->dump(kDrawText_Verb, &paint, "drawPosTextH(%s [%d] %g %g ...)", str.c_str(), byteLength, SkScalarToFloat(xpos[0]), SkScalarToFloat(constY)); }
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); }
static void getGlyphPositions(const SkPaint& paint, const uint16_t glyphs[], int count, SkScalar x, SkScalar y, SkPoint pos[]) { SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding()); SkAutoSTMalloc<128, SkScalar> widthStorage(count); SkScalar* widths = widthStorage.get(); paint.getTextWidths(glyphs, count * sizeof(uint16_t), widths); for (int i = 0; i < count; ++i) { pos[i].set(x, y); x += widths[i]; } }
SkDrawTextCommand::SkDrawTextCommand(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) : INHERITED(DRAW_TEXT) { fText = new char[byteLength]; memcpy(fText, text, byteLength); fByteLength = byteLength; fX = x; fY = y; fPaint = paint; fInfo.push(SkObjectParser::TextToString(text, byteLength, paint.getTextEncoding())); fInfo.push(SkObjectParser::ScalarToString(x, "SkScalar x: ")); fInfo.push(SkObjectParser::ScalarToString(y, "SkScalar y: ")); fInfo.push(SkObjectParser::PaintToString(paint)); }
void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len, const SkScalar pos[], SkScalar constY, int scalarsPerPos, const SkPaint& paint) { SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos); SkPaint textPaint = calculateTextPaint(paint); updateGSFromPaint(textPaint, true); // Make sure we have a glyph id encoding. SkAutoFree glyphStorage; uint16_t* glyphIDs; size_t numGlyphs; if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) { numGlyphs = paint.textToGlyphs(text, len, NULL); glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2, SK_MALLOC_TEMP | SK_MALLOC_THROW); glyphStorage.set(glyphIDs); paint.textToGlyphs(text, len, glyphIDs); textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); } else { SkASSERT((len & 1) == 0); numGlyphs = len / 2; glyphIDs = (uint16_t*)text; } SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc(); fContent.writeText("BT\n"); updateFont(textPaint, glyphIDs[0]); for (size_t i = 0; i < numGlyphs; i++) { SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont; uint16_t encodedValue = glyphIDs[i]; if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) { updateFont(textPaint, glyphIDs[i]); i--; continue; } SkScalar x = pos[i * scalarsPerPos]; SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1]; alignText(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL); setTextTransform(x, y, textPaint.getTextSkewX()); SkString encodedString = SkPDFString::formatString(&encodedValue, 1, font->multiByteGlyphs()); fContent.writeText(encodedString.c_str()); fContent.writeText(" Tj\n"); } fContent.writeText("ET\n"); }
static int breakText(JNIEnv* env, const SkPaint& paint, const jchar text[], int count, float maxWidth, jfloatArray jmeasured, SkPaint::TextBufferDirection tbd) { SkASSERT(paint.getTextEncoding() == SkPaint::kUTF16_TextEncoding); SkScalar measured; size_t bytes = paint.breakText(text, count << 1, SkFloatToScalar(maxWidth), &measured, tbd); SkASSERT((bytes & 1) == 0); if (jmeasured && env->GetArrayLength(jmeasured) > 0) { AutoJavaFloatArray autoMeasured(env, jmeasured, 1); jfloat* array = autoMeasured.ptr(); array[0] = SkScalarToFloat(measured); } return bytes >> 1; }
void SkBaseDevice::drawTextRSXform(const void* text, size_t len, const SkRSXform xform[], const SkPaint& paint) { CountTextProc proc = nullptr; switch (paint.getTextEncoding()) { case SkPaint::kUTF8_TextEncoding: proc = SkUTF8_CountUTF8Bytes; break; case SkPaint::kUTF16_TextEncoding: proc = count_utf16; break; case SkPaint::kUTF32_TextEncoding: proc = return_4; break; case SkPaint::kGlyphID_TextEncoding: proc = return_2; break; } SkPaint localPaint(paint); SkShader* shader = paint.getShader(); SkMatrix localM, currM; const void* stopText = (const char*)text + len; while ((const char*)text < (const char*)stopText) { localM.setRSXform(*xform++); currM.setConcat(this->ctm(), localM); SkAutoDeviceCTMRestore adc(this, currM); // We want to rotate each glyph by the rsxform, but we don't want to rotate "space" // (i.e. the shader that cares about the ctm) so we have to undo our little ctm trick // with a localmatrixshader so that the shader draws as if there was no change to the ctm. if (shader) { SkMatrix inverse; if (localM.invert(&inverse)) { localPaint.setShader(shader->makeWithLocalMatrix(inverse)); } else { localPaint.setShader(nullptr); // can't handle this xform } } int subLen = proc((const char*)text); this->drawText(text, subLen, 0, 0, localPaint); text = (const char*)text + subLen; } }
String LoggingCanvas::stringForText(const void* text, size_t byteLength, const SkPaint& paint) { SkPaint::TextEncoding encoding = paint.getTextEncoding(); switch (encoding) { case SkPaint::kUTF8_TextEncoding: case SkPaint::kUTF16_TextEncoding: case SkPaint::kUTF32_TextEncoding: return stringForUTFText(text, byteLength, encoding); case SkPaint::kGlyphID_TextEncoding: { WTF::Vector<SkUnichar> dataVector(byteLength / 2); SkUnichar* textData = dataVector.data(); paint.glyphsToUnichars(static_cast<const uint16_t*>(text), byteLength / 2, textData); return WTF::UTF32LittleEndianEncoding().decode(reinterpret_cast<const char*>(textData), byteLength * 2); } default: ASSERT_NOT_REACHED(); return "?"; } }
SkDrawPosTextCommand::SkDrawPosTextCommand(const void* text, size_t byteLength, const SkPoint pos[], const SkPaint& paint) : INHERITED(DRAW_POS_TEXT) { size_t numPts = paint.countText(text, byteLength); fText = new char[byteLength]; memcpy(fText, text, byteLength); fByteLength = byteLength; fPos = new SkPoint[numPts]; memcpy(fPos, pos, numPts * sizeof(SkPoint)); fPaint = paint; fInfo.push(SkObjectParser::TextToString(text, byteLength, paint.getTextEncoding())); // TODO(chudy): Test that this works. fInfo.push(SkObjectParser::PointsToString(pos, 1)); fInfo.push(SkObjectParser::PaintToString(paint)); }
void FindCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { SkPaint::TextEncoding encoding = paint.getTextEncoding(); //For complex text, transform utf16 to glyph if (encoding == SkPaint::kUTF16_TextEncoding) { int mCount = 0; uint16_t *glyphBuf = NULL; glyphBuf = new uint16_t[byteLength]; mCount = paint.textToGlyphs(text, byteLength, glyphBuf, byteLength); SkPaint clonePaint(paint); clonePaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); findHelper(glyphBuf, 2*mCount, clonePaint, &x, y, &FindCanvas::addMatchNormal); delete glyphBuf; return; } findHelper(text, byteLength, paint, &x, y, &FindCanvas::addMatchNormal); }
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(); }
SkDrawPosTextHCommand::SkDrawPosTextHCommand(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY, const SkPaint& paint) : INHERITED(DRAW_POS_TEXT_H) { size_t numPts = paint.countText(text, byteLength); fText = new char[byteLength]; memcpy(fText, text, byteLength); fByteLength = byteLength; fXpos = new SkScalar[numPts]; memcpy(fXpos, xpos, numPts * sizeof(SkScalar)); fConstY = constY; fPaint = paint; fInfo.push(SkObjectParser::TextToString(text, byteLength, paint.getTextEncoding())); fInfo.push(SkObjectParser::ScalarToString(xpos[0], "XPOS: ")); fInfo.push(SkObjectParser::ScalarToString(constY, "SkScalar constY: ")); fInfo.push(SkObjectParser::PaintToString(paint)); }
SkDrawTextOnPathCommand::SkDrawTextOnPathCommand(const void* text, size_t byteLength, const SkPath& path, const SkMatrix* matrix, const SkPaint& paint) : INHERITED(DRAW_TEXT_ON_PATH) { fText = new char[byteLength]; memcpy(fText, text, byteLength); fByteLength = byteLength; fPath = path; if (NULL != matrix) { fMatrix = *matrix; } else { fMatrix.setIdentity(); } fPaint = paint; fInfo.push(SkObjectParser::TextToString(text, byteLength, paint.getTextEncoding())); fInfo.push(SkObjectParser::PathToString(path)); if (NULL != matrix) { fInfo.push(SkObjectParser::MatrixToString(*matrix)); } fInfo.push(SkObjectParser::PaintToString(paint)); }
/* * 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); }
// 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 SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len, SkScalar x, SkScalar y, const SkPaint& paint) { SkPaint textPaint = calculateTextPaint(paint); updateGSFromPaint(textPaint, true); // We want the text in glyph id encoding and a writable buffer, so we end // up making a copy either way. size_t numGlyphs = paint.textToGlyphs(text, len, NULL); uint16_t* glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2, SK_MALLOC_TEMP | SK_MALLOC_THROW); SkAutoFree autoFreeGlyphIDs(glyphIDs); if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) { paint.textToGlyphs(text, len, glyphIDs); textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); } else { SkASSERT((len & 1) == 0); SkASSERT(len / 2 == numGlyphs); memcpy(glyphIDs, text, len); } SkScalar width; SkScalar* widthPtr = NULL; if (textPaint.isUnderlineText() || textPaint.isStrikeThruText()) widthPtr = &width; SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc(); alignText(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, widthPtr); fContent.writeText("BT\n"); setTextTransform(x, y, textPaint.getTextSkewX()); size_t consumedGlyphCount = 0; while (numGlyphs > consumedGlyphCount) { updateFont(textPaint, glyphIDs[consumedGlyphCount]); SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont; size_t availableGlyphs = font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount, numGlyphs - consumedGlyphCount); SkString encodedString = SkPDFString::formatString(glyphIDs + consumedGlyphCount, availableGlyphs, font->multiByteGlyphs()); fContent.writeText(encodedString.c_str()); consumedGlyphCount += availableGlyphs; fContent.writeText(" Tj\n"); } fContent.writeText("ET\n"); // Draw underline and/or strikethrough if the paint has them. // drawPosText() and drawTextOnPath() don't draw underline or strikethrough // because the raster versions don't. Use paint instead of textPaint // because we may have changed strokeWidth to do fakeBold text. if (paint.isUnderlineText() || paint.isStrikeThruText()) { SkScalar textSize = paint.getTextSize(); SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness); if (paint.isUnderlineText()) { SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y); SkRect r = SkRect::MakeXYWH(x, top - height, width, height); drawRect(d, r, paint); } if (paint.isStrikeThruText()) { SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y); SkRect r = SkRect::MakeXYWH(x, top - height, width, height); drawRect(d, r, paint); } } }
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); }
// 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 }
void FindCanvas::findHelper(const void* text, size_t byteLength, const SkPaint& paint, const SkScalar positions[], SkScalar y, SkRect (FindCanvas::*addMatch)(int index, const SkPaint& paint, int count, const uint16_t* glyphs, const SkScalar positions[], SkScalar y)) { SkASSERT(paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding); SkASSERT(mMatches); GlyphSet* glyphSet = getGlyphs(paint); const int count = glyphSet->getCount(); int numCharacters = byteLength >> 1; const uint16_t* chars = (const uint16_t*) text; // This block will check to see if we are continuing from another line. If // so, the user needs to have added a space, which we do not draw. if (mWorkingIndex) { SkPoint newY; getTotalMatrix().mapXY(0, y, &newY); SkIRect workingBounds = mWorkingRegion.getBounds(); int newYInt = SkScalarRound(newY.fY); if (workingBounds.fTop > newYInt) { // The new text is above the working region, so we know it's not // a continuation. resetWorkingCanvas(); mWorkingIndex = 0; mWorkingRegion.setEmpty(); } else if (workingBounds.fBottom < newYInt) { // Now we know that this line is lower than our partial match. SkPaint clonePaint(paint); clonePaint.setTextEncoding(SkPaint::kUTF8_TextEncoding); uint16_t space; clonePaint.textToGlyphs(" ", 1, &space); if (glyphSet->characterMatches(space, mWorkingIndex)) { mWorkingIndex++; if (mWorkingIndex == count) { // We already know that it is not clipped out because we // checked for that before saving the working region. insertMatchInfo(mWorkingRegion); resetWorkingCanvas(); mWorkingIndex = 0; mWorkingRegion.setEmpty(); // We have found a match, so continue on this line from // scratch. } } else { resetWorkingCanvas(); mWorkingIndex = 0; mWorkingRegion.setEmpty(); } } // If neither one is true, then we are likely continuing on the same // line, but are in a new draw call because the paint has changed. In // this case, we can continue without adding a space. } // j is the position in the search text // Start off with mWorkingIndex in case we are continuing from a prior call int j = mWorkingIndex; // index is the position in the drawn text int index = 0; for ( ; index != numCharacters; index++) { if (glyphSet->characterMatches(chars[index], j)) { // The jth character in the search text matches the indexth position // in the drawn text, so increase j. j++; if (j != count) { continue; } // The last count characters match, so we found the entire // search string. int remaining = count - mWorkingIndex; int matchIndex = index - remaining + 1; // Set up a pointer to the matching text in 'chars'. const uint16_t* glyphs = chars + matchIndex; SkRect rect = (this->*addMatch)(matchIndex, paint, remaining, glyphs, positions, y); // We need an SkIRect for SkRegion operations. SkIRect iRect; rect.roundOut(&iRect); // If the rectangle is partially clipped, assume that the text is // not visible, so skip this match. if (getTotalClip().contains(iRect)) { // Want to outset the drawn rectangle by the same amount as // mOutset iRect.inset(-INTEGER_OUTSET, -INTEGER_OUTSET); SkRegion regionToAdd(iRect); if (!mWorkingRegion.isEmpty()) { // If this is on the same line as our working region, make // sure that they are close enough together that they are // supposed to be part of the same text string. // The width of two spaces has arbitrarily been chosen. const SkIRect& workingBounds = mWorkingRegion.getBounds(); if (workingBounds.fTop <= iRect.fBottom && workingBounds.fBottom >= iRect.fTop && SkIntToScalar(iRect.fLeft - workingBounds.fRight) > approximateSpaceWidth(paint)) { index = -1; // Will increase to 0 on next run // In this case, we need to start from the beginning of // the text being searched and our search term. j = 0; mWorkingIndex = 0; mWorkingRegion.setEmpty(); continue; } // Add the mWorkingRegion, which contains rectangles from // the previous line(s). regionToAdd.op(mWorkingRegion, SkRegion::kUnion_Op); } insertMatchInfo(regionToAdd); #if INCLUDE_SUBSTRING_MATCHES // Reset index to the location of the match and reset j to the // beginning, so that on the next iteration of the loop, index // will advance by 1 and we will compare the next character in // chars to the first character in the GlyphSet. index = matchIndex; #endif } else { // This match was clipped out, so begin looking at the next // character from our hidden match index = matchIndex; } // Whether the clip contained it or not, we need to start over // with our recording canvas resetWorkingCanvas(); } else { // Index needs to be set to index - j + 1. // This is a ridiculous case, but imagine the situation where the // user is looking for the string "jjog" in the drawText call for // "jjjog". The first two letters match. However, when the index // is 2, and we discover that 'o' and 'j' do not match, we should go // back to 1, where we do, in fact, have a match // FIXME: This does not work if (as in our example) "jj" is in one // draw call and "jog" is in the next. Doing so would require a // stack, keeping track of multiple possible working indeces and // regions. This is likely an uncommon case. index = index - j; // index will be increased by one on the next // iteration } // We reach here in one of two cases: // 1) We just completed a match, so any working rectangle/index is no // longer needed, and we will start over from the beginning // 2) The glyphs do not match, so we start over at the beginning of // the search string. j = 0; mWorkingIndex = 0; mWorkingRegion.setEmpty(); } // At this point, we have searched all of the text in the current drawText // call. // Keep track of a partial match that may start on this line. if (j > 0) { // if j is greater than 0, we have a partial match int relativeCount = j - mWorkingIndex; // Number of characters in this // part of the match. int partialIndex = index - relativeCount; // Index that starts our // partial match. const uint16_t* partialGlyphs = chars + partialIndex; SkRect partial = (this->*addMatch)(partialIndex, paint, relativeCount, partialGlyphs, positions, y); partial.inset(mOutset, mOutset); SkIRect dest; partial.roundOut(&dest); // Only save a partial if it is in the current clip. if (getTotalClip().contains(dest)) { mWorkingRegion.op(dest, SkRegion::kUnion_Op); mWorkingIndex = j; return; } } // This string doesn't go into the next drawText, so reset our working // variables mWorkingRegion.setEmpty(); mWorkingIndex = 0; }
static SkTypeface::Encoding paint2encoding(const SkPaint& paint) { SkPaint::TextEncoding enc = paint.getTextEncoding(); SkASSERT(SkPaint::kGlyphID_TextEncoding != enc); return (SkTypeface::Encoding)enc; }