summaryrefslogtreecommitdiffstats
path: root/core/jni/android/graphics/Canvas.cpp
diff options
context:
space:
mode:
authorJohn Reck <jreck@google.com>2014-07-17 10:30:22 -0700
committerJohn Reck <jreck@google.com>2014-07-17 17:36:24 +0000
commit580ff8142b7d0455d0d41ee77572b4f55dd935f0 (patch)
treee6add1358ba52db2784f488cc23835980be2cf26 /core/jni/android/graphics/Canvas.cpp
parent4fc90de51b58771c079f2e0d51d15abd28a5015b (diff)
downloadframeworks_base-580ff8142b7d0455d0d41ee77572b4f55dd935f0.zip
frameworks_base-580ff8142b7d0455d0d41ee77572b4f55dd935f0.tar.gz
frameworks_base-580ff8142b7d0455d0d41ee77572b4f55dd935f0.tar.bz2
Revert "Separate Canvas JNI code from the implementation."
This reverts commit e28a5afee885cd69a5be5809f88116b601cb1a72. Appears to cause memory corruption and random appearances of chinese Bug: 16343240 Bug: 16336642 Change-Id: Ife169181f40adff4b12948ed5f9d3a88dcec935b
Diffstat (limited to 'core/jni/android/graphics/Canvas.cpp')
-rw-r--r--core/jni/android/graphics/Canvas.cpp1330
1 files changed, 1330 insertions, 0 deletions
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
new file mode 100644
index 0000000..6254f3d
--- /dev/null
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -0,0 +1,1330 @@
+/*
+ * Copyright (C) 2006-2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include "SkCanvas.h"
+#include "SkClipStack.h"
+#include "SkDevice.h"
+#include "SkDeque.h"
+#include "SkDrawFilter.h"
+#include "SkGraphics.h"
+#include <SkImageInfo.h>
+#include "SkPorterDuff.h"
+#include "SkShader.h"
+#include "SkTArray.h"
+#include "SkTemplates.h"
+
+#include <minikin/Layout.h>
+#include "MinikinSkia.h"
+#include "MinikinUtils.h"
+
+#include "TypefaceImpl.h"
+
+#include "unicode/ubidi.h"
+#include "unicode/ushape.h"
+
+#include <utils/Log.h>
+
+namespace android {
+
+class ClipCopier : public SkCanvas::ClipVisitor {
+public:
+ ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {}
+
+ virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) {
+ m_dstCanvas->clipRect(rect, op, antialias);
+ }
+ virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) {
+ m_dstCanvas->clipRRect(rrect, op, antialias);
+ }
+ virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) {
+ m_dstCanvas->clipPath(path, op, antialias);
+ }
+
+private:
+ SkCanvas* m_dstCanvas;
+};
+
+// Holds an SkCanvas reference plus additional native data.
+class NativeCanvasWrapper {
+private:
+ struct SaveRec {
+ int saveCount;
+ SkCanvas::SaveFlags saveFlags;
+ };
+
+public:
+ NativeCanvasWrapper(SkCanvas* canvas)
+ : mCanvas(canvas)
+ , mSaveStack(NULL) {
+ SkASSERT(canvas);
+ }
+
+ ~NativeCanvasWrapper() {
+ delete mSaveStack;
+ }
+
+ SkCanvas* getCanvas() const {
+ return mCanvas.get();
+ }
+
+ void setCanvas(SkCanvas* canvas) {
+ SkASSERT(canvas);
+ mCanvas.reset(canvas);
+
+ delete mSaveStack;
+ mSaveStack = NULL;
+ }
+
+ int save(SkCanvas::SaveFlags flags) {
+ int count = mCanvas->save();
+ recordPartialSave(flags);
+ return count;
+ }
+
+ int saveLayer(const SkRect* bounds, const SkPaint* paint,
+ SkCanvas::SaveFlags flags) {
+ int count = mCanvas->saveLayer(bounds, paint,
+ static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag));
+ recordPartialSave(flags);
+ return count;
+ }
+
+ int saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
+ SkCanvas::SaveFlags flags) {
+ int count = mCanvas->saveLayerAlpha(bounds, alpha,
+ static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag));
+ recordPartialSave(flags);
+ return count;
+ }
+
+ void restore() {
+ const SaveRec* rec = (NULL == mSaveStack)
+ ? NULL
+ : static_cast<SaveRec*>(mSaveStack->back());
+ int currentSaveCount = mCanvas->getSaveCount() - 1;
+ SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount);
+
+ if (NULL == rec || rec->saveCount != currentSaveCount) {
+ // Fast path - no record for this frame.
+ mCanvas->restore();
+ return;
+ }
+
+ bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag);
+ bool preserveClip = !(rec->saveFlags & SkCanvas::kClip_SaveFlag);
+
+ SkMatrix savedMatrix;
+ if (preserveMatrix) {
+ savedMatrix = mCanvas->getTotalMatrix();
+ }
+
+ SkTArray<SkClipStack::Element> savedClips;
+ if (preserveClip) {
+ saveClipsForFrame(savedClips, currentSaveCount);
+ }
+
+ mCanvas->restore();
+
+ if (preserveMatrix) {
+ mCanvas->setMatrix(savedMatrix);
+ }
+
+ if (preserveClip && !savedClips.empty()) {
+ applyClips(savedClips);
+ }
+
+ mSaveStack->pop_back();
+ }
+
+private:
+ void recordPartialSave(SkCanvas::SaveFlags flags) {
+ // A partial save is a save operation which doesn't capture the full canvas state.
+ // (either kMatrix_SaveFlags or kClip_SaveFlag is missing).
+
+ // Mask-out non canvas state bits.
+ flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag);
+
+ if (SkCanvas::kMatrixClip_SaveFlag == flags) {
+ // not a partial save.
+ return;
+ }
+
+ if (NULL == mSaveStack) {
+ mSaveStack = new SkDeque(sizeof(struct SaveRec), 8);
+ }
+
+ SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
+ // Store the save counter in the SkClipStack domain.
+ // (0-based, equal to the number of save ops on the stack).
+ rec->saveCount = mCanvas->getSaveCount() - 1;
+ rec->saveFlags = flags;
+ }
+
+ void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips,
+ int frameSaveCount) {
+ SkClipStack::Iter clipIterator(*mCanvas->getClipStack(),
+ SkClipStack::Iter::kTop_IterStart);
+ while (const SkClipStack::Element* elem = clipIterator.next()) {
+ if (elem->getSaveCount() < frameSaveCount) {
+ // done with the current frame.
+ break;
+ }
+ SkASSERT(elem->getSaveCount() == frameSaveCount);
+ clips.push_back(*elem);
+ }
+ }
+
+ void applyClips(const SkTArray<SkClipStack::Element>& clips) {
+ ClipCopier clipCopier(mCanvas);
+
+ // The clip stack stores clips in device space.
+ SkMatrix origMatrix = mCanvas->getTotalMatrix();
+ mCanvas->resetMatrix();
+
+ // We pushed the clips in reverse order.
+ for (int i = clips.count() - 1; i >= 0; --i) {
+ clips[i].replay(&clipCopier);
+ }
+
+ mCanvas->setMatrix(origMatrix);
+ }
+
+ SkAutoTUnref<SkCanvas> mCanvas;
+ SkDeque* mSaveStack; // lazily allocated, tracks partial saves.
+};
+
+// Returns true if the SkCanvas's clip is non-empty.
+static jboolean hasNonEmptyClip(const SkCanvas& canvas) {
+ bool emptyClip = canvas.isClipEmpty();
+ return emptyClip ? JNI_FALSE : JNI_TRUE;
+}
+
+class SkCanvasGlue {
+public:
+ // Get the native wrapper for a given handle.
+ static inline NativeCanvasWrapper* getNativeWrapper(jlong nativeHandle) {
+ SkASSERT(nativeHandle);
+ return reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
+ }
+
+ // Get the SkCanvas for a given native handle.
+ static inline SkCanvas* getNativeCanvas(jlong nativeHandle) {
+ NativeCanvasWrapper* wrapper = getNativeWrapper(nativeHandle);
+ SkCanvas* canvas = wrapper->getCanvas();
+ SkASSERT(canvas);
+
+ return canvas;
+ }
+
+ // Construct an SkCanvas from the bitmap.
+ static SkCanvas* createCanvas(SkBitmap* bitmap) {
+ if (bitmap) {
+ return SkNEW_ARGS(SkCanvas, (*bitmap));
+ }
+
+ // Create an empty bitmap device to prevent callers from crashing
+ // if they attempt to draw into this canvas.
+ SkBitmap emptyBitmap;
+ return new SkCanvas(emptyBitmap);
+ }
+
+ // Copy the canvas matrix & clip state.
+ static void copyCanvasState(SkCanvas* srcCanvas, SkCanvas* dstCanvas) {
+ if (srcCanvas && dstCanvas) {
+ dstCanvas->setMatrix(srcCanvas->getTotalMatrix());
+ if (NULL != srcCanvas->getDevice() && NULL != dstCanvas->getDevice()) {
+ ClipCopier copier(dstCanvas);
+ srcCanvas->replayClips(&copier);
+ }
+ }
+ }
+
+ // Native JNI handlers
+ static void finalizer(JNIEnv* env, jobject clazz, jlong nativeHandle) {
+ NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
+ delete wrapper;
+ }
+
+ // Native wrapper constructor used by Canvas(Bitmap)
+ static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
+ // No check - 0 is a valid bitmapHandle.
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ SkCanvas* canvas = createCanvas(bitmap);
+
+ return reinterpret_cast<jlong>(new NativeCanvasWrapper(canvas));
+ }
+
+ // Native wrapper constructor used by Canvas(native_canvas)
+ static jlong initCanvas(JNIEnv* env, jobject, jlong canvasHandle) {
+ SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ return reinterpret_cast<jlong>(new NativeCanvasWrapper(canvas));
+ }
+
+ // Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
+ // optionally copying canvas matrix & clip state.
+ static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+ jboolean copyState) {
+ NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(canvasHandle);
+ SkCanvas* newCanvas = createCanvas(reinterpret_cast<SkBitmap*>(bitmapHandle));
+ NPE_CHECK_RETURN_VOID(env, newCanvas);
+
+ if (copyState == JNI_TRUE) {
+ copyCanvasState(wrapper->getCanvas(), newCanvas);
+ }
+
+ // setCanvas() unrefs the old canvas.
+ wrapper->setCanvas(newCanvas);
+ }
+
+ static void freeCaches(JNIEnv* env, jobject) {
+ SkGraphics::PurgeFontCache();
+ }
+
+ static void freeTextLayoutCaches(JNIEnv* env, jobject) {
+ Layout::purgeCaches();
+ }
+
+ static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ bool result = canvas->getDevice()->accessBitmap(false).isOpaque();
+ return result ? JNI_TRUE : JNI_FALSE;
+ }
+
+ static jint getWidth(JNIEnv*, jobject, jlong canvasHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ int width = canvas->getDevice()->accessBitmap(false).width();
+ return static_cast<jint>(width);
+ }
+
+ static jint getHeight(JNIEnv*, jobject, jlong canvasHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ int height = canvas->getDevice()->accessBitmap(false).height();
+ return static_cast<jint>(height);
+ }
+
+ static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
+ NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
+ SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+ return static_cast<jint>(wrapper->save(flags));
+ }
+
+ static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle,
+ jfloat l, jfloat t, jfloat r, jfloat b,
+ jlong paintHandle, jint flagsHandle) {
+ NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+ SkRect bounds;
+ bounds.set(l, t, r, b);
+ return static_cast<jint>(wrapper->saveLayer(&bounds, paint, flags));
+ }
+
+ static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle,
+ jfloat l, jfloat t, jfloat r, jfloat b,
+ jint alpha, jint flagsHandle) {
+ NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
+ SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+ SkRect bounds;
+ bounds.set(l, t, r, b);
+ return static_cast<jint>(wrapper->saveLayerAlpha(&bounds, alpha, flags));
+ }
+
+ static void restore(JNIEnv* env, jobject, jlong canvasHandle) {
+ NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
+ if (wrapper->getCanvas()->getSaveCount() <= 1) { // cannot restore anymore
+ doThrowISE(env, "Underflow in restore");
+ return;
+ }
+ wrapper->restore();
+ }
+
+ static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) {
+ return static_cast<jint>(getNativeCanvas(canvasHandle)->getSaveCount());
+ }
+
+ static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle,
+ jint restoreCount) {
+ NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
+ if (restoreCount < 1) {
+ doThrowIAE(env, "Underflow in restoreToCount");
+ return;
+ }
+
+ while (wrapper->getCanvas()->getSaveCount() > restoreCount) {
+ wrapper->restore();
+ }
+ }
+
+ static void translate(JNIEnv*, jobject, jlong canvasHandle,
+ jfloat dx, jfloat dy) {
+ getNativeCanvas(canvasHandle)->translate(dx, dy);
+ }
+
+ static void scale__FF(JNIEnv*, jobject, jlong canvasHandle,
+ jfloat sx, jfloat sy) {
+ getNativeCanvas(canvasHandle)->scale(sx, sy);
+ }
+
+ static void rotate__F(JNIEnv*, jobject, jlong canvasHandle,
+ jfloat degrees) {
+ getNativeCanvas(canvasHandle)->rotate(degrees);
+ }
+
+ static void skew__FF(JNIEnv*, jobject, jlong canvasHandle,
+ jfloat sx, jfloat sy) {
+ getNativeCanvas(canvasHandle)->skew(sx, sy);
+ }
+
+ static void concat(JNIEnv* env, jobject, jlong canvasHandle,
+ jlong matrixHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+ canvas->concat(*matrix);
+ }
+
+ static void setMatrix(JNIEnv* env, jobject, jlong canvasHandle,
+ jlong matrixHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+ if (NULL == matrix) {
+ canvas->resetMatrix();
+ } else {
+ canvas->setMatrix(*matrix);
+ }
+ }
+
+ static jboolean clipRect(JNIEnv*, jobject, jlong canvasHandle,
+ jfloat left, jfloat top, jfloat right,
+ jfloat bottom, jint op) {
+ SkRect r;
+ r.set(left, top, right, bottom);
+ SkCanvas* c = getNativeCanvas(canvasHandle);
+ c->clipRect(r, static_cast<SkRegion::Op>(op));
+ return hasNonEmptyClip(*c);
+ }
+
+ static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle,
+ jlong pathHandle, jint op) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ canvas->clipPath(*reinterpret_cast<SkPath*>(pathHandle),
+ static_cast<SkRegion::Op>(op));
+ return hasNonEmptyClip(*canvas);
+ }
+
+ static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle,
+ jlong deviceRgnHandle, jint op) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
+ SkPath rgnPath;
+ if (deviceRgn->getBoundaryPath(&rgnPath)) {
+ // The region is specified in device space.
+ SkMatrix savedMatrix = canvas->getTotalMatrix();
+ canvas->resetMatrix();
+ canvas->clipPath(rgnPath, static_cast<SkRegion::Op>(op));
+ canvas->setMatrix(savedMatrix);
+ } else {
+ canvas->clipRect(SkRect::MakeEmpty(), static_cast<SkRegion::Op>(op));
+ }
+ return hasNonEmptyClip(*canvas);
+ }
+
+ static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle,
+ jlong filterHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ canvas->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
+ }
+
+ static jboolean quickReject__Path(JNIEnv* env, jobject, jlong canvasHandle,
+ jlong pathHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ bool result = canvas->quickReject(*reinterpret_cast<SkPath*>(pathHandle));
+ return result ? JNI_TRUE : JNI_FALSE;
+ }
+
+ static jboolean quickReject__FFFF(JNIEnv* env, jobject, jlong canvasHandle,
+ jfloat left, jfloat top, jfloat right,
+ jfloat bottom) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkRect r;
+ r.set(left, top, right, bottom);
+ bool result = canvas->quickReject(r);
+ return result ? JNI_TRUE : JNI_FALSE;
+ }
+
+ static void drawRGB(JNIEnv* env, jobject, jlong canvasHandle,
+ jint r, jint g, jint b) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ canvas->drawARGB(0xFF, r, g, b);
+ }
+
+ static void drawARGB(JNIEnv* env, jobject, jlong canvasHandle,
+ jint a, jint r, jint g, jint b) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ canvas->drawARGB(a, r, g, b);
+ }
+
+ static void drawColor__I(JNIEnv* env, jobject, jlong canvasHandle,
+ jint color) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ canvas->drawColor(color);
+ }
+
+ static void drawColor__II(JNIEnv* env, jobject, jlong canvasHandle,
+ jint color, jint modeHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPorterDuff::Mode mode = static_cast<SkPorterDuff::Mode>(modeHandle);
+ canvas->drawColor(color, SkPorterDuff::ToXfermodeMode(mode));
+ }
+
+ static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle,
+ jlong paintHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ canvas->drawPaint(*paint);
+ }
+
+ static void doPoints(JNIEnv* env, jlong canvasHandle,
+ jfloatArray jptsArray, jint offset, jint count,
+ jlong paintHandle, jint modeHandle) {
+ NPE_CHECK_RETURN_VOID(env, jptsArray);
+ SkCanvas::PointMode mode = static_cast<SkCanvas::PointMode>(modeHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+
+ AutoJavaFloatArray autoPts(env, jptsArray);
+ float* floats = autoPts.ptr();
+ const int length = autoPts.length();
+
+ if ((offset | count) < 0 || offset + count > length) {
+ doThrowAIOOBE(env);
+ return;
+ }
+
+ // now convert the floats into SkPoints
+ count >>= 1; // now it is the number of points
+ SkAutoSTMalloc<32, SkPoint> storage(count);
+ SkPoint* pts = storage.get();
+ const float* src = floats + offset;
+ for (int i = 0; i < count; i++) {
+ pts[i].set(src[0], src[1]);
+ src += 2;
+ }
+ canvas->drawPoints(mode, count, pts, *paint);
+ }
+
+ static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle,
+ jfloatArray jptsArray, jint offset,
+ jint count, jlong paintHandle) {
+ doPoints(env, canvasHandle, jptsArray, offset, count, paintHandle,
+ SkCanvas::kPoints_PointMode);
+ }
+
+ static void drawLines(JNIEnv* env, jobject, jlong canvasHandle,
+ jfloatArray jptsArray, jint offset, jint count,
+ jlong paintHandle) {
+ doPoints(env, canvasHandle, jptsArray, offset, count, paintHandle,
+ SkCanvas::kLines_PointMode);
+ }
+
+ static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
+ jlong paintHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ canvas->drawPoint(x, y, *paint);
+ }
+
+ static void drawLine__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle,
+ jfloat startX, jfloat startY, jfloat stopX,
+ jfloat stopY, jlong paintHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ canvas->drawLine(startX, startY, stopX, stopY, *paint);
+ }
+
+ static void drawRect__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle,
+ jfloat left, jfloat top, jfloat right,
+ jfloat bottom, jlong paintHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ canvas->drawRectCoords(left, top, right, bottom, *paint);
+ }
+
+ static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jlong paintHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
+ canvas->drawOval(oval, *paint);
+ }
+
+ static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx,
+ jfloat cy, jfloat radius, jlong paintHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ canvas->drawCircle(cx, cy, radius, *paint);
+ }
+
+ static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle, jboolean useCenter,
+ jlong paintHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
+ canvas->drawArc(oval, startAngle, sweepAngle, useCenter, *paint);
+ }
+
+ static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle,
+ jfloat left, jfloat top, jfloat right, jfloat bottom, jfloat rx, jfloat ry,
+ jlong paintHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+ canvas->drawRoundRect(rect, rx, ry, *paint);
+ }
+
+ static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
+ jlong paintHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ canvas->drawPath(*path, *paint);
+ }
+
+ static void drawBitmap__BitmapFFPaint(JNIEnv* env, jobject jcanvas,
+ jlong canvasHandle, jlong bitmapHandle,
+ jfloat left, jfloat top,
+ jlong paintHandle, jint canvasDensity,
+ jint screenDensity, jint bitmapDensity) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+
+ if (canvasDensity == bitmapDensity || canvasDensity == 0
+ || bitmapDensity == 0) {
+ if (screenDensity != 0 && screenDensity != bitmapDensity) {
+ SkPaint filteredPaint;
+ if (paint) {
+ filteredPaint = *paint;
+ }
+ filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
+ canvas->drawBitmap(*bitmap, left, top, &filteredPaint);
+ } else {
+ canvas->drawBitmap(*bitmap, left, top, paint);
+ }
+ } else {
+ canvas->save();
+ SkScalar scale = canvasDensity / (float)bitmapDensity;
+ canvas->translate(left, top);
+ canvas->scale(scale, scale);
+
+ SkPaint filteredPaint;
+ if (paint) {
+ filteredPaint = *paint;
+ }
+ filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
+
+ canvas->drawBitmap(*bitmap, 0, 0, &filteredPaint);
+
+ canvas->restore();
+ }
+ }
+
+ static void doDrawBitmap(JNIEnv* env, SkCanvas* canvas, SkBitmap* bitmap,
+ jobject srcIRect, const SkRect& dst, SkPaint* paint,
+ jint screenDensity, jint bitmapDensity) {
+ SkIRect src, *srcPtr = NULL;
+
+ if (NULL != srcIRect) {
+ GraphicsJNI::jrect_to_irect(env, srcIRect, &src);
+ srcPtr = &src;
+ }
+
+ if (screenDensity != 0 && screenDensity != bitmapDensity) {
+ SkPaint filteredPaint;
+ if (paint) {
+ filteredPaint = *paint;
+ }
+ filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
+ canvas->drawBitmapRect(*bitmap, srcPtr, dst, &filteredPaint);
+ } else {
+ canvas->drawBitmapRect(*bitmap, srcPtr, dst, paint);
+ }
+ }
+
+ static void drawBitmapRF(JNIEnv* env, jobject, jlong canvasHandle,
+ jlong bitmapHandle, jobject srcIRect,
+ jobject dstRectF, jlong paintHandle,
+ jint screenDensity, jint bitmapDensity) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ SkRect dst;
+ GraphicsJNI::jrectf_to_rect(env, dstRectF, &dst);
+ doDrawBitmap(env, canvas, bitmap, srcIRect, dst, paint,
+ screenDensity, bitmapDensity);
+ }
+
+ static void drawBitmapRR(JNIEnv* env, jobject, jlong canvasHandle,
+ jlong bitmapHandle, jobject srcIRect,
+ jobject dstRect, jlong paintHandle,
+ jint screenDensity, jint bitmapDensity) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ SkRect dst;
+ GraphicsJNI::jrect_to_rect(env, dstRect, &dst);
+ doDrawBitmap(env, canvas, bitmap, srcIRect, dst, paint,
+ screenDensity, bitmapDensity);
+ }
+
+ static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
+ jintArray jcolors, jint offset, jint stride,
+ jfloat x, jfloat y, jint width, jint height,
+ jboolean hasAlpha, jlong paintHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
+ // correct the alphaType to kOpaque_SkAlphaType.
+ SkImageInfo info = SkImageInfo::Make(width, height,
+ hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
+ kPremul_SkAlphaType);
+ SkBitmap bitmap;
+ if (!bitmap.allocPixels(info)) {
+ return;
+ }
+
+ if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride,
+ 0, 0, width, height, bitmap)) {
+ return;
+ }
+
+ canvas->drawBitmap(bitmap, x, y, paint);
+ }
+
+ static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle,
+ jlong bitmapHandle, jlong matrixHandle,
+ jlong paintHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+ const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ canvas->drawBitmapMatrix(*bitmap, *matrix, paint);
+ }
+
+ static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle,
+ jlong bitmapHandle, jint meshWidth, jint meshHeight,
+ jfloatArray jverts, jint vertIndex, jintArray jcolors,
+ jint colorIndex, jlong paintHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+
+ const int ptCount = (meshWidth + 1) * (meshHeight + 1);
+ const int indexCount = meshWidth * meshHeight * 6;
+
+ AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1));
+ AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
+
+ /* Our temp storage holds 2 or 3 arrays.
+ texture points [ptCount * sizeof(SkPoint)]
+ optionally vertex points [ptCount * sizeof(SkPoint)] if we need a
+ copy to convert from float to fixed
+ indices [ptCount * sizeof(uint16_t)]
+ */
+ ssize_t storageSize = ptCount * sizeof(SkPoint); // texs[]
+ storageSize += indexCount * sizeof(uint16_t); // indices[]
+
+ SkAutoMalloc storage(storageSize);
+ SkPoint* texs = (SkPoint*)storage.get();
+ SkPoint* verts;
+ uint16_t* indices;
+#ifdef SK_SCALAR_IS_FLOAT
+ verts = (SkPoint*)(vertA.ptr() + vertIndex);
+ indices = (uint16_t*)(texs + ptCount);
+#else
+ SkASSERT(false);
+#endif
+
+ // cons up texture coordinates and indices
+ {
+ const SkScalar w = SkIntToScalar(bitmap->width());
+ const SkScalar h = SkIntToScalar(bitmap->height());
+ const SkScalar dx = w / meshWidth;
+ const SkScalar dy = h / meshHeight;
+
+ SkPoint* texsPtr = texs;
+ SkScalar y = 0;
+ for (int i = 0; i <= meshHeight; i++) {
+ if (i == meshHeight) {
+ y = h; // to ensure numerically we hit h exactly
+ }
+ SkScalar x = 0;
+ for (int j = 0; j < meshWidth; j++) {
+ texsPtr->set(x, y);
+ texsPtr += 1;
+ x += dx;
+ }
+ texsPtr->set(w, y);
+ texsPtr += 1;
+ y += dy;
+ }
+ SkASSERT(texsPtr - texs == ptCount);
+ }
+
+ // cons up indices
+ {
+ uint16_t* indexPtr = indices;
+ int index = 0;
+ for (int i = 0; i < meshHeight; i++) {
+ for (int j = 0; j < meshWidth; j++) {
+ // lower-left triangle
+ *indexPtr++ = index;
+ *indexPtr++ = index + meshWidth + 1;
+ *indexPtr++ = index + meshWidth + 2;
+ // upper-right triangle
+ *indexPtr++ = index;
+ *indexPtr++ = index + meshWidth + 2;
+ *indexPtr++ = index + 1;
+ // bump to the next cell
+ index += 1;
+ }
+ // bump to the next row
+ index += 1;
+ }
+ SkASSERT(indexPtr - indices == indexCount);
+ SkASSERT((char*)indexPtr - (char*)storage.get() == storageSize);
+ }
+
+ // double-check that we have legal indices
+#ifdef SK_DEBUG
+ {
+ for (int i = 0; i < indexCount; i++) {
+ SkASSERT((unsigned)indices[i] < (unsigned)ptCount);
+ }
+ }
+#endif
+
+ // cons-up a shader for the bitmap
+ SkPaint tmpPaint;
+ if (paint) {
+ tmpPaint = *paint;
+ }
+ SkShader* shader = SkShader::CreateBitmapShader(*bitmap,
+ SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+ SkSafeUnref(tmpPaint.setShader(shader));
+
+ canvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, verts,
+ texs, (const SkColor*)colorA.ptr(), NULL, indices,
+ indexCount, tmpPaint);
+ }
+
+ static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
+ jint modeHandle, jint vertexCount,
+ jfloatArray jverts, jint vertIndex,
+ jfloatArray jtexs, jint texIndex,
+ jintArray jcolors, jint colorIndex,
+ jshortArray jindices, jint indexIndex,
+ jint indexCount, jlong paintHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(modeHandle);
+ const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+
+ AutoJavaFloatArray vertA(env, jverts, vertIndex + vertexCount);
+ AutoJavaFloatArray texA(env, jtexs, texIndex + vertexCount);
+ AutoJavaIntArray colorA(env, jcolors, colorIndex + vertexCount);
+ AutoJavaShortArray indexA(env, jindices, indexIndex + indexCount);
+
+ const int ptCount = vertexCount >> 1;
+
+ SkPoint* verts;
+ SkPoint* texs = NULL;
+#ifdef SK_SCALAR_IS_FLOAT
+ verts = (SkPoint*)(vertA.ptr() + vertIndex);
+ if (jtexs != NULL) {
+ texs = (SkPoint*)(texA.ptr() + texIndex);
+ }
+#else
+ SkASSERT(false);
+#endif
+
+ const SkColor* colors = NULL;
+ const uint16_t* indices = NULL;
+ if (jcolors != NULL) {
+ colors = (const SkColor*)(colorA.ptr() + colorIndex);
+ }
+ if (jindices != NULL) {
+ indices = (const uint16_t*)(indexA.ptr() + indexIndex);
+ }
+
+ canvas->drawVertices(mode, ptCount, verts, texs, colors, NULL,
+ indices, indexCount, *paint);
+ }
+
+
+ static void drawText___CIIFFIPaintTypeface(JNIEnv* env, jobject, jlong canvasHandle,
+ jcharArray text, jint index, jint count,
+ jfloat x, jfloat y, jint bidiFlags,
+ jlong paintHandle, jlong typefaceHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+ jchar* textArray = env->GetCharArrayElements(text, NULL);
+ drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, bidiFlags, paint, typeface);
+ env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
+ }
+
+ static void drawText__StringIIFFIPaintTypeface(JNIEnv* env, jobject,
+ jlong canvasHandle, jstring text,
+ jint start, jint end,
+ jfloat x, jfloat y, jint bidiFlags,
+ jlong paintHandle, jlong typefaceHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+ const jchar* textArray = env->GetStringChars(text, NULL);
+ drawTextWithGlyphs(canvas, textArray, start, end, x, y, bidiFlags, paint, typeface);
+ env->ReleaseStringChars(text, textArray);
+ }
+
+ class DrawTextFunctor {
+ public:
+ DrawTextFunctor(const Layout& layout, SkCanvas* canvas, jfloat x, jfloat y, SkPaint* paint,
+ uint16_t* glyphs, SkPoint* pos)
+ : layout(layout), canvas(canvas), x(x), y(y), paint(paint), glyphs(glyphs),
+ pos(pos) { }
+
+ void operator()(size_t start, size_t end) {
+ for (size_t i = start; i < end; i++) {
+ glyphs[i] = layout.getGlyphId(i);
+ pos[i].fX = x + layout.getX(i);
+ pos[i].fY = y + layout.getY(i);
+ }
+ canvas->drawPosText(glyphs + start, (end - start) << 1, pos + start, *paint);
+ }
+ private:
+ const Layout& layout;
+ SkCanvas* canvas;
+ jfloat x;
+ jfloat y;
+ SkPaint* paint;
+ uint16_t* glyphs;
+ SkPoint* pos;
+ };
+
+ static void drawGlyphsToSkia(SkCanvas* canvas, SkPaint* paint, const Layout& layout, float x, float y) {
+ size_t nGlyphs = layout.nGlyphs();
+ uint16_t* glyphs = new uint16_t[nGlyphs];
+ SkPoint* pos = new SkPoint[nGlyphs];
+
+ x += MinikinUtils::xOffsetForTextAlign(paint, layout);
+ SkPaint::Align align = paint->getTextAlign();
+ paint->setTextAlign(SkPaint::kLeft_Align);
+ paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ DrawTextFunctor f(layout, canvas, x, y, paint, glyphs, pos);
+ MinikinUtils::forFontRun(layout, paint, f);
+ doDrawTextDecorations(canvas, x, y, layout.getAdvance(), paint);
+ paint->setTextAlign(align);
+ delete[] glyphs;
+ delete[] pos;
+ }
+
+ static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
+ int start, int end,
+ jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
+
+ jint count = end - start;
+ drawTextWithGlyphs(canvas, textArray + start, 0, count, count, x, y, bidiFlags, paint,
+ typeface);
+ }
+
+ static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
+ int start, int count, int contextCount,
+ jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
+
+ Layout layout;
+ std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
+ layout.doLayout(textArray, start, count, contextCount, css);
+ drawGlyphsToSkia(canvas, paint, layout, x, y);
+ }
+
+// Same values used by Skia
+#define kStdStrikeThru_Offset (-6.0f / 21.0f)
+#define kStdUnderline_Offset (1.0f / 9.0f)
+#define kStdUnderline_Thickness (1.0f / 18.0f)
+
+ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat length,
+ SkPaint* paint) {
+ uint32_t flags;
+ SkDrawFilter* drawFilter = canvas->getDrawFilter();
+ if (drawFilter) {
+ SkPaint paintCopy(*paint);
+ drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
+ flags = paintCopy.getFlags();
+ } else {
+ flags = paint->getFlags();
+ }
+ if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
+ SkScalar left = x;
+ SkScalar right = x + length;
+ float textSize = paint->getTextSize();
+ float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
+ if (flags & SkPaint::kUnderlineText_Flag) {
+ SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
+ SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
+ canvas->drawRectCoords(left, top, right, bottom, *paint);
+ }
+ if (flags & SkPaint::kStrikeThruText_Flag) {
+ SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
+ SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
+ canvas->drawRectCoords(left, top, right, bottom, *paint);
+ }
+ }
+ }
+
+ static void doDrawGlyphsPos(SkCanvas* canvas, const jchar* glyphArray, const jfloat* posArray,
+ int index, int count, jfloat x, jfloat y, SkPaint* paint) {
+ SkPoint* posPtr = new SkPoint[count];
+ for (int indx = 0; indx < count; indx++) {
+ posPtr[indx].fX = x + posArray[indx * 2];
+ posPtr[indx].fY = y + posArray[indx * 2 + 1];
+ }
+ canvas->drawPosText(glyphArray, count << 1, posPtr, *paint);
+ delete[] posPtr;
+ }
+
+ static void drawTextRun___CIIIIFFZPaintTypeface(
+ JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
+ jint count, jint contextIndex, jint contextCount,
+ jfloat x, jfloat y, jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+ int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
+ jchar* chars = env->GetCharArrayElements(text, NULL);
+ drawTextWithGlyphs(canvas, chars + contextIndex, index - contextIndex,
+ count, contextCount, x, y, bidiFlags, paint, typeface);
+ env->ReleaseCharArrayElements(text, chars, JNI_ABORT);
+ }
+
+ static void drawTextRun__StringIIIIFFZPaintTypeface(
+ JNIEnv* env, jobject obj, jlong canvasHandle, jstring text, jint start,
+ jint end, jint contextStart, jint contextEnd,
+ jfloat x, jfloat y, jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+ int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
+ jint count = end - start;
+ jint contextCount = contextEnd - contextStart;
+ const jchar* chars = env->GetStringChars(text, NULL);
+ drawTextWithGlyphs(canvas, chars + contextStart, start - contextStart,
+ count, contextCount, x, y, bidiFlags, paint, typeface);
+ env->ReleaseStringChars(text, chars);
+ }
+
+ static void drawPosText___CII_FPaint(JNIEnv* env, jobject, jlong canvasHandle,
+ jcharArray text, jint index, jint count,
+ jfloatArray pos, jlong paintHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ jchar* textArray = text ? env->GetCharArrayElements(text, NULL) : NULL;
+ jsize textCount = text ? env->GetArrayLength(text) : NULL;
+ float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
+ int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
+ SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
+ int indx;
+ for (indx = 0; indx < posCount; indx++) {
+ posPtr[indx].fX = posArray[indx << 1];
+ posPtr[indx].fY = posArray[(indx << 1) + 1];
+ }
+
+ SkPaint::TextEncoding encoding = paint->getTextEncoding();
+ paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ canvas->drawPosText(textArray + index, count << 1, posPtr, *paint);
+ paint->setTextEncoding(encoding);
+
+ if (text) {
+ env->ReleaseCharArrayElements(text, textArray, 0);
+ }
+ if (pos) {
+ env->ReleaseFloatArrayElements(pos, posArray, 0);
+ }
+ delete[] posPtr;
+ }
+
+ static void drawPosText__String_FPaint(JNIEnv* env, jobject,
+ jlong canvasHandle, jstring text,
+ jfloatArray pos,
+ jlong paintHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ const void* text_ = text ? env->GetStringChars(text, NULL) : NULL;
+ int byteLength = text ? env->GetStringLength(text) : 0;
+ float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
+ int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
+ SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
+
+ for (int indx = 0; indx < posCount; indx++) {
+ posPtr[indx].fX = posArray[indx << 1];
+ posPtr[indx].fY = posArray[(indx << 1) + 1];
+ }
+
+ SkPaint::TextEncoding encoding = paint->getTextEncoding();
+ paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ canvas->drawPosText(text_, byteLength << 1, posPtr, *paint);
+ paint->setTextEncoding(encoding);
+
+ if (text) {
+ env->ReleaseStringChars(text, (const jchar*) text_);
+ }
+ if (pos) {
+ env->ReleaseFloatArrayElements(pos, posArray, 0);
+ }
+ delete[] posPtr;
+ }
+
+ class DrawTextOnPathFunctor {
+ public:
+ DrawTextOnPathFunctor(const Layout& layout, SkCanvas* canvas, float hOffset,
+ float vOffset, SkPaint* paint, SkPath* path)
+ : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
+ paint(paint), path(path) {
+ }
+ void operator()(size_t start, size_t end) {
+ uint16_t glyphs[1];
+ for (size_t i = start; i < end; i++) {
+ glyphs[0] = layout.getGlyphId(i);
+ float x = hOffset + layout.getX(i);
+ float y = vOffset + layout.getY(i);
+ canvas->drawTextOnPathHV(glyphs, sizeof(glyphs), *path, x, y, *paint);
+ }
+ }
+ private:
+ const Layout& layout;
+ SkCanvas* canvas;
+ float hOffset;
+ float vOffset;
+ SkPaint* paint;
+ SkPath* path;
+ };
+
+ static void doDrawTextOnPath(SkPaint* paint, const jchar* text, int count, int bidiFlags,
+ float hOffset, float vOffset, SkPath* path, SkCanvas* canvas, TypefaceImpl* typeface) {
+ Layout layout;
+ std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
+ layout.doLayout(text, 0, count, count, css);
+ hOffset += MinikinUtils::hOffsetForTextAlign(paint, layout, *path);
+ // Set align to left for drawing, as we don't want individual
+ // glyphs centered or right-aligned; the offset above takes
+ // care of all alignment.
+ SkPaint::Align align = paint->getTextAlign();
+ paint->setTextAlign(SkPaint::kLeft_Align);
+
+ DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paint, path);
+ MinikinUtils::forFontRun(layout, paint, f);
+ paint->setTextAlign(align);
+ }
+
+ static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject,
+ jlong canvasHandle, jcharArray text, jint index, jint count,
+ jlong pathHandle, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle,
+ jlong typefaceHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+ jchar* textArray = env->GetCharArrayElements(text, NULL);
+ doDrawTextOnPath(paint, textArray + index, count, bidiFlags, hOffset, vOffset,
+ path, canvas, typeface);
+ env->ReleaseCharArrayElements(text, textArray, 0);
+ }
+
+ static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject,
+ jlong canvasHandle, jstring text, jlong pathHandle,
+ jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle,
+ jlong typefaceHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+ const jchar* text_ = env->GetStringChars(text, NULL);
+ int count = env->GetStringLength(text);
+ doDrawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset,
+ path, canvas, typeface);
+ env->ReleaseStringChars(text, text_);
+ }
+
+
+ // This function is a mirror of SkCanvas::getClipBounds except that it does
+ // not outset the edge of the clip to account for anti-aliasing. There is
+ // a skia bug to investigate pushing this logic into back into skia.
+ // (see https://code.google.com/p/skia/issues/detail?id=1303)
+ static bool getHardClipBounds(SkCanvas* canvas, SkRect* bounds) {
+ SkIRect ibounds;
+ if (!canvas->getClipDeviceBounds(&ibounds)) {
+ return false;
+ }
+
+ SkMatrix inverse;
+ // if we can't invert the CTM, we can't return local clip bounds
+ if (!canvas->getTotalMatrix().invert(&inverse)) {
+ if (bounds) {
+ bounds->setEmpty();
+ }
+ return false;
+ }
+
+ if (NULL != bounds) {
+ SkRect r = SkRect::Make(ibounds);
+ inverse.mapRect(bounds, r);
+ }
+ return true;
+ }
+
+ static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle,
+ jobject bounds) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkRect r;
+ SkIRect ir;
+ bool result = getHardClipBounds(canvas, &r);
+
+ if (!result) {
+ r.setEmpty();
+ }
+ r.round(&ir);
+
+ (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
+ return result ? JNI_TRUE : JNI_FALSE;
+ }
+
+ static void getCTM(JNIEnv* env, jobject, jlong canvasHandle,
+ jlong matrixHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+ *matrix = canvas->getTotalMatrix();
+ }
+};
+
+static JNINativeMethod gCanvasMethods[] = {
+ {"finalizer", "(J)V", (void*) SkCanvasGlue::finalizer},
+ {"initRaster", "(J)J", (void*) SkCanvasGlue::initRaster},
+ {"initCanvas", "(J)J", (void*) SkCanvasGlue::initCanvas},
+ {"native_setBitmap", "(JJZ)V", (void*) SkCanvasGlue::setBitmap},
+ {"native_isOpaque","(J)Z", (void*) SkCanvasGlue::isOpaque},
+ {"native_getWidth","(J)I", (void*) SkCanvasGlue::getWidth},
+ {"native_getHeight","(J)I", (void*) SkCanvasGlue::getHeight},
+ {"native_save","(JI)I", (void*) SkCanvasGlue::save},
+ {"native_saveLayer","(JFFFFJI)I", (void*) SkCanvasGlue::saveLayer},
+ {"native_saveLayerAlpha","(JFFFFII)I", (void*) SkCanvasGlue::saveLayerAlpha},
+ {"native_restore","(J)V", (void*) SkCanvasGlue::restore},
+ {"native_getSaveCount","(J)I", (void*) SkCanvasGlue::getSaveCount},
+ {"native_restoreToCount","(JI)V", (void*) SkCanvasGlue::restoreToCount},
+ {"native_translate","(JFF)V", (void*) SkCanvasGlue::translate},
+ {"native_scale","(JFF)V", (void*) SkCanvasGlue::scale__FF},
+ {"native_rotate","(JF)V", (void*) SkCanvasGlue::rotate__F},
+ {"native_skew","(JFF)V", (void*) SkCanvasGlue::skew__FF},
+ {"native_concat","(JJ)V", (void*) SkCanvasGlue::concat},
+ {"native_setMatrix","(JJ)V", (void*) SkCanvasGlue::setMatrix},
+ {"native_clipRect","(JFFFFI)Z", (void*) SkCanvasGlue::clipRect},
+ {"native_clipPath","(JJI)Z", (void*) SkCanvasGlue::clipPath},
+ {"native_clipRegion","(JJI)Z", (void*) SkCanvasGlue::clipRegion},
+ {"nativeSetDrawFilter", "(JJ)V", (void*) SkCanvasGlue::setDrawFilter},
+ {"native_getClipBounds","(JLandroid/graphics/Rect;)Z",
+ (void*) SkCanvasGlue::getClipBounds},
+ {"native_getCTM", "(JJ)V", (void*)SkCanvasGlue::getCTM},
+ {"native_quickReject","(JJ)Z", (void*) SkCanvasGlue::quickReject__Path},
+ {"native_quickReject","(JFFFF)Z", (void*)SkCanvasGlue::quickReject__FFFF},
+ {"native_drawRGB","(JIII)V", (void*) SkCanvasGlue::drawRGB},
+ {"native_drawARGB","(JIIII)V", (void*) SkCanvasGlue::drawARGB},
+ {"native_drawColor","(JI)V", (void*) SkCanvasGlue::drawColor__I},
+ {"native_drawColor","(JII)V", (void*) SkCanvasGlue::drawColor__II},
+ {"native_drawPaint","(JJ)V", (void*) SkCanvasGlue::drawPaint},
+ {"native_drawPoint", "(JFFJ)V", (void*) SkCanvasGlue::drawPoint},
+ {"native_drawPoints", "(J[FIIJ)V", (void*) SkCanvasGlue::drawPoints},
+ {"native_drawLines", "(J[FIIJ)V", (void*) SkCanvasGlue::drawLines},
+ {"native_drawLine","(JFFFFJ)V", (void*) SkCanvasGlue::drawLine__FFFFPaint},
+ {"native_drawRect","(JFFFFJ)V", (void*) SkCanvasGlue::drawRect__FFFFPaint},
+ {"native_drawOval","(JFFFFJ)V", (void*) SkCanvasGlue::drawOval},
+ {"native_drawCircle","(JFFFJ)V", (void*) SkCanvasGlue::drawCircle},
+ {"native_drawArc","(JFFFFFFZJ)V", (void*) SkCanvasGlue::drawArc},
+ {"native_drawRoundRect","(JFFFFFFJ)V",
+ (void*) SkCanvasGlue::drawRoundRect},
+ {"native_drawPath","(JJJ)V", (void*) SkCanvasGlue::drawPath},
+ {"native_drawBitmap","(JJFFJIII)V",
+ (void*) SkCanvasGlue::drawBitmap__BitmapFFPaint},
+ {"native_drawBitmap","(JJLandroid/graphics/Rect;Landroid/graphics/RectF;JII)V",
+ (void*) SkCanvasGlue::drawBitmapRF},
+ {"native_drawBitmap","(JJLandroid/graphics/Rect;Landroid/graphics/Rect;JII)V",
+ (void*) SkCanvasGlue::drawBitmapRR},
+ {"native_drawBitmap", "(J[IIIFFIIZJ)V",
+ (void*)SkCanvasGlue::drawBitmapArray},
+ {"nativeDrawBitmapMatrix", "(JJJJ)V",
+ (void*)SkCanvasGlue::drawBitmapMatrix},
+ {"nativeDrawBitmapMesh", "(JJII[FI[IIJ)V",
+ (void*)SkCanvasGlue::drawBitmapMesh},
+ {"nativeDrawVertices", "(JII[FI[FI[II[SIIJ)V",
+ (void*)SkCanvasGlue::drawVertices},
+ {"native_drawText","(J[CIIFFIJJ)V",
+ (void*) SkCanvasGlue::drawText___CIIFFIPaintTypeface},
+ {"native_drawText","(JLjava/lang/String;IIFFIJJ)V",
+ (void*) SkCanvasGlue::drawText__StringIIFFIPaintTypeface},
+ {"native_drawTextRun","(J[CIIIIFFZJJ)V",
+ (void*) SkCanvasGlue::drawTextRun___CIIIIFFZPaintTypeface},
+ {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V",
+ (void*) SkCanvasGlue::drawTextRun__StringIIIIFFZPaintTypeface},
+ {"native_drawTextOnPath","(J[CIIJFFIJJ)V",
+ (void*) SkCanvasGlue::drawTextOnPath___CIIPathFFPaint},
+ {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V",
+ (void*) SkCanvasGlue::drawTextOnPath__StringPathFFPaint},
+
+ {"freeCaches", "()V", (void*) SkCanvasGlue::freeCaches},
+
+ {"freeTextLayoutCaches", "()V", (void*) SkCanvasGlue::freeTextLayoutCaches}
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include <android_runtime/AndroidRuntime.h>
+
+#define REG(env, name, array) \
+ result = android::AndroidRuntime::registerNativeMethods(env, name, array, \
+ SK_ARRAY_COUNT(array)); \
+ if (result < 0) return result
+
+int register_android_graphics_Canvas(JNIEnv* env) {
+ int result;
+
+ REG(env, "android/graphics/Canvas", gCanvasMethods);
+
+ return result;
+}
+
+} // namespace android
+
+// GraphicsJNI helper for external clients.
+// We keep the implementation here to avoid exposing NativeCanvasWrapper
+// externally.
+SkCanvas* GraphicsJNI::getNativeCanvas(jlong nativeHandle) {
+ return android::SkCanvasGlue::getNativeCanvas(nativeHandle);
+}