Home | History | Annotate | Download | only in images
      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 
     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(SkCopyStreamToData(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     // Search through the compressed formats to see if the KTX file is holding
     95     // compressed data
     96     bool ktxIsCompressed = false;
     97     SkTextureCompressor::Format ktxCompressedFormat;
     98     for (int i = 0; i < SkTextureCompressor::kFormatCnt; ++i) {
     99         SkTextureCompressor::Format fmt = static_cast<SkTextureCompressor::Format>(i);
    100         if (ktxFile.isCompressedFormat(fmt)) {
    101             ktxIsCompressed = true;
    102             ktxCompressedFormat = fmt;
    103             break;
    104         }
    105     }
    106 
    107     // If the compressed format is a grayscale image, then setup the bitmap properly...
    108     bool isCompressedAlpha = ktxIsCompressed &&
    109         ((SkTextureCompressor::kLATC_Format == ktxCompressedFormat) ||
    110          (SkTextureCompressor::kR11_EAC_Format == ktxCompressedFormat));
    111 
    112     // Set the image dimensions and underlying pixel type.
    113     if (isCompressedAlpha) {
    114         const int w = sampler.scaledWidth();
    115         const int h = sampler.scaledHeight();
    116         bm->setInfo(SkImageInfo::MakeA8(w, h));
    117     } else {
    118         const int w = sampler.scaledWidth();
    119         const int h = sampler.scaledHeight();
    120         bm->setInfo(SkImageInfo::MakeN32(w, h, alphaType));
    121     }
    122 
    123     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
    124         return true;
    125     }
    126 
    127     // If we've made it this far, then we know how to grok the data.
    128     if (!this->allocPixelRef(bm, NULL)) {
    129         return false;
    130     }
    131 
    132     // Lock the pixels, since we're about to write to them...
    133     SkAutoLockPixels alp(*bm);
    134 
    135     if (isCompressedAlpha) {
    136         if (!sampler.begin(bm, SkScaledBitmapSampler::kGray, *this)) {
    137             return false;
    138         }
    139 
    140         // Alpha data is only a single byte per pixel.
    141         int nPixels = width * height;
    142         SkAutoMalloc outRGBData(nPixels);
    143         uint8_t *outRGBDataPtr = reinterpret_cast<uint8_t *>(outRGBData.get());
    144 
    145         // Decode the compressed format
    146         const uint8_t *buf = reinterpret_cast<const uint8_t *>(ktxFile.pixelData());
    147         if (!SkTextureCompressor::DecompressBufferFromFormat(
    148                 outRGBDataPtr, width, buf, width, height, ktxCompressedFormat)) {
    149             return false;
    150         }
    151 
    152         // Set each of the pixels...
    153         const int srcRowBytes = width;
    154         const int dstHeight = sampler.scaledHeight();
    155         const uint8_t *srcRow = reinterpret_cast<uint8_t *>(outRGBDataPtr);
    156         srcRow += sampler.srcY0() * srcRowBytes;
    157         for (int y = 0; y < dstHeight; ++y) {
    158             sampler.next(srcRow);
    159             srcRow += sampler.srcDY() * srcRowBytes;
    160         }
    161 
    162         return true;
    163 
    164     } else if (ktxFile.isCompressedFormat(SkTextureCompressor::kETC1_Format)) {
    165         if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {
    166             return false;
    167         }
    168 
    169         // ETC1 Data is encoded as RGB pixels, so we should extract it as such
    170         int nPixels = width * height;
    171         SkAutoMalloc outRGBData(nPixels * 3);
    172         uint8_t *outRGBDataPtr = reinterpret_cast<uint8_t *>(outRGBData.get());
    173 
    174         // Decode ETC1
    175         const uint8_t *buf = reinterpret_cast<const uint8_t *>(ktxFile.pixelData());
    176         if (!SkTextureCompressor::DecompressBufferFromFormat(
    177                 outRGBDataPtr, width*3, buf, width, height, SkTextureCompressor::kETC1_Format)) {
    178             return false;
    179         }
    180 
    181         // Set each of the pixels...
    182         const int srcRowBytes = width * 3;
    183         const int dstHeight = sampler.scaledHeight();
    184         const uint8_t *srcRow = reinterpret_cast<uint8_t *>(outRGBDataPtr);
    185         srcRow += sampler.srcY0() * srcRowBytes;
    186         for (int y = 0; y < dstHeight; ++y) {
    187             sampler.next(srcRow);
    188             srcRow += sampler.srcDY() * srcRowBytes;
    189         }
    190 
    191         return true;
    192 
    193     } else if (ktxFile.isRGB8()) {
    194 
    195         // Uncompressed RGB data (without alpha)
    196         if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {
    197             return false;
    198         }
    199 
    200         // Just need to read RGB pixels
    201         const int srcRowBytes = width * 3;
    202         const int dstHeight = sampler.scaledHeight();
    203         const uint8_t *srcRow = reinterpret_cast<const uint8_t *>(ktxFile.pixelData());
    204         srcRow += sampler.srcY0() * srcRowBytes;
    205         for (int y = 0; y < dstHeight; ++y) {
    206             sampler.next(srcRow);
    207             srcRow += sampler.srcDY() * srcRowBytes;
    208         }
    209 
    210         return true;
    211 
    212     } else if (ktxFile.isRGBA8()) {
    213 
    214         // Uncompressed RGBA data
    215 
    216         // If we know that the image contains premultiplied alpha, then
    217         // we need to turn off the premultiplier
    218         SkScaledBitmapSampler::Options opts (*this);
    219         if (bSrcIsPremul) {
    220             SkASSERT(bm->alphaType() == kPremul_SkAlphaType);
    221             SkASSERT(!this->getRequireUnpremultipliedColors());
    222 
    223             opts.fPremultiplyAlpha = false;
    224         }
    225 
    226         if (!sampler.begin(bm, SkScaledBitmapSampler::kRGBA, opts)) {
    227             return false;
    228         }
    229 
    230         // Just need to read RGBA pixels
    231         const int srcRowBytes = width * 4;
    232         const int dstHeight = sampler.scaledHeight();
    233         const uint8_t *srcRow = reinterpret_cast<const uint8_t *>(ktxFile.pixelData());
    234         srcRow += sampler.srcY0() * srcRowBytes;
    235         for (int y = 0; y < dstHeight; ++y) {
    236             sampler.next(srcRow);
    237             srcRow += sampler.srcDY() * srcRowBytes;
    238         }
    239 
    240         return true;
    241     }
    242 
    243     return false;
    244 }
    245 
    246 ///////////////////////////////////////////////////////////////////////////////
    247 
    248 // KTX Image Encoder
    249 //
    250 // This encoder takes a best guess at how to encode the bitmap passed to it. If
    251 // there is an installed discardable pixel ref with existing PKM data, then we
    252 // will repurpose the existing ETC1 data into a KTX file. If the data contains
    253 // KTX data, then we simply return a copy of the same data. For all other files,
    254 // the underlying KTX library tries to do its best to encode the appropriate
    255 // data specified by the bitmap based on the config. (i.e. kAlpha8_Config will
    256 // be represented as a full resolution 8-bit image dump with the appropriate
    257 // OpenGL defines in the header).
    258 
    259 class SkKTXImageEncoder : public SkImageEncoder {
    260 protected:
    261     virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE;
    262 
    263 private:
    264     virtual bool encodePKM(SkWStream* stream, const SkData *data);
    265     typedef SkImageEncoder INHERITED;
    266 };
    267 
    268 bool SkKTXImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, int) {
    269     if (!bitmap.pixelRef()) {
    270         return false;
    271     }
    272     SkAutoDataUnref data(bitmap.pixelRef()->refEncodedData());
    273 
    274     // Is this even encoded data?
    275     if (data) {
    276         const uint8_t *bytes = data->bytes();
    277         if (etc1_pkm_is_valid(bytes)) {
    278             return this->encodePKM(stream, data);
    279         }
    280 
    281         // Is it a KTX file??
    282         if (SkKTXFile::is_ktx(bytes)) {
    283             return stream->write(bytes, data->size());
    284         }
    285 
    286         // If it's neither a KTX nor a PKM, then we need to
    287         // get at the actual pixels, so fall through and decompress...
    288     }
    289 
    290     return SkKTXFile::WriteBitmapToKTX(stream, bitmap);
    291 }
    292 
    293 bool SkKTXImageEncoder::encodePKM(SkWStream* stream, const SkData *data) {
    294     const uint8_t* bytes = data->bytes();
    295     SkASSERT(etc1_pkm_is_valid(bytes));
    296 
    297     etc1_uint32 width = etc1_pkm_get_width(bytes);
    298     etc1_uint32 height = etc1_pkm_get_height(bytes);
    299 
    300     // ETC1 Data is stored as compressed 4x4 pixel blocks, so we must make sure
    301     // that our dimensions are valid.
    302     if (width == 0 || (width & 3) != 0 || height == 0 || (height & 3) != 0) {
    303         return false;
    304     }
    305 
    306     // Advance pointer to etc1 data.
    307     bytes += ETC_PKM_HEADER_SIZE;
    308 
    309     return SkKTXFile::WriteETC1ToKTX(stream, bytes, width, height);
    310 }
    311 
    312 /////////////////////////////////////////////////////////////////////////////////////////
    313 DEFINE_DECODER_CREATOR(KTXImageDecoder);
    314 DEFINE_ENCODER_CREATOR(KTXImageEncoder);
    315 /////////////////////////////////////////////////////////////////////////////////////////
    316 
    317 static SkImageDecoder* sk_libktx_dfactory(SkStreamRewindable* stream) {
    318     if (SkKTXFile::is_ktx(stream)) {
    319         return SkNEW(SkKTXImageDecoder);
    320     }
    321     return NULL;
    322 }
    323 
    324 static SkImageDecoder::Format get_format_ktx(SkStreamRewindable* stream) {
    325     if (SkKTXFile::is_ktx(stream)) {
    326         return SkImageDecoder::kKTX_Format;
    327     }
    328     return SkImageDecoder::kUnknown_Format;
    329 }
    330 
    331 SkImageEncoder* sk_libktx_efactory(SkImageEncoder::Type t) {
    332     return (SkImageEncoder::kKTX_Type == t) ? SkNEW(SkKTXImageEncoder) : NULL;
    333 }
    334 
    335 static SkImageDecoder_DecodeReg gReg(sk_libktx_dfactory);
    336 static SkImageDecoder_FormatReg gFormatReg(get_format_ktx);
    337 static SkImageEncoder_EncodeReg gEReg(sk_libktx_efactory);
    338