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