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 |
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 |