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:
parent
8ffe4bc8cb
commit
5589c881c8
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
///@}
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user