зеркало из https://github.com/mozilla/moz-skia.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
This commit is contained in:
Родитель
db29a310f7
Коммит
ed43dffbf1
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<SkPaint> 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;
|
||||
SkCanonicalizePaint canon(*this);
|
||||
const SkPaint& paint = canon.getPaint();
|
||||
SkScalar scale = canon.getScale();
|
||||
|
||||
if (this->isLinearText()) {
|
||||
scale = fTextSize / kCanonicalTextSizeForPaths;
|
||||
// this gets restored by restore
|
||||
((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -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 {
|
||||
|
|
Загрузка…
Ссылка в новой задаче