From: reed@google.com Date: Tue, 4 Jun 2013 16:56:27 +0000 (+0000) Subject: add size limit for using glyphcache. above that, draw using paths X-Git-Tag: accepted/tizen/5.0/unified/20181102.025319~12209 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ed43dffbf140cebc0575bed7b4dff1b32430ad21;p=platform%2Fupstream%2FlibSkiaSharp.git add size limit for using glyphcache. above that, draw using paths BUG= R=bungeman@google.com Review URL: https://codereview.chromium.org/16336024 git-svn-id: http://skia.googlecode.com/svn/trunk@9429 2bbb7eff-a529-9590-31e7-b0007b416f81 --- diff --git a/include/core/SkDraw.h b/include/core/SkDraw.h index 2a8afc4..293d4e3 100644 --- a/include/core/SkDraw.h +++ b/include/core/SkDraw.h @@ -103,6 +103,10 @@ private: void drawDevMask(const SkMask& mask, const SkPaint&) const; void drawBitmapAsMask(const SkBitmap&, const SkPaint&) const; + void drawPosText_asPaths(const char text[], size_t byteLength, + const SkScalar pos[], SkScalar constY, + int scalarsPerPosition, const SkPaint&) const; + /** * Return the current clip bounds, in local coordinates, with slop to account * for antialiasing or hairlines (i.e. device-bounds outset by 1, and then @@ -114,6 +118,8 @@ private: bool SK_WARN_UNUSED_RESULT computeConservativeLocalClipBounds(SkRect* bounds) const; + static bool ShouldDrawTextAsPaths(const SkPaint&, const SkMatrix&); + public: const SkBitmap* fBitmap; // required const SkMatrix* fMatrix; // required diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index a6c9bd4..624f39c 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -13,6 +13,7 @@ #include "SkColor.h" #include "SkDrawLooper.h" +#include "SkMatrix.h" #include "SkXfermode.h" #ifdef SK_BUILD_FOR_ANDROID #include "SkPaintOptionsAndroid.h" @@ -30,7 +31,6 @@ struct SkRect; class SkGlyphCache; class SkImageFilter; class SkMaskFilter; -class SkMatrix; class SkPath; class SkPathEffect; struct SkPoint; @@ -935,6 +935,22 @@ public: const SkRect& doComputeFastBounds(const SkRect& orig, SkRect* storage, Style) const; + /** + * Return a matrix that applies the paint's text values: size, scale, skew + */ + static SkMatrix* SetTextMatrix(SkMatrix* matrix, SkScalar size, + SkScalar scaleX, SkScalar skewX) { + matrix->setScale(size * scaleX, size); + if (skewX) { + matrix->postSkew(skewX, 0); + } + return matrix; + } + + SkMatrix* setTextMatrix(SkMatrix* matrix) const { + return SetTextMatrix(matrix, fTextSize, fTextScaleX, fTextSkewX); + } + SkDEVCODE(void toString(SkString*) const;) private: @@ -989,14 +1005,53 @@ private: static void Term(); enum { - kCanonicalTextSizeForPaths = 64 + /* This is the size we use when we ask for a glyph's path. We then + * post-transform it as we draw to match the request. + * This is done to try to re-use cache entries for the path. + * + * This value is somewhat arbitrary. In theory, it could be 1, since + * we store paths as floats. However, we get the path from the font + * scaler, and it may represent its paths as fixed-point (or 26.6), + * so we shouldn't ask for something too big (might overflow 16.16) + * or too small (underflow 26.6). + * + * This value could track kMaxSizeForGlyphCache, assuming the above + * constraints, but since we ask for unhinted paths, the two values + * need not match per-se. + */ + kCanonicalTextSizeForPaths = 64, + + /* + * Above this size (taking into account CTM and textSize), we never use + * the cache for bits or metrics (we might overflow), so we just ask + * for a caononical size and post-transform that. + */ + kMaxSizeForGlyphCache = 256, }; + + static bool TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM); + + bool tooBigToUseCache() const; + bool tooBigToUseCache(const SkMatrix& ctm) const; + + // Set flags/hinting/textSize up to use for drawing text as paths. + // Returns scale factor to restore the original textSize, since will will + // have change it to kCanonicalTextSizeForPaths. + SkScalar setupForAsPaths(); + + static SkScalar MaxCacheSize2() { + static const SkScalar kMaxSize = SkIntToScalar(kMaxSizeForGlyphCache); + static const SkScalar kMag2Max = kMaxSize * kMaxSize; + return kMag2Max; + } + friend class SkAutoGlyphCache; friend class SkCanvas; friend class SkDraw; friend class SkGraphics; // So Term() can be called. friend class SkPDFDevice; friend class SkTextToPathIter; + friend class SkCanonicalizePaint; #ifdef SK_BUILD_FOR_ANDROID SkPaintOptionsAndroid fPaintOptionsAndroid; diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp index fc615ab..f313c4b 100644 --- a/src/core/SkDraw.cpp +++ b/src/core/SkDraw.cpp @@ -31,6 +31,22 @@ #include "SkDrawProcs.h" #include "SkMatrixUtils.h" +bool SkDraw::ShouldDrawTextAsPaths(const SkPaint& paint, const SkMatrix& ctm) { + // we don't cache hairlines in the cache + if (SkPaint::kStroke_Style == paint.getStyle() && + 0 == paint.getStrokeWidth()) { + return true; + } + + // we don't cache perspective + if (ctm.hasPerspective()) { + return true; + } + + SkMatrix textM; + return SkPaint::TooBigToUseCache(ctm, *paint.setTextMatrix(&textM)); +} + //#define TRACE_BITMAP_DRAWS #define kBlitterStorageLongCount (sizeof(SkBitmapProcShader) >> 2) @@ -1663,9 +1679,7 @@ void SkDraw::drawText(const char text[], size_t byteLength, // SkScalarRec doesn't currently have a way of representing hairline stroke and // will fill if its frame-width is 0. - if (/*paint.isLinearText() ||*/ - (fMatrix->hasPerspective()) || - (0 == paint.getStrokeWidth() && SkPaint::kStroke_Style == paint.getStyle())) { + if (ShouldDrawTextAsPaths(paint, *fMatrix)) { this->drawText_asPaths(text, byteLength, x, y, paint); return; } @@ -1839,6 +1853,50 @@ TextMapState::Proc TextMapState::pickProc(int scalarsPerPosition) { ////////////////////////////////////////////////////////////////////////////// +void SkDraw::drawPosText_asPaths(const char text[], size_t byteLength, + const SkScalar pos[], SkScalar constY, + int scalarsPerPosition, + const SkPaint& origPaint) const { + // setup our std paint, in hopes of getting hits in the cache + SkPaint paint(origPaint); + SkScalar matrixScale = paint.setupForAsPaths(); + + SkDraw draw(*this); + + // Now modify our matrix to account for the canonical text size + SkMatrix matrix = *fMatrix; + matrix.preScale(matrixScale, matrixScale); + const SkScalar posScale = SkScalarInvert(matrixScale); + + SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc(); + SkAutoGlyphCache autoCache(paint, NULL, NULL); + SkGlyphCache* cache = autoCache.getCache(); + + const char* stop = text + byteLength; + AlignProc alignProc = pick_align_proc(paint.getTextAlign()); + TextMapState tms(SkMatrix::I(), constY); + TextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition); + + while (text < stop) { + const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); + if (glyph.fWidth) { + const SkPath* path = cache->findPath(glyph); + if (path) { + tmsProc(tms, pos); + SkIPoint fixedLoc; + alignProc(tms.fLoc, glyph, &fixedLoc); + + SkMatrix localMatrix = matrix; + localMatrix.preTranslate(SkFixedToScalar(fixedLoc.fX) * posScale, + SkFixedToScalar(fixedLoc.fY) * posScale); + draw.fMatrix = &localMatrix; + draw.drawPath(*path, paint); + } + } + pos += scalarsPerPosition; + } +} + void SkDraw::drawPosText(const char text[], size_t byteLength, const SkScalar pos[], SkScalar constY, int scalarsPerPosition, const SkPaint& paint) const { @@ -1852,10 +1910,9 @@ void SkDraw::drawPosText(const char text[], size_t byteLength, return; } - if (/*paint.isLinearText() ||*/ - (fMatrix->hasPerspective())) { - // TODO !!!! -// this->drawText_asPaths(text, byteLength, x, y, paint); + if (ShouldDrawTextAsPaths(paint, *fMatrix)) { + this->drawPosText_asPaths(text, byteLength, pos, constY, + scalarsPerPosition, paint); return; } diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index 9b6b883..e781cd4 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -30,6 +30,7 @@ #include "SkStroke.h" #include "SkTextFormatParams.h" #include "SkTextToPathIter.h" +#include "SkTLazy.h" #include "SkTypeface.h" #include "SkXfermode.h" @@ -425,6 +426,37 @@ SkAnnotation* SkPaint::setAnnotation(SkAnnotation* annotation) { /////////////////////////////////////////////////////////////////////////////// +static SkScalar mag2(SkScalar x, SkScalar y) { + return x * x + y * y; +} + +static bool tooBig(const SkMatrix& m, SkScalar ma2max) { + return mag2(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewY]) > ma2max + || + mag2(m[SkMatrix::kMSkewX], m[SkMatrix::kMScaleY]) > ma2max; +} + +bool SkPaint::TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM) { + SkASSERT(!ctm.hasPerspective()); + SkASSERT(!textM.hasPerspective()); + + SkMatrix matrix; + matrix.setConcat(ctm, textM); + return tooBig(matrix, MaxCacheSize2()); +} + +bool SkPaint::tooBigToUseCache(const SkMatrix& ctm) const { + SkMatrix textM; + return TooBigToUseCache(ctm, *this->setTextMatrix(&textM)); +} + +bool SkPaint::tooBigToUseCache() const { + SkMatrix textM; + return tooBig(*this->setTextMatrix(&textM), MaxCacheSize2()); +} + +/////////////////////////////////////////////////////////////////////////////// + #include "SkGlyphCache.h" #include "SkUtils.h" @@ -915,33 +947,51 @@ SkDrawCacheProc SkPaint::getDrawCacheProc() const { /////////////////////////////////////////////////////////////////////////////// -class SkAutoRestorePaintTextSizeAndFrame { +#define TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE ( \ +SkPaint::kDevKernText_Flag | \ +SkPaint::kLinearText_Flag | \ +SkPaint::kLCDRenderText_Flag | \ +SkPaint::kEmbeddedBitmapText_Flag | \ +SkPaint::kAutoHinting_Flag | \ +SkPaint::kGenA8FromLCD_Flag ) + +SkScalar SkPaint::setupForAsPaths() { + uint32_t flags = this->getFlags(); + // clear the flags we don't care about + flags &= ~TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE; + // set the flags we do care about + flags |= SkPaint::kSubpixelText_Flag; + + this->setFlags(flags); + this->setHinting(SkPaint::kNo_Hinting); + + SkScalar textSize = fTextSize; + this->setTextSize(kCanonicalTextSizeForPaths); + return textSize / kCanonicalTextSizeForPaths; +} + +class SkCanonicalizePaint { public: - SkAutoRestorePaintTextSizeAndFrame(const SkPaint* paint) - : fPaint((SkPaint*)paint) { -#ifdef SK_BUILD_FOR_ANDROID - fGenerationID = fPaint->getGenerationID(); -#endif - fTextSize = paint->getTextSize(); - fStyle = paint->getStyle(); - fPaint->setStyle(SkPaint::kFill_Style); + SkCanonicalizePaint(const SkPaint& paint) : fPaint(&paint), fScale(0) { + if (paint.isLinearText() || paint.tooBigToUseCache()) { + SkPaint* p = fLazy.set(paint); + fScale = p->setupForAsPaths(); + fPaint = p; + } } - ~SkAutoRestorePaintTextSizeAndFrame() { - fPaint->setStyle(fStyle); - fPaint->setTextSize(fTextSize); -#ifdef SK_BUILD_FOR_ANDROID - fPaint->setGenerationID(fGenerationID); -#endif - } + const SkPaint& getPaint() const { return *fPaint; } + + /** + * Returns 0 if the paint was unmodified, or the scale factor need to + * the original textSize + */ + SkScalar getScale() const { return fScale; } private: - SkPaint* fPaint; - SkScalar fTextSize; - SkPaint::Style fStyle; -#ifdef SK_BUILD_FOR_ANDROID - uint32_t fGenerationID; -#endif + const SkPaint* fPaint; + SkScalar fScale; + SkTLazy fLazy; }; static void set_bounds(const SkGlyph& g, SkRect* bounds) { @@ -1069,14 +1119,9 @@ SkScalar SkPaint::measureText(const void* textData, size_t length, const char* text = (const char*)textData; SkASSERT(text != NULL || length == 0); - SkScalar scale = 0; - SkAutoRestorePaintTextSizeAndFrame restore(this); - - if (this->isLinearText()) { - scale = fTextSize / kCanonicalTextSizeForPaths; - // this gets restored by restore - ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths)); - } + SkCanonicalizePaint canon(*this); + const SkPaint& paint = canon.getPaint(); + SkScalar scale = canon.getScale(); SkMatrix zoomMatrix, *zoomPtr = NULL; if (zoom) { @@ -1084,7 +1129,7 @@ SkScalar SkPaint::measureText(const void* textData, size_t length, zoomPtr = &zoomMatrix; } - SkAutoGlyphCache autoCache(*this, NULL, zoomPtr); + SkAutoGlyphCache autoCache(paint, NULL, zoomPtr); SkGlyphCache* cache = autoCache.getCache(); SkScalar width = 0; @@ -1092,7 +1137,7 @@ SkScalar SkPaint::measureText(const void* textData, size_t length, if (length > 0) { int tempCount; - width = this->measure_text(cache, text, length, &tempCount, bounds); + width = paint.measure_text(cache, text, length, &tempCount, bounds); if (scale) { width = SkScalarMul(width, scale); if (bounds) { @@ -1153,23 +1198,22 @@ size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth, SkASSERT(textD != NULL); const char* text = (const char*)textD; - SkScalar scale = 0; - SkAutoRestorePaintTextSizeAndFrame restore(this); + SkCanonicalizePaint canon(*this); + const SkPaint& paint = canon.getPaint(); + SkScalar scale = canon.getScale(); - if (this->isLinearText()) { - scale = fTextSize / kCanonicalTextSizeForPaths; - maxWidth = SkScalarMulDiv(maxWidth, kCanonicalTextSizeForPaths, fTextSize); - // this gets restored by restore - ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths)); + // adjust max in case we changed the textSize in paint + if (scale) { + maxWidth /= scale; } - SkAutoGlyphCache autoCache(*this, NULL, NULL); + SkAutoGlyphCache autoCache(paint, NULL, NULL); SkGlyphCache* cache = autoCache.getCache(); - SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(tbd, false); + SkMeasureCacheProc glyphCacheProc = paint.getMeasureCacheProc(tbd, false); const char* stop; SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop); - const int xyIndex = this->isVerticalText() ? 1 : 0; + const int xyIndex = paint.isVerticalText() ? 1 : 0; // use 64bits for our accumulator, to avoid overflowing 16.16 Sk48Dot16 max = SkScalarToFixed(maxWidth); Sk48Dot16 width = 0; @@ -1227,15 +1271,10 @@ static void FontMetricsDescProc(SkTypeface* typeface, const SkDescriptor* desc, } SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const { - SkScalar scale = 0; - SkAutoRestorePaintTextSizeAndFrame restore(this); - - if (this->isLinearText()) { - scale = fTextSize / kCanonicalTextSizeForPaths; - // this gets restored by restore - ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths)); - } - + SkCanonicalizePaint canon(*this); + const SkPaint& paint = canon.getPaint(); + SkScalar scale = canon.getScale(); + SkMatrix zoomMatrix, *zoomPtr = NULL; if (zoom) { zoomMatrix.setScale(zoom, zoom); @@ -1247,7 +1286,7 @@ SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const { metrics = &storage; } - this->descriptorProc(NULL, zoomPtr, FontMetricsDescProc, metrics, true); + paint.descriptorProc(NULL, zoomPtr, FontMetricsDescProc, metrics, true); if (scale) { metrics->fTop = SkScalarMul(metrics->fTop, scale); @@ -1284,25 +1323,20 @@ int SkPaint::getTextWidths(const void* textData, size_t byteLength, return this->countText(textData, byteLength); } - SkAutoRestorePaintTextSizeAndFrame restore(this); - SkScalar scale = 0; - - if (this->isLinearText()) { - scale = fTextSize / kCanonicalTextSizeForPaths; - // this gets restored by restore - ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths)); - } + SkCanonicalizePaint canon(*this); + const SkPaint& paint = canon.getPaint(); + SkScalar scale = canon.getScale(); - SkAutoGlyphCache autoCache(*this, NULL, NULL); + SkAutoGlyphCache autoCache(paint, NULL, NULL); SkGlyphCache* cache = autoCache.getCache(); SkMeasureCacheProc glyphCacheProc; - glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection, + glyphCacheProc = paint.getMeasureCacheProc(kForward_TextBufferDirection, NULL != bounds); const char* text = (const char*)textData; const char* stop = text + byteLength; int count = 0; - const int xyIndex = this->isVerticalText() ? 1 : 0; + const int xyIndex = paint.isVerticalText() ? 1 : 0; if (this->isDevKernText()) { // we adjust the widths returned here through auto-kerning diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp index 1f71bf0..521cb9b 100644 --- a/src/core/SkScalerContext.cpp +++ b/src/core/SkScalerContext.cpp @@ -791,10 +791,7 @@ void SkScalerContextRec::getMatrixFrom2x2(SkMatrix* dst) const { } void SkScalerContextRec::getLocalMatrix(SkMatrix* m) const { - m->setScale(SkScalarMul(fTextSize, fPreScaleX), fTextSize); - if (fPreSkewX) { - m->postSkew(fPreSkewX, 0); - } + SkPaint::SetTextMatrix(m, fTextSize, fPreScaleX, fPreSkewX); } void SkScalerContextRec::getSingleMatrix(SkMatrix* m) const {