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