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
|
/*
* 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() { }
Format getFormat() const override {
return kPKM_Format;
}
protected:
Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
private:
typedef SkImageDecoder INHERITED;
};
/////////////////////////////////////////////////////////////////////////////////////////
SkImageDecoder::Result SkPKMImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
SkAutoMalloc autoMal;
const size_t length = SkCopyStreamToStorage(&autoMal, stream);
if (0 == length) {
return kFailure;
}
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);
// 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 kSuccess;
}
if (!this->allocPixelRef(bm, NULL)) {
return kFailure;
}
// Lock the pixels, since we're about to write to them...
SkAutoLockPixels alp(*bm);
if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {
return kFailure;
}
// 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 kFailure;
}
// 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 kSuccess;
}
/////////////////////////////////////////////////////////////////////////////////////////
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);
|