xref: /aosp_15_r20/external/skia/gm/bc1_transparency.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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