Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(257)

Unified Diff: src/gpu/GrStencilAndCoverTextContext.cpp

Issue 196133014: Implement text rendering with NVPR (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: rebase and fix postext Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/gpu/GrStencilAndCoverTextContext.cpp
diff --git a/src/gpu/GrStencilAndCoverTextContext.cpp b/src/gpu/GrStencilAndCoverTextContext.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..359b61e28ecbd9fea00f0ae4a81356d68f46356d
--- /dev/null
+++ b/src/gpu/GrStencilAndCoverTextContext.cpp
@@ -0,0 +1,399 @@
+/*
+ * 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 "SkTextMapState.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->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;
+
+ // 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 halfSampleX, halfSampleY;
+ if (cache->isSubpixel()) {
+ halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
+ SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getMatrix());
+ if (kX_SkAxisAlignment == baseline) {
+ halfSampleY = SK_FixedHalf;
+ } else if (kY_SkAxisAlignment == baseline) {
+ halfSampleX = SK_FixedHalf;
+ }
+ } else {
+ halfSampleX = halfSampleY = SK_FixedHalf;
+ }
+
+ SkFixed fx = SkScalarToFixed(x) + halfSampleX;
jvanverth1 2014/06/09 13:48:07 Doesn't this lead to glyphs being off by half a pi
bungeman-skia 2014/06/09 15:00:07 Yeah, this was done in SkDraw because of SkDraw1Gl
Kimmo Kinnunen 2014/06/11 12:33:24 Done.
+ SkFixed fy = SkScalarToFixed(y) + halfSampleY;
+ 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->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);
+
+ SkMatrix ctm = fContext->getMatrix();
+
+ const char* stop = text + byteLength;
+ SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign());
+ SkTextMapState tms(SkMatrix::I(), constY);
+ SkTextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition);
+
+ SkTDArray<const GrPath*> paths;
+ SkTDArray<SkMatrix> transforms;
+ SkScalar halfSampleX = 0, halfSampleY = 0;
+
+ if (cache->isSubpixel()) {
+ // maybe we should skip the rounding if linearText is set
+ SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
+
+#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
+ if (kX_SkAxisAlignment == baseline) {
+ halfSampleY = SK_ScalarHalf;
+ } else if (kY_SkAxisAlignment == baseline) {
+ halfSampleX = SK_ScalarHalf;
+ }
+#endif
+
+ if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
+ while (text < stop) {
+ tmsProc(tms, pos);
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+ if (glyph.fWidth) {
+ SkScalar x = tms.fLoc.fX + halfSampleX;
jvanverth1 2014/06/09 13:48:07 Same issue with half-pixel alignment as above? (a
Kimmo Kinnunen 2014/06/11 12:33:24 Done.
+ SkScalar y = tms.fLoc.fY + halfSampleY;
+
+ this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
+ glyph.getSubXFixed(),
+ glyph.getSubYFixed()),
+ SkPoint::Make(x, y),
+ strike,
+ scaler);
+ }
+ pos += scalarsPerPosition;
+ }
+ } else {
+ while (text < stop) {
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+ if (glyph.fWidth) {
+ tmsProc(tms, pos);
+ SkPoint loc;
+ alignProc(tms.fLoc, glyph, &loc);
+
+ loc.fX += halfSampleX;
+ loc.fY += halfSampleY;
+
+ this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
+ glyph.getSubXFixed(),
+ glyph.getSubYFixed()),
+ loc,
+ strike,
+ scaler);
+
+ }
+ pos += scalarsPerPosition;
+ }
+ }
+ } else { // Codepath for "not subpixel" case.
+ if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
jvanverth1 2014/06/09 13:48:07 Do we need to support non-subpixel layout when dra
Kimmo Kinnunen 2014/06/11 12:33:24 Done.
Kimmo Kinnunen 2014/06/11 12:39:21 Actually, I mean to say that I couldn't get the gl
+ while (text < stop) {
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+ if (glyph.fWidth) {
+ tmsProc(tms, pos);
+
+ SkScalar x = tms.fLoc.fX + SK_ScalarHalf; //halfSampleX;
+ SkScalar y = tms.fLoc.fY + SK_ScalarHalf; //halfSampleY;
+ this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
+ glyph.getSubXFixed(),
+ glyph.getSubYFixed()),
+ SkPoint::Make(x, y),
+ strike,
+ scaler);
+ }
+ pos += scalarsPerPosition;
+ }
+ } else {
+ while (text < stop) {
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+ if (glyph.fWidth) {
+ tmsProc(tms, pos);
+
+ SkPoint loc;
+ alignProc(tms.fLoc, glyph, &loc);
+
+ loc.fX += SK_ScalarHalf; //halfSampleX;
+ loc.fY += SK_ScalarHalf; //halfSampleY;
+ 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::kFill_Style
+ && paint.getStrokeWidth() == 0
+ && (fContext->getMatrix().hasPerspective()
+ || !fContext->getMatrix().invert(NULL))) {
+ 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);
+
+ if (SkDraw::ShouldDrawTextAsPaths(skPaint, fContext->getMatrix())) {
+ // This is to reproduce SkDraw::drawText_asPaths glyph positions.
+ fSkPaint.setLinearText(true);
+ fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
+ fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
+ } else {
+ fTextRatio = 1.0f;
+ }
+ // Note: if Chrome ever draws text in very different text sizes in real
+ // time, then glyphs need to be looked up untransformed and scaled, but
+ // still based on the requested text size. This will let the path cache
+ // work. This will probably not be needed until 1) text size is animating,
+ // 2) layout change animations are fast enough 3) pictures are rasterized
+ // directly to framebuffer.
+
+ if (fSkPaint.getStyle() != SkPaint::kFill_Style) {
+ SkScalar strokeWidth = fSkPaint.getStrokeWidth();
+ if (0 == strokeWidth) {
+ SkMatrix inv;
+ if (fContext->getMatrix().invert(&inv)) {
jvanverth1 2014/06/09 13:48:07 This won't work for non-uniform scale. And if you'
Kimmo Kinnunen 2014/06/11 12:33:24 Right..
+ strokeWidth = SK_Scalar1 * inv.getScaleX();
+ } else {
+ // Avoid unused return value warning.
+ SkASSERT(false);
+ strokeWidth = SK_Scalar1;
+ }
+ }
+ // Compensate the glyphs being scaled up by fTextRatio by scaling the
+ // stroke down.
+ fSkPaint.setStrokeWidth(strokeWidth / fTextRatio);
+ }
+ fStroke = SkStrokeRec(fSkPaint);
+
+ // Make glyph cache produce paths geometry for fill. We will stroke them
+ // by passing fStroke to drawPath.
+ 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);
+ GrTextContext::finish();
+}
+

Powered by Google App Engine
This is Rietveld 408576698