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