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