Add color emoji fallback for nvpr text

BUG=skia:

Review URL: https://codereview.chromium.org/759613005
This commit is contained in:
cdalton 2014-12-01 08:38:55 -08:00 committed by Commit bot
parent 371bcbcb9f
commit 20b373cf31
4 changed files with 132 additions and 49 deletions

View File

@ -85,6 +85,11 @@ public:
*/
bool applyToPath(SkPath* dst, const SkPath& src) const;
/**
* Apply these stroke parameters to a paint.
*/
void applyToPaint(SkPaint* paint) const;
bool operator==(const SkStrokeRec& other) const {
return fWidth == other.fWidth &&
fMiterLimit == other.fMiterLimit &&

View File

@ -114,3 +114,16 @@ bool SkStrokeRec::applyToPath(SkPath* dst, const SkPath& src) const {
stroker.strokePath(src, dst);
return true;
}
void SkStrokeRec::applyToPaint(SkPaint* paint) const {
if (fWidth < 0) { // fill
paint->setStyle(SkPaint::kFill_Style);
return;
}
paint->setStyle(fStrokeAndFill ? SkPaint::kStrokeAndFill_Style : SkPaint::kStroke_Style);
paint->setStrokeWidth(fWidth);
paint->setStrokeMiter(fMiterLimit);
paint->setStrokeCap(fCap);
paint->setStrokeJoin(fJoin);
}

View File

@ -23,7 +23,9 @@
GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(
GrContext* context, const SkDeviceProperties& properties)
: GrTextContext(context, properties)
, fPendingGlyphCount(0) {
, fStroke(SkStrokeRec::kFill_InitStyle)
, fQueuedGlyphCount(0)
, fFallbackGlyphsIdx(kGlyphBufferSize) {
}
GrStencilAndCoverTextContext* GrStencilAndCoverTextContext::Create(GrContext* context,
@ -63,10 +65,10 @@ bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) {
}
void GrStencilAndCoverTextContext::onDrawText(const GrPaint& paint,
const SkPaint& skPaint,
const char text[],
size_t byteLength,
SkScalar x, SkScalar y) {
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()*/) {
@ -91,7 +93,7 @@ void GrStencilAndCoverTextContext::onDrawText(const GrPaint& paint,
this->init(paint, skPaint, byteLength, kMaxAccuracy_RenderMode);
// Transform our starting point.
if (fNeedsDeviceSpaceGlyphs) {
if (fUsingDeviceSpaceGlyphs) {
SkPoint loc;
fContextInitialMatrix.mapXY(x, y, &loc);
x = loc.fX;
@ -140,7 +142,7 @@ void GrStencilAndCoverTextContext::onDrawText(const GrPaint& paint,
const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio);
if (glyph.fWidth) {
this->appendGlyph(glyph.getGlyphID(), SkFixedToScalar(fx), SkFixedToScalar(fy));
this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)));
}
fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio);
@ -151,12 +153,12 @@ void GrStencilAndCoverTextContext::onDrawText(const GrPaint& paint,
}
void GrStencilAndCoverTextContext::onDrawPosText(const GrPaint& paint,
const SkPaint& skPaint,
const char text[],
size_t byteLength,
const SkScalar pos[],
int scalarsPerPosition,
const SkPoint& offset) {
const SkPaint& skPaint,
const char text[],
size_t byteLength,
const SkScalar pos[],
int scalarsPerPosition,
const SkPoint& offset) {
SkASSERT(byteLength == 0 || text != NULL);
SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
@ -189,7 +191,7 @@ void GrStencilAndCoverTextContext::onDrawPosText(const GrPaint& paint,
SkPoint loc;
alignProc(tmsLoc, glyph, &loc);
this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y());
this->appendGlyph(glyph, loc);
}
pos += scalarsPerPosition;
}
@ -232,15 +234,19 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint,
const bool otherBackendsWillDrawAsPaths =
SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);
fNeedsDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
fUsingDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
kMaxAccuracy_RenderMode == renderMode &&
SkToBool(fContextInitialMatrix.getType() &
(SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask));
if (fNeedsDeviceSpaceGlyphs) {
if (fUsingDeviceSpaceGlyphs) {
// SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
SkASSERT(!fContextInitialMatrix.hasPerspective());
// The whole shape (including stroke) will be baked into the glyph outlines. Make
// NVPR just fill the baked shapes.
fStroke = SkStrokeRec(SkStrokeRec::kFill_InitStyle);
fTextRatio = fTextInverseRatio = 1.0f;
// Glyphs loaded by GPU path rendering have an inverted y-direction.
@ -254,30 +260,27 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint,
m.postScale(1, -1);
fPaint.localCoordChangeInverse(m);
// The whole shape (including stroke) will be baked into the glyph outlines. Make
// NVPR just fill the baked shapes.
fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, &fContextInitialMatrix, false);
fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
&fGlyphCache->getDescriptor(),
SkStrokeRec(SkStrokeRec::kFill_InitStyle));
&fGlyphCache->getDescriptor(), fStroke);
} else {
// Don't bake strokes into the glyph outlines. We will stroke the glyphs
// using the GPU instead. This is the fast path.
SkStrokeRec gpuStroke = SkStrokeRec(fSkPaint);
fStroke = SkStrokeRec(fSkPaint);
fSkPaint.setStyle(SkPaint::kFill_Style);
if (gpuStroke.isHairlineStyle()) {
if (fStroke.isHairlineStyle()) {
// Approximate hairline stroke.
SkScalar strokeWidth = SK_Scalar1 /
(SkVector::Make(fContextInitialMatrix.getScaleX(),
fContextInitialMatrix.getSkewY()).length());
gpuStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/);
fStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/);
} else if (fSkPaint.isFakeBoldText() &&
#ifdef SK_USE_FREETYPE_EMBOLDEN
kMaxPerformance_RenderMode == renderMode &&
#endif
SkStrokeRec::kStroke_Style != gpuStroke.getStyle()) {
SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
// Instead of baking fake bold into the glyph outlines, do it with the GPU stroke.
SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(),
@ -285,8 +288,8 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint,
kStdFakeBoldInterpValues,
kStdFakeBoldInterpLength);
SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale);
gpuStroke.setStrokeStyle(gpuStroke.needToApply() ? gpuStroke.getWidth() + extra : extra,
true /*strokeAndFill*/);
fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
true /*strokeAndFill*/);
fSkPaint.setFakeBoldText(false);
}
@ -299,9 +302,9 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint,
fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
// Compensate for the glyphs being scaled by fTextRatio.
if (!gpuStroke.isFillStyle()) {
gpuStroke.setStrokeStyle(gpuStroke.getWidth() / fTextRatio,
SkStrokeRec::kStrokeAndFill_Style == gpuStroke.getStyle());
if (!fStroke.isFillStyle()) {
fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
}
fSkPaint.setLinearText(true);
@ -328,9 +331,9 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint,
fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, false);
fGlyphs = canUseRawPaths ?
get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, gpuStroke) :
get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, fStroke) :
get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
&fGlyphCache->getDescriptor(), gpuStroke);
&fGlyphCache->getDescriptor(), fStroke);
}
fStateRestore.set(&fDrawState);
@ -347,31 +350,90 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint,
*fDrawState.stencil() = kStencilPass;
SkASSERT(0 == fPendingGlyphCount);
SkASSERT(0 == fQueuedGlyphCount);
SkASSERT(kGlyphBufferSize == fFallbackGlyphsIdx);
}
inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x, float y) {
if (fPendingGlyphCount >= kGlyphBufferSize) {
bool GrStencilAndCoverTextContext::mapToFallbackContext(GrContext::AutoMatrix& autoMatrix,
SkMatrix* inverse) {
// The current view matrix is flipped because GPU path rendering glyphs have an
// inverted y-direction. Unflip the view matrix for the fallback context. If using
// device-space glyphs, we'll also need to restore the original view matrix since
// we moved that transfomation into our local glyph cache for this scenario. Also
// track the inverse operation so the caller can unmap the paint and glyph positions.
if (fUsingDeviceSpaceGlyphs) {
autoMatrix.set(fContext, fContextInitialMatrix);
if (!fContextInitialMatrix.invert(inverse)) {
return false;
}
inverse->preScale(1, -1);
} else {
inverse->setScale(1, -1);
const SkMatrix& unflip = *inverse; // unflip is equal to its own inverse.
autoMatrix.setPreConcat(fContext, unflip);
}
return true;
}
inline void GrStencilAndCoverTextContext::appendGlyph(const SkGlyph& glyph, const SkPoint& pos) {
if (fQueuedGlyphCount >= fFallbackGlyphsIdx) {
SkASSERT(fQueuedGlyphCount == fFallbackGlyphsIdx);
this->flush();
}
fIndexBuffer[fPendingGlyphCount] = glyphID;
fTransformBuffer[2 * fPendingGlyphCount] = fTextInverseRatio * x;
fTransformBuffer[2 * fPendingGlyphCount + 1] = -fTextInverseRatio * y;
// Stick the glyphs we can't draw at the end of the buffer, growing backwards.
int index = (SkMask::kARGB32_Format == glyph.fMaskFormat) ?
--fFallbackGlyphsIdx : fQueuedGlyphCount++;
++fPendingGlyphCount;
fGlyphIndices[index] = glyph.getGlyphID();
fGlyphPositions[index].set(fTextInverseRatio * pos.x(), -fTextInverseRatio * pos.y());
}
static const SkScalar* get_xy_scalar_array(const SkPoint* pointArray) {
GR_STATIC_ASSERT(2 * sizeof(SkScalar) == sizeof(SkPoint));
GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
return &pointArray[0].fX;
}
void GrStencilAndCoverTextContext::flush() {
if (0 == fPendingGlyphCount) {
return;
if (fQueuedGlyphCount > 0) {
fDrawTarget->drawPaths(&fDrawState, fGlyphs,
fGlyphIndices, GrPathRange::kU16_PathIndexType,
get_xy_scalar_array(fGlyphPositions),
GrPathRendering::kTranslate_PathTransformType,
fQueuedGlyphCount, GrPathRendering::kWinding_FillType);
fQueuedGlyphCount = 0;
}
fDrawTarget->drawPaths(&fDrawState, fGlyphs, fIndexBuffer, GrPathRange::kU16_PathIndexType,
fTransformBuffer, GrPathRendering::kTranslate_PathTransformType,
fPendingGlyphCount, GrPathRendering::kWinding_FillType);
if (fFallbackGlyphsIdx < kGlyphBufferSize) {
int fallbackGlyphCount = kGlyphBufferSize - fFallbackGlyphsIdx;
fPendingGlyphCount = 0;
GrPaint paintFallback(fPaint);
SkPaint skPaintFallback(fSkPaint);
if (!fUsingDeviceSpaceGlyphs) {
fStroke.applyToPaint(&skPaintFallback);
}
skPaintFallback.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for.
skPaintFallback.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
GrContext::AutoMatrix autoMatrix;
SkMatrix inverse;
if (this->mapToFallbackContext(autoMatrix, &inverse)) {
paintFallback.localCoordChangeInverse(inverse);
inverse.mapPoints(&fGlyphPositions[fFallbackGlyphsIdx], fallbackGlyphCount);
}
fFallbackTextContext->drawPosText(paintFallback, skPaintFallback,
(char*)&fGlyphIndices[fFallbackGlyphsIdx],
2 * fallbackGlyphCount,
get_xy_scalar_array(&fGlyphPositions[fFallbackGlyphsIdx]),
2, SkPoint::Make(0, 0));
fFallbackGlyphsIdx = kGlyphBufferSize;
}
}
void GrStencilAndCoverTextContext::finish() {

View File

@ -57,11 +57,13 @@ private:
float fTextInverseRatio;
SkGlyphCache* fGlyphCache;
GrPathRange* fGlyphs;
uint16_t fIndexBuffer[kGlyphBufferSize];
float fTransformBuffer[2 * kGlyphBufferSize];
int fPendingGlyphCount;
SkStrokeRec fStroke;
uint16_t fGlyphIndices[kGlyphBufferSize];
SkPoint fGlyphPositions[kGlyphBufferSize];
int fQueuedGlyphCount;
int fFallbackGlyphsIdx;
SkMatrix fContextInitialMatrix;
bool fNeedsDeviceSpaceGlyphs;
bool fUsingDeviceSpaceGlyphs;
GrStencilAndCoverTextContext(GrContext*, const SkDeviceProperties&);
@ -76,7 +78,8 @@ private:
const SkPoint& offset) SK_OVERRIDE;
void init(const GrPaint&, const SkPaint&, size_t textByteLength, RenderMode);
void appendGlyph(uint16_t glyphID, float x, float y);
bool mapToFallbackContext(GrContext::AutoMatrix&, SkMatrix* inverse);
void appendGlyph(const SkGlyph&, const SkPoint&);
void flush();
void finish();