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
35class SkKTXImageDecoder : public SkImageDecoder {
36public:
37    SkKTXImageDecoder() { }
38
39    Format getFormat() const override {
40        return kKTX_Format;
41    }
42
43protected:
44    Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
45
46private:
47    typedef SkImageDecoder INHERITED;
48};
49
50SkImageDecoder::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
252class SkKTXImageEncoder : public SkImageEncoder {
253protected:
254    bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override;
255
256private:
257    virtual bool encodePKM(SkWStream* stream, const SkData *data);
258    typedef SkImageEncoder INHERITED;
259};
260
261bool 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
286bool 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/////////////////////////////////////////////////////////////////////////////////////////
306DEFINE_DECODER_CREATOR(KTXImageDecoder);
307DEFINE_ENCODER_CREATOR(KTXImageEncoder);
308/////////////////////////////////////////////////////////////////////////////////////////
309
310static SkImageDecoder* sk_libktx_dfactory(SkStreamRewindable* stream) {
311    if (SkKTXFile::is_ktx(stream)) {
312        return SkNEW(SkKTXImageDecoder);
313    }
314    return NULL;
315}
316
317static 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
324SkImageEncoder* sk_libktx_efactory(SkImageEncoder::Type t) {
325    return (SkImageEncoder::kKTX_Type == t) ? SkNEW(SkKTXImageEncoder) : NULL;
326}
327
328static SkImageDecoder_DecodeReg gReg(sk_libktx_dfactory);
329static SkImageDecoder_FormatReg gFormatReg(get_format_ktx);
330static SkImageEncoder_EncodeReg gEReg(sk_libktx_efactory);
331