OLD | NEW |
| (Empty) |
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 | |
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 | |
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 | |
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 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 | |
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 | |
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 } | |
OLD | NEW |