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