| /* |
| * Copyright 2022 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "gm/gm.h" |
| #include "include/core/SkCanvas.h" |
| #include "include/core/SkImage.h" |
| #include "include/core/SkPaint.h" |
| #include "include/core/SkPixmap.h" |
| #include "include/core/SkShader.h" |
| #include "include/core/SkSurface.h" |
| |
| /** |
| * Tests drawing images are half pixel offsets in device space with nearest filtering to show how |
| * rasterization and image sample snapping at boundary points interact. Both drawImage and drawRect |
| * with an image shader are tested. Scale factors 1 and -1 are tested. The images are all two pixels |
| * wide or tall so we either get both values once each or one value repeated twice. |
| */ |
| DEF_SIMPLE_GM_CAN_FAIL(nearest_half_pixel_image, canvas, errorMsg, 264, 235) { |
| // We don't run this test on the GPU because we're at the driver/hw's mercy for how this |
| // is handled. |
| if (canvas->recordingContext() || (canvas->getSurface() && canvas->getSurface()->recorder())) { |
| *errorMsg = "Test is only relevant to CPU backend"; |
| return skiagm::DrawResult::kSkip; |
| } |
| |
| // We make 2x1 and 1x2 images for each color type. |
| struct Images { |
| sk_sp<SkImage> imageX; |
| sk_sp<SkImage> imageY; |
| }; |
| |
| Images images[2]; |
| uint32_t colors[] {0xFFFF0000, 0xFF0000FF}; |
| SkPixmap cpmx(SkImageInfo::Make({2, 1}, |
| kRGBA_8888_SkColorType, |
| kPremul_SkAlphaType), |
| colors, |
| sizeof(colors)); |
| SkPixmap cpmy(SkImageInfo::Make({1, 2}, |
| kRGBA_8888_SkColorType, |
| kPremul_SkAlphaType), |
| colors, |
| sizeof(colors[0])); |
| images[0] = {SkImage::MakeRasterCopy(cpmx), SkImage::MakeRasterCopy(cpmy)}; |
| |
| uint8_t alphas[] {0xFF, 0xAA}; |
| SkPixmap apmx(SkImageInfo::Make({2, 1}, |
| kAlpha_8_SkColorType, |
| kPremul_SkAlphaType), |
| alphas, |
| sizeof(alphas)); |
| SkPixmap apmy(SkImageInfo::Make({1, 2}, |
| kAlpha_8_SkColorType, |
| kPremul_SkAlphaType), |
| alphas, |
| sizeof(alphas[0])); |
| images[1] = {SkImage::MakeRasterCopy(apmx), SkImage::MakeRasterCopy(apmy)}; |
| |
| // We draw offscreen and then zoom that up to make the result clear. |
| auto surf = canvas->makeSurface(canvas->imageInfo().makeWH(80, 80)); |
| if (!surf) { |
| *errorMsg = "Test only works with SkSurface backed canvases"; |
| return skiagm::DrawResult::kSkip; |
| } |
| auto* c = surf->getCanvas(); |
| c->clear(SK_ColorWHITE); |
| |
| // We scale up in the direction not being tested, the one with image dimension of 1, to make the |
| // result more easily visible. |
| static const float kOffAxisScale = 4; |
| |
| auto draw = [&](sk_sp<SkImage> image, bool shader, bool doX, bool mirror, uint8_t alpha) { |
| c->save(); |
| SkPaint paint; |
| paint.setAlpha(alpha); |
| if (shader) { |
| paint.setShader(image->makeShader(SkSamplingOptions{})); |
| } |
| if (doX) { |
| c->scale(mirror ? -1 : 1, kOffAxisScale); |
| c->translate(mirror ? -2.5 : 0.5, 0); |
| } else { |
| c->scale(kOffAxisScale, mirror ? -1 : 1); |
| c->translate(0, mirror ? -2.5 : 0.5); |
| } |
| |
| if (shader) { |
| c->drawRect(SkRect::Make(image->dimensions()), paint); |
| } else { |
| c->drawImage(image, 0, 0, SkSamplingOptions{}, &paint); |
| } |
| c->restore(); |
| }; |
| |
| for (bool shader : {false, true}) |
| for (uint8_t alpha : {0xFF , 0x70}) { |
| c->save(); |
| for (const auto& i : images) |
| for (auto mirror : {false, true}) { |
| draw(i.imageX, shader, /*doX=*/true, mirror, alpha); |
| c->save(); |
| c->translate(4, 0); |
| draw(i.imageY, shader, /*doX=*/false, mirror, alpha); |
| c->restore(); |
| c->translate(0, kOffAxisScale*2); |
| } |
| c->restore(); |
| c->translate(kOffAxisScale*2, 0); |
| } |
| canvas->scale(8, 8); |
| canvas->drawImage(surf->makeImageSnapshot(), 0, 0); |
| |
| return skiagm::DrawResult::kOk; |
| } |