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 "SkTextureCompressor.h"
9#include "SkTextureCompressor_ASTC.h"
10#include "SkTextureCompressor_LATC.h"
11#include "SkTextureCompressor_R11EAC.h"
12
13#include "SkBitmap.h"
14#include "SkBitmapProcShader.h"
15#include "SkData.h"
16#include "SkEndian.h"
17
18#include "SkTextureCompression_opts.h"
19
20#ifndef SK_IGNORE_ETC1_SUPPORT
21#  include "etc1.h"
22#endif
23
24// Convert ETC1 functions to our function signatures
25static bool compress_etc1_565(uint8_t* dst, const uint8_t* src,
26                              int width, int height, int rowBytes) {
27#ifndef SK_IGNORE_ETC1_SUPPORT
28    return 0 == etc1_encode_image(src, width, height, 2, rowBytes, dst);
29#else
30    return false;
31#endif
32}
33
34////////////////////////////////////////////////////////////////////////////////
35
36namespace SkTextureCompressor {
37
38void GetBlockDimensions(Format format, int* dimX, int* dimY, bool matchSpec) {
39    if (NULL == dimX || NULL == dimY) {
40        return;
41    }
42
43    if (!matchSpec && SkTextureCompressorGetPlatformDims(format, dimX, dimY)) {
44        return;
45    }
46
47    // No specialized arguments, return the dimensions as they are in the spec.
48    static const struct FormatDimensions {
49        const int fBlockSizeX;
50        const int fBlockSizeY;
51    } kFormatDimensions[kFormatCnt] = {
52        { 4, 4 }, // kLATC_Format
53        { 4, 4 }, // kR11_EAC_Format
54        { 4, 4 }, // kETC1_Format
55        { 4, 4 }, // kASTC_4x4_Format
56        { 5, 4 }, // kASTC_5x4_Format
57        { 5, 5 }, // kASTC_5x5_Format
58        { 6, 5 }, // kASTC_6x5_Format
59        { 6, 6 }, // kASTC_6x6_Format
60        { 8, 5 }, // kASTC_8x5_Format
61        { 8, 6 }, // kASTC_8x6_Format
62        { 8, 8 }, // kASTC_8x8_Format
63        { 10, 5 }, // kASTC_10x5_Format
64        { 10, 6 }, // kASTC_10x6_Format
65        { 10, 8 }, // kASTC_10x8_Format
66        { 10, 10 }, // kASTC_10x10_Format
67        { 12, 10 }, // kASTC_12x10_Format
68        { 12, 12 }, // kASTC_12x12_Format
69    };
70
71    *dimX = kFormatDimensions[format].fBlockSizeX;
72    *dimY = kFormatDimensions[format].fBlockSizeY;
73}
74
75int GetCompressedDataSize(Format fmt, int width, int height) {
76    int dimX, dimY;
77    GetBlockDimensions(fmt, &dimX, &dimY, true);
78
79    int encodedBlockSize = 0;
80
81    switch (fmt) {
82        // These formats are 64 bits per 4x4 block.
83        case kLATC_Format:
84        case kR11_EAC_Format:
85        case kETC1_Format:
86            encodedBlockSize = 8;
87            break;
88
89        // This format is 128 bits.
90        case kASTC_4x4_Format:
91        case kASTC_5x4_Format:
92        case kASTC_5x5_Format:
93        case kASTC_6x5_Format:
94        case kASTC_6x6_Format:
95        case kASTC_8x5_Format:
96        case kASTC_8x6_Format:
97        case kASTC_8x8_Format:
98        case kASTC_10x5_Format:
99        case kASTC_10x6_Format:
100        case kASTC_10x8_Format:
101        case kASTC_10x10_Format:
102        case kASTC_12x10_Format:
103        case kASTC_12x12_Format:
104            encodedBlockSize = 16;
105            break;
106
107        default:
108            SkFAIL("Unknown compressed format!");
109            return -1;
110    }
111
112    if(((width % dimX) == 0) && ((height % dimY) == 0)) {
113        const int blocksX = width / dimX;
114        const int blocksY = height / dimY;
115
116        return blocksX * blocksY * encodedBlockSize;
117    }
118
119    return -1;
120}
121
122bool CompressBufferToFormat(uint8_t* dst, const uint8_t* src, SkColorType srcColorType,
123                            int width, int height, int rowBytes, Format format, bool opt) {
124    CompressionProc proc = NULL;
125    if (opt) {
126        proc = SkTextureCompressorGetPlatformProc(srcColorType, format);
127    }
128
129    if (NULL == proc) {
130        switch (srcColorType) {
131            case kAlpha_8_SkColorType:
132            {
133                switch (format) {
134                    case kLATC_Format:
135                        proc = CompressA8ToLATC;
136                        break;
137                    case kR11_EAC_Format:
138                        proc = CompressA8ToR11EAC;
139                        break;
140                    case kASTC_12x12_Format:
141                        proc = CompressA8To12x12ASTC;
142                        break;
143                    default:
144                        // Do nothing...
145                        break;
146                }
147            }
148            break;
149
150            case kRGB_565_SkColorType:
151            {
152                switch (format) {
153                    case kETC1_Format:
154                        proc = compress_etc1_565;
155                        break;
156                    default:
157                        // Do nothing...
158                        break;
159                }
160            }
161            break;
162
163            default:
164                // Do nothing...
165                break;
166        }
167    }
168
169    if (proc) {
170        return proc(dst, src, width, height, rowBytes);
171    }
172
173    return false;
174}
175
176SkData* CompressBitmapToFormat(const SkBitmap &bitmap, Format format) {
177    SkAutoLockPixels alp(bitmap);
178
179    int compressedDataSize = GetCompressedDataSize(format, bitmap.width(), bitmap.height());
180    if (compressedDataSize < 0) {
181        return NULL;
182    }
183
184    const uint8_t* src = reinterpret_cast<const uint8_t*>(bitmap.getPixels());
185    SkData* dst = SkData::NewUninitialized(compressedDataSize);
186
187    if (!CompressBufferToFormat((uint8_t*)dst->writable_data(), src, bitmap.colorType(),
188                                bitmap.width(), bitmap.height(), bitmap.rowBytes(), format)) {
189        dst->unref();
190        dst = NULL;
191    }
192    return dst;
193}
194
195SkBlitter* CreateBlitterForFormat(int width, int height, void* compressedBuffer,
196                                  SkTBlitterAllocator *allocator, Format format) {
197    switch(format) {
198        case kLATC_Format:
199            return CreateLATCBlitter(width, height, compressedBuffer, allocator);
200
201        case kR11_EAC_Format:
202            return CreateR11EACBlitter(width, height, compressedBuffer, allocator);
203
204        case kASTC_12x12_Format:
205            return CreateASTCBlitter(width, height, compressedBuffer, allocator);
206
207        default:
208            return NULL;
209    }
210
211    return NULL;
212}
213
214bool DecompressBufferFromFormat(uint8_t* dst, int dstRowBytes, const uint8_t* src,
215                                int width, int height, Format format) {
216    int dimX, dimY;
217    GetBlockDimensions(format, &dimX, &dimY, true);
218
219    if (width < 0 || ((width % dimX) != 0) || height < 0 || ((height % dimY) != 0)) {
220        return false;
221    }
222
223    switch(format) {
224        case kLATC_Format:
225            DecompressLATC(dst, dstRowBytes, src, width, height);
226            return true;
227
228        case kR11_EAC_Format:
229            DecompressR11EAC(dst, dstRowBytes, src, width, height);
230            return true;
231
232#ifndef SK_IGNORE_ETC1_SUPPORT
233        case kETC1_Format:
234            return 0 == etc1_decode_image(src, dst, width, height, 3, dstRowBytes);
235#endif
236
237        case kASTC_4x4_Format:
238        case kASTC_5x4_Format:
239        case kASTC_5x5_Format:
240        case kASTC_6x5_Format:
241        case kASTC_6x6_Format:
242        case kASTC_8x5_Format:
243        case kASTC_8x6_Format:
244        case kASTC_8x8_Format:
245        case kASTC_10x5_Format:
246        case kASTC_10x6_Format:
247        case kASTC_10x8_Format:
248        case kASTC_10x10_Format:
249        case kASTC_12x10_Format:
250        case kASTC_12x12_Format:
251            DecompressASTC(dst, dstRowBytes, src, width, height, dimX, dimY);
252            return true;
253
254        default:
255            // Do nothing...
256            break;
257    }
258
259    return false;
260}
261
262}  // namespace SkTextureCompressor
263