]>
Commit | Line | Data |
---|---|---|
f911ba98 | 1 | /* gdkpixbufdecoder.c |
4f9533c7 | 2 | Copyright (C) 1999, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. |
f911ba98 TT |
3 | |
4 | This file is part of GNU Classpath. | |
5 | ||
6 | GNU Classpath is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 2, or (at your option) | |
9 | any later version. | |
10 | ||
11 | GNU Classpath is distributed in the hope that it will be useful, but | |
12 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with GNU Classpath; see the file COPYING. If not, write to the | |
18 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
19 | 02110-1301 USA. | |
20 | ||
21 | Linking this library statically or dynamically with other modules is | |
22 | making a combined work based on this library. Thus, the terms and | |
23 | conditions of the GNU General Public License cover the whole | |
24 | combination. | |
25 | ||
26 | As a special exception, the copyright holders of this library give you | |
27 | permission to link this library with independent modules to produce an | |
28 | executable, regardless of the license terms of these independent | |
29 | modules, and to copy and distribute the resulting executable under | |
30 | terms of your choice, provided that you also meet, for each linked | |
31 | independent module, the terms and conditions of the license of that | |
32 | module. An independent module is a module which is not derived from | |
33 | or based on this library. If you modify this library, you may extend | |
34 | this exception to your version of the library, but you are not | |
35 | obligated to do so. If you do not wish to do so, delete this | |
36 | exception statement from your version. */ | |
37 | ||
38 | #include <gtkpeer.h> | |
39 | #include <gdk/gdk.h> | |
40 | #include <gdk-pixbuf/gdk-pixbuf.h> | |
41 | #include <gdk-pixbuf/gdk-pixbuf-loader.h> | |
42 | ||
43 | #include <jni.h> | |
44 | #include <jcl.h> | |
45 | #include "native_state.h" | |
46 | #include "gnu_java_awt_peer_gtk_GdkPixbufDecoder.h" | |
47 | ||
48 | #include <string.h> | |
49 | #include <stdlib.h> | |
50 | ||
51 | static struct state_table *native_pixbufdecoder_state_table; | |
52 | ||
53 | #define NSA_PB_INIT(env, clazz) \ | |
54 | native_pixbufdecoder_state_table = cp_gtk_init_state_table (env, clazz) | |
55 | ||
56 | #define NSA_GET_PB_PTR(env, obj) \ | |
57 | cp_gtk_get_state (env, obj, native_pixbufdecoder_state_table) | |
58 | ||
59 | #define NSA_SET_PB_PTR(env, obj, ptr) \ | |
60 | cp_gtk_set_state (env, obj, native_pixbufdecoder_state_table, (void *)ptr) | |
61 | ||
62 | #define NSA_DEL_PB_PTR(env, obj) \ | |
63 | cp_gtk_remove_state_slot (env, obj, native_pixbufdecoder_state_table) | |
64 | ||
65 | /* Union used for type punning. */ | |
66 | union env_union | |
67 | { | |
68 | void **void_env; | |
69 | JNIEnv **jni_env; | |
70 | }; | |
71 | ||
72 | static JavaVM *vm; | |
73 | ||
74 | static jmethodID areaPreparedID; | |
75 | static jmethodID areaUpdatedID; | |
76 | static jmethodID dataOutputWriteID; | |
77 | static jmethodID registerFormatID; | |
78 | ||
79 | static void | |
80 | area_prepared_cb (GdkPixbufLoader *loader, | |
81 | jobject *decoder) | |
82 | { | |
83 | JNIEnv *env = NULL; | |
84 | union env_union e; | |
85 | jint width = 0; | |
86 | jint height = 0; | |
87 | GdkPixbuf *pixbuf = NULL; | |
88 | ||
89 | pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); | |
90 | g_assert (pixbuf != NULL); | |
91 | ||
92 | width = gdk_pixbuf_get_width (pixbuf); | |
93 | height = gdk_pixbuf_get_height (pixbuf); | |
94 | ||
95 | g_assert (decoder != NULL); | |
96 | ||
97 | e.jni_env = &env; | |
98 | (*vm)->GetEnv (vm, e.void_env, JNI_VERSION_1_1); | |
99 | ||
f911ba98 TT |
100 | (*env)->CallVoidMethod (env, |
101 | *decoder, | |
102 | areaPreparedID, | |
103 | width, height); | |
f911ba98 TT |
104 | } |
105 | ||
106 | static void | |
107 | area_updated_cb (GdkPixbufLoader *loader, | |
108 | gint x, gint y, | |
109 | gint width, gint height, | |
110 | jobject *decoder) | |
111 | { | |
112 | JNIEnv *env; | |
113 | union env_union e; | |
114 | jint stride_bytes, stride_pixels, n_channels, n_pixels; | |
115 | jintArray jpixels; | |
116 | jint *java_pixels; | |
117 | guchar *gdk_pixels; | |
118 | ||
119 | GdkPixbuf *pixbuf_no_alpha = NULL; | |
120 | GdkPixbuf *pixbuf = NULL; | |
121 | ||
122 | #ifndef WORDS_BIGENDIAN | |
123 | int i; | |
124 | #endif | |
125 | ||
126 | pixbuf_no_alpha = gdk_pixbuf_loader_get_pixbuf (loader); | |
127 | if (pixbuf_no_alpha == NULL) | |
128 | return; | |
129 | ||
130 | pixbuf = gdk_pixbuf_add_alpha(pixbuf_no_alpha, FALSE, 0, 0, 0); | |
131 | g_assert (gdk_pixbuf_get_has_alpha (pixbuf)); | |
132 | ||
133 | stride_bytes = gdk_pixbuf_get_rowstride (pixbuf); | |
134 | n_channels = gdk_pixbuf_get_n_channels (pixbuf); | |
135 | stride_pixels = stride_bytes / n_channels; | |
136 | n_pixels = height * stride_pixels; | |
137 | gdk_pixels = gdk_pixbuf_get_pixels (pixbuf); | |
138 | ||
139 | e.jni_env = &env; | |
140 | (*vm)->GetEnv (vm, e.void_env, JNI_VERSION_1_1); | |
141 | ||
f911ba98 TT |
142 | jpixels = (*env)->NewIntArray (env, n_pixels); |
143 | ||
f911ba98 TT |
144 | java_pixels = (*env)->GetIntArrayElements (env, jpixels, NULL); |
145 | ||
146 | memcpy (java_pixels, | |
147 | gdk_pixels + (y * stride_bytes), | |
148 | (height * stride_bytes)); | |
149 | ||
150 | #ifndef WORDS_BIGENDIAN | |
151 | /* convert pixels from 0xBBGGRRAA to 0xAARRGGBB */ | |
152 | for (i = 0; i < n_pixels; ++i) | |
153 | { | |
154 | java_pixels[i] = SWAPU32 ((unsigned)java_pixels[i]); | |
155 | } | |
156 | #endif | |
157 | ||
158 | g_object_unref (pixbuf); | |
159 | ||
f911ba98 TT |
160 | (*env)->ReleaseIntArrayElements (env, jpixels, java_pixels, 0); |
161 | ||
162 | (*env)->CallVoidMethod (env, | |
163 | *decoder, | |
164 | areaUpdatedID, | |
165 | (jint) x, (jint) y, | |
166 | (jint) width, (jint) height, | |
167 | jpixels, | |
168 | stride_pixels); | |
169 | ||
170 | (*env)->DeleteLocalRef(env, jpixels); | |
f911ba98 TT |
171 | } |
172 | ||
173 | static void | |
174 | closed_cb (GdkPixbufLoader *loader __attribute__((unused)), jobject *decoder) | |
175 | { | |
176 | JNIEnv *env; | |
177 | union env_union e; | |
178 | e.jni_env = &env; | |
179 | (*vm)->GetEnv (vm, e.void_env, JNI_VERSION_1_1); | |
180 | ||
f911ba98 TT |
181 | (*env)->DeleteGlobalRef (env, *decoder); |
182 | g_free (decoder); | |
f911ba98 TT |
183 | } |
184 | ||
185 | ||
186 | ||
187 | JNIEXPORT void JNICALL | |
188 | Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_initState | |
189 | (JNIEnv *env, jobject obj) | |
190 | { | |
191 | GdkPixbufLoader *loader = NULL; | |
192 | jobject *decoder = NULL; | |
193 | ||
f911ba98 TT |
194 | decoder = (jobject *) g_malloc (sizeof (jobject)); |
195 | g_assert (decoder != NULL); | |
196 | *decoder = (*env)->NewGlobalRef (env, obj); | |
197 | ||
198 | loader = gdk_pixbuf_loader_new (); | |
199 | g_assert (loader != NULL); | |
200 | g_signal_connect (loader, "area-prepared", G_CALLBACK (area_prepared_cb), decoder); | |
201 | g_signal_connect (loader, "area-updated", G_CALLBACK (area_updated_cb), decoder); | |
202 | g_signal_connect (loader, "closed", G_CALLBACK (closed_cb), decoder); | |
203 | ||
204 | NSA_SET_PB_PTR (env, obj, loader); | |
f911ba98 TT |
205 | } |
206 | ||
207 | static void | |
208 | query_formats (JNIEnv *env, jclass clazz) | |
209 | { | |
210 | jobject jformat; | |
211 | GSList *formats, *f; | |
212 | GdkPixbufFormat *format; | |
213 | char **ch, *name; | |
214 | ||
215 | jclass formatClass; | |
216 | jmethodID addExtensionID; | |
217 | jmethodID addMimeTypeID; | |
218 | jobject string; | |
219 | ||
220 | formatClass = (*env)->FindClass | |
221 | (env, "gnu/java/awt/peer/gtk/GdkPixbufDecoder$ImageFormatSpec"); | |
222 | ||
223 | g_assert(formatClass != NULL); | |
224 | ||
225 | addExtensionID = (*env)->GetMethodID (env, formatClass, | |
226 | "addExtension", | |
227 | "(Ljava/lang/String;)V"); | |
228 | ||
229 | addMimeTypeID = (*env)->GetMethodID (env, formatClass, | |
230 | "addMimeType", | |
231 | "(Ljava/lang/String;)V"); | |
232 | ||
233 | formats = gdk_pixbuf_get_formats (); | |
234 | ||
235 | for (f = formats; f; f = f->next) | |
236 | { | |
237 | format = (GdkPixbufFormat *) f->data; | |
238 | name = gdk_pixbuf_format_get_name(format); | |
239 | ||
240 | string = (*env)->NewStringUTF(env, name); | |
241 | g_assert(string != NULL); | |
242 | ||
243 | jformat = (*env)->CallStaticObjectMethod | |
244 | (env, clazz, registerFormatID, string, | |
245 | (jboolean) gdk_pixbuf_format_is_writable(format)); | |
246 | (*env)->DeleteLocalRef(env, string); | |
247 | ||
248 | g_assert(jformat != NULL); | |
249 | ||
250 | ch = gdk_pixbuf_format_get_extensions(format); | |
251 | while (*ch) | |
252 | { | |
253 | string = (*env)->NewStringUTF(env, *ch); | |
254 | g_assert(string != NULL); | |
255 | (*env)->CallVoidMethod (env, jformat, addExtensionID, string); | |
256 | (*env)->DeleteLocalRef(env, string); | |
257 | ++ch; | |
258 | } | |
259 | ||
260 | ch = gdk_pixbuf_format_get_mime_types(format); | |
261 | while (*ch) | |
262 | { | |
263 | string = (*env)->NewStringUTF(env, *ch); | |
264 | g_assert(string != NULL); | |
265 | (*env)->CallVoidMethod (env, jformat, addMimeTypeID, string); | |
266 | (*env)->DeleteLocalRef(env, string); | |
267 | ++ch; | |
268 | } | |
21276379 MW |
269 | |
270 | (*env)->DeleteLocalRef(env, jformat); | |
f911ba98 TT |
271 | } |
272 | ||
273 | g_slist_free(formats); | |
274 | } | |
275 | ||
276 | ||
277 | JNIEXPORT void JNICALL | |
278 | Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_initStaticState | |
279 | (JNIEnv *env, jclass clazz) | |
280 | { | |
281 | jclass dataOutputClass; | |
282 | ||
283 | (*env)->GetJavaVM(env, &vm); | |
284 | ||
285 | areaPreparedID = (*env)->GetMethodID (env, clazz, | |
286 | "areaPrepared", | |
287 | "(II)V"); | |
288 | ||
289 | areaUpdatedID = (*env)->GetMethodID (env, clazz, | |
290 | "areaUpdated", | |
291 | "(IIII[II)V"); | |
292 | ||
293 | registerFormatID = (*env)->GetStaticMethodID | |
294 | (env, clazz, | |
295 | "registerFormat", | |
296 | "(Ljava/lang/String;Z)" | |
297 | "Lgnu/java/awt/peer/gtk/GdkPixbufDecoder$ImageFormatSpec;"); | |
298 | ||
299 | ||
300 | dataOutputClass = (*env)->FindClass(env, "java/io/DataOutput"); | |
301 | dataOutputWriteID = (*env)->GetMethodID (env, dataOutputClass, | |
302 | "write", "([B)V"); | |
303 | ||
304 | query_formats (env, clazz); | |
305 | ||
306 | NSA_PB_INIT (env, clazz); | |
307 | } | |
308 | ||
309 | ||
310 | JNIEXPORT void JNICALL | |
311 | Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_finish | |
312 | (JNIEnv *env, jobject obj, jboolean needs_close) | |
313 | { | |
314 | GdkPixbufLoader *loader = NULL; | |
315 | ||
f911ba98 TT |
316 | loader = (GdkPixbufLoader *)NSA_DEL_PB_PTR (env, obj); |
317 | if (loader == NULL) | |
318 | return; | |
319 | ||
320 | if (needs_close) | |
321 | gdk_pixbuf_loader_close (loader, NULL); | |
322 | g_object_unref (loader); | |
f911ba98 TT |
323 | } |
324 | ||
325 | JNIEXPORT void JNICALL | |
326 | Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_pumpDone | |
327 | (JNIEnv *env, jobject obj) | |
328 | { | |
329 | GError *err = NULL; | |
330 | GdkPixbufLoader *loader = NULL; | |
331 | ||
f911ba98 TT |
332 | loader = (GdkPixbufLoader *)NSA_GET_PB_PTR (env, obj); |
333 | g_assert (loader != NULL); | |
334 | ||
335 | gdk_pixbuf_loader_close (loader, &err); | |
336 | ||
337 | if (err != NULL) | |
338 | { | |
339 | JCL_ThrowException (env, "java/io/IOException", err->message); | |
340 | g_error_free (err); | |
341 | } | |
f911ba98 TT |
342 | } |
343 | ||
344 | struct stream_save_request | |
345 | { | |
346 | JNIEnv *env; | |
347 | jobject *stream; | |
348 | }; | |
349 | ||
350 | static gboolean | |
351 | save_to_stream(const gchar *buf, | |
352 | gsize count, | |
353 | GError **error __attribute__((unused)), | |
354 | gpointer data) | |
355 | { | |
356 | struct stream_save_request *ssr = (struct stream_save_request *)data; | |
357 | ||
358 | jbyteArray jbuf; | |
359 | jbyte *cbuf; | |
360 | ||
4f9533c7 MW |
361 | /* FIXME. Don't call user code directly on this thread. |
362 | Store bytes and signal a "pump" thread to deliver to user code. | |
363 | Then we don't have to drop/acquire any locks. */ | |
f911ba98 TT |
364 | gdk_threads_leave (); |
365 | ||
366 | jbuf = (*(ssr->env))->NewByteArray ((ssr->env), count); | |
367 | cbuf = (*(ssr->env))->GetByteArrayElements ((ssr->env), jbuf, NULL); | |
368 | memcpy (cbuf, buf, count); | |
369 | (*(ssr->env))->ReleaseByteArrayElements ((ssr->env), jbuf, cbuf, 0); | |
370 | (*(ssr->env))->CallVoidMethod ((ssr->env), *(ssr->stream), | |
371 | dataOutputWriteID, jbuf); | |
21276379 | 372 | (*(ssr->env))->DeleteLocalRef((ssr->env), jbuf); |
f911ba98 TT |
373 | |
374 | gdk_threads_enter (); | |
375 | ||
376 | return TRUE; | |
377 | } | |
378 | ||
379 | ||
380 | JNIEXPORT void JNICALL | |
381 | Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_streamImage | |
382 | (JNIEnv *env, jclass clazz __attribute__((unused)), | |
383 | jintArray jarr, jstring jenctype, jint width, jint height, | |
384 | jboolean hasAlpha, jobject stream) | |
385 | { | |
386 | GdkPixbuf* pixbuf; | |
387 | jint *ints; | |
388 | guchar a, r, g, b, *pix, *p; | |
389 | GError *err = NULL; | |
390 | const char *enctype; | |
391 | int i; | |
392 | struct stream_save_request ssr; | |
393 | ||
f911ba98 TT |
394 | ssr.stream = &stream; |
395 | ssr.env = env; | |
396 | ||
397 | ints = (*env)->GetIntArrayElements (env, jarr, NULL); | |
398 | pix = g_malloc(width * height * (hasAlpha ? 4 : 3)); | |
399 | ||
400 | enctype = (*env)->GetStringUTFChars (env, jenctype, NULL); | |
401 | g_assert(enctype != NULL); | |
402 | ||
403 | g_assert (pix != NULL); | |
404 | g_assert (ints != NULL); | |
405 | ||
406 | p = pix; | |
407 | for (i = 0; i < width*height; ++i) | |
408 | { | |
409 | /* | |
410 | * Java encodes pixels as integers in a predictable arithmetic order: | |
411 | * 0xAARRGGBB. Since these are jints, JNI has already byte-swapped | |
412 | * them for us if necessary, so they're in "our" endianness, whatever | |
413 | * that is. It uses 4 bytes per pixel whether or not there's an alpha | |
414 | * channel. | |
415 | */ | |
416 | ||
417 | a = 0xff & (ints[i] >> 24); | |
418 | r = 0xff & (ints[i] >> 16); | |
419 | g = 0xff & (ints[i] >> 8); | |
420 | b = 0xff & ints[i]; | |
421 | ||
422 | /* | |
423 | * GDK-pixbuf has a very different storage model: | |
424 | * | |
425 | * - A different alpha order (alpha after colors). | |
426 | * - A different packing model (no alpha -> 3-bytes-per-pixel). | |
427 | * - A different "RGB" order (host memory order, not endian-neutral). | |
428 | */ | |
429 | ||
430 | *p++ = r; | |
431 | *p++ = g; | |
432 | *p++ = b; | |
433 | if (hasAlpha) | |
434 | *p++ = a; | |
435 | } | |
436 | ||
437 | pixbuf = gdk_pixbuf_new_from_data (pix, | |
438 | GDK_COLORSPACE_RGB, | |
439 | (gboolean) hasAlpha, | |
440 | 8, width, height, | |
441 | width * (hasAlpha ? 4 : 3), /* rowstride */ | |
442 | NULL, NULL); | |
443 | g_assert (pixbuf != NULL); | |
444 | ||
445 | g_assert(gdk_pixbuf_save_to_callback (pixbuf, | |
446 | &save_to_stream, | |
447 | &ssr, | |
448 | enctype, | |
449 | &err, NULL)); | |
450 | ||
451 | g_object_unref (pixbuf); | |
452 | ||
453 | g_free(pix); | |
454 | ||
455 | (*env)->ReleaseStringUTFChars (env, jenctype, enctype); | |
456 | (*env)->ReleaseIntArrayElements (env, jarr, ints, 0); | |
f911ba98 TT |
457 | } |
458 | ||
459 | ||
460 | JNIEXPORT void JNICALL | |
461 | Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_pumpBytes | |
462 | (JNIEnv *env, jobject obj, jbyteArray jarr, jint len) | |
463 | { | |
464 | GdkPixbufLoader *loader = NULL; | |
465 | jbyte *bytes = NULL; | |
466 | GError *err = NULL; | |
467 | ||
f911ba98 TT |
468 | g_assert (len >= 1); |
469 | g_assert (jarr != NULL); | |
470 | ||
471 | bytes = (*env)->GetByteArrayElements (env, jarr, NULL); | |
472 | g_assert (bytes != NULL); | |
473 | loader = (GdkPixbufLoader *)NSA_GET_PB_PTR (env, obj); | |
474 | g_assert (loader != NULL); | |
475 | ||
476 | gdk_pixbuf_loader_write (loader, (const guchar *) bytes, len, &err); | |
477 | ||
478 | (*env)->ReleaseByteArrayElements (env, jarr, bytes, 0); | |
479 | ||
480 | if (err != NULL) | |
481 | { | |
482 | JCL_ThrowException (env, "java/io/IOException", err->message); | |
483 | g_error_free (err); | |
484 | } | |
f911ba98 | 485 | } |