OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2014 Google Inc. | 2 * Copyright 2014 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #ifndef SkTextureCompressor_Blitter_DEFINED | 8 #ifndef SkTextureCompressor_Blitter_DEFINED |
9 #define SkTextureCompressor_Blitter_DEFINED | 9 #define SkTextureCompressor_Blitter_DEFINED |
10 | 10 |
11 #include "SkTypes.h" | 11 #include "SkTypes.h" |
12 #include "SkBlitter.h" | 12 #include "SkBlitter.h" |
13 | 13 |
14 namespace SkTextureCompressor { | 14 namespace SkTextureCompressor { |
15 | 15 |
16 // Ostensibly, SkBlitter::BlitRect is supposed to set a rect of pixels to full | |
17 // alpha. This becomes problematic when using compressed texture blitters, since | |
18 // the rect rarely falls along block boundaries. The proper way to handle this i s | |
19 // to update the compressed encoding of a block by resetting the proper paramete rs | |
20 // (and even recompressing the block) where a rect falls inbetween block boundar ies. | |
21 // PEDANTIC_BLIT_RECT attempts to do this by requiring the struct passed to | |
22 // SkTCompressedAlphaBlitter to implement an UpdateBlock function call. | |
23 // | |
24 // However, the way that BlitRect gets used almost exclusively is to bracket inv erse | |
25 // fills for paths. In other words, the top few rows and bottom few rows of a pa th | |
26 // that's getting inverse filled are called using blitRect. The rest are called using | |
27 // the standard blitAntiH. As a result, we can just call blitAntiH with a faux RLE | |
28 // of full alpha values, and then check in our flush() call that we don't run of f the | |
29 // edge of the buffer. This is why we do not need this flag to be turned on. | |
30 #define PEDANTIC_BLIT_RECT 1 | |
31 | |
16 // This class implements a blitter that blits directly into a buffer that will | 32 // This class implements a blitter that blits directly into a buffer that will |
17 // be used as an compressed alpha texture. We compute this buffer by | 33 // be used as an compressed alpha texture. We compute this buffer by |
18 // buffering scan lines and then outputting them all at once. The number of | 34 // buffering scan lines and then outputting them all at once. The number of |
19 // scan lines buffered is controlled by kBlockSize | 35 // scan lines buffered is controlled by kBlockSize |
20 // | 36 // |
21 // The CompressorType is a struct with a bunch of static methods that provides | 37 // The CompressorType is a struct with a bunch of static methods that provides |
22 // the specialized compression functionality of the blitter. A complete Compress orType | 38 // the specialized compression functionality of the blitter. A complete Compress orType |
23 // will implement the following static functions; | 39 // will implement the following static functions; |
24 // | 40 // |
25 // struct CompressorType { | 41 // struct CompressorType { |
26 // // The function used to compress an A8 block. The layout of the | 42 // // The function used to compress an A8 block. The layout of the |
27 // // block is also expected to be in column-major order. | 43 // // block is also expected to be in column-major order. |
28 // static void CompressA8Vertical(uint8_t* dst, const uint8_t block[]); | 44 // static void CompressA8Vertical(uint8_t* dst, const uint8_t block[]); |
29 // | 45 // |
30 // // The function used to compress an A8 block. The layout of the | 46 // // The function used to compress an A8 block. The layout of the |
31 // // block is also expected to be in row-major order. | 47 // // block is also expected to be in row-major order. |
32 // static void CompressA8Horizontal(uint8_t* dst, const uint8_t block[]); | 48 // static void CompressA8Horizontal(uint8_t* dst, const uint8_t* src, int sr cRowBytes); |
33 // | 49 // |
50 #if PEDANTIC_BLIT_RECT | |
34 // // The function used to update an already compressed block. This will | 51 // // The function used to update an already compressed block. This will |
35 // // most likely be implementation dependent. | 52 // // most likely be implementation dependent. The mask variable will have |
36 // static void UpdateBlock(uint8_t* dst, const uint8_t* src); | 53 // // 0xFF in positions where the block should be updated and 0 in positions |
54 // // where it shouldn't. src contains an uncompressed buffer of pixels. | |
55 // static void UpdateBlock(uint8_t* dst, const uint8_t* src, int srcRowBytes , | |
56 // const uint8_t* mask); | |
57 #endif | |
37 // }; | 58 // }; |
38 // | |
39 template<int BlockDim, int EncodedBlockSize, typename CompressorType> | 59 template<int BlockDim, int EncodedBlockSize, typename CompressorType> |
40 class SkTCompressedAlphaBlitter : public SkBlitter { | 60 class SkTCompressedAlphaBlitter : public SkBlitter { |
41 public: | 61 public: |
42 SkTCompressedAlphaBlitter(int width, int height, void *compressedBuffer) | 62 SkTCompressedAlphaBlitter(int width, int height, void *compressedBuffer) |
43 // 0x7FFE is one minus the largest positive 16-bit int. We use it for | 63 // 0x7FFE is one minus the largest positive 16-bit int. We use it for |
44 // debugging to make sure that we're properly setting the nextX distance | 64 // debugging to make sure that we're properly setting the nextX distance |
45 // in flushRuns(). | 65 // in flushRuns(). |
46 #ifdef SK_DEBUG | 66 #ifdef SK_DEBUG |
robertphillips
2014/08/11 19:16:22
Mv this ',' down to start of next line ?
krajcevski
2014/08/11 20:08:24
Done.
| |
47 : fBlitMaskCalled(false), | 67 : fCalledOnceWithNonzeroY(false), |
68 fBlitMaskCalled(false), | |
48 #else | 69 #else |
49 : | 70 : |
50 #endif | 71 #endif |
51 kLongestRun(0x7FFE), kZeroAlpha(0) | 72 kLongestRun(0x7FFE), kZeroAlpha(0) |
52 , fNextRun(0) | 73 , fNextRun(0) |
53 , fWidth(width) | 74 , fWidth(width) |
54 , fHeight(height) | 75 , fHeight(height) |
55 , fBuffer(compressedBuffer) | 76 , fBuffer(compressedBuffer) |
56 { | 77 { |
57 SkASSERT((width % BlockDim) == 0); | 78 SkASSERT((width % BlockDim) == 0); |
58 SkASSERT((height % BlockDim) == 0); | 79 SkASSERT((height % BlockDim) == 0); |
59 } | 80 } |
60 | 81 |
61 virtual ~SkTCompressedAlphaBlitter() { this->flushRuns(); } | 82 virtual ~SkTCompressedAlphaBlitter() { this->flushRuns(); } |
62 | 83 |
63 // Blit a horizontal run of one or more pixels. | 84 // Blit a horizontal run of one or more pixels. |
64 virtual void blitH(int x, int y, int width) SK_OVERRIDE { | 85 virtual void blitH(int x, int y, int width) SK_OVERRIDE { |
65 // This function is intended to be called from any standard RGB | 86 // This function is intended to be called from any standard RGB |
66 // buffer, so we should never encounter it. However, if some code | 87 // buffer, so we should never encounter it. However, if some code |
67 // path does end up here, then this needs to be investigated. | 88 // path does end up here, then this needs to be investigated. |
68 SkFAIL("Not implemented!"); | 89 SkFAIL("Not implemented!"); |
69 } | 90 } |
70 | 91 |
71 // Blit a horizontal run of antialiased pixels; runs[] is a *sparse* | 92 // Blit a horizontal run of antialiased pixels; runs[] is a *sparse* |
72 // zero-terminated run-length encoding of spans of constant alpha values. | 93 // zero-terminated run-length encoding of spans of constant alpha values. |
73 virtual void blitAntiH(int x, int y, | 94 virtual void blitAntiH(int x, int y, |
74 const SkAlpha antialias[], | 95 const SkAlpha antialias[], |
75 const int16_t runs[]) SK_OVERRIDE { | 96 const int16_t runs[]) SK_OVERRIDE { |
97 SkASSERT(0 == x); | |
98 | |
76 // Make sure that the new row to blit is either the first | 99 // Make sure that the new row to blit is either the first |
77 // row that we're blitting, or it's exactly the next scan row | 100 // row that we're blitting, or it's exactly the next scan row |
78 // since the last row that we blit. This is to ensure that when | 101 // since the last row that we blit. This is to ensure that when |
79 // we go to flush the runs, that they are all the same four | 102 // we go to flush the runs, that they are all the same four |
80 // runs. | 103 // runs. |
81 if (fNextRun > 0 && | 104 if (fNextRun > 0 && |
82 ((x != fBufferedRuns[fNextRun-1].fX) || | 105 ((x != fBufferedRuns[fNextRun-1].fX) || |
83 (y-1 != fBufferedRuns[fNextRun-1].fY))) { | 106 (y-1 != fBufferedRuns[fNextRun-1].fY))) { |
84 this->flushRuns(); | 107 this->flushRuns(); |
85 } | 108 } |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
124 // This function will be most easily implemented in one of two ways: | 147 // This function will be most easily implemented in one of two ways: |
125 // 1. Buffer each vertical column value and then construct a list | 148 // 1. Buffer each vertical column value and then construct a list |
126 // of alpha values and output all of the blocks at once. This only | 149 // of alpha values and output all of the blocks at once. This only |
127 // requires a write to the compressed buffer | 150 // requires a write to the compressed buffer |
128 // 2. Replace the indices of each block with the proper indices based | 151 // 2. Replace the indices of each block with the proper indices based |
129 // on the alpha value. This requires a read and write of the compress ed | 152 // on the alpha value. This requires a read and write of the compress ed |
130 // buffer, but much less overhead. | 153 // buffer, but much less overhead. |
131 SkFAIL("Not implemented!"); | 154 SkFAIL("Not implemented!"); |
132 } | 155 } |
133 | 156 |
134 // Blit a solid rectangle one or more pixels wide. | 157 // Blit a solid rectangle one or more pixels wide. It's assumed that blitRec t |
158 // is called as a way to bracket blitAntiH where above and below the path th e | |
159 // called path just needs a solid rectangle to fill in the mask. | |
160 #ifdef SK_DEBUG | |
161 bool fCalledOnceWithNonzeroY; | |
162 #endif | |
135 virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE { | 163 virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE { |
136 // Analogous to blitRow, this function is intended for RGB targets | 164 |
137 // and should never be called by this blitter. Any calls to this functio n | 165 // Assumptions: |
138 // are probably a bug and should be investigated. | 166 SkASSERT(0 == x); |
139 SkFAIL("Not implemented!"); | 167 SkASSERT(width <= fWidth); |
168 | |
169 // Make sure that we're only ever bracketing calls to blitAntiH. | |
170 SkASSERT((0 == y) || (!fCalledOnceWithNonzeroY && (fCalledOnceWithNonzer oY = true))); | |
171 | |
172 #if !(PEDANTIC_BLIT_RECT) | |
173 for (int i = 0; i < height; ++i) { | |
174 const SkAlpha kFullAlpha = 0xFF; | |
175 this->blitAntiH(x, y+i, &kFullAlpha, &kLongestRun); | |
176 } | |
177 #else | |
178 const int startBlockX = (x / BlockDim) * BlockDim; | |
179 const int startBlockY = (y / BlockDim) * BlockDim; | |
180 | |
181 const int endBlockX = ((x + width) / BlockDim) * BlockDim; | |
182 const int endBlockY = ((y + height) / BlockDim) * BlockDim; | |
183 | |
184 // If start and end are the same, then we only need to update a single b lock... | |
185 if (startBlockY == endBlockY && startBlockX == endBlockX) { | |
186 uint8_t mask[BlockDim*BlockDim]; | |
187 memset(mask, 0, sizeof(mask)); | |
188 | |
189 const int xoff = x - startBlockX; | |
190 SkASSERT((xoff + width) <= BlockDim); | |
191 | |
192 const int yoff = y - startBlockY; | |
193 SkASSERT((yoff + height) <= BlockDim); | |
194 | |
195 for (int j = 0; j < height; ++j) { | |
196 memset(mask + (j + yoff)*BlockDim + xoff, 0xFF, width); | |
197 } | |
198 | |
199 uint8_t* dst = this->getBlock(startBlockX, startBlockY); | |
200 CompressorType::UpdateBlock(dst, mask, BlockDim, mask); | |
201 | |
202 // If start and end are the same in the y dimension, then we can freely update an | |
203 // entire row of blocks... | |
204 } else if (startBlockY == endBlockY) { | |
205 | |
206 this->updateBlockRow(x, y, width, height, startBlockY, startBlockX, endBlockX); | |
207 | |
208 // Similarly, if the start and end are in the same column, then we can j ust update | |
209 // an entire column of blocks... | |
210 } else if (startBlockX == endBlockX) { | |
211 | |
212 this->updateBlockCol(x, y, width, height, startBlockX, startBlockY, endBlockY); | |
213 | |
214 // Otherwise, the rect spans a non-trivial region of blocks, and we have to construct | |
215 // a kind of 9-patch to update each of the pieces of the rect. The top a nd bottom | |
216 // rows are updated using updateBlockRow, and the left and right columns are updated | |
217 // using updateBlockColumn. Anything in the middle is simply memset to a n opaque block | |
218 // encoding. | |
219 } else { | |
220 | |
221 const int innerStartBlockX = startBlockX + BlockDim; | |
222 const int innerStartBlockY = startBlockY + BlockDim; | |
223 | |
224 // Blit top row | |
225 const int topRowHeight = innerStartBlockY - y; | |
226 this->updateBlockRow(x, y, width, topRowHeight, startBlockY, | |
227 startBlockX, endBlockX); | |
228 | |
229 // Advance y | |
230 y += topRowHeight; | |
231 height -= topRowHeight; | |
232 | |
233 // Blit middle | |
234 if (endBlockY > innerStartBlockY) { | |
235 | |
236 // Update left row | |
237 this->updateBlockCol(x, y, innerStartBlockX - x, endBlockY, star tBlockY, | |
238 startBlockX, innerStartBlockX); | |
239 | |
240 // Update the middle with an opaque encoding... | |
241 uint8_t mask[BlockDim*BlockDim]; | |
242 memset(mask, 0xFF, sizeof(mask)); | |
243 | |
244 uint8_t opaqueEncoding[EncodedBlockSize]; | |
245 CompressorType::CompressA8Horizontal(opaqueEncoding, mask, Block Dim); | |
246 | |
247 for (int j = innerStartBlockY; j < endBlockY; j += BlockDim) { | |
248 uint8_t* opaqueDst = this->getBlock(innerStartBlockX, j); | |
249 for (int i = innerStartBlockX; i < endBlockX; i += BlockDim) { | |
250 memcpy(opaqueDst, opaqueEncoding, EncodedBlockSize); | |
251 opaqueDst += EncodedBlockSize; | |
252 } | |
253 } | |
254 | |
255 // If we need to update the right column, do that too | |
256 if (x + width > endBlockX) { | |
257 this->updateBlockCol(endBlockX, y, x + width - endBlockX, en dBlockY, | |
258 endBlockX, innerStartBlockY, endBlockY) ; | |
259 } | |
260 | |
261 // Advance y | |
262 height = y + height - endBlockY; | |
263 y = endBlockY; | |
264 } | |
265 | |
266 // If we need to update the last row, then do that, too. | |
267 if (height > 0) { | |
268 this->updateBlockRow(x, y, width, height, endBlockY, | |
269 startBlockX, endBlockX); | |
270 } | |
271 } | |
272 #endif | |
140 } | 273 } |
141 | 274 |
142 // Blit a rectangle with one alpha-blended column on the left, | 275 // Blit a rectangle with one alpha-blended column on the left, |
143 // width (zero or more) opaque pixels, and one alpha-blended column | 276 // width (zero or more) opaque pixels, and one alpha-blended column |
144 // on the right. The result will always be at least two pixels wide. | 277 // on the right. The result will always be at least two pixels wide. |
145 virtual void blitAntiRect(int x, int y, int width, int height, | 278 virtual void blitAntiRect(int x, int y, int width, int height, |
146 SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE { | 279 SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE { |
147 // This function is currently not implemented. It is not explicitly | 280 // 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 | 281 // 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. | 282 // this function (which is entirely possible), it needs to be implemente d. |
(...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
393 for (int i = 0; i < BlockDim; ++i) { | 526 for (int i = 0; i < BlockDim; ++i) { |
394 nextX[i] = *(fBufferedRuns[i].fRuns); | 527 nextX[i] = *(fBufferedRuns[i].fRuns); |
395 curAlpha[i] = *(fBufferedRuns[i].fAlphas); | 528 curAlpha[i] = *(fBufferedRuns[i].fAlphas); |
396 | 529 |
397 finalX = SkMin32(nextX[i], finalX); | 530 finalX = SkMin32(nextX[i], finalX); |
398 } | 531 } |
399 | 532 |
400 // Make sure that we have a valid right-bound X value | 533 // Make sure that we have a valid right-bound X value |
401 SkASSERT(finalX < 0xFFFFF); | 534 SkASSERT(finalX < 0xFFFFF); |
402 | 535 |
536 // If the finalX is the longest run, then just blit until we have | |
537 // width... | |
538 if (kLongestRun == finalX) { | |
539 finalX = fWidth; | |
540 } | |
541 | |
403 // Run the blitter... | 542 // Run the blitter... |
404 while (curX != finalX) { | 543 while (curX != finalX) { |
405 SkASSERT(finalX >= curX); | 544 SkASSERT(finalX >= curX); |
406 | 545 |
407 // Do we need to populate the rest of the block? | 546 // Do we need to populate the rest of the block? |
408 if ((finalX - (BlockDim*(curX / BlockDim))) >= BlockDim) { | 547 if ((finalX - (BlockDim*(curX / BlockDim))) >= BlockDim) { |
409 const int col = curX % BlockDim; | 548 const int col = curX % BlockDim; |
410 const int colsLeft = BlockDim - col; | 549 const int colsLeft = BlockDim - col; |
411 SkASSERT(curX + colsLeft <= finalX); | 550 SkASSERT(curX + colsLeft <= finalX); |
412 | 551 |
(...skipping 29 matching lines...) Expand all Loading... | |
442 const int col = curX % BlockDim; | 581 const int col = curX % BlockDim; |
443 const int colsLeft = finalX - curX; | 582 const int colsLeft = finalX - curX; |
444 | 583 |
445 this->updateBlockColumns(block, col, colsLeft, curAlphaColumn); | 584 this->updateBlockColumns(block, col, colsLeft, curAlphaColumn); |
446 curX += colsLeft; | 585 curX += colsLeft; |
447 } | 586 } |
448 | 587 |
449 SkASSERT(curX == finalX); | 588 SkASSERT(curX == finalX); |
450 | 589 |
451 // Figure out what the next advancement is... | 590 // Figure out what the next advancement is... |
452 for (int i = 0; i < BlockDim; ++i) { | 591 if (finalX < fWidth) { |
453 if (nextX[i] == finalX) { | 592 for (int i = 0; i < BlockDim; ++i) { |
454 const int16_t run = *(fBufferedRuns[i].fRuns); | 593 if (nextX[i] == finalX) { |
455 fBufferedRuns[i].fRuns += run; | 594 const int16_t run = *(fBufferedRuns[i].fRuns); |
456 fBufferedRuns[i].fAlphas += run; | 595 fBufferedRuns[i].fRuns += run; |
457 curAlpha[i] = *(fBufferedRuns[i].fAlphas); | 596 fBufferedRuns[i].fAlphas += run; |
458 nextX[i] += *(fBufferedRuns[i].fRuns); | 597 curAlpha[i] = *(fBufferedRuns[i].fAlphas); |
598 nextX[i] += *(fBufferedRuns[i].fRuns); | |
599 } | |
459 } | 600 } |
460 } | |
461 | 601 |
462 finalX = 0xFFFFF; | 602 finalX = 0xFFFFF; |
463 for (int i = 0; i < BlockDim; ++i) { | 603 for (int i = 0; i < BlockDim; ++i) { |
464 finalX = SkMin32(nextX[i], finalX); | 604 finalX = SkMin32(nextX[i], finalX); |
605 } | |
606 } else { | |
607 curX = finalX; | |
465 } | 608 } |
466 } | 609 } |
467 | 610 |
468 // If we didn't land on a block boundary, output the block... | 611 // If we didn't land on a block boundary, output the block... |
469 if ((curX % BlockDim) > 0) { | 612 if ((curX % BlockDim) > 0) { |
470 #ifdef SK_DEBUG | 613 #ifdef SK_DEBUG |
471 for (int i = 0; i < BlockDim; ++i) { | 614 for (int i = 0; i < BlockDim; ++i) { |
472 SkASSERT(nextX[i] == kLongestRun || nextX[i] == curX); | 615 SkASSERT(nextX[i] == kLongestRun || nextX[i] == curX); |
473 } | 616 } |
474 #endif | 617 #endif |
475 const int col = curX % BlockDim; | 618 const int col = curX % BlockDim; |
476 const int colsLeft = BlockDim - col; | 619 const int colsLeft = BlockDim - col; |
477 | 620 |
478 memset(curAlphaColumn, 0, sizeof(curAlphaColumn)); | 621 memset(curAlphaColumn, 0, sizeof(curAlphaColumn)); |
479 this->updateBlockColumns(block, col, colsLeft, curAlphaColumn); | 622 this->updateBlockColumns(block, col, colsLeft, curAlphaColumn); |
480 | 623 |
481 CompressorType::CompressA8Vertical(outPtr, reinterpret_cast<uint8_t* >(block)); | 624 CompressorType::CompressA8Vertical(outPtr, reinterpret_cast<uint8_t* >(block)); |
482 } | 625 } |
483 | 626 |
484 fNextRun = 0; | 627 fNextRun = 0; |
485 } | 628 } |
629 | |
630 #if PEDANTIC_BLIT_RECT | |
631 void updateBlockRow(int x, int y, int width, int height, | |
632 int blockRow, int startBlockX, int endBlockX) { | |
633 if (0 == width || 0 == height || startBlockX == endBlockX) { | |
634 return; | |
635 } | |
636 | |
637 uint8_t* dst = this->getBlock(startBlockX, BlockDim * (y / BlockDim)); | |
638 | |
639 // One horizontal strip to update | |
640 uint8_t mask[BlockDim*BlockDim]; | |
641 memset(mask, 0, sizeof(mask)); | |
642 | |
643 // Update the left cap | |
644 int blockX = startBlockX; | |
645 const int yoff = y - blockRow; | |
646 for (int j = 0; j < height; ++j) { | |
647 const int xoff = x - blockX; | |
648 memset(mask + (j + yoff)*BlockDim + xoff, 0xFF, BlockDim - xoff); | |
649 } | |
650 CompressorType::UpdateBlock(dst, mask, BlockDim, mask); | |
651 dst += EncodedBlockSize; | |
652 blockX += BlockDim; | |
653 | |
654 // Update the middle | |
655 if (blockX < endBlockX) { | |
656 for (int j = 0; j < height; ++j) { | |
657 memset(mask + (j + yoff)*BlockDim, 0xFF, BlockDim); | |
658 } | |
659 while (blockX < endBlockX) { | |
660 CompressorType::UpdateBlock(dst, mask, BlockDim, mask); | |
661 dst += EncodedBlockSize; | |
662 blockX += BlockDim; | |
663 } | |
664 } | |
665 | |
666 SkASSERT(endBlockX == blockX); | |
667 | |
668 // Update the right cap (if we need to) | |
669 if (x + width > endBlockX) { | |
670 memset(mask, 0, sizeof(mask)); | |
671 for (int j = 0; j < height; ++j) { | |
672 const int xoff = (x+width-blockX); | |
673 memset(mask + (j+yoff)*BlockDim, 0xFF, xoff); | |
674 } | |
675 CompressorType::UpdateBlock(dst, mask, BlockDim, mask); | |
676 } | |
677 } | |
678 | |
679 void updateBlockCol(int x, int y, int width, int height, | |
680 int blockCol, int startBlockY, int endBlockY) { | |
681 if (0 == width || 0 == height || startBlockY == endBlockY) { | |
682 return; | |
683 } | |
684 | |
685 // One vertical strip to update | |
686 uint8_t mask[BlockDim*BlockDim]; | |
687 memset(mask, 0, sizeof(mask)); | |
688 const int maskX0 = x - blockCol; | |
689 const int maskWidth = maskX0 + width; | |
690 SkASSERT(maskWidth <= BlockDim); | |
691 | |
692 // Update the top cap | |
693 int blockY = startBlockY; | |
694 for (int j = (y - blockY); j < BlockDim; ++j) { | |
695 memset(mask + maskX0 + j*BlockDim, 0xFF, maskWidth); | |
696 } | |
697 CompressorType::UpdateBlock(this->getBlock(blockCol, blockY), mask, Bloc kDim, mask); | |
698 blockY += BlockDim; | |
699 | |
700 // Update middle | |
701 if (blockY < endBlockY) { | |
702 for (int j = 0; j < BlockDim; ++j) { | |
703 memset(mask + maskX0 + j*BlockDim, 0xFF, maskWidth); | |
704 } | |
705 while (blockY < endBlockY) { | |
706 CompressorType::UpdateBlock(this->getBlock(blockCol, blockY), | |
707 mask, BlockDim, mask); | |
708 blockY += BlockDim; | |
709 } | |
710 } | |
711 | |
712 SkASSERT(endBlockY == blockY); | |
713 | |
714 // Update bottom | |
715 if (y + height > endBlockY) { | |
716 for (int j = y+height; j < endBlockY + BlockDim; ++j) { | |
717 memset(mask + (j-endBlockY)*BlockDim, 0, BlockDim); | |
718 } | |
719 CompressorType::UpdateBlock(this->getBlock(blockCol, blockY), | |
720 mask, BlockDim, mask); | |
721 } | |
722 } | |
723 #endif // PEDANTIC_BLIT_RECT | |
724 | |
486 }; | 725 }; |
487 | 726 |
488 } // namespace SkTextureCompressor | 727 } // namespace SkTextureCompressor |
489 | 728 |
490 #endif // SkTextureCompressor_Blitter_DEFINED | 729 #endif // SkTextureCompressor_Blitter_DEFINED |
OLD | NEW |