Home | History | Annotate | Download | only in utils
      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