Simplify D1G so that it can inline DrawOneGlyph, and fix a bug in codegen
that only happens on ARM64 using GCC 4.9. Review URL: https://codereview.chromium.org/1507633004
This commit is contained in:
parent
ba923d38a5
commit
d4c24f6749
@ -40,6 +40,9 @@
|
||||
|
||||
//#define TRACE_BITMAP_DRAWS
|
||||
|
||||
// Helper function to fix code gen bug on ARM64.
|
||||
// See SkFindAndPlaceGlyph.h for more details.
|
||||
void FixGCC49Arm64Bug(int v) { }
|
||||
|
||||
/** Helper for allocating small blitters on the stack.
|
||||
*/
|
||||
@ -1423,170 +1426,120 @@ void SkDraw::drawText_asPaths(const char text[], size_t byteLength,
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
struct SkDraw1Glyph {
|
||||
const SkDraw* fDraw;
|
||||
const SkRegion* fClip;
|
||||
const SkAAClip* fAAClip;
|
||||
SkBlitter* fBlitter;
|
||||
SkGlyphCache* fCache;
|
||||
const SkPaint* fPaint;
|
||||
SkIRect fClipBounds;
|
||||
/** Half the sampling frequency of the rasterized glyph in x. */
|
||||
SkScalar fHalfSampleX;
|
||||
/** Half the sampling frequency of the rasterized glyph in y. */
|
||||
SkScalar fHalfSampleY;
|
||||
|
||||
/** Draws one glyph.
|
||||
*
|
||||
* The x and y are pre-biased, so implementations may just truncate them.
|
||||
* i.e. half the sampling frequency has been added.
|
||||
* e.g. 1/2 or 1/(2^(SkGlyph::kSubBits+1)) has already been added.
|
||||
* This added bias can be found in fHalfSampleX,Y.
|
||||
*/
|
||||
typedef void (*Proc)(const SkDraw1Glyph&, Sk48Dot16 x, Sk48Dot16 y, const SkGlyph&);
|
||||
class DrawOneGlyph {
|
||||
public:
|
||||
DrawOneGlyph(const SkDraw& draw, const SkPaint& paint, SkGlyphCache* cache, SkBlitter* blitter)
|
||||
: fUseRegionToDraw(UsingRegionToDraw(draw.fRC))
|
||||
, fGlyphCache(cache)
|
||||
, fBlitter(blitter)
|
||||
, fClip(fUseRegionToDraw ? &draw.fRC->bwRgn() : nullptr)
|
||||
, fDraw(draw)
|
||||
, fPaint(paint)
|
||||
, fClipBounds(PickClipBounds(draw)) { }
|
||||
|
||||
Proc init(const SkDraw* draw, SkBlitter* blitter, SkGlyphCache* cache,
|
||||
const SkPaint&);
|
||||
void operator()(const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
|
||||
position += rounding;
|
||||
Sk48Dot16 fx = SkScalarTo48Dot16(position.fX);
|
||||
Sk48Dot16 fy = SkScalarTo48Dot16(position.fY);
|
||||
// Prevent glyphs from being drawn outside of or straddling the edge of device space.
|
||||
if ((fx >> 16) > INT_MAX - (INT16_MAX + UINT16_MAX) ||
|
||||
(fx >> 16) < INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/) ||
|
||||
(fy >> 16) > INT_MAX - (INT16_MAX + UINT16_MAX) ||
|
||||
(fy >> 16) < INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int left = Sk48Dot16FloorToInt(fx);
|
||||
int top = Sk48Dot16FloorToInt(fy);
|
||||
SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
|
||||
|
||||
left += glyph.fLeft;
|
||||
top += glyph.fTop;
|
||||
|
||||
int right = left + glyph.fWidth;
|
||||
int bottom = top + glyph.fHeight;
|
||||
|
||||
SkMask mask;
|
||||
mask.fBounds.set(left, top, right, bottom);
|
||||
|
||||
if (fUseRegionToDraw) {
|
||||
SkRegion::Cliperator clipper(*fClip, mask.fBounds);
|
||||
|
||||
if (!clipper.done() && this->getImageData(glyph, &mask)) {
|
||||
const SkIRect& cr = clipper.rect();
|
||||
do {
|
||||
this->blitMask(mask, cr);
|
||||
clipper.next();
|
||||
} while (!clipper.done());
|
||||
}
|
||||
} else {
|
||||
SkIRect storage;
|
||||
SkIRect* bounds = &mask.fBounds;
|
||||
|
||||
// this extra test is worth it, assuming that most of the time it succeeds
|
||||
// since we can avoid writing to storage
|
||||
if (!fClipBounds.containsNoEmptyCheck(mask.fBounds)) {
|
||||
if (!storage.intersectNoEmptyCheck(mask.fBounds, fClipBounds))
|
||||
return;
|
||||
bounds = &storage;
|
||||
}
|
||||
|
||||
if (this->getImageData(glyph, &mask)) {
|
||||
this->blitMask(mask, *bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static bool UsingRegionToDraw(const SkRasterClip* rClip) {
|
||||
return rClip->isBW() && !rClip->isRect();
|
||||
}
|
||||
|
||||
static SkIRect PickClipBounds(const SkDraw& draw) {
|
||||
const SkRasterClip& rasterClip = *draw.fRC;
|
||||
|
||||
if (rasterClip.isBW()) {
|
||||
return rasterClip.bwRgn().getBounds();
|
||||
} else {
|
||||
return rasterClip.aaRgn().getBounds();
|
||||
}
|
||||
}
|
||||
|
||||
bool getImageData(const SkGlyph& glyph, SkMask* mask) {
|
||||
uint8_t* bits = (uint8_t*)(fGlyphCache->findImage(glyph));
|
||||
if (nullptr == bits) {
|
||||
return false; // can't rasterize glyph
|
||||
}
|
||||
mask->fImage = bits;
|
||||
mask->fRowBytes = glyph.rowBytes();
|
||||
mask->fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
|
||||
return true;
|
||||
}
|
||||
|
||||
// call this instead of fBlitter->blitMask() since this wrapper will handle
|
||||
// the case when the mask is ARGB32_Format
|
||||
//
|
||||
void blitMask(const SkMask& mask, const SkIRect& clip) const {
|
||||
if (SkMask::kARGB32_Format == mask.fFormat) {
|
||||
this->blitMaskAsSprite(mask);
|
||||
SkBitmap bm;
|
||||
bm.installPixels(
|
||||
SkImageInfo::MakeN32Premul(mask.fBounds.width(), mask.fBounds.height()),
|
||||
(SkPMColor*)mask.fImage, mask.fRowBytes);
|
||||
|
||||
fDraw.drawSprite(bm, mask.fBounds.x(), mask.fBounds.y(), fPaint);
|
||||
} else {
|
||||
fBlitter->blitMask(mask, clip);
|
||||
}
|
||||
}
|
||||
|
||||
// mask must be kARGB32_Format
|
||||
void blitMaskAsSprite(const SkMask& mask) const;
|
||||
const bool fUseRegionToDraw;
|
||||
SkGlyphCache * const fGlyphCache;
|
||||
SkBlitter * const fBlitter;
|
||||
const SkRegion* const fClip;
|
||||
const SkDraw& fDraw;
|
||||
const SkPaint& fPaint;
|
||||
const SkIRect fClipBounds;
|
||||
};
|
||||
|
||||
static void D1G_RectClip(const SkDraw1Glyph& state, Sk48Dot16 fx, Sk48Dot16 fy,
|
||||
const SkGlyph& glyph) {
|
||||
// Prevent glyphs from being drawn outside of or straddling the edge of device space.
|
||||
if ((fx >> 16) > INT_MAX - (INT16_MAX + UINT16_MAX) ||
|
||||
(fx >> 16) < INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/) ||
|
||||
(fy >> 16) > INT_MAX - (INT16_MAX + UINT16_MAX) ||
|
||||
(fy >> 16) < INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int left = Sk48Dot16FloorToInt(fx);
|
||||
int top = Sk48Dot16FloorToInt(fy);
|
||||
SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
|
||||
SkASSERT((nullptr == state.fClip && state.fAAClip) ||
|
||||
(state.fClip && nullptr == state.fAAClip && state.fClip->isRect()));
|
||||
|
||||
left += glyph.fLeft;
|
||||
top += glyph.fTop;
|
||||
|
||||
int right = left + glyph.fWidth;
|
||||
int bottom = top + glyph.fHeight;
|
||||
|
||||
SkMask mask;
|
||||
SkIRect storage;
|
||||
SkIRect* bounds = &mask.fBounds;
|
||||
|
||||
mask.fBounds.set(left, top, right, bottom);
|
||||
|
||||
// this extra test is worth it, assuming that most of the time it succeeds
|
||||
// since we can avoid writing to storage
|
||||
if (!state.fClipBounds.containsNoEmptyCheck(left, top, right, bottom)) {
|
||||
if (!storage.intersectNoEmptyCheck(mask.fBounds, state.fClipBounds))
|
||||
return;
|
||||
bounds = &storage;
|
||||
}
|
||||
|
||||
uint8_t* aa = (uint8_t*)glyph.fImage;
|
||||
if (nullptr == aa) {
|
||||
aa = (uint8_t*)state.fCache->findImage(glyph);
|
||||
if (nullptr == aa) {
|
||||
return; // can't rasterize glyph
|
||||
}
|
||||
}
|
||||
|
||||
mask.fRowBytes = glyph.rowBytes();
|
||||
mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
|
||||
mask.fImage = aa;
|
||||
state.blitMask(mask, *bounds);
|
||||
}
|
||||
|
||||
static void D1G_RgnClip(const SkDraw1Glyph& state, Sk48Dot16 fx, Sk48Dot16 fy,
|
||||
const SkGlyph& glyph) {
|
||||
int left = Sk48Dot16FloorToInt(fx);
|
||||
int top = Sk48Dot16FloorToInt(fy);
|
||||
SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
|
||||
SkASSERT(!state.fClip->isRect());
|
||||
|
||||
SkMask mask;
|
||||
|
||||
left += glyph.fLeft;
|
||||
top += glyph.fTop;
|
||||
|
||||
mask.fBounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight);
|
||||
SkRegion::Cliperator clipper(*state.fClip, mask.fBounds);
|
||||
|
||||
if (!clipper.done()) {
|
||||
const SkIRect& cr = clipper.rect();
|
||||
const uint8_t* aa = (uint8_t*)state.fCache->findImage(glyph);
|
||||
if (nullptr == aa) {
|
||||
return;
|
||||
}
|
||||
|
||||
mask.fRowBytes = glyph.rowBytes();
|
||||
mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
|
||||
mask.fImage = (uint8_t*)aa;
|
||||
do {
|
||||
state.blitMask(mask, cr);
|
||||
clipper.next();
|
||||
} while (!clipper.done());
|
||||
}
|
||||
}
|
||||
|
||||
SkDraw1Glyph::Proc SkDraw1Glyph::init(const SkDraw* draw, SkBlitter* blitter, SkGlyphCache* cache,
|
||||
const SkPaint& pnt) {
|
||||
fDraw = draw;
|
||||
fBlitter = blitter;
|
||||
fCache = cache;
|
||||
fPaint = &pnt;
|
||||
|
||||
if (cache->isSubpixel()) {
|
||||
fHalfSampleX = fHalfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
|
||||
} else {
|
||||
fHalfSampleX = fHalfSampleY = SK_ScalarHalf;
|
||||
}
|
||||
|
||||
if (draw->fRC->isBW()) {
|
||||
fAAClip = nullptr;
|
||||
fClip = &draw->fRC->bwRgn();
|
||||
fClipBounds = fClip->getBounds();
|
||||
if (fClip->isRect()) {
|
||||
return D1G_RectClip;
|
||||
} else {
|
||||
return D1G_RgnClip;
|
||||
}
|
||||
} else { // aaclip
|
||||
fAAClip = &draw->fRC->aaRgn();
|
||||
fClip = nullptr;
|
||||
fClipBounds = fAAClip->getBounds();
|
||||
return D1G_RectClip;
|
||||
}
|
||||
}
|
||||
|
||||
void SkDraw1Glyph::blitMaskAsSprite(const SkMask& mask) const {
|
||||
SkASSERT(SkMask::kARGB32_Format == mask.fFormat);
|
||||
|
||||
SkBitmap bm;
|
||||
bm.installPixels(SkImageInfo::MakeN32Premul(mask.fBounds.width(), mask.fBounds.height()),
|
||||
(SkPMColor*)mask.fImage, mask.fRowBytes);
|
||||
|
||||
fDraw->drawSprite(bm, mask.fBounds.x(), mask.fBounds.y(), *fPaint);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SkDraw::drawText(const char text[], size_t byteLength,
|
||||
SkScalar x, SkScalar y, const SkPaint& paint) const {
|
||||
@ -1606,25 +1559,17 @@ void SkDraw::drawText(const char text[], size_t byteLength,
|
||||
return;
|
||||
}
|
||||
|
||||
SkAutoGlyphCache autoCache(paint, &fDevice->surfaceProps(), fMatrix);
|
||||
SkGlyphCache* cache = autoCache.getCache();
|
||||
|
||||
SkAutoGlyphCache autoCache(paint, &fDevice->surfaceProps(), fMatrix);
|
||||
SkGlyphCache* cache = autoCache.getCache();
|
||||
|
||||
// The Blitter Choose needs to be live while using the blitter below.
|
||||
SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
|
||||
SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get());
|
||||
|
||||
SkDraw1Glyph d1g;
|
||||
SkDraw1Glyph::Proc proc = d1g.init(this, wrapper.getBlitter(), cache, paint);
|
||||
DrawOneGlyph drawOneGlyph(*this, paint, cache, wrapper.getBlitter());
|
||||
|
||||
SkFindAndPlaceGlyph::ProcessText(
|
||||
paint.getTextEncoding(), text, byteLength,
|
||||
{x, y}, *fMatrix, paint.getTextAlign(), cache,
|
||||
[&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
|
||||
position += rounding;
|
||||
proc(d1g, SkScalarTo48Dot16(position.fX), SkScalarTo48Dot16(position.fY), glyph);
|
||||
}
|
||||
);
|
||||
{x, y}, *fMatrix, paint.getTextAlign(), cache, drawOneGlyph);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@ -1696,24 +1641,18 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
|
||||
return;
|
||||
}
|
||||
|
||||
SkAutoGlyphCache autoCache(paint, &fDevice->surfaceProps(), fMatrix);
|
||||
SkGlyphCache* cache = autoCache.getCache();
|
||||
|
||||
// The Blitter Choose needs to be live while using the blitter below.
|
||||
SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
|
||||
SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get());
|
||||
|
||||
SkAutoGlyphCache autoCache(paint, &fDevice->surfaceProps(), fMatrix);
|
||||
SkGlyphCache* cache = autoCache.getCache();
|
||||
SkDraw1Glyph d1g;
|
||||
SkDraw1Glyph::Proc proc = d1g.init(this, wrapper.getBlitter(), cache, paint);
|
||||
SkPaint::Align textAlignment = paint.getTextAlign();
|
||||
DrawOneGlyph drawOneGlyph(*this, paint, cache, wrapper.getBlitter());
|
||||
SkPaint::Align textAlignment = paint.getTextAlign();
|
||||
|
||||
SkFindAndPlaceGlyph::ProcessPosText(
|
||||
paint.getTextEncoding(), text, byteLength,
|
||||
offset, *fMatrix, pos, scalarsPerPosition, textAlignment, cache,
|
||||
[&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
|
||||
position += rounding;
|
||||
proc(d1g, SkScalarTo48Dot16(position.fX), SkScalarTo48Dot16(position.fY), glyph);
|
||||
}
|
||||
);
|
||||
offset, *fMatrix, pos, scalarsPerPosition, textAlignment, cache, drawOneGlyph);
|
||||
}
|
||||
|
||||
#if defined _WIN32 && _MSC_VER >= 1300
|
||||
|
@ -30,6 +30,16 @@ struct SkMaxSizeOf<H, Ts...> {
|
||||
sizeof(H) >= SkMaxSizeOf<Ts...>::value ? sizeof(H) : SkMaxSizeOf<Ts...>::value;
|
||||
};
|
||||
|
||||
|
||||
// This is a temporary helper function to work around a bug in the code generation
|
||||
// for aarch64 (arm) on GCC 4.9. This bug does not show up on other platforms, so it
|
||||
// seems to be an aarch64 backend problem.
|
||||
//
|
||||
// GCC 4.9 on ARM64 does not generate the proper constructor code for PositionReader or
|
||||
// GlyphFindAndPlace. The vtable is not set properly without adding the fixme code.
|
||||
// The implementation is in SkDraw.cpp.
|
||||
extern void FixGCC49Arm64Bug(int v);
|
||||
|
||||
class SkFindAndPlaceGlyph {
|
||||
public:
|
||||
template<typename ProcessOneGlyph>
|
||||
@ -229,6 +239,9 @@ private:
|
||||
public:
|
||||
virtual ~PositionReaderInterface() { }
|
||||
virtual SkPoint nextPoint() = 0;
|
||||
// This is only here to fix a GCC 4.9 aarch64 code gen bug.
|
||||
// See comment at the top of the file.
|
||||
virtual int forceUseForBug() = 0;
|
||||
};
|
||||
|
||||
class HorizontalPositions final : public PositionReaderInterface {
|
||||
@ -241,6 +254,8 @@ private:
|
||||
return {x, 0};
|
||||
}
|
||||
|
||||
int forceUseForBug() override { return 1; }
|
||||
|
||||
private:
|
||||
const SkScalar* fPositions;
|
||||
};
|
||||
@ -256,6 +271,8 @@ private:
|
||||
return to_return;
|
||||
}
|
||||
|
||||
int forceUseForBug() override { return 2; }
|
||||
|
||||
private:
|
||||
const SkScalar* fPositions;
|
||||
};
|
||||
@ -410,7 +427,9 @@ private:
|
||||
class GlyphFindAndPlaceSubpixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> {
|
||||
public:
|
||||
GlyphFindAndPlaceSubpixel(LookupGlyph& glyphFinder)
|
||||
: fGlyphFinder(glyphFinder) { }
|
||||
: fGlyphFinder(glyphFinder) {
|
||||
FixGCC49Arm64Bug(1);
|
||||
}
|
||||
|
||||
SkPoint findAndPositionGlyph(
|
||||
const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override {
|
||||
@ -464,6 +483,7 @@ private:
|
||||
public:
|
||||
GlyphFindAndPlaceFullPixel(LookupGlyph& glyphFinder)
|
||||
: fGlyphFinder(glyphFinder) {
|
||||
FixGCC49Arm64Bug(2);
|
||||
// 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.");
|
||||
@ -601,6 +621,7 @@ inline void SkFindAndPlaceGlyph::ProcessPosText(
|
||||
} else {
|
||||
to_init->initialize<HorizontalPositions>(pos);
|
||||
}
|
||||
positionReader->forceUseForBug();
|
||||
}
|
||||
};
|
||||
|
||||
@ -617,7 +638,7 @@ inline void SkFindAndPlaceGlyph::ProcessPosText(
|
||||
}
|
||||
};
|
||||
|
||||
GlyphFindAndPlace<ProcessOneGlyph> findAndPosition{
|
||||
GlyphFindAndPlace<ProcessOneGlyph> findAndPosition {
|
||||
[&](typename GlyphFindAndPlace<ProcessOneGlyph>::Variants* to_init) {
|
||||
if (cache->isSubpixel()) {
|
||||
switch (textAlignment) {
|
||||
|
Loading…
Reference in New Issue
Block a user