Chromium Code Reviews| Index: src/gpu/GrStencilAndCoverTextContext.cpp |
| diff --git a/src/gpu/GrStencilAndCoverTextContext.cpp b/src/gpu/GrStencilAndCoverTextContext.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..a266abaaf6d75b92927ef36150b485d21cde35e5 |
| --- /dev/null |
| +++ b/src/gpu/GrStencilAndCoverTextContext.cpp |
| @@ -0,0 +1,289 @@ |
| +/* |
| + * 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 "SkGlyphCache.h" |
| +#include "SkGpuDevice.h" |
| +#include "SkPath.h" |
| + |
| +static const int kBaseSCFontSize = 64; |
| +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); |
| + |
| + // 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); |
| + |
| + 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) { |
| + // 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 fx = SkScalarToFixed(x) + SK_FixedHalf; |
| + SkFixed fy = SkScalarToFixed(y) + SK_FixedHalf; |
| + SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio); |
| + |
| + while (text < stop) { |
| + const SkGlyph& glyph = glyphCacheProc(cache, &text, fx, fy); |
|
jvanverth1
2014/03/24 14:51:05
I believe fx, fy should always be 0 in this case.
Kimmo Kinnunen
2014/03/25 12:32:48
Done.
|
| + |
| + 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); |
| + |
| + const char* stop = text + byteLength; |
| + |
| + SkTDArray<const GrPath*> paths; |
| + SkTDArray<SkMatrix> transforms; |
| + |
| + 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) { |
| + SkScalar x = pos[0]; |
| + SkScalar y = scalarsPerPosition == 1 ? constY : pos[1]; |
| + this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), |
| + glyph.getSubXFixed(), |
| + glyph.getSubYFixed()), |
| + SkPoint::Make(x, y), |
| + strike, |
| + scaler); |
| + } |
| + |
| + pos += scalarsPerPosition; |
| + } |
| + } else { |
| + int alignShift = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? 1 : 0; |
| + while (text < stop) { |
| + // the last 2 parameters are ignored |
| + const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); |
| + |
| + if (glyph.fWidth) { |
| + SkScalar x = pos[0]; |
| + SkScalar y = scalarsPerPosition == 1 ? constY : pos[1]; |
| + x -= SkFixedToScalar((glyph.fAdvanceX >> alignShift)); |
| + y -= SkFixedToScalar((glyph.fAdvanceY >> alignShift)); |
| + |
| + this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), |
| + glyph.getSubXFixed(), |
| + glyph.getSubYFixed()), |
| + SkPoint::Make(x, y), |
| + strike, |
| + scaler); |
| + } |
| + pos += scalarsPerPosition; |
| + } |
| + } |
| + |
| + this->finish(); |
| +} |
| + |
| +bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) { |
| + return !paint.getRasterizer() |
| + && !paint.getMaskFilter() |
| + && (paint.getStyle() == SkPaint::kFill_Style |
| + || paint.getStrokeWidth() > 0); |
| + |
| +} |
| + |
| +static bool has_thick_frame(const SkPaint& paint) { |
| + return paint.getStrokeWidth() > 0 && |
| + paint.getStyle() != SkPaint::kFill_Style; |
| +} |
| + |
| +void GrStencilAndCoverTextContext::init(const GrPaint& paint, |
| + const SkPaint& skPaint, |
| + size_t textByteLength) { |
| + GrTextContext::init(paint, skPaint); |
| + fTextRatio = fSkPaint.getTextSize() / kBaseSCFontSize; |
| + fSkPaint.setTextSize(SkIntToScalar(kBaseSCFontSize)); |
| + fSkPaint.setLCDRenderText(false); |
| + fSkPaint.setAutohinted(false); |
| + fSkPaint.setSubpixelText(false); |
|
jvanverth1
2014/03/24 14:51:05
You may want to always enable subpixel text so you
Kimmo Kinnunen
2014/03/25 12:32:48
Done.
|
| + if (has_thick_frame(fSkPaint)) { |
| + // Compensate the glyphs being scaled up by fTextRatio, by scaling the |
| + // stroke down. |
| + fSkPaint.setStrokeWidth(fSkPaint.getStrokeWidth() / 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: |
|
bsalomon
2014/03/24 13:53:37
tiny style nit: we indent the case/defaults (much
Kimmo Kinnunen
2014/03/25 12:32:48
Done.
|
| + 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 (scaler->getGlyphPath(glyph->glyphID(), &fTmpPath)) { |
| + *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(); |
| +} |
| + |