Brian Salomon | cf3fa75 | 2022-11-02 17:19:44 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2022 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #include "gm/gm.h" |
| 9 | #include "include/core/SkCanvas.h" |
| 10 | #include "include/core/SkImage.h" |
| 11 | #include "include/core/SkPaint.h" |
| 12 | #include "include/core/SkPixmap.h" |
Kevin Lubick | 08fc988 | 2023-01-30 16:05:54 -0500 | [diff] [blame] | 13 | #include "include/core/SkShader.h" |
Brian Salomon | cf3fa75 | 2022-11-02 17:19:44 -0400 | [diff] [blame] | 14 | #include "include/core/SkSurface.h" |
| 15 | |
| 16 | /** |
| 17 | * Tests drawing images are half pixel offsets in device space with nearest filtering to show how |
| 18 | * rasterization and image sample snapping at boundary points interact. Both drawImage and drawRect |
| 19 | * with an image shader are tested. Scale factors 1 and -1 are tested. The images are all two pixels |
| 20 | * wide or tall so we either get both values once each or one value repeated twice. |
| 21 | */ |
| 22 | DEF_SIMPLE_GM_CAN_FAIL(nearest_half_pixel_image, canvas, errorMsg, 264, 235) { |
| 23 | // We don't run this test on the GPU because we're at the driver/hw's mercy for how this |
| 24 | // is handled. |
| 25 | if (canvas->recordingContext() || (canvas->getSurface() && canvas->getSurface()->recorder())) { |
| 26 | *errorMsg = "Test is only relevant to CPU backend"; |
| 27 | return skiagm::DrawResult::kSkip; |
| 28 | } |
| 29 | |
| 30 | // We make 2x1 and 1x2 images for each color type. |
| 31 | struct Images { |
| 32 | sk_sp<SkImage> imageX; |
| 33 | sk_sp<SkImage> imageY; |
| 34 | }; |
| 35 | |
| 36 | Images images[2]; |
| 37 | uint32_t colors[] {0xFFFF0000, 0xFF0000FF}; |
| 38 | SkPixmap cpmx(SkImageInfo::Make({2, 1}, |
| 39 | kRGBA_8888_SkColorType, |
| 40 | kPremul_SkAlphaType), |
| 41 | colors, |
| 42 | sizeof(colors)); |
| 43 | SkPixmap cpmy(SkImageInfo::Make({1, 2}, |
| 44 | kRGBA_8888_SkColorType, |
| 45 | kPremul_SkAlphaType), |
| 46 | colors, |
| 47 | sizeof(colors[0])); |
Kevin Lubick | 77472bf | 2023-03-24 07:11:17 -0400 | [diff] [blame] | 48 | images[0] = {SkImages::RasterFromPixmapCopy(cpmx), SkImages::RasterFromPixmapCopy(cpmy)}; |
Brian Salomon | cf3fa75 | 2022-11-02 17:19:44 -0400 | [diff] [blame] | 49 | |
| 50 | uint8_t alphas[] {0xFF, 0xAA}; |
| 51 | SkPixmap apmx(SkImageInfo::Make({2, 1}, |
| 52 | kAlpha_8_SkColorType, |
| 53 | kPremul_SkAlphaType), |
| 54 | alphas, |
| 55 | sizeof(alphas)); |
| 56 | SkPixmap apmy(SkImageInfo::Make({1, 2}, |
| 57 | kAlpha_8_SkColorType, |
| 58 | kPremul_SkAlphaType), |
| 59 | alphas, |
| 60 | sizeof(alphas[0])); |
Kevin Lubick | 77472bf | 2023-03-24 07:11:17 -0400 | [diff] [blame] | 61 | images[1] = {SkImages::RasterFromPixmapCopy(apmx), SkImages::RasterFromPixmapCopy(apmy)}; |
Brian Salomon | cf3fa75 | 2022-11-02 17:19:44 -0400 | [diff] [blame] | 62 | |
| 63 | // We draw offscreen and then zoom that up to make the result clear. |
| 64 | auto surf = canvas->makeSurface(canvas->imageInfo().makeWH(80, 80)); |
| 65 | if (!surf) { |
| 66 | *errorMsg = "Test only works with SkSurface backed canvases"; |
| 67 | return skiagm::DrawResult::kSkip; |
| 68 | } |
| 69 | auto* c = surf->getCanvas(); |
Brian Salomon | bc4d60c | 2022-11-03 11:34:14 -0400 | [diff] [blame] | 70 | c->clear(SK_ColorWHITE); |
Brian Salomon | cf3fa75 | 2022-11-02 17:19:44 -0400 | [diff] [blame] | 71 | |
| 72 | // We scale up in the direction not being tested, the one with image dimension of 1, to make the |
| 73 | // result more easily visible. |
| 74 | static const float kOffAxisScale = 4; |
| 75 | |
| 76 | auto draw = [&](sk_sp<SkImage> image, bool shader, bool doX, bool mirror, uint8_t alpha) { |
| 77 | c->save(); |
| 78 | SkPaint paint; |
| 79 | paint.setAlpha(alpha); |
| 80 | if (shader) { |
Michael Ludwig | 2c49e60 | 2023-05-30 13:12:19 -0400 | [diff] [blame] | 81 | paint.setShader(image->makeShader(SkFilterMode::kNearest)); |
Brian Salomon | cf3fa75 | 2022-11-02 17:19:44 -0400 | [diff] [blame] | 82 | } |
| 83 | if (doX) { |
| 84 | c->scale(mirror ? -1 : 1, kOffAxisScale); |
| 85 | c->translate(mirror ? -2.5 : 0.5, 0); |
| 86 | } else { |
| 87 | c->scale(kOffAxisScale, mirror ? -1 : 1); |
| 88 | c->translate(0, mirror ? -2.5 : 0.5); |
| 89 | } |
| 90 | |
| 91 | if (shader) { |
| 92 | c->drawRect(SkRect::Make(image->dimensions()), paint); |
| 93 | } else { |
Michael Ludwig | 2c49e60 | 2023-05-30 13:12:19 -0400 | [diff] [blame] | 94 | c->drawImage(image, 0, 0, SkFilterMode::kNearest, &paint); |
Brian Salomon | cf3fa75 | 2022-11-02 17:19:44 -0400 | [diff] [blame] | 95 | } |
| 96 | c->restore(); |
| 97 | }; |
| 98 | |
| 99 | for (bool shader : {false, true}) |
| 100 | for (uint8_t alpha : {0xFF , 0x70}) { |
| 101 | c->save(); |
| 102 | for (const auto& i : images) |
| 103 | for (auto mirror : {false, true}) { |
| 104 | draw(i.imageX, shader, /*doX=*/true, mirror, alpha); |
| 105 | c->save(); |
| 106 | c->translate(4, 0); |
| 107 | draw(i.imageY, shader, /*doX=*/false, mirror, alpha); |
| 108 | c->restore(); |
| 109 | c->translate(0, kOffAxisScale*2); |
| 110 | } |
| 111 | c->restore(); |
| 112 | c->translate(kOffAxisScale*2, 0); |
| 113 | } |
| 114 | canvas->scale(8, 8); |
| 115 | canvas->drawImage(surf->makeImageSnapshot(), 0, 0); |
| 116 | |
| 117 | return skiagm::DrawResult::kOk; |
| 118 | } |