comparison mupdf-source/platform/java/jni/device.c @ 3:2c135c81b16c

MERGE: upstream PyMuPDF 1.26.4 with MuPDF 1.26.7
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:44:09 +0200
parents b50eed0cc0ef
children
comparison
equal deleted inserted replaced
0:6015a75abc2d 3:2c135c81b16c
1 // Copyright (C) 2004-2024 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 /* Device interface */
24
25 /*
26 Devices can either be implemented in C, or in Java.
27 We therefore have to think about 4 possible call combinations.
28
29 1) C -> C:
30 The standard mupdf case. No special worries here.
31 2) C -> Java:
32 This can only happen when we call run on a page/annotation/
33 displaylist. We need to ensure that the java Device has an
34 appropriate fz_java_device generated for it, which is done by the
35 Device constructor. The 'run' calls take care to lock/unlock for us.
36 3) Java -> C:
37 The C device will have a java shim (a subclass of NativeDevice).
38 All calls will go through the device methods in NativeDevice,
39 which converts the java objects to C ones, and lock/unlock
40 any underlying objects as required.
41 4) Java -> Java:
42 No special worries.
43 */
44
45 typedef struct
46 {
47 fz_device super;
48 JNIEnv *env;
49 jobject self;
50 }
51 fz_java_device;
52
53 static void
54 fz_java_device_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, fz_matrix ctm, fz_colorspace *cs, const float *color, float alpha, fz_color_params cs_params)
55 {
56 fz_java_device *jdev = (fz_java_device *)dev;
57 JNIEnv *env = jdev->env;
58 jobject jpath = to_Path(ctx, env, path);
59 jobject jcs = to_ColorSpace(ctx, env, cs);
60 jobject jctm = to_Matrix(ctx, env, ctm);
61 jfloatArray jcolor = to_floatArray(ctx, env, color, cs ? fz_colorspace_n(ctx, cs) : FZ_MAX_COLORS);
62 int jcp = to_ColorParams_safe(ctx, env, cs_params);
63
64 (*env)->CallVoidMethod(env, jdev->self, mid_Device_fillPath, jpath, (jboolean)even_odd, jctm, jcs, jcolor, alpha, jcp);
65 if ((*env)->ExceptionCheck(env))
66 fz_throw_java(ctx, env);
67 }
68
69 static void
70 fz_java_device_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *state, fz_matrix ctm, fz_colorspace *cs, const float *color, float alpha, fz_color_params cs_params)
71 {
72 fz_java_device *jdev = (fz_java_device *)dev;
73 JNIEnv *env = jdev->env;
74 jobject jpath = to_Path(ctx, env, path);
75 jobject jstate = to_StrokeState(ctx, env, state);
76 jobject jcs = to_ColorSpace(ctx, env, cs);
77 jobject jctm = to_Matrix(ctx, env, ctm);
78 jfloatArray jcolor = to_floatArray(ctx, env, color, cs ? fz_colorspace_n(ctx, cs) : FZ_MAX_COLORS);
79 int jcp = to_ColorParams_safe(ctx, env, cs_params);
80
81 (*env)->CallVoidMethod(env, jdev->self, mid_Device_strokePath, jpath, jstate, jctm, jcs, jcolor, alpha, jcp);
82 if ((*env)->ExceptionCheck(env))
83 fz_throw_java(ctx, env);
84 }
85
86 static void
87 fz_java_device_clip_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, fz_matrix ctm, fz_rect scissor)
88 {
89 fz_java_device *jdev = (fz_java_device *)dev;
90 JNIEnv *env = jdev->env;
91 jobject jpath = to_Path(ctx, env, path);
92 jobject jctm = to_Matrix(ctx, env, ctm);
93
94 (*env)->CallVoidMethod(env, jdev->self, mid_Device_clipPath, jpath, (jboolean)even_odd, jctm);
95 if ((*env)->ExceptionCheck(env))
96 fz_throw_java(ctx, env);
97 }
98
99 static void
100 fz_java_device_clip_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *state, fz_matrix ctm, fz_rect scissor)
101 {
102 fz_java_device *jdev = (fz_java_device *)dev;
103 JNIEnv *env = jdev->env;
104 jobject jpath = to_Path(ctx, env, path);
105 jobject jstate = to_StrokeState(ctx, env, state);
106 jobject jctm = to_Matrix(ctx, env, ctm);
107
108 (*env)->CallVoidMethod(env, jdev->self, mid_Device_clipStrokePath, jpath, jstate, jctm);
109 if ((*env)->ExceptionCheck(env))
110 fz_throw_java(ctx, env);
111 }
112
113 static void
114 fz_java_device_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm, fz_colorspace *cs, const float *color, float alpha, fz_color_params cs_params)
115 {
116 fz_java_device *jdev = (fz_java_device *)dev;
117 JNIEnv *env = jdev->env;
118 jobject jtext = to_Text(ctx, env, text);
119 jobject jctm = to_Matrix(ctx, env, ctm);
120 jobject jcs = to_ColorSpace(ctx, env, cs);
121 jfloatArray jcolor = to_floatArray(ctx, env, color, cs ? fz_colorspace_n(ctx, cs) : FZ_MAX_COLORS);
122 int jcp = to_ColorParams_safe(ctx, env, cs_params);
123
124 (*env)->CallVoidMethod(env, jdev->self, mid_Device_fillText, jtext, jctm, jcs, jcolor, alpha, jcp);
125 if ((*env)->ExceptionCheck(env))
126 fz_throw_java(ctx, env);
127 }
128
129 static void
130 fz_java_device_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *state, fz_matrix ctm, fz_colorspace *cs, const float *color, float alpha, fz_color_params cs_params)
131 {
132 fz_java_device *jdev = (fz_java_device *)dev;
133 JNIEnv *env = jdev->env;
134 jobject jtext = to_Text(ctx, env, text);
135 jobject jstate = to_StrokeState(ctx, env, state);
136 jobject jctm = to_Matrix(ctx, env, ctm);
137 jobject jcs = to_ColorSpace(ctx, env, cs);
138 jfloatArray jcolor = to_floatArray(ctx, env, color, cs ? fz_colorspace_n(ctx, cs) : FZ_MAX_COLORS);
139 int jcp = to_ColorParams_safe(ctx, env, cs_params);
140
141 (*env)->CallVoidMethod(env, jdev->self, mid_Device_strokeText, jtext, jstate, jctm, jcs, jcolor, alpha, jcp);
142 if ((*env)->ExceptionCheck(env))
143 fz_throw_java(ctx, env);
144 }
145
146 static void
147 fz_java_device_clip_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm, fz_rect scissor)
148 {
149 fz_java_device *jdev = (fz_java_device *)dev;
150 JNIEnv *env = jdev->env;
151 jobject jtext = to_Text(ctx, env, text);
152 jobject jctm = to_Matrix(ctx, env, ctm);
153
154 (*env)->CallVoidMethod(env, jdev->self, mid_Device_clipText, jtext, jctm);
155 if ((*env)->ExceptionCheck(env))
156 fz_throw_java(ctx, env);
157 }
158
159 static void
160 fz_java_device_clip_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *state, fz_matrix ctm, fz_rect scissor)
161 {
162 fz_java_device *jdev = (fz_java_device *)dev;
163 JNIEnv *env = jdev->env;
164 jobject jtext = to_Text(ctx, env, text);
165 jobject jstate = to_StrokeState(ctx, env, state);
166 jobject jctm = to_Matrix(ctx, env, ctm);
167
168 (*env)->CallVoidMethod(env, jdev->self, mid_Device_clipStrokeText, jtext, jstate, jctm);
169 if ((*env)->ExceptionCheck(env))
170 fz_throw_java(ctx, env);
171 }
172
173 static void
174 fz_java_device_ignore_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm)
175 {
176 fz_java_device *jdev = (fz_java_device *)dev;
177 JNIEnv *env = jdev->env;
178 jobject jtext = to_Text(ctx, env, text);
179 jobject jctm = to_Matrix(ctx, env, ctm);
180
181 (*env)->CallVoidMethod(env, jdev->self, mid_Device_ignoreText, jtext, jctm);
182 if ((*env)->ExceptionCheck(env))
183 fz_throw_java(ctx, env);
184 }
185
186 static void
187 fz_java_device_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shd, fz_matrix ctm, float alpha, fz_color_params color_params)
188 {
189 fz_java_device *jdev = (fz_java_device *)dev;
190 JNIEnv *env = jdev->env;
191 jobject jshd = to_Shade(ctx, env, shd);
192 jobject jctm = to_Matrix(ctx, env, ctm);
193
194 (*env)->CallVoidMethod(env, jdev->self, mid_Device_fillShade, jshd, jctm, alpha);
195 if ((*env)->ExceptionCheck(env))
196 fz_throw_java(ctx, env);
197 }
198
199 static void
200 fz_java_device_fill_image(fz_context *ctx, fz_device *dev, fz_image *img, fz_matrix ctm, float alpha, fz_color_params color_params)
201 {
202 fz_java_device *jdev = (fz_java_device *)dev;
203 JNIEnv *env = jdev->env;
204 jobject jimg = to_Image(ctx, env, img);
205 jobject jctm = to_Matrix(ctx, env, ctm);
206
207 (*env)->CallVoidMethod(env, jdev->self, mid_Device_fillImage, jimg, jctm, alpha);
208 if ((*env)->ExceptionCheck(env))
209 fz_throw_java(ctx, env);
210 }
211
212 static void
213 fz_java_device_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *img, fz_matrix ctm, fz_colorspace *cs, const float *color, float alpha, fz_color_params cs_params)
214 {
215 fz_java_device *jdev = (fz_java_device *)dev;
216 JNIEnv *env = jdev->env;
217 jobject jimg = to_Image(ctx, env, img);
218 jobject jctm = to_Matrix(ctx, env, ctm);
219 jobject jcs = to_ColorSpace(ctx, env, cs);
220 jfloatArray jcolor = to_floatArray(ctx, env, color, cs ? fz_colorspace_n(ctx, cs) : FZ_MAX_COLORS);
221 int jcp = to_ColorParams_safe(ctx, env, cs_params);
222
223 (*env)->CallVoidMethod(env, jdev->self, mid_Device_fillImageMask, jimg, jctm, jcs, jcolor, alpha, jcp);
224 if ((*env)->ExceptionCheck(env))
225 fz_throw_java(ctx, env);
226 }
227
228 static void
229 fz_java_device_clip_image_mask(fz_context *ctx, fz_device *dev, fz_image *img, fz_matrix ctm, fz_rect scissor)
230 {
231 fz_java_device *jdev = (fz_java_device *)dev;
232 JNIEnv *env = jdev->env;
233 jobject jimg = to_Image(ctx, env, img);
234 jobject jctm = to_Matrix(ctx, env, ctm);
235
236 (*env)->CallVoidMethod(env, jdev->self, mid_Device_clipImageMask, jimg, jctm);
237 if ((*env)->ExceptionCheck(env))
238 fz_throw_java(ctx, env);
239 }
240
241 static void
242 fz_java_device_pop_clip(fz_context *ctx, fz_device *dev)
243 {
244 fz_java_device *jdev = (fz_java_device *)dev;
245 JNIEnv *env = jdev->env;
246
247 (*env)->CallVoidMethod(env, jdev->self, mid_Device_popClip);
248 if ((*env)->ExceptionCheck(env))
249 fz_throw_java(ctx, env);
250 }
251
252 static void
253 fz_java_device_begin_layer(fz_context *ctx, fz_device *dev, const char *name)
254 {
255 fz_java_device *jdev = (fz_java_device *)dev;
256 JNIEnv *env = jdev->env;
257 jstring jname;
258
259 jname = (*env)->NewStringUTF(env, name);
260 if (!jname || (*env)->ExceptionCheck(env))
261 fz_throw_java(ctx, env);
262
263 (*env)->CallVoidMethod(env, jdev->self, mid_Device_beginLayer, jname);
264 if ((*env)->ExceptionCheck(env))
265 fz_throw_java(ctx, env);
266 }
267
268 static void
269 fz_java_device_end_layer(fz_context *ctx, fz_device *dev)
270 {
271 fz_java_device *jdev = (fz_java_device *)dev;
272 JNIEnv *env = jdev->env;
273
274 (*env)->CallVoidMethod(env, jdev->self, mid_Device_endLayer);
275 if ((*env)->ExceptionCheck(env))
276 fz_throw_java(ctx, env);
277 }
278
279 static void
280 fz_java_device_begin_mask(fz_context *ctx, fz_device *dev, fz_rect rect, int luminosity, fz_colorspace *cs, const float *bc, fz_color_params cs_params)
281 {
282 fz_java_device *jdev = (fz_java_device *)dev;
283 JNIEnv *env = jdev->env;
284 jobject jrect = to_Rect(ctx, env, rect);
285 jobject jcs = to_ColorSpace(ctx, env, cs);
286 jfloatArray jbc = to_floatArray(ctx, env, bc, cs ? fz_colorspace_n(ctx, cs) : FZ_MAX_COLORS);
287 int jcp = to_ColorParams_safe(ctx, env, cs_params);
288
289 (*env)->CallVoidMethod(env, jdev->self, mid_Device_beginMask, jrect, (jint)luminosity, jcs, jbc, jcp);
290 if ((*env)->ExceptionCheck(env))
291 fz_throw_java(ctx, env);
292 }
293
294 static void
295 fz_java_device_end_mask(fz_context *ctx, fz_device *dev, fz_function *tr)
296 {
297 fz_java_device *jdev = (fz_java_device *)dev;
298 JNIEnv *env = jdev->env;
299
300 // TODO: pass transfer function
301
302 (*env)->CallVoidMethod(env, jdev->self, mid_Device_endMask);
303 if ((*env)->ExceptionCheck(env))
304 fz_throw_java(ctx, env);
305 }
306
307 static void
308 fz_java_device_begin_group(fz_context *ctx, fz_device *dev, fz_rect rect, fz_colorspace *cs, int isolated, int knockout, int blendmode, float alpha)
309 {
310 fz_java_device *jdev = (fz_java_device *)dev;
311 JNIEnv *env = jdev->env;
312 jobject jrect = to_Rect(ctx, env, rect);
313 jobject jcs = to_ColorSpace(ctx, env, cs);
314
315 (*env)->CallVoidMethod(env, jdev->self, mid_Device_beginGroup, jrect, jcs, (jboolean)isolated, (jboolean)knockout, (jint)blendmode, alpha);
316 if ((*env)->ExceptionCheck(env))
317 fz_throw_java(ctx, env);
318 }
319
320 static void
321 fz_java_device_end_group(fz_context *ctx, fz_device *dev)
322 {
323 fz_java_device *jdev = (fz_java_device *)dev;
324 JNIEnv *env = jdev->env;
325
326 (*env)->CallVoidMethod(env, jdev->self, mid_Device_endGroup);
327 if ((*env)->ExceptionCheck(env))
328 fz_throw_java(ctx, env);
329 }
330
331 static int
332 fz_java_device_begin_tile(fz_context *ctx, fz_device *dev, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm, int id, int doc_id)
333 {
334 fz_java_device *jdev = (fz_java_device *)dev;
335 JNIEnv *env = jdev->env;
336 jobject jarea = to_Rect(ctx, env, area);
337 jobject jview = to_Rect(ctx, env, view);
338 jobject jctm = to_Matrix(ctx, env, ctm);
339 int res;
340
341 res = (*env)->CallIntMethod(env, jdev->self, mid_Device_beginTile, jarea, jview, xstep, ystep, jctm, (jint)id, (jint)doc_id);
342 if ((*env)->ExceptionCheck(env))
343 fz_throw_java(ctx, env);
344
345 return res;
346 }
347
348 static void
349 fz_java_device_end_tile(fz_context *ctx, fz_device *dev)
350 {
351 fz_java_device *jdev = (fz_java_device *)dev;
352 JNIEnv *env = jdev->env;
353
354 (*env)->CallVoidMethod(env, jdev->self, mid_Device_endTile);
355 if ((*env)->ExceptionCheck(env))
356 fz_throw_java(ctx, env);
357 }
358
359 static void
360 fz_java_device_render_flags(fz_context *ctx, fz_device *dev, int set, int clear)
361 {
362 fz_java_device *jdev = (fz_java_device *)dev;
363 JNIEnv *env = jdev->env;
364
365 (*env)->CallVoidMethod(env, jdev->self, mid_Device_renderFlags, set, clear);
366 if ((*env)->ExceptionCheck(env))
367 fz_throw_java(ctx, env);
368 }
369
370 static void
371 fz_java_device_set_default_colorspaces(fz_context *ctx, fz_device *dev, fz_default_colorspaces *dcs)
372 {
373 fz_java_device *jdev = (fz_java_device *)dev;
374 JNIEnv *env = jdev->env;
375 jobject jdcs = to_DefaultColorSpaces(ctx, env, dcs);
376
377 (*env)->CallVoidMethod(env, jdev->self, mid_Device_setDefaultColorSpaces, jdcs);
378 if ((*env)->ExceptionCheck(env))
379 fz_throw_java(ctx, env);
380 }
381
382 static void
383 fz_java_device_begin_structure(fz_context *ctx, fz_device *dev, fz_structure standard, const char *raw, int idx)
384 {
385 fz_java_device *jdev = (fz_java_device *)dev;
386 JNIEnv *env = jdev->env;
387 jstring jraw;
388
389 jraw = (*env)->NewStringUTF(env, raw);
390 if (!jraw || (*env)->ExceptionCheck(env))
391 fz_throw_java(ctx, env);
392
393 (*env)->CallVoidMethod(env, jdev->self, mid_Device_beginStructure, standard, jraw, idx);
394 if ((*env)->ExceptionCheck(env))
395 fz_throw_java(ctx, env);
396 }
397
398 static void
399 fz_java_device_end_structure(fz_context *ctx, fz_device *dev)
400 {
401 fz_java_device *jdev = (fz_java_device *)dev;
402 JNIEnv *env = jdev->env;
403
404 (*env)->CallVoidMethod(env, jdev->self, mid_Device_endStructure);
405 if ((*env)->ExceptionCheck(env))
406 fz_throw_java(ctx, env);
407 }
408
409 static void
410 fz_java_device_begin_metatext(fz_context *ctx, fz_device *dev, fz_metatext meta, const char *text)
411 {
412 fz_java_device *jdev = (fz_java_device *)dev;
413 JNIEnv *env = jdev->env;
414 jstring jtext;
415
416 jtext = (*env)->NewStringUTF(env, text);
417 if (!jtext || (*env)->ExceptionCheck(env))
418 fz_throw_java(ctx, env);
419
420 (*env)->CallVoidMethod(env, jdev->self, mid_Device_beginMetatext, meta, jtext);
421 if ((*env)->ExceptionCheck(env))
422 fz_throw_java(ctx, env);
423 }
424
425 static void
426 fz_java_device_end_metatext(fz_context *ctx, fz_device *dev)
427 {
428 fz_java_device *jdev = (fz_java_device *)dev;
429 JNIEnv *env = jdev->env;
430
431 (*env)->CallVoidMethod(env, jdev->self, mid_Device_endMetatext);
432 if ((*env)->ExceptionCheck(env))
433 fz_throw_java(ctx, env);
434 }
435
436 static void
437 fz_java_device_drop(fz_context *ctx, fz_device *dev)
438 {
439 fz_java_device *jdev = (fz_java_device *)dev;
440 JNIEnv *env = jdev->env;
441
442 (*env)->DeleteGlobalRef(env, jdev->self);
443 }
444
445 static fz_device *fz_new_java_device(fz_context *ctx, JNIEnv *env, jclass cls)
446 {
447 fz_java_device *dev = NULL;
448 jobject jself;
449
450 jself = (*env)->NewGlobalRef(env, cls);
451 if (!jself) return NULL;
452
453 fz_try(ctx)
454 {
455 dev = fz_new_derived_device(ctx, fz_java_device);
456 dev->env = env;
457 dev->self = jself;
458
459 dev->super.drop_device = fz_java_device_drop;
460
461 dev->super.fill_path = fz_java_device_fill_path;
462 dev->super.stroke_path = fz_java_device_stroke_path;
463 dev->super.clip_path = fz_java_device_clip_path;
464 dev->super.clip_stroke_path = fz_java_device_clip_stroke_path;
465
466 dev->super.fill_text = fz_java_device_fill_text;
467 dev->super.stroke_text = fz_java_device_stroke_text;
468 dev->super.clip_text = fz_java_device_clip_text;
469 dev->super.clip_stroke_text = fz_java_device_clip_stroke_text;
470 dev->super.ignore_text = fz_java_device_ignore_text;
471
472 dev->super.fill_shade = fz_java_device_fill_shade;
473 dev->super.fill_image = fz_java_device_fill_image;
474 dev->super.fill_image_mask = fz_java_device_fill_image_mask;
475 dev->super.clip_image_mask = fz_java_device_clip_image_mask;
476
477 dev->super.pop_clip = fz_java_device_pop_clip;
478
479 dev->super.begin_mask = fz_java_device_begin_mask;
480 dev->super.end_mask = fz_java_device_end_mask;
481 dev->super.begin_group = fz_java_device_begin_group;
482 dev->super.end_group = fz_java_device_end_group;
483
484 dev->super.begin_tile = fz_java_device_begin_tile;
485 dev->super.end_tile = fz_java_device_end_tile;
486
487 dev->super.render_flags = fz_java_device_render_flags;
488 dev->super.set_default_colorspaces = fz_java_device_set_default_colorspaces;
489
490 dev->super.begin_layer = fz_java_device_begin_layer;
491 dev->super.end_layer = fz_java_device_end_layer;
492
493 dev->super.begin_structure = fz_java_device_begin_structure;
494 dev->super.end_structure = fz_java_device_end_structure;
495
496 dev->super.begin_metatext = fz_java_device_begin_metatext;
497 dev->super.end_metatext = fz_java_device_end_metatext;
498 }
499 fz_catch(ctx)
500 {
501 fz_drop_device(ctx, &dev->super);
502 jni_rethrow(env, ctx);
503 }
504
505 return (fz_device *)dev;
506 }
507
508 JNIEXPORT jlong JNICALL
509 FUN(Device_newNative)(JNIEnv *env, jclass cls)
510 {
511 fz_context *ctx = get_context(env);
512 fz_device *dev = NULL;
513
514 if (!ctx) return 0;
515
516 fz_try(ctx)
517 dev = fz_new_java_device(ctx, env, cls);
518 fz_catch(ctx)
519 jni_rethrow(env, ctx);
520
521 return jlong_cast(dev);
522 }
523
524 JNIEXPORT void JNICALL
525 FUN(Device_finalize)(JNIEnv *env, jobject self)
526 {
527 fz_context *ctx = get_context(env);
528 fz_device *dev = from_Device_safe(env, self);
529 if (!ctx || !dev) return;
530 (*env)->SetLongField(env, self, fid_Device_pointer, 0);
531 fz_drop_device(ctx, dev);
532 }