Convert drawText to using the find and place code.

BUG=skia:

Review URL: https://codereview.chromium.org/1448453002
This commit is contained in:
herb 2015-11-18 10:54:39 -08:00 committed by Commit bot
parent 5520dede29
commit e59124ed1a
3 changed files with 159 additions and 191 deletions

View File

@ -34,7 +34,6 @@
#include "SkUtils.h"
#include "SkVertState.h"
#include "SkAutoKern.h"
#include "SkBitmapProcShader.h"
#include "SkDrawProcs.h"
#include "SkMatrixUtils.h"
@ -1374,26 +1373,6 @@ void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& ori
#include "SkTextToPathIter.h"
#include "SkUtils.h"
static void measure_text(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
const char text[], size_t byteLength, SkVector* stopVector) {
SkFixed x = 0, y = 0;
const char* stop = text + byteLength;
SkAutoKern autokern;
while (text < stop) {
// don't need x, y here, since all subpixel variants will have the
// same advance
const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
x += autokern.adjust(glyph) + glyph.fAdvanceX;
y += glyph.fAdvanceY;
}
stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y));
SkASSERT(text == stop);
}
bool SkDraw::ShouldDrawTextAsPaths(const SkPaint& paint, const SkMatrix& ctm) {
// hairline glyphs are fast enough so we don't need to cache them
if (SkPaint::kStroke_Style == paint.getStyle() && 0 == paint.getStrokeWidth()) {
@ -1601,38 +1580,10 @@ void SkDraw::drawText(const char text[], size_t byteLength,
return;
}
SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
SkAutoGlyphCache autoCache(paint, &fDevice->surfaceProps(), fMatrix);
SkGlyphCache* cache = autoCache.getCache();
// transform our starting point
{
SkPoint loc;
fMatrix->mapXY(x, y, &loc);
x = loc.fX;
y = loc.fY;
}
// need to measure first
if (paint.getTextAlign() != SkPaint::kLeft_Align) {
SkVector stop;
measure_text(cache, glyphCacheProc, text, byteLength, &stop);
SkScalar stopX = stop.fX;
SkScalar stopY = stop.fY;
if (paint.getTextAlign() == SkPaint::kCenter_Align) {
stopX = SkScalarHalf(stopX);
stopY = SkScalarHalf(stopY);
}
x -= stopX;
y -= stopY;
}
const char* stop = text + byteLength;
SkAAClipBlitter aaBlitter;
SkAutoBlitterChoose blitterChooser;
SkBlitter* blitter = nullptr;
@ -1645,38 +1596,16 @@ void SkDraw::drawText(const char text[], size_t byteLength,
}
}
SkAutoKern autokern;
SkDraw1Glyph d1g;
SkDraw1Glyph::Proc proc = d1g.init(this, blitter, cache, paint);
SkFixed fxMask = ~0;
SkFixed fyMask = ~0;
if (cache->isSubpixel()) {
SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(*fMatrix);
if (kX_SkAxisAlignment == baseline) {
fyMask = 0;
d1g.fHalfSampleY = SK_ScalarHalf;
} else if (kY_SkAxisAlignment == baseline) {
fxMask = 0;
d1g.fHalfSampleX = SK_ScalarHalf;
SkFindAndPlaceGlyph::ProcessText(
text, byteLength, {x, y}, *fMatrix, paint.getTextAlign(), glyphCacheProc, cache,
[&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
position += rounding;
proc(d1g, SkScalarTo48Dot16(position.fX), SkScalarTo48Dot16(position.fY), glyph);
}
}
Sk48Dot16 fx = SkScalarTo48Dot16(x + d1g.fHalfSampleX);
Sk48Dot16 fy = SkScalarTo48Dot16(y + d1g.fHalfSampleY);
while (text < stop) {
const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
fx += autokern.adjust(glyph);
if (glyph.fWidth) {
proc(d1g, fx, fy, glyph);
}
fx += glyph.fAdvanceX;
fy += glyph.fAdvanceY;
}
);
}
//////////////////////////////////////////////////////////////////////////////
@ -1773,7 +1702,7 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
textAlignment, glyphCacheProc, cache,
[&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
position += rounding;
proc(d1g, SkScalarToFixed(position.fX), SkScalarToFixed(position.fY), glyph);
proc(d1g, SkScalarTo48Dot16(position.fX), SkScalarTo48Dot16(position.fY), glyph);
}
);
}

View File

@ -8,6 +8,7 @@
#ifndef SkFindAndPositionGlyph_DEFINED
#define SkFindAndPositionGlyph_DEFINED
#include "SkAutoKern.h"
#include "SkGlyph.h"
#include "SkGlyphCache.h"
#include "SkPaint.h"
@ -30,6 +31,10 @@ struct SkMaxSizeOf<H, Ts...> {
class SkFindAndPlaceGlyph {
public:
template<typename ProcessOneGlyph>
static void ProcessText(const char text[], size_t byteLength, SkPoint offset, const
SkMatrix& matrix, SkPaint::Align textAlignment, SkDrawCacheProc& glyphCacheProc,
SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph);
// ProcessPosText handles all cases for finding and positioning glyphs. It has a very large
// multiplicity. It figures out the glyph, position and rounding and pass those parameters to
// processOneGlyph.
@ -48,7 +53,7 @@ public:
// This routine handles all of them using inline polymorphic variable (no heap allocation).
template<typename ProcessOneGlyph>
static void ProcessPosText(const char text[], size_t byteLength,
const SkPoint& offset, const SkMatrix& matrix,
SkPoint offset, const SkMatrix& matrix,
const SkScalar pos[], int scalarsPerPosition,
SkPaint::Align textAlignment, SkDrawCacheProc& glyphCacheProc,
SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph);
@ -142,7 +147,8 @@ private:
typedef PolymorphicVariant<PositionReaderInterface, HorizontalPositions, ArbitraryPositions>
PositionReader;
// MapperInterface given a point map it through the matrix. There are several shortcut variants.
// MapperInterface given a point map it through the matrix. There are several shortcut
// variants.
// * TranslationMapper - assumes a translation only matrix.
// * XScaleMapper - assumes an X scaling and a translation.
// * GeneralMapper - Does all other matricies.
@ -201,7 +207,7 @@ private:
typedef PolymorphicVariant<
MapperInterface, TranslationMapper, XScaleMapper, GeneralMapper> Mapper;
// Text alignment handles shifting the glyph based on its width.
// TextAlignmentAdjustment handles shifting the glyph based on its width.
static SkPoint TextAlignmentAdjustment(SkPaint::Align textAlignment, const SkGlyph& glyph) {
switch (textAlignment) {
case SkPaint::kLeft_Align:
@ -222,8 +228,7 @@ private:
// Needs to be a macro because you can't have a const float unless you make it constexpr.
#define kSubpixelRounding (SkFixedToScalar(SkGlyph::kSubpixelRound))
// Functions for handling sub-pixel aligned positions.
// The subpixel_position_rounding function returns a point suitable for rounding a sub-pixel
// The SubpixelPositionRounding function returns a point suitable for rounding a sub-pixel
// positioned glyph.
static SkPoint SubpixelPositionRounding(SkAxisAlignment axisAlignment) {
switch (axisAlignment) {
@ -238,7 +243,7 @@ private:
return {0.0f, 0.0f};
}
// The subpixel_position_alignment function produces a suitable position for the glyph cache to
// The SubpixelAlignment function produces a suitable position for the glyph cache to
// produce the correct sub-pixel alignment. If a position is aligned with an axis a shortcut
// of 0 is used for the sub-pixel position.
static SkIPoint SubpixelAlignment(SkAxisAlignment axisAlignment, SkPoint position) {
@ -266,36 +271,47 @@ private:
public:
virtual ~GlyphFindAndPlaceInterface() { };
// findAndPositionGlyph calculates the position of the glyph, finds the glyph, and
// returns the position of where the next glyph will be using the glyph's advance and
// possibly kerning. The returned position is used by drawText, but ignored by drawPosText.
// The compiler should prune all this calculation if the return value is not used.
//
// This should be a pure virtual, but some versions of GCC <= 4.8 have a bug that causes a
// compile error.
// See GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60277
virtual void findAndPositionGlyph(const char** text, SkPoint position,
ProcessOneGlyph&& processOneGlyph) { };
virtual SkPoint findAndPositionGlyph(
const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) {
SkFAIL("Should never get here.");
return {0.0f, 0.0f};
};
};
// GlyphFindAndPlaceSubpixel handles finding and placing glyphs when sub-pixel positioning is
// requested. After it has found and placed the glyph it calls the templated function
// ProcessOneGlyph in order to actually perform an action.
template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, SkAxisAlignment kAxisAlignment>
template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment,
SkAxisAlignment kAxisAlignment>
class GlyphFindAndPlaceSubpixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> {
public:
GlyphFindAndPlaceSubpixel(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc)
: fCache(cache), fGlyphCacheProc(glyphCacheProc) {
}
: fCache(cache)
, fGlyphCacheProc(glyphCacheProc) { }
void findAndPositionGlyph(const char** text, SkPoint position,
ProcessOneGlyph&& processOneGlyph) override {
SkPoint findAndPositionGlyph(
const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override {
SkPoint finalPosition = position;
if (kTextAlignment != SkPaint::kLeft_Align) {
// Get the width of an un-sub-pixel positioned glyph for calculating the alignment.
// This is not needed for kLeftAlign because its adjustment is always {0, 0}.
// Get the width of an un-sub-pixel positioned glyph for calculating the
// alignment. This is not needed for kLeftAlign because its adjustment is
// always {0, 0}.
const char* tempText = *text;
const SkGlyph &metricGlyph = fGlyphCacheProc(fCache, &tempText, 0, 0);
if (metricGlyph.fWidth <= 0) {
// Exiting early, be sure to update text pointer.
*text = tempText;
return;
return finalPosition + SkPoint{SkFixedToScalar(metricGlyph.fAdvanceX),
SkFixedToScalar(metricGlyph.fAdvanceY)};
}
// Adjust the final position by the alignment adjustment.
@ -304,14 +320,16 @@ private:
// Find the glyph.
SkIPoint lookupPosition = SubpixelAlignment(kAxisAlignment, finalPosition);
const SkGlyph& renderGlyph = fGlyphCacheProc(
fCache, text, lookupPosition.fX, lookupPosition.fY);
const SkGlyph& renderGlyph =
fGlyphCacheProc(fCache, text, lookupPosition.fX, lookupPosition.fY);
// If the glyph has no width (no pixels) then don't bother processing it.
if (renderGlyph.fWidth > 0) {
processOneGlyph(renderGlyph, finalPosition,
SubpixelPositionRounding(kAxisAlignment));
}
return finalPosition + SkPoint{SkFixedToScalar(renderGlyph.fAdvanceX),
SkFixedToScalar(renderGlyph.fAdvanceY)};
}
private:
@ -319,28 +337,43 @@ private:
SkDrawCacheProc fGlyphCacheProc;
};
enum SelectKerning {
kNoKerning = false,
kUseKerning = true
};
// GlyphFindAndPlaceFullPixel handles finding and placing glyphs when no sub-pixel
// positioning is requested.
template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment>
// positioning is requested. The kUseKerning argument should be true for drawText, and false
// for drawPosText.
template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, SelectKerning kUseKerning>
class GlyphFindAndPlaceFullPixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> {
public:
GlyphFindAndPlaceFullPixel(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc)
: fCache(cache), fGlyphCacheProc(glyphCacheProc) { }
: fCache(cache), fGlyphCacheProc(glyphCacheProc) {
// Kerning can only be used with SkPaint::kLeft_Align
static_assert(!kUseKerning || SkPaint::kLeft_Align == kTextAlignment,
"Kerning can only be used with left aligned text.");
}
void findAndPositionGlyph(const char** text, SkPoint position,
ProcessOneGlyph&& processOneGlyph) override {
SkPoint findAndPositionGlyph(
const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override {
SkPoint finalPosition = position;
const SkGlyph& glyph = fGlyphCacheProc(fCache, text, 0, 0);
if (glyph.fWidth <= 0) {
return;
if (kUseKerning) {
finalPosition += {SkFixedToScalar(fAutoKern.adjust(glyph)), 0.0f};
}
finalPosition -= TextAlignmentAdjustment(kTextAlignment, glyph);
processOneGlyph(glyph, finalPosition, {SK_ScalarHalf, SK_ScalarHalf});
if (glyph.fWidth > 0) {
finalPosition -= TextAlignmentAdjustment(kTextAlignment, glyph);
processOneGlyph(glyph, finalPosition, {SK_ScalarHalf, SK_ScalarHalf});
}
return finalPosition + SkPoint{SkFixedToScalar(glyph.fAdvanceX),
SkFixedToScalar(glyph.fAdvanceY)};
}
private:
SkGlyphCache* const fCache;
SkDrawCacheProc fGlyphCacheProc;
SkAutoKern fAutoKern;
};
// GlyphFindAndPlace is a large variant that encapsulates the multiple types of finding and
@ -365,9 +398,9 @@ private:
GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kRight_Align, kX_SkAxisAlignment >,
GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kRight_Align, kY_SkAxisAlignment >,
// Full pixel
GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kLeft_Align >,
GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kCenter_Align>,
GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kRight_Align >
GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kLeft_Align, kNoKerning>,
GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kCenter_Align, kNoKerning>,
GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kRight_Align, kNoKerning>
>;
// InitSubpixel is a helper function for initializing all the variants of
@ -396,11 +429,30 @@ private:
break;
}
}
static SkPoint MeasureText(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
const char text[], size_t byteLength) {
SkFixed x = 0, y = 0;
const char* stop = text + byteLength;
SkAutoKern autokern;
while (text < stop) {
// don't need x, y here, since all subpixel variants will have the
// same advance
const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
x += autokern.adjust(glyph) + glyph.fAdvanceX;
y += glyph.fAdvanceY;
}
SkASSERT(text == stop);
return {SkFixedToScalar(x), SkFixedToScalar(y)};
}
};
template<typename ProcessOneGlyph>
inline void SkFindAndPlaceGlyph::ProcessPosText(
const char text[], size_t byteLength, const SkPoint& offset, const SkMatrix& matrix,
const char text[], size_t byteLength, SkPoint offset, const SkMatrix& matrix,
const SkScalar pos[], int scalarsPerPosition, SkPaint::Align textAlignment,
SkDrawCacheProc& glyphCacheProc, SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) {
@ -475,17 +527,17 @@ inline void SkFindAndPlaceGlyph::ProcessPosText(
case SkPaint::kLeft_Align:
to_init->template initialize<
GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
SkPaint::kLeft_Align>>(cache, glyphCacheProc);
SkPaint::kLeft_Align, kNoKerning>>(cache, glyphCacheProc);
break;
case SkPaint::kCenter_Align:
to_init->template initialize<
GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
SkPaint::kCenter_Align>>(cache, glyphCacheProc);
SkPaint::kCenter_Align, kNoKerning>>(cache, glyphCacheProc);
break;
case SkPaint::kRight_Align:
to_init->template initialize<
GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
SkPaint::kRight_Align>>(cache, glyphCacheProc);
SkPaint::kRight_Align, kNoKerning>>(cache, glyphCacheProc);
break;
}
}
@ -500,4 +552,48 @@ inline void SkFindAndPlaceGlyph::ProcessPosText(
}
}
template<typename ProcessOneGlyph>
inline void SkFindAndPlaceGlyph::ProcessText(
const char text[], size_t byteLength, SkPoint offset, const SkMatrix& matrix,
SkPaint::Align textAlignment, SkDrawCacheProc& glyphCacheProc, SkGlyphCache* cache,
ProcessOneGlyph&& processOneGlyph) {
// transform the starting point
matrix.mapPoints(&offset, 1);
// need to measure first
if (textAlignment != SkPaint::kLeft_Align) {
SkVector stop = MeasureText(cache, glyphCacheProc, text, byteLength);
if (textAlignment == SkPaint::kCenter_Align) {
stop *= SK_ScalarHalf;
}
offset -= stop;
}
GlyphFindAndPlace<ProcessOneGlyph> findAndPosition{
[&](typename GlyphFindAndPlace<ProcessOneGlyph>::Variants* to_init) {
if (cache->isSubpixel()) {
SkAxisAlignment axisAlignment = SkComputeAxisAlignmentForHText(matrix);
InitSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>(
to_init, axisAlignment, cache, glyphCacheProc);
} else {
to_init->template initialize<
GlyphFindAndPlaceFullPixel<
ProcessOneGlyph, SkPaint::kLeft_Align, kUseKerning>>(
cache, glyphCacheProc);
}
}
};
const char* stop = text + byteLength;
SkPoint current = offset;
while (text < stop) {
current =
findAndPosition->findAndPositionGlyph(
&text, current, skstd::forward<ProcessOneGlyph>(processOneGlyph));
}
}
#endif // SkFindAndPositionGlyph_DEFINED

View File

@ -826,73 +826,16 @@ void GrAtlasTextContext::internalDrawBMPText(GrAtlasTextBlob* blob, int runIndex
// Get GrFontScaler from cache
GrFontScaler* fontScaler = GetGrFontScaler(cache);
// transform our starting point
{
SkPoint loc;
viewMatrix.mapXY(x, y, &loc);
x = loc.fX;
y = loc.fY;
}
// need to measure first
if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
SkVector stopVector;
MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
SkScalar stopX = stopVector.fX;
SkScalar stopY = stopVector.fY;
if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
stopX = SkScalarHalf(stopX);
stopY = SkScalarHalf(stopY);
SkFindAndPlaceGlyph::ProcessText(
text, byteLength, {x, y}, viewMatrix, skPaint.getTextAlign(), glyphCacheProc, cache,
[&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
position += rounding;
this->bmpAppendGlyph(
blob, runIndex, glyph,
SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
color, fontScaler, clipRect);
}
x -= stopX;
y -= stopY;
}
const char* stop = text + byteLength;
SkAutoKern autokern;
SkFixed fxMask = ~0;
SkFixed fyMask = ~0;
SkScalar halfSampleX, halfSampleY;
if (cache->isSubpixel()) {
halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
if (kX_SkAxisAlignment == baseline) {
fyMask = 0;
halfSampleY = SK_ScalarHalf;
} else if (kY_SkAxisAlignment == baseline) {
fxMask = 0;
halfSampleX = SK_ScalarHalf;
}
} else {
halfSampleX = halfSampleY = SK_ScalarHalf;
}
Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
while (text < stop) {
const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
fx += autokern.adjust(glyph);
if (glyph.fWidth) {
this->bmpAppendGlyph(blob,
runIndex,
glyph,
Sk48Dot16FloorToInt(fx),
Sk48Dot16FloorToInt(fy),
color,
fontScaler,
clipRect);
}
fx += glyph.fAdvanceX;
fy += glyph.fAdvanceY;
}
);
}
void GrAtlasTextContext::internalDrawBMPPosText(GrAtlasTextBlob* blob, int runIndex,