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