remove old text contexts and fontcache
BUG=skia: Review URL: https://codereview.chromium.org/1104343003
This commit is contained in:
parent
438de49857
commit
8f94bb2b25
@ -72,8 +72,6 @@
|
|||||||
'<(skia_src_path)/gpu/GrBatchFontCache.h',
|
'<(skia_src_path)/gpu/GrBatchFontCache.h',
|
||||||
'<(skia_src_path)/gpu/GrBatchTarget.cpp',
|
'<(skia_src_path)/gpu/GrBatchTarget.cpp',
|
||||||
'<(skia_src_path)/gpu/GrBatchTarget.h',
|
'<(skia_src_path)/gpu/GrBatchTarget.h',
|
||||||
'<(skia_src_path)/gpu/GrBitmapTextContext.cpp',
|
|
||||||
'<(skia_src_path)/gpu/GrBitmapTextContext.h',
|
|
||||||
'<(skia_src_path)/gpu/GrBlend.cpp',
|
'<(skia_src_path)/gpu/GrBlend.cpp',
|
||||||
'<(skia_src_path)/gpu/GrBlend.h',
|
'<(skia_src_path)/gpu/GrBlend.h',
|
||||||
'<(skia_src_path)/gpu/GrBufferAllocPool.cpp',
|
'<(skia_src_path)/gpu/GrBufferAllocPool.cpp',
|
||||||
@ -91,8 +89,6 @@
|
|||||||
'<(skia_src_path)/gpu/GrDefaultGeoProcFactory.h',
|
'<(skia_src_path)/gpu/GrDefaultGeoProcFactory.h',
|
||||||
'<(skia_src_path)/gpu/GrDefaultPathRenderer.cpp',
|
'<(skia_src_path)/gpu/GrDefaultPathRenderer.cpp',
|
||||||
'<(skia_src_path)/gpu/GrDefaultPathRenderer.h',
|
'<(skia_src_path)/gpu/GrDefaultPathRenderer.h',
|
||||||
'<(skia_src_path)/gpu/GrDistanceFieldTextContext.h',
|
|
||||||
'<(skia_src_path)/gpu/GrDistanceFieldTextContext.cpp',
|
|
||||||
'<(skia_src_path)/gpu/GrDrawTarget.cpp',
|
'<(skia_src_path)/gpu/GrDrawTarget.cpp',
|
||||||
'<(skia_src_path)/gpu/GrDrawTarget.h',
|
'<(skia_src_path)/gpu/GrDrawTarget.h',
|
||||||
'<(skia_src_path)/gpu/GrDrawTargetCaps.h',
|
'<(skia_src_path)/gpu/GrDrawTargetCaps.h',
|
||||||
@ -198,8 +194,6 @@
|
|||||||
'<(skia_src_path)/gpu/GrTextBlobCache.h',
|
'<(skia_src_path)/gpu/GrTextBlobCache.h',
|
||||||
'<(skia_src_path)/gpu/GrTextContext.cpp',
|
'<(skia_src_path)/gpu/GrTextContext.cpp',
|
||||||
'<(skia_src_path)/gpu/GrTextContext.h',
|
'<(skia_src_path)/gpu/GrTextContext.h',
|
||||||
'<(skia_src_path)/gpu/GrFontCache.cpp',
|
|
||||||
'<(skia_src_path)/gpu/GrFontCache.h',
|
|
||||||
'<(skia_src_path)/gpu/GrTexture.cpp',
|
'<(skia_src_path)/gpu/GrTexture.cpp',
|
||||||
'<(skia_src_path)/gpu/GrTexturePriv.h',
|
'<(skia_src_path)/gpu/GrTexturePriv.h',
|
||||||
'<(skia_src_path)/gpu/GrTextureAccess.cpp',
|
'<(skia_src_path)/gpu/GrTextureAccess.cpp',
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
class GrAARectRenderer;
|
class GrAARectRenderer;
|
||||||
class GrBatchFontCache;
|
class GrBatchFontCache;
|
||||||
class GrDrawTarget;
|
class GrDrawTarget;
|
||||||
class GrFontCache;
|
|
||||||
class GrFragmentProcessor;
|
class GrFragmentProcessor;
|
||||||
class GrGpu;
|
class GrGpu;
|
||||||
class GrGpuTraceMarker;
|
class GrGpuTraceMarker;
|
||||||
@ -647,16 +646,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
void discardRenderTarget(GrRenderTarget*);
|
void discardRenderTarget(GrRenderTarget*);
|
||||||
|
|
||||||
#ifdef SK_DEVELOPER
|
|
||||||
void dumpFontCache() const;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Functions intended for internal use only.
|
// Functions intended for internal use only.
|
||||||
GrGpu* getGpu() { return fGpu; }
|
GrGpu* getGpu() { return fGpu; }
|
||||||
const GrGpu* getGpu() const { return fGpu; }
|
const GrGpu* getGpu() const { return fGpu; }
|
||||||
GrBatchFontCache* getBatchFontCache() { return fBatchFontCache; }
|
GrBatchFontCache* getBatchFontCache() { return fBatchFontCache; }
|
||||||
GrFontCache* getFontCache() { return fFontCache; }
|
|
||||||
GrLayerCache* getLayerCache() { return fLayerCache.get(); }
|
GrLayerCache* getLayerCache() { return fLayerCache.get(); }
|
||||||
GrTextBlobCache* getTextBlobCache() { return fTextBlobCache; }
|
GrTextBlobCache* getTextBlobCache() { return fTextBlobCache; }
|
||||||
GrDrawTarget* getTextTarget();
|
GrDrawTarget* getTextTarget();
|
||||||
@ -699,7 +693,6 @@ private:
|
|||||||
|
|
||||||
GrResourceCache* fResourceCache;
|
GrResourceCache* fResourceCache;
|
||||||
GrBatchFontCache* fBatchFontCache;
|
GrBatchFontCache* fBatchFontCache;
|
||||||
GrFontCache* fFontCache;
|
|
||||||
SkAutoTDelete<GrLayerCache> fLayerCache;
|
SkAutoTDelete<GrLayerCache> fLayerCache;
|
||||||
SkAutoTDelete<GrTextBlobCache> fTextBlobCache;
|
SkAutoTDelete<GrTextBlobCache> fTextBlobCache;
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
*/
|
*/
|
||||||
#include "GrAtlasTextContext.h"
|
#include "GrAtlasTextContext.h"
|
||||||
|
|
||||||
#include "GrAtlas.h"
|
|
||||||
#include "GrBatch.h"
|
#include "GrBatch.h"
|
||||||
#include "GrBatchFontCache.h"
|
#include "GrBatchFontCache.h"
|
||||||
#include "GrBatchTarget.h"
|
#include "GrBatchTarget.h"
|
||||||
|
@ -1,647 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013 Google Inc.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by a BSD-style license that can be
|
|
||||||
* found in the LICENSE file.
|
|
||||||
*/
|
|
||||||
#include "GrBitmapTextContext.h"
|
|
||||||
|
|
||||||
#include "GrAtlas.h"
|
|
||||||
#include "GrBatch.h"
|
|
||||||
#include "GrBatchFontCache.h"
|
|
||||||
#include "GrBatchTarget.h"
|
|
||||||
#include "GrDefaultGeoProcFactory.h"
|
|
||||||
#include "GrDrawTarget.h"
|
|
||||||
#include "GrFontCache.h"
|
|
||||||
#include "GrFontScaler.h"
|
|
||||||
#include "GrIndexBuffer.h"
|
|
||||||
#include "GrStrokeInfo.h"
|
|
||||||
#include "GrTexturePriv.h"
|
|
||||||
|
|
||||||
#include "SkAutoKern.h"
|
|
||||||
#include "SkColorPriv.h"
|
|
||||||
#include "SkDraw.h"
|
|
||||||
#include "SkDrawFilter.h"
|
|
||||||
#include "SkDrawProcs.h"
|
|
||||||
#include "SkGlyphCache.h"
|
|
||||||
#include "SkGpuDevice.h"
|
|
||||||
#include "SkGr.h"
|
|
||||||
#include "SkPath.h"
|
|
||||||
#include "SkRTConf.h"
|
|
||||||
#include "SkStrokeRec.h"
|
|
||||||
#include "SkTextBlob.h"
|
|
||||||
#include "SkTextMapStateProc.h"
|
|
||||||
|
|
||||||
#include "effects/GrBitmapTextGeoProc.h"
|
|
||||||
#include "effects/GrSimpleTextureEffect.h"
|
|
||||||
|
|
||||||
SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
|
|
||||||
"Dump the contents of the font cache before every purge.");
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
|
|
||||||
|
|
||||||
// position + local coord
|
|
||||||
static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
|
|
||||||
|
|
||||||
static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
|
|
||||||
|
|
||||||
static const int kVerticesPerGlyph = 4;
|
|
||||||
static const int kIndicesPerGlyph = 6;
|
|
||||||
|
|
||||||
static size_t get_vertex_stride(GrMaskFormat maskFormat) {
|
|
||||||
switch (maskFormat) {
|
|
||||||
case kA8_GrMaskFormat:
|
|
||||||
return kGrayTextVASize;
|
|
||||||
case kARGB_GrMaskFormat:
|
|
||||||
return kColorTextVASize;
|
|
||||||
default:
|
|
||||||
return kLCDTextVASize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
GrBitmapTextContext::GrBitmapTextContext(GrContext* context,
|
|
||||||
SkGpuDevice* gpuDevice,
|
|
||||||
const SkDeviceProperties& properties)
|
|
||||||
: GrTextContext(context, gpuDevice, properties) {
|
|
||||||
fStrike = NULL;
|
|
||||||
|
|
||||||
fCurrTexture = NULL;
|
|
||||||
fEffectTextureUniqueID = SK_InvalidUniqueID;
|
|
||||||
|
|
||||||
fVertices = NULL;
|
|
||||||
fCurrVertex = 0;
|
|
||||||
fAllocVertexCount = 0;
|
|
||||||
fTotalVertexCount = 0;
|
|
||||||
|
|
||||||
fVertexBounds.setLargestInverted();
|
|
||||||
}
|
|
||||||
|
|
||||||
GrBitmapTextContext* GrBitmapTextContext::Create(GrContext* context,
|
|
||||||
SkGpuDevice* gpuDevice,
|
|
||||||
const SkDeviceProperties& props) {
|
|
||||||
return SkNEW_ARGS(GrBitmapTextContext, (context, gpuDevice, props));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GrBitmapTextContext::canDraw(const GrRenderTarget* rt,
|
|
||||||
const GrClip& clip,
|
|
||||||
const GrPaint& paint,
|
|
||||||
const SkPaint& skPaint,
|
|
||||||
const SkMatrix& viewMatrix) {
|
|
||||||
return !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void GrBitmapTextContext::init(GrRenderTarget* rt, const GrClip& clip,
|
|
||||||
const GrPaint& paint, const SkPaint& skPaint,
|
|
||||||
const SkIRect& regionClipBounds) {
|
|
||||||
GrTextContext::init(rt, clip, paint, skPaint, regionClipBounds);
|
|
||||||
|
|
||||||
fStrike = NULL;
|
|
||||||
|
|
||||||
fCurrTexture = NULL;
|
|
||||||
fCurrVertex = 0;
|
|
||||||
|
|
||||||
fVertices = NULL;
|
|
||||||
fAllocVertexCount = 0;
|
|
||||||
fTotalVertexCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrBitmapTextContext::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 /*|| fRC->isEmpty()*/) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->init(rt, clip, paint, skPaint, regionClipBounds);
|
|
||||||
|
|
||||||
SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
|
|
||||||
|
|
||||||
SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &viewMatrix);
|
|
||||||
SkGlyphCache* cache = autoCache.getCache();
|
|
||||||
GrFontScaler* fontScaler = GetGrFontScaler(cache);
|
|
||||||
|
|
||||||
// transform our starting point
|
|
||||||
{
|
|
||||||
SkPoint loc;
|
|
||||||
viewMatrix.mapXY(x, y, &loc);
|
|
||||||
x = loc.fX;
|
|
||||||
y = loc.fY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// need to measure first
|
|
||||||
int numGlyphs;
|
|
||||||
if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
|
|
||||||
SkVector stopVector;
|
|
||||||
numGlyphs = MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
|
|
||||||
|
|
||||||
SkScalar stopX = stopVector.fX;
|
|
||||||
SkScalar stopY = stopVector.fY;
|
|
||||||
|
|
||||||
if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
|
|
||||||
stopX = SkScalarHalf(stopX);
|
|
||||||
stopY = SkScalarHalf(stopY);
|
|
||||||
}
|
|
||||||
x -= stopX;
|
|
||||||
y -= stopY;
|
|
||||||
} else {
|
|
||||||
numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
|
|
||||||
}
|
|
||||||
fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
|
|
||||||
|
|
||||||
const char* stop = text + byteLength;
|
|
||||||
|
|
||||||
SkAutoKern autokern;
|
|
||||||
|
|
||||||
SkFixed fxMask = ~0;
|
|
||||||
SkFixed fyMask = ~0;
|
|
||||||
SkScalar halfSampleX, halfSampleY;
|
|
||||||
if (cache->isSubpixel()) {
|
|
||||||
halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
|
|
||||||
SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
|
|
||||||
if (kX_SkAxisAlignment == baseline) {
|
|
||||||
fyMask = 0;
|
|
||||||
halfSampleY = SK_ScalarHalf;
|
|
||||||
} else if (kY_SkAxisAlignment == baseline) {
|
|
||||||
fxMask = 0;
|
|
||||||
halfSampleX = SK_ScalarHalf;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
halfSampleX = halfSampleY = SK_ScalarHalf;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
|
|
||||||
Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
|
|
||||||
|
|
||||||
// if we have RGB, then we won't have any SkShaders so no need to use a localmatrix, but for
|
|
||||||
// performance reasons we just invert here instead
|
|
||||||
if (!viewMatrix.invert(&fLocalMatrix)) {
|
|
||||||
SkDebugf("Cannot invert viewmatrix\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (text < stop) {
|
|
||||||
const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
|
|
||||||
|
|
||||||
fx += autokern.adjust(glyph);
|
|
||||||
|
|
||||||
if (glyph.fWidth) {
|
|
||||||
this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
|
|
||||||
glyph.getSubXFixed(),
|
|
||||||
glyph.getSubYFixed(),
|
|
||||||
GrGlyph::kCoverage_MaskStyle),
|
|
||||||
Sk48Dot16FloorToInt(fx),
|
|
||||||
Sk48Dot16FloorToInt(fy),
|
|
||||||
fontScaler);
|
|
||||||
}
|
|
||||||
|
|
||||||
fx += glyph.fAdvanceX;
|
|
||||||
fy += glyph.fAdvanceY;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrBitmapTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
|
|
||||||
const GrPaint& paint, const SkPaint& skPaint,
|
|
||||||
const SkMatrix& viewMatrix,
|
|
||||||
const char text[], size_t byteLength,
|
|
||||||
const SkScalar pos[], int scalarsPerPosition,
|
|
||||||
const SkPoint& offset, const SkIRect& regionClipBounds) {
|
|
||||||
SkASSERT(byteLength == 0 || text != NULL);
|
|
||||||
SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
|
|
||||||
|
|
||||||
// nothing to draw
|
|
||||||
if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->init(rt, clip, paint, skPaint, regionClipBounds);
|
|
||||||
|
|
||||||
SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
|
|
||||||
|
|
||||||
SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &viewMatrix);
|
|
||||||
SkGlyphCache* cache = autoCache.getCache();
|
|
||||||
GrFontScaler* fontScaler = GetGrFontScaler(cache);
|
|
||||||
|
|
||||||
// if we have RGB, then we won't have any SkShaders so no need to use a localmatrix, but for
|
|
||||||
// performance reasons we just invert here instead
|
|
||||||
if (!viewMatrix.invert(&fLocalMatrix)) {
|
|
||||||
SkDebugf("Cannot invert viewmatrix\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
|
|
||||||
fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
|
|
||||||
|
|
||||||
const char* stop = text + byteLength;
|
|
||||||
SkTextAlignProc alignProc(fSkPaint.getTextAlign());
|
|
||||||
SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
|
|
||||||
|
|
||||||
if (cache->isSubpixel()) {
|
|
||||||
// maybe we should skip the rounding if linearText is set
|
|
||||||
SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
|
|
||||||
|
|
||||||
SkFixed fxMask = ~0;
|
|
||||||
SkFixed fyMask = ~0;
|
|
||||||
SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound);
|
|
||||||
SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
|
|
||||||
if (kX_SkAxisAlignment == baseline) {
|
|
||||||
fyMask = 0;
|
|
||||||
halfSampleY = SK_ScalarHalf;
|
|
||||||
} else if (kY_SkAxisAlignment == baseline) {
|
|
||||||
fxMask = 0;
|
|
||||||
halfSampleX = SK_ScalarHalf;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
|
|
||||||
while (text < stop) {
|
|
||||||
SkPoint tmsLoc;
|
|
||||||
tmsProc(pos, &tmsLoc);
|
|
||||||
Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
|
|
||||||
Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
|
|
||||||
|
|
||||||
const SkGlyph& glyph = glyphCacheProc(cache, &text,
|
|
||||||
fx & fxMask, fy & fyMask);
|
|
||||||
|
|
||||||
if (glyph.fWidth) {
|
|
||||||
this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
|
|
||||||
glyph.getSubXFixed(),
|
|
||||||
glyph.getSubYFixed(),
|
|
||||||
GrGlyph::kCoverage_MaskStyle),
|
|
||||||
Sk48Dot16FloorToInt(fx),
|
|
||||||
Sk48Dot16FloorToInt(fy),
|
|
||||||
fontScaler);
|
|
||||||
}
|
|
||||||
pos += scalarsPerPosition;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (text < stop) {
|
|
||||||
const char* currentText = text;
|
|
||||||
const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
|
|
||||||
|
|
||||||
if (metricGlyph.fWidth) {
|
|
||||||
SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
|
|
||||||
SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
|
|
||||||
SkPoint tmsLoc;
|
|
||||||
tmsProc(pos, &tmsLoc);
|
|
||||||
SkPoint alignLoc;
|
|
||||||
alignProc(tmsLoc, metricGlyph, &alignLoc);
|
|
||||||
|
|
||||||
Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
|
|
||||||
Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
|
|
||||||
|
|
||||||
// have to call again, now that we've been "aligned"
|
|
||||||
const SkGlyph& glyph = glyphCacheProc(cache, ¤tText,
|
|
||||||
fx & fxMask, fy & fyMask);
|
|
||||||
// the assumption is that the metrics haven't changed
|
|
||||||
SkASSERT(prevAdvX == glyph.fAdvanceX);
|
|
||||||
SkASSERT(prevAdvY == glyph.fAdvanceY);
|
|
||||||
SkASSERT(glyph.fWidth);
|
|
||||||
|
|
||||||
this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
|
|
||||||
glyph.getSubXFixed(),
|
|
||||||
glyph.getSubYFixed(),
|
|
||||||
GrGlyph::kCoverage_MaskStyle),
|
|
||||||
Sk48Dot16FloorToInt(fx),
|
|
||||||
Sk48Dot16FloorToInt(fy),
|
|
||||||
fontScaler);
|
|
||||||
}
|
|
||||||
pos += scalarsPerPosition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // not subpixel
|
|
||||||
|
|
||||||
if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
|
|
||||||
while (text < stop) {
|
|
||||||
// the last 2 parameters are ignored
|
|
||||||
const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
|
|
||||||
|
|
||||||
if (glyph.fWidth) {
|
|
||||||
SkPoint tmsLoc;
|
|
||||||
tmsProc(pos, &tmsLoc);
|
|
||||||
|
|
||||||
Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf);
|
|
||||||
Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf);
|
|
||||||
this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
|
|
||||||
glyph.getSubXFixed(),
|
|
||||||
glyph.getSubYFixed(),
|
|
||||||
GrGlyph::kCoverage_MaskStyle),
|
|
||||||
Sk48Dot16FloorToInt(fx),
|
|
||||||
Sk48Dot16FloorToInt(fy),
|
|
||||||
fontScaler);
|
|
||||||
}
|
|
||||||
pos += scalarsPerPosition;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (text < stop) {
|
|
||||||
// the last 2 parameters are ignored
|
|
||||||
const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
|
|
||||||
|
|
||||||
if (glyph.fWidth) {
|
|
||||||
SkPoint tmsLoc;
|
|
||||||
tmsProc(pos, &tmsLoc);
|
|
||||||
|
|
||||||
SkPoint alignLoc;
|
|
||||||
alignProc(tmsLoc, glyph, &alignLoc);
|
|
||||||
|
|
||||||
Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf);
|
|
||||||
Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf);
|
|
||||||
this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
|
|
||||||
glyph.getSubXFixed(),
|
|
||||||
glyph.getSubYFixed(),
|
|
||||||
GrGlyph::kCoverage_MaskStyle),
|
|
||||||
Sk48Dot16FloorToInt(fx),
|
|
||||||
Sk48Dot16FloorToInt(fy),
|
|
||||||
fontScaler);
|
|
||||||
}
|
|
||||||
pos += scalarsPerPosition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void* alloc_vertices(GrDrawTarget* drawTarget,
|
|
||||||
int numVertices,
|
|
||||||
GrMaskFormat maskFormat) {
|
|
||||||
if (numVertices <= 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up attributes
|
|
||||||
void* vertices = NULL;
|
|
||||||
bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
|
|
||||||
get_vertex_stride(maskFormat),
|
|
||||||
0,
|
|
||||||
&vertices,
|
|
||||||
NULL);
|
|
||||||
GrAlwaysAssert(success);
|
|
||||||
return vertices;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool GrBitmapTextContext::uploadGlyph(GrGlyph* glyph, GrFontScaler* scaler) {
|
|
||||||
if (!fStrike->glyphTooLargeForAtlas(glyph)) {
|
|
||||||
if (fStrike->addGlyphToAtlas(glyph, scaler)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to clear out an unused plot before we flush
|
|
||||||
if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
|
|
||||||
fStrike->addGlyphToAtlas(glyph, scaler)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c_DumpFontCache) {
|
|
||||||
#ifdef SK_DEVELOPER
|
|
||||||
fContext->getFontCache()->dump();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// before we purge the cache, we must flush any accumulated draws
|
|
||||||
this->flush();
|
|
||||||
fContext->flush();
|
|
||||||
|
|
||||||
// we should have an unused plot now
|
|
||||||
if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
|
|
||||||
fStrike->addGlyphToAtlas(glyph, scaler)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we should never get here
|
|
||||||
SkASSERT(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed,
|
|
||||||
int vx, int vy,
|
|
||||||
GrFontScaler* scaler) {
|
|
||||||
if (NULL == fDrawTarget) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL == fStrike) {
|
|
||||||
fStrike = fContext->getFontCache()->getStrike(scaler);
|
|
||||||
}
|
|
||||||
|
|
||||||
GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
|
|
||||||
if (NULL == glyph || glyph->fBounds.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int x = vx + glyph->fBounds.fLeft;
|
|
||||||
int y = vy + glyph->fBounds.fTop;
|
|
||||||
|
|
||||||
// keep them as ints until we've done the clip-test
|
|
||||||
int width = glyph->fBounds.width();
|
|
||||||
int height = glyph->fBounds.height();
|
|
||||||
|
|
||||||
// check if we clipped out
|
|
||||||
if (fClipRect.quickReject(x, y, x + width, y + height)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the glyph is too large we fall back to paths
|
|
||||||
if (NULL == glyph->fPlot && !uploadGlyph(glyph, scaler)) {
|
|
||||||
if (NULL == glyph->fPath) {
|
|
||||||
SkPath* path = SkNEW(SkPath);
|
|
||||||
if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
|
|
||||||
// flag the glyph as being dead?
|
|
||||||
delete path;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
glyph->fPath = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
// flush any accumulated draws before drawing this glyph as a path.
|
|
||||||
this->flush();
|
|
||||||
|
|
||||||
SkMatrix translate;
|
|
||||||
translate.setTranslate(SkIntToScalar(vx), SkIntToScalar(vy));
|
|
||||||
SkPath tmpPath(*glyph->fPath);
|
|
||||||
tmpPath.transform(translate);
|
|
||||||
GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
|
|
||||||
fContext->drawPath(fRenderTarget, fClip, fPaint, SkMatrix::I(), tmpPath, strokeInfo);
|
|
||||||
|
|
||||||
// remove this glyph from the vertices we need to allocate
|
|
||||||
fTotalVertexCount -= kVerticesPerGlyph;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SkASSERT(glyph->fPlot);
|
|
||||||
GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
|
|
||||||
glyph->fPlot->setDrawToken(drawToken);
|
|
||||||
|
|
||||||
// the current texture/maskformat must match what the glyph needs
|
|
||||||
GrTexture* texture = glyph->fPlot->texture();
|
|
||||||
SkASSERT(texture);
|
|
||||||
|
|
||||||
if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fAllocVertexCount) {
|
|
||||||
this->flush();
|
|
||||||
fCurrTexture = texture;
|
|
||||||
fCurrTexture->ref();
|
|
||||||
fCurrMaskFormat = glyph->fMaskFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL == fVertices) {
|
|
||||||
int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
|
|
||||||
fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
|
|
||||||
fVertices = alloc_vertices(fDrawTarget, fAllocVertexCount, fCurrMaskFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
SkRect r;
|
|
||||||
r.fLeft = SkIntToScalar(x);
|
|
||||||
r.fTop = SkIntToScalar(y);
|
|
||||||
r.fRight = r.fLeft + SkIntToScalar(width);
|
|
||||||
r.fBottom = r.fTop + SkIntToScalar(height);
|
|
||||||
|
|
||||||
fVertexBounds.joinNonEmptyArg(r);
|
|
||||||
|
|
||||||
int u0 = glyph->fAtlasLocation.fX;
|
|
||||||
int v0 = glyph->fAtlasLocation.fY;
|
|
||||||
int u1 = u0 + width;
|
|
||||||
int v1 = v0 + height;
|
|
||||||
|
|
||||||
size_t vertSize = get_vertex_stride(fCurrMaskFormat);
|
|
||||||
intptr_t vertex = reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex;
|
|
||||||
|
|
||||||
// V0
|
|
||||||
SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
|
|
||||||
position->set(r.fLeft, r.fTop);
|
|
||||||
if (kA8_GrMaskFormat == fCurrMaskFormat) {
|
|
||||||
SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
|
|
||||||
*color = fPaint.getColor();
|
|
||||||
}
|
|
||||||
SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize -
|
|
||||||
sizeof(SkIPoint16));
|
|
||||||
textureCoords->set(u0, v0);
|
|
||||||
vertex += vertSize;
|
|
||||||
|
|
||||||
// V1
|
|
||||||
position = reinterpret_cast<SkPoint*>(vertex);
|
|
||||||
position->set(r.fLeft, r.fBottom);
|
|
||||||
if (kA8_GrMaskFormat == fCurrMaskFormat) {
|
|
||||||
SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
|
|
||||||
*color = fPaint.getColor();
|
|
||||||
}
|
|
||||||
textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
|
|
||||||
textureCoords->set(u0, v1);
|
|
||||||
vertex += vertSize;
|
|
||||||
|
|
||||||
// V2
|
|
||||||
position = reinterpret_cast<SkPoint*>(vertex);
|
|
||||||
position->set(r.fRight, r.fBottom);
|
|
||||||
if (kA8_GrMaskFormat == fCurrMaskFormat) {
|
|
||||||
SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
|
|
||||||
*color = fPaint.getColor();
|
|
||||||
}
|
|
||||||
textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
|
|
||||||
textureCoords->set(u1, v1);
|
|
||||||
vertex += vertSize;
|
|
||||||
|
|
||||||
// V3
|
|
||||||
position = reinterpret_cast<SkPoint*>(vertex);
|
|
||||||
position->set(r.fRight, r.fTop);
|
|
||||||
if (kA8_GrMaskFormat == fCurrMaskFormat) {
|
|
||||||
SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
|
|
||||||
*color = fPaint.getColor();
|
|
||||||
}
|
|
||||||
textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
|
|
||||||
textureCoords->set(u1, v0);
|
|
||||||
|
|
||||||
fCurrVertex += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrBitmapTextContext::flush() {
|
|
||||||
if (NULL == fDrawTarget) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fCurrVertex > 0) {
|
|
||||||
GrPipelineBuilder pipelineBuilder;
|
|
||||||
pipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip);
|
|
||||||
|
|
||||||
// setup our sampler state for our text texture/atlas
|
|
||||||
SkASSERT(SkIsAlign4(fCurrVertex));
|
|
||||||
SkASSERT(fCurrTexture);
|
|
||||||
|
|
||||||
SkASSERT(fStrike);
|
|
||||||
GrColor color = fPaint.getColor();
|
|
||||||
switch (fCurrMaskFormat) {
|
|
||||||
// Color bitmap text
|
|
||||||
case kARGB_GrMaskFormat: {
|
|
||||||
int a = fSkPaint.getAlpha();
|
|
||||||
color = SkColorSetARGB(a, a, a, a);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// LCD text
|
|
||||||
case kA565_GrMaskFormat: {
|
|
||||||
// TODO: move supportsRGBCoverage check to setupCoverageEffect and only add LCD
|
|
||||||
// processor if the xp can support it. For now we will simply assume that if
|
|
||||||
// fUseLCDText is true, then we have a known color output.
|
|
||||||
const GrXPFactory* xpFactory = pipelineBuilder.getXPFactory();
|
|
||||||
if (!xpFactory->supportsRGBCoverage(0, kRGBA_GrColorComponentFlags)) {
|
|
||||||
SkDebugf("LCD Text will not draw correctly.\n");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Grayscale/BW text
|
|
||||||
case kA8_GrMaskFormat:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
SkFAIL("Unexpected mask format.");
|
|
||||||
}
|
|
||||||
|
|
||||||
GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
|
|
||||||
uint32_t textureUniqueID = fCurrTexture->getUniqueID();
|
|
||||||
if (textureUniqueID != fEffectTextureUniqueID ||
|
|
||||||
fCachedGeometryProcessor->color() != color ||
|
|
||||||
!fCachedGeometryProcessor->localMatrix().cheapEqualTo(fLocalMatrix)) {
|
|
||||||
// This will be ignored in the non A8 case
|
|
||||||
bool opaqueVertexColors = GrColorIsOpaque(fPaint.getColor());
|
|
||||||
fCachedGeometryProcessor.reset(GrBitmapTextGeoProc::Create(color,
|
|
||||||
fCurrTexture,
|
|
||||||
params,
|
|
||||||
fCurrMaskFormat,
|
|
||||||
opaqueVertexColors,
|
|
||||||
fLocalMatrix));
|
|
||||||
fEffectTextureUniqueID = textureUniqueID;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nGlyphs = fCurrVertex / kVerticesPerGlyph;
|
|
||||||
fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
|
|
||||||
fDrawTarget->drawIndexedInstances(&pipelineBuilder,
|
|
||||||
fCachedGeometryProcessor.get(),
|
|
||||||
kTriangles_GrPrimitiveType,
|
|
||||||
nGlyphs,
|
|
||||||
kVerticesPerGlyph,
|
|
||||||
kIndicesPerGlyph,
|
|
||||||
&fVertexBounds);
|
|
||||||
|
|
||||||
fDrawTarget->resetVertexSource();
|
|
||||||
fVertices = NULL;
|
|
||||||
fAllocVertexCount = 0;
|
|
||||||
// reset to be those that are left
|
|
||||||
fTotalVertexCount -= fCurrVertex;
|
|
||||||
fCurrVertex = 0;
|
|
||||||
fVertexBounds.setLargestInverted();
|
|
||||||
SkSafeSetNull(fCurrTexture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void GrBitmapTextContext::finish() {
|
|
||||||
this->flush();
|
|
||||||
fTotalVertexCount = 0;
|
|
||||||
|
|
||||||
GrTextContext::finish();
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013 Google Inc.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by a BSD-style license that can be
|
|
||||||
* found in the LICENSE file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef GrBitmapTextContext_DEFINED
|
|
||||||
#define GrBitmapTextContext_DEFINED
|
|
||||||
|
|
||||||
#include "GrTextContext.h"
|
|
||||||
|
|
||||||
#include "GrGeometryProcessor.h"
|
|
||||||
|
|
||||||
class GrTextStrike;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This class implements GrTextContext using standard bitmap fonts
|
|
||||||
*/
|
|
||||||
class GrBitmapTextContext : public GrTextContext {
|
|
||||||
public:
|
|
||||||
static GrBitmapTextContext* Create(GrContext*, SkGpuDevice*, const SkDeviceProperties&);
|
|
||||||
|
|
||||||
virtual ~GrBitmapTextContext() {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
GrTextStrike* fStrike;
|
|
||||||
void* fVertices;
|
|
||||||
int fCurrVertex;
|
|
||||||
int fAllocVertexCount;
|
|
||||||
int fTotalVertexCount;
|
|
||||||
SkRect fVertexBounds;
|
|
||||||
GrTexture* fCurrTexture;
|
|
||||||
GrMaskFormat fCurrMaskFormat;
|
|
||||||
SkAutoTUnref<const GrGeometryProcessor> fCachedGeometryProcessor;
|
|
||||||
// Used to check whether fCachedEffect is still valid.
|
|
||||||
uint32_t fEffectTextureUniqueID;
|
|
||||||
SkMatrix fLocalMatrix;
|
|
||||||
|
|
||||||
GrBitmapTextContext(GrContext*, SkGpuDevice*, const SkDeviceProperties&);
|
|
||||||
|
|
||||||
bool canDraw(const GrRenderTarget*, const GrClip&, const GrPaint&,
|
|
||||||
const SkPaint&, const SkMatrix& viewMatrix) override;
|
|
||||||
|
|
||||||
void onDrawText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
|
|
||||||
const SkMatrix& viewMatrix, const char text[], size_t byteLength,
|
|
||||||
SkScalar x, SkScalar y, const SkIRect& regionClipBounds) override;
|
|
||||||
void onDrawPosText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
|
|
||||||
const SkMatrix& viewMatrix,
|
|
||||||
const char text[], size_t byteLength,
|
|
||||||
const SkScalar pos[], int scalarsPerPosition,
|
|
||||||
const SkPoint& offset, const SkIRect& regionClipBounds) override;
|
|
||||||
|
|
||||||
void init(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
|
|
||||||
const SkIRect& regionClipBounds);
|
|
||||||
void appendGlyph(GrGlyph::PackedID, SkFixed left, SkFixed top, GrFontScaler*);
|
|
||||||
bool uploadGlyph(GrGlyph*, GrFontScaler*);
|
|
||||||
void flush(); // automatically called by destructor
|
|
||||||
void finish();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@ -15,10 +15,8 @@
|
|||||||
#include "GrBatchTarget.h"
|
#include "GrBatchTarget.h"
|
||||||
#include "GrBufferAllocPool.h"
|
#include "GrBufferAllocPool.h"
|
||||||
#include "GrDefaultGeoProcFactory.h"
|
#include "GrDefaultGeoProcFactory.h"
|
||||||
#include "GrFontCache.h"
|
|
||||||
#include "GrGpuResource.h"
|
#include "GrGpuResource.h"
|
||||||
#include "GrGpuResourcePriv.h"
|
#include "GrGpuResourcePriv.h"
|
||||||
#include "GrDistanceFieldTextContext.h"
|
|
||||||
#include "GrDrawTargetCaps.h"
|
#include "GrDrawTargetCaps.h"
|
||||||
#include "GrGpu.h"
|
#include "GrGpu.h"
|
||||||
#include "GrIndexBuffer.h"
|
#include "GrIndexBuffer.h"
|
||||||
@ -98,7 +96,6 @@ GrContext::GrContext(const Options& opts) : fOptions(opts) {
|
|||||||
fSoftwarePathRenderer = NULL;
|
fSoftwarePathRenderer = NULL;
|
||||||
fResourceCache = NULL;
|
fResourceCache = NULL;
|
||||||
fBatchFontCache = NULL;
|
fBatchFontCache = NULL;
|
||||||
fFontCache = NULL;
|
|
||||||
fDrawBuffer = NULL;
|
fDrawBuffer = NULL;
|
||||||
fDrawBufferVBAllocPool = NULL;
|
fDrawBufferVBAllocPool = NULL;
|
||||||
fDrawBufferIBAllocPool = NULL;
|
fDrawBufferIBAllocPool = NULL;
|
||||||
@ -123,8 +120,6 @@ void GrContext::initCommon() {
|
|||||||
fResourceCache = SkNEW(GrResourceCache);
|
fResourceCache = SkNEW(GrResourceCache);
|
||||||
fResourceCache->setOverBudgetCallback(OverBudgetCB, this);
|
fResourceCache->setOverBudgetCallback(OverBudgetCB, this);
|
||||||
|
|
||||||
fFontCache = SkNEW_ARGS(GrFontCache, (fGpu));
|
|
||||||
|
|
||||||
fLayerCache.reset(SkNEW_ARGS(GrLayerCache, (this)));
|
fLayerCache.reset(SkNEW_ARGS(GrLayerCache, (this)));
|
||||||
|
|
||||||
fAARectRenderer = SkNEW_ARGS(GrAARectRenderer, (fGpu));
|
fAARectRenderer = SkNEW_ARGS(GrAARectRenderer, (fGpu));
|
||||||
@ -153,7 +148,6 @@ GrContext::~GrContext() {
|
|||||||
|
|
||||||
SkDELETE(fResourceCache);
|
SkDELETE(fResourceCache);
|
||||||
SkDELETE(fBatchFontCache);
|
SkDELETE(fBatchFontCache);
|
||||||
SkDELETE(fFontCache);
|
|
||||||
SkDELETE(fDrawBuffer);
|
SkDELETE(fDrawBuffer);
|
||||||
SkDELETE(fDrawBufferVBAllocPool);
|
SkDELETE(fDrawBufferVBAllocPool);
|
||||||
SkDELETE(fDrawBufferIBAllocPool);
|
SkDELETE(fDrawBufferIBAllocPool);
|
||||||
@ -191,7 +185,6 @@ void GrContext::abandonContext() {
|
|||||||
fOvalRenderer->reset();
|
fOvalRenderer->reset();
|
||||||
|
|
||||||
fBatchFontCache->freeAll();
|
fBatchFontCache->freeAll();
|
||||||
fFontCache->freeAll();
|
|
||||||
fLayerCache->freeAll();
|
fLayerCache->freeAll();
|
||||||
fTextBlobCache->freeAll();
|
fTextBlobCache->freeAll();
|
||||||
}
|
}
|
||||||
@ -211,7 +204,6 @@ void GrContext::freeGpuResources() {
|
|||||||
fOvalRenderer->reset();
|
fOvalRenderer->reset();
|
||||||
|
|
||||||
fBatchFontCache->freeAll();
|
fBatchFontCache->freeAll();
|
||||||
fFontCache->freeAll();
|
|
||||||
fLayerCache->freeAll();
|
fLayerCache->freeAll();
|
||||||
// a path renderer may be holding onto resources
|
// a path renderer may be holding onto resources
|
||||||
SkSafeSetNull(fPathRendererChain);
|
SkSafeSetNull(fPathRendererChain);
|
||||||
@ -241,12 +233,7 @@ GrTextContext* GrContext::createTextContext(GrRenderTarget* renderTarget,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_BITMAP_TEXTBLOBS
|
|
||||||
return GrAtlasTextContext::Create(this, gpuDevice, leakyProperties, enableDistanceFieldFonts);
|
return GrAtlasTextContext::Create(this, gpuDevice, leakyProperties, enableDistanceFieldFonts);
|
||||||
#else
|
|
||||||
return GrDistanceFieldTextContext::Create(this, gpuDevice, leakyProperties,
|
|
||||||
enableDistanceFieldFonts);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -466,12 +453,6 @@ void GrContext::drawPaint(GrRenderTarget* rt,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SK_DEVELOPER
|
|
||||||
void GrContext::dumpFontCache() const {
|
|
||||||
fFontCache->dump();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static inline bool is_irect(const SkRect& r) {
|
static inline bool is_irect(const SkRect& r) {
|
||||||
|
@ -1,804 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013 Google Inc.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by a BSD-style license that can be
|
|
||||||
* found in the LICENSE file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "GrDistanceFieldTextContext.h"
|
|
||||||
#include "GrAtlas.h"
|
|
||||||
#include "GrAtlasTextContext.h"
|
|
||||||
#include "GrBitmapTextContext.h"
|
|
||||||
#include "GrDrawTarget.h"
|
|
||||||
#include "GrDrawTargetCaps.h"
|
|
||||||
#include "GrFontAtlasSizes.h"
|
|
||||||
#include "GrFontCache.h"
|
|
||||||
#include "GrFontScaler.h"
|
|
||||||
#include "GrGpu.h"
|
|
||||||
#include "GrIndexBuffer.h"
|
|
||||||
#include "GrStrokeInfo.h"
|
|
||||||
#include "GrTexturePriv.h"
|
|
||||||
|
|
||||||
#include "SkAutoKern.h"
|
|
||||||
#include "SkColorFilter.h"
|
|
||||||
#include "SkDistanceFieldGen.h"
|
|
||||||
#include "SkDraw.h"
|
|
||||||
#include "SkGlyphCache.h"
|
|
||||||
#include "SkGpuDevice.h"
|
|
||||||
#include "SkPath.h"
|
|
||||||
#include "SkRTConf.h"
|
|
||||||
#include "SkStrokeRec.h"
|
|
||||||
#include "effects/GrDistanceFieldGeoProc.h"
|
|
||||||
|
|
||||||
SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
|
|
||||||
"Dump the contents of the font cache before every purge.");
|
|
||||||
|
|
||||||
static const int kMinDFFontSize = 18;
|
|
||||||
static const int kSmallDFFontSize = 32;
|
|
||||||
static const int kSmallDFFontLimit = 32;
|
|
||||||
static const int kMediumDFFontSize = 72;
|
|
||||||
static const int kMediumDFFontLimit = 72;
|
|
||||||
static const int kLargeDFFontSize = 162;
|
|
||||||
|
|
||||||
static const int kVerticesPerGlyph = 4;
|
|
||||||
static const int kIndicesPerGlyph = 6;
|
|
||||||
|
|
||||||
#ifdef SK_DEBUG
|
|
||||||
static const int kExpectedDistanceAdjustTableSize = 8;
|
|
||||||
#endif
|
|
||||||
static const int kDistanceAdjustLumShift = 5;
|
|
||||||
|
|
||||||
GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
|
|
||||||
SkGpuDevice* gpuDevice,
|
|
||||||
const SkDeviceProperties& properties,
|
|
||||||
bool enable)
|
|
||||||
: GrTextContext(context, gpuDevice, properties) {
|
|
||||||
#if SK_FORCE_DISTANCE_FIELD_TEXT
|
|
||||||
fEnableDFRendering = true;
|
|
||||||
#else
|
|
||||||
fEnableDFRendering = enable;
|
|
||||||
#endif
|
|
||||||
fStrike = NULL;
|
|
||||||
fDistanceAdjustTable = NULL;
|
|
||||||
|
|
||||||
fEffectTextureUniqueID = SK_InvalidUniqueID;
|
|
||||||
fEffectColor = GrColor_ILLEGAL;
|
|
||||||
fEffectFlags = kInvalid_DistanceFieldEffectFlag;
|
|
||||||
|
|
||||||
fVertices = NULL;
|
|
||||||
fCurrVertex = 0;
|
|
||||||
fAllocVertexCount = 0;
|
|
||||||
fTotalVertexCount = 0;
|
|
||||||
fCurrTexture = NULL;
|
|
||||||
|
|
||||||
fVertexBounds.setLargestInverted();
|
|
||||||
}
|
|
||||||
|
|
||||||
GrDistanceFieldTextContext* GrDistanceFieldTextContext::Create(GrContext* context,
|
|
||||||
SkGpuDevice* gpuDevice,
|
|
||||||
const SkDeviceProperties& props,
|
|
||||||
bool enable) {
|
|
||||||
GrDistanceFieldTextContext* textContext = SkNEW_ARGS(GrDistanceFieldTextContext,
|
|
||||||
(context, gpuDevice, props, enable));
|
|
||||||
textContext->buildDistanceAdjustTable();
|
|
||||||
#ifdef USE_BITMAP_TEXTBLOBS
|
|
||||||
textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, gpuDevice, props,
|
|
||||||
enable);
|
|
||||||
#else
|
|
||||||
textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, gpuDevice, props);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return textContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrDistanceFieldTextContext::buildDistanceAdjustTable() {
|
|
||||||
|
|
||||||
// This is used for an approximation of the mask gamma hack, used by raster and bitmap
|
|
||||||
// text. The mask gamma hack is based off of guessing what the blend color is going to
|
|
||||||
// be, and adjusting the mask so that when run through the linear blend will
|
|
||||||
// produce the value closest to the desired result. However, in practice this means
|
|
||||||
// that the 'adjusted' mask is just increasing or decreasing the coverage of
|
|
||||||
// the mask depending on what it is thought it will blit against. For black (on
|
|
||||||
// assumed white) this means that coverages are decreased (on a curve). For white (on
|
|
||||||
// assumed black) this means that coverages are increased (on a a curve). At
|
|
||||||
// middle (perceptual) gray (which could be blit against anything) the coverages
|
|
||||||
// remain the same.
|
|
||||||
//
|
|
||||||
// The idea here is that instead of determining the initial (real) coverage and
|
|
||||||
// then adjusting that coverage, we determine an adjusted coverage directly by
|
|
||||||
// essentially manipulating the geometry (in this case, the distance to the glyph
|
|
||||||
// edge). So for black (on assumed white) this thins a bit; for white (on
|
|
||||||
// assumed black) this fake bolds the geometry a bit.
|
|
||||||
//
|
|
||||||
// The distance adjustment is calculated by determining the actual coverage value which
|
|
||||||
// when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
|
|
||||||
// actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
|
|
||||||
// actual edge. So by subtracting this distance adjustment and computing without the
|
|
||||||
// the coverage adjustment we should get 0.5 coverage at the same point.
|
|
||||||
//
|
|
||||||
// This has several implications:
|
|
||||||
// For non-gray lcd smoothed text, each subpixel essentially is using a
|
|
||||||
// slightly different geometry.
|
|
||||||
//
|
|
||||||
// For black (on assumed white) this may not cover some pixels which were
|
|
||||||
// previously covered; however those pixels would have been only slightly
|
|
||||||
// covered and that slight coverage would have been decreased anyway. Also, some pixels
|
|
||||||
// which were previously fully covered may no longer be fully covered.
|
|
||||||
//
|
|
||||||
// For white (on assumed black) this may cover some pixels which weren't
|
|
||||||
// previously covered at all.
|
|
||||||
|
|
||||||
int width, height;
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
#ifdef SK_GAMMA_CONTRAST
|
|
||||||
SkScalar contrast = SK_GAMMA_CONTRAST;
|
|
||||||
#else
|
|
||||||
SkScalar contrast = 0.5f;
|
|
||||||
#endif
|
|
||||||
SkScalar paintGamma = fDeviceProperties.gamma();
|
|
||||||
SkScalar deviceGamma = fDeviceProperties.gamma();
|
|
||||||
|
|
||||||
size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
|
|
||||||
&width, &height);
|
|
||||||
|
|
||||||
SkASSERT(kExpectedDistanceAdjustTableSize == height);
|
|
||||||
fDistanceAdjustTable = SkNEW_ARRAY(SkScalar, height);
|
|
||||||
|
|
||||||
SkAutoTArray<uint8_t> data((int)size);
|
|
||||||
SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
|
|
||||||
|
|
||||||
// find the inverse points where we cross 0.5
|
|
||||||
// binsearch might be better, but we only need to do this once on creation
|
|
||||||
for (int row = 0; row < height; ++row) {
|
|
||||||
uint8_t* rowPtr = data.get() + row*width;
|
|
||||||
for (int col = 0; col < width - 1; ++col) {
|
|
||||||
if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
|
|
||||||
// compute point where a mask value will give us a result of 0.5
|
|
||||||
float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
|
|
||||||
float borderAlpha = (col + interp) / 255.f;
|
|
||||||
|
|
||||||
// compute t value for that alpha
|
|
||||||
// this is an approximate inverse for smoothstep()
|
|
||||||
float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
|
|
||||||
|
|
||||||
// compute distance which gives us that t value
|
|
||||||
const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
|
|
||||||
float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
|
|
||||||
|
|
||||||
fDistanceAdjustTable[row] = d;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
|
|
||||||
SkDELETE_ARRAY(fDistanceAdjustTable);
|
|
||||||
fDistanceAdjustTable = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GrDistanceFieldTextContext::canDraw(const GrRenderTarget* rt,
|
|
||||||
const GrClip& clip,
|
|
||||||
const GrPaint& paint,
|
|
||||||
const SkPaint& skPaint,
|
|
||||||
const SkMatrix& viewMatrix) {
|
|
||||||
// TODO: support perspective (need getMaxScale replacement)
|
|
||||||
if (viewMatrix.hasPerspective()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SkScalar maxScale = viewMatrix.getMaxScale();
|
|
||||||
SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
|
|
||||||
// Hinted text looks far better at small resolutions
|
|
||||||
// Scaling up beyond 2x yields undesireable artifacts
|
|
||||||
if (scaledTextSize < kMinDFFontSize || scaledTextSize > 2*kLargeDFFontSize) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fEnableDFRendering && !skPaint.isDistanceFieldTextTEMP() &&
|
|
||||||
scaledTextSize < kLargeDFFontSize) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// rasterizers and mask filters modify alpha, which doesn't
|
|
||||||
// translate well to distance
|
|
||||||
if (skPaint.getRasterizer() || skPaint.getMaskFilter() ||
|
|
||||||
!fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: add some stroking support
|
|
||||||
if (skPaint.getStyle() != SkPaint::kFill_Style) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void GrDistanceFieldTextContext::init(GrRenderTarget* rt, const GrClip& clip,
|
|
||||||
const GrPaint& paint, const SkPaint& skPaint,
|
|
||||||
const SkIRect& regionClipBounds) {
|
|
||||||
GrTextContext::init(rt, clip, paint, skPaint, regionClipBounds);
|
|
||||||
|
|
||||||
fStrike = NULL;
|
|
||||||
|
|
||||||
const SkMatrix& ctm = fViewMatrix;
|
|
||||||
|
|
||||||
// getMaxScale doesn't support perspective, so neither do we at the moment
|
|
||||||
SkASSERT(!ctm.hasPerspective());
|
|
||||||
SkScalar maxScale = ctm.getMaxScale();
|
|
||||||
SkScalar textSize = fSkPaint.getTextSize();
|
|
||||||
SkScalar scaledTextSize = textSize;
|
|
||||||
// if we have non-unity scale, we need to choose our base text size
|
|
||||||
// based on the SkPaint's text size multiplied by the max scale factor
|
|
||||||
// TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
|
|
||||||
if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
|
|
||||||
scaledTextSize *= maxScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
fVertices = NULL;
|
|
||||||
fCurrVertex = 0;
|
|
||||||
fAllocVertexCount = 0;
|
|
||||||
fTotalVertexCount = 0;
|
|
||||||
|
|
||||||
if (scaledTextSize <= kSmallDFFontLimit) {
|
|
||||||
fTextRatio = textSize / kSmallDFFontSize;
|
|
||||||
fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
|
|
||||||
#if DEBUG_TEXT_SIZE
|
|
||||||
fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0x00, 0x7F));
|
|
||||||
fPaint.setColor(GrColorPackRGBA(0x00, 0x00, 0x7F, 0xFF));
|
|
||||||
#endif
|
|
||||||
} else if (scaledTextSize <= kMediumDFFontLimit) {
|
|
||||||
fTextRatio = textSize / kMediumDFFontSize;
|
|
||||||
fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
|
|
||||||
#if DEBUG_TEXT_SIZE
|
|
||||||
fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0x3F, 0x00));
|
|
||||||
fPaint.setColor(GrColorPackRGBA(0x00, 0x3F, 0x00, 0xFF));
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
fTextRatio = textSize / kLargeDFFontSize;
|
|
||||||
fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
|
|
||||||
#if DEBUG_TEXT_SIZE
|
|
||||||
fSkPaint.setColor(SkColorSetARGB(0xFF, 0x7F, 0x00, 0x00));
|
|
||||||
fPaint.setColor(GrColorPackRGBA(0x7F, 0x00, 0x00, 0xFF));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
fUseLCDText = fSkPaint.isLCDRenderText();
|
|
||||||
|
|
||||||
fSkPaint.setLCDRenderText(false);
|
|
||||||
fSkPaint.setAutohinted(false);
|
|
||||||
fSkPaint.setHinting(SkPaint::kNormal_Hinting);
|
|
||||||
fSkPaint.setSubpixelText(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 GrDistanceFieldTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
|
|
||||||
const GrPaint& paint,
|
|
||||||
const SkPaint& skPaint, const SkMatrix& viewMatrix,
|
|
||||||
const char text[], size_t byteLength,
|
|
||||||
const SkScalar pos[], int scalarsPerPosition,
|
|
||||||
const SkPoint& offset,
|
|
||||||
const SkIRect& regionClipBounds) {
|
|
||||||
|
|
||||||
SkASSERT(byteLength == 0 || text != NULL);
|
|
||||||
SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
|
|
||||||
|
|
||||||
// nothing to draw
|
|
||||||
if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fViewMatrix = viewMatrix;
|
|
||||||
this->init(rt, clip, paint, skPaint, regionClipBounds);
|
|
||||||
|
|
||||||
SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
|
|
||||||
|
|
||||||
SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
|
|
||||||
SkGlyphCache* cache = autoCache.getCache();
|
|
||||||
GrFontScaler* fontScaler = GetGrFontScaler(cache);
|
|
||||||
|
|
||||||
int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
|
|
||||||
fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
|
|
||||||
|
|
||||||
const char* stop = text + byteLength;
|
|
||||||
SkTArray<char> fallbackTxt;
|
|
||||||
SkTArray<SkScalar> fallbackPos;
|
|
||||||
|
|
||||||
if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
|
|
||||||
while (text < stop) {
|
|
||||||
const char* lastText = text;
|
|
||||||
// the last 2 parameters are ignored
|
|
||||||
const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
|
|
||||||
|
|
||||||
if (glyph.fWidth) {
|
|
||||||
SkScalar x = offset.x() + pos[0];
|
|
||||||
SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
|
|
||||||
|
|
||||||
if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
|
|
||||||
glyph.getSubXFixed(),
|
|
||||||
glyph.getSubYFixed(),
|
|
||||||
GrGlyph::kDistance_MaskStyle),
|
|
||||||
x, y, fontScaler)) {
|
|
||||||
// couldn't append, send to fallback
|
|
||||||
fallbackTxt.push_back_n(SkToInt(text-lastText), lastText);
|
|
||||||
fallbackPos.push_back(pos[0]);
|
|
||||||
if (2 == scalarsPerPosition) {
|
|
||||||
fallbackPos.push_back(pos[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pos += scalarsPerPosition;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
SkScalar alignMul = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? SK_ScalarHalf
|
|
||||||
: SK_Scalar1;
|
|
||||||
while (text < stop) {
|
|
||||||
const char* lastText = text;
|
|
||||||
// the last 2 parameters are ignored
|
|
||||||
const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
|
|
||||||
|
|
||||||
if (glyph.fWidth) {
|
|
||||||
SkScalar x = offset.x() + pos[0];
|
|
||||||
SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
|
|
||||||
|
|
||||||
SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX)*alignMul*fTextRatio;
|
|
||||||
SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY)*alignMul*fTextRatio;
|
|
||||||
|
|
||||||
if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
|
|
||||||
glyph.getSubXFixed(),
|
|
||||||
glyph.getSubYFixed(),
|
|
||||||
GrGlyph::kDistance_MaskStyle),
|
|
||||||
x - advanceX, y - advanceY, fontScaler)) {
|
|
||||||
// couldn't append, send to fallback
|
|
||||||
fallbackTxt.push_back_n(SkToInt(text-lastText), lastText);
|
|
||||||
fallbackPos.push_back(pos[0]);
|
|
||||||
if (2 == scalarsPerPosition) {
|
|
||||||
fallbackPos.push_back(pos[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pos += scalarsPerPosition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->finish();
|
|
||||||
|
|
||||||
if (fallbackTxt.count() > 0) {
|
|
||||||
fFallbackTextContext->drawPosText(rt, clip, paint, skPaint, viewMatrix,
|
|
||||||
fallbackTxt.begin(), fallbackTxt.count(),
|
|
||||||
fallbackPos.begin(), scalarsPerPosition, offset,
|
|
||||||
regionClipBounds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
|
|
||||||
unsigned r = SkColorGetR(c);
|
|
||||||
unsigned g = SkColorGetG(c);
|
|
||||||
unsigned b = SkColorGetB(c);
|
|
||||||
return GrColorPackRGBA(r, g, b, 0xff);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t get_vertex_stride(bool useColorVerts) {
|
|
||||||
return useColorVerts ? (sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16)) :
|
|
||||||
(sizeof(SkPoint) + sizeof(SkIPoint16));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void* alloc_vertices(GrDrawTarget* drawTarget,
|
|
||||||
int numVertices,
|
|
||||||
bool useColorVerts) {
|
|
||||||
if (numVertices <= 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* vertices = NULL;
|
|
||||||
bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
|
|
||||||
get_vertex_stride(useColorVerts),
|
|
||||||
0,
|
|
||||||
&vertices,
|
|
||||||
NULL);
|
|
||||||
GrAlwaysAssert(success);
|
|
||||||
return vertices;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
|
|
||||||
GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
|
|
||||||
GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
|
|
||||||
|
|
||||||
uint32_t textureUniqueID = fCurrTexture->getUniqueID();
|
|
||||||
const SkMatrix& ctm = fViewMatrix;
|
|
||||||
|
|
||||||
// set up any flags
|
|
||||||
uint32_t flags = 0;
|
|
||||||
flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
|
|
||||||
flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
|
|
||||||
flags |= fUseLCDText && ctm.rectStaysRect() ?
|
|
||||||
kRectToRect_DistanceFieldEffectFlag : 0;
|
|
||||||
bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
|
|
||||||
flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
|
|
||||||
|
|
||||||
// see if we need to create a new effect
|
|
||||||
if (textureUniqueID != fEffectTextureUniqueID ||
|
|
||||||
filteredColor != fEffectColor ||
|
|
||||||
flags != fEffectFlags ||
|
|
||||||
!fCachedGeometryProcessor->viewMatrix().cheapEqualTo(fViewMatrix)) {
|
|
||||||
GrColor color = fPaint.getColor();
|
|
||||||
|
|
||||||
if (fUseLCDText) {
|
|
||||||
GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
|
|
||||||
|
|
||||||
float redCorrection =
|
|
||||||
fDistanceAdjustTable[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
|
|
||||||
float greenCorrection =
|
|
||||||
fDistanceAdjustTable[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
|
|
||||||
float blueCorrection =
|
|
||||||
fDistanceAdjustTable[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
|
|
||||||
GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
|
|
||||||
GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
|
|
||||||
greenCorrection,
|
|
||||||
blueCorrection);
|
|
||||||
fCachedGeometryProcessor.reset(GrDistanceFieldLCDTextGeoProc::Create(color,
|
|
||||||
fViewMatrix,
|
|
||||||
fCurrTexture,
|
|
||||||
params,
|
|
||||||
widthAdjust,
|
|
||||||
flags));
|
|
||||||
} else {
|
|
||||||
flags |= kColorAttr_DistanceFieldEffectFlag;
|
|
||||||
bool opaque = GrColorIsOpaque(color);
|
|
||||||
#ifdef SK_GAMMA_APPLY_TO_A8
|
|
||||||
U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.gamma(),
|
|
||||||
filteredColor);
|
|
||||||
float correction = fDistanceAdjustTable[lum >> kDistanceAdjustLumShift];
|
|
||||||
fCachedGeometryProcessor.reset(GrDistanceFieldA8TextGeoProc::Create(color,
|
|
||||||
fViewMatrix,
|
|
||||||
fCurrTexture,
|
|
||||||
params,
|
|
||||||
correction,
|
|
||||||
flags,
|
|
||||||
opaque));
|
|
||||||
#else
|
|
||||||
fCachedGeometryProcessor.reset(GrDistanceFieldA8TextGeoProc::Create(color,
|
|
||||||
fViewMatrix,
|
|
||||||
fCurrTexture,
|
|
||||||
params,
|
|
||||||
flags,
|
|
||||||
opaque));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
fEffectTextureUniqueID = textureUniqueID;
|
|
||||||
fEffectColor = filteredColor;
|
|
||||||
fEffectFlags = flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool GrDistanceFieldTextContext::uploadGlyph(GrGlyph* glyph, GrFontScaler* scaler) {
|
|
||||||
if (!fStrike->glyphTooLargeForAtlas(glyph)) {
|
|
||||||
if (fStrike->addGlyphToAtlas(glyph, scaler)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to clear out an unused plot before we flush
|
|
||||||
if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
|
|
||||||
fStrike->addGlyphToAtlas(glyph, scaler)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c_DumpFontCache) {
|
|
||||||
#ifdef SK_DEVELOPER
|
|
||||||
fContext->getFontCache()->dump();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// before we purge the cache, we must flush any accumulated draws
|
|
||||||
this->flush();
|
|
||||||
fContext->flush();
|
|
||||||
|
|
||||||
// we should have an unused plot now
|
|
||||||
if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
|
|
||||||
fStrike->addGlyphToAtlas(glyph, scaler)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we should never get here
|
|
||||||
SkASSERT(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Returns true if this method handled the glyph, false if needs to be passed to fallback
|
|
||||||
//
|
|
||||||
bool GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed,
|
|
||||||
SkScalar sx, SkScalar sy,
|
|
||||||
GrFontScaler* scaler) {
|
|
||||||
if (NULL == fDrawTarget) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL == fStrike) {
|
|
||||||
fStrike = fContext->getFontCache()->getStrike(scaler);
|
|
||||||
}
|
|
||||||
|
|
||||||
GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
|
|
||||||
if (NULL == glyph || glyph->fBounds.isEmpty()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fallback to color glyph support
|
|
||||||
if (kA8_GrMaskFormat != glyph->fMaskFormat) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
|
|
||||||
SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
|
|
||||||
SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
|
|
||||||
SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
|
|
||||||
|
|
||||||
SkScalar scale = fTextRatio;
|
|
||||||
dx *= scale;
|
|
||||||
dy *= scale;
|
|
||||||
width *= scale;
|
|
||||||
height *= scale;
|
|
||||||
sx += dx;
|
|
||||||
sy += dy;
|
|
||||||
SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
|
|
||||||
|
|
||||||
// check if we clipped out
|
|
||||||
SkRect dstRect;
|
|
||||||
const SkMatrix& ctm = fViewMatrix;
|
|
||||||
(void) ctm.mapRect(&dstRect, glyphRect);
|
|
||||||
if (fClipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
|
|
||||||
SkScalarTruncToInt(dstRect.top()),
|
|
||||||
SkScalarTruncToInt(dstRect.right()),
|
|
||||||
SkScalarTruncToInt(dstRect.bottom()))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL == glyph->fPlot) {
|
|
||||||
// needs to be a separate conditional to avoid over-optimization
|
|
||||||
// on Nexus 7 and Nexus 10
|
|
||||||
|
|
||||||
// If the glyph is too large we fall back to paths
|
|
||||||
if (!uploadGlyph(glyph, scaler)) {
|
|
||||||
if (NULL == glyph->fPath) {
|
|
||||||
SkPath* path = SkNEW(SkPath);
|
|
||||||
if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
|
|
||||||
// flag the glyph as being dead?
|
|
||||||
delete path;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
glyph->fPath = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
// flush any accumulated draws before drawing this glyph as a path.
|
|
||||||
this->flush();
|
|
||||||
|
|
||||||
SkMatrix ctm;
|
|
||||||
ctm.postTranslate(sx - dx, sy - dy);
|
|
||||||
|
|
||||||
SkPath tmpPath(*glyph->fPath);
|
|
||||||
tmpPath.transform(ctm);
|
|
||||||
|
|
||||||
GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
|
|
||||||
fContext->drawPath(fRenderTarget, fClip, fPaint, fViewMatrix, tmpPath, strokeInfo);
|
|
||||||
|
|
||||||
// remove this glyph from the vertices we need to allocate
|
|
||||||
fTotalVertexCount -= kVerticesPerGlyph;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SkASSERT(glyph->fPlot);
|
|
||||||
GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
|
|
||||||
glyph->fPlot->setDrawToken(drawToken);
|
|
||||||
|
|
||||||
GrTexture* texture = glyph->fPlot->texture();
|
|
||||||
SkASSERT(texture);
|
|
||||||
|
|
||||||
if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fTotalVertexCount) {
|
|
||||||
this->flush();
|
|
||||||
fCurrTexture = texture;
|
|
||||||
fCurrTexture->ref();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool useColorVerts = !fUseLCDText;
|
|
||||||
|
|
||||||
if (NULL == fVertices) {
|
|
||||||
int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
|
|
||||||
fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
|
|
||||||
fVertices = alloc_vertices(fDrawTarget,
|
|
||||||
fAllocVertexCount,
|
|
||||||
useColorVerts);
|
|
||||||
}
|
|
||||||
|
|
||||||
fVertexBounds.joinNonEmptyArg(glyphRect);
|
|
||||||
|
|
||||||
int u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
|
|
||||||
int v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
|
|
||||||
int u1 = u0 + glyph->fBounds.width() - 2*SK_DistanceFieldInset;
|
|
||||||
int v1 = v0 + glyph->fBounds.height() - 2*SK_DistanceFieldInset;
|
|
||||||
|
|
||||||
size_t vertSize = get_vertex_stride(useColorVerts);
|
|
||||||
intptr_t vertex = reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex;
|
|
||||||
|
|
||||||
// V0
|
|
||||||
SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
|
|
||||||
position->set(glyphRect.fLeft, glyphRect.fTop);
|
|
||||||
if (useColorVerts) {
|
|
||||||
SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
|
|
||||||
*color = fPaint.getColor();
|
|
||||||
}
|
|
||||||
SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize -
|
|
||||||
sizeof(SkIPoint16));
|
|
||||||
textureCoords->set(u0, v0);
|
|
||||||
vertex += vertSize;
|
|
||||||
|
|
||||||
// V1
|
|
||||||
position = reinterpret_cast<SkPoint*>(vertex);
|
|
||||||
position->set(glyphRect.fLeft, glyphRect.fBottom);
|
|
||||||
if (useColorVerts) {
|
|
||||||
SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
|
|
||||||
*color = fPaint.getColor();
|
|
||||||
}
|
|
||||||
textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
|
|
||||||
textureCoords->set(u0, v1);
|
|
||||||
vertex += vertSize;
|
|
||||||
|
|
||||||
// V2
|
|
||||||
position = reinterpret_cast<SkPoint*>(vertex);
|
|
||||||
position->set(glyphRect.fRight, glyphRect.fBottom);
|
|
||||||
if (useColorVerts) {
|
|
||||||
SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
|
|
||||||
*color = fPaint.getColor();
|
|
||||||
}
|
|
||||||
textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
|
|
||||||
textureCoords->set(u1, v1);
|
|
||||||
vertex += vertSize;
|
|
||||||
|
|
||||||
// V3
|
|
||||||
position = reinterpret_cast<SkPoint*>(vertex);
|
|
||||||
position->set(glyphRect.fRight, glyphRect.fTop);
|
|
||||||
if (useColorVerts) {
|
|
||||||
SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
|
|
||||||
*color = fPaint.getColor();
|
|
||||||
}
|
|
||||||
textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
|
|
||||||
textureCoords->set(u1, v0);
|
|
||||||
|
|
||||||
fCurrVertex += 4;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrDistanceFieldTextContext::flush() {
|
|
||||||
if (NULL == fDrawTarget) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fCurrVertex > 0) {
|
|
||||||
GrPipelineBuilder pipelineBuilder;
|
|
||||||
pipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip);
|
|
||||||
|
|
||||||
// setup our sampler state for our text texture/atlas
|
|
||||||
SkASSERT(SkIsAlign4(fCurrVertex));
|
|
||||||
|
|
||||||
// get our current color
|
|
||||||
SkColor filteredColor;
|
|
||||||
SkColorFilter* colorFilter = fSkPaint.getColorFilter();
|
|
||||||
if (colorFilter) {
|
|
||||||
filteredColor = colorFilter->filterColor(fSkPaint.getColor());
|
|
||||||
} else {
|
|
||||||
filteredColor = fSkPaint.getColor();
|
|
||||||
}
|
|
||||||
this->setupCoverageEffect(filteredColor);
|
|
||||||
|
|
||||||
// Set draw state
|
|
||||||
if (fUseLCDText) {
|
|
||||||
// TODO: move supportsRGBCoverage check to setupCoverageEffect and only add LCD
|
|
||||||
// processor if the xp can support it. For now we will simply assume that if
|
|
||||||
// fUseLCDText is true, then we have a known color output.
|
|
||||||
const GrXPFactory* xpFactory = pipelineBuilder.getXPFactory();
|
|
||||||
if (!xpFactory->supportsRGBCoverage(0, kRGBA_GrColorComponentFlags)) {
|
|
||||||
SkDebugf("LCD Text will not draw correctly.\n");
|
|
||||||
}
|
|
||||||
SkASSERT(!fCachedGeometryProcessor->hasVertexColor());
|
|
||||||
} else {
|
|
||||||
// We're using per-vertex color.
|
|
||||||
SkASSERT(fCachedGeometryProcessor->hasVertexColor());
|
|
||||||
}
|
|
||||||
int nGlyphs = fCurrVertex / kVerticesPerGlyph;
|
|
||||||
fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
|
|
||||||
fDrawTarget->drawIndexedInstances(&pipelineBuilder,
|
|
||||||
fCachedGeometryProcessor.get(),
|
|
||||||
kTriangles_GrPrimitiveType,
|
|
||||||
nGlyphs,
|
|
||||||
kVerticesPerGlyph,
|
|
||||||
kIndicesPerGlyph,
|
|
||||||
&fVertexBounds);
|
|
||||||
fDrawTarget->resetVertexSource();
|
|
||||||
fVertices = NULL;
|
|
||||||
fTotalVertexCount -= fCurrVertex;
|
|
||||||
fCurrVertex = 0;
|
|
||||||
SkSafeSetNull(fCurrTexture);
|
|
||||||
fVertexBounds.setLargestInverted();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void GrDistanceFieldTextContext::finish() {
|
|
||||||
this->flush();
|
|
||||||
fTotalVertexCount = 0;
|
|
||||||
|
|
||||||
GrTextContext::finish();
|
|
||||||
}
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013 Google Inc.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by a BSD-style license that can be
|
|
||||||
* found in the LICENSE file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef GrDistanceFieldTextContext_DEFINED
|
|
||||||
#define GrDistanceFieldTextContext_DEFINED
|
|
||||||
|
|
||||||
#include "GrTextContext.h"
|
|
||||||
|
|
||||||
class GrGeometryProcessor;
|
|
||||||
class GrTextStrike;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This class implements GrTextContext using distance field fonts
|
|
||||||
*/
|
|
||||||
class GrDistanceFieldTextContext : public GrTextContext {
|
|
||||||
public:
|
|
||||||
static GrDistanceFieldTextContext* Create(GrContext*, SkGpuDevice*, const SkDeviceProperties&,
|
|
||||||
bool enable);
|
|
||||||
|
|
||||||
virtual ~GrDistanceFieldTextContext();
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum {
|
|
||||||
kMinRequestedGlyphs = 1,
|
|
||||||
kDefaultRequestedGlyphs = 64,
|
|
||||||
kMinRequestedVerts = kMinRequestedGlyphs * 4,
|
|
||||||
kDefaultRequestedVerts = kDefaultRequestedGlyphs * 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
GrTextStrike* fStrike;
|
|
||||||
SkScalar fTextRatio;
|
|
||||||
bool fUseLCDText;
|
|
||||||
bool fEnableDFRendering;
|
|
||||||
SkAutoTUnref<GrGeometryProcessor> fCachedGeometryProcessor;
|
|
||||||
SkScalar* fDistanceAdjustTable;
|
|
||||||
// Used to check whether fCachedEffect is still valid.
|
|
||||||
uint32_t fEffectTextureUniqueID;
|
|
||||||
SkColor fEffectColor;
|
|
||||||
uint32_t fEffectFlags;
|
|
||||||
void* fVertices;
|
|
||||||
int fCurrVertex;
|
|
||||||
int fAllocVertexCount;
|
|
||||||
int fTotalVertexCount;
|
|
||||||
GrTexture* fCurrTexture;
|
|
||||||
SkRect fVertexBounds;
|
|
||||||
SkMatrix fViewMatrix;
|
|
||||||
|
|
||||||
GrDistanceFieldTextContext(GrContext*, SkGpuDevice*, const SkDeviceProperties&, bool enable);
|
|
||||||
void buildDistanceAdjustTable();
|
|
||||||
|
|
||||||
bool canDraw(const GrRenderTarget*, const GrClip&, const GrPaint&,
|
|
||||||
const SkPaint&, const SkMatrix& viewMatrix) override;
|
|
||||||
|
|
||||||
void onDrawText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
|
|
||||||
const SkMatrix& viewMatrix,
|
|
||||||
const char text[], size_t byteLength,
|
|
||||||
SkScalar x, SkScalar y, const SkIRect& regionClipBounds) override;
|
|
||||||
void onDrawPosText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
|
|
||||||
const SkMatrix& viewMatrix,
|
|
||||||
const char text[], size_t byteLength,
|
|
||||||
const SkScalar pos[], int scalarsPerPosition,
|
|
||||||
const SkPoint& offset, const SkIRect& regionClipBounds) override;
|
|
||||||
|
|
||||||
void init(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
|
|
||||||
const SkIRect& regionClipBounds);
|
|
||||||
bool appendGlyph(GrGlyph::PackedID, SkScalar left, SkScalar top, GrFontScaler*);
|
|
||||||
bool uploadGlyph(GrGlyph*, GrFontScaler*);
|
|
||||||
void setupCoverageEffect(const SkColor& filteredColor);
|
|
||||||
void flush(); // automatically called by destructor
|
|
||||||
void finish();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#include "GrFlushToGpuDrawTarget.h"
|
#include "GrFlushToGpuDrawTarget.h"
|
||||||
#include "GrContext.h"
|
#include "GrContext.h"
|
||||||
#include "GrFontCache.h"
|
|
||||||
#include "GrGpu.h"
|
#include "GrGpu.h"
|
||||||
#include "GrBufferAllocPool.h"
|
#include "GrBufferAllocPool.h"
|
||||||
|
|
||||||
@ -86,8 +85,6 @@ void GrFlushToGpuDrawTarget::flush() {
|
|||||||
}
|
}
|
||||||
fFlushing = true;
|
fFlushing = true;
|
||||||
|
|
||||||
fGpu->getContext()->getFontCache()->updateTextures();
|
|
||||||
|
|
||||||
fGpu->saveActiveTraceMarkers();
|
fGpu->saveActiveTraceMarkers();
|
||||||
|
|
||||||
this->onFlush();
|
this->onFlush();
|
||||||
|
@ -1,351 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010 Google Inc.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by a BSD-style license that can be
|
|
||||||
* found in the LICENSE file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "GrFontCache.h"
|
|
||||||
#include "GrFontAtlasSizes.h"
|
|
||||||
#include "GrGpu.h"
|
|
||||||
#include "GrRectanizer.h"
|
|
||||||
#include "GrSurfacePriv.h"
|
|
||||||
#include "SkString.h"
|
|
||||||
|
|
||||||
#include "SkDistanceFieldGen.h"
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#define FONT_CACHE_STATS 0
|
|
||||||
#if FONT_CACHE_STATS
|
|
||||||
static int g_PurgeCount = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
|
|
||||||
gpu->ref();
|
|
||||||
for (int i = 0; i < kAtlasCount; ++i) {
|
|
||||||
fAtlases[i] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
fHead = fTail = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
GrFontCache::~GrFontCache() {
|
|
||||||
SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
|
|
||||||
while (!iter.done()) {
|
|
||||||
SkDELETE(&(*iter));
|
|
||||||
++iter;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < kAtlasCount; ++i) {
|
|
||||||
delete fAtlases[i];
|
|
||||||
}
|
|
||||||
fGpu->unref();
|
|
||||||
#if FONT_CACHE_STATS
|
|
||||||
SkDebugf("Num purges: %d\n", g_PurgeCount);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) {
|
|
||||||
static const GrPixelConfig sPixelConfigs[] = {
|
|
||||||
kAlpha_8_GrPixelConfig,
|
|
||||||
kRGB_565_GrPixelConfig,
|
|
||||||
kSkia8888_GrPixelConfig
|
|
||||||
};
|
|
||||||
SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_size_mismatch);
|
|
||||||
|
|
||||||
return sPixelConfigs[format];
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mask_format_to_atlas_index(GrMaskFormat format) {
|
|
||||||
static const int sAtlasIndices[] = {
|
|
||||||
GrFontCache::kA8_AtlasType,
|
|
||||||
GrFontCache::k565_AtlasType,
|
|
||||||
GrFontCache::k8888_AtlasType
|
|
||||||
};
|
|
||||||
SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
|
|
||||||
|
|
||||||
SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount);
|
|
||||||
return sAtlasIndices[format];
|
|
||||||
}
|
|
||||||
|
|
||||||
GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler) {
|
|
||||||
GrTextStrike* strike = SkNEW_ARGS(GrTextStrike, (this, scaler->getKey()));
|
|
||||||
fCache.add(strike);
|
|
||||||
|
|
||||||
if (fHead) {
|
|
||||||
fHead->fPrev = strike;
|
|
||||||
} else {
|
|
||||||
SkASSERT(NULL == fTail);
|
|
||||||
fTail = strike;
|
|
||||||
}
|
|
||||||
strike->fPrev = NULL;
|
|
||||||
strike->fNext = fHead;
|
|
||||||
fHead = strike;
|
|
||||||
|
|
||||||
return strike;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrFontCache::freeAll() {
|
|
||||||
SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
|
|
||||||
while (!iter.done()) {
|
|
||||||
SkDELETE(&(*iter));
|
|
||||||
++iter;
|
|
||||||
}
|
|
||||||
fCache.rewind();
|
|
||||||
for (int i = 0; i < kAtlasCount; ++i) {
|
|
||||||
delete fAtlases[i];
|
|
||||||
fAtlases[i] = NULL;
|
|
||||||
}
|
|
||||||
fHead = NULL;
|
|
||||||
fTail = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrFontCache::purgeStrike(GrTextStrike* strike) {
|
|
||||||
fCache.remove(*(strike->fFontScalerKey));
|
|
||||||
this->detachStrikeFromList(strike);
|
|
||||||
delete strike;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
GrPlot* GrFontCache::addToAtlas(GrMaskFormat format, GrAtlas::ClientPlotUsage* usage,
|
|
||||||
int width, int height, const void* image,
|
|
||||||
SkIPoint16* loc) {
|
|
||||||
GrPixelConfig config = mask_format_to_pixel_config(format);
|
|
||||||
int atlasIndex = mask_format_to_atlas_index(format);
|
|
||||||
if (NULL == fAtlases[atlasIndex]) {
|
|
||||||
if (kA8_GrMaskFormat == format) {
|
|
||||||
SkISize textureSize = SkISize::Make(GR_FONT_ATLAS_A8_TEXTURE_WIDTH,
|
|
||||||
GR_FONT_ATLAS_TEXTURE_HEIGHT);
|
|
||||||
fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrSurfaceFlags,
|
|
||||||
textureSize,
|
|
||||||
GR_FONT_ATLAS_A8_NUM_PLOTS_X,
|
|
||||||
GR_FONT_ATLAS_NUM_PLOTS_Y,
|
|
||||||
true));
|
|
||||||
} else {
|
|
||||||
SkISize textureSize = SkISize::Make(GR_FONT_ATLAS_TEXTURE_WIDTH,
|
|
||||||
GR_FONT_ATLAS_TEXTURE_HEIGHT);
|
|
||||||
fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrSurfaceFlags,
|
|
||||||
textureSize,
|
|
||||||
GR_FONT_ATLAS_NUM_PLOTS_X,
|
|
||||||
GR_FONT_ATLAS_NUM_PLOTS_Y,
|
|
||||||
true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fAtlases[atlasIndex]->addToAtlas(usage, width, height, image, loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike, const GrGlyph* glyph) {
|
|
||||||
SkASSERT(preserveStrike);
|
|
||||||
|
|
||||||
int index = mask_format_to_atlas_index(glyph->fMaskFormat);
|
|
||||||
GrAtlas* atlas = fAtlases[index];
|
|
||||||
GrPlot* plot = atlas->getUnusedPlot();
|
|
||||||
if (NULL == plot) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
plot->resetRects();
|
|
||||||
|
|
||||||
GrTextStrike* strike = fHead;
|
|
||||||
while (strike) {
|
|
||||||
GrTextStrike* strikeToPurge = strike;
|
|
||||||
strike = strikeToPurge->fNext;
|
|
||||||
strikeToPurge->removePlot(plot);
|
|
||||||
|
|
||||||
// clear out any empty strikes (except this one)
|
|
||||||
if (strikeToPurge != preserveStrike && strikeToPurge->fPlotUsage.isEmpty()) {
|
|
||||||
this->purgeStrike(strikeToPurge);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FONT_CACHE_STATS
|
|
||||||
++g_PurgeCount;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SK_DEBUG
|
|
||||||
void GrFontCache::validate() const {
|
|
||||||
int count = fCache.count();
|
|
||||||
if (0 == count) {
|
|
||||||
SkASSERT(!fHead);
|
|
||||||
SkASSERT(!fTail);
|
|
||||||
} else if (1 == count) {
|
|
||||||
SkASSERT(fHead == fTail);
|
|
||||||
} else {
|
|
||||||
SkASSERT(fHead != fTail);
|
|
||||||
}
|
|
||||||
|
|
||||||
int count2 = 0;
|
|
||||||
const GrTextStrike* strike = fHead;
|
|
||||||
while (strike) {
|
|
||||||
count2 += 1;
|
|
||||||
strike = strike->fNext;
|
|
||||||
}
|
|
||||||
SkASSERT(count == count2);
|
|
||||||
|
|
||||||
count2 = 0;
|
|
||||||
strike = fTail;
|
|
||||||
while (strike) {
|
|
||||||
count2 += 1;
|
|
||||||
strike = strike->fPrev;
|
|
||||||
}
|
|
||||||
SkASSERT(count == count2);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void GrFontCache::dump() const {
|
|
||||||
static int gDumpCount = 0;
|
|
||||||
for (int i = 0; i < kAtlasCount; ++i) {
|
|
||||||
if (fAtlases[i]) {
|
|
||||||
GrTexture* texture = fAtlases[i]->getTexture();
|
|
||||||
if (texture) {
|
|
||||||
SkString filename;
|
|
||||||
#ifdef SK_BUILD_FOR_ANDROID
|
|
||||||
filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
|
|
||||||
#else
|
|
||||||
filename.printf("fontcache_%d%d.png", gDumpCount, i);
|
|
||||||
#endif
|
|
||||||
texture->surfacePriv().savePixels(filename.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++gDumpCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#ifdef SK_DEBUG
|
|
||||||
static int gCounter;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
The text strike is specific to a given font/style/matrix setup, which is
|
|
||||||
represented by the GrHostFontScaler object we are given in getGlyph().
|
|
||||||
|
|
||||||
We map a 32bit glyphID to a GrGlyph record, which in turn points to a
|
|
||||||
atlas and a position within that texture.
|
|
||||||
*/
|
|
||||||
|
|
||||||
GrTextStrike::GrTextStrike(GrFontCache* cache, const GrFontDescKey* key)
|
|
||||||
: fPool(9/*start allocations at 512 bytes*/) {
|
|
||||||
fFontScalerKey = key;
|
|
||||||
fFontScalerKey->ref();
|
|
||||||
|
|
||||||
fFontCache = cache; // no need to ref, it won't go away before we do
|
|
||||||
|
|
||||||
#ifdef SK_DEBUG
|
|
||||||
// SkDebugf(" GrTextStrike %p %d\n", this, gCounter);
|
|
||||||
gCounter += 1;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
GrTextStrike::~GrTextStrike() {
|
|
||||||
fFontScalerKey->unref();
|
|
||||||
SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
|
|
||||||
while (!iter.done()) {
|
|
||||||
(*iter).free();
|
|
||||||
++iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SK_DEBUG
|
|
||||||
gCounter -= 1;
|
|
||||||
// SkDebugf("~GrTextStrike %p %d\n", this, gCounter);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
|
|
||||||
GrFontScaler* scaler) {
|
|
||||||
SkIRect bounds;
|
|
||||||
if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(packed)) {
|
|
||||||
if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GrMaskFormat format = scaler->getPackedGlyphMaskFormat(packed);
|
|
||||||
|
|
||||||
GrGlyph* glyph = (GrGlyph*)fPool.alloc(sizeof(GrGlyph), SK_MALLOC_THROW);
|
|
||||||
glyph->init(packed, bounds, format);
|
|
||||||
fCache.add(glyph);
|
|
||||||
return glyph;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrTextStrike::removePlot(const GrPlot* plot) {
|
|
||||||
SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
|
|
||||||
while (!iter.done()) {
|
|
||||||
if (plot == (*iter).fPlot) {
|
|
||||||
(*iter).fPlot = NULL;
|
|
||||||
}
|
|
||||||
++iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
GrAtlas::RemovePlot(&fPlotUsage, plot);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GrTextStrike::glyphTooLargeForAtlas(GrGlyph* glyph) {
|
|
||||||
int width = glyph->fBounds.width();
|
|
||||||
int height = glyph->fBounds.height();
|
|
||||||
bool useDistanceField =
|
|
||||||
(GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedID));
|
|
||||||
int pad = useDistanceField ? 2 * SK_DistanceFieldPad : 0;
|
|
||||||
int plotWidth = (kA8_GrMaskFormat == glyph->fMaskFormat) ? GR_FONT_ATLAS_A8_PLOT_WIDTH
|
|
||||||
: GR_FONT_ATLAS_PLOT_WIDTH;
|
|
||||||
if (width + pad > plotWidth) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (height + pad > GR_FONT_ATLAS_PLOT_HEIGHT) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
|
|
||||||
#if 0 // testing hack to force us to flush our cache often
|
|
||||||
static int gCounter;
|
|
||||||
if ((++gCounter % 10) == 0) return false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SkASSERT(glyph);
|
|
||||||
SkASSERT(scaler);
|
|
||||||
SkASSERT(fCache.find(glyph->fPackedID));
|
|
||||||
SkASSERT(NULL == glyph->fPlot);
|
|
||||||
|
|
||||||
SkAutoUnref ar(SkSafeRef(scaler));
|
|
||||||
|
|
||||||
int bytesPerPixel = GrMaskFormatBytesPerPixel(glyph->fMaskFormat);
|
|
||||||
|
|
||||||
size_t size = glyph->fBounds.area() * bytesPerPixel;
|
|
||||||
GrAutoMalloc<1024> storage(size);
|
|
||||||
|
|
||||||
if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedID)) {
|
|
||||||
if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
|
|
||||||
glyph->height(),
|
|
||||||
storage.get())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
|
|
||||||
glyph->height(),
|
|
||||||
glyph->width() * bytesPerPixel,
|
|
||||||
storage.get())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GrPlot* plot = fFontCache->addToAtlas(glyph->fMaskFormat, &fPlotUsage,
|
|
||||||
glyph->width(), glyph->height(),
|
|
||||||
storage.get(), &glyph->fAtlasLocation);
|
|
||||||
|
|
||||||
if (NULL == plot) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
glyph->fPlot = plot;
|
|
||||||
return true;
|
|
||||||
}
|
|
@ -1,181 +0,0 @@
|
|||||||
|
|
||||||
/*
|
|
||||||
* Copyright 2010 Google Inc.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by a BSD-style license that can be
|
|
||||||
* found in the LICENSE file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef GrTextStrike_DEFINED
|
|
||||||
#define GrTextStrike_DEFINED
|
|
||||||
|
|
||||||
#include "GrAtlas.h"
|
|
||||||
#include "GrDrawTarget.h"
|
|
||||||
#include "GrFontScaler.h"
|
|
||||||
#include "GrGlyph.h"
|
|
||||||
#include "SkTDynamicHash.h"
|
|
||||||
#include "SkVarAlloc.h"
|
|
||||||
|
|
||||||
class GrFontCache;
|
|
||||||
class GrGpu;
|
|
||||||
class GrFontPurgeListener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The textstrike maps a hostfontscaler instance to a dictionary of
|
|
||||||
* glyphid->strike
|
|
||||||
*/
|
|
||||||
class GrTextStrike {
|
|
||||||
public:
|
|
||||||
GrTextStrike(GrFontCache*, const GrFontDescKey* fontScalerKey);
|
|
||||||
~GrTextStrike();
|
|
||||||
|
|
||||||
const GrFontDescKey* getFontScalerKey() const { return fFontScalerKey; }
|
|
||||||
GrFontCache* getFontCache() const { return fFontCache; }
|
|
||||||
|
|
||||||
inline GrGlyph* getGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler) {
|
|
||||||
GrGlyph* glyph = fCache.find(packed);
|
|
||||||
if (NULL == glyph) {
|
|
||||||
glyph = this->generateGlyph(packed, scaler);
|
|
||||||
}
|
|
||||||
return glyph;
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns true if glyph (or glyph+padding for distance field)
|
|
||||||
// is too large to ever fit in texture atlas subregions (GrPlots)
|
|
||||||
bool glyphTooLargeForAtlas(GrGlyph*);
|
|
||||||
// returns true if glyph successfully added to texture atlas, false otherwise
|
|
||||||
bool addGlyphToAtlas(GrGlyph*, GrFontScaler*);
|
|
||||||
|
|
||||||
// testing
|
|
||||||
int countGlyphs() const { return fCache.count(); }
|
|
||||||
|
|
||||||
// remove any references to this plot
|
|
||||||
void removePlot(const GrPlot* plot);
|
|
||||||
|
|
||||||
static const GrFontDescKey& GetKey(const GrTextStrike& ts) {
|
|
||||||
return *(ts.fFontScalerKey);
|
|
||||||
}
|
|
||||||
static uint32_t Hash(const GrFontDescKey& key) {
|
|
||||||
return key.getHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// for easy removal from list
|
|
||||||
GrTextStrike* fPrev;
|
|
||||||
GrTextStrike* fNext;
|
|
||||||
|
|
||||||
private:
|
|
||||||
SkTDynamicHash<GrGlyph, GrGlyph::PackedID> fCache;
|
|
||||||
const GrFontDescKey* fFontScalerKey;
|
|
||||||
SkVarAlloc fPool;
|
|
||||||
|
|
||||||
GrFontCache* fFontCache;
|
|
||||||
|
|
||||||
GrAtlas::ClientPlotUsage fPlotUsage;
|
|
||||||
|
|
||||||
GrGlyph* generateGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler);
|
|
||||||
|
|
||||||
friend class GrFontCache;
|
|
||||||
};
|
|
||||||
|
|
||||||
class GrFontCache {
|
|
||||||
public:
|
|
||||||
GrFontCache(GrGpu*);
|
|
||||||
~GrFontCache();
|
|
||||||
|
|
||||||
inline GrTextStrike* getStrike(GrFontScaler* scaler) {
|
|
||||||
this->validate();
|
|
||||||
|
|
||||||
GrTextStrike* strike = fCache.find(*(scaler->getKey()));
|
|
||||||
if (NULL == strike) {
|
|
||||||
strike = this->generateStrike(scaler);
|
|
||||||
} else if (strike->fPrev) {
|
|
||||||
// Need to put the strike at the head of its dllist, since that is how
|
|
||||||
// we age the strikes for purging (we purge from the back of the list)
|
|
||||||
this->detachStrikeFromList(strike);
|
|
||||||
// attach at the head
|
|
||||||
fHead->fPrev = strike;
|
|
||||||
strike->fNext = fHead;
|
|
||||||
strike->fPrev = NULL;
|
|
||||||
fHead = strike;
|
|
||||||
}
|
|
||||||
this->validate();
|
|
||||||
return strike;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add to texture atlas that matches this format
|
|
||||||
GrPlot* addToAtlas(GrMaskFormat format, GrAtlas::ClientPlotUsage* usage,
|
|
||||||
int width, int height, const void* image,
|
|
||||||
SkIPoint16* loc);
|
|
||||||
|
|
||||||
void freeAll();
|
|
||||||
|
|
||||||
// make an unused plot available for this glyph
|
|
||||||
bool freeUnusedPlot(GrTextStrike* preserveStrike, const GrGlyph* glyph);
|
|
||||||
|
|
||||||
// testing
|
|
||||||
int countStrikes() const { return fCache.count(); }
|
|
||||||
GrTextStrike* getHeadStrike() const { return fHead; }
|
|
||||||
|
|
||||||
void updateTextures() {
|
|
||||||
for (int i = 0; i < kAtlasCount; ++i) {
|
|
||||||
if (fAtlases[i]) {
|
|
||||||
fAtlases[i]->uploadPlotsToTexture();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SK_DEBUG
|
|
||||||
void validate() const;
|
|
||||||
#else
|
|
||||||
void validate() const {}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void dump() const;
|
|
||||||
|
|
||||||
enum AtlasType {
|
|
||||||
kA8_AtlasType, //!< 1-byte per pixel
|
|
||||||
k565_AtlasType, //!< 2-bytes per pixel
|
|
||||||
k8888_AtlasType, //!< 4-bytes per pixel
|
|
||||||
|
|
||||||
kLast_AtlasType = k8888_AtlasType
|
|
||||||
};
|
|
||||||
static const int kAtlasCount = kLast_AtlasType + 1;
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class GrFontPurgeListener;
|
|
||||||
|
|
||||||
SkTDynamicHash<GrTextStrike, GrFontDescKey> fCache;
|
|
||||||
// for LRU
|
|
||||||
GrTextStrike* fHead;
|
|
||||||
GrTextStrike* fTail;
|
|
||||||
|
|
||||||
GrGpu* fGpu;
|
|
||||||
GrAtlas* fAtlases[kAtlasCount];
|
|
||||||
|
|
||||||
GrTextStrike* generateStrike(GrFontScaler*);
|
|
||||||
|
|
||||||
inline void detachStrikeFromList(GrTextStrike* strike) {
|
|
||||||
if (strike->fPrev) {
|
|
||||||
SkASSERT(fHead != strike);
|
|
||||||
strike->fPrev->fNext = strike->fNext;
|
|
||||||
} else {
|
|
||||||
SkASSERT(fHead == strike);
|
|
||||||
fHead = strike->fNext;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strike->fNext) {
|
|
||||||
SkASSERT(fTail != strike);
|
|
||||||
strike->fNext->fPrev = strike->fPrev;
|
|
||||||
} else {
|
|
||||||
SkASSERT(fTail == strike);
|
|
||||||
fTail = strike->fPrev;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void purgeStrike(GrTextStrike* strike);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#include "GrStencilAndCoverTextContext.h"
|
#include "GrStencilAndCoverTextContext.h"
|
||||||
#include "GrAtlasTextContext.h"
|
#include "GrAtlasTextContext.h"
|
||||||
#include "GrBitmapTextContext.h"
|
|
||||||
#include "GrDrawTarget.h"
|
#include "GrDrawTarget.h"
|
||||||
#include "GrGpu.h"
|
#include "GrGpu.h"
|
||||||
#include "GrPath.h"
|
#include "GrPath.h"
|
||||||
@ -35,12 +34,8 @@ GrStencilAndCoverTextContext::Create(GrContext* context, SkGpuDevice* gpuDevice,
|
|||||||
const SkDeviceProperties& props) {
|
const SkDeviceProperties& props) {
|
||||||
GrStencilAndCoverTextContext* textContext = SkNEW_ARGS(GrStencilAndCoverTextContext,
|
GrStencilAndCoverTextContext* textContext = SkNEW_ARGS(GrStencilAndCoverTextContext,
|
||||||
(context, gpuDevice, props));
|
(context, gpuDevice, props));
|
||||||
#ifdef USE_BITMAP_TEXTBLOBS
|
|
||||||
textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, gpuDevice, props,
|
textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, gpuDevice, props,
|
||||||
false);
|
false);
|
||||||
#else
|
|
||||||
textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, gpuDevice, props);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return textContext;
|
return textContext;
|
||||||
}
|
}
|
||||||
|
@ -7,14 +7,13 @@
|
|||||||
|
|
||||||
#include "SkGpuDevice.h"
|
#include "SkGpuDevice.h"
|
||||||
|
|
||||||
#include "GrBitmapTextContext.h"
|
|
||||||
#include "GrContext.h"
|
#include "GrContext.h"
|
||||||
#include "GrDistanceFieldTextContext.h"
|
|
||||||
#include "GrGpu.h"
|
#include "GrGpu.h"
|
||||||
#include "GrGpuResourcePriv.h"
|
#include "GrGpuResourcePriv.h"
|
||||||
#include "GrLayerHoister.h"
|
#include "GrLayerHoister.h"
|
||||||
#include "GrRecordReplaceDraw.h"
|
#include "GrRecordReplaceDraw.h"
|
||||||
#include "GrStrokeInfo.h"
|
#include "GrStrokeInfo.h"
|
||||||
|
#include "GrTextContext.h"
|
||||||
#include "GrTracing.h"
|
#include "GrTracing.h"
|
||||||
#include "SkCanvasPriv.h"
|
#include "SkCanvasPriv.h"
|
||||||
#include "SkDeviceImageFilterProxy.h"
|
#include "SkDeviceImageFilterProxy.h"
|
||||||
|
@ -478,9 +478,6 @@ int tool_main(int argc, char** argv) {
|
|||||||
if (renderer->isUsingGpuDevice()) {
|
if (renderer->isUsingGpuDevice()) {
|
||||||
GrContext* ctx = renderer->getGrContext();
|
GrContext* ctx = renderer->getGrContext();
|
||||||
ctx->printCacheStats();
|
ctx->printCacheStats();
|
||||||
#ifdef SK_DEVELOPER
|
|
||||||
ctx->dumpFontCache();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user