xref: /aosp_15_r20/external/skia/gm/bc1_transparency.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
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"
11 #include "include/core/SkTextureCompressionType.h"
12 #include "include/gpu/ganesh/GrDirectContext.h"
13 #include "include/gpu/ganesh/GrRecordingContext.h"
14 #include "include/gpu/ganesh/SkImageGanesh.h"
15 #include "src/core/SkCompressedDataUtils.h"
16 #include "src/gpu/ganesh/GrCaps.h"
17 #include "src/gpu/ganesh/GrImageContextPriv.h"
18 #include "src/gpu/ganesh/image/SkImage_GaneshBase.h"
19 #include "src/image/SkImage_Base.h"
20 #include "tools/gpu/ProxyUtils.h"
21 
22 constexpr int kImgWidth  = 16;
23 constexpr int kImgHeight = 8;
24 constexpr int kPad       = 4;
25 
26 struct BC1Block {
27     uint16_t fColor0;
28     uint16_t fColor1;
29     uint32_t fIndices;
30 };
31 
num_4x4_blocks(int size)32 static int num_4x4_blocks(int size) {
33     return ((size + 3) & ~3) >> 2;
34 }
35 
to565(SkColor col)36 static 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.
create_BC1_block(BC1Block * block,bool transparent)59 static 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.
make_compressed_data()81 static sk_sp<SkData> make_compressed_data() {
82     SkISize dim{ kImgWidth, kImgHeight };
83 
84     size_t totalSize = SkCompressedDataSize(SkTextureCompressionType::kBC1_RGB8_UNORM, dim,
85                                             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 
data_to_img(GrDirectContext * direct,sk_sp<SkData> data,SkTextureCompressionType compression)106 static sk_sp<SkImage> data_to_img(GrDirectContext *direct, sk_sp<SkData> data,
107                                   SkTextureCompressionType compression) {
108     if (direct) {
109         return SkImages::TextureFromCompressedTextureData(
110                 direct, std::move(data), kImgWidth, kImgHeight, compression, skgpu::Mipmapped::kNo);
111     } else {
112         return SkImages::RasterFromCompressedTextureData(
113                 std::move(data), kImgWidth, kImgHeight, compression);
114     }
115 }
116 
draw_image(SkCanvas * canvas,sk_sp<SkImage> image,int x,int y)117 static void draw_image(SkCanvas* canvas, sk_sp<SkImage> image, int x, int y) {
118 
119     bool isCompressed = false;
120     if (image && image->isTextureBacked()) {
121         const GrCaps* caps = as_IB(image)->context()->priv().caps();
122         GrTextureProxy* proxy = sk_gpu_test::GetTextureImageProxy(image.get(),
123                                                                   canvas->recordingContext());
124         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 
142 namespace 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
160 class BC1TransparencyGM : public GM {
161 public:
BC1TransparencyGM()162     BC1TransparencyGM() {
163         this->setBGColor(SK_ColorGREEN);
164     }
165 
166 protected:
getName() const167     SkString getName() const override { return SkString("bc1_transparency"); }
168 
getISize()169     SkISize getISize() override {
170         return SkISize::Make(kImgWidth + 2 * kPad, 2 * kImgHeight + 3 * kPad);
171     }
172 
onGpuSetup(SkCanvas * canvas,SkString * errorMsg,GraphiteTestContext *)173     DrawResult onGpuSetup(SkCanvas* canvas, SkString* errorMsg, GraphiteTestContext*) override {
174         auto dContext = GrAsDirectContext(canvas->recordingContext());
175         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 
183         fRGBImage = data_to_img(dContext, bc1Data, SkTextureCompressionType::kBC1_RGB8_UNORM);
184         fRGBAImage = data_to_img(dContext, std::move(bc1Data),
185                                  SkTextureCompressionType::kBC1_RGBA8_UNORM);
186         if (!fRGBImage || !fRGBAImage) {
187             *errorMsg = "Failed to create BC1 images.";
188             return DrawResult::kFail;
189         }
190 
191         return DrawResult::kOk;
192     }
193 
onGpuTeardown()194     void onGpuTeardown() override {
195         fRGBImage = nullptr;
196         fRGBAImage = nullptr;
197     }
198 
onDraw(SkCanvas * canvas)199     void onDraw(SkCanvas* canvas) override {
200         draw_image(canvas, fRGBImage, kPad, kPad);
201         draw_image(canvas, fRGBAImage, kPad, 2 * kPad + kImgHeight);
202     }
203 
204 private:
205     sk_sp<SkImage> fRGBImage;
206     sk_sp<SkImage> fRGBAImage;
207 
208     using INHERITED = GM;
209 };
210 
211 //////////////////////////////////////////////////////////////////////////////
212 
213 DEF_GM(return new BC1TransparencyGM;)
214 }  // namespace skiagm
215