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