1 /*
2    SANE EPSON backend
3    Copyright (C) 2001, 2005, 2008  SEIKO EPSON Corporation
4 
5    Date         Author      Reason
6    06/01/2001   N.Sasaki    New
7 
8    This file is part of the `iscan' program.
9 
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 
24    As a special exception, the copyright holders give permission
25    to link the code of this program with the esmod library and
26    distribute linked combinations including the two.  You must obey
27    the GNU General Public License in all respects for all of the
28    code used other then esmod.
29 */
30 
31 #include <config.h>
32 
33 #include "gettext.h"
34 #define  _(msg_id)	gettext (msg_id)
35 
36 /*------------------------------------------------------------*/
37 #include <gtk/gtk.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 
42 /*------------------------------------------------------------*/
43 #include "pisa_view_manager.h"
44 #include "pisa_gamma_correction.h"
45 #include "xpm_data.h"
46 #include "pisa_tool.h"
47 #include "pisa_error.h"
48 
49 #define RADIUS		3
50 #define MIN_DISTANCE	8
51 #define NUM_POINTS	13
52 
53 /*------------------------------------------------------------*/
54 char ** g_gamma_xpm [ ] =
55 {
56   gamma_m_xpm,	// mono
57   gamma_r_xpm,	// red
58   gamma_g_xpm,	// green
59   gamma_b_xpm,	// blue
60 };
61 
62 /*------------------------------------------------------------*/
switch_page(GtkNotebook * notebook,GtkNotebookPage * page,gint page_num,gpointer * data)63 static void switch_page ( GtkNotebook * notebook,
64 			  GtkNotebookPage * page,
65 			  gint page_num,
66 			  gpointer * data )
67 {
68   gamma_correction	* gamma_cls;
69 
70   notebook = notebook;
71   page = page;
72 
73   gamma_cls = ( gamma_correction * ) data;
74 
75   gamma_cls->change_page ( page_num );
76 }
77 
78 /*------------------------------------------------------------*/
click_reset(GtkWidget * widget,gpointer * data)79 static void click_reset ( GtkWidget * widget,
80 			  gpointer * data )
81 {
82   gamma_correction	* gamma_cls;
83 
84   widget = widget;
85 
86   gamma_cls = ( gamma_correction * ) data;
87 
88   gamma_cls->reset ( 0 );
89 
90   ::g_view_manager->update_lut ( );
91 
92   preview_window *prev_cls =
93     (preview_window *) ::g_view_manager->get_window_cls (ID_WINDOW_PREV);
94 
95   prev_cls->update_img ( );
96 }
97 
98 /*------------------------------------------------------------*/
gamma_curve_event(GtkWidget * widget,GdkEvent * event,gpointer * data)99 static gint gamma_curve_event ( GtkWidget * widget,
100 				GdkEvent * event,
101 				gpointer * data )
102 {
103   gamma_correction	* gamma_cls;
104 
105   gamma_cls = ( gamma_correction * ) data;
106 
107   return gamma_cls->event ( widget, event );
108 }
109 
110 /*------------------------------------------------------------*/
init(void)111 int gamma_correction::init ( void )
112 {
113   int i;
114 
115   m_page = 0;
116 
117   for ( i = 0; i < WG_GAMMA_NUM; i++ )
118     m_widget [ i ] = 0;
119 
120   for ( i = 0; i < 4; i++ )
121     {
122       m_pixmap [ i ] = 0;
123       m_active [ i ] = 0;
124 
125       reset_points ( i );
126     }
127 
128   m_basis [ 0 ] [ 0 ] = -0.5; m_basis [ 0 ] [ 1 ] =  1.5; m_basis [ 0 ] [ 2 ] = -1.5; m_basis [ 0 ] [ 3 ] =  0.5;
129   m_basis [ 1 ] [ 0 ] =  1.0; m_basis [ 1 ] [ 1 ] = -2.5; m_basis [ 1 ] [ 2 ] =  2.0; m_basis [ 1 ] [ 3 ] = -0.5;
130   m_basis [ 2 ] [ 0 ] = -0.5; m_basis [ 2 ] [ 1 ] =  0.0; m_basis [ 2 ] [ 2 ] =  0.5; m_basis [ 2 ] [ 3 ] =  0.0;
131   m_basis [ 3 ] [ 0 ] =  0.0; m_basis [ 3 ] [ 1 ] =  1.0; m_basis [ 3 ] [ 2 ] =  0.0; m_basis [ 3 ] [ 3 ] =  0.0;
132 
133   for ( i = 0; i < 2; i++ )
134     m_cursor [ i ] = 0;
135 
136   return PISA_ERR_SUCCESS;
137 }
138 
139 /*------------------------------------------------------------*/
create_window(GtkWidget * parent)140 GtkWidget * gamma_correction::create_window ( GtkWidget * parent )
141 {
142   GtkWidget	* frame;
143   GtkWidget	* vbox;
144   GtkWidget	* widget;
145 
146   frame = ::gtk_frame_new ( _( "Tone Correction" ) );
147   ::gtk_container_border_width ( GTK_CONTAINER ( frame ), 5 );
148 
149   vbox = ::gtk_vbox_new ( FALSE, 2 );
150   ::gtk_container_add ( GTK_CONTAINER ( frame ), vbox );
151   ::gtk_widget_show ( vbox );
152 
153   widget = create_gamma_notebook ( parent );
154   ::gtk_box_pack_start ( GTK_BOX ( vbox ), widget, FALSE, FALSE, 0 );
155   ::gtk_widget_show ( widget );
156 
157   widget = create_reset_button ( );
158   ::gtk_box_pack_start ( GTK_BOX ( vbox ), widget, FALSE, FALSE, 0 );
159   ::gtk_widget_show ( widget );
160 
161   m_cursor [ PISA_CS_GM_X      ] = ::gdk_cursor_new ( GDK_X_CURSOR );
162   m_cursor [ PISA_CS_GM_FLEUR  ] = ::gdk_cursor_new ( GDK_FLEUR );
163   m_cursor [ PISA_CS_GM_TCROSS ] = ::gdk_cursor_new ( GDK_TCROSS );
164 
165   return frame;
166 }
167 
168 /*------------------------------------------------------------*/
close_window(int destroy)169 int gamma_correction::close_window ( int destroy )
170 {
171   int i;
172 
173   destroy = destroy;
174 
175   for ( i = 0; i < 2; i++ )
176     {
177       if ( m_cursor [ i ] )
178 	::gdk_cursor_destroy ( m_cursor [ i ] );
179       m_cursor [ i ] = 0;
180     }
181 
182   return PISA_ERR_SUCCESS;
183 }
184 
185 /*------------------------------------------------------------*/
sensitive(int is_prev_img)186 void gamma_correction::sensitive ( int is_prev_img )
187 {
188   gboolean	enable_rgb, enable_r_g_b;
189 
190   settings set = g_view_manager->get_settings ();
191 
192   // grayout
193   switch ( set.imgtype.pixeltype )
194     {
195     case PISA_PT_RGB:
196       enable_rgb	= TRUE;
197       enable_r_g_b	= TRUE;
198       break;
199     case PISA_PT_GRAY:
200       enable_rgb	= TRUE;
201       enable_r_g_b	= FALSE;
202       break;
203     case PISA_PT_BW:
204       enable_rgb	= FALSE;
205       enable_r_g_b	= FALSE;
206       break;
207     default:
208       return;
209     }
210 
211   if ( is_prev_img == 0 )
212     enable_rgb = enable_r_g_b = FALSE;
213 
214   m_active [ 0 ] = enable_rgb;
215   m_active [ 1 ] = enable_r_g_b;
216   m_active [ 2 ] = enable_r_g_b;
217   m_active [ 3 ] = enable_r_g_b;
218 
219   ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_TAB_RGB ], enable_rgb );
220   ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_TAB_RED ], enable_r_g_b );
221   ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_TAB_GRN ], enable_r_g_b );
222   ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_TAB_BLU ], enable_r_g_b );
223 
224   ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_BOX_RGB ], enable_rgb );
225   ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_BOX_RED ], enable_r_g_b );
226   ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_BOX_GRN ], enable_r_g_b );
227   ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_BOX_BLU ], enable_r_g_b );
228 
229   if ( enable_rgb == TRUE && enable_r_g_b == FALSE )
230     ::gtk_notebook_set_page ( GTK_NOTEBOOK ( m_widget [ WG_GAMMA_NOTE ] ),
231 			      GAMMA_RGB );
232 
233   ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_RESET ], m_active [ m_page ] );
234 
235   update_gamma ( );
236 }
237 
238 /*------------------------------------------------------------*/
change_page(int page)239 void gamma_correction::change_page ( int page )
240 {
241   m_page = page;
242 
243   ::gtk_widget_set_sensitive ( m_widget [ WG_GAMMA_RESET ], m_active [ m_page ] );
244 
245 }
246 
247 
248 /*------------------------------------------------------------*/
reset(int all)249 void gamma_correction::reset ( int all )
250 {
251   if ( all == 1 )
252     {
253       int i;
254 
255       for ( i = 0; i < 4; i++ )
256 	reset_points ( i );
257     }
258   else
259     reset_points ( m_page );
260 
261   calculate_curve ( );
262   update_gamma ( );
263 }
264 
265 /*------------------------------------------------------------*/
event(GtkWidget * widget,GdkEvent * event)266 gint gamma_correction::event ( GtkWidget * widget, GdkEvent * event )
267 {
268   static int cursor_type = PISA_CS_GM_FLEUR;
269   int new_type;
270   GdkEventButton	* bevent;
271   GdkEventMotion	* mevent;
272   gint	tx, ty, x, y;
273   gint	i, distance, closest_point;
274 
275   ::gdk_window_get_pointer ( m_drawarea [ m_page ]->window,
276 			     & tx, & ty, 0 );
277   x = CLAMP ( ( tx - RADIUS ), 0, GAMMA_WIDTH  - 1 );
278   y = CLAMP ( ( ty - RADIUS ), 0, GAMMA_HEIGHT - 1 );
279 
280   distance = G_MAXINT;
281   closest_point = 0;
282 
283   for ( i = 0; i < NUM_POINTS; i++ )
284     {
285       if ( m_points [ m_page ] [ i ] [ 0 ] != -1 )
286 	if ( ::abs ( x - m_points [ m_page ] [ i ] [ 0 ] ) < distance )
287 	  {
288 	    distance = ::abs ( x - m_points [ m_page ] [ i ] [ 0 ] );
289 	    closest_point = i;
290 	  }
291     }
292 
293   if ( distance > MIN_DISTANCE )
294     closest_point = ( x + 8 ) / 16;
295 
296   switch ( event->type )
297     {
298     case GDK_EXPOSE:
299       if ( m_pixmap [ m_page ] == 0 )
300 	m_pixmap [ m_page ] = ::gdk_pixmap_new ( m_drawarea [ m_page ]->window,
301 						 GAMMA_WIDTH + RADIUS * 2,
302 						 GAMMA_HEIGHT + RADIUS * 2,
303 						 -1 );
304       update_gamma ( );
305       break;
306 
307     case GDK_BUTTON_PRESS:
308       bevent = ( GdkEventButton * ) event;
309       new_type = PISA_CS_GM_TCROSS;
310 
311       m_leftmost = -1;
312       for ( i = closest_point - 1; i >= 0; i-- )
313 	if ( m_points [ m_page ] [ i ] [ 0 ] != -1 )
314 	  {
315 	    m_leftmost = m_points [ m_page ] [ i ] [ 0 ];
316 	    break;
317 	  }
318       m_rightmost = GAMMA_WIDTH;
319       for ( i = closest_point + 1; i < NUM_POINTS; i++ )
320 	if ( m_points [ m_page ] [ i ] [ 0 ] != -1 )
321 	  {
322 	    m_rightmost = m_points [ m_page ] [ i ][ 0 ];
323 	    break;
324 	  }
325 
326       m_grabpoint = closest_point;
327       m_points [ m_page ] [ m_grabpoint ] [ 0 ] = x;
328       m_points [ m_page ] [ m_grabpoint ] [ 1 ] = GAMMA_HEIGHT - 1 - y;
329 
330       calculate_curve ( );
331       update_gamma ( );
332       gtk_grab_add ( widget );
333       break;
334 
335     case GDK_BUTTON_RELEASE:
336       new_type = PISA_CS_GM_FLEUR;
337       m_grabpoint = -1;
338       gtk_grab_remove ( widget );
339       ::g_view_manager->update_lut ( );
340       {
341       preview_window *prev_cls =
342 	(preview_window *) ::g_view_manager->get_window_cls (ID_WINDOW_PREV);
343 
344       prev_cls->update_img ( );
345       }
346       break;
347 
348     case GDK_MOTION_NOTIFY:
349       mevent = ( GdkEventMotion * ) event;
350 
351       if ( mevent->is_hint )
352 	{
353 	  mevent->x = tx;
354 	  mevent->y = ty;
355 	}
356 
357       if ( m_grabpoint == -1 )
358 	{
359 	  if ( m_points [ m_page ] [ closest_point ] [ 0 ] != -1 )
360 	    new_type = PISA_CS_GM_FLEUR;
361 	  else
362 	    new_type = PISA_CS_GM_TCROSS;
363 	}
364       else
365 	{
366 	  new_type = PISA_CS_GM_TCROSS;
367 
368 	  m_points [ m_page ] [ m_grabpoint ] [ 0 ] = -1;
369 
370 	  if ( x > m_leftmost && x < m_rightmost )
371 	    {
372 	      closest_point = ( x + 8 ) / 16;
373 	      if ( m_points [ m_page ] [ closest_point ] [ 0 ] == -1 )
374 		m_grabpoint = closest_point;
375 
376 	      m_points [ m_page ] [ m_grabpoint ] [ 0 ] = x;
377 	      m_points [ m_page ] [ m_grabpoint ] [ 1 ] = GAMMA_HEIGHT - 1 - y;
378 	    }
379 	  calculate_curve ( );
380 	  update_gamma ( );
381 	}
382 
383       if ( new_type != cursor_type )
384 	{
385 	  cursor_type = new_type;
386 	  ::gdk_window_set_cursor ( m_drawarea [ m_page ]->window,
387 				    m_cursor [ cursor_type ] );
388 	}
389 
390       break;
391 
392     default:
393       break;
394     }
395 
396   return FALSE;
397 }
398 
399 /*------------------------------------------------------------*/
create_gamma_notebook(GtkWidget * parent)400 GtkWidget * gamma_correction::create_gamma_notebook ( GtkWidget * parent )
401 {
402   GtkWidget	* hbox;
403   GtkWidget	* notebook;
404   GtkWidget	* vbox;
405   GtkWidget	* img;
406   GtkWidget	* gamma_curve;
407   int		i;
408 
409   hbox = ::gtk_hbox_new ( FALSE, 2 );
410 
411   notebook = ::gtk_notebook_new ( );
412   ::gtk_container_border_width ( GTK_CONTAINER ( notebook ), 2 );
413   ::gtk_box_pack_start ( GTK_BOX ( hbox ), notebook, TRUE, FALSE, 0 );
414 
415   for ( i = 0; i < 4; i++ )
416     {
417       vbox = gtk_vbox_new ( FALSE, 5 );
418 
419       img = ::xpm2widget ( parent, g_gamma_xpm [ i ] );
420 
421       m_widget [ i ] = img;
422 
423       ::gtk_notebook_append_page ( GTK_NOTEBOOK ( notebook ), vbox, img );
424       ::gtk_widget_show ( vbox );
425 
426       gamma_curve = create_gamma_curve ( i );
427 
428       m_widget [ i + 4 ] = gamma_curve;
429 
430       ::gtk_box_pack_start ( GTK_BOX ( vbox ), gamma_curve, FALSE, FALSE, 0 );
431       ::gtk_widget_show ( gamma_curve );
432     }
433 
434   ::gtk_signal_connect ( GTK_OBJECT ( notebook ),
435 			 "switch_page",
436 			 GTK_SIGNAL_FUNC ( ::switch_page ),
437 			 this );
438 
439   ::gtk_widget_show ( notebook );
440 
441   m_widget [ WG_GAMMA_NOTE ] = notebook;
442 
443   return hbox;
444 }
445 
446 /*------------------------------------------------------------*/
create_gamma_curve(int i)447 GtkWidget * gamma_correction::create_gamma_curve ( int i )
448 {
449   GtkWidget	* frame;
450   GtkWidget	* gamma_curve;
451 
452   frame = ::gtk_frame_new ( 0 );
453   ::gtk_frame_set_shadow_type ( GTK_FRAME ( frame ), GTK_SHADOW_ETCHED_IN );
454 
455   gamma_curve = ::gtk_drawing_area_new ( );
456   ::gtk_drawing_area_size ( GTK_DRAWING_AREA ( gamma_curve ),
457 			    GAMMA_WIDTH + RADIUS * 2,
458 			    GAMMA_HEIGHT + RADIUS * 2 );
459 
460   ::gtk_widget_set_events ( gamma_curve,
461 			    GDK_EXPOSURE_MASK |
462 			    GDK_POINTER_MOTION_MASK |
463 			    GDK_POINTER_MOTION_HINT_MASK |
464 			    GDK_ENTER_NOTIFY_MASK |
465 			    GDK_BUTTON_PRESS_MASK |
466 			    GDK_BUTTON_RELEASE_MASK |
467 			    GDK_BUTTON1_MOTION_MASK );
468 
469   m_drawarea [ i ] = gamma_curve;
470 
471   ::gtk_signal_connect ( GTK_OBJECT ( gamma_curve ), "event",
472 			 GTK_SIGNAL_FUNC ( gamma_curve_event ), this );
473 
474 
475   ::gtk_container_add ( GTK_CONTAINER ( frame ), gamma_curve );
476 
477   ::gtk_widget_show ( gamma_curve );
478 
479   return frame;
480 }
481 
482 /*------------------------------------------------------------*/
create_reset_button(void)483 GtkWidget * gamma_correction::create_reset_button ( void )
484 {
485   GtkWidget	* hbox;
486   GtkWidget	* button;
487 
488   hbox = ::gtk_hbox_new ( FALSE, 5 );
489   ::gtk_container_border_width ( GTK_CONTAINER ( hbox ), 5 );
490 
491   button = ::gtk_button_new_with_label ( _( "  Reset  " ) );
492   ::gtk_box_pack_start ( GTK_BOX ( hbox ), button, TRUE, FALSE, 0 );
493   ::gtk_signal_connect ( GTK_OBJECT ( button ), "clicked",
494 			 GTK_SIGNAL_FUNC ( ::click_reset ), this );
495   ::gtk_widget_show ( button );
496 
497   m_widget [ WG_GAMMA_RESET ] = button;
498 
499   return hbox;
500 }
501 
502 /*------------------------------------------------------------*/
reset_points(int i)503 void gamma_correction::reset_points ( int i )
504 {
505   int j;
506 
507   m_grabpoint = -1;
508 
509   for ( j = 0; j < NUM_POINTS; j++ )
510     {
511       m_points [ i ] [ j ] [ 0 ] = -1;
512       m_points [ i ] [ j ] [ 1 ] = -1;
513     }
514 
515   for ( j = 0; j < GAMMA_WIDTH; j++ )
516     m_curve [ i ] [ j ] = j;
517 
518   m_points [ i ] [ 0 ] [ 0 ] = 0;
519   m_points [ i ] [ 0 ] [ 1 ] = 0;
520   m_points [ i ] [ NUM_POINTS - 1 ] [ 0 ] = GAMMA_WIDTH - 1;
521   m_points [ i ] [ NUM_POINTS - 1 ] [ 1 ] = GAMMA_HEIGHT - 1;
522 }
523 
524 /*------------------------------------------------------------*/
update_gamma(void)525 void gamma_correction::update_gamma ( void )
526 {
527   int i;
528   GdkPoint points [ GAMMA_WIDTH ];
529   GdkGC		* gc;
530 
531   if ( m_pixmap [ m_page ] == 0 )
532     return;
533 
534   // clear the pixmap
535   ::gdk_draw_rectangle ( m_pixmap [ m_page ],
536 			 m_drawarea [ m_page ]->style->bg_gc [ GTK_STATE_NORMAL ],
537 			 TRUE, 0, 0,
538 			 GAMMA_WIDTH + RADIUS * 2, GAMMA_HEIGHT + RADIUS * 2 );
539 
540   // draw the grid line
541   for ( i = 0; i < 13; i++ )
542     {
543       ::gdk_draw_line ( m_pixmap [ m_page ],
544 			m_drawarea [ m_page ]->style->dark_gc [ GTK_STATE_NORMAL ],
545 			RADIUS, i * ( GAMMA_HEIGHT / 12 ) + RADIUS,
546 			GAMMA_WIDTH + RADIUS, i * ( GAMMA_HEIGHT / 12 ) + RADIUS );
547       ::gdk_draw_line ( m_pixmap [ m_page ],
548 			m_drawarea [ m_page ]->style->dark_gc [ GTK_STATE_NORMAL ],
549 			i * ( GAMMA_WIDTH / 12 ) + RADIUS, RADIUS,
550 			i * ( GAMMA_WIDTH / 12 ) + RADIUS, GAMMA_HEIGHT + RADIUS );
551     }
552 
553   if ( m_active [ m_page ] )
554     gc = m_drawarea [ m_page ]->style->black_gc;
555   else
556     gc = m_drawarea [ m_page ]->style->dark_gc [ GTK_STATE_NORMAL ];
557 
558   // draw the curve
559   for ( i = 0; i < GAMMA_WIDTH; i++ )
560     {
561       points [ i ].x = i + RADIUS;
562       points [ i ].y = ( GAMMA_HEIGHT  ) - m_curve [ m_page ] [ i ] + RADIUS;
563     }
564 
565   ::gdk_draw_points ( m_pixmap [ m_page ],
566 		      gc, points, GAMMA_WIDTH );
567 
568   // draw the points
569   for ( i = 0; i < NUM_POINTS; i++ )
570     {
571       if ( m_points [ m_page ] [ i ] [ 0 ] != -1 )
572 	::gdk_draw_arc ( m_pixmap [ m_page ],
573 			 gc,
574 			 TRUE,
575 			 m_points [ m_page ] [ i ] [ 0 ],
576 			 ( GAMMA_HEIGHT - 1 ) - m_points [ m_page ] [ i ] [ 1 ],
577 			 RADIUS * 2, RADIUS * 2, 0, 23040 );
578     }
579 
580   ::gdk_draw_pixmap ( m_drawarea [ m_page ]->window,
581 		      m_drawarea [ m_page ]->style->black_gc,
582 		      m_pixmap [ m_page ],
583 		      0, 0, 0, 0, GAMMA_WIDTH + RADIUS * 2,
584 		      GAMMA_HEIGHT + RADIUS * 2 );
585   return;
586 }
587 
588 /*------------------------------------------------------------*/
calculate_curve(void)589 void gamma_correction::calculate_curve ( void )
590 {
591   int i, num;
592   int p1, p2, p3, p4;
593   int points [ NUM_POINTS ];
594   marquee * marq;
595 
596   num = 0;
597 
598   for ( i = 0; i < NUM_POINTS; i++ )
599     if ( m_points [ m_page ] [ i ] [ 0 ] != -1 )
600       points [ num++ ] = i;
601 
602   if ( 0 < num )
603     {
604       for ( i = 0; i < m_points [ m_page ] [ points [ 0 ] ] [ 0 ]; i++ )
605 	{
606 	  m_curve [ m_page ] [ i ] =
607 	    m_points [ m_page ] [ points [ 0 ] ] [ 1 ];
608 	}
609 
610       for ( i = m_points [ m_page ] [ points [ num - 1 ] ] [ 0 ]; i < GAMMA_WIDTH; i++ )
611 	{
612 	  m_curve [ m_page ] [ i ] =
613 	    m_points [ m_page ] [ points [ num - 1 ] ] [ 1 ];
614 	}
615     }
616 
617   for ( i = 0; i < num - 1; i++ )
618     {
619       p1 = ( i == 0 ) ? points [ i ] : points [ ( i - 1 ) ];
620       p2 = points [ i ];
621       p3 = points [ ( i + 1 ) ];
622       p4 = ( i == ( num - 2 ) ) ? points [ ( num - 1 ) ] : points [ ( i + 2 ) ];
623 
624       plot_curve ( p1, p2, p3, p4 );
625     }
626 
627   marq = & g_view_manager->get_marquee ();
628 
629   for ( i = 0; i < 256; i++ )
630     marq->gamma_table [ m_page ] [ i ] =
631       255 * ( m_curve [ m_page ] [ 191 * i / 255 ] ) / ( GAMMA_WIDTH - 1 );
632 }
633 
634 /*------------------------------------------------------------*/
plot_curve(int p1,int p2,int p3,int p4)635 void gamma_correction::plot_curve ( int p1, int p2, int p3, int p4 )
636 {
637   matrix geometry;
638   matrix tmp1, tmp2;
639   matrix deltas;
640   double x, dx, dx2, dx3;
641   double y, dy, dy2, dy3;
642   double d, d2, d3;
643   int lastx, lasty;
644   int newx, newy;
645   int i;
646 
647   for ( i = 0; i < 4; i++ )
648     {
649       geometry [ i ] [ 0 ] = 0;
650       geometry [ i ] [ 1 ] = 0;
651       geometry [ i ] [ 2 ] = 0;
652       geometry [ i ] [ 3 ] = 0;
653     }
654 
655   for ( i = 0; i < 2; i++ )
656     {
657       geometry [ 0 ] [ i ] = m_points [ m_page ] [ p1 ] [ i ];
658       geometry [ 1 ] [ i ] = m_points [ m_page ] [ p2 ] [ i ];
659       geometry [ 2 ] [ i ] = m_points [ m_page ] [ p3 ] [ i ];
660       geometry [ 3 ] [ i ] = m_points [ m_page ] [ p4 ] [ i ];
661     }
662 
663   d = 1.0 / 1000;
664   d2 = d * d;
665   d3 = d * d * d;
666 
667   tmp2 [ 0 ] [ 0 ] = 0;      tmp2 [ 0 ] [ 1 ] = 0;      tmp2 [ 0 ] [ 2 ] = 0; tmp2 [ 0 ] [ 3 ] = 1;
668   tmp2 [ 1 ] [ 0 ] = d3;     tmp2 [ 1 ] [ 1 ] = d2;     tmp2 [ 1 ] [ 2 ] = d; tmp2 [ 1 ] [ 3 ] = 0;
669   tmp2 [ 2 ] [ 0 ] = 6 * d3; tmp2 [ 2 ] [ 1 ] = 2 * d2; tmp2 [ 2 ] [ 2 ] = 0; tmp2 [ 2 ] [ 3 ] = 0;
670   tmp2 [ 3 ] [ 0 ] = 6 * d3; tmp2 [ 3 ] [ 1 ] = 0;      tmp2 [ 3 ] [ 2 ] = 0; tmp2 [ 3 ] [ 3 ] = 0;
671 
672   compose_curve ( m_basis, geometry, tmp1 );
673 
674   compose_curve ( tmp2, tmp1, deltas );
675 
676   x	= deltas [ 0 ] [ 0 ];
677   dx	= deltas [ 1 ] [ 0 ];
678   dx2	= deltas [ 2 ] [ 0 ];
679   dx3	= deltas [ 3 ] [ 0 ];
680 
681   y	= deltas [ 0 ] [ 1 ];
682   dy	= deltas [ 1 ] [ 1 ];
683   dy2	= deltas [ 2 ] [ 1 ];
684   dy3	= deltas [ 3 ] [ 1 ];
685 
686   lastx = ( int ) ( CLAMP ( x, 0, GAMMA_WIDTH - 1 ) );
687   lasty = ( int ) ( CLAMP ( y, 0, GAMMA_HEIGHT - 1 ));
688 
689   m_curve [ m_page ] [ lastx ] = lasty;
690 
691   for ( i = 0; i < 1000; i++ )
692     {
693       x += dx;
694       dx += dx2;
695       dx2 += dx3;
696 
697       y += dy;
698       dy += dy2;
699       dy2 += dy3;
700 
701       newx = CLAMP ( int ( x + 0.5 ), 0, GAMMA_WIDTH - 1 );
702       newy = CLAMP ( int ( y + 0.5 ), 0, GAMMA_HEIGHT - 1 );
703 
704       if ( ( lastx != newx ) || ( lasty != newy ) )
705 	m_curve [ m_page ] [ newx ] = newy;
706 
707       lastx = newx;
708       lasty = newy;
709     }
710 }
711 
712 /*------------------------------------------------------------*/
compose_curve(matrix a,matrix b,matrix ab)713 void gamma_correction::compose_curve ( matrix a, matrix b, matrix ab )
714 {
715   int i, j;
716 
717   for ( i = 0; i < 4; i++ )
718     {
719       for ( j = 0; j < 4; j++ )
720 	{
721 	  ab [ i ] [ j ] = ( a [ i ] [ 0 ] * b [ 0 ] [ j ] +
722 			     a [ i ] [ 1 ] * b [ 1 ] [ j ] +
723 			     a [ i ] [ 2 ] * b [ 2 ] [ j ] +
724 			     a [ i ] [ 3 ] * b [ 3 ] [ j ] );
725 	}
726     }
727 }
728