Matrix.cpp revision cc11f15f76a62ded3e403cb2bc818c6aa5bf261c
1/* libs/android_runtime/android/graphics/Matrix.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include "jni.h"
19#include "GraphicsJNI.h"
20#include <android_runtime/AndroidRuntime.h>
21
22#include "SkMatrix.h"
23#include "SkTemplates.h"
24
25#include "Matrix.h"
26
27#include <Caches.h>
28
29namespace android {
30
31class SkMatrixGlue {
32public:
33
34    static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
35        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
36        delete obj;
37    }
38
39    static jlong create(JNIEnv* env, jobject clazz, jlong srcHandle) {
40        const SkMatrix* src = reinterpret_cast<SkMatrix*>(srcHandle);
41        SkMatrix* obj = new SkMatrix();
42        if (src)
43            *obj = *src;
44        else
45            obj->reset();
46        return reinterpret_cast<jlong>(obj);
47    }
48
49    static jboolean isIdentity(JNIEnv* env, jobject clazz, jlong objHandle) {
50        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
51        return obj->isIdentity() ? JNI_TRUE : JNI_FALSE;
52    }
53    static jboolean rectStaysRect(JNIEnv* env, jobject clazz, jlong objHandle) {
54        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
55        return obj->rectStaysRect() ? JNI_TRUE : JNI_FALSE;
56    }
57    static void reset(JNIEnv* env, jobject clazz, jlong objHandle) {
58        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
59        obj->reset();
60    }
61     static void set(JNIEnv* env, jobject clazz, jlong objHandle, jlong otherHandle) {
62        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
63        SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
64        *obj = *other;
65    }
66     static void setTranslate(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
67        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
68        SkScalar dx_ = SkFloatToScalar(dx);
69        SkScalar dy_ = SkFloatToScalar(dy);
70        obj->setTranslate(dx_, dy_);
71    }
72     static void setScale__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy, jfloat px, jfloat py) {
73        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
74        SkScalar sx_ = SkFloatToScalar(sx);
75        SkScalar sy_ = SkFloatToScalar(sy);
76        SkScalar px_ = SkFloatToScalar(px);
77        SkScalar py_ = SkFloatToScalar(py);
78        obj->setScale(sx_, sy_, px_, py_);
79    }
80     static void setScale__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy) {
81        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
82        SkScalar sx_ = SkFloatToScalar(sx);
83        SkScalar sy_ = SkFloatToScalar(sy);
84        obj->setScale(sx_, sy_);
85    }
86     static void setRotate__FFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees, jfloat px, jfloat py) {
87        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
88        SkScalar degrees_ = SkFloatToScalar(degrees);
89        SkScalar px_ = SkFloatToScalar(px);
90        SkScalar py_ = SkFloatToScalar(py);
91        obj->setRotate(degrees_, px_, py_);
92    }
93     static void setRotate__F(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees) {
94        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
95        SkScalar degrees_ = SkFloatToScalar(degrees);
96        obj->setRotate(degrees_);
97    }
98     static void setSinCos__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sinValue, jfloat cosValue, jfloat px, jfloat py) {
99        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
100        SkScalar sinValue_ = SkFloatToScalar(sinValue);
101        SkScalar cosValue_ = SkFloatToScalar(cosValue);
102        SkScalar px_ = SkFloatToScalar(px);
103        SkScalar py_ = SkFloatToScalar(py);
104        obj->setSinCos(sinValue_, cosValue_, px_, py_);
105    }
106     static void setSinCos__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sinValue, jfloat cosValue) {
107        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
108        SkScalar sinValue_ = SkFloatToScalar(sinValue);
109        SkScalar cosValue_ = SkFloatToScalar(cosValue);
110        obj->setSinCos(sinValue_, cosValue_);
111    }
112     static void setSkew__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky, jfloat px, jfloat py) {
113        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
114        SkScalar kx_ = SkFloatToScalar(kx);
115        SkScalar ky_ = SkFloatToScalar(ky);
116        SkScalar px_ = SkFloatToScalar(px);
117        SkScalar py_ = SkFloatToScalar(py);
118        obj->setSkew(kx_, ky_, px_, py_);
119    }
120     static void setSkew__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky) {
121        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
122        SkScalar kx_ = SkFloatToScalar(kx);
123        SkScalar ky_ = SkFloatToScalar(ky);
124        obj->setSkew(kx_, ky_);
125    }
126     static void setConcat(JNIEnv* env, jobject clazz, jlong objHandle, jlong aHandle, jlong bHandle) {
127        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
128        SkMatrix* a = reinterpret_cast<SkMatrix*>(aHandle);
129        SkMatrix* b = reinterpret_cast<SkMatrix*>(bHandle);
130        obj->setConcat(*a, *b);
131    }
132
133    static void preTranslate(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
134        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
135        SkScalar dx_ = SkFloatToScalar(dx);
136        SkScalar dy_ = SkFloatToScalar(dy);
137        obj->preTranslate(dx_, dy_);
138    }
139
140    static void preScale__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy, jfloat px, jfloat py) {
141        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
142        SkScalar sx_ = SkFloatToScalar(sx);
143        SkScalar sy_ = SkFloatToScalar(sy);
144        SkScalar px_ = SkFloatToScalar(px);
145        SkScalar py_ = SkFloatToScalar(py);
146        obj->preScale(sx_, sy_, px_, py_);
147    }
148
149    static void preScale__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy) {
150        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
151        SkScalar sx_ = SkFloatToScalar(sx);
152        SkScalar sy_ = SkFloatToScalar(sy);
153        obj->preScale(sx_, sy_);
154    }
155
156    static void preRotate__FFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees, jfloat px, jfloat py) {
157        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
158        SkScalar degrees_ = SkFloatToScalar(degrees);
159        SkScalar px_ = SkFloatToScalar(px);
160        SkScalar py_ = SkFloatToScalar(py);
161        obj->preRotate(degrees_, px_, py_);
162    }
163
164    static void preRotate__F(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees) {
165        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
166        SkScalar degrees_ = SkFloatToScalar(degrees);
167        obj->preRotate(degrees_);
168    }
169
170    static void preSkew__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky, jfloat px, jfloat py) {
171        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
172        SkScalar kx_ = SkFloatToScalar(kx);
173        SkScalar ky_ = SkFloatToScalar(ky);
174        SkScalar px_ = SkFloatToScalar(px);
175        SkScalar py_ = SkFloatToScalar(py);
176        obj->preSkew(kx_, ky_, px_, py_);
177    }
178
179    static void preSkew__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky) {
180        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
181        SkScalar kx_ = SkFloatToScalar(kx);
182        SkScalar ky_ = SkFloatToScalar(ky);
183        obj->preSkew(kx_, ky_);
184    }
185
186    static void preConcat(JNIEnv* env, jobject clazz, jlong objHandle, jlong otherHandle) {
187        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
188        SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
189        obj->preConcat(*other);
190    }
191
192    static void postTranslate(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
193        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
194        SkScalar dx_ = SkFloatToScalar(dx);
195        SkScalar dy_ = SkFloatToScalar(dy);
196        obj->postTranslate(dx_, dy_);
197    }
198
199    static void postScale__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy, jfloat px, jfloat py) {
200        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
201        SkScalar sx_ = SkFloatToScalar(sx);
202        SkScalar sy_ = SkFloatToScalar(sy);
203        SkScalar px_ = SkFloatToScalar(px);
204        SkScalar py_ = SkFloatToScalar(py);
205        obj->postScale(sx_, sy_, px_, py_);
206    }
207
208    static void postScale__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy) {
209        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
210        SkScalar sx_ = SkFloatToScalar(sx);
211        SkScalar sy_ = SkFloatToScalar(sy);
212        obj->postScale(sx_, sy_);
213    }
214
215    static void postRotate__FFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees, jfloat px, jfloat py) {
216        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
217        SkScalar degrees_ = SkFloatToScalar(degrees);
218        SkScalar px_ = SkFloatToScalar(px);
219        SkScalar py_ = SkFloatToScalar(py);
220        obj->postRotate(degrees_, px_, py_);
221    }
222
223    static void postRotate__F(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees) {
224        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
225        SkScalar degrees_ = SkFloatToScalar(degrees);
226        obj->postRotate(degrees_);
227    }
228
229    static void postSkew__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky, jfloat px, jfloat py) {
230        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
231        SkScalar kx_ = SkFloatToScalar(kx);
232        SkScalar ky_ = SkFloatToScalar(ky);
233        SkScalar px_ = SkFloatToScalar(px);
234        SkScalar py_ = SkFloatToScalar(py);
235        obj->postSkew(kx_, ky_, px_, py_);
236    }
237
238    static void postSkew__FF(JNIEnv* env, jobject clazz, jlong matrixHandle, jfloat kx, jfloat ky) {
239        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
240        SkScalar kx_ = SkFloatToScalar(kx);
241        SkScalar ky_ = SkFloatToScalar(ky);
242        matrix->postSkew(kx_, ky_);
243    }
244
245    static void postConcat(JNIEnv* env, jobject clazz, jlong matrixHandle, jlong otherHandle) {
246        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
247        SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
248        matrix->postConcat(*other);
249    }
250
251    static jboolean setRectToRect(JNIEnv* env, jobject clazz, jlong matrixHandle, jobject src, jobject dst, jint stfHandle) {
252        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
253        SkMatrix::ScaleToFit stf = static_cast<SkMatrix::ScaleToFit>(stfHandle);
254        SkRect src_;
255        GraphicsJNI::jrectf_to_rect(env, src, &src_);
256        SkRect dst_;
257        GraphicsJNI::jrectf_to_rect(env, dst, &dst_);
258        return matrix->setRectToRect(src_, dst_, stf) ? JNI_TRUE : JNI_FALSE;
259    }
260
261    static jboolean setPolyToPoly(JNIEnv* env, jobject clazz, jlong matrixHandle,
262                                  jfloatArray jsrc, jint srcIndex,
263                                  jfloatArray jdst, jint dstIndex, jint ptCount) {
264        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
265        SkASSERT(srcIndex >= 0);
266        SkASSERT(dstIndex >= 0);
267        SkASSERT((unsigned)ptCount <= 4);
268
269        AutoJavaFloatArray autoSrc(env, jsrc, srcIndex + (ptCount << 1), kRO_JNIAccess);
270        AutoJavaFloatArray autoDst(env, jdst, dstIndex + (ptCount << 1), kRW_JNIAccess);
271        float* src = autoSrc.ptr() + srcIndex;
272        float* dst = autoDst.ptr() + dstIndex;
273        bool result;
274
275#ifdef SK_SCALAR_IS_FLOAT
276        result = matrix->setPolyToPoly((const SkPoint*)src, (const SkPoint*)dst,
277                                     ptCount);
278#else
279        SkASSERT(false);
280#endif
281        return result ? JNI_TRUE : JNI_FALSE;
282    }
283
284    static jboolean invert(JNIEnv* env, jobject clazz, jlong matrixHandle, jlong inverseHandle) {
285        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
286        SkMatrix* inverse = reinterpret_cast<SkMatrix*>(inverseHandle);
287        return matrix->invert(inverse);
288    }
289
290    static void mapPoints(JNIEnv* env, jobject clazz, jlong matrixHandle,
291                              jfloatArray dst, jint dstIndex,
292                              jfloatArray src, jint srcIndex,
293                              jint ptCount, jboolean isPts) {
294        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
295        SkASSERT(ptCount >= 0);
296        AutoJavaFloatArray autoSrc(env, src, srcIndex + (ptCount << 1), kRO_JNIAccess);
297        AutoJavaFloatArray autoDst(env, dst, dstIndex + (ptCount << 1), kRW_JNIAccess);
298        float* srcArray = autoSrc.ptr() + srcIndex;
299        float* dstArray = autoDst.ptr() + dstIndex;
300#ifdef SK_SCALAR_IS_FLOAT
301        if (isPts)
302            matrix->mapPoints((SkPoint*)dstArray, (const SkPoint*)srcArray,
303                              ptCount);
304        else
305            matrix->mapVectors((SkVector*)dstArray, (const SkVector*)srcArray,
306                               ptCount);
307#else
308        SkASSERT(false);
309#endif
310    }
311
312    static jboolean mapRect__RectFRectF(JNIEnv* env, jobject clazz, jlong matrixHandle, jobjectArray dst, jobject src) {
313        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
314        SkRect dst_, src_;
315        GraphicsJNI::jrectf_to_rect(env, src, &src_);
316        jboolean rectStaysRect = matrix->mapRect(&dst_, src_);
317        GraphicsJNI::rect_to_jrectf(dst_, env, dst);
318        return rectStaysRect ? JNI_TRUE : JNI_FALSE;
319    }
320
321    static jfloat mapRadius(JNIEnv* env, jobject clazz, jlong matrixHandle, jfloat radius) {
322        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
323        float result;
324        result = SkScalarToFloat(matrix->mapRadius(SkFloatToScalar(radius)));
325        return static_cast<jfloat>(result);
326    }
327    static void getValues(JNIEnv* env, jobject clazz, jlong matrixHandle, jfloatArray values) {
328        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
329        AutoJavaFloatArray autoValues(env, values, 9, kRW_JNIAccess);
330        float* dst = autoValues.ptr();
331#ifdef SK_SCALAR_IS_FLOAT
332        for (int i = 0; i < 9; i++) {
333            dst[i] = matrix->get(i);
334        }
335#else
336        SkASSERT(false);
337#endif
338    }
339
340    static void setValues(JNIEnv* env, jobject clazz, jlong matrixHandle, jfloatArray values) {
341        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
342        AutoJavaFloatArray autoValues(env, values, 9, kRO_JNIAccess);
343        const float* src = autoValues.ptr();
344
345#ifdef SK_SCALAR_IS_FLOAT
346        for (int i = 0; i < 9; i++) {
347            matrix->set(i, src[i]);
348        }
349#else
350        SkASSERT(false);
351#endif
352    }
353
354    static jboolean equals(JNIEnv* env, jobject clazz, jlong aHandle, jlong bHandle) {
355        const SkMatrix* a = reinterpret_cast<SkMatrix*>(aHandle);
356        const SkMatrix* b = reinterpret_cast<SkMatrix*>(bHandle);
357        return *a == *b;
358    }
359 };
360
361static JNINativeMethod methods[] = {
362    {"finalizer", "(J)V", (void*) SkMatrixGlue::finalizer},
363    {"native_create","(J)J", (void*) SkMatrixGlue::create},
364    {"native_isIdentity","(J)Z", (void*) SkMatrixGlue::isIdentity},
365    {"native_rectStaysRect","(J)Z", (void*) SkMatrixGlue::rectStaysRect},
366    {"native_reset","(J)V", (void*) SkMatrixGlue::reset},
367    {"native_set","(JJ)V", (void*) SkMatrixGlue::set},
368    {"native_setTranslate","(JFF)V", (void*) SkMatrixGlue::setTranslate},
369    {"native_setScale","(JFFFF)V", (void*) SkMatrixGlue::setScale__FFFF},
370    {"native_setScale","(JFF)V", (void*) SkMatrixGlue::setScale__FF},
371    {"native_setRotate","(JFFF)V", (void*) SkMatrixGlue::setRotate__FFF},
372    {"native_setRotate","(JF)V", (void*) SkMatrixGlue::setRotate__F},
373    {"native_setSinCos","(JFFFF)V", (void*) SkMatrixGlue::setSinCos__FFFF},
374    {"native_setSinCos","(JFF)V", (void*) SkMatrixGlue::setSinCos__FF},
375    {"native_setSkew","(JFFFF)V", (void*) SkMatrixGlue::setSkew__FFFF},
376    {"native_setSkew","(JFF)V", (void*) SkMatrixGlue::setSkew__FF},
377    {"native_setConcat","(JJJ)V", (void*) SkMatrixGlue::setConcat},
378    {"native_preTranslate","(JFF)V", (void*) SkMatrixGlue::preTranslate},
379    {"native_preScale","(JFFFF)V", (void*) SkMatrixGlue::preScale__FFFF},
380    {"native_preScale","(JFF)V", (void*) SkMatrixGlue::preScale__FF},
381    {"native_preRotate","(JFFF)V", (void*) SkMatrixGlue::preRotate__FFF},
382    {"native_preRotate","(JF)V", (void*) SkMatrixGlue::preRotate__F},
383    {"native_preSkew","(JFFFF)V", (void*) SkMatrixGlue::preSkew__FFFF},
384    {"native_preSkew","(JFF)V", (void*) SkMatrixGlue::preSkew__FF},
385    {"native_preConcat","(JJ)V", (void*) SkMatrixGlue::preConcat},
386    {"native_postTranslate","(JFF)V", (void*) SkMatrixGlue::postTranslate},
387    {"native_postScale","(JFFFF)V", (void*) SkMatrixGlue::postScale__FFFF},
388    {"native_postScale","(JFF)V", (void*) SkMatrixGlue::postScale__FF},
389    {"native_postRotate","(JFFF)V", (void*) SkMatrixGlue::postRotate__FFF},
390    {"native_postRotate","(JF)V", (void*) SkMatrixGlue::postRotate__F},
391    {"native_postSkew","(JFFFF)V", (void*) SkMatrixGlue::postSkew__FFFF},
392    {"native_postSkew","(JFF)V", (void*) SkMatrixGlue::postSkew__FF},
393    {"native_postConcat","(JJ)V", (void*) SkMatrixGlue::postConcat},
394    {"native_setRectToRect","(JLandroid/graphics/RectF;Landroid/graphics/RectF;I)Z", (void*) SkMatrixGlue::setRectToRect},
395    {"native_setPolyToPoly","(J[FI[FII)Z", (void*) SkMatrixGlue::setPolyToPoly},
396    {"native_invert","(JJ)Z", (void*) SkMatrixGlue::invert},
397    {"native_mapPoints","(J[FI[FIIZ)V", (void*) SkMatrixGlue::mapPoints},
398    {"native_mapRect","(JLandroid/graphics/RectF;Landroid/graphics/RectF;)Z", (void*) SkMatrixGlue::mapRect__RectFRectF},
399    {"native_mapRadius","(JF)F", (void*) SkMatrixGlue::mapRadius},
400    {"native_getValues","(J[F)V", (void*) SkMatrixGlue::getValues},
401    {"native_setValues","(J[F)V", (void*) SkMatrixGlue::setValues},
402    {"native_equals", "(JJ)Z", (void*) SkMatrixGlue::equals}
403};
404
405static jfieldID sNativeInstanceField;
406
407int register_android_graphics_Matrix(JNIEnv* env) {
408    int result = AndroidRuntime::registerNativeMethods(env, "android/graphics/Matrix", methods,
409        sizeof(methods) / sizeof(methods[0]));
410
411    jclass clazz = env->FindClass("android/graphics/Matrix");
412    sNativeInstanceField = env->GetFieldID(clazz, "native_instance", "J");
413
414    return result;
415}
416
417SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj) {
418    return reinterpret_cast<SkMatrix*>(env->GetLongField(matrixObj, sNativeInstanceField));
419}
420
421}
422