1/*
2 * Copyright 2021 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "include/core/SkCanvas.h"
9#include "include/core/SkColor.h"
10#include "include/core/SkPaint.h"
11#include "include/core/SkRRect.h"
12#include "include/core/SkShader.h"
13#include "modules/androidkit/src/Utils.h"
14
15#include <jni.h>
16
17namespace {
18
19jint Canvas_GetWidth(JNIEnv* env, jobject, jlong native_instance) {
20    const auto* canvas = reinterpret_cast<const SkCanvas*>(native_instance);
21    return canvas ? canvas->imageInfo().width() : 0;
22}
23
24jint Canvas_GetHeight(JNIEnv* env, jobject, jlong native_instance) {
25    const auto* canvas = reinterpret_cast<const SkCanvas*>(native_instance);
26    return canvas ? canvas->imageInfo().height() : 0;
27}
28
29jint Canvas_Save(JNIEnv* env, jobject, jlong native_instance) {
30    if (auto* canvas = reinterpret_cast<SkCanvas*>(native_instance)) {
31        return canvas->save();
32    }
33    return 0;
34}
35
36void Canvas_Restore(JNIEnv* env, jobject, jlong native_instance) {
37    if (auto* canvas = reinterpret_cast<SkCanvas*>(native_instance)) {
38        canvas->restore();
39    }
40}
41
42void Canvas_RestoreToCount(JNIEnv* env, jobject, jlong native_instance, jint count) {
43    if (auto* canvas = reinterpret_cast<SkCanvas*>(native_instance)) {
44        canvas->restoreToCount(count);
45    }
46}
47
48jint Canvas_SaveLayer(JNIEnv* env, jobject, jlong native_instance, jlong native_paint) {
49    auto* paint = reinterpret_cast<SkPaint* >(native_paint);
50    if (auto* canvas = reinterpret_cast<SkCanvas*>(native_instance)) {
51        return canvas->saveLayer(nullptr, paint);
52    }
53    return 0;
54}
55
56jlong Canvas_LocalToDevice(JNIEnv* env, jobject, jlong native_instance) {
57    if (auto* canvas = reinterpret_cast<SkCanvas*>(native_instance)) {
58        SkM44* m = new SkM44(canvas->getLocalToDevice());
59        return reinterpret_cast<jlong>(m);
60    }
61    return 0;
62}
63
64void Canvas_Concat(JNIEnv* env, jobject, jlong native_instance, jlong native_matrix) {
65    auto* canvas = reinterpret_cast<SkCanvas*>(native_instance);
66    auto* matrix = reinterpret_cast<SkM44*   >(native_matrix);
67
68    if (canvas && matrix) {
69        canvas->concat(*matrix);
70    }
71}
72
73void Canvas_Concat16f(JNIEnv* env, jobject, jlong native_instance, jfloatArray jmatrix) {
74    SkASSERT(env->GetArrayLength(jmatrix) == 16);
75
76    if (auto* canvas = reinterpret_cast<SkCanvas*>(native_instance)) {
77        auto* m = env->GetFloatArrayElements(jmatrix, nullptr);
78        canvas->concat(SkM44::RowMajor(m));
79        env->ReleaseFloatArrayElements(jmatrix, m, 0);
80    }
81}
82
83void Canvas_Translate(JNIEnv* env, jobject, jlong native_instance,
84                      jfloat tx, jfloat ty, jfloat tz) {
85    if (auto* canvas = reinterpret_cast<SkCanvas*>(native_instance)) {
86        canvas->concat(SkM44::Translate(tx, ty, tz));
87    }
88}
89
90void Canvas_Scale(JNIEnv* env, jobject, jlong native_instance, jfloat sx, jfloat sy, jfloat sz) {
91    if (auto* canvas = reinterpret_cast<SkCanvas*>(native_instance)) {
92        canvas->concat(SkM44::Scale(sx, sy, sz));
93    }
94}
95
96void Canvas_ClipPath(JNIEnv* env, jobject, jlong native_instance, jlong native_path,
97                                           jint native_clipOp, jboolean doAA) {
98    if (auto* canvas = reinterpret_cast<SkCanvas*>(native_instance)) {
99        if (auto* path = reinterpret_cast<SkPath*>(native_path)) {
100            canvas->clipPath(*path, static_cast<SkClipOp>(native_clipOp), doAA);
101        }
102    }
103}
104
105void Canvas_ClipRect(JNIEnv* env, jobject, jlong native_instance, jfloat l, jfloat t, jfloat r, jfloat b,
106                                           jint native_clipOp, jboolean doAA) {
107    if (auto* canvas = reinterpret_cast<SkCanvas*>(native_instance)) {
108        canvas->clipRect(SkRect::MakeLTRB(l, t, r, b), static_cast<SkClipOp>(native_clipOp), doAA);
109    }
110}
111
112void Canvas_ClipRRect(JNIEnv* env, jobject, jlong native_instance, jfloat l, jfloat t, jfloat r, jfloat b,
113                                                                   jfloat xRad, jfloat yRad,
114                                                                   jint native_clipOp, jboolean doAA) {
115    if (auto* canvas = reinterpret_cast<SkCanvas*>(native_instance)) {
116        canvas->clipRRect(SkRRect::MakeRectXY(SkRect::MakeLTRB(l, t, r, b), xRad, yRad),
117                          static_cast<SkClipOp>(native_clipOp), doAA);
118    }
119}
120
121void Canvas_ClipShader(JNIEnv* env, jobject, jlong native_instance, jlong native_shader, jint native_clipOp) {
122    if (auto* canvas = reinterpret_cast<SkCanvas*>(native_instance)) {
123        if (auto* shader = reinterpret_cast<SkShader*>(native_shader)) {
124            canvas->clipShader(sk_ref_sp(shader), static_cast<SkClipOp>(native_clipOp));
125        }
126    }
127}
128
129void Canvas_DrawColor(JNIEnv* env, jobject, jlong native_instance,
130                      float r, float g, float b, float a) {
131    if (auto* canvas = reinterpret_cast<SkCanvas*>(native_instance)) {
132        canvas->drawColor(SkColor4f{r, g, b, a});
133    }
134}
135
136void Canvas_DrawRect(JNIEnv* env, jobject, jlong native_instance,
137                     jfloat left, jfloat top, jfloat right, jfloat bottom,
138                     jlong native_paint) {
139    auto* canvas = reinterpret_cast<SkCanvas*>(native_instance);
140    auto* paint  = reinterpret_cast<SkPaint* >(native_paint);
141    if (canvas && paint) {
142        canvas->drawRect(SkRect::MakeLTRB(left, top, right, bottom), *paint);
143    }
144}
145
146void Canvas_DrawImage(JNIEnv* env, jobject, jlong native_instance, jlong native_image,
147                      jfloat x, jfloat y,
148                      jint sampling_desc, jfloat sampling_b, jfloat sampling_c) {
149    auto* canvas = reinterpret_cast<SkCanvas*>(native_instance);
150    auto*  image = reinterpret_cast<SkImage *>(native_image);
151
152    if (canvas && image) {
153        canvas->drawImage(image, x, y,
154            androidkit::utils::SamplingOptions(sampling_desc, sampling_b, sampling_c));
155    }
156}
157
158void Canvas_DrawPath(JNIEnv* env, jobject, jlong native_instance, jlong native_path,
159                     jlong native_paint) {
160    auto* canvas = reinterpret_cast<SkCanvas*>(native_instance);
161    auto* path  = reinterpret_cast<SkPath* >(native_path);
162    auto* paint  = reinterpret_cast<SkPaint* >(native_paint);
163    if (canvas && paint && path) {
164        canvas->drawPath(*path, *paint);
165    }
166}
167
168// jPos: a composite array in the form of [x1, y1, x2, y2, ... ,xn, yn]
169// callers of this function check should throw IllegalArgumentException in Java
170void Canvas_DrawGlyphs(JNIEnv* env, jobject, jlong native_instance, jcharArray jglyphs,
171                                             jfloatArray jPos, jfloat xOrigin, jfloat yOrigin,
172                                             jlong native_font, jlong native_paint) {
173    auto* canvas = reinterpret_cast<SkCanvas*>(native_instance);
174    auto* font = reinterpret_cast<SkFont*>(native_font);
175    auto* paint  = reinterpret_cast<SkPaint* >(native_paint);
176    if (canvas && font && paint) {
177        int count = env->GetArrayLength(jglyphs);
178        auto* compositePositions = env->GetFloatArrayElements(jPos, nullptr);
179        auto* positions = reinterpret_cast<SkPoint*>(compositePositions);
180        auto* glyphs = env->GetCharArrayElements(jglyphs, nullptr);
181        canvas->drawGlyphs(count, glyphs, positions, {xOrigin, yOrigin}, *font, *paint);
182
183        env->ReleaseCharArrayElements(jglyphs, glyphs, 0);
184        env->ReleaseFloatArrayElements(jPos, compositePositions, 0);
185    }
186}
187
188}  // namespace
189
190int register_androidkit_Canvas(JNIEnv* env) {
191    static const JNINativeMethod methods[] = {
192        {"nGetWidth"        , "(J)I"           , reinterpret_cast<void*>(Canvas_GetWidth)      },
193        {"nGetHeight"       , "(J)I"           , reinterpret_cast<void*>(Canvas_GetHeight)     },
194        {"nSave"            , "(J)I"           , reinterpret_cast<void*>(Canvas_Save)          },
195        {"nSaveLayer"       , "(JJ)I"          , reinterpret_cast<void*>(Canvas_SaveLayer)     },
196        {"nRestore"         , "(J)V"           , reinterpret_cast<void*>(Canvas_Restore)       },
197        {"nRestoreToCount"  , "(JI)V"          , reinterpret_cast<void*>(Canvas_RestoreToCount)},
198        {"nGetLocalToDevice", "(J)J"           , reinterpret_cast<void*>(Canvas_LocalToDevice) },
199        {"nConcat"          , "(JJ)V"          , reinterpret_cast<void*>(Canvas_Concat)        },
200        {"nConcat16f"       , "(J[F)V"         , reinterpret_cast<void*>(Canvas_Concat16f)     },
201        {"nTranslate"       , "(JFFF)V"        , reinterpret_cast<void*>(Canvas_Translate)     },
202        {"nScale"           , "(JFFF)V"        , reinterpret_cast<void*>(Canvas_Scale)         },
203        {"nClipPath"        , "(JJIZ)V"        , reinterpret_cast<void*>(Canvas_ClipPath)      },
204        {"nClipRect"        , "(JFFFFIZ)V"     , reinterpret_cast<void*>(Canvas_ClipRect)      },
205        {"nClipRRect"       , "(JFFFFFFIZ)V"   , reinterpret_cast<void*>(Canvas_ClipRRect)     },
206        {"nClipShader"      , "(JJI)V"         , reinterpret_cast<void*>(Canvas_ClipShader)    },
207        {"nDrawColor"       , "(JFFFF)V"       , reinterpret_cast<void*>(Canvas_DrawColor)     },
208        {"nDrawRect"        , "(JFFFFJ)V"      , reinterpret_cast<void*>(Canvas_DrawRect)      },
209        {"nDrawImage"       , "(JJFFIFF)V"     , reinterpret_cast<void*>(Canvas_DrawImage)     },
210        {"nDrawPath"        , "(JJJ)V"         , reinterpret_cast<void*>(Canvas_DrawPath)      },
211        {"nDrawGlyphs"      , "(J[C[FFFJJ)V", reinterpret_cast<void*>(Canvas_DrawGlyphs)    },
212
213    };
214
215    const auto clazz = env->FindClass("org/skia/androidkit/Canvas");
216    return clazz
217        ? env->RegisterNatives(clazz, methods, SK_ARRAY_COUNT(methods))
218        : JNI_ERR;
219}
220