Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(79)

Side by Side Diff: src/utils/SkTextureCompressor.cpp

Issue 325733004: Add texture compression utility (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Code review changes Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
10 #include "SkBitmap.h"
11 #include "SkData.h"
12 #include "SkEndian.h"
13
14 ////////////////////////////////////////////////////////////////////////////////
15 //
16 // LATC compressor
17 //
18 ////////////////////////////////////////////////////////////////////////////////
19
20 // Return the squared minimum error cost of approximating 'pixel' using the
21 // provided palette.
22 static uint16_t compute_error(uint16_t pixel, uint8_t palette[8]) {
23 uint16_t error = 65535;
24 for (int i = 0; i < 8; ++i) {
25 uint16_t diff = SkTAbsDiff(static_cast<uint16_t>(palette[i]), pixel);
26 error = SkMin32(diff, error);
27 }
28 return error*error;
29 }
30
31 // Compress LATC block. Each 4x4 block of pixels is decompressed by LATC from tw o
32 // values LUM0 and LUM1, and an index into the generated palette. LATC construct s
33 // a palette of eight colors from LUM0 and LUM1 using the algorithm:
34 //
35 // LUM0, if lum0 > lum1 and code(x,y) == 0
36 // LUM1, if lum0 > lum1 and code(x,y) == 1
37 // (6*LUM0+ LUM1)/7, if lum0 > lum1 and code(x,y) == 2
38 // (5*LUM0+2*LUM1)/7, if lum0 > lum1 and code(x,y) == 3
39 // (4*LUM0+3*LUM1)/7, if lum0 > lum1 and code(x,y) == 4
40 // (3*LUM0+4*LUM1)/7, if lum0 > lum1 and code(x,y) == 5
41 // (2*LUM0+5*LUM1)/7, if lum0 > lum1 and code(x,y) == 6
42 // ( LUM0+6*LUM1)/7, if lum0 > lum1 and code(x,y) == 7
43 //
44 // LUM0, if lum0 <= lum1 and code(x,y) == 0
45 // LUM1, if lum0 <= lum1 and code(x,y) == 1
46 // (4*LUM0+ LUM1)/5, if lum0 <= lum1 and code(x,y) == 2
47 // (3*LUM0+2*LUM1)/5, if lum0 <= lum1 and code(x,y) == 3
48 // (2*LUM0+3*LUM1)/5, if lum0 <= lum1 and code(x,y) == 4
49 // ( LUM0+4*LUM1)/5, if lum0 <= lum1 and code(x,y) == 5
50 // 0, if lum0 <= lum1 and code(x,y) == 6
51 // 255, if lum0 <= lum1 and code(x,y) == 7
52 //
53 // We compute the LATC palette using the following simple algorithm:
54 // 1. Choose the minimum and maximum values in the block as LUM0 and LUM1
55 // 2. Figure out which of the two possible palettes is better.
56
57 static uint64_t compress_latc_block(uint8_t block[16]) {
58 // Just do a simple min/max but choose from which of the
59 // two palettes is better
60 uint8_t maxVal = 0;
61 uint8_t minVal = 255;
62 for (int i = 0; i < 16; ++i) {
63 maxVal = SkMax32(maxVal, block[i]);
64 minVal = SkMin32(minVal, block[i]);
65 }
66
67 // Generate palettes
68 uint8_t palettes[2][8];
69
70 // Straight linear ramp
71 palettes[0][0] = maxVal;
72 palettes[0][1] = minVal;
73 for (int i = 1; i < 7; ++i) {
74 palettes[0][i+1] = ((7-i)*maxVal + i*minVal) / 7;
75 }
76
77 // Smaller linear ramp with min and max byte values at the end.
78 palettes[1][0] = minVal;
79 palettes[1][1] = maxVal;
80 for (int i = 1; i < 5; ++i) {
81 palettes[1][i+1] = ((5-i)*maxVal + i*minVal) / 5;
82 }
83 palettes[1][6] = 0;
84 palettes[1][7] = 255;
85
86 // Figure out which of the two is better
87 uint16_t error[2] = { 0, 0 };
88 for (int i = 0; i < 16; ++i) {
89 uint16_t pixel = static_cast<uint16_t>(block[i]);
90 error[0] += compute_error(pixel, palettes[0]);
91 error[1] += compute_error(pixel, palettes[1]);
92 }
93 uint8_t *palette = (error[0] > error[1]) ? palettes[1] : palettes[0];
94
95 // Compress the chosen palette
96 uint64_t result = 0;
97 result |= static_cast<uint64_t>(palette[0]);
98 result |= static_cast<uint64_t>(palette[1]) << 8;
99
100 uint64_t indices = 0;
101 for (int i = 15; i >= 0; --i) {
102 uint8_t pixel = block[i];
103
104 // The palette is selected in the loop above. This loop
105 // is to determine what index into the palette to store for
106 // each pixel.
107 uint8_t minError = SkTAbsDiff(pixel, palette[0]);
108 uint8_t minErrorIndex = 0;
109 for (int j = 1; j < 8; ++j) {
110 uint8_t error = SkTAbsDiff(pixel, palette[j]);
111 if (error < minError) {
112 minError = error;
113 minErrorIndex = j;
114 }
115 }
116 SkASSERT(minErrorIndex < 8);
117
118 indices <<= 3;
119 indices |= minErrorIndex;
120 }
121 result |= indices << 16;
122
123 // We assume everything is little endian, if it's not then make it so.
124 return SkEndian_SwapLE64(result);
125 }
126
127 static SkData *compress_a8_to_latc(const SkBitmap &bm) {
128 // LATC compressed texels down into square 4x4 blocks
129 static const int kLATCBlockSize = 4;
130
131 // Make sure that our data is well-formed enough to be
132 // considered for LATC compression
133 if (bm.width() == 0 || bm.height() == 0 ||
134 (bm.width() % kLATCBlockSize) != 0 ||
135 (bm.height() % kLATCBlockSize) != 0 ||
136 (bm.config() != SkBitmap::kA8_Config)) {
scroggo 2014/06/09 20:54:18 Use colorType() instead of config(). We are removi
krajcevski 2014/06/09 21:06:02 Done.
137 return NULL;
138 }
139
140 // The LATC format is 64 bits per 4x4 block.
141 static const int kLATCEncodedBlockSize = 8;
142
143 int blocksX = bm.width() / kLATCBlockSize;
144 int blocksY = bm.height() / kLATCBlockSize;
145
146 int compressedDataSize = blocksX * blocksY * kLATCEncodedBlockSize;
147 uint64_t* dst = reinterpret_cast<uint64_t*>(sk_malloc_throw(compressedDataSi ze));
148
149 uint8_t block[16];
150 const uint8_t* row = reinterpret_cast<const uint8_t*>(bm.getPixels());
151 uint64_t* encPtr = dst;
152 for (int y = 0; y < blocksY; ++y) {
153 for (int x = 0; x < blocksX; ++x) {
154 memcpy(block, row + (kLATCBlockSize * x), 4);
155 memcpy(block + 4, row + bm.rowBytes() + (kLATCBlockSize * x), 4);
156 memcpy(block + 8, row + 2*bm.rowBytes() + (kLATCBlockSize * x), 4);
157 memcpy(block + 12, row + 3*bm.rowBytes() + (kLATCBlockSize * x), 4);
158
159 *encPtr = compress_latc_block(block);
160 ++encPtr;
161 }
162 row += kLATCBlockSize * bm.rowBytes();
163 }
164
165 return SkData::NewFromMalloc(dst, compressedDataSize);
166 }
167
168 ////////////////////////////////////////////////////////////////////////////////
169
170 namespace SkTextureCompressor {
171
172 typedef SkData *(*CompressBitmapProc)(const SkBitmap &bitmap);
173
174 SkData *CompressBitmapToConfig(const SkBitmap &bitmap, Format config) {
scroggo 2014/06/09 20:54:18 Shouldn't this be ToFormat, as in the header file?
krajcevski 2014/06/09 21:06:02 Yes, whoops. Done.
175 CompressBitmapProc kProcMap[SkBitmap::kConfigCount][kFormatCnt];
176 memset(kProcMap, 0, sizeof(kProcMap));
177
178 // Map available bitmap configs to compression functions
179 kProcMap[SkBitmap::kA8_Config][kLATC_Format] = compress_a8_to_latc;
180
181 CompressBitmapProc proc = kProcMap[bitmap.config()][config];
182 if (NULL != proc) {
183 return proc(bitmap);
184 }
185
186 return NULL;
187 }
188
189 } // namespace SkTextureCompressor
OLDNEW
« src/utils/SkTextureCompressor.h ('K') | « src/utils/SkTextureCompressor.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698