1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2020 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "gm/gm.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTextureCompressionType.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrDirectContext.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrRecordingContext.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/SkImageGanesh.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkCompressedDataUtils.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrCaps.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrImageContextPriv.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/image/SkImage_GaneshBase.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/image/SkImage_Base.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "tools/gpu/ProxyUtils.h"
21*c8dee2aaSAndroid Build Coastguard Worker
22*c8dee2aaSAndroid Build Coastguard Worker constexpr int kImgWidth = 16;
23*c8dee2aaSAndroid Build Coastguard Worker constexpr int kImgHeight = 8;
24*c8dee2aaSAndroid Build Coastguard Worker constexpr int kPad = 4;
25*c8dee2aaSAndroid Build Coastguard Worker
26*c8dee2aaSAndroid Build Coastguard Worker struct BC1Block {
27*c8dee2aaSAndroid Build Coastguard Worker uint16_t fColor0;
28*c8dee2aaSAndroid Build Coastguard Worker uint16_t fColor1;
29*c8dee2aaSAndroid Build Coastguard Worker uint32_t fIndices;
30*c8dee2aaSAndroid Build Coastguard Worker };
31*c8dee2aaSAndroid Build Coastguard Worker
num_4x4_blocks(int size)32*c8dee2aaSAndroid Build Coastguard Worker static int num_4x4_blocks(int size) {
33*c8dee2aaSAndroid Build Coastguard Worker return ((size + 3) & ~3) >> 2;
34*c8dee2aaSAndroid Build Coastguard Worker }
35*c8dee2aaSAndroid Build Coastguard Worker
to565(SkColor col)36*c8dee2aaSAndroid Build Coastguard Worker static uint16_t to565(SkColor col) {
37*c8dee2aaSAndroid Build Coastguard Worker int r5 = SkMulDiv255Round(31, SkColorGetR(col));
38*c8dee2aaSAndroid Build Coastguard Worker int g6 = SkMulDiv255Round(63, SkColorGetG(col));
39*c8dee2aaSAndroid Build Coastguard Worker int b5 = SkMulDiv255Round(31, SkColorGetB(col));
40*c8dee2aaSAndroid Build Coastguard Worker
41*c8dee2aaSAndroid Build Coastguard Worker return (r5 << 11) | (g6 << 5) | b5;
42*c8dee2aaSAndroid Build Coastguard Worker }
43*c8dee2aaSAndroid Build Coastguard Worker
44*c8dee2aaSAndroid Build Coastguard Worker // BC1 has per-block transparency. If, taken as ints,
45*c8dee2aaSAndroid Build Coastguard Worker // fColor0 < fColor1 -> the block has transparency (& it is in color3)
46*c8dee2aaSAndroid Build Coastguard Worker // fColor1 > fColor0 -> the block is opaque
47*c8dee2aaSAndroid Build Coastguard Worker //
48*c8dee2aaSAndroid Build Coastguard Worker // This method can create two blocks to test out BC1's behavior. If BC1
49*c8dee2aaSAndroid Build Coastguard Worker // behaves as expected (i.e., w/ per-block transparency) then, for RGBA textures,
50*c8dee2aaSAndroid Build Coastguard Worker // the transparent block(s) should appear as:
51*c8dee2aaSAndroid Build Coastguard Worker // opaque black, medium grey, transparent black, white.
52*c8dee2aaSAndroid Build Coastguard Worker // and the opaque block(s) should appear as:
53*c8dee2aaSAndroid Build Coastguard Worker // opaque black, dark grey, light grey, white
54*c8dee2aaSAndroid Build Coastguard Worker //
55*c8dee2aaSAndroid Build Coastguard Worker // For RGB textures, however, the transparent block(s) should appear as:
56*c8dee2aaSAndroid Build Coastguard Worker // opaque black, medium grey, _opaque_ black, white
57*c8dee2aaSAndroid Build Coastguard Worker // and the opaque block(s) should appear as:
58*c8dee2aaSAndroid Build Coastguard Worker // opaque black, dark grey, light grey, white.
create_BC1_block(BC1Block * block,bool transparent)59*c8dee2aaSAndroid Build Coastguard Worker static void create_BC1_block(BC1Block* block, bool transparent) {
60*c8dee2aaSAndroid Build Coastguard Worker unsigned int byte;
61*c8dee2aaSAndroid Build Coastguard Worker
62*c8dee2aaSAndroid Build Coastguard Worker if (transparent) {
63*c8dee2aaSAndroid Build Coastguard Worker block->fColor0 = to565(SK_ColorBLACK);
64*c8dee2aaSAndroid Build Coastguard Worker block->fColor1 = to565(SK_ColorWHITE);
65*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(block->fColor0 <= block->fColor1); // this signals a transparent block
66*c8dee2aaSAndroid Build Coastguard Worker // opaque black (col0), medium grey (col2), transparent black (col3), white (col1).
67*c8dee2aaSAndroid Build Coastguard Worker byte = (0x0 << 0) | (0x2 << 2) | (0x3 << 4) | (0x1 << 6);
68*c8dee2aaSAndroid Build Coastguard Worker } else {
69*c8dee2aaSAndroid Build Coastguard Worker block->fColor0 = to565(SK_ColorWHITE);
70*c8dee2aaSAndroid Build Coastguard Worker block->fColor1 = to565(SK_ColorBLACK);
71*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(block->fColor0 > block->fColor1); // this signals an opaque block
72*c8dee2aaSAndroid Build Coastguard Worker // opaque black (col1), dark grey (col3), light grey (col2), white (col0)
73*c8dee2aaSAndroid Build Coastguard Worker byte = (0x1 << 0) | (0x3 << 2) | (0x2 << 4) | (0x0 << 6);
74*c8dee2aaSAndroid Build Coastguard Worker }
75*c8dee2aaSAndroid Build Coastguard Worker
76*c8dee2aaSAndroid Build Coastguard Worker block->fIndices = (byte << 24) | (byte << 16) | (byte << 8) | byte;
77*c8dee2aaSAndroid Build Coastguard Worker }
78*c8dee2aaSAndroid Build Coastguard Worker
79*c8dee2aaSAndroid Build Coastguard Worker // This makes a 16x8 BC1 texture which has the top 4 rows be officially transparent
80*c8dee2aaSAndroid Build Coastguard Worker // and the bottom 4 rows be officially opaque.
make_compressed_data()81*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkData> make_compressed_data() {
82*c8dee2aaSAndroid Build Coastguard Worker SkISize dim{ kImgWidth, kImgHeight };
83*c8dee2aaSAndroid Build Coastguard Worker
84*c8dee2aaSAndroid Build Coastguard Worker size_t totalSize = SkCompressedDataSize(SkTextureCompressionType::kBC1_RGB8_UNORM, dim,
85*c8dee2aaSAndroid Build Coastguard Worker nullptr, false);
86*c8dee2aaSAndroid Build Coastguard Worker
87*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> tmp = SkData::MakeUninitialized(totalSize);
88*c8dee2aaSAndroid Build Coastguard Worker BC1Block* dstBlocks = reinterpret_cast<BC1Block*>(tmp->writable_data());
89*c8dee2aaSAndroid Build Coastguard Worker
90*c8dee2aaSAndroid Build Coastguard Worker BC1Block transBlock, opaqueBlock;
91*c8dee2aaSAndroid Build Coastguard Worker create_BC1_block(&transBlock, true);
92*c8dee2aaSAndroid Build Coastguard Worker create_BC1_block(&opaqueBlock, false);
93*c8dee2aaSAndroid Build Coastguard Worker
94*c8dee2aaSAndroid Build Coastguard Worker int numXBlocks = num_4x4_blocks(kImgWidth);
95*c8dee2aaSAndroid Build Coastguard Worker int numYBlocks = num_4x4_blocks(kImgHeight);
96*c8dee2aaSAndroid Build Coastguard Worker
97*c8dee2aaSAndroid Build Coastguard Worker for (int y = 0; y < numYBlocks; ++y) {
98*c8dee2aaSAndroid Build Coastguard Worker for (int x = 0; x < numXBlocks; ++x) {
99*c8dee2aaSAndroid Build Coastguard Worker dstBlocks[y*numXBlocks + x] = (y < numYBlocks/2) ? transBlock : opaqueBlock;
100*c8dee2aaSAndroid Build Coastguard Worker }
101*c8dee2aaSAndroid Build Coastguard Worker }
102*c8dee2aaSAndroid Build Coastguard Worker
103*c8dee2aaSAndroid Build Coastguard Worker return tmp;
104*c8dee2aaSAndroid Build Coastguard Worker }
105*c8dee2aaSAndroid Build Coastguard Worker
data_to_img(GrDirectContext * direct,sk_sp<SkData> data,SkTextureCompressionType compression)106*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkImage> data_to_img(GrDirectContext *direct, sk_sp<SkData> data,
107*c8dee2aaSAndroid Build Coastguard Worker SkTextureCompressionType compression) {
108*c8dee2aaSAndroid Build Coastguard Worker if (direct) {
109*c8dee2aaSAndroid Build Coastguard Worker return SkImages::TextureFromCompressedTextureData(
110*c8dee2aaSAndroid Build Coastguard Worker direct, std::move(data), kImgWidth, kImgHeight, compression, skgpu::Mipmapped::kNo);
111*c8dee2aaSAndroid Build Coastguard Worker } else {
112*c8dee2aaSAndroid Build Coastguard Worker return SkImages::RasterFromCompressedTextureData(
113*c8dee2aaSAndroid Build Coastguard Worker std::move(data), kImgWidth, kImgHeight, compression);
114*c8dee2aaSAndroid Build Coastguard Worker }
115*c8dee2aaSAndroid Build Coastguard Worker }
116*c8dee2aaSAndroid Build Coastguard Worker
draw_image(SkCanvas * canvas,sk_sp<SkImage> image,int x,int y)117*c8dee2aaSAndroid Build Coastguard Worker static void draw_image(SkCanvas* canvas, sk_sp<SkImage> image, int x, int y) {
118*c8dee2aaSAndroid Build Coastguard Worker
119*c8dee2aaSAndroid Build Coastguard Worker bool isCompressed = false;
120*c8dee2aaSAndroid Build Coastguard Worker if (image && image->isTextureBacked()) {
121*c8dee2aaSAndroid Build Coastguard Worker const GrCaps* caps = as_IB(image)->context()->priv().caps();
122*c8dee2aaSAndroid Build Coastguard Worker GrTextureProxy* proxy = sk_gpu_test::GetTextureImageProxy(image.get(),
123*c8dee2aaSAndroid Build Coastguard Worker canvas->recordingContext());
124*c8dee2aaSAndroid Build Coastguard Worker isCompressed = caps->isFormatCompressed(proxy->backendFormat());
125*c8dee2aaSAndroid Build Coastguard Worker }
126*c8dee2aaSAndroid Build Coastguard Worker
127*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(image, x, y);
128*c8dee2aaSAndroid Build Coastguard Worker
129*c8dee2aaSAndroid Build Coastguard Worker if (!isCompressed) {
130*c8dee2aaSAndroid Build Coastguard Worker SkRect r = SkRect::MakeXYWH(x, y, kImgWidth, kImgHeight);
131*c8dee2aaSAndroid Build Coastguard Worker r.outset(1.0f, 1.0f);
132*c8dee2aaSAndroid Build Coastguard Worker
133*c8dee2aaSAndroid Build Coastguard Worker SkPaint redStroke;
134*c8dee2aaSAndroid Build Coastguard Worker redStroke.setColor(SK_ColorRED);
135*c8dee2aaSAndroid Build Coastguard Worker redStroke.setStyle(SkPaint::kStroke_Style);
136*c8dee2aaSAndroid Build Coastguard Worker redStroke.setStrokeWidth(2.0f);
137*c8dee2aaSAndroid Build Coastguard Worker
138*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(r, redStroke);
139*c8dee2aaSAndroid Build Coastguard Worker }
140*c8dee2aaSAndroid Build Coastguard Worker }
141*c8dee2aaSAndroid Build Coastguard Worker
142*c8dee2aaSAndroid Build Coastguard Worker namespace skiagm {
143*c8dee2aaSAndroid Build Coastguard Worker
144*c8dee2aaSAndroid Build Coastguard Worker // This GM draws the BC1 compressed texture filled with "make_compressed_data"s data twice.
145*c8dee2aaSAndroid Build Coastguard Worker //
146*c8dee2aaSAndroid Build Coastguard Worker // It is drawn once (on the top) as a kBC1_RGB8_UNORM texture and then again (on the bottom)
147*c8dee2aaSAndroid Build Coastguard Worker // as a kBC1_RGBA8_UNORM texture.
148*c8dee2aaSAndroid Build Coastguard Worker //
149*c8dee2aaSAndroid Build Coastguard Worker // If BC1 behaves as expected we should see:
150*c8dee2aaSAndroid Build Coastguard Worker //
151*c8dee2aaSAndroid Build Coastguard Worker // RGB8 Black MidGrey Black* White ...
152*c8dee2aaSAndroid Build Coastguard Worker // Black DrkGrey LtGrey White ...
153*c8dee2aaSAndroid Build Coastguard Worker //
154*c8dee2aaSAndroid Build Coastguard Worker // RGBA8 Black MidGrey Green+ White ...
155*c8dee2aaSAndroid Build Coastguard Worker // Black DrkGrey LtGrey White ...
156*c8dee2aaSAndroid Build Coastguard Worker //
157*c8dee2aaSAndroid Build Coastguard Worker // * We expect this to be black bc the transparent black will be forced to opaque. If BC1 were
158*c8dee2aaSAndroid Build Coastguard Worker // treating it as an opaque block then it would be LtGrey - not black.
159*c8dee2aaSAndroid Build Coastguard Worker // + This is just the background showing through the transparent black
160*c8dee2aaSAndroid Build Coastguard Worker class BC1TransparencyGM : public GM {
161*c8dee2aaSAndroid Build Coastguard Worker public:
BC1TransparencyGM()162*c8dee2aaSAndroid Build Coastguard Worker BC1TransparencyGM() {
163*c8dee2aaSAndroid Build Coastguard Worker this->setBGColor(SK_ColorGREEN);
164*c8dee2aaSAndroid Build Coastguard Worker }
165*c8dee2aaSAndroid Build Coastguard Worker
166*c8dee2aaSAndroid Build Coastguard Worker protected:
getName() const167*c8dee2aaSAndroid Build Coastguard Worker SkString getName() const override { return SkString("bc1_transparency"); }
168*c8dee2aaSAndroid Build Coastguard Worker
getISize()169*c8dee2aaSAndroid Build Coastguard Worker SkISize getISize() override {
170*c8dee2aaSAndroid Build Coastguard Worker return SkISize::Make(kImgWidth + 2 * kPad, 2 * kImgHeight + 3 * kPad);
171*c8dee2aaSAndroid Build Coastguard Worker }
172*c8dee2aaSAndroid Build Coastguard Worker
onGpuSetup(SkCanvas * canvas,SkString * errorMsg,GraphiteTestContext *)173*c8dee2aaSAndroid Build Coastguard Worker DrawResult onGpuSetup(SkCanvas* canvas, SkString* errorMsg, GraphiteTestContext*) override {
174*c8dee2aaSAndroid Build Coastguard Worker auto dContext = GrAsDirectContext(canvas->recordingContext());
175*c8dee2aaSAndroid Build Coastguard Worker if (dContext && dContext->abandoned()) {
176*c8dee2aaSAndroid Build Coastguard Worker // This isn't a GpuGM so a null 'context' is okay but an abandoned context
177*c8dee2aaSAndroid Build Coastguard Worker // if forbidden.
178*c8dee2aaSAndroid Build Coastguard Worker return DrawResult::kSkip;
179*c8dee2aaSAndroid Build Coastguard Worker }
180*c8dee2aaSAndroid Build Coastguard Worker
181*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> bc1Data = make_compressed_data();
182*c8dee2aaSAndroid Build Coastguard Worker
183*c8dee2aaSAndroid Build Coastguard Worker fRGBImage = data_to_img(dContext, bc1Data, SkTextureCompressionType::kBC1_RGB8_UNORM);
184*c8dee2aaSAndroid Build Coastguard Worker fRGBAImage = data_to_img(dContext, std::move(bc1Data),
185*c8dee2aaSAndroid Build Coastguard Worker SkTextureCompressionType::kBC1_RGBA8_UNORM);
186*c8dee2aaSAndroid Build Coastguard Worker if (!fRGBImage || !fRGBAImage) {
187*c8dee2aaSAndroid Build Coastguard Worker *errorMsg = "Failed to create BC1 images.";
188*c8dee2aaSAndroid Build Coastguard Worker return DrawResult::kFail;
189*c8dee2aaSAndroid Build Coastguard Worker }
190*c8dee2aaSAndroid Build Coastguard Worker
191*c8dee2aaSAndroid Build Coastguard Worker return DrawResult::kOk;
192*c8dee2aaSAndroid Build Coastguard Worker }
193*c8dee2aaSAndroid Build Coastguard Worker
onGpuTeardown()194*c8dee2aaSAndroid Build Coastguard Worker void onGpuTeardown() override {
195*c8dee2aaSAndroid Build Coastguard Worker fRGBImage = nullptr;
196*c8dee2aaSAndroid Build Coastguard Worker fRGBAImage = nullptr;
197*c8dee2aaSAndroid Build Coastguard Worker }
198*c8dee2aaSAndroid Build Coastguard Worker
onDraw(SkCanvas * canvas)199*c8dee2aaSAndroid Build Coastguard Worker void onDraw(SkCanvas* canvas) override {
200*c8dee2aaSAndroid Build Coastguard Worker draw_image(canvas, fRGBImage, kPad, kPad);
201*c8dee2aaSAndroid Build Coastguard Worker draw_image(canvas, fRGBAImage, kPad, 2 * kPad + kImgHeight);
202*c8dee2aaSAndroid Build Coastguard Worker }
203*c8dee2aaSAndroid Build Coastguard Worker
204*c8dee2aaSAndroid Build Coastguard Worker private:
205*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> fRGBImage;
206*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> fRGBAImage;
207*c8dee2aaSAndroid Build Coastguard Worker
208*c8dee2aaSAndroid Build Coastguard Worker using INHERITED = GM;
209*c8dee2aaSAndroid Build Coastguard Worker };
210*c8dee2aaSAndroid Build Coastguard Worker
211*c8dee2aaSAndroid Build Coastguard Worker //////////////////////////////////////////////////////////////////////////////
212*c8dee2aaSAndroid Build Coastguard Worker
213*c8dee2aaSAndroid Build Coastguard Worker DEF_GM(return new BC1TransparencyGM;)
214*c8dee2aaSAndroid Build Coastguard Worker } // namespace skiagm
215