aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar kkinnunen <kkinnunen@nvidia.com>2014-06-24 00:12:27 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2014-06-24 00:12:27 -0700
commitc6cb56f36c4aad8ed45486a3bb4de614bb822f1b (patch)
treed231e40d459dfd1f5545e489fcf004318e82c2e4
parent6fc763e3c2b1e539510cb18ece8b97e183099ab1 (diff)
Implement text rendering with NVPR
Use path rendering to render the text from outlines if supported by the GPU. Implement this in GrStencilAndCoverTextContext by copying chunks of code from GrBitmapTextContext. The drawing is implemented with "instanced" path drawing functions. Moves the creation of the "main" text context from SkGpuDevice to the GrContext::createTextContext. This is done because the decision of which text renderer is optimal can be made only with the internal implementation-specific information of the context. Remove a windows assertion from SkScalerContext_GDI::getGDIGlyphPath. The GetGlyphOutlineW fails in fontmgr_match for the initial space char in the string " [700] ...". According to MSDN, this is a known problem. Just return that the glyph has no path data in these cases. R=jvanverth@google.com, bsalomon@google.com, mtklein@google.com Author: kkinnunen@nvidia.com Review URL: https://codereview.chromium.org/196133014
-rw-r--r--gyp/gpu.gypi2
-rw-r--r--include/core/SkPaint.h1
-rw-r--r--include/gpu/GrContext.h12
-rw-r--r--include/gpu/GrPaint.h1
-rwxr-xr-x[-rw-r--r--]src/gpu/GrContext.cpp15
-rw-r--r--src/gpu/GrStencilAndCoverTextContext.cpp372
-rw-r--r--src/gpu/GrStencilAndCoverTextContext.h54
-rw-r--r--src/gpu/SkGpuDevice.cpp9
-rwxr-xr-xsrc/ports/SkFontHost_win.cpp3
9 files changed, 463 insertions, 6 deletions
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index c03f8eff3b..9b01a46118 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -117,6 +117,8 @@
'<(skia_src_path)/gpu/GrStencil.h',
'<(skia_src_path)/gpu/GrStencilAndCoverPathRenderer.cpp',
'<(skia_src_path)/gpu/GrStencilAndCoverPathRenderer.h',
+ '<(skia_src_path)/gpu/GrStencilAndCoverTextContext.cpp',
+ '<(skia_src_path)/gpu/GrStencilAndCoverTextContext.h',
'<(skia_src_path)/gpu/GrStencilBuffer.cpp',
'<(skia_src_path)/gpu/GrStencilBuffer.h',
'<(skia_src_path)/gpu/GrStrokeInfo.h',
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h
index a73faec42e..e8d85ce122 100644
--- a/include/core/SkPaint.h
+++ b/include/core/SkPaint.h
@@ -1136,6 +1136,7 @@ private:
friend class SkPDFDevice;
friend class GrBitmapTextContext;
friend class GrDistanceFieldTextContext;
+ friend class GrStencilAndCoverTextContext;
friend class SkTextToPathIter;
friend class SkCanonicalizePaint;
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 608ec507b2..af44aa7c51 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -38,6 +38,7 @@ class GrResourceEntry;
class GrResourceCache;
class GrStencilBuffer;
class GrTestTarget;
+class GrTextContext;
class GrTextureParams;
class GrVertexBuffer;
class GrVertexBufferAllocPool;
@@ -194,6 +195,16 @@ public:
*/
GrCacheable* findAndRefCachedResource(const GrResourceKey&);
+ /**
+ * Creates a new text rendering context that is optimal for the
+ * render target and the context. Caller assumes the ownership
+ * of the returned object. The returned object must be deleted
+ * before the context is destroyed.
+ */
+ GrTextContext* createTextContext(GrRenderTarget*,
+ const SkDeviceProperties&,
+ bool enableDistanceFieldFonts);
+
///////////////////////////////////////////////////////////////////////////
// Textures
@@ -1018,6 +1029,7 @@ private:
// addExistingTextureToCache
friend class GrTexture;
friend class GrStencilAndCoverPathRenderer;
+ friend class GrStencilAndCoverTextContext;
// Add an existing texture to the texture cache. This is intended solely
// for use with textures released from an GrAutoScratchTexture.
diff --git a/include/gpu/GrPaint.h b/include/gpu/GrPaint.h
index 50a32a5743..db9b8cc2a6 100644
--- a/include/gpu/GrPaint.h
+++ b/include/gpu/GrPaint.h
@@ -214,6 +214,7 @@ private:
}
friend class GrContext; // To access above two functions
+ friend class GrStencilAndCoverTextContext; // To access above two functions
SkSTArray<4, GrEffectStage> fColorStages;
SkSTArray<2, GrEffectStage> fCoverageStages;
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 6c90c178d3..d59d2e8d40 100644..100755
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -16,6 +16,7 @@
#include "GrAARectRenderer.h"
#include "GrBufferAllocPool.h"
#include "GrGpu.h"
+#include "GrDistanceFieldTextContext.h"
#include "GrDrawTargetCaps.h"
#include "GrIndexBuffer.h"
#include "GrInOrderDrawBuffer.h"
@@ -26,6 +27,7 @@
#include "GrResourceCache.h"
#include "GrSoftwarePathRenderer.h"
#include "GrStencilBuffer.h"
+#include "GrStencilAndCoverTextContext.h"
#include "GrStrokeInfo.h"
#include "GrTextStrike.h"
#include "GrTraceMarker.h"
@@ -236,6 +238,19 @@ void GrContext::getResourceCacheUsage(int* resourceCount, size_t* resourceBytes)
}
}
+GrTextContext* GrContext::createTextContext(GrRenderTarget* renderTarget,
+ const SkDeviceProperties&
+ leakyProperties,
+ bool enableDistanceFieldFonts) {
+ if (fGpu->caps()->pathRenderingSupport()) {
+ if (renderTarget->getStencilBuffer() && renderTarget->isMultisampled()) {
+ return SkNEW_ARGS(GrStencilAndCoverTextContext, (this, leakyProperties));
+ }
+ }
+ return SkNEW_ARGS(GrDistanceFieldTextContext, (this, leakyProperties,
+ enableDistanceFieldFonts));
+}
+
////////////////////////////////////////////////////////////////////////////////
GrTexture* GrContext::findAndRefTexture(const GrTextureDesc& desc,
diff --git a/src/gpu/GrStencilAndCoverTextContext.cpp b/src/gpu/GrStencilAndCoverTextContext.cpp
new file mode 100644
index 0000000000..637c85a997
--- /dev/null
+++ b/src/gpu/GrStencilAndCoverTextContext.cpp
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrStencilAndCoverTextContext.h"
+#include "GrDrawTarget.h"
+#include "GrFontScaler.h"
+#include "GrGpu.h"
+#include "GrPath.h"
+#include "GrTextStrike.h"
+#include "GrTextStrike_impl.h"
+#include "SkAutoKern.h"
+#include "SkDraw.h"
+#include "SkDrawProcs.h"
+#include "SkGlyphCache.h"
+#include "SkGpuDevice.h"
+#include "SkPath.h"
+#include "SkTextMapStateProc.h"
+
+static const int kMaxReservedGlyphs = 64;
+
+GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(
+ GrContext* context, const SkDeviceProperties& properties)
+ : GrTextContext(context, properties)
+ , fStroke(SkStrokeRec::kFill_InitStyle) {
+}
+
+GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
+}
+
+void GrStencilAndCoverTextContext::drawText(const GrPaint& paint,
+ const SkPaint& skPaint,
+ const char text[],
+ size_t byteLength,
+ SkScalar x, SkScalar y) {
+ SkASSERT(byteLength == 0 || text != NULL);
+
+ if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
+ return;
+ }
+
+ // This is the slow path, mainly used by Skia unit tests. The other
+ // backends (8888, gpu, ...) use device-space dependent glyph caches. In
+ // order to match the glyph positions that the other code paths produce, we
+ // must also use device-space dependent glyph cache. This has the
+ // side-effect that the glyph shape outline will be in device-space,
+ // too. This in turn has the side-effect that NVPR can not stroke the paths,
+ // as the stroke in NVPR is defined in object-space.
+ // NOTE: here we have following coincidence that works at the moment:
+ // - When using the device-space glyphs, the transforms we pass to NVPR
+ // instanced drawing are the global transforms, and the view transform is
+ // identity. NVPR can not use non-affine transforms in the instanced
+ // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it
+ // will turn off the use of device-space glyphs when perspective transforms
+ // are in use.
+
+ fGlyphTransform = fContext->getMatrix();
+
+ this->init(paint, skPaint, byteLength);
+
+ SkMatrix* glyphCacheTransform = NULL;
+ // Transform our starting point.
+ if (fNeedsDeviceSpaceGlyphs) {
+ SkPoint loc;
+ fGlyphTransform.mapXY(x, y, &loc);
+ x = loc.fX;
+ y = loc.fY;
+ glyphCacheTransform = &fGlyphTransform;
+ }
+
+ SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
+ SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, glyphCacheTransform);
+ SkGlyphCache* cache = autoCache.getCache();
+ GrFontScaler* scaler = GetGrFontScaler(cache);
+ GrTextStrike* strike =
+ fContext->getFontCache()->getStrike(scaler, true);
+
+ const char* stop = text + byteLength;
+
+ // Measure first if needed.
+ if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
+ SkFixed stopX = 0;
+ SkFixed stopY = 0;
+
+ const char* textPtr = text;
+ while (textPtr < stop) {
+ // We don't need x, y here, since all subpixel variants will have the
+ // same advance.
+ const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
+
+ stopX += glyph.fAdvanceX;
+ stopY += glyph.fAdvanceY;
+ }
+ SkASSERT(textPtr == stop);
+
+ SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
+ SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
+
+ if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
+ alignX = SkScalarHalf(alignX);
+ alignY = SkScalarHalf(alignY);
+ }
+
+ x -= alignX;
+ y -= alignY;
+ }
+
+ SkAutoKern autokern;
+
+ SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
+
+ SkFixed fx = SkScalarToFixed(x);
+ SkFixed fy = SkScalarToFixed(y);
+ while (text < stop) {
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+ fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio);
+ if (glyph.fWidth) {
+ this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
+ glyph.getSubXFixed(),
+ glyph.getSubYFixed()),
+ SkPoint::Make(
+ SkFixedToScalar(fx),
+ SkFixedToScalar(fy)),
+ strike,
+ scaler);
+ }
+
+ fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio);
+ fy += SkFixedMul_portable(glyph.fAdvanceY, fixedSizeRatio);
+ }
+
+ this->finish();
+}
+
+void GrStencilAndCoverTextContext::drawPosText(const GrPaint& paint,
+ const SkPaint& skPaint,
+ const char text[],
+ size_t byteLength,
+ const SkScalar pos[],
+ SkScalar constY,
+ int scalarsPerPosition) {
+ SkASSERT(byteLength == 0 || text != NULL);
+ SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
+
+ // nothing to draw
+ if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
+ return;
+ }
+
+ // This is the fast path. Here we do not bake in the device-transform to
+ // the glyph outline or the advances. This is because we do not need to
+ // position the glyphs at all, since the caller has done the positioning.
+ // The positioning is based on SkPaint::measureText of individual
+ // glyphs. That already uses glyph cache without device transforms. Device
+ // transform is not part of SkPaint::measureText API, and thus we use the
+ // same glyphs as what were measured.
+ fGlyphTransform.reset();
+
+ this->init(paint, skPaint, byteLength);
+
+ SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
+
+ SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL);
+ SkGlyphCache* cache = autoCache.getCache();
+ GrFontScaler* scaler = GetGrFontScaler(cache);
+ GrTextStrike* strike =
+ fContext->getFontCache()->getStrike(scaler, true);
+
+ const char* stop = text + byteLength;
+ SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign());
+ SkTextMapStateProc tmsProc(SkMatrix::I(), constY, scalarsPerPosition);
+
+ if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
+ while (text < stop) {
+ SkPoint loc;
+ tmsProc(pos, &loc);
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+ if (glyph.fWidth) {
+ this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
+ glyph.getSubXFixed(),
+ glyph.getSubYFixed()),
+ loc,
+ strike,
+ scaler);
+ }
+ pos += scalarsPerPosition;
+ }
+ } else {
+ while (text < stop) {
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+ if (glyph.fWidth) {
+ SkPoint tmsLoc;
+ tmsProc(pos, &tmsLoc);
+ SkPoint loc;
+ alignProc(tmsLoc, glyph, &loc);
+
+ this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
+ glyph.getSubXFixed(),
+ glyph.getSubYFixed()),
+ loc,
+ strike,
+ scaler);
+
+ }
+ pos += scalarsPerPosition;
+ }
+ }
+
+ this->finish();
+}
+
+bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) {
+ if (paint.getRasterizer()) {
+ return false;
+ }
+ if (paint.getMaskFilter()) {
+ return false;
+ }
+ if (paint.getPathEffect()) {
+ return false;
+ }
+
+ // No hairlines unless we can map the 1 px width to the object space.
+ if (paint.getStyle() == SkPaint::kStroke_Style
+ && paint.getStrokeWidth() == 0
+ && fContext->getMatrix().hasPerspective()) {
+ return false;
+ }
+
+ // No color bitmap fonts.
+ SkScalerContext::Rec rec;
+ SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
+ return rec.getFormat() != SkMask::kARGB32_Format;
+}
+
+void GrStencilAndCoverTextContext::init(const GrPaint& paint,
+ const SkPaint& skPaint,
+ size_t textByteLength) {
+ GrTextContext::init(paint, skPaint);
+
+ bool otherBackendsWillDrawAsPaths =
+ SkDraw::ShouldDrawTextAsPaths(skPaint, fContext->getMatrix());
+
+ if (otherBackendsWillDrawAsPaths) {
+ // This is to reproduce SkDraw::drawText_asPaths glyph positions.
+ fSkPaint.setLinearText(true);
+ fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
+ fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
+ if (fSkPaint.getStyle() != SkPaint::kFill_Style) {
+ // Compensate the glyphs being scaled up by fTextRatio by scaling the
+ // stroke down.
+ fSkPaint.setStrokeWidth(fSkPaint.getStrokeWidth() / fTextRatio);
+ }
+ fNeedsDeviceSpaceGlyphs = false;
+ } else {
+ fTextRatio = 1.0f;
+ fNeedsDeviceSpaceGlyphs = (fGlyphTransform.getType() &
+ (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask)) != 0;
+ // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
+ SkASSERT(!fGlyphTransform.hasPerspective());
+ if (fNeedsDeviceSpaceGlyphs) {
+ fPaint.localCoordChangeInverse(fGlyphTransform);
+ fContext->setIdentityMatrix();
+ }
+ }
+
+ fStroke = SkStrokeRec(fSkPaint);
+
+ if (fNeedsDeviceSpaceGlyphs) {
+ // The whole shape is baked into the glyph. Make NVPR just fill the
+ // baked shape.
+ fStroke.setStrokeStyle(-1, false);
+ } else {
+ if (fSkPaint.getStrokeWidth() == 0.0f) {
+ if (fSkPaint.getStyle() == SkPaint::kStrokeAndFill_Style) {
+ fStroke.setStrokeStyle(-1, false);
+ } else if (fSkPaint.getStyle() == SkPaint::kStroke_Style) {
+ // Approximate hairline stroke.
+ const SkMatrix& ctm = fContext->getMatrix();
+ SkScalar strokeWidth = SK_Scalar1 /
+ (fTextRatio * SkVector::Make(ctm.getScaleX(), ctm.getSkewY()).length());
+ fStroke.setStrokeStyle(strokeWidth, false);
+ }
+ }
+
+ // Make glyph cache produce paths geometry for fill. We will stroke them
+ // by passing fStroke to drawPath. This is the fast path.
+ fSkPaint.setStyle(SkPaint::kFill_Style);
+ }
+ fStateRestore.set(fDrawTarget->drawState());
+
+ fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(),
+ fContext->getRenderTarget());
+
+ GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
+ kZero_StencilOp,
+ kZero_StencilOp,
+ kNotEqual_StencilFunc,
+ 0xffff,
+ 0x0000,
+ 0xffff);
+
+ *fDrawTarget->drawState()->stencil() = kStencilPass;
+
+ size_t reserveAmount;
+ switch (skPaint.getTextEncoding()) {
+ default:
+ SkASSERT(false);
+ case SkPaint::kUTF8_TextEncoding:
+ reserveAmount = textByteLength;
+ break;
+ case SkPaint::kUTF16_TextEncoding:
+ reserveAmount = textByteLength / 2;
+ break;
+ case SkPaint::kUTF32_TextEncoding:
+ case SkPaint::kGlyphID_TextEncoding:
+ reserveAmount = textByteLength / 4;
+ break;
+ }
+ fPaths.setReserve(reserveAmount);
+ fTransforms.setReserve(reserveAmount);
+}
+
+inline void GrStencilAndCoverTextContext::appendGlyph(GrGlyph::PackedID glyphID,
+ const SkPoint& pos,
+ GrTextStrike* strike,
+ GrFontScaler* scaler) {
+ GrGlyph* glyph = strike->getGlyph(glyphID, scaler);
+ if (NULL == glyph || glyph->fBounds.isEmpty()) {
+ return;
+ }
+
+ if (scaler->getGlyphPath(glyph->glyphID(), &fTmpPath)) {
+ if (!fTmpPath.isEmpty()) {
+ *fPaths.append() = fContext->createPath(fTmpPath, fStroke);
+ SkMatrix* t = fTransforms.append();
+ t->setTranslate(pos.fX, pos.fY);
+ t->preScale(fTextRatio, fTextRatio);
+ }
+ }
+}
+
+void GrStencilAndCoverTextContext::finish() {
+ if (fPaths.count() > 0) {
+ fDrawTarget->drawPaths(static_cast<size_t>(fPaths.count()),
+ fPaths.begin(), fTransforms.begin(),
+ SkPath::kWinding_FillType, fStroke.getStyle());
+
+ for (int i = 0; i < fPaths.count(); ++i) {
+ fPaths[i]->unref();
+ }
+ if (fPaths.count() > kMaxReservedGlyphs) {
+ fPaths.reset();
+ fTransforms.reset();
+ } else {
+ fPaths.rewind();
+ fTransforms.rewind();
+ }
+ }
+ fTmpPath.reset();
+
+ fDrawTarget->drawState()->stencil()->setDisabled();
+ fStateRestore.set(NULL);
+ if (fNeedsDeviceSpaceGlyphs) {
+ fContext->setMatrix(fGlyphTransform);
+ }
+ GrTextContext::finish();
+}
+
diff --git a/src/gpu/GrStencilAndCoverTextContext.h b/src/gpu/GrStencilAndCoverTextContext.h
new file mode 100644
index 0000000000..2e79f5c9b7
--- /dev/null
+++ b/src/gpu/GrStencilAndCoverTextContext.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrStencilAndCoverTextContext_DEFINED
+#define GrStencilAndCoverTextContext_DEFINED
+
+#include "GrTextContext.h"
+#include "GrDrawState.h"
+#include "SkStrokeRec.h"
+
+class GrTextStrike;
+class GrPath;
+
+/*
+ * This class implements text rendering using stencil and cover path rendering
+ * (by the means of GrDrawTarget::drawPath).
+ * This class exposes the functionality through GrTextContext interface.
+ */
+class GrStencilAndCoverTextContext : public GrTextContext {
+public:
+ GrStencilAndCoverTextContext(GrContext*, const SkDeviceProperties&);
+ virtual ~GrStencilAndCoverTextContext();
+
+ virtual void drawText(const GrPaint&, const SkPaint&, const char text[],
+ size_t byteLength,
+ SkScalar x, SkScalar y) SK_OVERRIDE;
+ virtual void drawPosText(const GrPaint&, const SkPaint&,
+ const char text[], size_t byteLength,
+ const SkScalar pos[], SkScalar constY,
+ int scalarsPerPosition) SK_OVERRIDE;
+
+ virtual bool canDraw(const SkPaint& paint) SK_OVERRIDE;
+
+private:
+ void init(const GrPaint&, const SkPaint&, size_t textByteLength);
+ void appendGlyph(GrGlyph::PackedID, const SkPoint&,
+ GrTextStrike*, GrFontScaler*);
+ void finish();
+
+ GrDrawState::AutoRestoreEffects fStateRestore;
+ SkScalar fTextRatio;
+ SkStrokeRec fStroke;
+ SkTDArray<const GrPath*> fPaths;
+ SkTDArray<SkMatrix> fTransforms;
+ SkPath fTmpPath;
+ SkMatrix fGlyphTransform;
+ bool fNeedsDeviceSpaceGlyphs;
+};
+
+#endif
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 8312356d92..861383bf6a 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -167,11 +167,6 @@ void SkGpuDevice::initFromRenderTarget(GrContext* context,
fContext = context;
fContext->ref();
- bool useDFFonts = !!(flags & kDFFonts_Flag);
- fMainTextContext = SkNEW_ARGS(GrDistanceFieldTextContext, (fContext, fLeakyProperties,
- useDFFonts));
- fFallbackTextContext = SkNEW_ARGS(GrBitmapTextContext, (fContext, fLeakyProperties));
-
fRenderTarget = NULL;
fNeedClear = flags & kNeedClear_Flag;
@@ -192,6 +187,10 @@ void SkGpuDevice::initFromRenderTarget(GrContext* context,
(surface->info(), surface, SkToBool(flags & kCached_Flag)));
this->setPixelRef(pr)->unref();
+
+ bool useDFFonts = !!(flags & kDFFonts_Flag);
+ fMainTextContext = fContext->createTextContext(fRenderTarget, fLeakyProperties, useDFFonts);
+ fFallbackTextContext = SkNEW_ARGS(GrBitmapTextContext, (fContext, fLeakyProperties));
}
SkGpuDevice* SkGpuDevice::Create(GrContext* context, const SkImageInfo& origInfo,
diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
index 5a902148bd..a6ccf4d2ee 100755
--- a/src/ports/SkFontHost_win.cpp
+++ b/src/ports/SkFontHost_win.cpp
@@ -1706,7 +1706,8 @@ DWORD SkScalerContext_GDI::getGDIGlyphPath(const SkGlyph& glyph, UINT flags,
LogFontTypeface::EnsureAccessible(this->getTypeface());
total_size = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, 0, NULL, &fMat22);
if (GDI_ERROR == total_size) {
- SkASSERT(false);
+ // GetGlyphOutlineW is known to fail for some characters, such as spaces.
+ // In these cases, just return that the glyph does not have a shape.
return 0;
}
}