blob: 08282db45355eb07307e87d14a357f1174ea0ab7 [file] [log] [blame]
krajcevski99ffe242014-06-03 13:04:35 -07001/*
2 * Copyright 2014 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 "ktx.h"
krajcevskic250d2e2014-06-06 06:16:28 -07009#include "SkBitmap.h"
krajcevski99ffe242014-06-03 13:04:35 -070010#include "SkStream.h"
11#include "SkEndian.h"
12
13#include "gl/GrGLDefines.h"
krajcevski40a1e112014-08-05 14:13:36 -070014#include "GrConfig.h"
krajcevski99ffe242014-06-03 13:04:35 -070015
krajcevskic250d2e2014-06-06 06:16:28 -070016#include "etc1.h"
17
krajcevski40a1e112014-08-05 14:13:36 -070018static inline uint32_t compressed_fmt_to_gl_define(SkTextureCompressor::Format fmt) {
19 static const uint32_t kGLDefineMap[SkTextureCompressor::kFormatCnt] = {
egdanield588c012015-03-27 12:22:10 -070020 GR_GL_COMPRESSED_LUMINANCE_LATC1, // kLATC_Format
21 GR_GL_COMPRESSED_R11_EAC, // kR11_EAC_Format
22 GR_GL_COMPRESSED_ETC1_RGB8, // kETC1_Format
bsalomon54c6fe82015-12-16 11:51:22 -080023 GR_GL_COMPRESSED_RGBA_ASTC_4x4, // kASTC_4x4_Format
24 GR_GL_COMPRESSED_RGBA_ASTC_5x4, // kASTC_5x4_Format
25 GR_GL_COMPRESSED_RGBA_ASTC_5x5, // kASTC_5x5_Format
26 GR_GL_COMPRESSED_RGBA_ASTC_6x5, // kASTC_6x5_Format
27 GR_GL_COMPRESSED_RGBA_ASTC_6x6, // kASTC_6x6_Format
28 GR_GL_COMPRESSED_RGBA_ASTC_8x5, // kASTC_8x5_Format
29 GR_GL_COMPRESSED_RGBA_ASTC_8x6, // kASTC_8x6_Format
30 GR_GL_COMPRESSED_RGBA_ASTC_8x8, // kASTC_8x8_Format
31 GR_GL_COMPRESSED_RGBA_ASTC_10x5, // kASTC_10x5_Format
32 GR_GL_COMPRESSED_RGBA_ASTC_10x6, // kASTC_10x6_Format
33 GR_GL_COMPRESSED_RGBA_ASTC_10x8, // kASTC_10x8_Format
34 GR_GL_COMPRESSED_RGBA_ASTC_10x10, // kASTC_10x10_Format
35 GR_GL_COMPRESSED_RGBA_ASTC_12x10, // kASTC_12x10_Format
36 GR_GL_COMPRESSED_RGBA_ASTC_12x12, // kASTC_12x12_Format
krajcevski40a1e112014-08-05 14:13:36 -070037 };
38
39 GR_STATIC_ASSERT(0 == SkTextureCompressor::kLATC_Format);
40 GR_STATIC_ASSERT(1 == SkTextureCompressor::kR11_EAC_Format);
41 GR_STATIC_ASSERT(2 == SkTextureCompressor::kETC1_Format);
krajcevski95b1b3d2014-08-07 12:58:38 -070042 GR_STATIC_ASSERT(3 == SkTextureCompressor::kASTC_4x4_Format);
43 GR_STATIC_ASSERT(4 == SkTextureCompressor::kASTC_5x4_Format);
44 GR_STATIC_ASSERT(5 == SkTextureCompressor::kASTC_5x5_Format);
45 GR_STATIC_ASSERT(6 == SkTextureCompressor::kASTC_6x5_Format);
46 GR_STATIC_ASSERT(7 == SkTextureCompressor::kASTC_6x6_Format);
47 GR_STATIC_ASSERT(8 == SkTextureCompressor::kASTC_8x5_Format);
48 GR_STATIC_ASSERT(9 == SkTextureCompressor::kASTC_8x6_Format);
49 GR_STATIC_ASSERT(10 == SkTextureCompressor::kASTC_8x8_Format);
50 GR_STATIC_ASSERT(11 == SkTextureCompressor::kASTC_10x5_Format);
51 GR_STATIC_ASSERT(12 == SkTextureCompressor::kASTC_10x6_Format);
52 GR_STATIC_ASSERT(13 == SkTextureCompressor::kASTC_10x8_Format);
53 GR_STATIC_ASSERT(14 == SkTextureCompressor::kASTC_10x10_Format);
54 GR_STATIC_ASSERT(15 == SkTextureCompressor::kASTC_12x10_Format);
55 GR_STATIC_ASSERT(16 == SkTextureCompressor::kASTC_12x12_Format);
krajcevski40a1e112014-08-05 14:13:36 -070056 GR_STATIC_ASSERT(SK_ARRAY_COUNT(kGLDefineMap) == SkTextureCompressor::kFormatCnt);
57
58 return kGLDefineMap[fmt];
59}
60
krajcevski99ffe242014-06-03 13:04:35 -070061#define KTX_FILE_IDENTIFIER_SIZE 12
62static const uint8_t KTX_FILE_IDENTIFIER[KTX_FILE_IDENTIFIER_SIZE] = {
63 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
64};
65
66static const uint32_t kKTX_ENDIANNESS_CODE = 0x04030201;
67
68bool SkKTXFile::KeyValue::readKeyAndValue(const uint8_t* data) {
69 const char *key = reinterpret_cast<const char *>(data);
70 const char *value = key;
71
72 size_t bytesRead = 0;
73 while (*value != '\0' && bytesRead < this->fDataSz) {
74 ++bytesRead;
75 ++value;
76 }
77
78 // Error of some sort..
79 if (bytesRead >= this->fDataSz) {
80 return false;
81 }
82
83 // Read the zero terminator
84 ++bytesRead;
85 ++value;
86
87 size_t bytesLeft = this->fDataSz - bytesRead;
krajcevskic250d2e2014-06-06 06:16:28 -070088
89 // We ignore the null terminator when setting the string value.
90 this->fKey.set(key, bytesRead - 1);
krajcevski99ffe242014-06-03 13:04:35 -070091 if (bytesLeft > 0) {
krajcevskic250d2e2014-06-06 06:16:28 -070092 this->fValue.set(value, bytesLeft - 1);
krajcevski99ffe242014-06-03 13:04:35 -070093 } else {
94 return false;
95 }
96
97 return true;
98}
99
krajcevskic250d2e2014-06-06 06:16:28 -0700100bool SkKTXFile::KeyValue::writeKeyAndValueForKTX(SkWStream* strm) {
101 size_t bytesWritten = 0;
102 if (!strm->write(&(this->fDataSz), 4)) {
103 return false;
104 }
105
106 bytesWritten += 4;
107
108 // Here we know that C-strings must end with a null terminating
109 // character, so when we get a c_str(), it will have as many
110 // bytes of data as size() returns plus a zero, so we just
111 // write size() + 1 bytes into the stream.
112
113 size_t keySize = this->fKey.size() + 1;
114 if (!strm->write(this->fKey.c_str(), keySize)) {
115 return false;
116 }
117
118 bytesWritten += keySize;
119
120 size_t valueSize = this->fValue.size() + 1;
121 if (!strm->write(this->fValue.c_str(), valueSize)) {
122 return false;
123 }
124
125 bytesWritten += valueSize;
126
127 size_t bytesWrittenPadFour = (bytesWritten + 3) & ~3;
128 uint8_t nullBuf[4] = { 0, 0, 0, 0 };
129
130 size_t padding = bytesWrittenPadFour - bytesWritten;
131 SkASSERT(padding < 4);
132
133 return strm->write(nullBuf, padding);
134}
135
krajcevski99ffe242014-06-03 13:04:35 -0700136uint32_t SkKTXFile::readInt(const uint8_t** buf, size_t* bytesLeft) const {
bsalomon49f085d2014-09-05 13:34:00 -0700137 SkASSERT(buf && bytesLeft);
krajcevski99ffe242014-06-03 13:04:35 -0700138
139 uint32_t result;
140
141 if (*bytesLeft < 4) {
142 SkASSERT(false);
143 return 0;
144 }
145
146 memcpy(&result, *buf, 4);
147 *buf += 4;
148
149 if (fSwapBytes) {
150 SkEndianSwap32(result);
151 }
152
153 *bytesLeft -= 4;
154
155 return result;
156}
157
krajcevskic250d2e2014-06-06 06:16:28 -0700158SkString SkKTXFile::getValueForKey(const SkString& key) const {
159 const KeyValue *begin = this->fKeyValuePairs.begin();
160 const KeyValue *end = this->fKeyValuePairs.end();
161 for (const KeyValue *kv = begin; kv != end; ++kv) {
162 if (kv->key() == key) {
163 return kv->value();
164 }
165 }
166 return SkString();
167}
168
krajcevski40a1e112014-08-05 14:13:36 -0700169bool SkKTXFile::isCompressedFormat(SkTextureCompressor::Format fmt) const {
170 if (!this->valid()) {
171 return false;
172 }
173
174 // This has many aliases
175 bool isFmt = false;
176 if (fmt == SkTextureCompressor::kLATC_Format) {
177 isFmt = GR_GL_COMPRESSED_RED_RGTC1 == fHeader.fGLInternalFormat ||
178 GR_GL_COMPRESSED_3DC_X == fHeader.fGLInternalFormat;
179 }
180
181 return isFmt || compressed_fmt_to_gl_define(fmt) == fHeader.fGLInternalFormat;
krajcevski99ffe242014-06-03 13:04:35 -0700182}
183
184bool SkKTXFile::isRGBA8() const {
185 return this->valid() && GR_GL_RGBA8 == fHeader.fGLInternalFormat;
186}
187
188bool SkKTXFile::isRGB8() const {
189 return this->valid() && GR_GL_RGB8 == fHeader.fGLInternalFormat;
190}
191
192bool SkKTXFile::readKTXFile(const uint8_t* data, size_t dataLen) {
193 const uint8_t *buf = data;
194 size_t bytesLeft = dataLen;
195
196 // Make sure original KTX header is there... this should have been checked
197 // already by a call to is_ktx()
198 SkASSERT(bytesLeft > KTX_FILE_IDENTIFIER_SIZE);
199 SkASSERT(0 == memcmp(KTX_FILE_IDENTIFIER, buf, KTX_FILE_IDENTIFIER_SIZE));
200 buf += KTX_FILE_IDENTIFIER_SIZE;
201 bytesLeft -= KTX_FILE_IDENTIFIER_SIZE;
202
203 // Read header, but first make sure that we have the proper space: we need
204 // two 32-bit ints: 1 for endianness, and another for the mandatory image
205 // size after the header.
206 if (bytesLeft < 8 + sizeof(Header)) {
207 return false;
208 }
209
210 uint32_t endianness = this->readInt(&buf, &bytesLeft);
211 fSwapBytes = kKTX_ENDIANNESS_CODE != endianness;
212
213 // Read header values
214 fHeader.fGLType = this->readInt(&buf, &bytesLeft);
215 fHeader.fGLTypeSize = this->readInt(&buf, &bytesLeft);
216 fHeader.fGLFormat = this->readInt(&buf, &bytesLeft);
217 fHeader.fGLInternalFormat = this->readInt(&buf, &bytesLeft);
218 fHeader.fGLBaseInternalFormat = this->readInt(&buf, &bytesLeft);
219 fHeader.fPixelWidth = this->readInt(&buf, &bytesLeft);
220 fHeader.fPixelHeight = this->readInt(&buf, &bytesLeft);
221 fHeader.fPixelDepth = this->readInt(&buf, &bytesLeft);
222 fHeader.fNumberOfArrayElements = this->readInt(&buf, &bytesLeft);
223 fHeader.fNumberOfFaces = this->readInt(&buf, &bytesLeft);
224 fHeader.fNumberOfMipmapLevels = this->readInt(&buf, &bytesLeft);
225 fHeader.fBytesOfKeyValueData = this->readInt(&buf, &bytesLeft);
226
227 // Check for things that we understand...
228 {
229 // First, we only support compressed formats and single byte
230 // representations at the moment. If the internal format is
231 // compressed, the the GLType field in the header must be zero.
232 // In the future, we may support additional data types (such
233 // as GL_UNSIGNED_SHORT_5_6_5)
234 if (fHeader.fGLType != 0 && fHeader.fGLType != GR_GL_UNSIGNED_BYTE) {
235 return false;
236 }
237
238 // This means that for well-formatted KTX files, the glTypeSize
239 // field must be one...
240 if (fHeader.fGLTypeSize != 1) {
241 return false;
242 }
243
244 // We don't support 3D textures.
245 if (fHeader.fPixelDepth > 1) {
246 return false;
247 }
248
249 // We don't support texture arrays
250 if (fHeader.fNumberOfArrayElements > 1) {
251 return false;
252 }
253
254 // We don't support cube maps
255 if (fHeader.fNumberOfFaces > 1) {
256 return false;
257 }
djsollen6b0d6b42014-08-27 08:00:09 -0700258
259 // We don't support width and/or height <= 0
260 if (fHeader.fPixelWidth <= 0 || fHeader.fPixelHeight <= 0) {
261 return false;
262 }
krajcevski99ffe242014-06-03 13:04:35 -0700263 }
264
265 // Make sure that we have enough bytes left for the key/value
266 // data according to what was said in the header.
267 if (bytesLeft < fHeader.fBytesOfKeyValueData) {
268 return false;
269 }
270
271 // Next read the key value pairs
272 size_t keyValueBytesRead = 0;
273 while (keyValueBytesRead < fHeader.fBytesOfKeyValueData) {
274 uint32_t keyValueBytes = this->readInt(&buf, &bytesLeft);
275 keyValueBytesRead += 4;
276
277 if (keyValueBytes > bytesLeft) {
278 return false;
279 }
280
281 KeyValue kv(keyValueBytes);
282 if (!kv.readKeyAndValue(buf)) {
283 return false;
284 }
285
286 fKeyValuePairs.push_back(kv);
287
288 uint32_t keyValueBytesPadded = (keyValueBytes + 3) & ~3;
289 buf += keyValueBytesPadded;
290 keyValueBytesRead += keyValueBytesPadded;
291 bytesLeft -= keyValueBytesPadded;
292 }
293
294 // Read the pixel data...
295 int mipmaps = SkMax32(fHeader.fNumberOfMipmapLevels, 1);
296 SkASSERT(mipmaps == 1);
297
298 int arrayElements = SkMax32(fHeader.fNumberOfArrayElements, 1);
299 SkASSERT(arrayElements == 1);
300
301 int faces = SkMax32(fHeader.fNumberOfFaces, 1);
302 SkASSERT(faces == 1);
303
304 int depth = SkMax32(fHeader.fPixelDepth, 1);
305 SkASSERT(depth == 1);
306
307 for (int mipmap = 0; mipmap < mipmaps; ++mipmap) {
308 // Make sure that we have at least 4 more bytes for the first image size
309 if (bytesLeft < 4) {
310 return false;
311 }
312
313 uint32_t imgSize = this->readInt(&buf, &bytesLeft);
314
315 // Truncated file.
316 if (bytesLeft < imgSize) {
317 return false;
318 }
319
320 // !FIXME! If support is ever added for cube maps then the padding
321 // needs to be taken into account here.
322 for (int arrayElement = 0; arrayElement < arrayElements; ++arrayElement) {
323 for (int face = 0; face < faces; ++face) {
324 for (int z = 0; z < depth; ++z) {
325 PixelData pd(buf, imgSize);
326 fPixelData.append(1, &pd);
327 }
328 }
329 }
halcanary9d524f22016-03-29 09:03:52 -0700330
krajcevski99ffe242014-06-03 13:04:35 -0700331 uint32_t imgSizePadded = (imgSize + 3) & ~3;
332 buf += imgSizePadded;
333 bytesLeft -= imgSizePadded;
334 }
335
336 return bytesLeft == 0;
337}
338
scroggob8e09602016-04-12 07:41:22 -0700339bool SkKTXFile::is_ktx(const uint8_t data[], size_t size) {
340 return size >= KTX_FILE_IDENTIFIER_SIZE &&
341 0 == memcmp(KTX_FILE_IDENTIFIER, data, KTX_FILE_IDENTIFIER_SIZE);
krajcevski99ffe242014-06-03 13:04:35 -0700342}
343
344bool SkKTXFile::is_ktx(SkStreamRewindable* stream) {
345 // Read the KTX header and make sure it's valid.
346 unsigned char buf[KTX_FILE_IDENTIFIER_SIZE];
347 bool largeEnough =
348 stream->read((void*)buf, KTX_FILE_IDENTIFIER_SIZE) == KTX_FILE_IDENTIFIER_SIZE;
349 stream->rewind();
350 if (!largeEnough) {
351 return false;
352 }
scroggob8e09602016-04-12 07:41:22 -0700353 return is_ktx(buf, KTX_FILE_IDENTIFIER_SIZE);
krajcevski99ffe242014-06-03 13:04:35 -0700354}
krajcevskic250d2e2014-06-06 06:16:28 -0700355
356SkKTXFile::KeyValue SkKTXFile::CreateKeyValue(const char *cstrKey, const char *cstrValue) {
357 SkString key(cstrKey);
358 SkString value(cstrValue);
359
360 // Size of buffer is length of string plus the null terminators...
361 size_t size = key.size() + 1 + value.size() + 1;
362
363 SkAutoSMalloc<256> buf(size);
364 uint8_t* kvBuf = reinterpret_cast<uint8_t*>(buf.get());
365 memcpy(kvBuf, key.c_str(), key.size() + 1);
366 memcpy(kvBuf + key.size() + 1, value.c_str(), value.size() + 1);
367
368 KeyValue kv(size);
369 SkAssertResult(kv.readKeyAndValue(kvBuf));
370 return kv;
371}
372
373bool SkKTXFile::WriteETC1ToKTX(SkWStream* stream, const uint8_t *etc1Data,
374 uint32_t width, uint32_t height) {
375 // First thing's first, write out the magic identifier and endianness...
376 if (!stream->write(KTX_FILE_IDENTIFIER, KTX_FILE_IDENTIFIER_SIZE)) {
377 return false;
378 }
379
380 if (!stream->write(&kKTX_ENDIANNESS_CODE, 4)) {
381 return false;
382 }
halcanary9d524f22016-03-29 09:03:52 -0700383
krajcevskic250d2e2014-06-06 06:16:28 -0700384 Header hdr;
385 hdr.fGLType = 0;
386 hdr.fGLTypeSize = 1;
387 hdr.fGLFormat = 0;
egdanield588c012015-03-27 12:22:10 -0700388 hdr.fGLInternalFormat = GR_GL_COMPRESSED_ETC1_RGB8;
krajcevskic250d2e2014-06-06 06:16:28 -0700389 hdr.fGLBaseInternalFormat = GR_GL_RGB;
390 hdr.fPixelWidth = width;
391 hdr.fPixelHeight = height;
392 hdr.fNumberOfArrayElements = 0;
393 hdr.fNumberOfFaces = 1;
394 hdr.fNumberOfMipmapLevels = 1;
395
396 // !FIXME! The spec suggests that we put KTXOrientation as a
397 // key value pair in the header, but that means that we'd have to
398 // pipe through the bitmap's orientation to properly do that.
399 hdr.fBytesOfKeyValueData = 0;
400
401 // Write the header
402 if (!stream->write(&hdr, sizeof(hdr))) {
403 return false;
404 }
405
406 // Write the size of the image data
407 etc1_uint32 dataSize = etc1_get_encoded_data_size(width, height);
408 if (!stream->write(&dataSize, 4)) {
409 return false;
410 }
411
412 // Write the actual image data
413 if (!stream->write(etc1Data, dataSize)) {
414 return false;
415 }
416
417 return true;
418}
419
420bool SkKTXFile::WriteBitmapToKTX(SkWStream* stream, const SkBitmap& bitmap) {
reedc3b32662014-06-17 08:38:31 -0700421 const SkColorType ct = bitmap.colorType();
krajcevskic250d2e2014-06-06 06:16:28 -0700422 SkAutoLockPixels alp(bitmap);
423
424 const int width = bitmap.width();
425 const int height = bitmap.width();
426 const uint8_t* src = reinterpret_cast<uint8_t*>(bitmap.getPixels());
427 if (NULL == bitmap.getPixels()) {
428 return false;
429 }
430
431 // First thing's first, write out the magic identifier and endianness...
432 if (!stream->write(KTX_FILE_IDENTIFIER, KTX_FILE_IDENTIFIER_SIZE) ||
433 !stream->write(&kKTX_ENDIANNESS_CODE, 4)) {
434 return false;
435 }
436
437 // Collect our key/value pairs...
438 SkTArray<KeyValue> kvPairs;
439
440 // Next, write the header based on the bitmap's config.
441 Header hdr;
reedc3b32662014-06-17 08:38:31 -0700442 switch (ct) {
443 case kIndex_8_SkColorType:
krajcevskic250d2e2014-06-06 06:16:28 -0700444 // There is a compressed format for this, but we don't support it yet.
445 SkDebugf("Writing indexed bitmap to KTX unsupported.\n");
446 // VVV fall through VVV
447 default:
reedc3b32662014-06-17 08:38:31 -0700448 case kUnknown_SkColorType:
krajcevskic250d2e2014-06-06 06:16:28 -0700449 // Bitmap hasn't been configured.
450 return false;
451
reedc3b32662014-06-17 08:38:31 -0700452 case kAlpha_8_SkColorType:
krajcevskic250d2e2014-06-06 06:16:28 -0700453 hdr.fGLType = GR_GL_UNSIGNED_BYTE;
454 hdr.fGLTypeSize = 1;
455 hdr.fGLFormat = GR_GL_RED;
456 hdr.fGLInternalFormat = GR_GL_R8;
457 hdr.fGLBaseInternalFormat = GR_GL_RED;
458 break;
459
reedc3b32662014-06-17 08:38:31 -0700460 case kRGB_565_SkColorType:
krajcevskic250d2e2014-06-06 06:16:28 -0700461 hdr.fGLType = GR_GL_UNSIGNED_SHORT_5_6_5;
462 hdr.fGLTypeSize = 2;
463 hdr.fGLFormat = GR_GL_RGB;
464 hdr.fGLInternalFormat = GR_GL_RGB;
465 hdr.fGLBaseInternalFormat = GR_GL_RGB;
466 break;
467
reedc3b32662014-06-17 08:38:31 -0700468 case kARGB_4444_SkColorType:
krajcevskic250d2e2014-06-06 06:16:28 -0700469 hdr.fGLType = GR_GL_UNSIGNED_SHORT_4_4_4_4;
470 hdr.fGLTypeSize = 2;
471 hdr.fGLFormat = GR_GL_RGBA;
472 hdr.fGLInternalFormat = GR_GL_RGBA4;
473 hdr.fGLBaseInternalFormat = GR_GL_RGBA;
474 kvPairs.push_back(CreateKeyValue("KTXPremultipliedAlpha", "True"));
475 break;
476
reedc3b32662014-06-17 08:38:31 -0700477 case kN32_SkColorType:
krajcevskic250d2e2014-06-06 06:16:28 -0700478 hdr.fGLType = GR_GL_UNSIGNED_BYTE;
479 hdr.fGLTypeSize = 1;
480 hdr.fGLFormat = GR_GL_RGBA;
481 hdr.fGLInternalFormat = GR_GL_RGBA8;
482 hdr.fGLBaseInternalFormat = GR_GL_RGBA;
483 kvPairs.push_back(CreateKeyValue("KTXPremultipliedAlpha", "True"));
484 break;
485 }
486
487 // Everything else in the header is shared.
488 hdr.fPixelWidth = width;
489 hdr.fPixelHeight = height;
490 hdr.fNumberOfArrayElements = 0;
491 hdr.fNumberOfFaces = 1;
492 hdr.fNumberOfMipmapLevels = 1;
493
494 // Calculate the key value data size
495 hdr.fBytesOfKeyValueData = 0;
496 for (KeyValue *kv = kvPairs.begin(); kv != kvPairs.end(); ++kv) {
497 // Key value size is the size of the key value data,
498 // four bytes for saying how big the key value size is
499 // and then additional bytes for padding to four byte boundary
500 size_t kvsize = kv->size();
501 kvsize += 4;
502 kvsize = (kvsize + 3) & ~3;
bsalomonfbaace02014-12-12 16:41:46 -0800503 hdr.fBytesOfKeyValueData = SkToU32(hdr.fBytesOfKeyValueData + kvsize);
krajcevskic250d2e2014-06-06 06:16:28 -0700504 }
505
506 // Write the header
507 if (!stream->write(&hdr, sizeof(hdr))) {
508 return false;
509 }
510
511 // Write out each key value pair
512 for (KeyValue *kv = kvPairs.begin(); kv != kvPairs.end(); ++kv) {
513 if (!kv->writeKeyAndValueForKTX(stream)) {
514 return false;
515 }
516 }
517
518 // Calculate the size of the data
519 int bpp = bitmap.bytesPerPixel();
520 uint32_t dataSz = bpp * width * height;
521
522 if (0 >= bpp) {
523 return false;
524 }
525
526 // Write it into the buffer
527 if (!stream->write(&dataSz, 4)) {
528 return false;
529 }
530
531 // Write the pixel data...
532 const uint8_t* rowPtr = src;
reedc3b32662014-06-17 08:38:31 -0700533 if (kN32_SkColorType == ct) {
krajcevskic250d2e2014-06-06 06:16:28 -0700534 for (int j = 0; j < height; ++j) {
535 const uint32_t* pixelsPtr = reinterpret_cast<const uint32_t*>(rowPtr);
536 for (int i = 0; i < width; ++i) {
537 uint32_t pixel = pixelsPtr[i];
538 uint8_t dstPixel[4];
539 dstPixel[0] = pixel >> SK_R32_SHIFT;
540 dstPixel[1] = pixel >> SK_G32_SHIFT;
541 dstPixel[2] = pixel >> SK_B32_SHIFT;
542 dstPixel[3] = pixel >> SK_A32_SHIFT;
543 if (!stream->write(dstPixel, 4)) {
544 return false;
545 }
546 }
547 rowPtr += bitmap.rowBytes();
548 }
549 } else {
550 for (int i = 0; i < height; ++i) {
551 if (!stream->write(rowPtr, bpp*width)) {
552 return false;
553 }
554 rowPtr += bitmap.rowBytes();
555 }
556 }
halcanary9d524f22016-03-29 09:03:52 -0700557
krajcevskic250d2e2014-06-06 06:16:28 -0700558 return true;
559}