• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 ** Copyright 2006, The Android Open Source Project
3 **
4 ** Licensed under the Apache License, Version 2.0 (the "License");
5 ** you may not use this file except in compliance with the License.
6 ** You may obtain a copy of the License at
7 **
8 **     http://www.apache.org/licenses/LICENSE-2.0
9 **
10 ** Unless required by applicable law or agreed to in writing, software
11 ** distributed under the License is distributed on an "AS IS" BASIS,
12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 ** See the License for the specific language governing permissions and
14 ** limitations under the License.
15 */
16 
17 #include "SkNinePatch.h"
18 #include "SkCanvas.h"
19 #include "SkShader.h"
20 
21 static const uint16_t g3x3Indices[] = {
22     0, 5, 1,    0, 4, 5,
23     1, 6, 2,    1, 5, 6,
24     2, 7, 3,    2, 6, 7,
25 
26     4, 9, 5,    4, 8, 9,
27     5, 10, 6,   5, 9, 10,
28     6, 11, 7,   6, 10, 11,
29 
30     8, 13, 9,   8, 12, 13,
31     9, 14, 10,  9, 13, 14,
32     10, 15, 11, 10, 14, 15
33 };
34 
fillIndices(uint16_t indices[],int xCount,int yCount)35 static int fillIndices(uint16_t indices[], int xCount, int yCount) {
36     uint16_t* startIndices = indices;
37 
38     int n = 0;
39     for (int y = 0; y < yCount; y++) {
40         for (int x = 0; x < xCount; x++) {
41             *indices++ = n;
42             *indices++ = n + xCount + 2;
43             *indices++ = n + 1;
44 
45             *indices++ = n;
46             *indices++ = n + xCount + 1;
47             *indices++ = n + xCount + 2;
48 
49             n += 1;
50         }
51         n += 1;
52     }
53     return indices - startIndices;
54 }
55 
fillRow(SkPoint verts[],SkPoint texs[],const SkScalar vy,const SkScalar ty,const SkRect & bounds,const int32_t xDivs[],int numXDivs,const SkScalar stretchX,int width)56 static void fillRow(SkPoint verts[], SkPoint texs[],
57                     const SkScalar vy, const SkScalar ty,
58                     const SkRect& bounds, const int32_t xDivs[], int numXDivs,
59                     const SkScalar stretchX, int width) {
60     SkScalar vx = bounds.fLeft;
61     verts->set(vx, vy); verts++;
62     texs->set(0, ty); texs++;
63     for (int x = 0; x < numXDivs; x++) {
64         SkScalar tx = SkIntToScalar(xDivs[x]);
65         if (x & 1) {
66             vx += stretchX;
67         } else {
68             vx += tx;
69         }
70         verts->set(vx, vy); verts++;
71         texs->set(tx, ty); texs++;
72     }
73     verts->set(bounds.fRight, vy); verts++;
74     texs->set(SkIntToScalar(width), ty); texs++;
75 }
76 
77 struct Mesh {
78     const SkPoint*  fVerts;
79     const SkPoint*  fTexs;
80     const SkColor*  fColors;
81     const uint16_t* fIndices;
82 };
83 
DrawMesh(SkCanvas * canvas,const SkRect & bounds,const SkBitmap & bitmap,const int32_t xDivs[],int numXDivs,const int32_t yDivs[],int numYDivs,const SkPaint * paint)84 void SkNinePatch::DrawMesh(SkCanvas* canvas, const SkRect& bounds,
85                            const SkBitmap& bitmap,
86                            const int32_t xDivs[], int numXDivs,
87                            const int32_t yDivs[], int numYDivs,
88                            const SkPaint* paint) {
89     if (bounds.isEmpty() || bitmap.width() == 0 || bitmap.height() == 0) {
90         return;
91     }
92 
93     // should try a quick-reject test before calling lockPixels
94     SkAutoLockPixels alp(bitmap);
95     // after the lock, it is valid to check
96     if (!bitmap.readyToDraw()) {
97         return;
98     }
99 
100     // check for degenerate divs (just an optimization, not required)
101     {
102         int i;
103         int zeros = 0;
104         for (i = 0; i < numYDivs && yDivs[i] == 0; i++) {
105             zeros += 1;
106         }
107         numYDivs -= zeros;
108         yDivs += zeros;
109         for (i = numYDivs - 1; i >= 0 && yDivs[i] == bitmap.height(); --i) {
110             numYDivs -= 1;
111         }
112     }
113 
114     Mesh mesh;
115 
116     const int numXStretch = (numXDivs + 1) >> 1;
117     const int numYStretch = (numYDivs + 1) >> 1;
118 
119     if (numXStretch < 1 && numYStretch < 1) {
120     BITMAP_RECT:
121 //        SkDebugf("------ drawasamesh revert to bitmaprect\n");
122         canvas->drawBitmapRect(bitmap, NULL, bounds, paint);
123         return;
124     }
125 
126     if (false) {
127         int i;
128         for (i = 0; i < numXDivs; i++) {
129             SkDebugf("--- xdivs[%d] %d\n", i, xDivs[i]);
130         }
131         for (i = 0; i < numYDivs; i++) {
132             SkDebugf("--- ydivs[%d] %d\n", i, yDivs[i]);
133         }
134     }
135 
136     SkScalar stretchX = 0, stretchY = 0;
137 
138     if (numXStretch > 0) {
139         int stretchSize = 0;
140         for (int i = 1; i < numXDivs; i += 2) {
141             stretchSize += xDivs[i] - xDivs[i-1];
142         }
143         int fixed = bitmap.width() - stretchSize;
144         stretchX = (bounds.width() - SkIntToScalar(fixed)) / numXStretch;
145         if (stretchX < 0) {
146             goto BITMAP_RECT;
147         }
148     }
149 
150     if (numYStretch > 0) {
151         int stretchSize = 0;
152         for (int i = 1; i < numYDivs; i += 2) {
153             stretchSize += yDivs[i] - yDivs[i-1];
154         }
155         int fixed = bitmap.height() - stretchSize;
156         stretchY = (bounds.height() - SkIntToScalar(fixed)) / numYStretch;
157         if (stretchY < 0) {
158             goto BITMAP_RECT;
159         }
160     }
161 
162 #if 0
163     SkDebugf("---- drawasamesh [%d %d] -> [%g %g] <%d %d> (%g %g)\n",
164              bitmap.width(), bitmap.height(),
165              SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()),
166              numXDivs + 1, numYDivs + 1,
167              SkScalarToFloat(stretchX), SkScalarToFloat(stretchY));
168 #endif
169 
170     const int vCount = (numXDivs + 2) * (numYDivs + 2);
171     // number of celss * 2 (tris per cell) * 3 (verts per tri)
172     const int indexCount = (numXDivs + 1) * (numYDivs + 1) * 2 * 3;
173     // allocate 2 times, one for verts, one for texs, plus indices
174     SkAutoMalloc storage(vCount * sizeof(SkPoint) * 2 +
175                          indexCount * sizeof(uint16_t));
176     SkPoint* verts = (SkPoint*)storage.get();
177     SkPoint* texs = verts + vCount;
178     uint16_t* indices = (uint16_t*)(texs + vCount);
179 
180     mesh.fVerts = verts;
181     mesh.fTexs = texs;
182     mesh.fColors = NULL;
183     mesh.fIndices = NULL;
184 
185     // we use <= for YDivs, since the prebuild indices work for 3x2 and 3x1 too
186     if (numXDivs == 2 && numYDivs <= 2) {
187         mesh.fIndices = g3x3Indices;
188     } else {
189         SkDEBUGCODE(int n =) fillIndices(indices, numXDivs + 1, numYDivs + 1);
190         SkASSERT(n == indexCount);
191         mesh.fIndices = indices;
192     }
193 
194     SkScalar vy = bounds.fTop;
195     fillRow(verts, texs, vy, 0, bounds, xDivs, numXDivs,
196             stretchX, bitmap.width());
197     verts += numXDivs + 2;
198     texs += numXDivs + 2;
199     for (int y = 0; y < numYDivs; y++) {
200         const SkScalar ty = SkIntToScalar(yDivs[y]);
201         if (y & 1) {
202             vy += stretchY;
203         } else {
204             vy += ty;
205         }
206         fillRow(verts, texs, vy, ty, bounds, xDivs, numXDivs,
207                 stretchX, bitmap.width());
208         verts += numXDivs + 2;
209         texs += numXDivs + 2;
210     }
211     fillRow(verts, texs, bounds.fBottom, SkIntToScalar(bitmap.height()),
212             bounds, xDivs, numXDivs, stretchX, bitmap.width());
213 
214     SkShader* shader = SkShader::CreateBitmapShader(bitmap,
215                                                     SkShader::kClamp_TileMode,
216                                                     SkShader::kClamp_TileMode);
217     SkPaint p;
218     if (paint) {
219         p = *paint;
220     }
221     p.setShader(shader)->unref();
222     canvas->drawVertices(SkCanvas::kTriangles_VertexMode, vCount,
223                          mesh.fVerts, mesh.fTexs, mesh.fColors, NULL,
224                          mesh.fIndices, indexCount, p);
225 }
226 
227 ///////////////////////////////////////////////////////////////////////////////
228 
drawNineViaRects(SkCanvas * canvas,const SkRect & dst,const SkBitmap & bitmap,const SkIRect & margins,const SkPaint * paint)229 static void drawNineViaRects(SkCanvas* canvas, const SkRect& dst,
230                              const SkBitmap& bitmap, const SkIRect& margins,
231                              const SkPaint* paint) {
232     const int32_t srcX[4] = {
233         0, margins.fLeft, bitmap.width() - margins.fRight, bitmap.width()
234     };
235     const int32_t srcY[4] = {
236         0, margins.fTop, bitmap.height() - margins.fBottom, bitmap.height()
237     };
238     const SkScalar dstX[4] = {
239         dst.fLeft, dst.fLeft + SkIntToScalar(margins.fLeft),
240         dst.fRight - SkIntToScalar(margins.fRight), dst.fRight
241     };
242     const SkScalar dstY[4] = {
243         dst.fTop, dst.fTop + SkIntToScalar(margins.fTop),
244         dst.fBottom - SkIntToScalar(margins.fBottom), dst.fBottom
245     };
246 
247     SkIRect s;
248     SkRect  d;
249     for (int y = 0; y < 3; y++) {
250         s.fTop = srcY[y];
251         s.fBottom = srcY[y+1];
252         d.fTop = dstY[y];
253         d.fBottom = dstY[y+1];
254         for (int x = 0; x < 3; x++) {
255             s.fLeft = srcX[x];
256             s.fRight = srcX[x+1];
257             d.fLeft = dstX[x];
258             d.fRight = dstX[x+1];
259             canvas->drawBitmapRect(bitmap, &s, d, paint);
260         }
261     }
262 }
263 
DrawNine(SkCanvas * canvas,const SkRect & bounds,const SkBitmap & bitmap,const SkIRect & margins,const SkPaint * paint)264 void SkNinePatch::DrawNine(SkCanvas* canvas, const SkRect& bounds,
265                            const SkBitmap& bitmap, const SkIRect& margins,
266                            const SkPaint* paint) {
267     /** Our vertices code has numerical precision problems if the transformed
268      coordinates land directly on a 1/2 pixel boundary. To work around that
269      for now, we only take the vertices case if we are in opengl. Also,
270      when not in GL, the vertices impl is slower (more math) than calling
271      the viaRects code.
272      */
273     if (canvas->getViewport(NULL)) {    // returns true for OpenGL
274         int32_t xDivs[2];
275         int32_t yDivs[2];
276 
277         xDivs[0] = margins.fLeft;
278         xDivs[1] = bitmap.width() - margins.fRight;
279         yDivs[0] = margins.fTop;
280         yDivs[1] = bitmap.height() - margins.fBottom;
281 
282         SkNinePatch::DrawMesh(canvas, bounds, bitmap,
283                               xDivs, 2, yDivs, 2, paint);
284     } else {
285         drawNineViaRects(canvas, bounds, bitmap, margins, paint);
286     }
287 }
288