blob: 0dd987cef19e2e4986829afffbeba72453a5703d [file] [log] [blame]
krajcevski99ffe242014-06-03 13:04:35 -07001/*
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"
krajcevskic250d2e2014-06-06 06:16:28 -070010#include "SkPixelRef.h"
krajcevski99ffe242014-06-03 13:04:35 -070011#include "SkScaledBitmapSampler.h"
12#include "SkStream.h"
13#include "SkStreamHelpers.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 virtual Format getFormat() const SK_OVERRIDE {
40 return kKTX_Format;
41 }
42
43protected:
44 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
45
46private:
47 typedef SkImageDecoder INHERITED;
48};
49
50bool SkKTXImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
51 // TODO: Implement SkStream::copyToData() that's cheap for memory and file streams
52 SkAutoDataUnref data(CopyStreamToData(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 // should we allow the Chooser (if present) to pick a config for us???
reed6c225732014-06-09 19:52:07 -070066 if (!this->chooseFromOneChoice(kN32_SkColorType, width, height)) {
krajcevski99ffe242014-06-03 13:04:35 -070067 return false;
68 }
69
70 // Setup the sampler...
71 SkScaledBitmapSampler sampler(width, height, this->getSampleSize());
72
73 // Set the config...
reed6c225732014-06-09 19:52:07 -070074 bm->setInfo(SkImageInfo::MakeN32(sampler.scaledWidth(), sampler.scaledHeight(),
75 ktxFile.isRGBA8()? kPremul_SkAlphaType : kOpaque_SkAlphaType));
krajcevski99ffe242014-06-03 13:04:35 -070076 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
77 return true;
78 }
79
80 // If we've made it this far, then we know how to grok the data.
81 if (!this->allocPixelRef(bm, NULL)) {
82 return false;
83 }
84
85 // Lock the pixels, since we're about to write to them...
86 SkAutoLockPixels alp(*bm);
87
88 if (ktxFile.isETC1()) {
89 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {
90 return false;
91 }
92
93 // ETC1 Data is encoded as RGB pixels, so we should extract it as such
94 int nPixels = width * height;
95 SkAutoMalloc outRGBData(nPixels * 3);
96 etc1_byte *outRGBDataPtr = reinterpret_cast<etc1_byte *>(outRGBData.get());
97
98 // Decode ETC1
99 const etc1_byte *buf = reinterpret_cast<const etc1_byte *>(ktxFile.pixelData());
100 if (etc1_decode_image(buf, outRGBDataPtr, width, height, 3, width*3)) {
101 return false;
102 }
103
104 // Set each of the pixels...
105 const int srcRowBytes = width * 3;
106 const int dstHeight = sampler.scaledHeight();
107 const uint8_t *srcRow = reinterpret_cast<uint8_t *>(outRGBDataPtr);
108 srcRow += sampler.srcY0() * srcRowBytes;
109 for (int y = 0; y < dstHeight; ++y) {
110 sampler.next(srcRow);
111 srcRow += sampler.srcDY() * srcRowBytes;
112 }
113
114 return true;
115
116 } else if (ktxFile.isRGB8()) {
117
118 // Uncompressed RGB data (without alpha)
119 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {
120 return false;
121 }
122
123 // Just need to read RGB pixels
124 const int srcRowBytes = width * 3;
125 const int dstHeight = sampler.scaledHeight();
126 const uint8_t *srcRow = reinterpret_cast<const uint8_t *>(ktxFile.pixelData());
127 srcRow += sampler.srcY0() * srcRowBytes;
128 for (int y = 0; y < dstHeight; ++y) {
129 sampler.next(srcRow);
130 srcRow += sampler.srcDY() * srcRowBytes;
131 }
132
133 return true;
134
135 } else if (ktxFile.isRGBA8()) {
136
krajcevskic250d2e2014-06-06 06:16:28 -0700137 // If we know that the image contains premultiplied alpha, then
138 // don't premultiply it upon decoding.
139 bool setRequireUnpremul = false;
140 const SkString premulKey("KTXPremultipliedAlpha");
141 if (ktxFile.getValueForKey(premulKey) == SkString("True")) {
142 this->setRequireUnpremultipliedColors(true);
143 setRequireUnpremul = true;
144 }
145
krajcevski99ffe242014-06-03 13:04:35 -0700146 // Uncompressed RGBA data
147 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGBA, *this)) {
148 return false;
149 }
150
151 // Just need to read RGBA pixels
152 const int srcRowBytes = width * 4;
153 const int dstHeight = sampler.scaledHeight();
154 const uint8_t *srcRow = reinterpret_cast<const uint8_t *>(ktxFile.pixelData());
155 srcRow += sampler.srcY0() * srcRowBytes;
156 for (int y = 0; y < dstHeight; ++y) {
157 sampler.next(srcRow);
158 srcRow += sampler.srcDY() * srcRowBytes;
159 }
160
krajcevskic250d2e2014-06-06 06:16:28 -0700161 // Reset this in case the decoder needs to be used again.
162 if (setRequireUnpremul) {
163 this->setRequireUnpremultipliedColors(false);
164 }
165
krajcevski99ffe242014-06-03 13:04:35 -0700166 return true;
167 }
168
169 return false;
170}
171
krajcevskic250d2e2014-06-06 06:16:28 -0700172///////////////////////////////////////////////////////////////////////////////
173
174// KTX Image Encoder
175//
176// This encoder takes a best guess at how to encode the bitmap passed to it. If
177// there is an installed discardable pixel ref with existing PKM data, then we
178// will repurpose the existing ETC1 data into a KTX file. If the data contains
179// KTX data, then we simply return a copy of the same data. For all other files,
180// the underlying KTX library tries to do its best to encode the appropriate
181// data specified by the bitmap based on the config. (i.e. kAlpha8_Config will
182// be represented as a full resolution 8-bit image dump with the appropriate
183// OpenGL defines in the header).
184
185class SkKTXImageEncoder : public SkImageEncoder {
186protected:
187 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE;
188
189private:
190 virtual bool encodePKM(SkWStream* stream, const SkData *data);
191 typedef SkImageEncoder INHERITED;
192};
193
194bool SkKTXImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, int) {
195 SkAutoDataUnref data(bitmap.pixelRef()->refEncodedData());
196
197 // Is this even encoded data?
198 if (NULL != data) {
199 const uint8_t *bytes = data->bytes();
200 if (etc1_pkm_is_valid(bytes)) {
201 return this->encodePKM(stream, data);
202 }
203
204 // Is it a KTX file??
205 if (SkKTXFile::is_ktx(bytes)) {
206 return stream->write(bytes, data->size());
207 }
208
209 // If it's neither a KTX nor a PKM, then we need to
210 // get at the actual pixels, so fall through and decompress...
211 }
212
213 return SkKTXFile::WriteBitmapToKTX(stream, bitmap);
214}
215
216bool SkKTXImageEncoder::encodePKM(SkWStream* stream, const SkData *data) {
217 const uint8_t* bytes = data->bytes();
218 SkASSERT(etc1_pkm_is_valid(bytes));
219
220 etc1_uint32 width = etc1_pkm_get_width(bytes);
221 etc1_uint32 height = etc1_pkm_get_height(bytes);
222
223 // ETC1 Data is stored as compressed 4x4 pixel blocks, so we must make sure
224 // that our dimensions are valid.
225 if (width == 0 || (width & 3) != 0 || height == 0 || (height & 3) != 0) {
226 return false;
227 }
228
229 // Advance pointer to etc1 data.
230 bytes += ETC_PKM_HEADER_SIZE;
231
232 return SkKTXFile::WriteETC1ToKTX(stream, bytes, width, height);
233}
234
krajcevski99ffe242014-06-03 13:04:35 -0700235/////////////////////////////////////////////////////////////////////////////////////////
236DEFINE_DECODER_CREATOR(KTXImageDecoder);
krajcevskic250d2e2014-06-06 06:16:28 -0700237DEFINE_ENCODER_CREATOR(KTXImageEncoder);
krajcevski99ffe242014-06-03 13:04:35 -0700238/////////////////////////////////////////////////////////////////////////////////////////
239
240static SkImageDecoder* sk_libktx_dfactory(SkStreamRewindable* stream) {
241 if (SkKTXFile::is_ktx(stream)) {
242 return SkNEW(SkKTXImageDecoder);
243 }
244 return NULL;
245}
246
krajcevski99ffe242014-06-03 13:04:35 -0700247static SkImageDecoder::Format get_format_ktx(SkStreamRewindable* stream) {
248 if (SkKTXFile::is_ktx(stream)) {
249 return SkImageDecoder::kKTX_Format;
250 }
251 return SkImageDecoder::kUnknown_Format;
252}
253
krajcevskic250d2e2014-06-06 06:16:28 -0700254SkImageEncoder* sk_libktx_efactory(SkImageEncoder::Type t) {
255 return (SkImageEncoder::kKTX_Type == t) ? SkNEW(SkKTXImageEncoder) : NULL;
256}
257
258static SkImageDecoder_DecodeReg gReg(sk_libktx_dfactory);
krajcevski99ffe242014-06-03 13:04:35 -0700259static SkImageDecoder_FormatReg gFormatReg(get_format_ktx);
krajcevskic250d2e2014-06-06 06:16:28 -0700260static SkImageEncoder_EncodeReg gEReg(sk_libktx_efactory);