1 /* gdkpixbufdecoder.c
2 Copyright (C) 1999, 2003, 2004, 2005 Free Software Foundation, Inc.
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
area_prepared_cb(GdkPixbufLoader * loader,jobject * decoder)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
100 (*env)->CallVoidMethod (env,
101 *decoder,
102 areaPreparedID,
103 width, height);
104 }
105
106 static void
area_updated_cb(GdkPixbufLoader * loader,gint x,gint y,gint width,gint height,jobject * decoder)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
142 jpixels = (*env)->NewIntArray (env, n_pixels);
143
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
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);
171 }
172
173 static void
closed_cb(GdkPixbufLoader * loader,jobject * decoder)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
181 (*env)->DeleteGlobalRef (env, *decoder);
182 g_free (decoder);
183 }
184
185
186
187 JNIEXPORT void JNICALL
Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_initState(JNIEnv * env,jobject obj)188 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_initState
189 (JNIEnv *env, jobject obj)
190 {
191 GdkPixbufLoader *loader = NULL;
192 jobject *decoder = NULL;
193
194 gdk_threads_enter ();
195
196 decoder = (jobject *) g_malloc (sizeof (jobject));
197 g_assert (decoder != NULL);
198 *decoder = (*env)->NewGlobalRef (env, obj);
199
200 loader = gdk_pixbuf_loader_new ();
201 g_assert (loader != NULL);
202 g_signal_connect (loader, "area-prepared", G_CALLBACK (area_prepared_cb), decoder);
203 g_signal_connect (loader, "area-updated", G_CALLBACK (area_updated_cb), decoder);
204 g_signal_connect (loader, "closed", G_CALLBACK (closed_cb), decoder);
205
206 NSA_SET_PB_PTR (env, obj, loader);
207
208 gdk_threads_leave ();
209 }
210
211 static void
query_formats(JNIEnv * env,jclass clazz)212 query_formats (JNIEnv *env, jclass clazz)
213 {
214 jobject jformat;
215 GSList *formats, *f;
216 GdkPixbufFormat *format;
217 char **ch, *name;
218
219 jclass formatClass;
220 jmethodID addExtensionID;
221 jmethodID addMimeTypeID;
222 jobject string;
223
224 formatClass = (*env)->FindClass
225 (env, "gnu/java/awt/peer/gtk/GdkPixbufDecoder$ImageFormatSpec");
226
227 g_assert(formatClass != NULL);
228
229 addExtensionID = (*env)->GetMethodID (env, formatClass,
230 "addExtension",
231 "(Ljava/lang/String;)V");
232
233 addMimeTypeID = (*env)->GetMethodID (env, formatClass,
234 "addMimeType",
235 "(Ljava/lang/String;)V");
236
237 formats = gdk_pixbuf_get_formats ();
238
239 for (f = formats; f; f = f->next)
240 {
241 format = (GdkPixbufFormat *) f->data;
242 name = gdk_pixbuf_format_get_name(format);
243
244 string = (*env)->NewStringUTF(env, name);
245 g_assert(string != NULL);
246
247 jformat = (*env)->CallStaticObjectMethod
248 (env, clazz, registerFormatID, string,
249 (jboolean) gdk_pixbuf_format_is_writable(format));
250 (*env)->DeleteLocalRef(env, string);
251
252 g_assert(jformat != NULL);
253
254 ch = gdk_pixbuf_format_get_extensions(format);
255 while (*ch)
256 {
257 string = (*env)->NewStringUTF(env, *ch);
258 g_assert(string != NULL);
259 (*env)->CallVoidMethod (env, jformat, addExtensionID, string);
260 (*env)->DeleteLocalRef(env, string);
261 ++ch;
262 }
263
264 ch = gdk_pixbuf_format_get_mime_types(format);
265 while (*ch)
266 {
267 string = (*env)->NewStringUTF(env, *ch);
268 g_assert(string != NULL);
269 (*env)->CallVoidMethod (env, jformat, addMimeTypeID, string);
270 (*env)->DeleteLocalRef(env, string);
271 ++ch;
272 }
273 }
274
275 g_slist_free(formats);
276 }
277
278
279 JNIEXPORT void JNICALL
Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_initStaticState(JNIEnv * env,jclass clazz)280 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_initStaticState
281 (JNIEnv *env, jclass clazz)
282 {
283 jclass dataOutputClass;
284
285 (*env)->GetJavaVM(env, &vm);
286
287 areaPreparedID = (*env)->GetMethodID (env, clazz,
288 "areaPrepared",
289 "(II)V");
290
291 areaUpdatedID = (*env)->GetMethodID (env, clazz,
292 "areaUpdated",
293 "(IIII[II)V");
294
295 registerFormatID = (*env)->GetStaticMethodID
296 (env, clazz,
297 "registerFormat",
298 "(Ljava/lang/String;Z)"
299 "Lgnu/java/awt/peer/gtk/GdkPixbufDecoder$ImageFormatSpec;");
300
301
302 dataOutputClass = (*env)->FindClass(env, "java/io/DataOutput");
303 dataOutputWriteID = (*env)->GetMethodID (env, dataOutputClass,
304 "write", "([B)V");
305
306 query_formats (env, clazz);
307
308 NSA_PB_INIT (env, clazz);
309 }
310
311
312 JNIEXPORT void JNICALL
Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_finish(JNIEnv * env,jobject obj,jboolean needs_close)313 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_finish
314 (JNIEnv *env, jobject obj, jboolean needs_close)
315 {
316 GdkPixbufLoader *loader = NULL;
317
318 gdk_threads_enter ();
319
320 loader = (GdkPixbufLoader *)NSA_DEL_PB_PTR (env, obj);
321 if (loader == NULL)
322 return;
323
324 if (needs_close)
325 gdk_pixbuf_loader_close (loader, NULL);
326 g_object_unref (loader);
327
328 gdk_threads_leave ();
329 }
330
331 JNIEXPORT void JNICALL
Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_pumpDone(JNIEnv * env,jobject obj)332 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_pumpDone
333 (JNIEnv *env, jobject obj)
334 {
335 GError *err = NULL;
336 GdkPixbufLoader *loader = NULL;
337
338 gdk_threads_enter ();
339
340 loader = (GdkPixbufLoader *)NSA_GET_PB_PTR (env, obj);
341 g_assert (loader != NULL);
342
343 gdk_pixbuf_loader_close (loader, &err);
344
345 if (err != NULL)
346 {
347 JCL_ThrowException (env, "java/io/IOException", err->message);
348 g_error_free (err);
349 }
350
351 gdk_threads_leave ();
352 }
353
354 struct stream_save_request
355 {
356 JNIEnv *env;
357 jobject *stream;
358 };
359
360 static gboolean
save_to_stream(const gchar * buf,gsize count,GError ** error,gpointer data)361 save_to_stream(const gchar *buf,
362 gsize count,
363 GError **error __attribute__((unused)),
364 gpointer data)
365 {
366 struct stream_save_request *ssr = (struct stream_save_request *)data;
367
368 jbyteArray jbuf;
369 jbyte *cbuf;
370
371 gdk_threads_leave ();
372
373 jbuf = (*(ssr->env))->NewByteArray ((ssr->env), count);
374 cbuf = (*(ssr->env))->GetByteArrayElements ((ssr->env), jbuf, NULL);
375 memcpy (cbuf, buf, count);
376 (*(ssr->env))->ReleaseByteArrayElements ((ssr->env), jbuf, cbuf, 0);
377 (*(ssr->env))->CallVoidMethod ((ssr->env), *(ssr->stream),
378 dataOutputWriteID, jbuf);
379
380 gdk_threads_enter ();
381
382 return TRUE;
383 }
384
385
386 JNIEXPORT void JNICALL
Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_streamImage(JNIEnv * env,jclass clazz,jintArray jarr,jstring jenctype,jint width,jint height,jboolean hasAlpha,jobject stream)387 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_streamImage
388 (JNIEnv *env, jclass clazz __attribute__((unused)),
389 jintArray jarr, jstring jenctype, jint width, jint height,
390 jboolean hasAlpha, jobject stream)
391 {
392 GdkPixbuf* pixbuf;
393 jint *ints;
394 guchar a, r, g, b, *pix, *p;
395 GError *err = NULL;
396 const char *enctype;
397 int i;
398 struct stream_save_request ssr;
399
400 gdk_threads_enter ();
401
402 ssr.stream = &stream;
403 ssr.env = env;
404
405 ints = (*env)->GetIntArrayElements (env, jarr, NULL);
406 pix = g_malloc(width * height * (hasAlpha ? 4 : 3));
407
408 enctype = (*env)->GetStringUTFChars (env, jenctype, NULL);
409 g_assert(enctype != NULL);
410
411 g_assert (pix != NULL);
412 g_assert (ints != NULL);
413
414 p = pix;
415 for (i = 0; i < width*height; ++i)
416 {
417 /*
418 * Java encodes pixels as integers in a predictable arithmetic order:
419 * 0xAARRGGBB. Since these are jints, JNI has already byte-swapped
420 * them for us if necessary, so they're in "our" endianness, whatever
421 * that is. It uses 4 bytes per pixel whether or not there's an alpha
422 * channel.
423 */
424
425 a = 0xff & (ints[i] >> 24);
426 r = 0xff & (ints[i] >> 16);
427 g = 0xff & (ints[i] >> 8);
428 b = 0xff & ints[i];
429
430 /*
431 * GDK-pixbuf has a very different storage model:
432 *
433 * - A different alpha order (alpha after colors).
434 * - A different packing model (no alpha -> 3-bytes-per-pixel).
435 * - A different "RGB" order (host memory order, not endian-neutral).
436 */
437
438 *p++ = r;
439 *p++ = g;
440 *p++ = b;
441 if (hasAlpha)
442 *p++ = a;
443 }
444
445 pixbuf = gdk_pixbuf_new_from_data (pix,
446 GDK_COLORSPACE_RGB,
447 (gboolean) hasAlpha,
448 8, width, height,
449 width * (hasAlpha ? 4 : 3), /* rowstride */
450 NULL, NULL);
451 g_assert (pixbuf != NULL);
452
453 g_assert(gdk_pixbuf_save_to_callback (pixbuf,
454 &save_to_stream,
455 &ssr,
456 enctype,
457 &err, NULL));
458
459 g_object_unref (pixbuf);
460
461 g_free(pix);
462
463 (*env)->ReleaseStringUTFChars (env, jenctype, enctype);
464 (*env)->ReleaseIntArrayElements (env, jarr, ints, 0);
465
466 gdk_threads_leave ();
467 }
468
469
470 JNIEXPORT void JNICALL
Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_pumpBytes(JNIEnv * env,jobject obj,jbyteArray jarr,jint len)471 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_pumpBytes
472 (JNIEnv *env, jobject obj, jbyteArray jarr, jint len)
473 {
474 GdkPixbufLoader *loader = NULL;
475 jbyte *bytes = NULL;
476 GError *err = NULL;
477
478 gdk_threads_enter ();
479
480 g_assert (len >= 1);
481 g_assert (jarr != NULL);
482
483 bytes = (*env)->GetByteArrayElements (env, jarr, NULL);
484 g_assert (bytes != NULL);
485 loader = (GdkPixbufLoader *)NSA_GET_PB_PTR (env, obj);
486 g_assert (loader != NULL);
487
488 gdk_pixbuf_loader_write (loader, (const guchar *) bytes, len, &err);
489
490 (*env)->ReleaseByteArrayElements (env, jarr, bytes, 0);
491
492 if (err != NULL)
493 {
494 JCL_ThrowException (env, "java/io/IOException", err->message);
495 g_error_free (err);
496 }
497
498 gdk_threads_leave ();
499 }
500