xref: /aosp_15_r20/external/skia/gm/exoticformats.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/SkStream.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTextureCompressionType.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrDirectContext.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrRecordingContext.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/SkImageGanesh.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkCompressedDataUtils.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkMipmap.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrCaps.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrImageContextPriv.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrRecordingContextPriv.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/gl/GrGLDefines.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/image/SkImage_GaneshBase.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/image/SkImage_Base.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "tools/Resources.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "tools/gpu/ProxyUtils.h"
26*c8dee2aaSAndroid Build Coastguard Worker 
27*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
28*c8dee2aaSAndroid Build Coastguard Worker 
29*c8dee2aaSAndroid Build Coastguard Worker //-------------------------------------------------------------------------------------------------
30*c8dee2aaSAndroid Build Coastguard Worker struct ImageInfo {
31*c8dee2aaSAndroid Build Coastguard Worker     SkISize fDim;
32*c8dee2aaSAndroid Build Coastguard Worker     skgpu::Mipmapped fMipmapped;
33*c8dee2aaSAndroid Build Coastguard Worker     SkTextureCompressionType fCompressionType;
34*c8dee2aaSAndroid Build Coastguard Worker };
35*c8dee2aaSAndroid Build Coastguard Worker 
36*c8dee2aaSAndroid Build Coastguard Worker /*
37*c8dee2aaSAndroid Build Coastguard Worker  * Get an int from a buffer
38*c8dee2aaSAndroid Build Coastguard Worker  * This method is unsafe, the caller is responsible for performing a check
39*c8dee2aaSAndroid Build Coastguard Worker  */
get_uint(uint8_t * buffer,uint32_t i)40*c8dee2aaSAndroid Build Coastguard Worker static inline uint32_t get_uint(uint8_t* buffer, uint32_t i) {
41*c8dee2aaSAndroid Build Coastguard Worker     uint32_t result;
42*c8dee2aaSAndroid Build Coastguard Worker     memcpy(&result, &(buffer[i]), 4);
43*c8dee2aaSAndroid Build Coastguard Worker     return result;
44*c8dee2aaSAndroid Build Coastguard Worker }
45*c8dee2aaSAndroid Build Coastguard Worker 
46*c8dee2aaSAndroid Build Coastguard Worker // This KTX loader is barely sufficient to load the specific files this GM requires. Use
47*c8dee2aaSAndroid Build Coastguard Worker // at your own peril.
load_ktx(const char * filename,ImageInfo * imageInfo)48*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkData> load_ktx(const char* filename, ImageInfo* imageInfo) {
49*c8dee2aaSAndroid Build Coastguard Worker     SkFILEStream input(filename);
50*c8dee2aaSAndroid Build Coastguard Worker     if (!input.isValid()) {
51*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
52*c8dee2aaSAndroid Build Coastguard Worker     }
53*c8dee2aaSAndroid Build Coastguard Worker 
54*c8dee2aaSAndroid Build Coastguard Worker     constexpr int kKTXIdentifierSize = 12;
55*c8dee2aaSAndroid Build Coastguard Worker     constexpr int kKTXHeaderSize = kKTXIdentifierSize + 13 * sizeof(uint32_t);
56*c8dee2aaSAndroid Build Coastguard Worker     uint8_t header[kKTXHeaderSize];
57*c8dee2aaSAndroid Build Coastguard Worker 
58*c8dee2aaSAndroid Build Coastguard Worker     if (input.read(header, kKTXHeaderSize) != kKTXHeaderSize) {
59*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
60*c8dee2aaSAndroid Build Coastguard Worker     }
61*c8dee2aaSAndroid Build Coastguard Worker 
62*c8dee2aaSAndroid Build Coastguard Worker     static const uint8_t kExpectedIdentifier[kKTXIdentifierSize] = {
63*c8dee2aaSAndroid Build Coastguard Worker         0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
64*c8dee2aaSAndroid Build Coastguard Worker     };
65*c8dee2aaSAndroid Build Coastguard Worker 
66*c8dee2aaSAndroid Build Coastguard Worker     if (0 != memcmp(header, kExpectedIdentifier, kKTXIdentifierSize)) {
67*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
68*c8dee2aaSAndroid Build Coastguard Worker     }
69*c8dee2aaSAndroid Build Coastguard Worker 
70*c8dee2aaSAndroid Build Coastguard Worker     uint32_t endianness = get_uint(header, 12);
71*c8dee2aaSAndroid Build Coastguard Worker     if (endianness != 0x04030201) {
72*c8dee2aaSAndroid Build Coastguard Worker         // TODO: need to swap rest of header and, if glTypeSize is > 1, all
73*c8dee2aaSAndroid Build Coastguard Worker         // the texture data.
74*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
75*c8dee2aaSAndroid Build Coastguard Worker     }
76*c8dee2aaSAndroid Build Coastguard Worker 
77*c8dee2aaSAndroid Build Coastguard Worker     uint32_t glType = get_uint(header, 16);
78*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(uint32_t glTypeSize = get_uint(header, 20);)
79*c8dee2aaSAndroid Build Coastguard Worker     uint32_t glFormat = get_uint(header, 24);
80*c8dee2aaSAndroid Build Coastguard Worker     uint32_t glInternalFormat = get_uint(header, 28);
81*c8dee2aaSAndroid Build Coastguard Worker     //uint32_t glBaseInternalFormat = get_uint(header, 32);
82*c8dee2aaSAndroid Build Coastguard Worker     uint32_t pixelWidth = get_uint(header, 36);
83*c8dee2aaSAndroid Build Coastguard Worker     uint32_t pixelHeight = get_uint(header, 40);
84*c8dee2aaSAndroid Build Coastguard Worker     uint32_t pixelDepth = get_uint(header, 44);
85*c8dee2aaSAndroid Build Coastguard Worker     //uint32_t numberOfArrayElements = get_uint(header, 48);
86*c8dee2aaSAndroid Build Coastguard Worker     uint32_t numberOfFaces = get_uint(header, 52);
87*c8dee2aaSAndroid Build Coastguard Worker     int numberOfMipmapLevels = get_uint(header, 56);
88*c8dee2aaSAndroid Build Coastguard Worker     uint32_t bytesOfKeyValueData = get_uint(header, 60);
89*c8dee2aaSAndroid Build Coastguard Worker 
90*c8dee2aaSAndroid Build Coastguard Worker     if (glType != 0 || glFormat != 0) {  // only care about compressed data for now
91*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
92*c8dee2aaSAndroid Build Coastguard Worker     }
93*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(glTypeSize == 1); // required for compressed data
94*c8dee2aaSAndroid Build Coastguard Worker 
95*c8dee2aaSAndroid Build Coastguard Worker     // We only handle these four formats right now
96*c8dee2aaSAndroid Build Coastguard Worker     switch (glInternalFormat) {
97*c8dee2aaSAndroid Build Coastguard Worker         case GR_GL_COMPRESSED_ETC1_RGB8:
98*c8dee2aaSAndroid Build Coastguard Worker         case GR_GL_COMPRESSED_RGB8_ETC2:
99*c8dee2aaSAndroid Build Coastguard Worker             imageInfo->fCompressionType = SkTextureCompressionType::kETC2_RGB8_UNORM;
100*c8dee2aaSAndroid Build Coastguard Worker             break;
101*c8dee2aaSAndroid Build Coastguard Worker         case GR_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
102*c8dee2aaSAndroid Build Coastguard Worker             imageInfo->fCompressionType = SkTextureCompressionType::kBC1_RGB8_UNORM;
103*c8dee2aaSAndroid Build Coastguard Worker             break;
104*c8dee2aaSAndroid Build Coastguard Worker         case GR_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
105*c8dee2aaSAndroid Build Coastguard Worker             imageInfo->fCompressionType = SkTextureCompressionType::kBC1_RGBA8_UNORM;
106*c8dee2aaSAndroid Build Coastguard Worker             break;
107*c8dee2aaSAndroid Build Coastguard Worker         default:
108*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
109*c8dee2aaSAndroid Build Coastguard Worker     }
110*c8dee2aaSAndroid Build Coastguard Worker 
111*c8dee2aaSAndroid Build Coastguard Worker     imageInfo->fDim.fWidth = pixelWidth;
112*c8dee2aaSAndroid Build Coastguard Worker     imageInfo->fDim.fHeight = pixelHeight;
113*c8dee2aaSAndroid Build Coastguard Worker 
114*c8dee2aaSAndroid Build Coastguard Worker     if (pixelDepth != 0) {
115*c8dee2aaSAndroid Build Coastguard Worker         return nullptr; // pixel depth is always zero for 2D textures
116*c8dee2aaSAndroid Build Coastguard Worker     }
117*c8dee2aaSAndroid Build Coastguard Worker 
118*c8dee2aaSAndroid Build Coastguard Worker     if (numberOfFaces != 1) {
119*c8dee2aaSAndroid Build Coastguard Worker         return nullptr; // we don't support cube maps right now
120*c8dee2aaSAndroid Build Coastguard Worker     }
121*c8dee2aaSAndroid Build Coastguard Worker 
122*c8dee2aaSAndroid Build Coastguard Worker     if (numberOfMipmapLevels == 1) {
123*c8dee2aaSAndroid Build Coastguard Worker         imageInfo->fMipmapped = skgpu::Mipmapped::kNo;
124*c8dee2aaSAndroid Build Coastguard Worker     } else {
125*c8dee2aaSAndroid Build Coastguard Worker         int numRequiredMipLevels = SkMipmap::ComputeLevelCount(pixelWidth, pixelHeight)+1;
126*c8dee2aaSAndroid Build Coastguard Worker         if (numberOfMipmapLevels != numRequiredMipLevels) {
127*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
128*c8dee2aaSAndroid Build Coastguard Worker         }
129*c8dee2aaSAndroid Build Coastguard Worker         imageInfo->fMipmapped = skgpu::Mipmapped::kYes;
130*c8dee2aaSAndroid Build Coastguard Worker     }
131*c8dee2aaSAndroid Build Coastguard Worker 
132*c8dee2aaSAndroid Build Coastguard Worker     if (bytesOfKeyValueData != 0) {
133*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
134*c8dee2aaSAndroid Build Coastguard Worker     }
135*c8dee2aaSAndroid Build Coastguard Worker 
136*c8dee2aaSAndroid Build Coastguard Worker     TArray<size_t> individualMipOffsets(numberOfMipmapLevels);
137*c8dee2aaSAndroid Build Coastguard Worker 
138*c8dee2aaSAndroid Build Coastguard Worker     size_t dataSize = SkCompressedDataSize(imageInfo->fCompressionType,
139*c8dee2aaSAndroid Build Coastguard Worker                                            {(int)pixelWidth, (int)pixelHeight},
140*c8dee2aaSAndroid Build Coastguard Worker                                            &individualMipOffsets,
141*c8dee2aaSAndroid Build Coastguard Worker                                            imageInfo->fMipmapped == skgpu::Mipmapped::kYes);
142*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(individualMipOffsets.size() == numberOfMipmapLevels);
143*c8dee2aaSAndroid Build Coastguard Worker 
144*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> data = SkData::MakeUninitialized(dataSize);
145*c8dee2aaSAndroid Build Coastguard Worker 
146*c8dee2aaSAndroid Build Coastguard Worker     uint8_t* dest = (uint8_t*) data->writable_data();
147*c8dee2aaSAndroid Build Coastguard Worker 
148*c8dee2aaSAndroid Build Coastguard Worker     size_t offset = 0;
149*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < numberOfMipmapLevels; ++i) {
150*c8dee2aaSAndroid Build Coastguard Worker         uint32_t imageSize;
151*c8dee2aaSAndroid Build Coastguard Worker 
152*c8dee2aaSAndroid Build Coastguard Worker         if (input.read(&imageSize, 4) != 4) {
153*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
154*c8dee2aaSAndroid Build Coastguard Worker         }
155*c8dee2aaSAndroid Build Coastguard Worker 
156*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(offset + imageSize <= dataSize);
157*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(offset == individualMipOffsets[i]);
158*c8dee2aaSAndroid Build Coastguard Worker 
159*c8dee2aaSAndroid Build Coastguard Worker         if (input.read(&dest[offset], imageSize) != imageSize) {
160*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
161*c8dee2aaSAndroid Build Coastguard Worker         }
162*c8dee2aaSAndroid Build Coastguard Worker 
163*c8dee2aaSAndroid Build Coastguard Worker         offset += imageSize;
164*c8dee2aaSAndroid Build Coastguard Worker     }
165*c8dee2aaSAndroid Build Coastguard Worker 
166*c8dee2aaSAndroid Build Coastguard Worker     return data;
167*c8dee2aaSAndroid Build Coastguard Worker }
168*c8dee2aaSAndroid Build Coastguard Worker 
169*c8dee2aaSAndroid Build Coastguard Worker //-------------------------------------------------------------------------------------------------
170*c8dee2aaSAndroid Build Coastguard Worker typedef uint32_t DWORD;
171*c8dee2aaSAndroid Build Coastguard Worker 
172*c8dee2aaSAndroid Build Coastguard Worker // Values for the DDS_PIXELFORMAT 'dwFlags' field
173*c8dee2aaSAndroid Build Coastguard Worker constexpr unsigned int kDDPF_FOURCC      = 0x4;
174*c8dee2aaSAndroid Build Coastguard Worker 
175*c8dee2aaSAndroid Build Coastguard Worker struct DDS_PIXELFORMAT {
176*c8dee2aaSAndroid Build Coastguard Worker     DWORD dwSize;
177*c8dee2aaSAndroid Build Coastguard Worker     DWORD dwFlags;
178*c8dee2aaSAndroid Build Coastguard Worker     DWORD dwFourCC;
179*c8dee2aaSAndroid Build Coastguard Worker     DWORD dwRGBBitCount;
180*c8dee2aaSAndroid Build Coastguard Worker     DWORD dwRBitMask;
181*c8dee2aaSAndroid Build Coastguard Worker     DWORD dwGBitMask;
182*c8dee2aaSAndroid Build Coastguard Worker     DWORD dwBBitMask;
183*c8dee2aaSAndroid Build Coastguard Worker     DWORD dwABitMask;
184*c8dee2aaSAndroid Build Coastguard Worker };
185*c8dee2aaSAndroid Build Coastguard Worker 
186*c8dee2aaSAndroid Build Coastguard Worker // Values for the DDS_HEADER 'dwFlags' field
187*c8dee2aaSAndroid Build Coastguard Worker constexpr unsigned int kDDSD_CAPS        = 0x1;        // required
188*c8dee2aaSAndroid Build Coastguard Worker constexpr unsigned int kDDSD_HEIGHT      = 0x2;        // required
189*c8dee2aaSAndroid Build Coastguard Worker constexpr unsigned int kDDSD_WIDTH       = 0x4;        // required
190*c8dee2aaSAndroid Build Coastguard Worker constexpr unsigned int kDDSD_PITCH       = 0x8;
191*c8dee2aaSAndroid Build Coastguard Worker constexpr unsigned int kDDSD_PIXELFORMAT = 0x001000;   // required
192*c8dee2aaSAndroid Build Coastguard Worker constexpr unsigned int kDDSD_MIPMAPCOUNT = 0x020000;
193*c8dee2aaSAndroid Build Coastguard Worker constexpr unsigned int kDDSD_LINEARSIZE  = 0x080000;
194*c8dee2aaSAndroid Build Coastguard Worker constexpr unsigned int kDDSD_DEPTH       = 0x800000;
195*c8dee2aaSAndroid Build Coastguard Worker 
196*c8dee2aaSAndroid Build Coastguard Worker constexpr unsigned int kDDSD_REQUIRED = kDDSD_CAPS | kDDSD_HEIGHT | kDDSD_WIDTH | kDDSD_PIXELFORMAT;
197*c8dee2aaSAndroid Build Coastguard Worker 
198*c8dee2aaSAndroid Build Coastguard Worker typedef struct {
199*c8dee2aaSAndroid Build Coastguard Worker     DWORD           dwSize;
200*c8dee2aaSAndroid Build Coastguard Worker     DWORD           dwFlags;
201*c8dee2aaSAndroid Build Coastguard Worker     DWORD           dwHeight;
202*c8dee2aaSAndroid Build Coastguard Worker     DWORD           dwWidth;
203*c8dee2aaSAndroid Build Coastguard Worker     DWORD           dwPitchOrLinearSize;
204*c8dee2aaSAndroid Build Coastguard Worker     DWORD           dwDepth;
205*c8dee2aaSAndroid Build Coastguard Worker     DWORD           dwMipMapCount;
206*c8dee2aaSAndroid Build Coastguard Worker     DWORD           dwReserved1[11];
207*c8dee2aaSAndroid Build Coastguard Worker     DDS_PIXELFORMAT ddspf;
208*c8dee2aaSAndroid Build Coastguard Worker     DWORD           dwCaps;
209*c8dee2aaSAndroid Build Coastguard Worker     DWORD           dwCaps2;
210*c8dee2aaSAndroid Build Coastguard Worker     DWORD           dwCaps3;
211*c8dee2aaSAndroid Build Coastguard Worker     DWORD           dwCaps4;
212*c8dee2aaSAndroid Build Coastguard Worker     DWORD           dwReserved2;
213*c8dee2aaSAndroid Build Coastguard Worker } DDS_HEADER;
214*c8dee2aaSAndroid Build Coastguard Worker 
215*c8dee2aaSAndroid Build Coastguard Worker // This DDS loader is barely sufficient to load the specific files this GM requires. Use
216*c8dee2aaSAndroid Build Coastguard Worker // at your own peril.
load_dds(const char * filename,ImageInfo * imageInfo)217*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkData> load_dds(const char* filename, ImageInfo* imageInfo) {
218*c8dee2aaSAndroid Build Coastguard Worker     SkFILEStream input(filename);
219*c8dee2aaSAndroid Build Coastguard Worker     if (!input.isValid()) {
220*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
221*c8dee2aaSAndroid Build Coastguard Worker     }
222*c8dee2aaSAndroid Build Coastguard Worker 
223*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kMagic = 0x20534444;
224*c8dee2aaSAndroid Build Coastguard Worker     uint32_t magic;
225*c8dee2aaSAndroid Build Coastguard Worker 
226*c8dee2aaSAndroid Build Coastguard Worker     if (input.read(&magic, 4) != 4) {
227*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
228*c8dee2aaSAndroid Build Coastguard Worker     }
229*c8dee2aaSAndroid Build Coastguard Worker 
230*c8dee2aaSAndroid Build Coastguard Worker     if (magic != kMagic) {
231*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
232*c8dee2aaSAndroid Build Coastguard Worker     }
233*c8dee2aaSAndroid Build Coastguard Worker 
234*c8dee2aaSAndroid Build Coastguard Worker     constexpr size_t kDDSHeaderSize = sizeof(DDS_HEADER);
235*c8dee2aaSAndroid Build Coastguard Worker     static_assert(kDDSHeaderSize == 124);
236*c8dee2aaSAndroid Build Coastguard Worker     constexpr size_t kDDSPixelFormatSize = sizeof(DDS_PIXELFORMAT);
237*c8dee2aaSAndroid Build Coastguard Worker     static_assert(kDDSPixelFormatSize == 32);
238*c8dee2aaSAndroid Build Coastguard Worker 
239*c8dee2aaSAndroid Build Coastguard Worker     DDS_HEADER header;
240*c8dee2aaSAndroid Build Coastguard Worker 
241*c8dee2aaSAndroid Build Coastguard Worker     if (input.read(&header, kDDSHeaderSize) != kDDSHeaderSize) {
242*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
243*c8dee2aaSAndroid Build Coastguard Worker     }
244*c8dee2aaSAndroid Build Coastguard Worker 
245*c8dee2aaSAndroid Build Coastguard Worker     if (header.dwSize != kDDSHeaderSize ||
246*c8dee2aaSAndroid Build Coastguard Worker         header.ddspf.dwSize != kDDSPixelFormatSize) {
247*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
248*c8dee2aaSAndroid Build Coastguard Worker     }
249*c8dee2aaSAndroid Build Coastguard Worker 
250*c8dee2aaSAndroid Build Coastguard Worker     if ((header.dwFlags & kDDSD_REQUIRED) != kDDSD_REQUIRED) {
251*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
252*c8dee2aaSAndroid Build Coastguard Worker     }
253*c8dee2aaSAndroid Build Coastguard Worker 
254*c8dee2aaSAndroid Build Coastguard Worker     if (header.dwFlags & (kDDSD_PITCH | kDDSD_LINEARSIZE | kDDSD_DEPTH)) {
255*c8dee2aaSAndroid Build Coastguard Worker         // TODO: support these features
256*c8dee2aaSAndroid Build Coastguard Worker     }
257*c8dee2aaSAndroid Build Coastguard Worker 
258*c8dee2aaSAndroid Build Coastguard Worker     imageInfo->fDim.fWidth = header.dwWidth;
259*c8dee2aaSAndroid Build Coastguard Worker     imageInfo->fDim.fHeight = header.dwHeight;
260*c8dee2aaSAndroid Build Coastguard Worker 
261*c8dee2aaSAndroid Build Coastguard Worker     int numberOfMipmapLevels = 1;
262*c8dee2aaSAndroid Build Coastguard Worker     if (header.dwFlags & kDDSD_MIPMAPCOUNT) {
263*c8dee2aaSAndroid Build Coastguard Worker         if (header.dwMipMapCount == 1) {
264*c8dee2aaSAndroid Build Coastguard Worker             imageInfo->fMipmapped = skgpu::Mipmapped::kNo;
265*c8dee2aaSAndroid Build Coastguard Worker         } else {
266*c8dee2aaSAndroid Build Coastguard Worker             int numRequiredLevels = SkMipmap::ComputeLevelCount(header.dwWidth, header.dwHeight)+1;
267*c8dee2aaSAndroid Build Coastguard Worker             if (header.dwMipMapCount != (unsigned) numRequiredLevels) {
268*c8dee2aaSAndroid Build Coastguard Worker                 return nullptr;
269*c8dee2aaSAndroid Build Coastguard Worker             }
270*c8dee2aaSAndroid Build Coastguard Worker             imageInfo->fMipmapped = skgpu::Mipmapped::kYes;
271*c8dee2aaSAndroid Build Coastguard Worker             numberOfMipmapLevels = numRequiredLevels;
272*c8dee2aaSAndroid Build Coastguard Worker         }
273*c8dee2aaSAndroid Build Coastguard Worker     } else {
274*c8dee2aaSAndroid Build Coastguard Worker         imageInfo->fMipmapped = skgpu::Mipmapped::kNo;
275*c8dee2aaSAndroid Build Coastguard Worker     }
276*c8dee2aaSAndroid Build Coastguard Worker 
277*c8dee2aaSAndroid Build Coastguard Worker     if (!(header.ddspf.dwFlags & kDDPF_FOURCC)) {
278*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
279*c8dee2aaSAndroid Build Coastguard Worker     }
280*c8dee2aaSAndroid Build Coastguard Worker 
281*c8dee2aaSAndroid Build Coastguard Worker     // We only handle these one format right now
282*c8dee2aaSAndroid Build Coastguard Worker     switch (header.ddspf.dwFourCC) {
283*c8dee2aaSAndroid Build Coastguard Worker         case 0x31545844: // DXT1
284*c8dee2aaSAndroid Build Coastguard Worker             imageInfo->fCompressionType = SkTextureCompressionType::kBC1_RGB8_UNORM;
285*c8dee2aaSAndroid Build Coastguard Worker             break;
286*c8dee2aaSAndroid Build Coastguard Worker         default:
287*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
288*c8dee2aaSAndroid Build Coastguard Worker     }
289*c8dee2aaSAndroid Build Coastguard Worker 
290*c8dee2aaSAndroid Build Coastguard Worker     TArray<size_t> individualMipOffsets(numberOfMipmapLevels);
291*c8dee2aaSAndroid Build Coastguard Worker 
292*c8dee2aaSAndroid Build Coastguard Worker     size_t dataSize = SkCompressedDataSize(imageInfo->fCompressionType,
293*c8dee2aaSAndroid Build Coastguard Worker                                            {(int)header.dwWidth, (int)header.dwHeight},
294*c8dee2aaSAndroid Build Coastguard Worker                                            &individualMipOffsets,
295*c8dee2aaSAndroid Build Coastguard Worker                                            imageInfo->fMipmapped == skgpu::Mipmapped::kYes);
296*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(individualMipOffsets.size() == numberOfMipmapLevels);
297*c8dee2aaSAndroid Build Coastguard Worker 
298*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> data = SkData::MakeUninitialized(dataSize);
299*c8dee2aaSAndroid Build Coastguard Worker 
300*c8dee2aaSAndroid Build Coastguard Worker     uint8_t* dest = (uint8_t*) data->writable_data();
301*c8dee2aaSAndroid Build Coastguard Worker 
302*c8dee2aaSAndroid Build Coastguard Worker     size_t amountRead = input.read(dest, dataSize);
303*c8dee2aaSAndroid Build Coastguard Worker     if (amountRead != dataSize) {
304*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
305*c8dee2aaSAndroid Build Coastguard Worker     }
306*c8dee2aaSAndroid Build Coastguard Worker 
307*c8dee2aaSAndroid Build Coastguard Worker     return data;
308*c8dee2aaSAndroid Build Coastguard Worker }
309*c8dee2aaSAndroid Build Coastguard Worker 
310*c8dee2aaSAndroid Build Coastguard Worker //-------------------------------------------------------------------------------------------------
data_to_img(GrDirectContext * direct,sk_sp<SkData> data,const ImageInfo & info)311*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkImage> data_to_img(GrDirectContext *direct, sk_sp<SkData> data,
312*c8dee2aaSAndroid Build Coastguard Worker                                   const ImageInfo& info) {
313*c8dee2aaSAndroid Build Coastguard Worker     if (direct) {
314*c8dee2aaSAndroid Build Coastguard Worker         return SkImages::TextureFromCompressedTextureData(direct,
315*c8dee2aaSAndroid Build Coastguard Worker                                                           std::move(data),
316*c8dee2aaSAndroid Build Coastguard Worker                                                           info.fDim.fWidth,
317*c8dee2aaSAndroid Build Coastguard Worker                                                           info.fDim.fHeight,
318*c8dee2aaSAndroid Build Coastguard Worker                                                           info.fCompressionType,
319*c8dee2aaSAndroid Build Coastguard Worker                                                           info.fMipmapped);
320*c8dee2aaSAndroid Build Coastguard Worker     } else {
321*c8dee2aaSAndroid Build Coastguard Worker         return SkImages::RasterFromCompressedTextureData(
322*c8dee2aaSAndroid Build Coastguard Worker                 std::move(data), info.fDim.fWidth, info.fDim.fHeight, info.fCompressionType);
323*c8dee2aaSAndroid Build Coastguard Worker     }
324*c8dee2aaSAndroid Build Coastguard Worker }
325*c8dee2aaSAndroid Build Coastguard Worker 
326*c8dee2aaSAndroid Build Coastguard Worker namespace skiagm {
327*c8dee2aaSAndroid Build Coastguard Worker 
328*c8dee2aaSAndroid Build Coastguard Worker // This GM exercises our handling of some of the more exotic formats using externally
329*c8dee2aaSAndroid Build Coastguard Worker // generated content. Right now it only tests ETC1 and BC1.
330*c8dee2aaSAndroid Build Coastguard Worker class ExoticFormatsGM : public GM {
331*c8dee2aaSAndroid Build Coastguard Worker public:
ExoticFormatsGM()332*c8dee2aaSAndroid Build Coastguard Worker     ExoticFormatsGM() {
333*c8dee2aaSAndroid Build Coastguard Worker         this->setBGColor(SK_ColorBLACK);
334*c8dee2aaSAndroid Build Coastguard Worker     }
335*c8dee2aaSAndroid Build Coastguard Worker 
336*c8dee2aaSAndroid Build Coastguard Worker protected:
getName() const337*c8dee2aaSAndroid Build Coastguard Worker     SkString getName() const override { return SkString("exoticformats"); }
338*c8dee2aaSAndroid Build Coastguard Worker 
getISize()339*c8dee2aaSAndroid Build Coastguard Worker     SkISize getISize() override {
340*c8dee2aaSAndroid Build Coastguard Worker         return SkISize::Make(2*kImgWidthHeight + 3 * kPad, kImgWidthHeight + 2 * kPad);
341*c8dee2aaSAndroid Build Coastguard Worker     }
342*c8dee2aaSAndroid Build Coastguard Worker 
loadImages(GrDirectContext * direct)343*c8dee2aaSAndroid Build Coastguard Worker     bool loadImages(GrDirectContext *direct) {
344*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!fETC1Image && !fBC1Image);
345*c8dee2aaSAndroid Build Coastguard Worker 
346*c8dee2aaSAndroid Build Coastguard Worker         {
347*c8dee2aaSAndroid Build Coastguard Worker             ImageInfo info;
348*c8dee2aaSAndroid Build Coastguard Worker             sk_sp<SkData> data = load_ktx(GetResourcePath("images/flower-etc1.ktx").c_str(), &info);
349*c8dee2aaSAndroid Build Coastguard Worker             if (data) {
350*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(info.fDim.equals(kImgWidthHeight, kImgWidthHeight));
351*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(info.fMipmapped == skgpu::Mipmapped::kNo);
352*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(info.fCompressionType == SkTextureCompressionType::kETC2_RGB8_UNORM);
353*c8dee2aaSAndroid Build Coastguard Worker 
354*c8dee2aaSAndroid Build Coastguard Worker                 fETC1Image = data_to_img(direct, std::move(data), info);
355*c8dee2aaSAndroid Build Coastguard Worker             } else {
356*c8dee2aaSAndroid Build Coastguard Worker                 SkDebugf("failed to load flower-etc1.ktx\n");
357*c8dee2aaSAndroid Build Coastguard Worker                 return false;
358*c8dee2aaSAndroid Build Coastguard Worker             }
359*c8dee2aaSAndroid Build Coastguard Worker         }
360*c8dee2aaSAndroid Build Coastguard Worker 
361*c8dee2aaSAndroid Build Coastguard Worker         {
362*c8dee2aaSAndroid Build Coastguard Worker             ImageInfo info;
363*c8dee2aaSAndroid Build Coastguard Worker             sk_sp<SkData> data = load_dds(GetResourcePath("images/flower-bc1.dds").c_str(), &info);
364*c8dee2aaSAndroid Build Coastguard Worker             if (data) {
365*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(info.fDim.equals(kImgWidthHeight, kImgWidthHeight));
366*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(info.fMipmapped == skgpu::Mipmapped::kNo);
367*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(info.fCompressionType == SkTextureCompressionType::kBC1_RGB8_UNORM);
368*c8dee2aaSAndroid Build Coastguard Worker 
369*c8dee2aaSAndroid Build Coastguard Worker                 fBC1Image = data_to_img(direct, std::move(data), info);
370*c8dee2aaSAndroid Build Coastguard Worker             } else {
371*c8dee2aaSAndroid Build Coastguard Worker                 SkDebugf("failed to load flower-bc1.dds\n");
372*c8dee2aaSAndroid Build Coastguard Worker                 return false;
373*c8dee2aaSAndroid Build Coastguard Worker             }
374*c8dee2aaSAndroid Build Coastguard Worker         }
375*c8dee2aaSAndroid Build Coastguard Worker 
376*c8dee2aaSAndroid Build Coastguard Worker         return true;
377*c8dee2aaSAndroid Build Coastguard Worker     }
378*c8dee2aaSAndroid Build Coastguard Worker 
drawImage(SkCanvas * canvas,SkImage * image,int x,int y)379*c8dee2aaSAndroid Build Coastguard Worker     void drawImage(SkCanvas* canvas, SkImage* image, int x, int y) {
380*c8dee2aaSAndroid Build Coastguard Worker         if (!image) {
381*c8dee2aaSAndroid Build Coastguard Worker             return;
382*c8dee2aaSAndroid Build Coastguard Worker         }
383*c8dee2aaSAndroid Build Coastguard Worker 
384*c8dee2aaSAndroid Build Coastguard Worker         bool isCompressed = false;
385*c8dee2aaSAndroid Build Coastguard Worker         if (image->isTextureBacked()) {
386*c8dee2aaSAndroid Build Coastguard Worker             const GrCaps* caps = as_IB(image)->context()->priv().caps();
387*c8dee2aaSAndroid Build Coastguard Worker             GrTextureProxy* proxy = sk_gpu_test::GetTextureImageProxy(image,
388*c8dee2aaSAndroid Build Coastguard Worker                                                                       canvas->recordingContext());
389*c8dee2aaSAndroid Build Coastguard Worker             isCompressed = caps->isFormatCompressed(proxy->backendFormat());
390*c8dee2aaSAndroid Build Coastguard Worker         }
391*c8dee2aaSAndroid Build Coastguard Worker 
392*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawImage(image, x, y);
393*c8dee2aaSAndroid Build Coastguard Worker 
394*c8dee2aaSAndroid Build Coastguard Worker         if (!isCompressed) {
395*c8dee2aaSAndroid Build Coastguard Worker             // Make it obvious which drawImages used decompressed images
396*c8dee2aaSAndroid Build Coastguard Worker             SkRect r = SkRect::MakeXYWH(x, y, kImgWidthHeight, kImgWidthHeight);
397*c8dee2aaSAndroid Build Coastguard Worker             SkPaint paint;
398*c8dee2aaSAndroid Build Coastguard Worker             paint.setColor(SK_ColorRED);
399*c8dee2aaSAndroid Build Coastguard Worker             paint.setStyle(SkPaint::kStroke_Style);
400*c8dee2aaSAndroid Build Coastguard Worker             paint.setStrokeWidth(2.0f);
401*c8dee2aaSAndroid Build Coastguard Worker             canvas->drawRect(r, paint);
402*c8dee2aaSAndroid Build Coastguard Worker         }
403*c8dee2aaSAndroid Build Coastguard Worker     }
404*c8dee2aaSAndroid Build Coastguard Worker 
onGpuSetup(SkCanvas * canvas,SkString * errorMsg,GraphiteTestContext *)405*c8dee2aaSAndroid Build Coastguard Worker     DrawResult onGpuSetup(SkCanvas* canvas, SkString* errorMsg, GraphiteTestContext*) override {
406*c8dee2aaSAndroid Build Coastguard Worker         auto dContext = GrAsDirectContext(canvas->recordingContext());
407*c8dee2aaSAndroid Build Coastguard Worker         if (dContext && dContext->abandoned()) {
408*c8dee2aaSAndroid Build Coastguard Worker             // This isn't a GpuGM so a null 'context' is okay but an abandoned context
409*c8dee2aaSAndroid Build Coastguard Worker             // if forbidden.
410*c8dee2aaSAndroid Build Coastguard Worker             return DrawResult::kSkip;
411*c8dee2aaSAndroid Build Coastguard Worker         }
412*c8dee2aaSAndroid Build Coastguard Worker 
413*c8dee2aaSAndroid Build Coastguard Worker         if (!this->loadImages(dContext)) {
414*c8dee2aaSAndroid Build Coastguard Worker             *errorMsg = "Failed to create images.";
415*c8dee2aaSAndroid Build Coastguard Worker             return DrawResult::kFail;
416*c8dee2aaSAndroid Build Coastguard Worker         }
417*c8dee2aaSAndroid Build Coastguard Worker 
418*c8dee2aaSAndroid Build Coastguard Worker         return DrawResult::kOk;
419*c8dee2aaSAndroid Build Coastguard Worker     }
420*c8dee2aaSAndroid Build Coastguard Worker 
onGpuTeardown()421*c8dee2aaSAndroid Build Coastguard Worker     void onGpuTeardown() override {
422*c8dee2aaSAndroid Build Coastguard Worker         fETC1Image = nullptr;
423*c8dee2aaSAndroid Build Coastguard Worker         fBC1Image = nullptr;
424*c8dee2aaSAndroid Build Coastguard Worker     }
425*c8dee2aaSAndroid Build Coastguard Worker 
onDraw(SkCanvas * canvas)426*c8dee2aaSAndroid Build Coastguard Worker     void onDraw(SkCanvas* canvas) override {
427*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fETC1Image && fBC1Image);
428*c8dee2aaSAndroid Build Coastguard Worker 
429*c8dee2aaSAndroid Build Coastguard Worker         this->drawImage(canvas, fETC1Image.get(), kPad, kPad);
430*c8dee2aaSAndroid Build Coastguard Worker         this->drawImage(canvas, fBC1Image.get(), kImgWidthHeight + 2 * kPad, kPad);
431*c8dee2aaSAndroid Build Coastguard Worker     }
432*c8dee2aaSAndroid Build Coastguard Worker 
433*c8dee2aaSAndroid Build Coastguard Worker private:
434*c8dee2aaSAndroid Build Coastguard Worker     static const int kImgWidthHeight = 128;
435*c8dee2aaSAndroid Build Coastguard Worker     static const int kPad = 4;
436*c8dee2aaSAndroid Build Coastguard Worker 
437*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkImage> fETC1Image;
438*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkImage> fBC1Image;
439*c8dee2aaSAndroid Build Coastguard Worker 
440*c8dee2aaSAndroid Build Coastguard Worker     using INHERITED = GM;
441*c8dee2aaSAndroid Build Coastguard Worker };
442*c8dee2aaSAndroid Build Coastguard Worker 
443*c8dee2aaSAndroid Build Coastguard Worker //////////////////////////////////////////////////////////////////////////////
444*c8dee2aaSAndroid Build Coastguard Worker 
445*c8dee2aaSAndroid Build Coastguard Worker DEF_GM(return new ExoticFormatsGM;)
446*c8dee2aaSAndroid Build Coastguard Worker }  // namespace skiagm
447