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

Side by Side Diff: src/utils/SkTextureCompressor_Blitter.h

Issue 421593004: Generalize compressed blitter into its own templated class (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Sync Created 6 years, 4 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
« no previous file with comments | « gyp/utils.gypi ('k') | src/utils/SkTextureCompressor_R11EAC.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 #ifndef SkTextureCompressor_Blitter_DEFINED
9 #define SkTextureCompressor_Blitter_DEFINED
10
11 #include "SkTypes.h"
12 #include "SkBlitter.h"
13
14 namespace SkTextureCompressor {
15
16 // The function used to compress an A8 block. This function is expected to be
17 // used as a template argument to SkCompressedAlphaBlitter. The layout of the
18 // block is also expected to be in column-major order.
19 typedef void (*CompressA8Proc)(uint8_t* dst, const uint8_t block[]);
20
21 // This class implements a blitter that blits directly into a buffer that will
22 // be used as an compressed alpha texture. We compute this buffer by
23 // buffering scan lines and then outputting them all at once. The number of
24 // scan lines buffered is controlled by kBlockSize
25 template<int BlockDim, int EncodedBlockSize, CompressA8Proc CompressionProc>
26 class SkTCompressedAlphaBlitter : public SkBlitter {
27 public:
28 SkTCompressedAlphaBlitter(int width, int height, void *compressedBuffer)
29 // 0x7FFE is one minus the largest positive 16-bit int. We use it for
30 // debugging to make sure that we're properly setting the nextX distance
31 // in flushRuns().
32 : kLongestRun(0x7FFE), kZeroAlpha(0)
33 , fNextRun(0)
34 , fWidth(width)
35 , fHeight(height)
36 , fBuffer(compressedBuffer)
37 {
38 SkASSERT((width % BlockDim) == 0);
39 SkASSERT((height % BlockDim) == 0);
40 }
41
42 virtual ~SkTCompressedAlphaBlitter() { this->flushRuns(); }
43
44 // Blit a horizontal run of one or more pixels.
45 virtual void blitH(int x, int y, int width) SK_OVERRIDE {
46 // This function is intended to be called from any standard RGB
47 // buffer, so we should never encounter it. However, if some code
48 // path does end up here, then this needs to be investigated.
49 SkFAIL("Not implemented!");
50 }
51
52 // Blit a horizontal run of antialiased pixels; runs[] is a *sparse*
53 // zero-terminated run-length encoding of spans of constant alpha values.
54 virtual void blitAntiH(int x, int y,
55 const SkAlpha antialias[],
56 const int16_t runs[]) SK_OVERRIDE {
57 // Make sure that the new row to blit is either the first
58 // row that we're blitting, or it's exactly the next scan row
59 // since the last row that we blit. This is to ensure that when
60 // we go to flush the runs, that they are all the same four
61 // runs.
62 if (fNextRun > 0 &&
63 ((x != fBufferedRuns[fNextRun-1].fX) ||
64 (y-1 != fBufferedRuns[fNextRun-1].fY))) {
65 this->flushRuns();
66 }
67
68 // Align the rows to a block boundary. If we receive rows that
69 // are not on a block boundary, then fill in the preceding runs
70 // with zeros. We do this by producing a single RLE that says
71 // that we have 0x7FFE pixels of zero (0x7FFE = 32766).
72 const int row = BlockDim * (y / BlockDim);
73 while ((row + fNextRun) < y) {
74 fBufferedRuns[fNextRun].fAlphas = &kZeroAlpha;
75 fBufferedRuns[fNextRun].fRuns = &kLongestRun;
76 fBufferedRuns[fNextRun].fX = 0;
77 fBufferedRuns[fNextRun].fY = row + fNextRun;
78 ++fNextRun;
79 }
80
81 // Make sure that our assumptions aren't violated...
82 SkASSERT(fNextRun == (y % BlockDim));
83 SkASSERT(fNextRun == 0 || fBufferedRuns[fNextRun - 1].fY < y);
84
85 // Set the values of the next run
86 fBufferedRuns[fNextRun].fAlphas = antialias;
87 fBufferedRuns[fNextRun].fRuns = runs;
88 fBufferedRuns[fNextRun].fX = x;
89 fBufferedRuns[fNextRun].fY = y;
90
91 // If we've output a block of scanlines in a row that don't violate our
92 // assumptions, then it's time to flush them...
93 if (BlockDim == ++fNextRun) {
94 this->flushRuns();
95 }
96 }
97
98 // Blit a vertical run of pixels with a constant alpha value.
99 virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE {
100 // This function is currently not implemented. It is not explicitly
101 // required by the contract, but if at some time a code path runs into
102 // this function (which is entirely possible), it needs to be implemente d.
103 //
104 // TODO (krajcevski):
105 // This function will be most easily implemented in one of two ways:
106 // 1. Buffer each vertical column value and then construct a list
107 // of alpha values and output all of the blocks at once. This only
108 // requires a write to the compressed buffer
109 // 2. Replace the indices of each block with the proper indices based
110 // on the alpha value. This requires a read and write of the compress ed
111 // buffer, but much less overhead.
112 SkFAIL("Not implemented!");
113 }
114
115 // Blit a solid rectangle one or more pixels wide.
116 virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
117 // Analogous to blitRow, this function is intended for RGB targets
118 // and should never be called by this blitter. Any calls to this functio n
119 // are probably a bug and should be investigated.
120 SkFAIL("Not implemented!");
121 }
122
123 // Blit a rectangle with one alpha-blended column on the left,
124 // width (zero or more) opaque pixels, and one alpha-blended column
125 // on the right. The result will always be at least two pixels wide.
126 virtual void blitAntiRect(int x, int y, int width, int height,
127 SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE {
128 // This function is currently not implemented. It is not explicitly
129 // required by the contract, but if at some time a code path runs into
130 // this function (which is entirely possible), it needs to be implemente d.
131 //
132 // TODO (krajcevski):
133 // This function will be most easily implemented as follows:
134 // 1. If width/height are smaller than a block, then update the
135 // indices of the affected blocks.
136 // 2. If width/height are larger than a block, then construct a 9-patch
137 // of block encodings that represent the rectangle, and write them
138 // to the compressed buffer as necessary. Whether or not the blocks
139 // are overwritten by zeros or just their indices are updated is up
140 // to debate.
141 SkFAIL("Not implemented!");
142 }
143
144 // Blit a pattern of pixels defined by a rectangle-clipped mask;
145 // typically used for text.
146 virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE {
147 // This function is currently not implemented. It is not explicitly
148 // required by the contract, but if at some time a code path runs into
149 // this function (which is entirely possible), it needs to be implemente d.
150 //
151 // TODO (krajcevski):
152 // This function will be most easily implemented in the same way as
153 // blitAntiRect above.
154 SkFAIL("Not implemented!");
155 }
156
157 // If the blitter just sets a single value for each pixel, return the
158 // bitmap it draws into, and assign value. If not, return NULL and ignore
159 // the value parameter.
160 virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE {
161 return NULL;
162 }
163
164 /**
165 * Compressed texture blitters only really work correctly if they get
166 * BlockDim rows at a time. That being said, this blitter tries it's best
167 * to preserve semantics if blitAntiH doesn't get called in too many
168 * weird ways...
169 */
170 virtual int requestRowsPreserved() const { return BlockDim; }
171
172 private:
173 static const int kPixelsPerBlock = BlockDim * BlockDim;
174
175 // The longest possible run of pixels that this blitter will receive.
176 // This is initialized in the constructor to 0x7FFE, which is one less
177 // than the largest positive 16-bit integer. We make sure that it's one
178 // less for debugging purposes. We also don't make this variable static
179 // in order to make sure that we can construct a valid pointer to it.
180 const int16_t kLongestRun;
181
182 // Usually used in conjunction with kLongestRun. This is initialized to
183 // zero.
184 const SkAlpha kZeroAlpha;
185
186 // This is the information that we buffer whenever we're asked to blit
187 // a row with this blitter.
188 struct BufferedRun {
189 const SkAlpha* fAlphas;
190 const int16_t* fRuns;
191 int fX, fY;
192 } fBufferedRuns[BlockDim];
193
194 // The next row [0, BlockDim) that we need to blit.
195 int fNextRun;
196
197 // The width and height of the image that we're blitting
198 const int fWidth;
199 const int fHeight;
200
201 // The compressed buffer that we're blitting into. It is assumed that the bu ffer
202 // is large enough to store a compressed image of size fWidth*fHeight.
203 void* const fBuffer;
204
205 // Various utility functions
206 int blocksWide() const { return fWidth / BlockDim; }
207 int blocksTall() const { return fHeight / BlockDim; }
208 int totalBlocks() const { return (fWidth * fHeight) / kPixelsPerBlock; }
209
210 // Returns the block index for the block containing pixel (x, y). Block
211 // indices start at zero and proceed in raster order.
212 int getBlockOffset(int x, int y) const {
213 SkASSERT(x < fWidth);
214 SkASSERT(y < fHeight);
215 const int blockCol = x / BlockDim;
216 const int blockRow = y / BlockDim;
217 return blockRow * this->blocksWide() + blockCol;
218 }
219
220 // Returns a pointer to the block containing pixel (x, y)
221 uint8_t *getBlock(int x, int y) const {
222 uint8_t* ptr = reinterpret_cast<uint8_t*>(fBuffer);
223 return ptr + EncodedBlockSize*this->getBlockOffset(x, y);
224 }
225
226 // Updates the block whose columns are stored in block. curAlphai is expecte d
227 // to store the alpha values that will be placed within each of the columns in
228 // the range [col, col+colsLeft).
229 typedef uint32_t Column[BlockDim/4];
230 typedef uint32_t Block[BlockDim][BlockDim/4];
231 inline void updateBlockColumns(Block block, const int col,
232 const int colsLeft, const Column curAlphai) {
233 SkASSERT(NULL != block);
234 SkASSERT(col + colsLeft <= 4);
235
236 for (int i = col; i < (col + colsLeft); ++i) {
237 memcpy(block[i], curAlphai, sizeof(Column));
238 }
239 }
240
241 // The following function writes the buffered runs to compressed blocks.
242 // If fNextRun < BlockDim, then we fill the runs that we haven't buffered wi th
243 // the constant zero buffer.
244 void flushRuns() {
245 // If we don't have any runs, then just return.
246 if (0 == fNextRun) {
247 return;
248 }
249
250 #ifndef NDEBUG
251 // Make sure that if we have any runs, they all match
252 for (int i = 1; i < fNextRun; ++i) {
253 SkASSERT(fBufferedRuns[i].fY == fBufferedRuns[i-1].fY + 1);
254 SkASSERT(fBufferedRuns[i].fX == fBufferedRuns[i-1].fX);
255 }
256 #endif
257
258 // If we don't have as many runs as we have rows, fill in the remaining
259 // runs with constant zeros.
260 for (int i = fNextRun; i < BlockDim; ++i) {
261 fBufferedRuns[i].fY = fBufferedRuns[0].fY + i;
262 fBufferedRuns[i].fX = fBufferedRuns[0].fX;
263 fBufferedRuns[i].fAlphas = &kZeroAlpha;
264 fBufferedRuns[i].fRuns = &kLongestRun;
265 }
266
267 // Make sure that our assumptions aren't violated.
268 SkASSERT(fNextRun > 0 && fNextRun <= BlockDim);
269 SkASSERT((fBufferedRuns[0].fY % BlockDim) == 0);
270
271 // The following logic walks BlockDim rows at a time and outputs compres sed
272 // blocks to the buffer passed into the constructor.
273 // We do the following:
274 //
275 // c1 c2 c3 c4
276 // --------------------------------------------------------------------- --
277 // ... | | | | | ----> fBufferedRuns[0]
278 // --------------------------------------------------------------------- --
279 // ... | | | | | ----> fBufferedRuns[1]
280 // --------------------------------------------------------------------- --
281 // ... | | | | | ----> fBufferedRuns[2]
282 // --------------------------------------------------------------------- --
283 // ... | | | | | ----> fBufferedRuns[3]
284 // --------------------------------------------------------------------- --
285 //
286 // curX -- the macro X value that we've gotten to.
287 // c[BlockDim] -- the buffers that represent the columns of the current block
288 // that we're operating on
289 // curAlphaColumn -- buffer containing the column of alpha values from f BufferedRuns.
290 // nextX -- for each run, the next point at which we need to update curA lphaColumn
291 // after the value of curX.
292 // finalX -- the minimum of all the nextX values.
293 //
294 // curX advances to finalX outputting any blocks that it passes along
295 // the way. Since finalX will not change when we reach the end of a
296 // run, the termination criteria will be whenever curX == finalX at the
297 // end of a loop.
298
299 // Setup:
300 Block block;
301 sk_bzero(block, sizeof(block));
302
303 Column curAlphaColumn;
304 sk_bzero(curAlphaColumn, sizeof(curAlphaColumn));
305
306 SkAlpha *curAlpha = reinterpret_cast<SkAlpha*>(&curAlphaColumn);
307
308 int nextX[BlockDim];
309 for (int i = 0; i < BlockDim; ++i) {
310 nextX[i] = 0x7FFFFF;
311 }
312
313 uint8_t* outPtr = this->getBlock(fBufferedRuns[0].fX, fBufferedRuns[0].f Y);
314
315 // Populate the first set of runs and figure out how far we need to
316 // advance on the first step
317 int curX = 0;
318 int finalX = 0xFFFFF;
319 for (int i = 0; i < BlockDim; ++i) {
320 nextX[i] = *(fBufferedRuns[i].fRuns);
321 curAlpha[i] = *(fBufferedRuns[i].fAlphas);
322
323 finalX = SkMin32(nextX[i], finalX);
324 }
325
326 // Make sure that we have a valid right-bound X value
327 SkASSERT(finalX < 0xFFFFF);
328
329 // Run the blitter...
330 while (curX != finalX) {
331 SkASSERT(finalX >= curX);
332
333 // Do we need to populate the rest of the block?
334 if ((finalX - (BlockDim*(curX / BlockDim))) >= BlockDim) {
335 const int col = curX % BlockDim;
336 const int colsLeft = BlockDim - col;
337 SkASSERT(curX + colsLeft <= finalX);
338
339 this->updateBlockColumns(block, col, colsLeft, curAlphaColumn);
340
341 // Write this block
342 CompressionProc(outPtr, reinterpret_cast<uint8_t*>(block));
343 outPtr += EncodedBlockSize;
344 curX += colsLeft;
345 }
346
347 // If we can advance even further, then just keep memsetting the blo ck
348 if ((finalX - curX) >= BlockDim) {
349 SkASSERT((curX % BlockDim) == 0);
350
351 const int col = 0;
352 const int colsLeft = BlockDim;
353
354 this->updateBlockColumns(block, col, colsLeft, curAlphaColumn);
355
356 // While we can keep advancing, just keep writing the block.
357 uint8_t lastBlock[EncodedBlockSize];
358 CompressionProc(lastBlock, reinterpret_cast<uint8_t*>(block));
359 while((finalX - curX) >= BlockDim) {
360 memcpy(outPtr, lastBlock, EncodedBlockSize);
361 outPtr += EncodedBlockSize;
362 curX += BlockDim;
363 }
364 }
365
366 // If we haven't advanced within the block then do so.
367 if (curX < finalX) {
368 const int col = curX % BlockDim;
369 const int colsLeft = finalX - curX;
370
371 this->updateBlockColumns(block, col, colsLeft, curAlphaColumn);
372 curX += colsLeft;
373 }
374
375 SkASSERT(curX == finalX);
376
377 // Figure out what the next advancement is...
378 for (int i = 0; i < BlockDim; ++i) {
379 if (nextX[i] == finalX) {
380 const int16_t run = *(fBufferedRuns[i].fRuns);
381 fBufferedRuns[i].fRuns += run;
382 fBufferedRuns[i].fAlphas += run;
383 curAlpha[i] = *(fBufferedRuns[i].fAlphas);
384 nextX[i] += *(fBufferedRuns[i].fRuns);
385 }
386 }
387
388 finalX = 0xFFFFF;
389 for (int i = 0; i < BlockDim; ++i) {
390 finalX = SkMin32(nextX[i], finalX);
391 }
392 }
393
394 // If we didn't land on a block boundary, output the block...
395 if ((curX % BlockDim) > 1) {
396 CompressionProc(outPtr, reinterpret_cast<uint8_t*>(block));
397 }
398
399 fNextRun = 0;
400 }
401 };
402
403 } // namespace SkTextureCompressor
404
405 #endif // SkTextureCompressor_Blitter_DEFINED
OLDNEW
« no previous file with comments | « gyp/utils.gypi ('k') | src/utils/SkTextureCompressor_R11EAC.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698