1 /*
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 "SkColorPriv.h"
9 #include "SkImageDecoder.h"
10 #include "SkPixelRef.h"
11 #include "SkScaledBitmapSampler.h"
12 #include "SkStream.h"
13 #include "SkStreamPriv.h"
14 #include "SkTypes.h"
15
16 #include "ktx.h"
17 #include "etc1.h"
18
19 /////////////////////////////////////////////////////////////////////////////////////////
20
21
22 /////////////////////////////////////////////////////////////////////////////////////////
23
24 // KTX Image decoder
25 // ---
26 // KTX is a general texture data storage file format ratified by the Khronos Group. As an
27 // overview, a KTX file contains all of the appropriate values needed to fully specify a
28 // texture in an OpenGL application, including the use of compressed data.
29 //
30 // This decoder is meant to be used with an SkDiscardablePixelRef so that GPU backends
31 // can sniff the data before creating a texture. If they encounter a compressed format
32 // that they understand, they can then upload the data directly to the GPU. Otherwise,
33 // they will decode the data into a format that Skia supports.
34
35 class SkKTXImageDecoder : public SkImageDecoder {
36 public:
SkKTXImageDecoder()37 SkKTXImageDecoder() { }
38
getFormat() const39 Format getFormat() const override {
40 return kKTX_Format;
41 }
42
43 protected:
44 Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
45
46 private:
47 typedef SkImageDecoder INHERITED;
48 };
49
onDecode(SkStream * stream,SkBitmap * bm,Mode mode)50 SkImageDecoder::Result SkKTXImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
51 // TODO: Implement SkStream::copyToData() that's cheap for memory and file streams
52 SkAutoDataUnref data(SkCopyStreamToData(stream));
53 if (NULL == data) {
54 return kFailure;
55 }
56
57 SkKTXFile ktxFile(data);
58 if (!ktxFile.valid()) {
59 return kFailure;
60 }
61
62 const unsigned short width = ktxFile.width();
63 const unsigned short height = ktxFile.height();
64
65 // Set a flag if our source is premultiplied alpha
66 const SkString premulKey("KTXPremultipliedAlpha");
67 const bool bSrcIsPremul = ktxFile.getValueForKey(premulKey) == SkString("True");
68
69 // Setup the sampler...
70 SkScaledBitmapSampler sampler(width, height, this->getSampleSize());
71
72 // Determine the alpha of the bitmap...
73 SkAlphaType alphaType = kOpaque_SkAlphaType;
74 if (ktxFile.isRGBA8()) {
75 if (this->getRequireUnpremultipliedColors()) {
76 alphaType = kUnpremul_SkAlphaType;
77 // If the client wants unpremul colors and we only have
78 // premul, then we cannot honor their wish.
79 if (bSrcIsPremul) {
80 return kFailure;
81 }
82 } else {
83 alphaType = kPremul_SkAlphaType;
84 }
85 }
86
87 // Search through the compressed formats to see if the KTX file is holding
88 // compressed data
89 bool ktxIsCompressed = false;
90 SkTextureCompressor::Format ktxCompressedFormat;
91 for (int i = 0; i < SkTextureCompressor::kFormatCnt; ++i) {
92 SkTextureCompressor::Format fmt = static_cast<SkTextureCompressor::Format>(i);
93 if (ktxFile.isCompressedFormat(fmt)) {
94 ktxIsCompressed = true;
95 ktxCompressedFormat = fmt;
96 break;
97 }
98 }
99
100 // If the compressed format is a grayscale image, then setup the bitmap properly...
101 bool isCompressedAlpha = ktxIsCompressed &&
102 ((SkTextureCompressor::kLATC_Format == ktxCompressedFormat) ||
103 (SkTextureCompressor::kR11_EAC_Format == ktxCompressedFormat));
104
105 // Set the image dimensions and underlying pixel type.
106 if (isCompressedAlpha) {
107 const int w = sampler.scaledWidth();
108 const int h = sampler.scaledHeight();
109 bm->setInfo(SkImageInfo::MakeA8(w, h));
110 } else {
111 const int w = sampler.scaledWidth();
112 const int h = sampler.scaledHeight();
113 bm->setInfo(SkImageInfo::MakeN32(w, h, alphaType));
114 }
115
116 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
117 return kSuccess;
118 }
119
120 // If we've made it this far, then we know how to grok the data.
121 if (!this->allocPixelRef(bm, NULL)) {
122 return kFailure;
123 }
124
125 // Lock the pixels, since we're about to write to them...
126 SkAutoLockPixels alp(*bm);
127
128 if (isCompressedAlpha) {
129 if (!sampler.begin(bm, SkScaledBitmapSampler::kGray, *this)) {
130 return kFailure;
131 }
132
133 // Alpha data is only a single byte per pixel.
134 int nPixels = width * height;
135 SkAutoMalloc outRGBData(nPixels);
136 uint8_t *outRGBDataPtr = reinterpret_cast<uint8_t *>(outRGBData.get());
137
138 // Decode the compressed format
139 const uint8_t *buf = reinterpret_cast<const uint8_t *>(ktxFile.pixelData());
140 if (!SkTextureCompressor::DecompressBufferFromFormat(
141 outRGBDataPtr, width, buf, width, height, ktxCompressedFormat)) {
142 return kFailure;
143 }
144
145 // Set each of the pixels...
146 const int srcRowBytes = width;
147 const int dstHeight = sampler.scaledHeight();
148 const uint8_t *srcRow = reinterpret_cast<uint8_t *>(outRGBDataPtr);
149 srcRow += sampler.srcY0() * srcRowBytes;
150 for (int y = 0; y < dstHeight; ++y) {
151 sampler.next(srcRow);
152 srcRow += sampler.srcDY() * srcRowBytes;
153 }
154
155 return kSuccess;
156
157 } else if (ktxFile.isCompressedFormat(SkTextureCompressor::kETC1_Format)) {
158 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {
159 return kFailure;
160 }
161
162 // ETC1 Data is encoded as RGB pixels, so we should extract it as such
163 int nPixels = width * height;
164 SkAutoMalloc outRGBData(nPixels * 3);
165 uint8_t *outRGBDataPtr = reinterpret_cast<uint8_t *>(outRGBData.get());
166
167 // Decode ETC1
168 const uint8_t *buf = reinterpret_cast<const uint8_t *>(ktxFile.pixelData());
169 if (!SkTextureCompressor::DecompressBufferFromFormat(
170 outRGBDataPtr, width*3, buf, width, height, SkTextureCompressor::kETC1_Format)) {
171 return kFailure;
172 }
173
174 // Set each of the pixels...
175 const int srcRowBytes = width * 3;
176 const int dstHeight = sampler.scaledHeight();
177 const uint8_t *srcRow = reinterpret_cast<uint8_t *>(outRGBDataPtr);
178 srcRow += sampler.srcY0() * srcRowBytes;
179 for (int y = 0; y < dstHeight; ++y) {
180 sampler.next(srcRow);
181 srcRow += sampler.srcDY() * srcRowBytes;
182 }
183
184 return kSuccess;
185
186 } else if (ktxFile.isRGB8()) {
187
188 // Uncompressed RGB data (without alpha)
189 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {
190 return kFailure;
191 }
192
193 // Just need to read RGB pixels
194 const int srcRowBytes = width * 3;
195 const int dstHeight = sampler.scaledHeight();
196 const uint8_t *srcRow = reinterpret_cast<const uint8_t *>(ktxFile.pixelData());
197 srcRow += sampler.srcY0() * srcRowBytes;
198 for (int y = 0; y < dstHeight; ++y) {
199 sampler.next(srcRow);
200 srcRow += sampler.srcDY() * srcRowBytes;
201 }
202
203 return kSuccess;
204
205 } else if (ktxFile.isRGBA8()) {
206
207 // Uncompressed RGBA data
208
209 // If we know that the image contains premultiplied alpha, then
210 // we need to turn off the premultiplier
211 SkScaledBitmapSampler::Options opts (*this);
212 if (bSrcIsPremul) {
213 SkASSERT(bm->alphaType() == kPremul_SkAlphaType);
214 SkASSERT(!this->getRequireUnpremultipliedColors());
215
216 opts.fPremultiplyAlpha = false;
217 }
218
219 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGBA, opts)) {
220 return kFailure;
221 }
222
223 // Just need to read RGBA pixels
224 const int srcRowBytes = width * 4;
225 const int dstHeight = sampler.scaledHeight();
226 const uint8_t *srcRow = reinterpret_cast<const uint8_t *>(ktxFile.pixelData());
227 srcRow += sampler.srcY0() * srcRowBytes;
228 for (int y = 0; y < dstHeight; ++y) {
229 sampler.next(srcRow);
230 srcRow += sampler.srcDY() * srcRowBytes;
231 }
232
233 return kSuccess;
234 }
235
236 return kFailure;
237 }
238
239 ///////////////////////////////////////////////////////////////////////////////
240
241 // KTX Image Encoder
242 //
243 // This encoder takes a best guess at how to encode the bitmap passed to it. If
244 // there is an installed discardable pixel ref with existing PKM data, then we
245 // will repurpose the existing ETC1 data into a KTX file. If the data contains
246 // KTX data, then we simply return a copy of the same data. For all other files,
247 // the underlying KTX library tries to do its best to encode the appropriate
248 // data specified by the bitmap based on the config. (i.e. kAlpha8_Config will
249 // be represented as a full resolution 8-bit image dump with the appropriate
250 // OpenGL defines in the header).
251
252 class SkKTXImageEncoder : public SkImageEncoder {
253 protected:
254 bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override;
255
256 private:
257 virtual bool encodePKM(SkWStream* stream, const SkData *data);
258 typedef SkImageEncoder INHERITED;
259 };
260
onEncode(SkWStream * stream,const SkBitmap & bitmap,int)261 bool SkKTXImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, int) {
262 if (!bitmap.pixelRef()) {
263 return false;
264 }
265 SkAutoDataUnref data(bitmap.pixelRef()->refEncodedData());
266
267 // Is this even encoded data?
268 if (data) {
269 const uint8_t *bytes = data->bytes();
270 if (etc1_pkm_is_valid(bytes)) {
271 return this->encodePKM(stream, data);
272 }
273
274 // Is it a KTX file??
275 if (SkKTXFile::is_ktx(bytes)) {
276 return stream->write(bytes, data->size());
277 }
278
279 // If it's neither a KTX nor a PKM, then we need to
280 // get at the actual pixels, so fall through and decompress...
281 }
282
283 return SkKTXFile::WriteBitmapToKTX(stream, bitmap);
284 }
285
encodePKM(SkWStream * stream,const SkData * data)286 bool SkKTXImageEncoder::encodePKM(SkWStream* stream, const SkData *data) {
287 const uint8_t* bytes = data->bytes();
288 SkASSERT(etc1_pkm_is_valid(bytes));
289
290 etc1_uint32 width = etc1_pkm_get_width(bytes);
291 etc1_uint32 height = etc1_pkm_get_height(bytes);
292
293 // ETC1 Data is stored as compressed 4x4 pixel blocks, so we must make sure
294 // that our dimensions are valid.
295 if (width == 0 || (width & 3) != 0 || height == 0 || (height & 3) != 0) {
296 return false;
297 }
298
299 // Advance pointer to etc1 data.
300 bytes += ETC_PKM_HEADER_SIZE;
301
302 return SkKTXFile::WriteETC1ToKTX(stream, bytes, width, height);
303 }
304
305 /////////////////////////////////////////////////////////////////////////////////////////
306 DEFINE_DECODER_CREATOR(KTXImageDecoder);
307 DEFINE_ENCODER_CREATOR(KTXImageEncoder);
308 /////////////////////////////////////////////////////////////////////////////////////////
309
sk_libktx_dfactory(SkStreamRewindable * stream)310 static SkImageDecoder* sk_libktx_dfactory(SkStreamRewindable* stream) {
311 if (SkKTXFile::is_ktx(stream)) {
312 return SkNEW(SkKTXImageDecoder);
313 }
314 return NULL;
315 }
316
get_format_ktx(SkStreamRewindable * stream)317 static SkImageDecoder::Format get_format_ktx(SkStreamRewindable* stream) {
318 if (SkKTXFile::is_ktx(stream)) {
319 return SkImageDecoder::kKTX_Format;
320 }
321 return SkImageDecoder::kUnknown_Format;
322 }
323
sk_libktx_efactory(SkImageEncoder::Type t)324 SkImageEncoder* sk_libktx_efactory(SkImageEncoder::Type t) {
325 return (SkImageEncoder::kKTX_Type == t) ? SkNEW(SkKTXImageEncoder) : NULL;
326 }
327
328 static SkImageDecoder_DecodeReg gReg(sk_libktx_dfactory);
329 static SkImageDecoder_FormatReg gFormatReg(get_format_ktx);
330 static SkImageEncoder_EncodeReg gEReg(sk_libktx_efactory);
331