OSDN Git Service

libjava/
[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 "gnu_java_awt_peer_gtk_GdkPixbufDecoder.h"
46
47 #include <string.h>
48 #include <stdlib.h>
49
50 /* Union used for type punning. */
51 union env_union
52 {
53   void **void_env;
54   JNIEnv **jni_env;
55 };
56
57 static JavaVM *vm;
58
59 static jmethodID areaPreparedID;
60 static jmethodID areaUpdatedID;
61 static jmethodID dataOutputWriteID;
62 static jmethodID registerFormatID;
63
64 static void
65 area_prepared_cb (GdkPixbufLoader *loader, 
66                jobject *decoder)
67 {
68   JNIEnv *env = NULL;
69   union env_union e;
70   jint width = 0;
71   jint height = 0;
72   GdkPixbuf *pixbuf = NULL;
73
74   pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
75   g_assert (pixbuf != NULL);
76
77   width = gdk_pixbuf_get_width (pixbuf); 
78   height = gdk_pixbuf_get_height (pixbuf);
79
80   g_assert (decoder != NULL);
81
82   e.jni_env = &env;
83   (*vm)->GetEnv (vm, e.void_env, JNI_VERSION_1_1);
84
85   (*env)->CallVoidMethod (env,
86                           *decoder,
87                           areaPreparedID,
88                           width, height);
89 }
90
91 static void
92 area_updated_cb (GdkPixbufLoader *loader, 
93               gint x, gint y, 
94               gint width, gint height,
95               jobject *decoder)
96 {
97   JNIEnv *env;
98   union env_union e;
99   jint stride_bytes, stride_pixels, n_channels, n_pixels;
100   jintArray jpixels;  
101   jint *java_pixels;
102   guchar *gdk_pixels;
103
104   GdkPixbuf *pixbuf_no_alpha = NULL;
105   GdkPixbuf *pixbuf = NULL;
106
107 #ifndef WORDS_BIGENDIAN
108   int i;
109 #endif
110
111   pixbuf_no_alpha = gdk_pixbuf_loader_get_pixbuf (loader);
112   if (pixbuf_no_alpha == NULL)
113     return;
114
115   pixbuf = gdk_pixbuf_add_alpha(pixbuf_no_alpha, FALSE, 0, 0, 0);
116   g_assert (gdk_pixbuf_get_has_alpha (pixbuf));
117   
118   stride_bytes = gdk_pixbuf_get_rowstride (pixbuf);
119   n_channels = gdk_pixbuf_get_n_channels (pixbuf);
120   stride_pixels =  stride_bytes / n_channels;
121   n_pixels = height * stride_pixels;
122   gdk_pixels = gdk_pixbuf_get_pixels (pixbuf);
123
124   e.jni_env = &env;
125   (*vm)->GetEnv (vm, e.void_env, JNI_VERSION_1_1);
126
127   jpixels = (*env)->NewIntArray (env, n_pixels);
128
129   java_pixels = (*env)->GetIntArrayElements (env, jpixels, NULL);
130
131   memcpy (java_pixels, 
132           gdk_pixels + (y * stride_bytes), 
133           (height * stride_bytes));
134
135 #ifndef WORDS_BIGENDIAN
136   /* convert pixels from 0xBBGGRRAA to 0xAARRGGBB */
137   for (i = 0; i < n_pixels; ++i)
138     {
139       java_pixels[i] = SWAPU32 ((unsigned)java_pixels[i]);
140     }
141 #endif
142
143   g_object_unref (pixbuf);
144
145   (*env)->ReleaseIntArrayElements (env, jpixels, java_pixels, 0);
146
147   (*env)->CallVoidMethod (env, 
148                           *decoder, 
149                           areaUpdatedID,
150                           (jint) x, (jint) y,
151                           (jint) width, (jint) height,
152                           jpixels,
153                           stride_pixels);
154
155   (*env)->DeleteLocalRef(env, jpixels);
156 }
157
158 static void
159 closed_cb (GdkPixbufLoader *loader __attribute__((unused)), jobject *decoder)
160 {
161   JNIEnv *env;
162   union env_union e;
163   e.jni_env = &env;
164   (*vm)->GetEnv (vm, e.void_env, JNI_VERSION_1_1);
165
166   (*env)->DeleteGlobalRef (env, *decoder); 
167   g_free (decoder);
168 }
169
170
171
172 JNIEXPORT void JNICALL
173 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_initState
174   (JNIEnv *env, jobject obj)
175 {
176   GdkPixbufLoader *loader = NULL;
177   jobject *decoder = NULL;
178
179   decoder = (jobject *) g_malloc (sizeof (jobject));
180   g_assert (decoder != NULL);
181   *decoder = (*env)->NewGlobalRef (env, obj);
182
183   loader = gdk_pixbuf_loader_new ();
184   g_assert (loader != NULL);  
185   g_signal_connect (loader, "area-prepared", G_CALLBACK (area_prepared_cb), decoder);  
186   g_signal_connect (loader, "area-updated", G_CALLBACK (area_updated_cb), decoder);
187   g_signal_connect (loader, "closed", G_CALLBACK (closed_cb), decoder);
188
189   gtkpeer_set_pixbuf_loader (env, obj, loader);
190 }
191
192 static void
193 query_formats (JNIEnv *env, jclass clazz)
194 {
195   jobject jformat;
196   GSList *formats, *f;
197   GdkPixbufFormat *format;
198   gchar **ch, *name;
199   gint count;
200
201   jclass formatClass;
202   jmethodID addExtensionID;
203   jmethodID addMimeTypeID;
204   jobject string;
205
206   formatClass = (*env)->FindClass
207     (env, "gnu/java/awt/peer/gtk/GdkPixbufDecoder$ImageFormatSpec");
208
209   g_assert(formatClass != NULL);
210
211   addExtensionID = (*env)->GetMethodID (env, formatClass, 
212                                         "addExtension", 
213                                         "(Ljava/lang/String;)V");
214
215   addMimeTypeID = (*env)->GetMethodID (env, formatClass, 
216                                        "addMimeType", 
217                                        "(Ljava/lang/String;)V");
218   
219   formats = gdk_pixbuf_get_formats ();
220
221   for (f = formats; f; f = f->next)
222     {
223       format = (GdkPixbufFormat *) f->data;
224       name = gdk_pixbuf_format_get_name(format);
225
226       string = (*env)->NewStringUTF(env, name);
227       g_assert(string != NULL);
228
229       jformat = (*env)->CallStaticObjectMethod
230         (env, clazz, registerFormatID, string,
231          (jboolean) gdk_pixbuf_format_is_writable(format));
232       (*env)->DeleteLocalRef(env, string);
233       g_free(name);
234
235       g_assert(jformat != NULL);
236
237       ch = gdk_pixbuf_format_get_extensions(format);
238       count = 0;
239       while (*ch)
240         {
241           string = (*env)->NewStringUTF(env, *ch);
242           g_assert(string != NULL);
243           (*env)->CallVoidMethod (env, jformat, addExtensionID, string);
244           (*env)->DeleteLocalRef(env, string);
245           ++ch;
246           ++count;
247         }
248       g_strfreev(ch - count);
249
250       ch = gdk_pixbuf_format_get_mime_types(format);
251       count = 0;
252       while (*ch)
253         {
254           string = (*env)->NewStringUTF(env, *ch);
255           g_assert(string != NULL);
256           (*env)->CallVoidMethod (env, jformat, addMimeTypeID, string);
257           (*env)->DeleteLocalRef(env, string);
258           ++ch;
259           ++count;
260         }
261       g_strfreev(ch - count);
262       (*env)->DeleteLocalRef(env, jformat);
263     }
264
265   g_slist_free(formats);
266 }
267
268
269 JNIEXPORT void JNICALL
270 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_initStaticState 
271   (JNIEnv *env, jclass clazz)
272 {
273   jclass writerClass;
274
275   (*env)->GetJavaVM(env, &vm);
276
277   areaPreparedID = (*env)->GetMethodID (env, clazz, 
278                                         "areaPrepared", 
279                                         "(II)V");
280
281   areaUpdatedID = (*env)->GetMethodID (env, clazz,
282                                        "areaUpdated",
283                                        "(IIII[II)V");
284
285   registerFormatID = (*env)->GetStaticMethodID 
286     (env, clazz, 
287      "registerFormat", 
288      "(Ljava/lang/String;Z)"
289      "Lgnu/java/awt/peer/gtk/GdkPixbufDecoder$ImageFormatSpec;");
290
291   writerClass = (*env)->FindClass
292     (env, "gnu/java/awt/peer/gtk/GdkPixbufDecoder$GdkPixbufWriter");
293   dataOutputWriteID = (*env)->GetMethodID (env, writerClass,
294                                              "write", "([B)V");
295
296   query_formats (env, clazz);
297   
298   gtkpeer_init_pixbuf_IDs (env);
299 }
300
301
302 JNIEXPORT void JNICALL
303 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_finish
304 (JNIEnv *env, jobject obj, jboolean needs_close)
305 {
306   GdkPixbufLoader *loader = NULL;
307
308   loader = (GdkPixbufLoader *) gtkpeer_get_pixbuf_loader(env, obj);
309   if (loader == NULL)
310     return;
311
312   if (needs_close)
313     gdk_pixbuf_loader_close (loader, NULL);
314   g_object_unref (loader);
315 }
316
317 JNIEXPORT void JNICALL
318 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_pumpDone
319 (JNIEnv *env, jobject obj)
320 {
321   GError *err = NULL;
322   GdkPixbufLoader *loader = NULL;
323
324   loader = (GdkPixbufLoader *) gtkpeer_get_pixbuf_loader (env, obj);
325   g_assert (loader != NULL);
326
327   gdk_pixbuf_loader_close (loader, &err);
328
329   if (err != NULL)
330     {
331       JCL_ThrowException (env, "java/io/IOException", err->message);
332       g_error_free (err);
333     }
334 }
335
336 struct stream_save_request
337 {
338   JNIEnv *env;
339   jobject *writer;
340 };
341
342 static gboolean
343 save_to_stream(const gchar *buf,
344                gsize count,
345                GError **error __attribute__((unused)),
346                gpointer data)
347 {
348   struct stream_save_request *ssr = (struct stream_save_request *)data;
349
350   jbyteArray jbuf;
351   jbyte *cbuf;
352
353   jbuf = (*(ssr->env))->NewByteArray ((ssr->env), count);
354   cbuf = (*(ssr->env))->GetByteArrayElements ((ssr->env), jbuf, NULL);
355   memcpy (cbuf, buf, count);
356   (*(ssr->env))->ReleaseByteArrayElements ((ssr->env), jbuf, cbuf, 0);
357   (*(ssr->env))->CallVoidMethod ((ssr->env), *(ssr->writer), 
358                                  dataOutputWriteID, jbuf);  
359   (*(ssr->env))->DeleteLocalRef((ssr->env), jbuf);
360
361   return TRUE;
362 }
363
364
365 JNIEXPORT void JNICALL
366 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_streamImage
367 (JNIEnv *env, jclass clazz __attribute__((unused)),
368  jintArray jarr, jstring jenctype, jint width, jint height, 
369  jboolean hasAlpha, jobject writer) 
370 {
371   GdkPixbuf* pixbuf;  
372   jint *ints;
373   guchar a, r, g, b, *pix, *p;
374   GError *err = NULL;
375   const char *enctype;
376   int i;
377   struct stream_save_request ssr;
378
379   ssr.writer = &writer;
380   ssr.env = env;
381
382   ints = (*env)->GetIntArrayElements (env, jarr, NULL);
383   pix = g_malloc(width * height * (hasAlpha ? 4 : 3));
384
385   enctype = (*env)->GetStringUTFChars (env, jenctype, NULL);
386   g_assert(enctype != NULL);
387
388   g_assert (pix != NULL);
389   g_assert (ints != NULL);
390
391   p = pix;
392   for (i = 0; i < width*height; ++i)
393     {
394       /* 
395        * Java encodes pixels as integers in a predictable arithmetic order:
396        * 0xAARRGGBB. Since these are jints, JNI has already byte-swapped
397        * them for us if necessary, so they're in "our" endianness, whatever
398        * that is. It uses 4 bytes per pixel whether or not there's an alpha
399        * channel.
400        */
401
402       a = 0xff & (ints[i] >> 24);
403       r = 0xff & (ints[i] >> 16);
404       g = 0xff & (ints[i] >> 8);
405       b = 0xff & ints[i];
406
407       /* 
408        * GDK-pixbuf has a very different storage model:
409        *
410        *  - A different alpha order (alpha after colors).
411        *  - A different packing model (no alpha -> 3-bytes-per-pixel).
412        *  - A different "RGB" order (host memory order, not endian-neutral).
413        */
414
415       *p++ = r;
416       *p++ = g;
417       *p++ = b;
418       if (hasAlpha)
419         *p++ = a;
420     }
421
422   pixbuf =  gdk_pixbuf_new_from_data (pix,
423                                       GDK_COLORSPACE_RGB,
424                                       (gboolean) hasAlpha,
425                                       8, width, height, 
426                                       width * (hasAlpha ? 4 : 3), /* rowstride */
427                                       NULL, NULL);
428   g_assert (pixbuf != NULL);
429
430   g_assert(gdk_pixbuf_save_to_callback (pixbuf,
431                                         &save_to_stream,
432                                         &ssr,
433                                         enctype,
434                                         &err, NULL));
435
436   g_object_unref (pixbuf);
437
438   g_free(pix);
439
440   (*env)->ReleaseStringUTFChars (env, jenctype, enctype);  
441   (*env)->ReleaseIntArrayElements (env, jarr, ints, 0);
442 }
443
444
445 JNIEXPORT void JNICALL
446 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_pumpBytes
447   (JNIEnv *env, jobject obj, jbyteArray jarr, jint len)
448 {
449   GdkPixbufLoader *loader = NULL;
450   jbyte *bytes = NULL;
451   GError *err = NULL;
452
453   g_assert (len >= 1);
454   g_assert (jarr != NULL);
455
456   bytes = (*env)->GetByteArrayElements (env, jarr, NULL);
457   g_assert (bytes != NULL);
458   loader = (GdkPixbufLoader *) gtkpeer_get_pixbuf_loader (env, obj);
459   g_assert (loader != NULL);
460
461   gdk_pixbuf_loader_write (loader, (const guchar *) bytes, len, &err);
462
463   (*env)->ReleaseByteArrayElements (env, jarr, bytes, 0);
464
465   if (err != NULL)
466     {
467       JCL_ThrowException (env, "java/io/IOException", err->message);
468       g_error_free (err);
469     }
470 }