|  | /* | 
|  | * 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] = {SkImages::RasterFromPixmapCopy(cpmx), SkImages::RasterFromPixmapCopy(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] = {SkImages::RasterFromPixmapCopy(apmx), SkImages::RasterFromPixmapCopy(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(SkFilterMode::kNearest)); | 
|  | } | 
|  | 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, SkFilterMode::kNearest, &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; | 
|  | } |