blob: ed179494fb050fd0c8983f6a148207f18a34e19f [file] [log] [blame]
Robert Phillipsd095b9f2020-02-03 16:12:51 -05001/*
2 * Copyright 2020 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"
Kevin Lubick321734e2023-03-14 10:46:56 -040011#include "include/core/SkTextureCompressionType.h"
Adlai Holler4caa9352020-07-16 10:58:58 -040012#include "include/gpu/GrDirectContext.h"
Robert Phillipsb7bfbc22020-07-01 12:55:01 -040013#include "include/gpu/GrRecordingContext.h"
Kevin Lubickeb981252023-06-13 07:51:26 -040014#include "include/gpu/ganesh/SkImageGanesh.h"
Robert Phillipsd095b9f2020-02-03 16:12:51 -050015#include "src/core/SkCompressedDataUtils.h"
Greg Daniel719239c2022-04-07 11:20:24 -040016#include "src/gpu/ganesh/GrCaps.h"
17#include "src/gpu/ganesh/GrImageContextPriv.h"
Kevin Lubickbf174bc2023-03-27 11:24:20 -040018#include "src/gpu/ganesh/image/SkImage_GaneshBase.h"
Robert Phillipsd095b9f2020-02-03 16:12:51 -050019#include "src/image/SkImage_Base.h"
Brian Salomone6662542021-02-23 10:45:39 -050020#include "tools/gpu/ProxyUtils.h"
Robert Phillipsd095b9f2020-02-03 16:12:51 -050021
22constexpr int kImgWidth = 16;
23constexpr int kImgHeight = 8;
24constexpr int kPad = 4;
25
26struct BC1Block {
27 uint16_t fColor0;
28 uint16_t fColor1;
29 uint32_t fIndices;
30};
31
32static int num_4x4_blocks(int size) {
33 return ((size + 3) & ~3) >> 2;
34}
35
36static uint16_t to565(SkColor col) {
37 int r5 = SkMulDiv255Round(31, SkColorGetR(col));
38 int g6 = SkMulDiv255Round(63, SkColorGetG(col));
39 int b5 = SkMulDiv255Round(31, SkColorGetB(col));
40
41 return (r5 << 11) | (g6 << 5) | b5;
42}
43
44// BC1 has per-block transparency. If, taken as ints,
45// fColor0 < fColor1 -> the block has transparency (& it is in color3)
46// fColor1 > fColor0 -> the block is opaque
47//
48// This method can create two blocks to test out BC1's behavior. If BC1
49// behaves as expected (i.e., w/ per-block transparency) then, for RGBA textures,
50// the transparent block(s) should appear as:
51// opaque black, medium grey, transparent black, white.
52// and the opaque block(s) should appear as:
53// opaque black, dark grey, light grey, white
54//
55// For RGB textures, however, the transparent block(s) should appear as:
56// opaque black, medium grey, _opaque_ black, white
57// and the opaque block(s) should appear as:
58// opaque black, dark grey, light grey, white.
59static void create_BC1_block(BC1Block* block, bool transparent) {
60 unsigned int byte;
61
62 if (transparent) {
63 block->fColor0 = to565(SK_ColorBLACK);
64 block->fColor1 = to565(SK_ColorWHITE);
65 SkASSERT(block->fColor0 <= block->fColor1); // this signals a transparent block
66 // opaque black (col0), medium grey (col2), transparent black (col3), white (col1).
67 byte = (0x0 << 0) | (0x2 << 2) | (0x3 << 4) | (0x1 << 6);
68 } else {
69 block->fColor0 = to565(SK_ColorWHITE);
70 block->fColor1 = to565(SK_ColorBLACK);
71 SkASSERT(block->fColor0 > block->fColor1); // this signals an opaque block
72 // opaque black (col1), dark grey (col3), light grey (col2), white (col0)
73 byte = (0x1 << 0) | (0x3 << 2) | (0x2 << 4) | (0x0 << 6);
74 }
75
76 block->fIndices = (byte << 24) | (byte << 16) | (byte << 8) | byte;
77}
78
79// This makes a 16x8 BC1 texture which has the top 4 rows be officially transparent
80// and the bottom 4 rows be officially opaque.
81static sk_sp<SkData> make_compressed_data() {
82 SkISize dim{ kImgWidth, kImgHeight };
83
Kevin Lubick321734e2023-03-14 10:46:56 -040084 size_t totalSize = SkCompressedDataSize(SkTextureCompressionType::kBC1_RGB8_UNORM, dim,
Robert Phillipsd095b9f2020-02-03 16:12:51 -050085 nullptr, false);
86
87 sk_sp<SkData> tmp = SkData::MakeUninitialized(totalSize);
88 BC1Block* dstBlocks = reinterpret_cast<BC1Block*>(tmp->writable_data());
89
90 BC1Block transBlock, opaqueBlock;
91 create_BC1_block(&transBlock, true);
92 create_BC1_block(&opaqueBlock, false);
93
94 int numXBlocks = num_4x4_blocks(kImgWidth);
95 int numYBlocks = num_4x4_blocks(kImgHeight);
96
97 for (int y = 0; y < numYBlocks; ++y) {
98 for (int x = 0; x < numXBlocks; ++x) {
99 dstBlocks[y*numXBlocks + x] = (y < numYBlocks/2) ? transBlock : opaqueBlock;
100 }
101 }
102
103 return tmp;
104}
105
Adlai Holler4caa9352020-07-16 10:58:58 -0400106static sk_sp<SkImage> data_to_img(GrDirectContext *direct, sk_sp<SkData> data,
Kevin Lubick321734e2023-03-14 10:46:56 -0400107 SkTextureCompressionType compression) {
Adlai Holler4caa9352020-07-16 10:58:58 -0400108 if (direct) {
Kevin Lubick77472bf2023-03-24 07:11:17 -0400109 return SkImages::TextureFromCompressedTextureData(
Kevin Lubickdf73d162023-09-11 11:56:53 -0400110 direct, std::move(data), kImgWidth, kImgHeight, compression, skgpu::Mipmapped::kNo);
Robert Phillipsd095b9f2020-02-03 16:12:51 -0500111 } else {
Kevin Lubick77472bf2023-03-24 07:11:17 -0400112 return SkImages::RasterFromCompressedTextureData(
113 std::move(data), kImgWidth, kImgHeight, compression);
Robert Phillipsd095b9f2020-02-03 16:12:51 -0500114 }
115}
116
Robert Phillipsa84caa32020-07-28 09:57:26 -0400117static void draw_image(SkCanvas* canvas, sk_sp<SkImage> image, int x, int y) {
Robert Phillipsd095b9f2020-02-03 16:12:51 -0500118
119 bool isCompressed = false;
120 if (image && image->isTextureBacked()) {
Adlai Holler302e8fb2020-09-14 11:58:06 -0400121 const GrCaps* caps = as_IB(image)->context()->priv().caps();
Brian Salomone6662542021-02-23 10:45:39 -0500122 GrTextureProxy* proxy = sk_gpu_test::GetTextureImageProxy(image.get(),
123 canvas->recordingContext());
Robert Phillipsd095b9f2020-02-03 16:12:51 -0500124 isCompressed = caps->isFormatCompressed(proxy->backendFormat());
125 }
126
127 canvas->drawImage(image, x, y);
128
129 if (!isCompressed) {
130 SkRect r = SkRect::MakeXYWH(x, y, kImgWidth, kImgHeight);
131 r.outset(1.0f, 1.0f);
132
133 SkPaint redStroke;
134 redStroke.setColor(SK_ColorRED);
135 redStroke.setStyle(SkPaint::kStroke_Style);
136 redStroke.setStrokeWidth(2.0f);
137
138 canvas->drawRect(r, redStroke);
139 }
140}
141
142namespace skiagm {
143
144// This GM draws the BC1 compressed texture filled with "make_compressed_data"s data twice.
145//
146// It is drawn once (on the top) as a kBC1_RGB8_UNORM texture and then again (on the bottom)
147// as a kBC1_RGBA8_UNORM texture.
148//
149// If BC1 behaves as expected we should see:
150//
151// RGB8 Black MidGrey Black* White ...
152// Black DrkGrey LtGrey White ...
153//
154// RGBA8 Black MidGrey Green+ White ...
155// Black DrkGrey LtGrey White ...
156//
157// * We expect this to be black bc the transparent black will be forced to opaque. If BC1 were
158// treating it as an opaque block then it would be LtGrey - not black.
159// + This is just the background showing through the transparent black
160class BC1TransparencyGM : public GM {
161public:
162 BC1TransparencyGM() {
163 this->setBGColor(SK_ColorGREEN);
164 }
165
166protected:
Leandro Lovisolo24fa2112023-08-15 19:05:17 +0000167 SkString getName() const override { return SkString("bc1_transparency"); }
Robert Phillipsd095b9f2020-02-03 16:12:51 -0500168
Leandro Lovisolo8f023882023-08-15 21:13:52 +0000169 SkISize getISize() override {
Robert Phillipsd095b9f2020-02-03 16:12:51 -0500170 return SkISize::Make(kImgWidth + 2 * kPad, 2 * kImgHeight + 3 * kPad);
171 }
172
Brian Salomonc759bbf2023-12-05 11:11:27 -0500173 DrawResult onGpuSetup(SkCanvas* canvas, SkString* errorMsg, GraphiteTestContext*) override {
Jim Van Vertha8624432023-02-13 16:48:09 -0500174 auto dContext = GrAsDirectContext(canvas->recordingContext());
Robert Phillipsa84caa32020-07-28 09:57:26 -0400175 if (dContext && dContext->abandoned()) {
176 // This isn't a GpuGM so a null 'context' is okay but an abandoned context
177 // if forbidden.
178 return DrawResult::kSkip;
179 }
180
181 sk_sp<SkData> bc1Data = make_compressed_data();
182
Kevin Lubick321734e2023-03-14 10:46:56 -0400183 fRGBImage = data_to_img(dContext, bc1Data, SkTextureCompressionType::kBC1_RGB8_UNORM);
Robert Phillipsa84caa32020-07-28 09:57:26 -0400184 fRGBAImage = data_to_img(dContext, std::move(bc1Data),
Kevin Lubick321734e2023-03-14 10:46:56 -0400185 SkTextureCompressionType::kBC1_RGBA8_UNORM);
Robert Phillipsa84caa32020-07-28 09:57:26 -0400186 if (!fRGBImage || !fRGBAImage) {
187 *errorMsg = "Failed to create BC1 images.";
188 return DrawResult::kFail;
189 }
190
191 return DrawResult::kOk;
192 }
193
194 void onGpuTeardown() override {
195 fRGBImage = nullptr;
196 fRGBAImage = nullptr;
Robert Phillipsd095b9f2020-02-03 16:12:51 -0500197 }
198
199 void onDraw(SkCanvas* canvas) override {
Robert Phillipsa84caa32020-07-28 09:57:26 -0400200 draw_image(canvas, fRGBImage, kPad, kPad);
201 draw_image(canvas, fRGBAImage, kPad, 2 * kPad + kImgHeight);
Robert Phillipsd095b9f2020-02-03 16:12:51 -0500202 }
203
204private:
Robert Phillipsa84caa32020-07-28 09:57:26 -0400205 sk_sp<SkImage> fRGBImage;
206 sk_sp<SkImage> fRGBAImage;
Robert Phillipsd095b9f2020-02-03 16:12:51 -0500207
John Stiles7571f9e2020-09-02 22:42:33 -0400208 using INHERITED = GM;
Robert Phillipsd095b9f2020-02-03 16:12:51 -0500209};
210
211//////////////////////////////////////////////////////////////////////////////
212
213DEF_GM(return new BC1TransparencyGM;)
John Stilesa6841be2020-08-06 14:11:56 -0400214} // namespace skiagm