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 "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 35 class SkKTXImageDecoder : public SkImageDecoder { 36 public: 37 SkKTXImageDecoder() { } 38 39 virtual Format getFormat() const SK_OVERRIDE { 40 return kKTX_Format; 41 } 42 43 protected: 44 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE; 45 46 private: 47 typedef SkImageDecoder INHERITED; 48 }; 49 50 bool 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 #ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER 66 // should we allow the Chooser (if present) to pick a config for us??? 67 if (!this->chooseFromOneChoice(kN32_SkColorType, width, height)) { 68 return false; 69 } 70 #endif 71 72 // Set a flag if our source is premultiplied alpha 73 const SkString premulKey("KTXPremultipliedAlpha"); 74 const bool bSrcIsPremul = ktxFile.getValueForKey(premulKey) == SkString("True"); 75 76 // Setup the sampler... 77 SkScaledBitmapSampler sampler(width, height, this->getSampleSize()); 78 79 // Determine the alpha of the bitmap... 80 SkAlphaType alphaType = kOpaque_SkAlphaType; 81 if (ktxFile.isRGBA8()) { 82 if (this->getRequireUnpremultipliedColors()) { 83 alphaType = kUnpremul_SkAlphaType; 84 // If the client wants unpremul colors and we only have 85 // premul, then we cannot honor their wish. 86 if (bSrcIsPremul) { 87 return false; 88 } 89 } else { 90 alphaType = kPremul_SkAlphaType; 91 } 92 } 93 94 // Set the config... 95 bm->setInfo(SkImageInfo::MakeN32(sampler.scaledWidth(), sampler.scaledHeight(), alphaType)); 96 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 97 return true; 98 } 99 100 // If we've made it this far, then we know how to grok the data. 101 if (!this->allocPixelRef(bm, NULL)) { 102 return false; 103 } 104 105 // Lock the pixels, since we're about to write to them... 106 SkAutoLockPixels alp(*bm); 107 108 if (ktxFile.isETC1()) { 109 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) { 110 return false; 111 } 112 113 // ETC1 Data is encoded as RGB pixels, so we should extract it as such 114 int nPixels = width * height; 115 SkAutoMalloc outRGBData(nPixels * 3); 116 etc1_byte *outRGBDataPtr = reinterpret_cast<etc1_byte *>(outRGBData.get()); 117 118 // Decode ETC1 119 const etc1_byte *buf = reinterpret_cast<const etc1_byte *>(ktxFile.pixelData()); 120 if (etc1_decode_image(buf, outRGBDataPtr, width, height, 3, width*3)) { 121 return false; 122 } 123 124 // Set each of the pixels... 125 const int srcRowBytes = width * 3; 126 const int dstHeight = sampler.scaledHeight(); 127 const uint8_t *srcRow = reinterpret_cast<uint8_t *>(outRGBDataPtr); 128 srcRow += sampler.srcY0() * srcRowBytes; 129 for (int y = 0; y < dstHeight; ++y) { 130 sampler.next(srcRow); 131 srcRow += sampler.srcDY() * srcRowBytes; 132 } 133 134 return true; 135 136 } else if (ktxFile.isRGB8()) { 137 138 // Uncompressed RGB data (without alpha) 139 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) { 140 return false; 141 } 142 143 // Just need to read RGB pixels 144 const int srcRowBytes = width * 3; 145 const int dstHeight = sampler.scaledHeight(); 146 const uint8_t *srcRow = reinterpret_cast<const uint8_t *>(ktxFile.pixelData()); 147 srcRow += sampler.srcY0() * srcRowBytes; 148 for (int y = 0; y < dstHeight; ++y) { 149 sampler.next(srcRow); 150 srcRow += sampler.srcDY() * srcRowBytes; 151 } 152 153 return true; 154 155 } else if (ktxFile.isRGBA8()) { 156 157 // Uncompressed RGBA data 158 159 // If we know that the image contains premultiplied alpha, then 160 // we need to turn off the premultiplier 161 SkScaledBitmapSampler::Options opts (*this); 162 if (bSrcIsPremul) { 163 SkASSERT(bm->alphaType() == kPremul_SkAlphaType); 164 SkASSERT(!this->getRequireUnpremultipliedColors()); 165 166 opts.fPremultiplyAlpha = false; 167 } 168 169 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGBA, opts)) { 170 return false; 171 } 172 173 // Just need to read RGBA pixels 174 const int srcRowBytes = width * 4; 175 const int dstHeight = sampler.scaledHeight(); 176 const uint8_t *srcRow = reinterpret_cast<const uint8_t *>(ktxFile.pixelData()); 177 srcRow += sampler.srcY0() * srcRowBytes; 178 for (int y = 0; y < dstHeight; ++y) { 179 sampler.next(srcRow); 180 srcRow += sampler.srcDY() * srcRowBytes; 181 } 182 183 return true; 184 } 185 186 return false; 187 } 188 189 /////////////////////////////////////////////////////////////////////////////// 190 191 // KTX Image Encoder 192 // 193 // This encoder takes a best guess at how to encode the bitmap passed to it. If 194 // there is an installed discardable pixel ref with existing PKM data, then we 195 // will repurpose the existing ETC1 data into a KTX file. If the data contains 196 // KTX data, then we simply return a copy of the same data. For all other files, 197 // the underlying KTX library tries to do its best to encode the appropriate 198 // data specified by the bitmap based on the config. (i.e. kAlpha8_Config will 199 // be represented as a full resolution 8-bit image dump with the appropriate 200 // OpenGL defines in the header). 201 202 class SkKTXImageEncoder : public SkImageEncoder { 203 protected: 204 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE; 205 206 private: 207 virtual bool encodePKM(SkWStream* stream, const SkData *data); 208 typedef SkImageEncoder INHERITED; 209 }; 210 211 bool SkKTXImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, int) { 212 SkAutoDataUnref data(bitmap.pixelRef()->refEncodedData()); 213 214 // Is this even encoded data? 215 if (NULL != data) { 216 const uint8_t *bytes = data->bytes(); 217 if (etc1_pkm_is_valid(bytes)) { 218 return this->encodePKM(stream, data); 219 } 220 221 // Is it a KTX file?? 222 if (SkKTXFile::is_ktx(bytes)) { 223 return stream->write(bytes, data->size()); 224 } 225 226 // If it's neither a KTX nor a PKM, then we need to 227 // get at the actual pixels, so fall through and decompress... 228 } 229 230 return SkKTXFile::WriteBitmapToKTX(stream, bitmap); 231 } 232 233 bool SkKTXImageEncoder::encodePKM(SkWStream* stream, const SkData *data) { 234 const uint8_t* bytes = data->bytes(); 235 SkASSERT(etc1_pkm_is_valid(bytes)); 236 237 etc1_uint32 width = etc1_pkm_get_width(bytes); 238 etc1_uint32 height = etc1_pkm_get_height(bytes); 239 240 // ETC1 Data is stored as compressed 4x4 pixel blocks, so we must make sure 241 // that our dimensions are valid. 242 if (width == 0 || (width & 3) != 0 || height == 0 || (height & 3) != 0) { 243 return false; 244 } 245 246 // Advance pointer to etc1 data. 247 bytes += ETC_PKM_HEADER_SIZE; 248 249 return SkKTXFile::WriteETC1ToKTX(stream, bytes, width, height); 250 } 251 252 ///////////////////////////////////////////////////////////////////////////////////////// 253 DEFINE_DECODER_CREATOR(KTXImageDecoder); 254 DEFINE_ENCODER_CREATOR(KTXImageEncoder); 255 ///////////////////////////////////////////////////////////////////////////////////////// 256 257 static SkImageDecoder* sk_libktx_dfactory(SkStreamRewindable* stream) { 258 if (SkKTXFile::is_ktx(stream)) { 259 return SkNEW(SkKTXImageDecoder); 260 } 261 return NULL; 262 } 263 264 static SkImageDecoder::Format get_format_ktx(SkStreamRewindable* stream) { 265 if (SkKTXFile::is_ktx(stream)) { 266 return SkImageDecoder::kKTX_Format; 267 } 268 return SkImageDecoder::kUnknown_Format; 269 } 270 271 SkImageEncoder* sk_libktx_efactory(SkImageEncoder::Type t) { 272 return (SkImageEncoder::kKTX_Type == t) ? SkNEW(SkKTXImageEncoder) : NULL; 273 } 274 275 static SkImageDecoder_DecodeReg gReg(sk_libktx_dfactory); 276 static SkImageDecoder_FormatReg gFormatReg(get_format_ktx); 277 static SkImageEncoder_EncodeReg gEReg(sk_libktx_efactory); 278