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