Add color emoji fallback for nvpr text
BUG=skia: Review URL: https://codereview.chromium.org/759613005
This commit is contained in:
parent
371bcbcb9f
commit
20b373cf31
@ -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 &&
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user