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