Flutter Engine
The Flutter Engine
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//-------------------------------------------------------------------------------------------------
30struct ImageInfo {
34};
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) {
124 } else {
125 int numRequiredMipLevels = SkMipmap::ComputeLevelCount(pixelWidth, pixelHeight)+1;
126 if (numberOfMipmapLevels != numRequiredMipLevels) {
127 return nullptr;
128 }
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
184};
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
198typedef struct {
206 DWORD dwReserved1[11];
213} DDS_HEADER;
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) {
265 } else {
266 int numRequiredLevels = SkMipmap::ComputeLevelCount(header.dwWidth, header.dwHeight)+1;
267 if (header.dwMipMapCount != (unsigned) numRequiredLevels) {
268 return nullptr;
269 }
271 numberOfMipmapLevels = numRequiredLevels;
272 }
273 } else {
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:
334 }
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
Definition: GrGLDefines.h:261
#define GR_GL_COMPRESSED_ETC1_RGB8
Definition: GrGLDefines.h:278
#define GR_GL_COMPRESSED_RGB8_ETC2
Definition: GrGLDefines.h:285
#define GR_GL_COMPRESSED_RGB_S3TC_DXT1_EXT
Definition: GrGLDefines.h:260
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
static SkImage_Base * as_IB(SkImage *image)
Definition: SkImage_Base.h:201
static const char kMagic[]
Definition: SkPicture.cpp:58
SkTextureCompressionType
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
const GrCaps * caps() const
Definition: GrCaps.h:57
bool isFormatCompressed(const GrBackendFormat &format) const
Definition: GrCaps.cpp:457
GrImageContextPriv priv()
const GrBackendFormat & backendFormat() const
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
virtual GrRecordingContext * recordingContext() const
Definition: SkCanvas.cpp:1637
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
Definition: SkImage_Base.h:112
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:421
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
Definition: gm.h:110
GM(SkColor backgroundColor=SK_ColorWHITE)
Definition: gm.cpp:81
void setBGColor(SkColor)
Definition: gm.cpp:159
const Paint & paint
Definition: color_source.cc:38
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
GAsyncResult * result
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)
sk_sp< const SkImage > image
Definition: SkRecords.h:269
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
GrTextureProxy * GetTextureImageProxy(SkImage *image, GrRecordingContext *rContext)
Definition: ProxyUtils.cpp:32
Mipmapped
Definition: GpuTypes.h:53
DEF_GM(return F(C(clipbox), 0.0f, 0.0f, {})) DEF_GM(return F(C(clipbox)
DrawResult
Definition: gm.h:104
dest
Definition: zip.py:79
static const char header[]
Definition: skpbench.cpp:88
SeparatedVector2 offset
DDS_PIXELFORMAT ddspf
DWORD dwMipMapCount
DWORD dwPitchOrLinearSize
SkTextureCompressionType fCompressionType
SkISize fDim
skgpu::Mipmapped fMipmapped
Definition: SkSize.h:16
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
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
unsigned long DWORD
Definition: windows_types.h:22