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
compress_etc1_565(uint8_t * dst,const uint8_t * src,int width,int height,size_t rowBytes)25 static bool compress_etc1_565(uint8_t* dst, const uint8_t* src,
26                               int width, int height, size_t rowBytes) {
27 #ifndef SK_IGNORE_ETC1_SUPPORT
28     return 0 == etc1_encode_image(src, width, height, 2, SkToInt(rowBytes), dst);
29 #else
30     return false;
31 #endif
32 }
33 
34 ////////////////////////////////////////////////////////////////////////////////
35 
36 namespace SkTextureCompressor {
37 
GetBlockDimensions(Format format,int * dimX,int * dimY,bool matchSpec)38 void 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 
GetCompressedDataSize(Format fmt,int width,int height)75 int 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 
CompressBufferToFormat(uint8_t * dst,const uint8_t * src,SkColorType srcColorType,int width,int height,size_t rowBytes,Format format,bool opt)122 bool CompressBufferToFormat(uint8_t* dst, const uint8_t* src, SkColorType srcColorType,
123                             int width, int height, size_t 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 
CompressBitmapToFormat(const SkBitmap & bitmap,Format format)176 SkData* 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 
CreateBlitterForFormat(int width,int height,void * compressedBuffer,SkTBlitterAllocator * allocator,Format format)195 SkBlitter* 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 
DecompressBufferFromFormat(uint8_t * dst,int dstRowBytes,const uint8_t * src,int width,int height,Format format)214 bool 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