#include "SkColor.h"
#include "SkDrawLooper.h"
+#include "SkMatrix.h"
#include "SkXfermode.h"
#ifdef SK_BUILD_FOR_ANDROID
#include "SkPaintOptionsAndroid.h"
class SkGlyphCache;
class SkImageFilter;
class SkMaskFilter;
-class SkMatrix;
class SkPath;
class SkPathEffect;
struct SkPoint;
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:
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;
#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)
// 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;
}
//////////////////////////////////////////////////////////////////////////////
+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 {
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;
}
#include "SkStroke.h"
#include "SkTextFormatParams.h"
#include "SkTextToPathIter.h"
+#include "SkTLazy.h"
#include "SkTypeface.h"
#include "SkXfermode.h"
///////////////////////////////////////////////////////////////////////////////
+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"
///////////////////////////////////////////////////////////////////////////////
-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<SkPaint> fLazy;
};
static void set_bounds(const SkGlyph& g, SkRect* bounds) {
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) {
zoomPtr = &zoomMatrix;
}
- SkAutoGlyphCache autoCache(*this, NULL, zoomPtr);
+ SkAutoGlyphCache autoCache(paint, NULL, zoomPtr);
SkGlyphCache* cache = autoCache.getCache();
SkScalar width = 0;
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) {
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;
}
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);
metrics = &storage;
}
- this->descriptorProc(NULL, zoomPtr, FontMetricsDescProc, metrics, true);
+ paint.descriptorProc(NULL, zoomPtr, FontMetricsDescProc, metrics, true);
if (scale) {
metrics->fTop = SkScalarMul(metrics->fTop, scale);
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