OSDN Git Service

86cad1487c619b4ba0d96263774178be39ae96a2
[pf3gnuchains/gcc-fork.git] / libjava / classpath / native / jni / gtk-peer / gnu_java_awt_peer_gtk_GtkImage.c
1 /* gtkimage.c
2    Copyright (C) 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 "gnu_java_awt_peer_gtk_GtkImage.h"
40 #include <gdk-pixbuf/gdk-pixbuf.h>
41
42 /* The constant fields in java.awt.Image */   
43 #define SCALE_DEFAULT      1
44 #define SCALE_FAST         2
45 #define SCALE_SMOOTH       4
46 #define SCALE_REPLICATE    8 
47 #define SCALE_AREA_AVERAGING  16
48
49 /* local stuff */
50 static GdkInterpType mapHints(jint hints);
51 static jboolean offScreen (JNIEnv * env, jobject obj);
52 static void *getData (JNIEnv * env, jobject obj);
53 static void createRawData (JNIEnv * env, jobject obj, void *ptr);
54 static void setWidthHeight (JNIEnv * env, jobject obj, int width, int height);
55
56 /**
57  * Loads a pixmap from a file.
58  */
59 JNIEXPORT jboolean JNICALL
60 Java_gnu_java_awt_peer_gtk_GtkImage_loadPixbuf
61   (JNIEnv *env, jobject obj, jstring name)
62 {
63   const char *filename;
64   int width, height;
65   GdkPixbuf *pixbuf;
66
67   gdk_threads_enter ();
68
69   /* Don't use the JCL convert function because it throws an exception
70      on failure */
71   filename = (*env)->GetStringUTFChars (env, name, 0);
72
73   if (filename == NULL)
74     {
75       gdk_threads_leave ();
76       return JNI_FALSE;
77     }
78
79   pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
80   if (pixbuf == NULL)
81     {
82       (*env)->ReleaseStringUTFChars (env, name, filename);
83       gdk_threads_leave ();
84       return JNI_FALSE;
85     }
86
87   width =  gdk_pixbuf_get_width (pixbuf);
88   height = gdk_pixbuf_get_height (pixbuf);
89   
90   createRawData (env, obj, pixbuf);
91   setWidthHeight(env, obj, width, height);
92   (*env)->ReleaseStringUTFChars (env, name, filename);
93
94   gdk_threads_leave ();
95
96   return JNI_TRUE;
97 }
98
99 /**
100  * Returns a copy of the pixel data as a java array.
101  */
102 JNIEXPORT jintArray JNICALL 
103 Java_gnu_java_awt_peer_gtk_GtkImage_getPixels(JNIEnv *env, jobject obj)
104 {
105   GdkPixbuf *pixbuf;
106   int width, height, rowstride;
107   guchar *pixeldata;
108   jintArray result_array;
109   jint *result_array_iter, *dst;
110   int i,j;
111
112   gdk_threads_enter ();
113
114   pixbuf = cp_gtk_image_get_pixbuf (env, obj);
115   width =  gdk_pixbuf_get_width (pixbuf);
116   height = gdk_pixbuf_get_height (pixbuf);
117   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
118
119   /* Must release the GDK lock before allocating memory through the
120      JVM, since some JVMs use the same lock for allocations and
121      finalization.  Deadlock can occur on those JVMs. */
122   gdk_threads_leave ();
123
124   result_array = (*env)->NewIntArray (env, (width * height));
125
126   gdk_threads_enter ();
127
128   dst = result_array_iter = 
129     (*env)->GetIntArrayElements (env, result_array, NULL);
130
131
132   pixeldata = gdk_pixbuf_get_pixels (pixbuf);
133
134   g_assert (gdk_pixbuf_get_bits_per_sample (pixbuf) == 8);
135
136   if (gdk_pixbuf_get_has_alpha (pixbuf))
137     {
138       for(i = 0 ; i < height; i++)
139         {
140           memcpy(dst, (void *)pixeldata, width * 4);
141           dst += width;
142           pixeldata += rowstride;
143         }
144     } else {
145       for(i = 0; i < height; i++)
146         {
147           for(j = 0; j < width; j++)
148             dst[j] = 0xFF000000 |
149               (pixeldata[j*3 + 2] & 0xFF) << 16 |
150               (pixeldata[j*3 + 1] & 0xFF) << 8 |
151               (pixeldata[j*3] & 0xFF);
152           dst += width;
153           pixeldata += rowstride;
154         }
155     }
156   
157   if (offScreen (env, obj) == JNI_TRUE)
158     gdk_pixbuf_unref (pixbuf);
159
160   (*env)->ReleaseIntArrayElements (env, result_array, result_array_iter, 0);
161     
162   gdk_threads_leave ();
163
164   return result_array;
165 }
166
167 /**
168  * Returns a copy of the pixel data as a java array.
169  * (GdkPixbuf only)
170  */
171 JNIEXPORT void JNICALL 
172 Java_gnu_java_awt_peer_gtk_GtkImage_setPixels(JNIEnv *env, jobject obj,
173                                               jintArray pixels)
174 {
175   GdkPixbuf *pixbuf = (GdkPixbuf *)getData (env, obj);
176   int width, height, rowstride;
177   guchar *pixeldata;
178   jint *src_array_iter, *src;
179   int i;
180
181   gdk_threads_enter ();
182
183   width =  gdk_pixbuf_get_width (pixbuf);
184   height = gdk_pixbuf_get_height (pixbuf);
185   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
186
187   src = src_array_iter = 
188     (*env)->GetIntArrayElements (env, pixels, NULL);
189
190   pixeldata = gdk_pixbuf_get_pixels (pixbuf);
191   for(i = 0 ; i < height; i++)
192     {
193       memcpy((void *)pixeldata, (void *)src, width * 4);
194       src += width;
195       pixeldata += rowstride;
196     }
197
198   (*env)->ReleaseIntArrayElements (env, pixels, src_array_iter, 0);
199
200   gdk_threads_leave ();
201 }
202
203 /**
204  * Allocates a Gtk Pixbuf or Pixmap.
205  */
206 JNIEXPORT void JNICALL
207 Java_gnu_java_awt_peer_gtk_GtkImage_createPixmap(JNIEnv *env, jobject obj)
208 {
209   int width, height;
210   jclass cls;
211   jfieldID field;
212
213   gdk_threads_enter ();
214
215   cls = (*env)->GetObjectClass (env, obj);
216   field = (*env)->GetFieldID (env, cls, "width", "I");
217   g_assert (field != 0);
218   width = (*env)->GetIntField (env, obj, field);
219
220   field = (*env)->GetFieldID (env, cls, "height", "I");
221   g_assert (field != 0);
222   height = (*env)->GetIntField (env, obj, field);
223
224   if (offScreen (env, obj) == JNI_FALSE)
225     createRawData (env, obj, gdk_pixbuf_new (GDK_COLORSPACE_RGB, 
226                                              TRUE,
227                                              8,
228                                              width,
229                                              height));
230   else
231     createRawData (env, obj, gdk_pixmap_new (NULL, width, height,
232                                              gdk_rgb_get_visual ()->depth));
233
234   gdk_threads_leave ();
235 }
236
237 /**
238  * Frees the Gtk Pixmap.
239  */
240 JNIEXPORT void JNICALL
241 Java_gnu_java_awt_peer_gtk_GtkImage_freePixmap(JNIEnv *env, jobject obj)
242 {
243   gdk_threads_enter ();
244   if (offScreen (env, obj) == JNI_FALSE)
245     gdk_pixbuf_unref ((GdkPixbuf *)getData (env, obj));
246   else
247     gdk_pixmap_unref ((GdkPixmap *)getData (env, obj));
248
249   gdk_threads_leave ();
250 }
251
252 /**
253  * Sets this pixmap to a scaled version of the source pixmap.
254  * width and height of the destination GtkImage must be set.
255  */
256 JNIEXPORT void JNICALL 
257 Java_gnu_java_awt_peer_gtk_GtkImage_createScaledPixmap(JNIEnv *env, 
258                                                        jobject destination, 
259                                                        jobject source,
260                                                        jint hints)
261 {
262   GdkPixbuf* dst;
263   int width, height;
264   jclass cls;
265   jfieldID field;
266
267   GdkPixbuf *pixbuf;
268
269   gdk_threads_enter ();
270
271   cls = (*env)->GetObjectClass (env, destination);
272   field = (*env)->GetFieldID (env, cls, "width", "I");
273   g_assert (field != 0);
274   width = (*env)->GetIntField (env, destination, field);
275
276   field = (*env)->GetFieldID (env, cls, "height", "I");
277   g_assert (field != 0);
278   height = (*env)->GetIntField (env, destination, field);
279
280   pixbuf = cp_gtk_image_get_pixbuf (env, source);
281
282   dst = gdk_pixbuf_scale_simple(pixbuf,
283                                 width, height,
284                                 mapHints(hints));
285
286   if (offScreen (env, source) == JNI_TRUE)
287       gdk_pixbuf_unref (pixbuf);
288
289   createRawData (env, destination, (void *)dst);
290
291   gdk_threads_leave ();
292 }
293
294 /**
295  * Draws the pixbuf at x, y, scaled to width and height and 
296  * optionally composited with a given background color.
297  */
298 JNIEXPORT void JNICALL
299 Java_gnu_java_awt_peer_gtk_GtkImage_drawPixelsScaled 
300   (JNIEnv *env, jobject obj, jobject gc_obj,
301    jint bg_red, jint bg_green, jint bg_blue, 
302    jint x, jint y, jint width, jint height, jboolean composite)
303 {
304   GdkPixbuf* dst;
305   struct graphics *g;
306   guint32 bgColor;
307
308   gdk_threads_enter ();
309   
310   bgColor = ((bg_red & 0xFF) << 16) |
311     ((bg_green & 0xFF) << 8) | (bg_blue & 0xFF);
312     
313   g = (struct graphics *) NSA_GET_PTR (env, gc_obj);
314   
315   if (!g || !GDK_IS_DRAWABLE (g->drawable))
316     {
317       gdk_threads_leave ();
318       return;
319     }
320
321   if (offScreen (env, obj) == JNI_FALSE)
322     {
323       GdkPixbuf* pixbuf = (GdkPixbuf *)getData (env, obj);
324
325       /* Scale and composite the image */
326       if (composite == JNI_TRUE)
327         dst = gdk_pixbuf_composite_color_simple (pixbuf,
328                                                  width,
329                                                  height,
330                                                  GDK_INTERP_BILINEAR,
331                                                  255,
332                                                  width,
333                                                  bgColor,
334                                                  bgColor);
335       else
336         dst = gdk_pixbuf_scale_simple(pixbuf,
337                                       width, height,
338                                       GDK_INTERP_BILINEAR);
339
340       gdk_draw_pixbuf (g->drawable,
341                        g->gc,
342                        dst,
343                        0, 0,
344                        x + g->x_offset, y + g->y_offset, 
345                        width, height,
346                        GDK_RGB_DITHER_NORMAL, 0, 0);
347       gdk_pixbuf_unref (dst);
348
349     } else {
350       /* Get a pixmap */
351       GdkPixmap* pixmap = (GdkPixmap *)getData (env, obj);
352       gdk_draw_drawable (g->drawable,
353                          g->gc,
354                          pixmap,
355                          0, 0, /* src x,y */
356                          x + g->x_offset, y + g->y_offset, 
357                          width, height);
358     }
359     
360   gdk_threads_leave ();
361 }
362
363 /**
364  * Draws the pixbuf at x, y, scaled to width and height and 
365  * optionally composited and/or flipped with a given background color.
366  */
367 JNIEXPORT void JNICALL 
368 Java_gnu_java_awt_peer_gtk_GtkImage_drawPixelsScaledFlipped 
369 (JNIEnv *env, jobject obj, jobject gc_obj,
370  jint bg_red, jint bg_green, jint bg_blue, 
371  jboolean flipx, jboolean flipy,
372  jint srcx, jint srcy, jint srcwidth, jint srcheight, 
373  jint dstx, jint dsty, jint dstwidth, jint dstheight, 
374  jboolean composite)
375 {
376   GdkPixbuf *pixbuf;
377   GdkPixbuf *tmp, *dst;
378   struct graphics *g;
379   guint32 bgColor;
380
381   gdk_threads_enter ();
382   
383   bgColor = ((bg_red & 0xFF) << 16) |
384     ((bg_green & 0xFF) << 8) | (bg_blue & 0xFF);
385     
386   g = (struct graphics *) NSA_GET_PTR (env, gc_obj);
387   
388   if (!g || !GDK_IS_DRAWABLE (g->drawable))
389     {
390       gdk_threads_leave ();
391       return;
392     }
393
394   if (offScreen (env, obj) == JNI_FALSE)
395     {
396       pixbuf = (GdkPixbuf *)getData (env, obj);
397
398       /* Get the source area */
399       tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 
400                             TRUE,
401                             8,
402                             srcwidth,
403                             srcheight);
404
405       gdk_pixbuf_copy_area (pixbuf, 
406                             srcx, srcy,
407                             srcwidth, srcheight,
408                             tmp, 
409                             0, 0); /* dst x , dst y */
410     } else {
411       /* Get a pixbuf from the pixmap */
412       GdkDrawable *pixmap = (GdkDrawable *)getData(env, obj);
413       tmp = gdk_pixbuf_get_from_drawable (NULL,
414                                           pixmap,
415                                           gdk_drawable_get_colormap( pixmap ),
416                                           srcx, srcy,
417                                           0, 0, /* dst x , dst y */
418                                           srcwidth, srcheight);
419     }
420
421   /* FIXME: This #if should be discarded once I feel comfortable about
422      GTK 2.6 dependence */
423 #if GTK_MINOR_VERSION > 4
424   /* Flip it if necessary. */
425   if (flipx == JNI_TRUE)
426     {
427       GdkPixbuf *tmp2 = gdk_pixbuf_flip (tmp, TRUE);
428       gdk_pixbuf_unref (tmp);
429       tmp = tmp2;
430     }
431   if (flipy == JNI_TRUE)
432     {
433       GdkPixbuf *tmp2 = gdk_pixbuf_flip (tmp, FALSE);
434       gdk_pixbuf_unref (tmp);
435       tmp = tmp2;
436     }
437 #endif
438   
439   /* Scale and composite the image */
440   if (composite == JNI_TRUE)
441     dst = gdk_pixbuf_composite_color_simple (tmp,
442                                              dstwidth,
443                                              dstheight,
444                                              GDK_INTERP_BILINEAR,
445                                              255,
446                                              dstwidth,
447                                              bgColor,
448                                              bgColor);
449   else
450     dst = gdk_pixbuf_scale_simple(tmp,
451                                   dstwidth, dstheight,
452                                   GDK_INTERP_BILINEAR);
453   gdk_pixbuf_unref (tmp);
454     
455   gdk_draw_pixbuf (g->drawable,
456                    g->gc,
457                    dst,
458                    0, 0,
459                    dstx + g->x_offset, dsty + g->y_offset, 
460                    dstwidth, dstheight,
461                    GDK_RGB_DITHER_NORMAL, 0, 0);
462   
463   gdk_pixbuf_unref (dst);
464
465   gdk_threads_leave ();
466 }
467
468 /**
469  * Used by GtkFramePeer
470  */
471 GdkPixbuf *cp_gtk_image_get_pixbuf (JNIEnv *env, jobject obj)
472 {
473   int width, height;
474   GdkPixbuf *pixbuf;
475   GdkPixmap* pixmap;
476   jclass cls;
477   jfieldID field;
478
479   if (offScreen (env, obj) == JNI_FALSE)
480     return (GdkPixbuf *)getData (env, obj);
481
482   cls = (*env)->GetObjectClass (env, obj);
483   field = (*env)->GetFieldID (env, cls, "width", "I");
484   g_assert (field != 0);
485   width = (*env)->GetIntField (env, obj, field);
486    
487   field = (*env)->GetFieldID (env, cls, "height", "I");
488   g_assert (field != 0);
489   height = (*env)->GetIntField (env, obj, field);
490
491   /* Get a pixmap */
492   pixmap = (GdkPixmap *)getData (env, obj);
493   pixbuf = gdk_pixbuf_get_from_drawable (NULL,
494                                          pixmap,
495                                          gdk_drawable_get_colormap( pixmap ),
496                                          0, 0, /* src x , src y */
497                                          0, 0, /* dst x , dst y */
498                                          width, height);
499   return pixbuf;
500 }
501
502 /**
503  * Used by GdkGraphics
504  */
505 GdkPixmap *cp_gtk_image_get_pixmap (JNIEnv *env, jobject obj)
506 {
507   if (offScreen (env, obj) == JNI_FALSE)
508     return NULL;
509   return (GdkPixmap *)getData (env, obj);
510 }
511
512 jboolean cp_gtk_image_is_offscreen (JNIEnv *env, jobject obj)
513 {
514   return offScreen(env, obj);
515 }
516
517 /**
518  * Maps java.awt.Image scaling hints to the native GDK ones.
519  */
520 static GdkInterpType mapHints(jint hints)
521 {
522   switch ( hints ) 
523     {
524       /* For FAST, we use the nearest-neighbor. Fastest and lowest quality. */
525     case SCALE_FAST:
526     case SCALE_REPLICATE:
527       return GDK_INTERP_NEAREST;
528
529       /* Hyperbolic for smooth. Slowest too. */
530     case SCALE_SMOOTH:
531       return GDK_INTERP_HYPER;
532       
533       /* the inbetweenish method */
534     case SCALE_AREA_AVERAGING:
535       return GDK_INTERP_TILES;
536
537       /* default to bilinear */
538     }
539   return GDK_INTERP_BILINEAR;
540 }
541
542 /* Sets the width and height fields of a GtkImage object. */
543 static void setWidthHeight (JNIEnv * env, jobject obj, int width, int height)
544 {
545   jclass cls;
546   jfieldID field;
547   
548   cls = (*env)->GetObjectClass (env, obj);
549   g_assert (cls != 0);
550   field = (*env)->GetFieldID (env, cls, "width", "I");
551   g_assert (field != 0);
552   (*env)->SetIntField (env, obj, field, (jint)width);
553    
554   field = (*env)->GetFieldID (env, cls, "height", "I");
555   g_assert (field != 0);
556   (*env)->SetIntField (env, obj, field, (jint)height);
557 }
558
559 /* Returns the value of the offScreen field. */
560 static jboolean offScreen (JNIEnv *env, jobject obj)
561 {
562   jclass cls;
563   jfieldID field;
564
565   cls = (*env)->GetObjectClass (env, obj);
566   field = (*env)->GetFieldID (env, cls, "offScreen", "Z");
567   g_assert (field != 0);
568   return (*env)->GetBooleanField (env, obj, field);
569 }
570
571 /* Store and get the pixbuf pointer */
572 static void
573 createRawData (JNIEnv * env, jobject obj, void *ptr)
574 {
575   jclass cls;
576   jmethodID method;
577   jobject data;
578   jfieldID data_fid;
579
580   cls = (*env)->GetObjectClass (env, obj);
581   data_fid = (*env)->GetFieldID (env, cls, "pixmap", 
582                                  "Lgnu/classpath/RawData;");
583   g_assert (data_fid != 0);
584
585 #if SIZEOF_VOID_P == 8
586   cls = (*env)->FindClass (env, "gnu/classpath/RawData64");
587   method = (*env)->GetMethodID (env, cls, "<init>", "(J)V");
588   data = (*env)->NewObject (env, cls, method, (jlong) ptr);
589 #else
590   cls = (*env)->FindClass (env, "gnu/classpath/RawData32");
591   method = (*env)->GetMethodID (env, cls, "<init>", "(I)V");
592   data = (*env)->NewObject (env, cls, method, (jint) ptr);
593 #endif
594
595   (*env)->SetObjectField (env, obj, data_fid, data);
596 }
597
598 static void *
599 getData (JNIEnv * env, jobject obj)
600 {
601   jclass cls;
602   jfieldID field;
603   jfieldID data_fid;
604   jobject data;
605
606   cls = (*env)->GetObjectClass (env, obj);
607   data_fid = (*env)->GetFieldID (env, cls, "pixmap", 
608                                  "Lgnu/classpath/RawData;");
609   g_assert (data_fid != 0);
610   data = (*env)->GetObjectField (env, obj, data_fid);
611
612 #if SIZEOF_VOID_P == 8
613   cls = (*env)->FindClass (env, "gnu/classpath/RawData64");
614   field = (*env)->GetFieldID (env, cls, "data", "J");
615   return (void *) (*env)->GetLongField (env, data, field);
616 #else
617   cls = (*env)->FindClass (env, "gnu/classpath/RawData32");
618   field = (*env)->GetFieldID (env, cls, "data", "I");
619   return (void *) (*env)->GetIntField (env, data, field);
620 #endif
621 }