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