| 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 |