Revert "In the CPU backend, stop treating alpha images as coverage"

Restore the old behavior behind a flag, so it can be maintained in the
Android framework.

This reverts commit f1660bf1ba.

Bug: b/231400686
Change-Id: I01fadaed52eecc42416deb2307d03499bca85a4c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/557391
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Leon Scroggins <scroggo@google.com>
This commit is contained in:
Brian Osman 2022-07-11 11:08:11 -04:00 committed by SkCQ
parent 8ffe4bc8cb
commit 5589c881c8
5 changed files with 196 additions and 0 deletions

View File

@ -17,6 +17,7 @@
#include "include/core/SkPaint.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkShader.h"
#include "tools/Resources.h"
static SkBitmap make_alpha_image(int w, int h) {
SkBitmap bm;
@ -84,3 +85,41 @@ DEF_SIMPLE_GM(alpha_image_alpha_tint, canvas, 152, 80) {
paint.setShader(image->makeShader(SkSamplingOptions()));
canvas->drawRect({ 0, 0, 64, 64 }, paint);
}
#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
// For a long time, the CPU backend treated A8 bitmaps as coverage, rather than alpha. This was
// inconsistent with the GPU backend (skbug.com/9692). When this was fixed, it altered behavior
// for some Android apps (b/231400686). This GM verifies that our Android framework workaround
// produces the old result (mandrill with a round-rect border).
DEF_SIMPLE_GM(alpha_bitmap_is_coverage_ANDROID, canvas, 128, 128) {
SkBitmap maskBitmap;
maskBitmap.allocPixels(SkImageInfo::MakeA8(128, 128));
{
SkCanvas maskCanvas(maskBitmap);
maskCanvas.clear(SK_ColorWHITE);
SkPaint maskPaint;
maskPaint.setAntiAlias(true);
maskPaint.setColor(SK_ColorWHITE);
maskPaint.setBlendMode(SkBlendMode::kClear);
maskCanvas.drawRoundRect({0, 0, 128, 128}, 16, 16, maskPaint);
}
SkBitmap offscreenBitmap;
offscreenBitmap.allocN32Pixels(128, 128);
{
SkCanvas offscreenCanvas(offscreenBitmap);
offscreenCanvas.drawImage(GetResourceAsImage("images/mandrill_128.png"), 0, 0);
SkPaint clearPaint;
clearPaint.setAntiAlias(true);
clearPaint.setBlendMode(SkBlendMode::kClear);
// At tip-of-tree (or at any time on the GPU backend), this draw produces full coverage,
// completely erasing the mandrill. With the workaround enabled, the alpha border is treated
// as coverage, so we only apply kClear to those pixels, just erasing the outer border.
offscreenCanvas.drawImage(maskBitmap.asImage(), 0, 0, SkSamplingOptions{}, &clearPaint);
}
canvas->drawImage(offscreenBitmap.asImage(), 0, 0);
}
#endif

View File

@ -273,6 +273,22 @@ void SkBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {
/////////////////////// these are not virtual, just helpers
#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
void SkBlitter::blitMaskRegion(const SkMask& mask, const SkRegion& clip) {
if (clip.quickReject(mask.fBounds)) {
return;
}
SkRegion::Cliperator clipper(clip, mask.fBounds);
while (!clipper.done()) {
const SkIRect& cr = clipper.rect();
this->blitMask(mask, cr);
clipper.next();
}
}
#endif
void SkBlitter::blitRectRegion(const SkIRect& rect, const SkRegion& clip) {
SkRegion::Cliperator clipper(clip, rect);

View File

@ -132,6 +132,9 @@ public:
}
///@name non-virtual helpers
#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
void blitMaskRegion(const SkMask& mask, const SkRegion& clip);
#endif
void blitRectRegion(const SkIRect& rect, const SkRegion& clip);
void blitRegion(const SkRegion& clip);
///@}

View File

@ -705,6 +705,39 @@ void SkDraw::drawRect(const SkRect& prePaintRect, const SkPaint& paint,
}
}
#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
if (srcM.fBounds.isEmpty()) {
return;
}
const SkMask* mask = &srcM;
SkMask dstM;
if (paint.getMaskFilter() &&
as_MFB(paint.getMaskFilter())
->filterMask(&dstM, srcM, fMatrixProvider->localToDevice(), nullptr)) {
mask = &dstM;
}
SkAutoMaskFreeImage ami(dstM.fImage);
SkAutoBlitterChoose blitterChooser(*this, nullptr, paint);
SkBlitter* blitter = blitterChooser.get();
SkAAClipBlitterWrapper wrapper;
const SkRegion* clipRgn;
if (fRC->isBW()) {
clipRgn = &fRC->bwRgn();
} else {
wrapper.init(*fRC, blitter);
clipRgn = &wrapper.getRgn();
blitter = wrapper.getBlitter();
}
blitter->blitMaskRegion(*mask, *clipRgn);
}
#endif
static SkScalar fast_len(const SkVector& vec) {
SkScalar x = SkScalarAbs(vec.fX);
SkScalar y = SkScalarAbs(vec.fY);
@ -925,6 +958,94 @@ void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill);
}
#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkSamplingOptions& sampling,
const SkPaint& paint) const {
SkASSERT(bitmap.colorType() == kAlpha_8_SkColorType);
// nothing to draw
if (fRC->isEmpty()) {
return;
}
SkMatrix ctm = fMatrixProvider->localToDevice();
if (SkTreatAsSprite(ctm, bitmap.dimensions(), sampling, paint))
{
int ix = SkScalarRoundToInt(ctm.getTranslateX());
int iy = SkScalarRoundToInt(ctm.getTranslateY());
SkPixmap pmap;
if (!bitmap.peekPixels(&pmap)) {
return;
}
SkMask mask;
mask.fBounds.setXYWH(ix, iy, pmap.width(), pmap.height());
mask.fFormat = SkMask::kA8_Format;
mask.fRowBytes = SkToU32(pmap.rowBytes());
// fImage is typed as writable, but in this case it is used read-only
mask.fImage = (uint8_t*)pmap.addr8(0, 0);
this->drawDevMask(mask, paint);
} else { // need to xform the bitmap first
SkRect r;
SkMask mask;
r.setIWH(bitmap.width(), bitmap.height());
ctm.mapRect(&r);
r.round(&mask.fBounds);
// set the mask's bounds to the transformed bitmap-bounds,
// clipped to the actual device and further limited by the clip bounds
{
SkASSERT(fDst.bounds().contains(fRC->getBounds()));
SkIRect devBounds = fDst.bounds();
devBounds.intersect(fRC->getBounds().makeOutset(1, 1));
// need intersect(l, t, r, b) on irect
if (!mask.fBounds.intersect(devBounds)) {
return;
}
}
mask.fFormat = SkMask::kA8_Format;
mask.fRowBytes = SkAlign4(mask.fBounds.width());
size_t size = mask.computeImageSize();
if (0 == size) {
// the mask is too big to allocated, draw nothing
return;
}
// allocate (and clear) our temp buffer to hold the transformed bitmap
SkAutoTMalloc<uint8_t> storage(size);
mask.fImage = storage.get();
memset(mask.fImage, 0, size);
// now draw our bitmap(src) into mask(dst), transformed by the matrix
{
SkBitmap device;
device.installPixels(SkImageInfo::MakeA8(mask.fBounds.width(), mask.fBounds.height()),
mask.fImage, mask.fRowBytes);
SkCanvas c(device);
// need the unclipped top/left for the translate
c.translate(-SkIntToScalar(mask.fBounds.fLeft),
-SkIntToScalar(mask.fBounds.fTop));
c.concat(ctm);
// We can't call drawBitmap, or we'll infinitely recurse. Instead
// we manually build a shader and draw that into our new mask
SkPaint tmpPaint;
tmpPaint.setAntiAlias(paint.isAntiAlias());
tmpPaint.setDither(paint.isDither());
SkPaint paintWithShader = make_paint_with_image(tmpPaint, bitmap, sampling);
SkRect rr;
rr.setIWH(bitmap.width(), bitmap.height());
c.drawRect(rr, paintWithShader);
}
this->drawDevMask(mask, paint);
}
}
#endif
static bool clipped_out(const SkMatrix& m, const SkRasterClip& c,
const SkRect& srcR) {
SkRect dstR;
@ -998,6 +1119,16 @@ void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
SkDraw draw(*this);
draw.fMatrixProvider = &matrixProvider;
// For a long time, the CPU backend treated A8 bitmaps as coverage, rather than alpha. This was
// inconsistent with the GPU backend (skbug.com/9692). When this was fixed, it altered behavior
// for some Android apps (b/231400686). Thus: keep the old behavior in the framework.
#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
if (bitmap.colorType() == kAlpha_8_SkColorType && !paint->getColorFilter()) {
draw.drawBitmapAsMask(bitmap, sampling, *paint);
return;
}
#endif
SkPaint paintWithShader = make_paint_with_image(*paint, bitmap, sampling);
const SkRect srcBounds = SkRect::MakeIWH(bitmap.width(), bitmap.height());
if (dstBounds) {

View File

@ -102,6 +102,10 @@ public:
SkMask* mask, SkMask::CreateMode mode,
SkStrokeRec::InitStyle style);
#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
void drawDevMask(const SkMask& mask, const SkPaint&) const;
#endif
enum RectType {
kHair_RectType,
kFill_RectType,
@ -121,6 +125,9 @@ public:
SkPoint* strokeSize);
private:
#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
void drawBitmapAsMask(const SkBitmap&, const SkSamplingOptions&, const SkPaint&) const;
#endif
void drawFixedVertices(const SkVertices* vertices,
sk_sp<SkBlender> blender,
const SkPaint& paint,