1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/utils/SkNinePatch.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,335 @@ 1.4 + 1.5 +/* 1.6 + * Copyright 2006 The Android Open Source Project 1.7 + * 1.8 + * Use of this source code is governed by a BSD-style license that can be 1.9 + * found in the LICENSE file. 1.10 + */ 1.11 + 1.12 + 1.13 +#include "SkNinePatch.h" 1.14 +#include "SkCanvas.h" 1.15 +#include "SkShader.h" 1.16 + 1.17 +static const uint16_t g3x3Indices[] = { 1.18 + 0, 5, 1, 0, 4, 5, 1.19 + 1, 6, 2, 1, 5, 6, 1.20 + 2, 7, 3, 2, 6, 7, 1.21 + 1.22 + 4, 9, 5, 4, 8, 9, 1.23 + 5, 10, 6, 5, 9, 10, 1.24 + 6, 11, 7, 6, 10, 11, 1.25 + 1.26 + 8, 13, 9, 8, 12, 13, 1.27 + 9, 14, 10, 9, 13, 14, 1.28 + 10, 15, 11, 10, 14, 15 1.29 +}; 1.30 + 1.31 +static int fillIndices(uint16_t indices[], int xCount, int yCount) { 1.32 + uint16_t* startIndices = indices; 1.33 + 1.34 + int n = 0; 1.35 + for (int y = 0; y < yCount; y++) { 1.36 + for (int x = 0; x < xCount; x++) { 1.37 + *indices++ = n; 1.38 + *indices++ = n + xCount + 2; 1.39 + *indices++ = n + 1; 1.40 + 1.41 + *indices++ = n; 1.42 + *indices++ = n + xCount + 1; 1.43 + *indices++ = n + xCount + 2; 1.44 + 1.45 + n += 1; 1.46 + } 1.47 + n += 1; 1.48 + } 1.49 + return static_cast<int>(indices - startIndices); 1.50 +} 1.51 + 1.52 +// Computes the delta between vertices along a single axis 1.53 +static SkScalar computeVertexDelta(bool isStretchyVertex, 1.54 + SkScalar currentVertex, 1.55 + SkScalar prevVertex, 1.56 + SkScalar stretchFactor) { 1.57 + // the standard delta between vertices if no stretching is required 1.58 + SkScalar delta = currentVertex - prevVertex; 1.59 + 1.60 + // if the stretch factor is negative or zero we need to shrink the 9-patch 1.61 + // to fit within the target bounds. This means that we will eliminate all 1.62 + // stretchy areas and scale the fixed areas to fit within the target bounds. 1.63 + if (stretchFactor <= 0) { 1.64 + if (isStretchyVertex) 1.65 + delta = 0; // collapse stretchable areas 1.66 + else 1.67 + delta = SkScalarMul(delta, -stretchFactor); // scale fixed areas 1.68 + // if the stretch factor is positive then we use the standard delta for 1.69 + // fixed and scale the stretchable areas to fill the target bounds. 1.70 + } else if (isStretchyVertex) { 1.71 + delta = SkScalarMul(delta, stretchFactor); 1.72 + } 1.73 + 1.74 + return delta; 1.75 +} 1.76 + 1.77 +static void fillRow(SkPoint verts[], SkPoint texs[], 1.78 + const SkScalar vy, const SkScalar ty, 1.79 + const SkRect& bounds, const int32_t xDivs[], int numXDivs, 1.80 + const SkScalar stretchX, int width) { 1.81 + SkScalar vx = bounds.fLeft; 1.82 + verts->set(vx, vy); verts++; 1.83 + texs->set(0, ty); texs++; 1.84 + 1.85 + SkScalar prev = 0; 1.86 + for (int x = 0; x < numXDivs; x++) { 1.87 + 1.88 + const SkScalar tx = SkIntToScalar(xDivs[x]); 1.89 + vx += computeVertexDelta(x & 1, tx, prev, stretchX); 1.90 + prev = tx; 1.91 + 1.92 + verts->set(vx, vy); verts++; 1.93 + texs->set(tx, ty); texs++; 1.94 + } 1.95 + verts->set(bounds.fRight, vy); verts++; 1.96 + texs->set(SkIntToScalar(width), ty); texs++; 1.97 +} 1.98 + 1.99 +struct Mesh { 1.100 + const SkPoint* fVerts; 1.101 + const SkPoint* fTexs; 1.102 + const SkColor* fColors; 1.103 + const uint16_t* fIndices; 1.104 +}; 1.105 + 1.106 +void SkNinePatch::DrawMesh(SkCanvas* canvas, const SkRect& bounds, 1.107 + const SkBitmap& bitmap, 1.108 + const int32_t xDivs[], int numXDivs, 1.109 + const int32_t yDivs[], int numYDivs, 1.110 + const SkPaint* paint) { 1.111 + if (bounds.isEmpty() || bitmap.width() == 0 || bitmap.height() == 0) { 1.112 + return; 1.113 + } 1.114 + 1.115 + // should try a quick-reject test before calling lockPixels 1.116 + SkAutoLockPixels alp(bitmap); 1.117 + // after the lock, it is valid to check 1.118 + if (!bitmap.readyToDraw()) { 1.119 + return; 1.120 + } 1.121 + 1.122 + // check for degenerate divs (just an optimization, not required) 1.123 + { 1.124 + int i; 1.125 + int zeros = 0; 1.126 + for (i = 0; i < numYDivs && yDivs[i] == 0; i++) { 1.127 + zeros += 1; 1.128 + } 1.129 + numYDivs -= zeros; 1.130 + yDivs += zeros; 1.131 + for (i = numYDivs - 1; i >= 0 && yDivs[i] == bitmap.height(); --i) { 1.132 + numYDivs -= 1; 1.133 + } 1.134 + } 1.135 + 1.136 + Mesh mesh; 1.137 + 1.138 + const int numXStretch = (numXDivs + 1) >> 1; 1.139 + const int numYStretch = (numYDivs + 1) >> 1; 1.140 + 1.141 + if (numXStretch < 1 && numYStretch < 1) { 1.142 + canvas->drawBitmapRect(bitmap, NULL, bounds, paint); 1.143 + return; 1.144 + } 1.145 + 1.146 + if (false) { 1.147 + int i; 1.148 + for (i = 0; i < numXDivs; i++) { 1.149 + SkDebugf("--- xdivs[%d] %d\n", i, xDivs[i]); 1.150 + } 1.151 + for (i = 0; i < numYDivs; i++) { 1.152 + SkDebugf("--- ydivs[%d] %d\n", i, yDivs[i]); 1.153 + } 1.154 + } 1.155 + 1.156 + SkScalar stretchX = 0, stretchY = 0; 1.157 + 1.158 + if (numXStretch > 0) { 1.159 + int stretchSize = 0; 1.160 + for (int i = 1; i < numXDivs; i += 2) { 1.161 + stretchSize += xDivs[i] - xDivs[i-1]; 1.162 + } 1.163 + const SkScalar fixed = SkIntToScalar(bitmap.width() - stretchSize); 1.164 + if (bounds.width() >= fixed) 1.165 + stretchX = (bounds.width() - fixed) / stretchSize; 1.166 + else // reuse stretchX, but keep it negative as a signal 1.167 + stretchX = SkScalarDiv(-bounds.width(), fixed); 1.168 + } 1.169 + 1.170 + if (numYStretch > 0) { 1.171 + int stretchSize = 0; 1.172 + for (int i = 1; i < numYDivs; i += 2) { 1.173 + stretchSize += yDivs[i] - yDivs[i-1]; 1.174 + } 1.175 + const SkScalar fixed = SkIntToScalar(bitmap.height() - stretchSize); 1.176 + if (bounds.height() >= fixed) 1.177 + stretchY = (bounds.height() - fixed) / stretchSize; 1.178 + else // reuse stretchX, but keep it negative as a signal 1.179 + stretchY = SkScalarDiv(-bounds.height(), fixed); 1.180 + } 1.181 + 1.182 +#if 0 1.183 + SkDebugf("---- drawasamesh [%d %d] -> [%g %g] <%d %d> (%g %g)\n", 1.184 + bitmap.width(), bitmap.height(), 1.185 + SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()), 1.186 + numXDivs + 1, numYDivs + 1, 1.187 + SkScalarToFloat(stretchX), SkScalarToFloat(stretchY)); 1.188 +#endif 1.189 + 1.190 + const int vCount = (numXDivs + 2) * (numYDivs + 2); 1.191 + // number of celss * 2 (tris per cell) * 3 (verts per tri) 1.192 + const int indexCount = (numXDivs + 1) * (numYDivs + 1) * 2 * 3; 1.193 + // allocate 2 times, one for verts, one for texs, plus indices 1.194 + SkAutoMalloc storage(vCount * sizeof(SkPoint) * 2 + 1.195 + indexCount * sizeof(uint16_t)); 1.196 + SkPoint* verts = (SkPoint*)storage.get(); 1.197 + SkPoint* texs = verts + vCount; 1.198 + uint16_t* indices = (uint16_t*)(texs + vCount); 1.199 + 1.200 + mesh.fVerts = verts; 1.201 + mesh.fTexs = texs; 1.202 + mesh.fColors = NULL; 1.203 + mesh.fIndices = NULL; 1.204 + 1.205 + // we use <= for YDivs, since the prebuild indices work for 3x2 and 3x1 too 1.206 + if (numXDivs == 2 && numYDivs <= 2) { 1.207 + mesh.fIndices = g3x3Indices; 1.208 + } else { 1.209 + SkDEBUGCODE(int n =) fillIndices(indices, numXDivs + 1, numYDivs + 1); 1.210 + SkASSERT(n == indexCount); 1.211 + mesh.fIndices = indices; 1.212 + } 1.213 + 1.214 + SkScalar vy = bounds.fTop; 1.215 + fillRow(verts, texs, vy, 0, bounds, xDivs, numXDivs, 1.216 + stretchX, bitmap.width()); 1.217 + verts += numXDivs + 2; 1.218 + texs += numXDivs + 2; 1.219 + for (int y = 0; y < numYDivs; y++) { 1.220 + const SkScalar ty = SkIntToScalar(yDivs[y]); 1.221 + if (stretchY >= 0) { 1.222 + if (y & 1) { 1.223 + vy += stretchY; 1.224 + } else { 1.225 + vy += ty; 1.226 + } 1.227 + } else { // shrink fixed sections, and collaps stretchy sections 1.228 + if (y & 1) { 1.229 + ;// do nothing 1.230 + } else { 1.231 + vy += SkScalarMul(ty, -stretchY); 1.232 + } 1.233 + } 1.234 + fillRow(verts, texs, vy, ty, bounds, xDivs, numXDivs, 1.235 + stretchX, bitmap.width()); 1.236 + verts += numXDivs + 2; 1.237 + texs += numXDivs + 2; 1.238 + } 1.239 + fillRow(verts, texs, bounds.fBottom, SkIntToScalar(bitmap.height()), 1.240 + bounds, xDivs, numXDivs, stretchX, bitmap.width()); 1.241 + 1.242 + SkShader* shader = SkShader::CreateBitmapShader(bitmap, 1.243 + SkShader::kClamp_TileMode, 1.244 + SkShader::kClamp_TileMode); 1.245 + SkPaint p; 1.246 + if (paint) { 1.247 + p = *paint; 1.248 + } 1.249 + p.setShader(shader)->unref(); 1.250 + canvas->drawVertices(SkCanvas::kTriangles_VertexMode, vCount, 1.251 + mesh.fVerts, mesh.fTexs, mesh.fColors, NULL, 1.252 + mesh.fIndices, indexCount, p); 1.253 +} 1.254 + 1.255 +/////////////////////////////////////////////////////////////////////////////// 1.256 + 1.257 +static void drawNineViaRects(SkCanvas* canvas, const SkRect& dst, 1.258 + const SkBitmap& bitmap, const SkIRect& margins, 1.259 + const SkPaint* paint) { 1.260 + const int32_t srcX[4] = { 1.261 + 0, margins.fLeft, bitmap.width() - margins.fRight, bitmap.width() 1.262 + }; 1.263 + const int32_t srcY[4] = { 1.264 + 0, margins.fTop, bitmap.height() - margins.fBottom, bitmap.height() 1.265 + }; 1.266 + SkScalar dstX[4] = { 1.267 + dst.fLeft, dst.fLeft + SkIntToScalar(margins.fLeft), 1.268 + dst.fRight - SkIntToScalar(margins.fRight), dst.fRight 1.269 + }; 1.270 + SkScalar dstY[4] = { 1.271 + dst.fTop, dst.fTop + SkIntToScalar(margins.fTop), 1.272 + dst.fBottom - SkIntToScalar(margins.fBottom), dst.fBottom 1.273 + }; 1.274 + 1.275 + if (dstX[1] > dstX[2]) { 1.276 + dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * SkIntToScalar(margins.fLeft) / 1.277 + (SkIntToScalar(margins.fLeft) + SkIntToScalar(margins.fRight)); 1.278 + dstX[2] = dstX[1]; 1.279 + } 1.280 + 1.281 + if (dstY[1] > dstY[2]) { 1.282 + dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * SkIntToScalar(margins.fTop) / 1.283 + (SkIntToScalar(margins.fTop) + SkIntToScalar(margins.fBottom)); 1.284 + dstY[2] = dstY[1]; 1.285 + } 1.286 + 1.287 + SkIRect s; 1.288 + SkRect d; 1.289 + for (int y = 0; y < 3; y++) { 1.290 + s.fTop = srcY[y]; 1.291 + s.fBottom = srcY[y+1]; 1.292 + d.fTop = dstY[y]; 1.293 + d.fBottom = dstY[y+1]; 1.294 + for (int x = 0; x < 3; x++) { 1.295 + s.fLeft = srcX[x]; 1.296 + s.fRight = srcX[x+1]; 1.297 + d.fLeft = dstX[x]; 1.298 + d.fRight = dstX[x+1]; 1.299 + canvas->drawBitmapRect(bitmap, &s, d, paint); 1.300 + } 1.301 + } 1.302 +} 1.303 + 1.304 +void SkNinePatch::DrawNine(SkCanvas* canvas, const SkRect& bounds, 1.305 + const SkBitmap& bitmap, const SkIRect& margins, 1.306 + const SkPaint* paint) { 1.307 + /** Our vertices code has numerical precision problems if the transformed 1.308 + coordinates land directly on a 1/2 pixel boundary. To work around that 1.309 + for now, we only take the vertices case if we are in opengl. Also, 1.310 + when not in GL, the vertices impl is slower (more math) than calling 1.311 + the viaRects code. 1.312 + */ 1.313 + if (false /* is our canvas backed by a gpu?*/) { 1.314 + int32_t xDivs[2]; 1.315 + int32_t yDivs[2]; 1.316 + 1.317 + xDivs[0] = margins.fLeft; 1.318 + xDivs[1] = bitmap.width() - margins.fRight; 1.319 + yDivs[0] = margins.fTop; 1.320 + yDivs[1] = bitmap.height() - margins.fBottom; 1.321 + 1.322 + if (xDivs[0] > xDivs[1]) { 1.323 + xDivs[0] = bitmap.width() * margins.fLeft / 1.324 + (margins.fLeft + margins.fRight); 1.325 + xDivs[1] = xDivs[0]; 1.326 + } 1.327 + if (yDivs[0] > yDivs[1]) { 1.328 + yDivs[0] = bitmap.height() * margins.fTop / 1.329 + (margins.fTop + margins.fBottom); 1.330 + yDivs[1] = yDivs[0]; 1.331 + } 1.332 + 1.333 + SkNinePatch::DrawMesh(canvas, bounds, bitmap, 1.334 + xDivs, 2, yDivs, 2, paint); 1.335 + } else { 1.336 + drawNineViaRects(canvas, bounds, bitmap, margins, paint); 1.337 + } 1.338 +}