/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "gm.h" #include "SkBlitter.h" #include "SkCanvas.h" #include "SkColor.h" #include "SkImage.h" #include "SkImageInfo.h" #include "SkLinearBitmapPipeline.h" #include "SkXfermodePriv.h" #include "SkPM4fPriv.h" #include "SkShader.h" static void fill_in_bits(SkBitmap& bm, SkIRect ir, SkColor c, bool premul) { bm.allocN32Pixels(ir.width(), ir.height()); SkPixmap pm; bm.peekPixels(&pm); SkPMColor b = SkColorSetARGBMacro(255, 0, 0, 0); SkPMColor w; if (premul) { w = SkPreMultiplyColor(c); } else { w = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), SkColorGetB(c)); } for (int y = 0; y < ir.height(); y++) { for (int x = 0; x < ir.width(); x++) { if ((x ^ y) & 16) { *pm.writable_addr32(x, y) = b; } else { *pm.writable_addr32(x, y) = w; } } } } static void draw_rect_orig(SkCanvas* canvas, const SkRect& r, SkColor c, const SkMatrix* mat, bool useBilerp) { const SkIRect ir = r.round(); SkBitmap bmsrc; fill_in_bits(bmsrc, ir, c, true); SkPixmap pmsrc; bmsrc.peekPixels(&pmsrc); SkBitmap bmdst; bmdst.allocN32Pixels(ir.width(), ir.height()); bmdst.eraseColor(0xFFFFFFFF); SkPixmap pmdst; bmdst.peekPixels(&pmdst); SkImageInfo info = SkImageInfo::MakeN32Premul(ir.width(), ir.height()); sk_sp image(SkImage::MakeRasterCopy(SkPixmap(info, pmsrc.addr32(), pmsrc.rowBytes()))); SkPaint paint; int32_t storage[kSkBlitterContextSize]; sk_sp shader = image->makeShader(SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); if (useBilerp) { paint.setFilterQuality(SkFilterQuality::kLow_SkFilterQuality); } else { paint.setFilterQuality(SkFilterQuality::kNone_SkFilterQuality); } paint.setShader(std::move(shader)); const SkShader::ContextRec rec(paint, *mat, nullptr, SkBlitter::PreferredShaderDest(pmsrc.info()), canvas->imageInfo().colorSpace()); SkASSERT(paint.getShader()->contextSize(rec) <= sizeof(storage)); SkShader::Context* ctx = paint.getShader()->createContext(rec, storage); for (int y = 0; y < ir.height(); y++) { ctx->shadeSpan(0, y, pmdst.writable_addr32(0, y), ir.width()); } canvas->drawBitmap(bmdst, r.left(), r.top(), nullptr); ctx->~Context(); } static void draw_rect_fp(SkCanvas* canvas, const SkRect& r, SkColor c, const SkMatrix* mat, bool useBilerp) { const SkIRect ir = r.round(); SkBitmap bmsrc; fill_in_bits(bmsrc, ir, c, true); SkPixmap pmsrc; bmsrc.peekPixels(&pmsrc); SkBitmap bmdst; bmdst.allocN32Pixels(ir.width(), ir.height()); bmdst.eraseColor(0xFFFFFFFF); SkPixmap pmdst; bmdst.peekPixels(&pmdst); SkPM4f* dstBits = new SkPM4f[ir.width()]; SkMatrix inv; bool trash = mat->invert(&inv); sk_ignore_unused_variable(trash); SkFilterQuality filterQuality; if (useBilerp) { filterQuality = SkFilterQuality::kLow_SkFilterQuality; } else { filterQuality = SkFilterQuality::kNone_SkFilterQuality; } uint32_t flags = 0; auto procN = SkXfermode::GetD32Proc(SkBlendMode::kSrcOver, flags); char storage[512]; SkArenaAlloc allocator{storage, sizeof(storage)}; SkLinearBitmapPipeline pipeline{ inv, filterQuality, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, SK_ColorBLACK, pmsrc, &allocator}; for (int y = 0; y < ir.height(); y++) { pipeline.shadeSpan4f(0, y, dstBits, ir.width()); procN(SkBlendMode::kSrcOver, pmdst.writable_addr32(0, y), dstBits, ir.width(), nullptr); } delete [] dstBits; canvas->drawBitmap(bmdst, r.left(), r.top(), nullptr); } static void draw_rect_none(SkCanvas* canvas, const SkRect& r, SkColor c) { const SkIRect ir = r.round(); SkBitmap bm; fill_in_bits(bm, ir, c, true); canvas->drawBitmap(bm, r.left(), r.top(), nullptr); } /* * Test SkXfer4fProcs directly for src-over, comparing them to current SkColor blits. */ DEF_SIMPLE_GM(linear_pipeline, canvas, 580, 2200) { const int IW = 50; const SkScalar W = IW; const SkScalar H = 100; const SkColor colors[] = { 0x880000FF, 0x8800FF00, 0x88FF0000, 0x88000000, SK_ColorBLUE, SK_ColorGREEN, SK_ColorRED, SK_ColorBLACK, }; canvas->translate(20, 20); SkMatrix mi = SkMatrix::I(); SkMatrix mlr; mlr.setScale(-1.0f, 1.0f, 20, 0.0f); SkMatrix mt; mt.setTranslate(8, 8); SkMatrix mt2; mt2.setTranslate(-18, -18); SkMatrix ms; ms.setScale(2.7f, 2.7f, -1.5f, 0); SkMatrix ms2; ms2.setScale(-0.4f, 0.4f); SkMatrix mr; mr.setRotate(10); const SkMatrix* mats[] = {nullptr, &mi, &mlr, &mt, &mt2, &ms, &ms2, &mr}; const SkRect r = SkRect::MakeWH(W, H); bool useBilerp = false; while (true) { canvas->save(); for (auto mat : mats) { canvas->save(); for (SkColor c : colors) { if (mat == nullptr) { SkPaint p; p.setColor(c); draw_rect_none(canvas, r, c); canvas->translate(W + 20, 0); draw_rect_none(canvas, r, c); } else { draw_rect_orig(canvas, r, c, mat, useBilerp); canvas->translate(W + 20, 0); draw_rect_fp(canvas, r, c, mat, useBilerp); } canvas->translate(W + 20, 0); } canvas->restore(); canvas->translate(0, H + 20); } canvas->restore(); canvas->translate(0, (H + 20) * SK_ARRAY_COUNT(mats)); if (useBilerp) break; useBilerp = true; } }