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