Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright (C) 2014 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 "jni.h"
     18 #include "GraphicsJNI.h"
     19 #include "core_jni_helpers.h"
     20 
     21 #include <Canvas.h>
     22 #include "SkDrawFilter.h"
     23 #include "SkGraphics.h"
     24 #include "Paint.h"
     25 #include "TypefaceImpl.h"
     26 
     27 #include "MinikinUtils.h"
     28 
     29 namespace android {
     30 
     31 namespace CanvasJNI {
     32 
     33 static Canvas* get_canvas(jlong canvasHandle) {
     34     return reinterpret_cast<Canvas*>(canvasHandle);
     35 }
     36 
     37 static void finalizer(JNIEnv* env, jobject clazz, jlong canvasHandle) {
     38     delete get_canvas(canvasHandle);
     39 }
     40 
     41 // Native wrapper constructor used by Canvas(Bitmap)
     42 static jlong initRaster(JNIEnv* env, jobject, jobject jbitmap) {
     43     SkBitmap bitmap;
     44     if (jbitmap != NULL) {
     45         GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
     46     }
     47     return reinterpret_cast<jlong>(Canvas::create_canvas(bitmap));
     48 }
     49 
     50 // Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
     51 // optionally copying canvas matrix & clip state.
     52 static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap) {
     53     SkBitmap bitmap;
     54     if (jbitmap != NULL) {
     55         GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
     56     }
     57     get_canvas(canvasHandle)->setBitmap(bitmap);
     58 }
     59 
     60 static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
     61     return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE;
     62 }
     63 
     64 static jint getWidth(JNIEnv*, jobject, jlong canvasHandle) {
     65     return static_cast<jint>(get_canvas(canvasHandle)->width());
     66 }
     67 
     68 static jint getHeight(JNIEnv*, jobject, jlong canvasHandle) {
     69     return static_cast<jint>(get_canvas(canvasHandle)->height());
     70 }
     71 
     72 static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) {
     73     return static_cast<jint>(get_canvas(canvasHandle)->getSaveCount());
     74 }
     75 
     76 static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
     77     SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
     78     return static_cast<jint>(get_canvas(canvasHandle)->save(flags));
     79 }
     80 
     81 static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
     82                       jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) {
     83     Paint* paint  = reinterpret_cast<Paint*>(paintHandle);
     84     SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
     85     return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint, flags));
     86 }
     87 
     88 static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
     89                            jfloat r, jfloat b, jint alpha, jint flagsHandle) {
     90     SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
     91     return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
     92 }
     93 
     94 static void restore(JNIEnv* env, jobject, jlong canvasHandle, jboolean throwOnUnderflow) {
     95     Canvas* canvas = get_canvas(canvasHandle);
     96     if (canvas->getSaveCount() <= 1) {  // cannot restore anymore
     97         if (throwOnUnderflow) {
     98             doThrowISE(env, "Underflow in restore - more restores than saves");
     99         }
    100         return; // compat behavior - return without throwing
    101     }
    102     canvas->restore();
    103 }
    104 
    105 static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle, jint restoreCount,
    106         jboolean throwOnUnderflow) {
    107     Canvas* canvas = get_canvas(canvasHandle);
    108     if (restoreCount < 1 || restoreCount > canvas->getSaveCount()) {
    109         if (throwOnUnderflow) {
    110             doThrowIAE(env, "Underflow in restoreToCount - more restores than saves");
    111             return;
    112         }
    113         restoreCount = 1; // compat behavior - restore as far as possible
    114     }
    115     canvas->restoreToCount(restoreCount);
    116 }
    117 
    118 static void getCTM(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
    119     SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
    120     get_canvas(canvasHandle)->getMatrix(matrix);
    121 }
    122 
    123 static void setMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
    124     const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
    125     get_canvas(canvasHandle)->setMatrix(matrix ? *matrix : SkMatrix::I());
    126 }
    127 
    128 static void concat(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
    129     const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
    130     get_canvas(canvasHandle)->concat(*matrix);
    131 }
    132 
    133 static void rotate(JNIEnv*, jobject, jlong canvasHandle, jfloat degrees) {
    134     get_canvas(canvasHandle)->rotate(degrees);
    135 }
    136 
    137 static void scale(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
    138     get_canvas(canvasHandle)->scale(sx, sy);
    139 }
    140 
    141 static void skew(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
    142     get_canvas(canvasHandle)->skew(sx, sy);
    143 }
    144 
    145 static void translate(JNIEnv*, jobject, jlong canvasHandle, jfloat dx, jfloat dy) {
    146     get_canvas(canvasHandle)->translate(dx, dy);
    147 }
    148 
    149 static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds) {
    150     SkRect   r;
    151     SkIRect ir;
    152     bool result = get_canvas(canvasHandle)->getClipBounds(&r);
    153 
    154     if (!result) {
    155         r.setEmpty();
    156     }
    157     r.round(&ir);
    158 
    159     (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
    160     return result ? JNI_TRUE : JNI_FALSE;
    161 }
    162 
    163 static jboolean quickRejectRect(JNIEnv* env, jobject, jlong canvasHandle,
    164                                 jfloat left, jfloat top, jfloat right, jfloat bottom) {
    165     bool result = get_canvas(canvasHandle)->quickRejectRect(left, top, right, bottom);
    166     return result ? JNI_TRUE : JNI_FALSE;
    167 }
    168 
    169 static jboolean quickRejectPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle) {
    170     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
    171     bool result = get_canvas(canvasHandle)->quickRejectPath(*path);
    172     return result ? JNI_TRUE : JNI_FALSE;
    173 }
    174 
    175 static jboolean clipRect(JNIEnv*, jobject, jlong canvasHandle, jfloat l, jfloat t,
    176                          jfloat r, jfloat b, jint opHandle) {
    177     SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
    178     bool nonEmptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b, op);
    179     return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
    180 }
    181 
    182 static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
    183                          jint opHandle) {
    184     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
    185     SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
    186     bool nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, op);
    187     return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
    188 }
    189 
    190 static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong deviceRgnHandle,
    191                            jint opHandle) {
    192     SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
    193     SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
    194     bool nonEmptyClip = get_canvas(canvasHandle)->clipRegion(deviceRgn, op);
    195     return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
    196 }
    197 
    198 static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) {
    199     SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(modeHandle);
    200     get_canvas(canvasHandle)->drawColor(color, mode);
    201 }
    202 
    203 static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle, jlong paintHandle) {
    204     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    205     get_canvas(canvasHandle)->drawPaint(*paint);
    206 }
    207 
    208 static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
    209                       jlong paintHandle) {
    210     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    211     get_canvas(canvasHandle)->drawPoint(x, y, *paint);
    212 }
    213 
    214 static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
    215                        jint offset, jint count, jlong paintHandle) {
    216     NPE_CHECK_RETURN_VOID(env, jptsArray);
    217     AutoJavaFloatArray autoPts(env, jptsArray);
    218     float* floats = autoPts.ptr();
    219     const int length = autoPts.length();
    220 
    221     if ((offset | count) < 0 || offset + count > length) {
    222         doThrowAIOOBE(env);
    223         return;
    224     }
    225 
    226     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    227     get_canvas(canvasHandle)->drawPoints(floats + offset, count, *paint);
    228 }
    229 
    230 static void drawLine(JNIEnv* env, jobject, jlong canvasHandle, jfloat startX, jfloat startY,
    231                      jfloat stopX, jfloat stopY, jlong paintHandle) {
    232     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    233     get_canvas(canvasHandle)->drawLine(startX, startY, stopX, stopY, *paint);
    234 }
    235 
    236 static void drawLines(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
    237                       jint offset, jint count, jlong paintHandle) {
    238     NPE_CHECK_RETURN_VOID(env, jptsArray);
    239     AutoJavaFloatArray autoPts(env, jptsArray);
    240     float* floats = autoPts.ptr();
    241     const int length = autoPts.length();
    242 
    243     if ((offset | count) < 0 || offset + count > length) {
    244         doThrowAIOOBE(env);
    245         return;
    246     }
    247 
    248     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    249     get_canvas(canvasHandle)->drawLines(floats + offset, count, *paint);
    250 }
    251 
    252 static void drawRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
    253                      jfloat right, jfloat bottom, jlong paintHandle) {
    254     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    255     get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint);
    256 }
    257 
    258 static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
    259                           jfloat right, jfloat bottom, jfloat rx, jfloat ry, jlong paintHandle) {
    260     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    261     get_canvas(canvasHandle)->drawRoundRect(left, top, right, bottom, rx, ry, *paint);
    262 }
    263 
    264 static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx, jfloat cy,
    265                        jfloat radius, jlong paintHandle) {
    266     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    267     get_canvas(canvasHandle)->drawCircle(cx, cy, radius, *paint);
    268 }
    269 
    270 static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
    271                      jfloat right, jfloat bottom, jlong paintHandle) {
    272     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    273     get_canvas(canvasHandle)->drawOval(left, top, right, bottom, *paint);
    274 }
    275 
    276 static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
    277                     jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle,
    278                     jboolean useCenter, jlong paintHandle) {
    279     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    280     get_canvas(canvasHandle)->drawArc(left, top, right, bottom, startAngle, sweepAngle,
    281                                        useCenter, *paint);
    282 }
    283 
    284 static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
    285                      jlong paintHandle) {
    286     const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
    287     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    288     get_canvas(canvasHandle)->drawPath(*path, *paint);
    289 }
    290 
    291 static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
    292                          jint modeHandle, jint vertexCount,
    293                          jfloatArray jverts, jint vertIndex,
    294                          jfloatArray jtexs, jint texIndex,
    295                          jintArray jcolors, jint colorIndex,
    296                          jshortArray jindices, jint indexIndex,
    297                          jint indexCount, jlong paintHandle) {
    298     AutoJavaFloatArray  vertA(env, jverts, vertIndex + vertexCount);
    299     AutoJavaFloatArray  texA(env, jtexs, texIndex + vertexCount);
    300     AutoJavaIntArray    colorA(env, jcolors, colorIndex + vertexCount);
    301     AutoJavaShortArray  indexA(env, jindices, indexIndex + indexCount);
    302 
    303     const float* verts = vertA.ptr() + vertIndex;
    304     const float* texs = texA.ptr() + vertIndex;
    305     const int* colors = NULL;
    306     const uint16_t* indices = NULL;
    307 
    308     if (jcolors != NULL) {
    309         colors = colorA.ptr() + colorIndex;
    310     }
    311     if (jindices != NULL) {
    312         indices = (const uint16_t*)(indexA.ptr() + indexIndex);
    313     }
    314 
    315     SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(modeHandle);
    316     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    317     get_canvas(canvasHandle)->drawVertices(mode, vertexCount, verts, texs, colors,
    318                                            indices, indexCount, *paint);
    319 }
    320 
    321 static void drawBitmap(JNIEnv* env, jobject jcanvas, jlong canvasHandle, jobject jbitmap,
    322                        jfloat left, jfloat top, jlong paintHandle, jint canvasDensity,
    323                        jint screenDensity, jint bitmapDensity) {
    324     Canvas* canvas = get_canvas(canvasHandle);
    325     SkBitmap bitmap;
    326     GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
    327     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    328 
    329     if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
    330         if (screenDensity != 0 && screenDensity != bitmapDensity) {
    331             Paint filteredPaint;
    332             if (paint) {
    333                 filteredPaint = *paint;
    334             }
    335             filteredPaint.setFilterQuality(kLow_SkFilterQuality);
    336             canvas->drawBitmap(bitmap, left, top, &filteredPaint);
    337         } else {
    338             canvas->drawBitmap(bitmap, left, top, paint);
    339         }
    340     } else {
    341         canvas->save(SkCanvas::kMatrixClip_SaveFlag);
    342         SkScalar scale = canvasDensity / (float)bitmapDensity;
    343         canvas->translate(left, top);
    344         canvas->scale(scale, scale);
    345 
    346         Paint filteredPaint;
    347         if (paint) {
    348             filteredPaint = *paint;
    349         }
    350         filteredPaint.setFilterQuality(kLow_SkFilterQuality);
    351 
    352         canvas->drawBitmap(bitmap, 0, 0, &filteredPaint);
    353         canvas->restore();
    354     }
    355 }
    356 
    357 static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap,
    358                              jlong matrixHandle, jlong paintHandle) {
    359     const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
    360     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    361     SkBitmap bitmap;
    362     GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
    363     get_canvas(canvasHandle)->drawBitmap(bitmap, *matrix, paint);
    364 }
    365 
    366 static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap,
    367                            float srcLeft, float srcTop, float srcRight, float srcBottom,
    368                            float dstLeft, float dstTop, float dstRight, float dstBottom,
    369                            jlong paintHandle, jint screenDensity, jint bitmapDensity) {
    370     Canvas* canvas = get_canvas(canvasHandle);
    371     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    372 
    373     SkBitmap bitmap;
    374     GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
    375     if (screenDensity != 0 && screenDensity != bitmapDensity) {
    376         Paint filteredPaint;
    377         if (paint) {
    378             filteredPaint = *paint;
    379         }
    380         filteredPaint.setFilterQuality(kLow_SkFilterQuality);
    381         canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
    382                            dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
    383     } else {
    384         canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
    385                            dstLeft, dstTop, dstRight, dstBottom, paint);
    386     }
    387 }
    388 
    389 static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
    390                             jintArray jcolors, jint offset, jint stride,
    391                             jfloat x, jfloat y, jint width, jint height,
    392                             jboolean hasAlpha, jlong paintHandle) {
    393     // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
    394     // correct the alphaType to kOpaque_SkAlphaType.
    395     SkImageInfo info = SkImageInfo::Make(width, height,
    396                            hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
    397                            kPremul_SkAlphaType);
    398     SkBitmap bitmap;
    399     bitmap.setInfo(info);
    400     if (!GraphicsJNI::allocatePixels(env, &bitmap, NULL)) {
    401         return;
    402     }
    403 
    404     if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride, 0, 0, width, height, bitmap)) {
    405         return;
    406     }
    407 
    408     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    409     get_canvas(canvasHandle)->drawBitmap(bitmap, x, y, paint);
    410 }
    411 
    412 static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap,
    413                            jint meshWidth, jint meshHeight, jfloatArray jverts,
    414                            jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) {
    415     const int ptCount = (meshWidth + 1) * (meshHeight + 1);
    416     AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1));
    417     AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
    418 
    419     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    420     SkBitmap bitmap;
    421     GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
    422     get_canvas(canvasHandle)->drawBitmapMesh(bitmap, meshWidth, meshHeight,
    423                                              vertA.ptr(), colorA.ptr(), paint);
    424 }
    425 
    426 class DrawTextFunctor {
    427 public:
    428     DrawTextFunctor(const Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos,
    429                     const SkPaint& paint, float x, float y, MinikinRect& bounds,
    430                     float totalAdvance)
    431             : layout(layout), canvas(canvas), glyphs(glyphs), pos(pos), paint(paint),
    432               x(x), y(y), bounds(bounds), totalAdvance(totalAdvance) { }
    433 
    434     void operator()(size_t start, size_t end) {
    435         if (canvas->drawTextAbsolutePos()) {
    436             for (size_t i = start; i < end; i++) {
    437                 glyphs[i] = layout.getGlyphId(i);
    438                 pos[2 * i] = x + layout.getX(i);
    439                 pos[2 * i + 1] = y + layout.getY(i);
    440             }
    441         } else {
    442             for (size_t i = start; i < end; i++) {
    443                 glyphs[i] = layout.getGlyphId(i);
    444                 pos[2 * i] = layout.getX(i);
    445                 pos[2 * i + 1] = layout.getY(i);
    446             }
    447         }
    448 
    449         size_t glyphCount = end - start;
    450         canvas->drawText(glyphs + start, pos + (2 * start), glyphCount, paint, x, y,
    451                          bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom,
    452                          totalAdvance);
    453     }
    454 private:
    455     const Layout& layout;
    456     Canvas* canvas;
    457     uint16_t* glyphs;
    458     float* pos;
    459     const SkPaint& paint;
    460     float x;
    461     float y;
    462     MinikinRect& bounds;
    463     float totalAdvance;
    464 };
    465 
    466 // Same values used by Skia
    467 #define kStdStrikeThru_Offset   (-6.0f / 21.0f)
    468 #define kStdUnderline_Offset    (1.0f / 9.0f)
    469 #define kStdUnderline_Thickness (1.0f / 18.0f)
    470 
    471 void drawTextDecorations(Canvas* canvas, float x, float y, float length, const SkPaint& paint) {
    472     uint32_t flags;
    473     SkDrawFilter* drawFilter = canvas->getDrawFilter();
    474     if (drawFilter) {
    475         SkPaint paintCopy(paint);
    476         drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
    477         flags = paintCopy.getFlags();
    478     } else {
    479         flags = paint.getFlags();
    480     }
    481     if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
    482         SkScalar left = x;
    483         SkScalar right = x + length;
    484         float textSize = paint.getTextSize();
    485         float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
    486         if (flags & SkPaint::kUnderlineText_Flag) {
    487             SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
    488             SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
    489             canvas->drawRect(left, top, right, bottom, paint);
    490         }
    491         if (flags & SkPaint::kStrikeThruText_Flag) {
    492             SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
    493             SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
    494             canvas->drawRect(left, top, right, bottom, paint);
    495         }
    496     }
    497 }
    498 
    499 void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int contextCount,
    500              float x, float y, int bidiFlags, const Paint& origPaint, TypefaceImpl* typeface) {
    501     // minikin may modify the original paint
    502     Paint paint(origPaint);
    503 
    504     Layout layout;
    505     MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount);
    506 
    507     size_t nGlyphs = layout.nGlyphs();
    508     uint16_t* glyphs = new uint16_t[nGlyphs];
    509     float* pos = new float[nGlyphs * 2];
    510 
    511     x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
    512 
    513     MinikinRect bounds;
    514     layout.getBounds(&bounds);
    515     if (!canvas->drawTextAbsolutePos()) {
    516         bounds.offset(x, y);
    517     }
    518 
    519     DrawTextFunctor f(layout, canvas, glyphs, pos, paint, x, y, bounds, layout.getAdvance());
    520     MinikinUtils::forFontRun(layout, &paint, f);
    521 
    522     drawTextDecorations(canvas, x, y, layout.getAdvance(), paint);
    523 
    524     delete[] glyphs;
    525     delete[] pos;
    526 }
    527 
    528 static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
    529                           jint index, jint count, jfloat x, jfloat y, jint bidiFlags,
    530                           jlong paintHandle, jlong typefaceHandle) {
    531     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    532     TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
    533     jchar* jchars = env->GetCharArrayElements(text, NULL);
    534     drawText(get_canvas(canvasHandle), jchars + index, 0, count, count, x, y,
    535                                        bidiFlags, *paint, typeface);
    536     env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
    537 }
    538 
    539 static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
    540                            jint start, jint end, jfloat x, jfloat y, jint bidiFlags,
    541                            jlong paintHandle, jlong typefaceHandle) {
    542     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    543     TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
    544     const int count = end - start;
    545     const jchar* jchars = env->GetStringChars(text, NULL);
    546     drawText(get_canvas(canvasHandle), jchars + start, 0, count, count, x, y,
    547                                        bidiFlags, *paint, typeface);
    548     env->ReleaseStringChars(text, jchars);
    549 }
    550 
    551 static void drawTextRunChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
    552                              jint count, jint contextIndex, jint contextCount, jfloat x, jfloat y,
    553                              jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
    554     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    555     TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
    556 
    557     const int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
    558     jchar* jchars = env->GetCharArrayElements(text, NULL);
    559     drawText(get_canvas(canvasHandle), jchars + contextIndex, index - contextIndex, count,
    560                                        contextCount, x, y, bidiFlags, *paint, typeface);
    561     env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
    562 }
    563 
    564 static void drawTextRunString(JNIEnv* env, jobject obj, jlong canvasHandle, jstring text,
    565                               jint start, jint end, jint contextStart, jint contextEnd,
    566                               jfloat x, jfloat y, jboolean isRtl, jlong paintHandle,
    567                               jlong typefaceHandle) {
    568     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    569     TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
    570 
    571     int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
    572     jint count = end - start;
    573     jint contextCount = contextEnd - contextStart;
    574     const jchar* jchars = env->GetStringChars(text, NULL);
    575     drawText(get_canvas(canvasHandle), jchars + contextStart, start - contextStart, count,
    576                                        contextCount, x, y, bidiFlags, *paint, typeface);
    577     env->ReleaseStringChars(text, jchars);
    578 }
    579 
    580 class DrawTextOnPathFunctor {
    581 public:
    582     DrawTextOnPathFunctor(const Layout& layout, Canvas* canvas, float hOffset,
    583                 float vOffset, const Paint& paint, const SkPath& path)
    584             : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
    585                 paint(paint), path(path) {
    586     }
    587     void operator()(size_t start, size_t end) {
    588         uint16_t glyphs[1];
    589         for (size_t i = start; i < end; i++) {
    590             glyphs[0] = layout.getGlyphId(i);
    591             float x = hOffset + layout.getX(i);
    592             float y = vOffset + layout.getY(i);
    593             canvas->drawTextOnPath(glyphs, 1, path, x, y, paint);
    594         }
    595     }
    596 private:
    597     const Layout& layout;
    598     Canvas* canvas;
    599     float hOffset;
    600     float vOffset;
    601     const Paint& paint;
    602     const SkPath& path;
    603 };
    604 
    605 static void drawTextOnPath(Canvas* canvas, const uint16_t* text, int count, int bidiFlags,
    606                            const SkPath& path, float hOffset, float vOffset,
    607                            const Paint& paint, TypefaceImpl* typeface) {
    608     Paint paintCopy(paint);
    609     Layout layout;
    610     MinikinUtils::doLayout(&layout, &paintCopy, bidiFlags, typeface, text, 0, count, count);
    611     hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
    612 
    613     // Set align to left for drawing, as we don't want individual
    614     // glyphs centered or right-aligned; the offset above takes
    615     // care of all alignment.
    616     paintCopy.setTextAlign(Paint::kLeft_Align);
    617 
    618     DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paintCopy, path);
    619     MinikinUtils::forFontRun(layout, &paintCopy, f);
    620 }
    621 
    622 static void drawTextOnPathChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
    623                                 jint index, jint count, jlong pathHandle, jfloat hOffset,
    624                                 jfloat vOffset, jint bidiFlags, jlong paintHandle,
    625                                 jlong typefaceHandle) {
    626     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
    627     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    628     TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
    629 
    630     jchar* jchars = env->GetCharArrayElements(text, NULL);
    631 
    632     drawTextOnPath(get_canvas(canvasHandle), jchars + index, count, bidiFlags, *path,
    633                    hOffset, vOffset, *paint, typeface);
    634 
    635     env->ReleaseCharArrayElements(text, jchars, 0);
    636 }
    637 
    638 static void drawTextOnPathString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
    639                                  jlong pathHandle, jfloat hOffset, jfloat vOffset,
    640                                  jint bidiFlags, jlong paintHandle, jlong typefaceHandle) {
    641     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
    642     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    643     TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
    644 
    645     const jchar* jchars = env->GetStringChars(text, NULL);
    646     int count = env->GetStringLength(text);
    647 
    648     drawTextOnPath(get_canvas(canvasHandle), jchars, count, bidiFlags, *path,
    649                    hOffset, vOffset, *paint, typeface);
    650 
    651     env->ReleaseStringChars(text, jchars);
    652 }
    653 
    654 static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle, jlong filterHandle) {
    655     get_canvas(canvasHandle)->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
    656 }
    657 
    658 static void freeCaches(JNIEnv* env, jobject) {
    659     SkGraphics::PurgeFontCache();
    660 }
    661 
    662 static void freeTextLayoutCaches(JNIEnv* env, jobject) {
    663     Layout::purgeCaches();
    664 }
    665 
    666 }; // namespace CanvasJNI
    667 
    668 static JNINativeMethod gMethods[] = {
    669     {"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
    670     {"initRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster},
    671     {"native_setBitmap", "(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
    672     {"native_isOpaque","(J)Z", (void*) CanvasJNI::isOpaque},
    673     {"native_getWidth","(J)I", (void*) CanvasJNI::getWidth},
    674     {"native_getHeight","(J)I", (void*) CanvasJNI::getHeight},
    675     {"native_save","(JI)I", (void*) CanvasJNI::save},
    676     {"native_saveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
    677     {"native_saveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
    678     {"native_getSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
    679     {"native_restore","(JZ)V", (void*) CanvasJNI::restore},
    680     {"native_restoreToCount","(JIZ)V", (void*) CanvasJNI::restoreToCount},
    681     {"native_getCTM", "(JJ)V", (void*)CanvasJNI::getCTM},
    682     {"native_setMatrix","(JJ)V", (void*) CanvasJNI::setMatrix},
    683     {"native_concat","(JJ)V", (void*) CanvasJNI::concat},
    684     {"native_rotate","(JF)V", (void*) CanvasJNI::rotate},
    685     {"native_scale","(JFF)V", (void*) CanvasJNI::scale},
    686     {"native_skew","(JFF)V", (void*) CanvasJNI::skew},
    687     {"native_translate","(JFF)V", (void*) CanvasJNI::translate},
    688     {"native_getClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
    689     {"native_quickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath},
    690     {"native_quickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
    691     {"native_clipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect},
    692     {"native_clipPath","(JJI)Z", (void*) CanvasJNI::clipPath},
    693     {"native_clipRegion","(JJI)Z", (void*) CanvasJNI::clipRegion},
    694     {"native_drawColor","(JII)V", (void*) CanvasJNI::drawColor},
    695     {"native_drawPaint","(JJ)V", (void*) CanvasJNI::drawPaint},
    696     {"native_drawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint},
    697     {"native_drawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
    698     {"native_drawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine},
    699     {"native_drawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines},
    700     {"native_drawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect},
    701     {"native_drawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
    702     {"native_drawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle},
    703     {"native_drawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval},
    704     {"native_drawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
    705     {"native_drawPath","(JJJ)V", (void*) CanvasJNI::drawPath},
    706     {"nativeDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
    707     {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFJIII)V", (void*) CanvasJNI::drawBitmap},
    708     {"nativeDrawBitmapMatrix", "(JLandroid/graphics/Bitmap;JJ)V", (void*)CanvasJNI::drawBitmapMatrix},
    709     {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
    710     {"native_drawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
    711     {"nativeDrawBitmapMesh", "(JLandroid/graphics/Bitmap;II[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
    712     {"native_drawText","(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars},
    713     {"native_drawText","(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString},
    714     {"native_drawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
    715     {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString},
    716     {"native_drawTextOnPath","(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars},
    717     {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString},
    718     {"nativeSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setDrawFilter},
    719     {"freeCaches", "()V", (void*) CanvasJNI::freeCaches},
    720     {"freeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches}
    721 };
    722 
    723 int register_android_graphics_Canvas(JNIEnv* env) {
    724     return RegisterMethodsOrDie(env, "android/graphics/Canvas", gMethods, NELEM(gMethods));
    725 }
    726 
    727 }; // namespace android
    728