OSDN Git Service

2006-08-14 Mark Wielaard <mark@klomp.org>
[pf3gnuchains/gcc-fork.git] / libjava / classpath / native / jni / gtk-peer / gnu_java_awt_peer_gtk_GdkPixbufDecoder.c
1 /* gdkpixbufdecoder.c
2    Copyright (C) 1999, 2003, 2004, 2005, 2006 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
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
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
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
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   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);
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         }
269
270       (*env)->DeleteLocalRef(env, jformat);
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 writerClass;
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   writerClass = (*env)->FindClass
300     (env, "gnu/java/awt/peer/gtk/GdkPixbufDecoder$GdkPixbufWriter");
301   dataOutputWriteID = (*env)->GetMethodID (env, writerClass,
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
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);
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
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     }
342 }
343
344 struct stream_save_request
345 {
346   JNIEnv *env;
347   jobject *writer;
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
361   jbuf = (*(ssr->env))->NewByteArray ((ssr->env), count);
362   cbuf = (*(ssr->env))->GetByteArrayElements ((ssr->env), jbuf, NULL);
363   memcpy (cbuf, buf, count);
364   (*(ssr->env))->ReleaseByteArrayElements ((ssr->env), jbuf, cbuf, 0);
365   (*(ssr->env))->CallVoidMethod ((ssr->env), *(ssr->writer), 
366                                  dataOutputWriteID, jbuf);  
367   (*(ssr->env))->DeleteLocalRef((ssr->env), jbuf);
368
369   return TRUE;
370 }
371
372
373 JNIEXPORT void JNICALL
374 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_streamImage
375 (JNIEnv *env, jclass clazz __attribute__((unused)),
376  jintArray jarr, jstring jenctype, jint width, jint height, 
377  jboolean hasAlpha, jobject writer) 
378 {
379   GdkPixbuf* pixbuf;  
380   jint *ints;
381   guchar a, r, g, b, *pix, *p;
382   GError *err = NULL;
383   const char *enctype;
384   int i;
385   struct stream_save_request ssr;
386
387   ssr.writer = &writer;
388   ssr.env = env;
389
390   ints = (*env)->GetIntArrayElements (env, jarr, NULL);
391   pix = g_malloc(width * height * (hasAlpha ? 4 : 3));
392
393   enctype = (*env)->GetStringUTFChars (env, jenctype, NULL);
394   g_assert(enctype != NULL);
395
396   g_assert (pix != NULL);
397   g_assert (ints != NULL);
398
399   p = pix;
400   for (i = 0; i < width*height; ++i)
401     {
402       /* 
403        * Java encodes pixels as integers in a predictable arithmetic order:
404        * 0xAARRGGBB. Since these are jints, JNI has already byte-swapped
405        * them for us if necessary, so they're in "our" endianness, whatever
406        * that is. It uses 4 bytes per pixel whether or not there's an alpha
407        * channel.
408        */
409
410       a = 0xff & (ints[i] >> 24);
411       r = 0xff & (ints[i] >> 16);
412       g = 0xff & (ints[i] >> 8);
413       b = 0xff & ints[i];
414
415       /* 
416        * GDK-pixbuf has a very different storage model:
417        *
418        *  - A different alpha order (alpha after colors).
419        *  - A different packing model (no alpha -> 3-bytes-per-pixel).
420        *  - A different "RGB" order (host memory order, not endian-neutral).
421        */
422
423       *p++ = r;
424       *p++ = g;
425       *p++ = b;
426       if (hasAlpha)
427         *p++ = a;
428     }
429
430   pixbuf =  gdk_pixbuf_new_from_data (pix,
431                                       GDK_COLORSPACE_RGB,
432                                       (gboolean) hasAlpha,
433                                       8, width, height, 
434                                       width * (hasAlpha ? 4 : 3), /* rowstride */
435                                       NULL, NULL);
436   g_assert (pixbuf != NULL);
437
438   g_assert(gdk_pixbuf_save_to_callback (pixbuf,
439                                         &save_to_stream,
440                                         &ssr,
441                                         enctype,
442                                         &err, NULL));
443
444   g_object_unref (pixbuf);
445
446   g_free(pix);
447
448   (*env)->ReleaseStringUTFChars (env, jenctype, enctype);  
449   (*env)->ReleaseIntArrayElements (env, jarr, ints, 0);
450 }
451
452
453 JNIEXPORT void JNICALL
454 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_pumpBytes
455   (JNIEnv *env, jobject obj, jbyteArray jarr, jint len)
456 {
457   GdkPixbufLoader *loader = NULL;
458   jbyte *bytes = NULL;
459   GError *err = NULL;
460
461   g_assert (len >= 1);
462   g_assert (jarr != NULL);
463
464   bytes = (*env)->GetByteArrayElements (env, jarr, NULL);
465   g_assert (bytes != NULL);
466   loader = (GdkPixbufLoader *)NSA_GET_PB_PTR (env, obj);
467   g_assert (loader != NULL);
468
469   gdk_pixbuf_loader_write (loader, (const guchar *) bytes, len, &err);
470
471   (*env)->ReleaseByteArrayElements (env, jarr, bytes, 0);
472
473   if (err != NULL)
474     {
475       JCL_ThrowException (env, "java/io/IOException", err->message);
476       g_error_free (err);
477     }
478 }