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 14namespace SkTextureCompressor { 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 is 19// to update the compressed encoding of a block by resetting the proper parameters 20// (and even recompressing the block) where a rect falls inbetween block boundaries. 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 inverse 25// fills for paths. In other words, the top few rows and bottom few rows of a path 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 off the 29// edge of the buffer. This is why we do not need this flag to be turned on. 30// 31// NOTE: This code is unfinished, but is inteded as a starting point if an when 32// bugs are introduced from the existing code. 33#define PEDANTIC_BLIT_RECT 0 34 35// This class implements a blitter that blits directly into a buffer that will 36// be used as an compressed alpha texture. We compute this buffer by 37// buffering scan lines and then outputting them all at once. The number of 38// scan lines buffered is controlled by kBlockSize 39// 40// The CompressorType is a struct with a bunch of static methods that provides 41// the specialized compression functionality of the blitter. A complete CompressorType 42// will implement the following static functions; 43// 44// struct CompressorType { 45// // The function used to compress an A8 block. The layout of the 46// // block is also expected to be in column-major order. 47// static void CompressA8Vertical(uint8_t* dst, const uint8_t block[]); 48// 49// // The function used to compress an A8 block. The layout of the 50// // block is also expected to be in row-major order. 51// static void CompressA8Horizontal(uint8_t* dst, const uint8_t* src, int srcRowBytes); 52// 53#if PEDANTIC_BLIT_RECT 54// // The function used to update an already compressed block. This will 55// // most likely be implementation dependent. The mask variable will have 56// // 0xFF in positions where the block should be updated and 0 in positions 57// // where it shouldn't. src contains an uncompressed buffer of pixels. 58// static void UpdateBlock(uint8_t* dst, const uint8_t* src, int srcRowBytes, 59// const uint8_t* mask); 60#endif 61// }; 62template<int BlockDim, int EncodedBlockSize, typename CompressorType> 63class SkTCompressedAlphaBlitter : public SkBlitter { 64public: 65 SkTCompressedAlphaBlitter(int width, int height, void *compressedBuffer) 66 // 0x7FFE is one minus the largest positive 16-bit int. We use it for 67 // debugging to make sure that we're properly setting the nextX distance 68 // in flushRuns(). 69#ifdef SK_DEBUG 70 : fCalledOnceWithNonzeroY(false) 71 , fBlitMaskCalled(false), 72#else 73 : 74#endif 75 kLongestRun(0x7FFE), kZeroAlpha(0) 76 , fNextRun(0) 77 , fWidth(width) 78 , fHeight(height) 79 , fBuffer(compressedBuffer) 80 { 81 SkASSERT((width % BlockDim) == 0); 82 SkASSERT((height % BlockDim) == 0); 83 } 84 85 virtual ~SkTCompressedAlphaBlitter() { this->flushRuns(); } 86 87 // Blit a horizontal run of one or more pixels. 88 virtual void blitH(int x, int y, int width) SK_OVERRIDE { 89 // This function is intended to be called from any standard RGB 90 // buffer, so we should never encounter it. However, if some code 91 // path does end up here, then this needs to be investigated. 92 SkFAIL("Not implemented!"); 93 } 94 95 // Blit a horizontal run of antialiased pixels; runs[] is a *sparse* 96 // zero-terminated run-length encoding of spans of constant alpha values. 97 virtual void blitAntiH(int x, int y, 98 const SkAlpha antialias[], 99 const int16_t runs[]) SK_OVERRIDE { 100 SkASSERT(0 == x); 101 102 // Make sure that the new row to blit is either the first 103 // row that we're blitting, or it's exactly the next scan row 104 // since the last row that we blit. This is to ensure that when 105 // we go to flush the runs, that they are all the same four 106 // runs. 107 if (fNextRun > 0 && 108 ((x != fBufferedRuns[fNextRun-1].fX) || 109 (y-1 != fBufferedRuns[fNextRun-1].fY))) { 110 this->flushRuns(); 111 } 112 113 // Align the rows to a block boundary. If we receive rows that 114 // are not on a block boundary, then fill in the preceding runs 115 // with zeros. We do this by producing a single RLE that says 116 // that we have 0x7FFE pixels of zero (0x7FFE = 32766). 117 const int row = BlockDim * (y / BlockDim); 118 while ((row + fNextRun) < y) { 119 fBufferedRuns[fNextRun].fAlphas = &kZeroAlpha; 120 fBufferedRuns[fNextRun].fRuns = &kLongestRun; 121 fBufferedRuns[fNextRun].fX = 0; 122 fBufferedRuns[fNextRun].fY = row + fNextRun; 123 ++fNextRun; 124 } 125 126 // Make sure that our assumptions aren't violated... 127 SkASSERT(fNextRun == (y % BlockDim)); 128 SkASSERT(fNextRun == 0 || fBufferedRuns[fNextRun - 1].fY < y); 129 130 // Set the values of the next run 131 fBufferedRuns[fNextRun].fAlphas = antialias; 132 fBufferedRuns[fNextRun].fRuns = runs; 133 fBufferedRuns[fNextRun].fX = x; 134 fBufferedRuns[fNextRun].fY = y; 135 136 // If we've output a block of scanlines in a row that don't violate our 137 // assumptions, then it's time to flush them... 138 if (BlockDim == ++fNextRun) { 139 this->flushRuns(); 140 } 141 } 142 143 // Blit a vertical run of pixels with a constant alpha value. 144 virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE { 145 // This function is currently not implemented. It is not explicitly 146 // required by the contract, but if at some time a code path runs into 147 // this function (which is entirely possible), it needs to be implemented. 148 // 149 // TODO (krajcevski): 150 // This function will be most easily implemented in one of two ways: 151 // 1. Buffer each vertical column value and then construct a list 152 // of alpha values and output all of the blocks at once. This only 153 // requires a write to the compressed buffer 154 // 2. Replace the indices of each block with the proper indices based 155 // on the alpha value. This requires a read and write of the compressed 156 // buffer, but much less overhead. 157 SkFAIL("Not implemented!"); 158 } 159 160 // Blit a solid rectangle one or more pixels wide. It's assumed that blitRect 161 // is called as a way to bracket blitAntiH where above and below the path the 162 // called path just needs a solid rectangle to fill in the mask. 163#ifdef SK_DEBUG 164 bool fCalledOnceWithNonzeroY; 165#endif 166 virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE { 167 168 // Assumptions: 169 SkASSERT(0 == x); 170 SkASSERT(width <= fWidth); 171 172 // Make sure that we're only ever bracketing calls to blitAntiH. 173 SkASSERT((0 == y) || (!fCalledOnceWithNonzeroY && (fCalledOnceWithNonzeroY = true))); 174 175#if !(PEDANTIC_BLIT_RECT) 176 for (int i = 0; i < height; ++i) { 177 const SkAlpha kFullAlpha = 0xFF; 178 this->blitAntiH(x, y+i, &kFullAlpha, &kLongestRun); 179 } 180#else 181 const int startBlockX = (x / BlockDim) * BlockDim; 182 const int startBlockY = (y / BlockDim) * BlockDim; 183 184 const int endBlockX = ((x + width) / BlockDim) * BlockDim; 185 const int endBlockY = ((y + height) / BlockDim) * BlockDim; 186 187 // If start and end are the same, then we only need to update a single block... 188 if (startBlockY == endBlockY && startBlockX == endBlockX) { 189 uint8_t mask[BlockDim*BlockDim]; 190 memset(mask, 0, sizeof(mask)); 191 192 const int xoff = x - startBlockX; 193 SkASSERT((xoff + width) <= BlockDim); 194 195 const int yoff = y - startBlockY; 196 SkASSERT((yoff + height) <= BlockDim); 197 198 for (int j = 0; j < height; ++j) { 199 memset(mask + (j + yoff)*BlockDim + xoff, 0xFF, width); 200 } 201 202 uint8_t* dst = this->getBlock(startBlockX, startBlockY); 203 CompressorType::UpdateBlock(dst, mask, BlockDim, mask); 204 205 // If start and end are the same in the y dimension, then we can freely update an 206 // entire row of blocks... 207 } else if (startBlockY == endBlockY) { 208 209 this->updateBlockRow(x, y, width, height, startBlockY, startBlockX, endBlockX); 210 211 // Similarly, if the start and end are in the same column, then we can just update 212 // an entire column of blocks... 213 } else if (startBlockX == endBlockX) { 214 215 this->updateBlockCol(x, y, width, height, startBlockX, startBlockY, endBlockY); 216 217 // Otherwise, the rect spans a non-trivial region of blocks, and we have to construct 218 // a kind of 9-patch to update each of the pieces of the rect. The top and bottom 219 // rows are updated using updateBlockRow, and the left and right columns are updated 220 // using updateBlockColumn. Anything in the middle is simply memset to an opaque block 221 // encoding. 222 } else { 223 224 const int innerStartBlockX = startBlockX + BlockDim; 225 const int innerStartBlockY = startBlockY + BlockDim; 226 227 // Blit top row 228 const int topRowHeight = innerStartBlockY - y; 229 this->updateBlockRow(x, y, width, topRowHeight, startBlockY, 230 startBlockX, endBlockX); 231 232 // Advance y 233 y += topRowHeight; 234 height -= topRowHeight; 235 236 // Blit middle 237 if (endBlockY > innerStartBlockY) { 238 239 // Update left row 240 this->updateBlockCol(x, y, innerStartBlockX - x, endBlockY, startBlockY, 241 startBlockX, innerStartBlockX); 242 243 // Update the middle with an opaque encoding... 244 uint8_t mask[BlockDim*BlockDim]; 245 memset(mask, 0xFF, sizeof(mask)); 246 247 uint8_t opaqueEncoding[EncodedBlockSize]; 248 CompressorType::CompressA8Horizontal(opaqueEncoding, mask, BlockDim); 249 250 for (int j = innerStartBlockY; j < endBlockY; j += BlockDim) { 251 uint8_t* opaqueDst = this->getBlock(innerStartBlockX, j); 252 for (int i = innerStartBlockX; i < endBlockX; i += BlockDim) { 253 memcpy(opaqueDst, opaqueEncoding, EncodedBlockSize); 254 opaqueDst += EncodedBlockSize; 255 } 256 } 257 258 // If we need to update the right column, do that too 259 if (x + width > endBlockX) { 260 this->updateBlockCol(endBlockX, y, x + width - endBlockX, endBlockY, 261 endBlockX, innerStartBlockY, endBlockY); 262 } 263 264 // Advance y 265 height = y + height - endBlockY; 266 y = endBlockY; 267 } 268 269 // If we need to update the last row, then do that, too. 270 if (height > 0) { 271 this->updateBlockRow(x, y, width, height, endBlockY, 272 startBlockX, endBlockX); 273 } 274 } 275#endif 276 } 277 278 // Blit a rectangle with one alpha-blended column on the left, 279 // width (zero or more) opaque pixels, and one alpha-blended column 280 // on the right. The result will always be at least two pixels wide. 281 virtual void blitAntiRect(int x, int y, int width, int height, 282 SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE { 283 // This function is currently not implemented. It is not explicitly 284 // required by the contract, but if at some time a code path runs into 285 // this function (which is entirely possible), it needs to be implemented. 286 // 287 // TODO (krajcevski): 288 // This function will be most easily implemented as follows: 289 // 1. If width/height are smaller than a block, then update the 290 // indices of the affected blocks. 291 // 2. If width/height are larger than a block, then construct a 9-patch 292 // of block encodings that represent the rectangle, and write them 293 // to the compressed buffer as necessary. Whether or not the blocks 294 // are overwritten by zeros or just their indices are updated is up 295 // to debate. 296 SkFAIL("Not implemented!"); 297 } 298 299 // Blit a pattern of pixels defined by a rectangle-clipped mask; We make an 300 // assumption here that if this function gets called, then it will replace all 301 // of the compressed texture blocks that it touches. Hence, two separate calls 302 // to blitMask that have clips next to one another will cause artifacts. Most 303 // of the time, however, this function gets called because constructing the mask 304 // was faster than constructing the RLE for blitAntiH, and this function will 305 // only be called once. 306#ifdef SK_DEBUG 307 bool fBlitMaskCalled; 308#endif 309 virtual void blitMask(const SkMask& mask, const SkIRect& clip) SK_OVERRIDE { 310 311 // Assumptions: 312 SkASSERT(!fBlitMaskCalled && (fBlitMaskCalled = true)); 313 SkASSERT(SkMask::kA8_Format == mask.fFormat); 314 SkASSERT(mask.fBounds.contains(clip)); 315 316 // Start from largest block boundary less than the clip boundaries. 317 const int startI = BlockDim * (clip.left() / BlockDim); 318 const int startJ = BlockDim * (clip.top() / BlockDim); 319 320 for (int j = startJ; j < clip.bottom(); j += BlockDim) { 321 322 // Get the destination for this block row 323 uint8_t* dst = this->getBlock(startI, j); 324 for (int i = startI; i < clip.right(); i += BlockDim) { 325 326 // At this point, the block should intersect the clip. 327 SkASSERT(SkIRect::IntersectsNoEmptyCheck( 328 SkIRect::MakeXYWH(i, j, BlockDim, BlockDim), clip)); 329 330 // Do we need to pad it? 331 if (i < clip.left() || j < clip.top() || 332 i + BlockDim > clip.right() || j + BlockDim > clip.bottom()) { 333 334 uint8_t block[BlockDim*BlockDim]; 335 memset(block, 0, sizeof(block)); 336 337 const int startX = SkMax32(i, clip.left()); 338 const int startY = SkMax32(j, clip.top()); 339 340 const int endX = SkMin32(i + BlockDim, clip.right()); 341 const int endY = SkMin32(j + BlockDim, clip.bottom()); 342 343 for (int y = startY; y < endY; ++y) { 344 const int col = startX - i; 345 const int row = y - j; 346 const int valsWide = endX - startX; 347 SkASSERT(valsWide <= BlockDim); 348 SkASSERT(0 <= col && col < BlockDim); 349 SkASSERT(0 <= row && row < BlockDim); 350 memcpy(block + row*BlockDim + col, 351 mask.getAddr8(startX, j + row), valsWide); 352 } 353 354 CompressorType::CompressA8Horizontal(dst, block, BlockDim); 355 } else { 356 // Otherwise, just compress it. 357 uint8_t*const src = mask.getAddr8(i, j); 358 const uint32_t rb = mask.fRowBytes; 359 CompressorType::CompressA8Horizontal(dst, src, rb); 360 } 361 362 dst += EncodedBlockSize; 363 } 364 } 365 } 366 367 // If the blitter just sets a single value for each pixel, return the 368 // bitmap it draws into, and assign value. If not, return NULL and ignore 369 // the value parameter. 370 virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE { 371 return NULL; 372 } 373 374 /** 375 * Compressed texture blitters only really work correctly if they get 376 * BlockDim rows at a time. That being said, this blitter tries it's best 377 * to preserve semantics if blitAntiH doesn't get called in too many 378 * weird ways... 379 */ 380 virtual int requestRowsPreserved() const { return BlockDim; } 381 382private: 383 static const int kPixelsPerBlock = BlockDim * BlockDim; 384 385 // The longest possible run of pixels that this blitter will receive. 386 // This is initialized in the constructor to 0x7FFE, which is one less 387 // than the largest positive 16-bit integer. We make sure that it's one 388 // less for debugging purposes. We also don't make this variable static 389 // in order to make sure that we can construct a valid pointer to it. 390 const int16_t kLongestRun; 391 392 // Usually used in conjunction with kLongestRun. This is initialized to 393 // zero. 394 const SkAlpha kZeroAlpha; 395 396 // This is the information that we buffer whenever we're asked to blit 397 // a row with this blitter. 398 struct BufferedRun { 399 const SkAlpha* fAlphas; 400 const int16_t* fRuns; 401 int fX, fY; 402 } fBufferedRuns[BlockDim]; 403 404 // The next row [0, BlockDim) that we need to blit. 405 int fNextRun; 406 407 // The width and height of the image that we're blitting 408 const int fWidth; 409 const int fHeight; 410 411 // The compressed buffer that we're blitting into. It is assumed that the buffer 412 // is large enough to store a compressed image of size fWidth*fHeight. 413 void* const fBuffer; 414 415 // Various utility functions 416 int blocksWide() const { return fWidth / BlockDim; } 417 int blocksTall() const { return fHeight / BlockDim; } 418 int totalBlocks() const { return (fWidth * fHeight) / kPixelsPerBlock; } 419 420 // Returns the block index for the block containing pixel (x, y). Block 421 // indices start at zero and proceed in raster order. 422 int getBlockOffset(int x, int y) const { 423 SkASSERT(x < fWidth); 424 SkASSERT(y < fHeight); 425 const int blockCol = x / BlockDim; 426 const int blockRow = y / BlockDim; 427 return blockRow * this->blocksWide() + blockCol; 428 } 429 430 // Returns a pointer to the block containing pixel (x, y) 431 uint8_t *getBlock(int x, int y) const { 432 uint8_t* ptr = reinterpret_cast<uint8_t*>(fBuffer); 433 return ptr + EncodedBlockSize*this->getBlockOffset(x, y); 434 } 435 436 // Updates the block whose columns are stored in block. curAlphai is expected 437 // to store the alpha values that will be placed within each of the columns in 438 // the range [col, col+colsLeft). 439 typedef uint32_t Column[BlockDim/4]; 440 typedef uint32_t Block[BlockDim][BlockDim/4]; 441 inline void updateBlockColumns(Block block, const int col, 442 const int colsLeft, const Column curAlphai) { 443 SkASSERT(block); 444 SkASSERT(col + colsLeft <= BlockDim); 445 446 for (int i = col; i < (col + colsLeft); ++i) { 447 memcpy(block[i], curAlphai, sizeof(Column)); 448 } 449 } 450 451 // The following function writes the buffered runs to compressed blocks. 452 // If fNextRun < BlockDim, then we fill the runs that we haven't buffered with 453 // the constant zero buffer. 454 void flushRuns() { 455 // If we don't have any runs, then just return. 456 if (0 == fNextRun) { 457 return; 458 } 459 460#ifndef NDEBUG 461 // Make sure that if we have any runs, they all match 462 for (int i = 1; i < fNextRun; ++i) { 463 SkASSERT(fBufferedRuns[i].fY == fBufferedRuns[i-1].fY + 1); 464 SkASSERT(fBufferedRuns[i].fX == fBufferedRuns[i-1].fX); 465 } 466#endif 467 468 // If we don't have as many runs as we have rows, fill in the remaining 469 // runs with constant zeros. 470 for (int i = fNextRun; i < BlockDim; ++i) { 471 fBufferedRuns[i].fY = fBufferedRuns[0].fY + i; 472 fBufferedRuns[i].fX = fBufferedRuns[0].fX; 473 fBufferedRuns[i].fAlphas = &kZeroAlpha; 474 fBufferedRuns[i].fRuns = &kLongestRun; 475 } 476 477 // Make sure that our assumptions aren't violated. 478 SkASSERT(fNextRun > 0 && fNextRun <= BlockDim); 479 SkASSERT((fBufferedRuns[0].fY % BlockDim) == 0); 480 481 // The following logic walks BlockDim rows at a time and outputs compressed 482 // blocks to the buffer passed into the constructor. 483 // We do the following: 484 // 485 // c1 c2 c3 c4 486 // ----------------------------------------------------------------------- 487 // ... | | | | | ----> fBufferedRuns[0] 488 // ----------------------------------------------------------------------- 489 // ... | | | | | ----> fBufferedRuns[1] 490 // ----------------------------------------------------------------------- 491 // ... | | | | | ----> fBufferedRuns[2] 492 // ----------------------------------------------------------------------- 493 // ... | | | | | ----> fBufferedRuns[3] 494 // ----------------------------------------------------------------------- 495 // 496 // curX -- the macro X value that we've gotten to. 497 // c[BlockDim] -- the buffers that represent the columns of the current block 498 // that we're operating on 499 // curAlphaColumn -- buffer containing the column of alpha values from fBufferedRuns. 500 // nextX -- for each run, the next point at which we need to update curAlphaColumn 501 // after the value of curX. 502 // finalX -- the minimum of all the nextX values. 503 // 504 // curX advances to finalX outputting any blocks that it passes along 505 // the way. Since finalX will not change when we reach the end of a 506 // run, the termination criteria will be whenever curX == finalX at the 507 // end of a loop. 508 509 // Setup: 510 Block block; 511 sk_bzero(block, sizeof(block)); 512 513 Column curAlphaColumn; 514 sk_bzero(curAlphaColumn, sizeof(curAlphaColumn)); 515 516 SkAlpha *curAlpha = reinterpret_cast<SkAlpha*>(&curAlphaColumn); 517 518 int nextX[BlockDim]; 519 for (int i = 0; i < BlockDim; ++i) { 520 nextX[i] = 0x7FFFFF; 521 } 522 523 uint8_t* outPtr = this->getBlock(fBufferedRuns[0].fX, fBufferedRuns[0].fY); 524 525 // Populate the first set of runs and figure out how far we need to 526 // advance on the first step 527 int curX = 0; 528 int finalX = 0xFFFFF; 529 for (int i = 0; i < BlockDim; ++i) { 530 nextX[i] = *(fBufferedRuns[i].fRuns); 531 curAlpha[i] = *(fBufferedRuns[i].fAlphas); 532 533 finalX = SkMin32(nextX[i], finalX); 534 } 535 536 // Make sure that we have a valid right-bound X value 537 SkASSERT(finalX < 0xFFFFF); 538 539 // If the finalX is the longest run, then just blit until we have 540 // width... 541 if (kLongestRun == finalX) { 542 finalX = fWidth; 543 } 544 545 // Run the blitter... 546 while (curX != finalX) { 547 SkASSERT(finalX >= curX); 548 549 // Do we need to populate the rest of the block? 550 if ((finalX - (BlockDim*(curX / BlockDim))) >= BlockDim) { 551 const int col = curX % BlockDim; 552 const int colsLeft = BlockDim - col; 553 SkASSERT(curX + colsLeft <= finalX); 554 555 this->updateBlockColumns(block, col, colsLeft, curAlphaColumn); 556 557 // Write this block 558 CompressorType::CompressA8Vertical(outPtr, reinterpret_cast<uint8_t*>(block)); 559 outPtr += EncodedBlockSize; 560 curX += colsLeft; 561 } 562 563 // If we can advance even further, then just keep memsetting the block 564 if ((finalX - curX) >= BlockDim) { 565 SkASSERT((curX % BlockDim) == 0); 566 567 const int col = 0; 568 const int colsLeft = BlockDim; 569 570 this->updateBlockColumns(block, col, colsLeft, curAlphaColumn); 571 572 // While we can keep advancing, just keep writing the block. 573 uint8_t lastBlock[EncodedBlockSize]; 574 CompressorType::CompressA8Vertical(lastBlock, reinterpret_cast<uint8_t*>(block)); 575 while((finalX - curX) >= BlockDim) { 576 memcpy(outPtr, lastBlock, EncodedBlockSize); 577 outPtr += EncodedBlockSize; 578 curX += BlockDim; 579 } 580 } 581 582 // If we haven't advanced within the block then do so. 583 if (curX < finalX) { 584 const int col = curX % BlockDim; 585 const int colsLeft = finalX - curX; 586 587 this->updateBlockColumns(block, col, colsLeft, curAlphaColumn); 588 curX += colsLeft; 589 } 590 591 SkASSERT(curX == finalX); 592 593 // Figure out what the next advancement is... 594 if (finalX < fWidth) { 595 for (int i = 0; i < BlockDim; ++i) { 596 if (nextX[i] == finalX) { 597 const int16_t run = *(fBufferedRuns[i].fRuns); 598 fBufferedRuns[i].fRuns += run; 599 fBufferedRuns[i].fAlphas += run; 600 curAlpha[i] = *(fBufferedRuns[i].fAlphas); 601 nextX[i] += *(fBufferedRuns[i].fRuns); 602 } 603 } 604 605 finalX = 0xFFFFF; 606 for (int i = 0; i < BlockDim; ++i) { 607 finalX = SkMin32(nextX[i], finalX); 608 } 609 } else { 610 curX = finalX; 611 } 612 } 613 614 // If we didn't land on a block boundary, output the block... 615 if ((curX % BlockDim) > 0) { 616#ifdef SK_DEBUG 617 for (int i = 0; i < BlockDim; ++i) { 618 SkASSERT(nextX[i] == kLongestRun || nextX[i] == curX); 619 } 620#endif 621 const int col = curX % BlockDim; 622 const int colsLeft = BlockDim - col; 623 624 memset(curAlphaColumn, 0, sizeof(curAlphaColumn)); 625 this->updateBlockColumns(block, col, colsLeft, curAlphaColumn); 626 627 CompressorType::CompressA8Vertical(outPtr, reinterpret_cast<uint8_t*>(block)); 628 } 629 630 fNextRun = 0; 631 } 632 633#if PEDANTIC_BLIT_RECT 634 void updateBlockRow(int x, int y, int width, int height, 635 int blockRow, int startBlockX, int endBlockX) { 636 if (0 == width || 0 == height || startBlockX == endBlockX) { 637 return; 638 } 639 640 uint8_t* dst = this->getBlock(startBlockX, BlockDim * (y / BlockDim)); 641 642 // One horizontal strip to update 643 uint8_t mask[BlockDim*BlockDim]; 644 memset(mask, 0, sizeof(mask)); 645 646 // Update the left cap 647 int blockX = startBlockX; 648 const int yoff = y - blockRow; 649 for (int j = 0; j < height; ++j) { 650 const int xoff = x - blockX; 651 memset(mask + (j + yoff)*BlockDim + xoff, 0xFF, BlockDim - xoff); 652 } 653 CompressorType::UpdateBlock(dst, mask, BlockDim, mask); 654 dst += EncodedBlockSize; 655 blockX += BlockDim; 656 657 // Update the middle 658 if (blockX < endBlockX) { 659 for (int j = 0; j < height; ++j) { 660 memset(mask + (j + yoff)*BlockDim, 0xFF, BlockDim); 661 } 662 while (blockX < endBlockX) { 663 CompressorType::UpdateBlock(dst, mask, BlockDim, mask); 664 dst += EncodedBlockSize; 665 blockX += BlockDim; 666 } 667 } 668 669 SkASSERT(endBlockX == blockX); 670 671 // Update the right cap (if we need to) 672 if (x + width > endBlockX) { 673 memset(mask, 0, sizeof(mask)); 674 for (int j = 0; j < height; ++j) { 675 const int xoff = (x+width-blockX); 676 memset(mask + (j+yoff)*BlockDim, 0xFF, xoff); 677 } 678 CompressorType::UpdateBlock(dst, mask, BlockDim, mask); 679 } 680 } 681 682 void updateBlockCol(int x, int y, int width, int height, 683 int blockCol, int startBlockY, int endBlockY) { 684 if (0 == width || 0 == height || startBlockY == endBlockY) { 685 return; 686 } 687 688 // One vertical strip to update 689 uint8_t mask[BlockDim*BlockDim]; 690 memset(mask, 0, sizeof(mask)); 691 const int maskX0 = x - blockCol; 692 const int maskWidth = maskX0 + width; 693 SkASSERT(maskWidth <= BlockDim); 694 695 // Update the top cap 696 int blockY = startBlockY; 697 for (int j = (y - blockY); j < BlockDim; ++j) { 698 memset(mask + maskX0 + j*BlockDim, 0xFF, maskWidth); 699 } 700 CompressorType::UpdateBlock(this->getBlock(blockCol, blockY), mask, BlockDim, mask); 701 blockY += BlockDim; 702 703 // Update middle 704 if (blockY < endBlockY) { 705 for (int j = 0; j < BlockDim; ++j) { 706 memset(mask + maskX0 + j*BlockDim, 0xFF, maskWidth); 707 } 708 while (blockY < endBlockY) { 709 CompressorType::UpdateBlock(this->getBlock(blockCol, blockY), 710 mask, BlockDim, mask); 711 blockY += BlockDim; 712 } 713 } 714 715 SkASSERT(endBlockY == blockY); 716 717 // Update bottom 718 if (y + height > endBlockY) { 719 for (int j = y+height; j < endBlockY + BlockDim; ++j) { 720 memset(mask + (j-endBlockY)*BlockDim, 0, BlockDim); 721 } 722 CompressorType::UpdateBlock(this->getBlock(blockCol, blockY), 723 mask, BlockDim, mask); 724 } 725 } 726#endif // PEDANTIC_BLIT_RECT 727 728}; 729 730} // namespace SkTextureCompressor 731 732#endif // SkTextureCompressor_Blitter_DEFINED 733