OSDN Git Service

b92d017a5d4c482d391da017494b997fc25ec741
[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   gchar **ch, *name;
214   gint count;
215
216   jclass formatClass;
217   jmethodID addExtensionID;
218   jmethodID addMimeTypeID;
219   jobject string;
220
221   formatClass = (*env)->FindClass
222     (env, "gnu/java/awt/peer/gtk/GdkPixbufDecoder$ImageFormatSpec");
223
224   g_assert(formatClass != NULL);
225
226   addExtensionID = (*env)->GetMethodID (env, formatClass, 
227                                         "addExtension", 
228                                         "(Ljava/lang/String;)V");
229
230   addMimeTypeID = (*env)->GetMethodID (env, formatClass, 
231                                        "addMimeType", 
232                                        "(Ljava/lang/String;)V");
233   
234   formats = gdk_pixbuf_get_formats ();
235
236   for (f = formats; f; f = f->next)
237     {
238       format = (GdkPixbufFormat *) f->data;
239       name = gdk_pixbuf_format_get_name(format);
240
241       string = (*env)->NewStringUTF(env, name);
242       g_assert(string != NULL);
243
244       jformat = (*env)->CallStaticObjectMethod
245         (env, clazz, registerFormatID, string,
246          (jboolean) gdk_pixbuf_format_is_writable(format));
247       (*env)->DeleteLocalRef(env, string);
248       g_free(name);
249
250       g_assert(jformat != NULL);
251
252       ch = gdk_pixbuf_format_get_extensions(format);
253       count = 0;
254       while (*ch)
255         {
256           string = (*env)->NewStringUTF(env, *ch);
257           g_assert(string != NULL);
258           (*env)->CallVoidMethod (env, jformat, addExtensionID, string);
259           (*env)->DeleteLocalRef(env, string);
260           ++ch;
261           ++count;
262         }
263       g_strfreev(ch - count);
264
265       ch = gdk_pixbuf_format_get_mime_types(format);
266       count = 0;
267       while (*ch)
268         {
269           string = (*env)->NewStringUTF(env, *ch);
270           g_assert(string != NULL);
271           (*env)->CallVoidMethod (env, jformat, addMimeTypeID, string);
272           (*env)->DeleteLocalRef(env, string);
273           ++ch;
274           ++count;
275         }
276       g_strfreev(ch - count);
277       (*env)->DeleteLocalRef(env, jformat);
278     }
279
280   g_slist_free(formats);
281 }
282
283
284 JNIEXPORT void JNICALL
285 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_initStaticState 
286   (JNIEnv *env, jclass clazz)
287 {
288   jclass writerClass;
289
290   (*env)->GetJavaVM(env, &vm);
291
292   areaPreparedID = (*env)->GetMethodID (env, clazz, 
293                                         "areaPrepared", 
294                                         "(II)V");
295
296   areaUpdatedID = (*env)->GetMethodID (env, clazz,
297                                        "areaUpdated",
298                                        "(IIII[II)V");
299
300   registerFormatID = (*env)->GetStaticMethodID 
301     (env, clazz, 
302      "registerFormat", 
303      "(Ljava/lang/String;Z)"
304      "Lgnu/java/awt/peer/gtk/GdkPixbufDecoder$ImageFormatSpec;");
305
306   writerClass = (*env)->FindClass
307     (env, "gnu/java/awt/peer/gtk/GdkPixbufDecoder$GdkPixbufWriter");
308   dataOutputWriteID = (*env)->GetMethodID (env, writerClass,
309                                              "write", "([B)V");
310
311   query_formats (env, clazz);
312   
313   NSA_PB_INIT (env, clazz);
314 }
315
316
317 JNIEXPORT void JNICALL
318 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_finish
319 (JNIEnv *env, jobject obj, jboolean needs_close)
320 {
321   GdkPixbufLoader *loader = NULL;
322
323   loader = (GdkPixbufLoader *)NSA_DEL_PB_PTR (env, obj);
324   if (loader == NULL)
325     return;
326
327   if (needs_close)
328     gdk_pixbuf_loader_close (loader, NULL);
329   g_object_unref (loader);
330 }
331
332 JNIEXPORT void JNICALL
333 Java_gnu_java_awt_peer_gtk_GdkPixbufDecoder_pumpDone
334 (JNIEnv *env, jobject obj)
335 {
336   GError *err = NULL;
337   GdkPixbufLoader *loader = NULL;
338
339   loader = (GdkPixbufLoader *)NSA_GET_PB_PTR (env, obj);
340   g_assert (loader != NULL);
341
342   gdk_pixbuf_loader_close (loader, &err);
343
344   if (err != NULL)
345     {
346       JCL_ThrowException (env, "java/io/IOException", err->message);
347       g_error_free (err);
348     }
349 }
350
351 struct stream_save_request
352 {
353   JNIEnv *env;
354   jobject *writer;
355 };
356
357 static gboolean
358 save_to_stream(const gchar *buf,
359                gsize count,
360                GError **error __attribute__((unused)),
361                gpointer data)
362 {
363   struct stream_save_request *ssr = (struct stream_save_request *)data;
364
365   jbyteArray jbuf;
366   jbyte *cbuf;
367
368   jbuf = (*(ssr->env))->NewByteArray ((ssr->env), count);
369   cbuf = (*(ssr->env))->GetByteArrayElements ((ssr->env), jbuf, NULL);
370   memcpy (cbuf, buf, count);
371   (*(ssr->env))->ReleaseByteArrayElements ((ssr->env), jbuf, cbuf, 0);
372   (*(ssr->env))->CallVoidMethod ((ssr->env), *(ssr->writer), 
373                                  dataOutputWriteID, jbuf);  
374   (*(ssr->env))->DeleteLocalRef((ssr->env), jbuf);
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 writer) 
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
394   ssr.writer = &writer;
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);
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
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     }
485 }