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 "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 Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
     45 
     46 private:
     47     typedef SkImageDecoder INHERITED;
     48 };
     49 
     50 SkImageDecoder::Result 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 kFailure;
     55     }
     56 
     57     SkKTXFile ktxFile(data);
     58     if (!ktxFile.valid()) {
     59         return kFailure;
     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 kFailure;
     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 kFailure;
     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 kSuccess;
     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 kFailure;
    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 kFailure;
    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 kFailure;
    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 kSuccess;
    135 
    136     } else if (ktxFile.isRGB8()) {
    137 
    138         // Uncompressed RGB data (without alpha)
    139         if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {
    140             return kFailure;
    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 kSuccess;
    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 kFailure;
    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 kSuccess;
    184     }
    185 
    186     return kFailure;
    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