]> Pileus Git - ~andy/gtk/blob - gtk/gtkpreview.c
39d2618a0f3c0a49c4feb076c49f2f8a19f7bd40
[~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 Library 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library 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-1999.  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      (GtkObject        *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   GtkObjectClass *object_class;
99   GtkWidgetClass *widget_class;
100
101   object_class = (GtkObjectClass*) klass;
102   widget_class = (GtkWidgetClass*) klass;
103
104   parent_class = gtk_type_class (GTK_TYPE_WIDGET);
105   preview_class = klass;
106
107   object_class->set_arg = gtk_preview_set_arg;
108   object_class->get_arg = gtk_preview_get_arg;
109   object_class->finalize = gtk_preview_finalize;
110
111   widget_class->realize = gtk_preview_realize;
112   widget_class->size_allocate = gtk_preview_size_allocate;
113   widget_class->expose_event = gtk_preview_expose;
114
115   klass->info.visual = NULL;
116   klass->info.cmap = NULL;
117
118   klass->info.lookup = NULL;
119
120   klass->info.gamma = 1.0;
121
122   gdk_rgb_init ();
123   klass->info.cmap = gdk_rgb_get_cmap ();
124   klass->info.visual = gdk_rgb_get_visual ();
125
126   gtk_object_add_arg_type ("GtkPreview::expand",
127                            GTK_TYPE_BOOL,
128                            GTK_ARG_READWRITE,
129                            ARG_EXPAND);
130 }
131
132 static void
133 gtk_preview_set_arg (GtkObject *object,
134                      GtkArg    *arg,
135                      guint      arg_id)
136 {
137   GtkPreview *preview = GTK_PREVIEW (object);
138   
139   switch (arg_id)
140     {
141     case ARG_EXPAND:
142       gtk_preview_set_expand (preview, GTK_VALUE_BOOL (*arg));
143       break;
144     }
145 }
146
147 static void
148 gtk_preview_get_arg (GtkObject *object,
149                      GtkArg    *arg,
150                      guint      arg_id)
151 {
152   GtkPreview *preview;
153   
154   preview = GTK_PREVIEW (object);
155   
156   switch (arg_id)
157     {
158     case ARG_EXPAND:
159       GTK_VALUE_BOOL (*arg) = preview->expand;
160       break;
161     default:
162       arg->type = GTK_TYPE_INVALID;
163       break;
164     }
165 }
166
167 void
168 gtk_preview_reset (void)
169 {
170   /* unimplemented */
171 }
172
173 static void
174 gtk_preview_init (GtkPreview *preview)
175 {
176   preview->buffer = NULL;
177   preview->buffer_width = 0;
178   preview->buffer_height = 0;
179   preview->expand = FALSE;
180 }
181
182 void
183 gtk_preview_uninit (void)
184 {
185
186   /* unimplemented */
187 }
188
189 GtkWidget*
190 gtk_preview_new (GtkPreviewType type)
191 {
192   GtkPreview *preview;
193
194   preview = gtk_type_new (gtk_preview_get_type ());
195   preview->type = type;
196
197   if (type == GTK_PREVIEW_COLOR)
198     preview->bpp = 3;
199   else
200     preview->bpp = 1;
201
202   preview->dither = GDK_RGB_DITHER_NORMAL;
203
204   return GTK_WIDGET (preview);
205 }
206
207 void
208 gtk_preview_size (GtkPreview *preview,
209                   gint        width,
210                   gint        height)
211 {
212   g_return_if_fail (preview != NULL);
213   g_return_if_fail (GTK_IS_PREVIEW (preview));
214
215   if ((width != GTK_WIDGET (preview)->requisition.width) ||
216       (height != GTK_WIDGET (preview)->requisition.height))
217     {
218       GTK_WIDGET (preview)->requisition.width = width;
219       GTK_WIDGET (preview)->requisition.height = height;
220
221       if (preview->buffer)
222         g_free (preview->buffer);
223       preview->buffer = NULL;
224     }
225 }
226
227 void
228 gtk_preview_put (GtkPreview   *preview,
229                  GdkWindow    *window,
230                  GdkGC        *gc,
231                  gint          srcx,
232                  gint          srcy,
233                  gint          destx,
234                  gint          desty,
235                  gint          width,
236                  gint          height)
237 {
238   GtkWidget *widget;
239   GdkRectangle r1, r2, r3;
240   guchar *src;
241   guint bpp;
242   guint rowstride;
243
244   g_return_if_fail (preview != NULL);
245   g_return_if_fail (GTK_IS_PREVIEW (preview));
246   g_return_if_fail (window != NULL);
247
248   if (!preview->buffer)
249     return;
250
251   widget = GTK_WIDGET (preview);
252
253   r1.x = 0;
254   r1.y = 0;
255   r1.width = preview->buffer_width;
256   r1.height = preview->buffer_height;
257
258   r2.x = srcx;
259   r2.y = srcy;
260   r2.width = width;
261   r2.height = height;
262
263   if (!gdk_rectangle_intersect (&r1, &r2, &r3))
264     return;
265
266   bpp = preview->bpp;
267   rowstride = preview->rowstride;
268
269   src = preview->buffer + r3.y * rowstride + r3.x * bpp;
270
271   if (preview->type == GTK_PREVIEW_COLOR)
272     gdk_draw_rgb_image (window,
273                         gc,
274                         destx + (r3.x - srcx),
275                         desty + (r3.y - srcy),
276                         r3.width,
277                         r3.height,
278                         preview->dither,
279                         src,
280                         rowstride);
281   else
282     gdk_draw_gray_image (window,
283                          gc,
284                          destx + (r3.x - srcx),
285                          desty + (r3.y - srcy),
286                          r3.width,
287                          r3.height,
288                          preview->dither,
289                          src,
290                          rowstride);
291                         
292 }
293
294 void
295 gtk_preview_draw_row (GtkPreview *preview,
296                       guchar     *data,
297                       gint        x,
298                       gint        y,
299                       gint        w)
300 {
301   guint bpp;
302   guint rowstride;
303
304   g_return_if_fail (preview != NULL);
305   g_return_if_fail (GTK_IS_PREVIEW (preview));
306   g_return_if_fail (data != NULL);
307   g_return_if_fail (preview_class->info.visual != NULL);
308   
309   bpp = (preview->type == GTK_PREVIEW_COLOR ? 3 : 1);
310   rowstride = (preview->buffer_width * bpp + 3) & -4;
311
312   if ((w <= 0) || (y < 0))
313     return;
314
315   g_return_if_fail (data != NULL);
316
317   gtk_preview_make_buffer (preview);
318
319   if (x + w > preview->buffer_width)
320     return;
321
322   if (y + 1 > preview->buffer_height)
323     return;
324
325   if (preview_class->info.gamma == 1.0)
326     memcpy (preview->buffer + y * rowstride + x * bpp, data, w * bpp);
327   else
328     {
329       guint i, size;
330       guchar *src, *dst;
331       guchar *lookup;
332
333       if (preview_class->info.lookup != NULL)
334         lookup = preview_class->info.lookup;
335       else
336         {
337           preview_class->info.lookup = g_new (guchar, 256);
338           gtk_fill_lookup_array (preview_class->info.lookup);
339           lookup = preview_class->info.lookup;
340         }
341       size = w * bpp;
342       src = data;
343       dst = preview->buffer + y * rowstride + x * bpp;
344       for (i = 0; i < size; i++)
345         *dst++ = lookup[*src++];
346     }
347 }
348
349 void
350 gtk_preview_set_expand (GtkPreview *preview,
351                         gboolean    expand)
352 {
353   g_return_if_fail (preview != NULL);
354   g_return_if_fail (GTK_IS_PREVIEW (preview));
355
356   expand = expand != FALSE;
357
358   if (preview->expand != expand)
359     {
360       preview->expand = expand;
361       gtk_widget_queue_resize (GTK_WIDGET (preview));
362     }
363 }
364
365 void
366 gtk_preview_set_gamma (double _gamma)
367 {
368   if (!preview_class)
369     preview_class = gtk_type_class (gtk_preview_get_type ());
370
371   if (preview_class->info.gamma != _gamma)
372     {
373       preview_class->info.gamma = _gamma;
374       if (preview_class->info.lookup != NULL)
375         {
376           g_free (preview_class->info.lookup);
377           preview_class->info.lookup = NULL;
378         }
379     }
380 }
381
382 void
383 gtk_preview_set_color_cube (guint nred_shades,
384                             guint ngreen_shades,
385                             guint nblue_shades,
386                             guint ngray_shades)
387 {
388   /* unimplemented */
389 }
390
391 void
392 gtk_preview_set_install_cmap (gint _install_cmap)
393 {
394   /* effectively unimplemented */
395   install_cmap = _install_cmap;
396 }
397
398 void
399 gtk_preview_set_reserved (gint nreserved)
400 {
401
402   /* unimplemented */
403 }
404
405 void
406 gtk_preview_set_dither (GtkPreview      *preview,
407                         GdkRgbDither     dither)
408 {
409   preview->dither = dither;
410 }
411
412 GdkVisual*
413 gtk_preview_get_visual (void)
414 {
415   if (!preview_class)
416     preview_class = gtk_type_class (gtk_preview_get_type ());
417
418   return preview_class->info.visual;
419 }
420
421 GdkColormap*
422 gtk_preview_get_cmap (void)
423 {
424   if (!preview_class)
425     preview_class = gtk_type_class (gtk_preview_get_type ());
426
427   return preview_class->info.cmap;
428 }
429
430 GtkPreviewInfo*
431 gtk_preview_get_info (void)
432 {
433   if (!preview_class)
434     preview_class = gtk_type_class (gtk_preview_get_type ());
435
436   return &preview_class->info;
437 }
438
439
440 static void
441 gtk_preview_finalize (GtkObject *object)
442 {
443   GtkPreview *preview;
444
445   g_return_if_fail (object != NULL);
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   (* GTK_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 }