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:
herb 2015-12-07 12:12:29 -08:00 committed by Commit bot
parent ba923d38a5
commit d4c24f6749
2 changed files with 138 additions and 178 deletions

View File

@ -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

View File

@ -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) {