1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2015 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "include/core/SkRect.h"
9cb93a386Sopenharmony_ci#include "src/core/SkLatticeIter.h"
10cb93a386Sopenharmony_ci
11cb93a386Sopenharmony_ci/**
12cb93a386Sopenharmony_ci *  Divs must be in increasing order with no duplicates.
13cb93a386Sopenharmony_ci */
14cb93a386Sopenharmony_cistatic bool valid_divs(const int* divs, int count, int start, int end) {
15cb93a386Sopenharmony_ci    int prev = start - 1;
16cb93a386Sopenharmony_ci    for (int i = 0; i < count; i++) {
17cb93a386Sopenharmony_ci        if (prev >= divs[i] || divs[i] >= end) {
18cb93a386Sopenharmony_ci            return false;
19cb93a386Sopenharmony_ci        }
20cb93a386Sopenharmony_ci        prev = divs[i];
21cb93a386Sopenharmony_ci    }
22cb93a386Sopenharmony_ci
23cb93a386Sopenharmony_ci    return true;
24cb93a386Sopenharmony_ci}
25cb93a386Sopenharmony_ci
26cb93a386Sopenharmony_cibool SkLatticeIter::Valid(int width, int height, const SkCanvas::Lattice& lattice) {
27cb93a386Sopenharmony_ci    SkIRect totalBounds = SkIRect::MakeWH(width, height);
28cb93a386Sopenharmony_ci    SkASSERT(lattice.fBounds);
29cb93a386Sopenharmony_ci    const SkIRect latticeBounds = *lattice.fBounds;
30cb93a386Sopenharmony_ci    if (!totalBounds.contains(latticeBounds)) {
31cb93a386Sopenharmony_ci        return false;
32cb93a386Sopenharmony_ci    }
33cb93a386Sopenharmony_ci
34cb93a386Sopenharmony_ci    bool zeroXDivs = lattice.fXCount <= 0 || (1 == lattice.fXCount &&
35cb93a386Sopenharmony_ci                                              latticeBounds.fLeft == lattice.fXDivs[0]);
36cb93a386Sopenharmony_ci    bool zeroYDivs = lattice.fYCount <= 0 || (1 == lattice.fYCount &&
37cb93a386Sopenharmony_ci                                              latticeBounds.fTop == lattice.fYDivs[0]);
38cb93a386Sopenharmony_ci    if (zeroXDivs && zeroYDivs) {
39cb93a386Sopenharmony_ci        return false;
40cb93a386Sopenharmony_ci    }
41cb93a386Sopenharmony_ci
42cb93a386Sopenharmony_ci    return valid_divs(lattice.fXDivs, lattice.fXCount, latticeBounds.fLeft, latticeBounds.fRight)
43cb93a386Sopenharmony_ci        && valid_divs(lattice.fYDivs, lattice.fYCount, latticeBounds.fTop, latticeBounds.fBottom);
44cb93a386Sopenharmony_ci}
45cb93a386Sopenharmony_ci
46cb93a386Sopenharmony_ci/**
47cb93a386Sopenharmony_ci *  Count the number of pixels that are in "scalable" patches.
48cb93a386Sopenharmony_ci */
49cb93a386Sopenharmony_cistatic int count_scalable_pixels(const int32_t* divs, int numDivs, bool firstIsScalable,
50cb93a386Sopenharmony_ci                                 int start, int end) {
51cb93a386Sopenharmony_ci    if (0 == numDivs) {
52cb93a386Sopenharmony_ci        return firstIsScalable ? end - start : 0;
53cb93a386Sopenharmony_ci    }
54cb93a386Sopenharmony_ci
55cb93a386Sopenharmony_ci    int i;
56cb93a386Sopenharmony_ci    int count;
57cb93a386Sopenharmony_ci    if (firstIsScalable) {
58cb93a386Sopenharmony_ci        count = divs[0] - start;
59cb93a386Sopenharmony_ci        i = 1;
60cb93a386Sopenharmony_ci    } else {
61cb93a386Sopenharmony_ci        count = 0;
62cb93a386Sopenharmony_ci        i = 0;
63cb93a386Sopenharmony_ci    }
64cb93a386Sopenharmony_ci
65cb93a386Sopenharmony_ci    for (; i < numDivs; i += 2) {
66cb93a386Sopenharmony_ci        // Alternatively, we could use |top| and |bottom| as variable names, instead of
67cb93a386Sopenharmony_ci        // |left| and |right|.
68cb93a386Sopenharmony_ci        int left = divs[i];
69cb93a386Sopenharmony_ci        int right = (i + 1 < numDivs) ? divs[i + 1] : end;
70cb93a386Sopenharmony_ci        count += right - left;
71cb93a386Sopenharmony_ci    }
72cb93a386Sopenharmony_ci
73cb93a386Sopenharmony_ci    return count;
74cb93a386Sopenharmony_ci}
75cb93a386Sopenharmony_ci
76cb93a386Sopenharmony_ci/**
77cb93a386Sopenharmony_ci *  Set points for the src and dst rects on subsequent draw calls.
78cb93a386Sopenharmony_ci */
79cb93a386Sopenharmony_cistatic void set_points(float* dst, int* src, const int* divs, int divCount, int srcFixed,
80cb93a386Sopenharmony_ci                       int srcScalable, int srcStart, int srcEnd, float dstStart, float dstEnd,
81cb93a386Sopenharmony_ci                       bool isScalable) {
82cb93a386Sopenharmony_ci    float dstLen = dstEnd - dstStart;
83cb93a386Sopenharmony_ci    float scale;
84cb93a386Sopenharmony_ci    if (srcFixed <= dstLen) {
85cb93a386Sopenharmony_ci        // This is the "normal" case, where we scale the "scalable" patches and leave
86cb93a386Sopenharmony_ci        // the other patches fixed.
87cb93a386Sopenharmony_ci        scale = (dstLen - ((float) srcFixed)) / ((float) srcScalable);
88cb93a386Sopenharmony_ci    } else {
89cb93a386Sopenharmony_ci        // In this case, we eliminate the "scalable" patches and scale the "fixed" patches.
90cb93a386Sopenharmony_ci        scale = dstLen / ((float) srcFixed);
91cb93a386Sopenharmony_ci    }
92cb93a386Sopenharmony_ci
93cb93a386Sopenharmony_ci    src[0] = srcStart;
94cb93a386Sopenharmony_ci    dst[0] = dstStart;
95cb93a386Sopenharmony_ci    for (int i = 0; i < divCount; i++) {
96cb93a386Sopenharmony_ci        src[i + 1] = divs[i];
97cb93a386Sopenharmony_ci        int srcDelta = src[i + 1] - src[i];
98cb93a386Sopenharmony_ci        float dstDelta;
99cb93a386Sopenharmony_ci        if (srcFixed <= dstLen) {
100cb93a386Sopenharmony_ci            dstDelta = isScalable ? scale * srcDelta : srcDelta;
101cb93a386Sopenharmony_ci        } else {
102cb93a386Sopenharmony_ci            dstDelta = isScalable ? 0.0f : scale * srcDelta;
103cb93a386Sopenharmony_ci        }
104cb93a386Sopenharmony_ci        dst[i + 1] = dst[i] + dstDelta;
105cb93a386Sopenharmony_ci
106cb93a386Sopenharmony_ci        // Alternate between "scalable" and "fixed" patches.
107cb93a386Sopenharmony_ci        isScalable = !isScalable;
108cb93a386Sopenharmony_ci    }
109cb93a386Sopenharmony_ci
110cb93a386Sopenharmony_ci    src[divCount + 1] = srcEnd;
111cb93a386Sopenharmony_ci    dst[divCount + 1] = dstEnd;
112cb93a386Sopenharmony_ci}
113cb93a386Sopenharmony_ci
114cb93a386Sopenharmony_ciSkLatticeIter::SkLatticeIter(const SkCanvas::Lattice& lattice, const SkRect& dst) {
115cb93a386Sopenharmony_ci    const int* xDivs = lattice.fXDivs;
116cb93a386Sopenharmony_ci    const int origXCount = lattice.fXCount;
117cb93a386Sopenharmony_ci    const int* yDivs = lattice.fYDivs;
118cb93a386Sopenharmony_ci    const int origYCount = lattice.fYCount;
119cb93a386Sopenharmony_ci    SkASSERT(lattice.fBounds);
120cb93a386Sopenharmony_ci    const SkIRect src = *lattice.fBounds;
121cb93a386Sopenharmony_ci
122cb93a386Sopenharmony_ci    // In the x-dimension, the first rectangle always starts at x = 0 and is "scalable".
123cb93a386Sopenharmony_ci    // If xDiv[0] is 0, it indicates that the first rectangle is degenerate, so the
124cb93a386Sopenharmony_ci    // first real rectangle "scalable" in the x-direction.
125cb93a386Sopenharmony_ci    //
126cb93a386Sopenharmony_ci    // The same interpretation applies to the y-dimension.
127cb93a386Sopenharmony_ci    //
128cb93a386Sopenharmony_ci    // As we move left to right across the image, alternating patches will be "fixed" or
129cb93a386Sopenharmony_ci    // "scalable" in the x-direction.  Similarly, as move top to bottom, alternating
130cb93a386Sopenharmony_ci    // patches will be "fixed" or "scalable" in the y-direction.
131cb93a386Sopenharmony_ci    int xCount = origXCount;
132cb93a386Sopenharmony_ci    int yCount = origYCount;
133cb93a386Sopenharmony_ci    bool xIsScalable = (xCount > 0 && src.fLeft == xDivs[0]);
134cb93a386Sopenharmony_ci    if (xIsScalable) {
135cb93a386Sopenharmony_ci        // Once we've decided that the first patch is "scalable", we don't need the
136cb93a386Sopenharmony_ci        // xDiv.  It is always implied that we start at the edge of the bounds.
137cb93a386Sopenharmony_ci        xDivs++;
138cb93a386Sopenharmony_ci        xCount--;
139cb93a386Sopenharmony_ci    }
140cb93a386Sopenharmony_ci    bool yIsScalable = (yCount > 0 && src.fTop == yDivs[0]);
141cb93a386Sopenharmony_ci    if (yIsScalable) {
142cb93a386Sopenharmony_ci        // Once we've decided that the first patch is "scalable", we don't need the
143cb93a386Sopenharmony_ci        // yDiv.  It is always implied that we start at the edge of the bounds.
144cb93a386Sopenharmony_ci        yDivs++;
145cb93a386Sopenharmony_ci        yCount--;
146cb93a386Sopenharmony_ci    }
147cb93a386Sopenharmony_ci
148cb93a386Sopenharmony_ci    // Count "scalable" and "fixed" pixels in each dimension.
149cb93a386Sopenharmony_ci    int xCountScalable = count_scalable_pixels(xDivs, xCount, xIsScalable, src.fLeft, src.fRight);
150cb93a386Sopenharmony_ci    int xCountFixed = src.width() - xCountScalable;
151cb93a386Sopenharmony_ci    int yCountScalable = count_scalable_pixels(yDivs, yCount, yIsScalable, src.fTop, src.fBottom);
152cb93a386Sopenharmony_ci    int yCountFixed = src.height() - yCountScalable;
153cb93a386Sopenharmony_ci
154cb93a386Sopenharmony_ci    fSrcX.reset(xCount + 2);
155cb93a386Sopenharmony_ci    fDstX.reset(xCount + 2);
156cb93a386Sopenharmony_ci    set_points(fDstX.begin(), fSrcX.begin(), xDivs, xCount, xCountFixed, xCountScalable,
157cb93a386Sopenharmony_ci               src.fLeft, src.fRight, dst.fLeft, dst.fRight, xIsScalable);
158cb93a386Sopenharmony_ci
159cb93a386Sopenharmony_ci    fSrcY.reset(yCount + 2);
160cb93a386Sopenharmony_ci    fDstY.reset(yCount + 2);
161cb93a386Sopenharmony_ci    set_points(fDstY.begin(), fSrcY.begin(), yDivs, yCount, yCountFixed, yCountScalable,
162cb93a386Sopenharmony_ci               src.fTop, src.fBottom, dst.fTop, dst.fBottom, yIsScalable);
163cb93a386Sopenharmony_ci
164cb93a386Sopenharmony_ci    fCurrX = fCurrY = 0;
165cb93a386Sopenharmony_ci    fNumRectsInLattice = (xCount + 1) * (yCount + 1);
166cb93a386Sopenharmony_ci    fNumRectsToDraw = fNumRectsInLattice;
167cb93a386Sopenharmony_ci
168cb93a386Sopenharmony_ci    if (lattice.fRectTypes) {
169cb93a386Sopenharmony_ci        fRectTypes.push_back_n(fNumRectsInLattice);
170cb93a386Sopenharmony_ci        fColors.push_back_n(fNumRectsInLattice);
171cb93a386Sopenharmony_ci
172cb93a386Sopenharmony_ci        const SkCanvas::Lattice::RectType* flags = lattice.fRectTypes;
173cb93a386Sopenharmony_ci        const SkColor* colors = lattice.fColors;
174cb93a386Sopenharmony_ci
175cb93a386Sopenharmony_ci        bool hasPadRow = (yCount != origYCount);
176cb93a386Sopenharmony_ci        bool hasPadCol = (xCount != origXCount);
177cb93a386Sopenharmony_ci        if (hasPadRow) {
178cb93a386Sopenharmony_ci            // The first row of rects are all empty, skip the first row of flags.
179cb93a386Sopenharmony_ci            flags += origXCount + 1;
180cb93a386Sopenharmony_ci            colors += origXCount + 1;
181cb93a386Sopenharmony_ci        }
182cb93a386Sopenharmony_ci
183cb93a386Sopenharmony_ci        int i = 0;
184cb93a386Sopenharmony_ci        for (int y = 0; y < yCount + 1; y++) {
185cb93a386Sopenharmony_ci            for (int x = 0; x < origXCount + 1; x++) {
186cb93a386Sopenharmony_ci                if (0 == x && hasPadCol) {
187cb93a386Sopenharmony_ci                    // The first column of rects are all empty.  Skip a rect.
188cb93a386Sopenharmony_ci                    flags++;
189cb93a386Sopenharmony_ci                    colors++;
190cb93a386Sopenharmony_ci                    continue;
191cb93a386Sopenharmony_ci                }
192cb93a386Sopenharmony_ci
193cb93a386Sopenharmony_ci                fRectTypes[i] = *flags;
194cb93a386Sopenharmony_ci                fColors[i] = SkCanvas::Lattice::kFixedColor == *flags ? *colors : 0;
195cb93a386Sopenharmony_ci                flags++;
196cb93a386Sopenharmony_ci                colors++;
197cb93a386Sopenharmony_ci                i++;
198cb93a386Sopenharmony_ci            }
199cb93a386Sopenharmony_ci        }
200cb93a386Sopenharmony_ci
201cb93a386Sopenharmony_ci        for (int j = 0; j < fRectTypes.count(); j++) {
202cb93a386Sopenharmony_ci            if (SkCanvas::Lattice::kTransparent == fRectTypes[j]) {
203cb93a386Sopenharmony_ci                fNumRectsToDraw--;
204cb93a386Sopenharmony_ci            }
205cb93a386Sopenharmony_ci        }
206cb93a386Sopenharmony_ci    }
207cb93a386Sopenharmony_ci}
208cb93a386Sopenharmony_ci
209cb93a386Sopenharmony_cibool SkLatticeIter::Valid(int width, int height, const SkIRect& center) {
210cb93a386Sopenharmony_ci    return !center.isEmpty() && SkIRect::MakeWH(width, height).contains(center);
211cb93a386Sopenharmony_ci}
212cb93a386Sopenharmony_ci
213cb93a386Sopenharmony_ciSkLatticeIter::SkLatticeIter(int w, int h, const SkIRect& c, const SkRect& dst) {
214cb93a386Sopenharmony_ci    SkASSERT(SkIRect::MakeWH(w, h).contains(c));
215cb93a386Sopenharmony_ci
216cb93a386Sopenharmony_ci    fSrcX.reset(4);
217cb93a386Sopenharmony_ci    fSrcY.reset(4);
218cb93a386Sopenharmony_ci    fDstX.reset(4);
219cb93a386Sopenharmony_ci    fDstY.reset(4);
220cb93a386Sopenharmony_ci
221cb93a386Sopenharmony_ci    fSrcX[0] = 0;
222cb93a386Sopenharmony_ci    fSrcX[1] = SkIntToScalar(c.fLeft);
223cb93a386Sopenharmony_ci    fSrcX[2] = SkIntToScalar(c.fRight);
224cb93a386Sopenharmony_ci    fSrcX[3] = SkIntToScalar(w);
225cb93a386Sopenharmony_ci
226cb93a386Sopenharmony_ci    fSrcY[0] = 0;
227cb93a386Sopenharmony_ci    fSrcY[1] = SkIntToScalar(c.fTop);
228cb93a386Sopenharmony_ci    fSrcY[2] = SkIntToScalar(c.fBottom);
229cb93a386Sopenharmony_ci    fSrcY[3] = SkIntToScalar(h);
230cb93a386Sopenharmony_ci
231cb93a386Sopenharmony_ci    fDstX[0] = dst.fLeft;
232cb93a386Sopenharmony_ci    fDstX[1] = dst.fLeft + SkIntToScalar(c.fLeft);
233cb93a386Sopenharmony_ci    fDstX[2] = dst.fRight - SkIntToScalar(w - c.fRight);
234cb93a386Sopenharmony_ci    fDstX[3] = dst.fRight;
235cb93a386Sopenharmony_ci
236cb93a386Sopenharmony_ci    fDstY[0] = dst.fTop;
237cb93a386Sopenharmony_ci    fDstY[1] = dst.fTop + SkIntToScalar(c.fTop);
238cb93a386Sopenharmony_ci    fDstY[2] = dst.fBottom - SkIntToScalar(h - c.fBottom);
239cb93a386Sopenharmony_ci    fDstY[3] = dst.fBottom;
240cb93a386Sopenharmony_ci
241cb93a386Sopenharmony_ci    if (fDstX[1] > fDstX[2]) {
242cb93a386Sopenharmony_ci        fDstX[1] = fDstX[0] + (fDstX[3] - fDstX[0]) * c.fLeft / (w - c.width());
243cb93a386Sopenharmony_ci        fDstX[2] = fDstX[1];
244cb93a386Sopenharmony_ci    }
245cb93a386Sopenharmony_ci
246cb93a386Sopenharmony_ci    if (fDstY[1] > fDstY[2]) {
247cb93a386Sopenharmony_ci        fDstY[1] = fDstY[0] + (fDstY[3] - fDstY[0]) * c.fTop / (h - c.height());
248cb93a386Sopenharmony_ci        fDstY[2] = fDstY[1];
249cb93a386Sopenharmony_ci    }
250cb93a386Sopenharmony_ci
251cb93a386Sopenharmony_ci    fCurrX = fCurrY = 0;
252cb93a386Sopenharmony_ci    fNumRectsInLattice = 9;
253cb93a386Sopenharmony_ci    fNumRectsToDraw = 9;
254cb93a386Sopenharmony_ci}
255cb93a386Sopenharmony_ci
256cb93a386Sopenharmony_cibool SkLatticeIter::next(SkIRect* src, SkRect* dst, bool* isFixedColor, SkColor* fixedColor) {
257cb93a386Sopenharmony_ci    int currRect = fCurrX + fCurrY * (fSrcX.count() - 1);
258cb93a386Sopenharmony_ci    if (currRect == fNumRectsInLattice) {
259cb93a386Sopenharmony_ci        return false;
260cb93a386Sopenharmony_ci    }
261cb93a386Sopenharmony_ci
262cb93a386Sopenharmony_ci    const int x = fCurrX;
263cb93a386Sopenharmony_ci    const int y = fCurrY;
264cb93a386Sopenharmony_ci    SkASSERT(x >= 0 && x < fSrcX.count() - 1);
265cb93a386Sopenharmony_ci    SkASSERT(y >= 0 && y < fSrcY.count() - 1);
266cb93a386Sopenharmony_ci
267cb93a386Sopenharmony_ci    if (fSrcX.count() - 1 == ++fCurrX) {
268cb93a386Sopenharmony_ci        fCurrX = 0;
269cb93a386Sopenharmony_ci        fCurrY += 1;
270cb93a386Sopenharmony_ci    }
271cb93a386Sopenharmony_ci
272cb93a386Sopenharmony_ci    if (fRectTypes.count() > 0
273cb93a386Sopenharmony_ci        && SkToBool(SkCanvas::Lattice::kTransparent == fRectTypes[currRect])) {
274cb93a386Sopenharmony_ci        return this->next(src, dst, isFixedColor, fixedColor);
275cb93a386Sopenharmony_ci    }
276cb93a386Sopenharmony_ci
277cb93a386Sopenharmony_ci    src->setLTRB(fSrcX[x], fSrcY[y], fSrcX[x + 1], fSrcY[y + 1]);
278cb93a386Sopenharmony_ci    dst->setLTRB(fDstX[x], fDstY[y], fDstX[x + 1], fDstY[y + 1]);
279cb93a386Sopenharmony_ci    if (isFixedColor && fixedColor) {
280cb93a386Sopenharmony_ci        *isFixedColor = fRectTypes.count() > 0
281cb93a386Sopenharmony_ci                     && SkToBool(SkCanvas::Lattice::kFixedColor == fRectTypes[currRect]);
282cb93a386Sopenharmony_ci        if (*isFixedColor) {
283cb93a386Sopenharmony_ci            *fixedColor = fColors[currRect];
284cb93a386Sopenharmony_ci        }
285cb93a386Sopenharmony_ci    }
286cb93a386Sopenharmony_ci    return true;
287cb93a386Sopenharmony_ci}
288cb93a386Sopenharmony_ci
289cb93a386Sopenharmony_civoid SkLatticeIter::mapDstScaleTranslate(const SkMatrix& matrix) {
290cb93a386Sopenharmony_ci    SkASSERT(matrix.isScaleTranslate());
291cb93a386Sopenharmony_ci    SkScalar tx = matrix.getTranslateX();
292cb93a386Sopenharmony_ci    SkScalar sx = matrix.getScaleX();
293cb93a386Sopenharmony_ci    for (int i = 0; i < fDstX.count(); i++) {
294cb93a386Sopenharmony_ci        fDstX[i] = fDstX[i] * sx + tx;
295cb93a386Sopenharmony_ci    }
296cb93a386Sopenharmony_ci
297cb93a386Sopenharmony_ci    SkScalar ty = matrix.getTranslateY();
298cb93a386Sopenharmony_ci    SkScalar sy = matrix.getScaleY();
299cb93a386Sopenharmony_ci    for (int i = 0; i < fDstY.count(); i++) {
300cb93a386Sopenharmony_ci        fDstY[i] = fDstY[i] * sy + ty;
301cb93a386Sopenharmony_ci    }
302cb93a386Sopenharmony_ci}
303