]> Pileus Git - ~andy/gtk/blob - gtk/gtkpreview.c
hack on this some
[~andy/gtk] / gtk / gtkpreview.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "config.h"
28
29 #include <math.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #ifdef HAVE_SYS_PARAM_H
33 #include <sys/param.h>
34 #endif
35 #include "gdk/gdkrgb.h"
36 #include "gtkpreview.h"
37 #include "gtksignal.h"
38
39
40 #define PREVIEW_CLASS(w)      GTK_PREVIEW_CLASS (GTK_OBJECT (w)->klass)
41
42 enum {
43   ARG_0,
44   ARG_EXPAND
45 };
46
47
48 static void   gtk_preview_class_init    (GtkPreviewClass  *klass);
49 static void   gtk_preview_init          (GtkPreview       *preview);
50 static void   gtk_preview_set_arg       (GtkObject        *object,
51                                          GtkArg           *arg,
52                                          guint             arg_id);
53 static void   gtk_preview_get_arg       (GtkObject        *object,
54                                          GtkArg           *arg,
55                                          guint             arg_id);
56 static void   gtk_preview_finalize      (GObject          *object);
57 static void   gtk_preview_realize       (GtkWidget        *widget);
58 static void   gtk_preview_size_allocate (GtkWidget        *widget,
59                                          GtkAllocation    *allocation);
60 static gint   gtk_preview_expose        (GtkWidget        *widget,
61                                          GdkEventExpose   *event);
62 static void   gtk_preview_make_buffer   (GtkPreview       *preview);
63 static void   gtk_fill_lookup_array     (guchar           *array);
64
65 static GtkWidgetClass *parent_class = NULL;
66 static GtkPreviewClass *preview_class = NULL;
67 static gint install_cmap = FALSE;
68
69
70 GtkType
71 gtk_preview_get_type (void)
72 {
73   static GtkType preview_type = 0;
74
75   if (!preview_type)
76     {
77       static const GtkTypeInfo preview_info =
78       {
79         "GtkPreview",
80         sizeof (GtkPreview),
81         sizeof (GtkPreviewClass),
82         (GtkClassInitFunc) gtk_preview_class_init,
83         (GtkObjectInitFunc) gtk_preview_init,
84         /* reserved_1 */ NULL,
85         /* reserved_2 */ NULL,
86         (GtkClassInitFunc) NULL,
87       };
88
89       preview_type = gtk_type_unique (GTK_TYPE_WIDGET, &preview_info);
90     }
91
92   return preview_type;
93 }
94
95 static void
96 gtk_preview_class_init (GtkPreviewClass *klass)
97 {
98   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
99   GtkObjectClass *object_class;
100   GtkWidgetClass *widget_class;
101
102   object_class = (GtkObjectClass*) klass;
103   widget_class = (GtkWidgetClass*) klass;
104
105   parent_class = gtk_type_class (GTK_TYPE_WIDGET);
106   preview_class = klass;
107
108   gobject_class->finalize = gtk_preview_finalize;
109
110   object_class->set_arg = gtk_preview_set_arg;
111   object_class->get_arg = gtk_preview_get_arg;
112
113   widget_class->realize = gtk_preview_realize;
114   widget_class->size_allocate = gtk_preview_size_allocate;
115   widget_class->expose_event = gtk_preview_expose;
116
117   klass->info.visual = NULL;
118   klass->info.cmap = NULL;
119
120   klass->info.lookup = NULL;
121
122   klass->info.gamma = 1.0;
123
124   gdk_rgb_init ();
125   klass->info.cmap = gdk_rgb_get_cmap ();
126   klass->info.visual = gdk_rgb_get_visual ();
127
128   gtk_object_add_arg_type ("GtkPreview::expand",
129                            GTK_TYPE_BOOL,
130                            GTK_ARG_READWRITE,
131                            ARG_EXPAND);
132 }
133
134 static void
135 gtk_preview_set_arg (GtkObject *object,
136                      GtkArg    *arg,
137                      guint      arg_id)
138 {
139   GtkPreview *preview = GTK_PREVIEW (object);
140   
141   switch (arg_id)
142     {
143     case ARG_EXPAND:
144       gtk_preview_set_expand (preview, GTK_VALUE_BOOL (*arg));
145       break;
146     }
147 }
148
149 static void
150 gtk_preview_get_arg (GtkObject *object,
151                      GtkArg    *arg,
152                      guint      arg_id)
153 {
154   GtkPreview *preview;
155   
156   preview = GTK_PREVIEW (object);
157   
158   switch (arg_id)
159     {
160     case ARG_EXPAND:
161       GTK_VALUE_BOOL (*arg) = preview->expand;
162       break;
163     default:
164       arg->type = GTK_TYPE_INVALID;
165       break;
166     }
167 }
168
169 void
170 gtk_preview_reset (void)
171 {
172   /* unimplemented */
173 }
174
175 static void
176 gtk_preview_init (GtkPreview *preview)
177 {
178   preview->buffer = NULL;
179   preview->buffer_width = 0;
180   preview->buffer_height = 0;
181   preview->expand = FALSE;
182 }
183
184 void
185 gtk_preview_uninit (void)
186 {
187   /* unimplemented */
188 }
189
190 GtkWidget*
191 gtk_preview_new (GtkPreviewType type)
192 {
193   GtkPreview *preview;
194
195   preview = gtk_type_new (gtk_preview_get_type ());
196   preview->type = type;
197
198   if (type == GTK_PREVIEW_COLOR)
199     preview->bpp = 3;
200   else
201     preview->bpp = 1;
202
203   preview->dither = GDK_RGB_DITHER_NORMAL;
204
205   return GTK_WIDGET (preview);
206 }
207
208 void
209 gtk_preview_size (GtkPreview *preview,
210                   gint        width,
211                   gint        height)
212 {
213   g_return_if_fail (preview != NULL);
214   g_return_if_fail (GTK_IS_PREVIEW (preview));
215
216   if ((width != GTK_WIDGET (preview)->requisition.width) ||
217       (height != GTK_WIDGET (preview)->requisition.height))
218     {
219       GTK_WIDGET (preview)->requisition.width = width;
220       GTK_WIDGET (preview)->requisition.height = height;
221
222       if (preview->buffer)
223         g_free (preview->buffer);
224       preview->buffer = NULL;
225     }
226 }
227
228 void
229 gtk_preview_put (GtkPreview   *preview,
230                  GdkWindow    *window,
231                  GdkGC        *gc,
232                  gint          srcx,
233                  gint          srcy,
234                  gint          destx,
235                  gint          desty,
236                  gint          width,
237                  gint          height)
238 {
239   GtkWidget *widget;
240   GdkRectangle r1, r2, r3;
241   guchar *src;
242   guint bpp;
243   guint rowstride;
244
245   g_return_if_fail (preview != NULL);
246   g_return_if_fail (GTK_IS_PREVIEW (preview));
247   g_return_if_fail (window != NULL);
248
249   if (!preview->buffer)
250     return;
251
252   widget = GTK_WIDGET (preview);
253
254   r1.x = 0;
255   r1.y = 0;
256   r1.width = preview->buffer_width;
257   r1.height = preview->buffer_height;
258
259   r2.x = srcx;
260   r2.y = srcy;
261   r2.width = width;
262   r2.height = height;
263
264   if (!gdk_rectangle_intersect (&r1, &r2, &r3))
265     return;
266
267   bpp = preview->bpp;
268   rowstride = preview->rowstride;
269
270   src = preview->buffer + r3.y * rowstride + r3.x * bpp;
271
272   if (preview->type == GTK_PREVIEW_COLOR)
273     gdk_draw_rgb_image (window,
274                         gc,
275                         destx + (r3.x - srcx),
276                         desty + (r3.y - srcy),
277                         r3.width,
278                         r3.height,
279                         preview->dither,
280                         src,
281                         rowstride);
282   else
283     gdk_draw_gray_image (window,
284                          gc,
285                          destx + (r3.x - srcx),
286                          desty + (r3.y - srcy),
287                          r3.width,
288                          r3.height,
289                          preview->dither,
290                          src,
291                          rowstride);
292                         
293 }
294
295 void
296 gtk_preview_draw_row (GtkPreview *preview,
297                       guchar     *data,
298                       gint        x,
299                       gint        y,
300                       gint        w)
301 {
302   guint bpp;
303   guint rowstride;
304
305   g_return_if_fail (preview != NULL);
306   g_return_if_fail (GTK_IS_PREVIEW (preview));
307   g_return_if_fail (data != NULL);
308   g_return_if_fail (preview_class->info.visual != NULL);
309   
310   bpp = (preview->type == GTK_PREVIEW_COLOR ? 3 : 1);
311   rowstride = (preview->buffer_width * bpp + 3) & -4;
312
313   if ((w <= 0) || (y < 0))
314     return;
315
316   g_return_if_fail (data != NULL);
317
318   gtk_preview_make_buffer (preview);
319
320   if (x + w > preview->buffer_width)
321     return;
322
323   if (y + 1 > preview->buffer_height)
324     return;
325
326   if (preview_class->info.gamma == 1.0)
327     memcpy (preview->buffer + y * rowstride + x * bpp, data, w * bpp);
328   else
329     {
330       guint i, size;
331       guchar *src, *dst;
332       guchar *lookup;
333
334       if (preview_class->info.lookup != NULL)
335         lookup = preview_class->info.lookup;
336       else
337         {
338           preview_class->info.lookup = g_new (guchar, 256);
339           gtk_fill_lookup_array (preview_class->info.lookup);
340           lookup = preview_class->info.lookup;
341         }
342       size = w * bpp;
343       src = data;
344       dst = preview->buffer + y * rowstride + x * bpp;
345       for (i = 0; i < size; i++)
346         *dst++ = lookup[*src++];
347     }
348 }
349
350 void
351 gtk_preview_set_expand (GtkPreview *preview,
352                         gboolean    expand)
353 {
354   g_return_if_fail (preview != NULL);
355   g_return_if_fail (GTK_IS_PREVIEW (preview));
356
357   expand = expand != FALSE;
358
359   if (preview->expand != expand)
360     {
361       preview->expand = expand;
362       gtk_widget_queue_resize (GTK_WIDGET (preview));
363     }
364 }
365
366 void
367 gtk_preview_set_gamma (double _gamma)
368 {
369   if (!preview_class)
370     preview_class = gtk_type_class (gtk_preview_get_type ());
371
372   if (preview_class->info.gamma != _gamma)
373     {
374       preview_class->info.gamma = _gamma;
375       if (preview_class->info.lookup != NULL)
376         {
377           g_free (preview_class->info.lookup);
378           preview_class->info.lookup = NULL;
379         }
380     }
381 }
382
383 void
384 gtk_preview_set_color_cube (guint nred_shades,
385                             guint ngreen_shades,
386                             guint nblue_shades,
387                             guint ngray_shades)
388 {
389   /* unimplemented */
390 }
391
392 void
393 gtk_preview_set_install_cmap (gint _install_cmap)
394 {
395   /* effectively unimplemented */
396   install_cmap = _install_cmap;
397 }
398
399 void
400 gtk_preview_set_reserved (gint nreserved)
401 {
402
403   /* unimplemented */
404 }
405
406 void
407 gtk_preview_set_dither (GtkPreview      *preview,
408                         GdkRgbDither     dither)
409 {
410   preview->dither = dither;
411 }
412
413 GdkVisual*
414 gtk_preview_get_visual (void)
415 {
416   if (!preview_class)
417     preview_class = gtk_type_class (gtk_preview_get_type ());
418
419   return preview_class->info.visual;
420 }
421
422 GdkColormap*
423 gtk_preview_get_cmap (void)
424 {
425   if (!preview_class)
426     preview_class = gtk_type_class (gtk_preview_get_type ());
427
428   return preview_class->info.cmap;
429 }
430
431 GtkPreviewInfo*
432 gtk_preview_get_info (void)
433 {
434   if (!preview_class)
435     preview_class = gtk_type_class (gtk_preview_get_type ());
436
437   return &preview_class->info;
438 }
439
440
441 static void
442 gtk_preview_finalize (GObject *object)
443 {
444   GtkPreview *preview;
445
446   g_return_if_fail (GTK_IS_PREVIEW (object));
447
448   preview = GTK_PREVIEW (object);
449   if (preview->buffer)
450     g_free (preview->buffer);
451   preview->type = (GtkPreviewType) -1;
452
453   G_OBJECT_CLASS (parent_class)->finalize (object);
454 }
455
456 static void
457 gtk_preview_realize (GtkWidget *widget)
458 {
459   GtkPreview *preview;
460   GdkWindowAttr attributes;
461   gint attributes_mask;
462
463   g_return_if_fail (widget != NULL);
464   g_return_if_fail (GTK_IS_PREVIEW (widget));
465
466   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
467   preview = GTK_PREVIEW (widget);
468
469   attributes.window_type = GDK_WINDOW_CHILD;
470
471   if (preview->expand)
472     {
473       attributes.width = widget->allocation.width;
474       attributes.height = widget->allocation.height;
475     }
476   else
477     {
478       attributes.width = MIN (widget->requisition.width, widget->allocation.width);
479       attributes.height = MIN (widget->requisition.height, widget->allocation.height);
480     }
481
482   attributes.x = widget->allocation.x + (widget->allocation.width - attributes.width) / 2;
483   attributes.y = widget->allocation.y + (widget->allocation.height - attributes.height) / 2;;
484
485   attributes.wclass = GDK_INPUT_OUTPUT;
486   attributes.visual = preview_class->info.visual;
487   attributes.colormap = preview_class->info.cmap;
488   attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
489   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
490
491   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
492   gdk_window_set_user_data (widget->window, widget);
493
494   widget->style = gtk_style_attach (widget->style, widget->window);
495   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
496 }
497
498 static void   
499 gtk_preview_size_allocate (GtkWidget        *widget,
500                            GtkAllocation    *allocation)
501 {
502   GtkPreview *preview;
503   gint width, height;
504
505   g_return_if_fail (widget != NULL);
506   g_return_if_fail (GTK_IS_PREVIEW (widget));
507
508   preview = GTK_PREVIEW (widget);
509   widget->allocation = *allocation;
510
511   if (GTK_WIDGET_REALIZED (widget))
512     {
513       if (preview->expand)
514         {
515           width = widget->allocation.width;
516           height = widget->allocation.height;
517         }
518       else
519         {
520           width = MIN (widget->allocation.width, widget->requisition.width);
521           height = MIN (widget->allocation.height, widget->requisition.height);
522         }
523
524       gdk_window_move_resize (widget->window,
525                               widget->allocation.x + (widget->allocation.width - width) / 2,
526                               widget->allocation.y + (widget->allocation.height - height) / 2,
527                               width, height);
528     }
529 }
530
531 static gint
532 gtk_preview_expose (GtkWidget      *widget,
533                     GdkEventExpose *event)
534 {
535   GtkPreview *preview;
536   gint width, height;
537
538   g_return_val_if_fail (widget != NULL, FALSE);
539   g_return_val_if_fail (GTK_IS_PREVIEW (widget), FALSE);
540   g_return_val_if_fail (event != NULL, FALSE);
541
542   if (GTK_WIDGET_DRAWABLE (widget))
543     {
544       preview = GTK_PREVIEW (widget);
545       
546       gdk_window_get_size (widget->window, &width, &height);
547
548       gtk_preview_put (GTK_PREVIEW (widget),
549                        widget->window, widget->style->black_gc,
550                        event->area.x - (width - preview->buffer_width)/2,
551                        event->area.y - (height - preview->buffer_height)/2,
552                        event->area.x, event->area.y,
553                        event->area.width, event->area.height);
554     }
555   
556   return FALSE;
557 }
558
559 static void
560 gtk_preview_make_buffer (GtkPreview *preview)
561 {
562   GtkWidget *widget;
563   gint width;
564   gint height;
565
566   g_return_if_fail (preview != NULL);
567   g_return_if_fail (GTK_IS_PREVIEW (preview));
568
569   widget = GTK_WIDGET (preview);
570
571   if (preview->expand &&
572       (widget->allocation.width != 0) &&
573       (widget->allocation.height != 0))
574     {
575       width = widget->allocation.width;
576       height = widget->allocation.height;
577     }
578   else
579     {
580       width = widget->requisition.width;
581       height = widget->requisition.height;
582     }
583
584   if (!preview->buffer ||
585       (preview->buffer_width != width) ||
586       (preview->buffer_height != height))
587     {
588       if (preview->buffer)
589         g_free (preview->buffer);
590
591       preview->buffer_width = width;
592       preview->buffer_height = height;
593
594       preview->rowstride = (preview->buffer_width * preview->bpp + 3) & -4;
595       preview->buffer = g_new0 (guchar,
596                                 preview->buffer_height *
597                                 preview->rowstride);
598     }
599 }
600
601 /* This is used for implementing gamma. */
602 static void
603 gtk_fill_lookup_array (guchar *array)
604 {
605   double one_over_gamma;
606   double ind;
607   int val;
608   int i;
609
610   one_over_gamma = 1.0 / preview_class->info.gamma;
611
612   for (i = 0; i < 256; i++)
613     {
614       ind = (double) i / 255.0;
615       val = (int) (255 * pow (ind, one_over_gamma));
616       array[i] = val;
617     }
618 }