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