OLD | NEW |
---|---|
(Empty) | |
1 | |
2 /* | |
3 * Copyright 2014 Google Inc. | |
4 * | |
5 * Use of this source code is governed by a BSD-style license that can be | |
6 * found in the LICENSE file. | |
7 */ | |
8 | |
9 #include "SkKTXFile.h" | |
10 #include "SkStream.h" | |
11 | |
robertphillips
2014/06/03 13:27:41
Hmmm - we may need to make this official (in the g
krajcevski
2014/06/03 14:58:35
Done.
| |
12 #include "../gpu/gl/GrGLDefines.h" | |
13 | |
14 #define KTX_FILE_IDENTIFIER_SIZE 12 | |
15 static const uint8_t KTX_FILE_IDENTIFIER[KTX_FILE_IDENTIFIER_SIZE] = { | |
16 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A | |
17 }; | |
18 | |
robertphillips
2014/06/03 13:27:41
SkEndianSwap32 ?
krajcevski
2014/06/03 14:46:27
Done.
| |
19 static void swap_uint32(uint32_t& v) { | |
20 uint8_t *ptr = reinterpret_cast<uint8_t *>(&v); | |
21 | |
22 uint8_t t; | |
23 t = ptr[0]; | |
24 ptr[0] = ptr[3]; | |
25 ptr[3] = t; | |
26 | |
27 t = ptr[1]; | |
28 ptr[1] = ptr[2]; | |
29 ptr[2] = t; | |
30 } | |
31 | |
robertphillips
2014/06/03 13:27:41
I'm not entirely sure this method is working as in
krajcevski
2014/06/03 14:46:27
None of the tools that I found produce .ktx files
| |
32 bool SkKTXFile::KeyValue::readKeyAndValue(const uint8_t* data) { | |
33 const char *key = reinterpret_cast<const char *>(data); | |
34 const char *value = key; | |
35 | |
36 size_t bytesRead = 0; | |
37 while (bytesRead < this->fDataSz) { | |
38 ++bytesRead; | |
39 if (*value == '\0') { | |
40 break; | |
41 } | |
42 ++value; | |
43 } | |
44 | |
45 // Error of some sort.. | |
robertphillips
2014/06/03 13:27:41
Why is "bytesRead > 0" an error?
krajcevski
2014/06/03 14:46:27
It's not.
Done.
| |
46 if (bytesRead > 0 || bytesRead == this->fDataSz) { | |
47 return false; | |
48 } | |
49 | |
50 fKey.set(key, bytesRead); | |
51 if (this->fDataSz > bytesRead) { | |
52 fValue.set(value, this->fDataSz - bytesRead); | |
53 } else { | |
54 return false; | |
55 } | |
56 | |
57 return true; | |
58 } | |
59 | |
robertphillips
2014/06/03 13:27:41
left align '&' ?
We only pass in const values by r
krajcevski
2014/06/03 14:46:27
Done.
| |
60 uint32_t SkKTXFile::readInt(const uint8_t*& buf, size_t &bytesLeft) const { | |
61 uint32_t result; | |
62 | |
63 if(bytesLeft < 4) { | |
64 SkASSERT(false); | |
65 return 0; | |
66 } | |
67 | |
68 memcpy(&result, buf, 4); | |
69 buf += 4; | |
70 | |
71 if (fSwapBytes) { | |
72 swap_uint32(result); | |
73 } | |
74 | |
75 bytesLeft -= 4; | |
76 | |
77 return result; | |
78 } | |
79 | |
80 bool SkKTXFile::isETC1() const { | |
robertphillips
2014/06/03 13:27:41
this->valid() ?
yoda-fy the '==' check ?
krajcevski
2014/06/03 14:46:27
Done.
| |
81 return valid() && fHeader.fGLInternalFormat == GR_GL_COMPRESSED_RGB8_ETC1; | |
82 } | |
83 | |
84 bool SkKTXFile::isRGBA8() const { | |
robertphillips
2014/06/03 13:27:41
ditto
krajcevski
2014/06/03 14:46:27
Done.
| |
85 return valid() && fHeader.fGLInternalFormat == GR_GL_RGBA8; | |
86 } | |
87 | |
88 bool SkKTXFile::isRGB8() const { | |
robertphillips
2014/06/03 13:27:41
ditto
krajcevski
2014/06/03 14:46:27
Done.
| |
89 return valid() && fHeader.fGLInternalFormat == GR_GL_RGB8; | |
90 } | |
91 | |
92 bool SkKTXFile::readKTXFile(const uint8_t* data, size_t dataLen) { | |
93 const uint8_t *buf = data; | |
94 size_t bytesLeft = dataLen; | |
95 | |
96 // Make sure original KTX header is there... this should have been checked | |
97 // already by a call to is_ktx() | |
98 SkASSERT(bytesLeft > KTX_FILE_IDENTIFIER_SIZE); | |
99 SkASSERT(0 == memcmp(KTX_FILE_IDENTIFIER, buf, KTX_FILE_IDENTIFIER_SIZE)); | |
100 buf += KTX_FILE_IDENTIFIER_SIZE; | |
101 bytesLeft -= KTX_FILE_IDENTIFIER_SIZE; | |
102 | |
103 // Read header, but first make sure that we have the proper space: we need | |
104 // two 32-bit ints: 1 for endianness, and another for the mandatory image | |
105 // size after the header. | |
106 if (bytesLeft < 8 + sizeof(Header)) { | |
107 return false; | |
108 } | |
109 | |
110 uint32_t magic = this->readInt(buf, bytesLeft); | |
robertphillips
2014/06/03 13:27:41
kKTXEndianessCode ?
krajcevski
2014/06/03 14:46:27
Done.
| |
111 fSwapBytes = 0x04030201 != magic; | |
112 | |
113 // Read header values | |
114 fHeader.fGLType = this->readInt(buf, bytesLeft); | |
115 fHeader.fGLTypeSize = this->readInt(buf, bytesLeft); | |
116 fHeader.fGLFormat = this->readInt(buf, bytesLeft); | |
117 fHeader.fGLInternalFormat = this->readInt(buf, bytesLeft); | |
118 fHeader.fGLBaseInternalFormat = this->readInt(buf, bytesLeft); | |
119 fHeader.fPixelWidth = this->readInt(buf, bytesLeft); | |
120 fHeader.fPixelHeight = this->readInt(buf, bytesLeft); | |
121 fHeader.fPixelDepth = this->readInt(buf, bytesLeft); | |
122 fHeader.fNumberOfArrayElements = this->readInt(buf, bytesLeft); | |
123 fHeader.fNumberOfFaces = this->readInt(buf, bytesLeft); | |
124 fHeader.fNumberOfMipmapLevels = this->readInt(buf, bytesLeft); | |
125 fHeader.fBytesOfKeyValueData = this->readInt(buf, bytesLeft); | |
126 | |
127 // Check for things that we understand... | |
128 { | |
129 // First, we only support compressed formats and single byte | |
130 // representations at the moment. In the future, we may support | |
131 // additional data types (such as GL_UNSIGNED_SHORT_5_6_5) | |
robertphillips
2014/06/03 13:27:41
Zero means what in this case?
krajcevski
2014/06/03 14:46:27
Done. Fixed the comment.
| |
132 if (fHeader.fGLType != 0 && fHeader.fGLType != GR_GL_UNSIGNED_BYTE) { | |
133 return false; | |
134 } | |
135 | |
robertphillips
2014/06/03 13:27:41
This comment doesn't seem quite right.
krajcevski
2014/06/03 14:46:27
According to the KTX spec, if the GLType is 0 (com
| |
136 // This means that for well-formatted KTX files, the glTypeSize | |
137 // field must be one... | |
138 if (fHeader.fGLTypeSize != 1) { | |
139 return false; | |
140 } | |
141 | |
142 // We don't support 3D textures. | |
143 if (fHeader.fPixelDepth > 1) { | |
144 return false; | |
145 } | |
146 | |
147 // We don't support texture arrays | |
148 if (fHeader.fNumberOfArrayElements > 1) { | |
149 return false; | |
150 } | |
151 | |
152 // We don't support cube maps | |
153 if (fHeader.fNumberOfFaces > 1) { | |
154 return false; | |
155 } | |
156 } | |
157 | |
158 // Next read the key value pairs | |
159 if (bytesLeft < fHeader.fBytesOfKeyValueData) { | |
robertphillips
2014/06/03 13:27:41
// file has been truncated somehow ?
krajcevski
2014/06/03 14:46:27
Done.
| |
160 return false; | |
161 } | |
162 | |
163 size_t keyValueBytesRead = 0; | |
164 while (keyValueBytesRead < fHeader.fBytesOfKeyValueData) { | |
165 uint32_t keyValueBytes = this->readInt(buf, bytesLeft); | |
166 keyValueBytesRead = 4; | |
167 | |
168 if (keyValueBytes > bytesLeft) { | |
169 return false; | |
170 } | |
171 | |
172 KeyValue kv(keyValueBytes); | |
173 if (!kv.readKeyAndValue(buf)) { | |
174 return false; | |
175 } | |
176 | |
177 fKeyValuePairs.append(1, &kv); | |
178 | |
179 uint32_t keyValueBytesPadded = (keyValueBytes + 3) & ~3; | |
180 buf += keyValueBytesPadded; | |
181 keyValueBytesRead += keyValueBytesPadded; | |
182 bytesLeft -= keyValueBytesPadded; | |
183 } | |
184 | |
185 // Read the pixel data... | |
186 int mipmaps = SkMax32(fHeader.fNumberOfMipmapLevels, 1); | |
187 SkASSERT(mipmaps == 1); | |
188 | |
189 int arrayElements = SkMax32(fHeader.fNumberOfArrayElements, 1); | |
190 SkASSERT(arrayElements == 1); | |
191 | |
192 int faces = SkMax32(fHeader.fNumberOfFaces, 1); | |
193 SkASSERT(faces == 1); | |
194 | |
195 int depth = SkMax32(fHeader.fPixelDepth, 1); | |
196 SkASSERT(depth == 1); | |
197 | |
198 int height = SkMax32(fHeader.fPixelHeight, 1); | |
199 | |
200 for (int mipmap = 0; mipmap < mipmaps; ++mipmap) { | |
201 // Make sure that we have at least 4 more bytes for the first image size | |
202 if (bytesLeft < 4) { | |
203 return false; | |
204 } | |
205 | |
206 uint32_t imgSize = this->readInt(buf, bytesLeft); | |
207 | |
208 if (bytesLeft < imgSize) { | |
robertphillips
2014/06/03 13:27:41
// truncated file ?
krajcevski
2014/06/03 14:46:27
Done.
| |
209 return false; | |
210 } | |
211 | |
212 // !FIXME! If support is ever added for cube maps then the padding | |
213 // needs to be taken into account here. | |
robertphillips
2014/06/03 13:27:41
add '{}'s and nesting ?
krajcevski
2014/06/03 14:46:27
Done.
| |
214 for (int arrayElement = 0; arrayElement < arrayElements; ++arrayElement) | |
215 for (int face = 0; face < faces; ++face) | |
216 for (int z = 0; z < depth; ++z) | |
217 for (int y = 0; y < height; ++y) { | |
robertphillips
2014/06/03 13:27:41
extra space between 'pd' and '(' ?
It looks like w
krajcevski
2014/06/03 14:46:27
Yes, there is a comment about it above the SkKTXFi
| |
218 PixelData pd (buf, imgSize); | |
219 fPixelData.append(1, &pd); | |
220 } | |
221 | |
222 uint32_t imgSizePadded = (imgSize + 3) & ~3; | |
223 buf += imgSizePadded; | |
224 bytesLeft -= imgSizePadded; | |
225 } | |
226 | |
227 return bytesLeft == 0; | |
228 } | |
229 | |
230 bool SkKTXFile::is_ktx(const uint8_t *data) { | |
231 return 0 == memcmp(KTX_FILE_IDENTIFIER, data, KTX_FILE_IDENTIFIER_SIZE); | |
232 } | |
233 | |
234 bool SkKTXFile::is_ktx(SkStreamRewindable* stream) { | |
235 // Read the KTX header and make sure it's valid. | |
236 unsigned char buf[KTX_FILE_IDENTIFIER_SIZE]; | |
237 bool largeEnough = | |
238 stream->read((void*)buf, KTX_FILE_IDENTIFIER_SIZE) == KTX_FILE_IDENTIFIE R_SIZE; | |
239 stream->rewind(); | |
240 if (!largeEnough) { | |
241 return false; | |
242 } | |
243 return is_ktx(buf); | |
244 } | |
OLD | NEW |