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