aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/images/SkImageDecoder_pkm.cpp
blob: f5fd4b3fbcad54f7c2327b1c072159a97b0b1a73 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
 * Copyright 2014 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkColorPriv.h"
#include "SkImageDecoder.h"
#include "SkScaledBitmapSampler.h"
#include "SkStream.h"
#include "SkStreamPriv.h"
#include "SkTextureCompressor.h"
#include "SkTypes.h"

#include "etc1.h"

class SkPKMImageDecoder : public SkImageDecoder {
public:
    SkPKMImageDecoder() { }

    virtual Format getFormat() const SK_OVERRIDE {
        return kPKM_Format;
    }

protected:
    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;

private:
    typedef SkImageDecoder INHERITED;
};

/////////////////////////////////////////////////////////////////////////////////////////

bool SkPKMImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
    SkAutoMalloc autoMal;
    const size_t length = SkCopyStreamToStorage(&autoMal, stream);
    if (0 == length) {
        return false;
    }

    unsigned char* buf = (unsigned char*)autoMal.get();

    // Make sure original PKM header is there...
    SkASSERT(etc1_pkm_is_valid(buf));

    const unsigned short width = etc1_pkm_get_width(buf);
    const unsigned short height = etc1_pkm_get_height(buf);

#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
    // should we allow the Chooser (if present) to pick a config for us???
    if (!this->chooseFromOneChoice(kN32_SkColorType, width, height)) {
        return false;
    }
#endif

    // Setup the sampler...
    SkScaledBitmapSampler sampler(width, height, this->getSampleSize());

    // Set the config...
    bm->setInfo(SkImageInfo::MakeN32(sampler.scaledWidth(), sampler.scaledHeight(),
                                     kOpaque_SkAlphaType));
    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
        return true;
    }

    if (!this->allocPixelRef(bm, NULL)) {
        return false;
    }

    // Lock the pixels, since we're about to write to them...
    SkAutoLockPixels alp(*bm);

    if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {
        return false;
    }

    // Advance buffer past the header
    buf += ETC_PKM_HEADER_SIZE;

    // ETC1 Data is encoded as RGB pixels, so we should extract it as such
    int nPixels = width * height;
    SkAutoMalloc outRGBData(nPixels * 3);
    uint8_t *outRGBDataPtr = reinterpret_cast<uint8_t *>(outRGBData.get());

    // Decode ETC1
    if (!SkTextureCompressor::DecompressBufferFromFormat(
            outRGBDataPtr, width*3, buf, width, height, SkTextureCompressor::kETC1_Format)) {
        return false;
    }

    // Set each of the pixels...
    const int srcRowBytes = width * 3;
    const int dstHeight = sampler.scaledHeight();
    const uint8_t *srcRow = reinterpret_cast<uint8_t *>(outRGBDataPtr);
    srcRow += sampler.srcY0() * srcRowBytes;
    for (int y = 0; y < dstHeight; ++y) {
        sampler.next(srcRow);
        srcRow += sampler.srcDY() * srcRowBytes;
    }

    return true;
}

/////////////////////////////////////////////////////////////////////////////////////////
DEFINE_DECODER_CREATOR(PKMImageDecoder);
/////////////////////////////////////////////////////////////////////////////////////////

static bool is_pkm(SkStreamRewindable* stream) {
    // Read the PKM header and make sure it's valid.
    unsigned char buf[ETC_PKM_HEADER_SIZE];
    if (stream->read((void*)buf, ETC_PKM_HEADER_SIZE) != ETC_PKM_HEADER_SIZE) {
        return false;
    }

    return SkToBool(etc1_pkm_is_valid(buf));
}

static SkImageDecoder* sk_libpkm_dfactory(SkStreamRewindable* stream) {
    if (is_pkm(stream)) {
        return SkNEW(SkPKMImageDecoder);
    }
    return NULL;
}

static SkImageDecoder_DecodeReg gReg(sk_libpkm_dfactory);

static SkImageDecoder::Format get_format_pkm(SkStreamRewindable* stream) {
    if (is_pkm(stream)) {
        return SkImageDecoder::kPKM_Format;
    }
    return SkImageDecoder::kUnknown_Format;
}

static SkImageDecoder_FormatReg gFormatReg(get_format_pkm);