1 /* Manage display conversion parameters.
2 */
3
4 /*
5
6 Copyright (C) 1991-2003 The National Gallery
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with this program; if not, write to the Free Software Foundation, Inc.,
20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22 */
23
24 /*
25
26 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
27
28 */
29
30 /*
31 #define DEBUG
32 */
33
34 #include "ip.h"
35
36 /* Our signals.
37 */
38 enum {
39 SIG_AREA_CHANGED, /* Area of repaint image has changed */
40 SIG_IMAGEINFO_CHANGED, /* The imageinfo we hold has been replaced */
41 SIG_LAST
42 };
43
44 static guint conversion_signals[SIG_LAST] = { 0 };
45
46 static ModelClass *parent_class = NULL;
47
48 /* All active conversions.
49 */
50 static GSList *conversion_all = NULL;
51
52 static void *
conversion_imageinfo_changed(Conversion * conv)53 conversion_imageinfo_changed( Conversion *conv )
54 {
55 #ifdef DEBUG
56 g_print( "conversion_imageinfo_changed: " );
57 iobject_print( IOBJECT( conv ) );
58 #endif /*DEBUG*/
59
60 g_signal_emit( G_OBJECT( conv ),
61 conversion_signals[SIG_IMAGEINFO_CHANGED], 0 );
62
63 return( NULL );
64 }
65
66 static void
conversion_area_changed(Conversion * conv,Rect * dirty)67 conversion_area_changed( Conversion *conv, Rect *dirty )
68 {
69 g_signal_emit( G_OBJECT( conv ),
70 conversion_signals[SIG_AREA_CHANGED], 0, dirty );
71 }
72
73 static void
conversion_dispose(GObject * gobject)74 conversion_dispose( GObject *gobject )
75 {
76 Conversion *conv;
77
78 g_return_if_fail( gobject != NULL );
79 g_return_if_fail( IS_CONVERSION( gobject ) );
80
81 conv = CONVERSION( gobject );
82
83 #ifdef DEBUG
84 g_print( "conversion_dispose: " );
85 iobject_print( IOBJECT( conv ) );
86 #endif /*DEBUG*/
87
88 FREESID( conv->changed_sid, conv->ii );
89 FREESID( conv->area_changed_sid, conv->ii );
90
91 G_OBJECT_CLASS( parent_class )->dispose( gobject );
92 }
93
94 static void
conversion_finalize(GObject * gobject)95 conversion_finalize( GObject *gobject )
96 {
97 Conversion *conv;
98
99 g_return_if_fail( gobject != NULL );
100 g_return_if_fail( IS_CONVERSION( gobject ) );
101
102 conv = CONVERSION( gobject );
103
104 #ifdef DEBUG
105 g_print( "conversion_finalize: " );
106 iobject_print( IOBJECT( conv ) );
107 #endif /*DEBUG*/
108
109 conversion_all = g_slist_remove( conversion_all, conv );
110
111 IM_FREEF( im_region_free, conv->ireg );
112 IM_FREEF( im_region_free, conv->mreg );
113 IM_FREEF( im_region_free, conv->reg );
114
115 MANAGED_UNREF( conv->repaint_ii );
116 MANAGED_UNREF( conv->display_ii );
117 MANAGED_UNREF( conv->visual_ii );
118 MANAGED_UNREF( conv->ii );
119
120 G_OBJECT_CLASS( parent_class )->finalize( gobject );
121 }
122
123 /* Make the visualisation image ... eg. we im_histplot histograms, and we
124 * log scale fourier images.
125 */
126 static IMAGE *
conversion_make_visualise(Conversion * conv,IMAGE * in)127 conversion_make_visualise( Conversion *conv, IMAGE *in )
128 {
129 IMAGE *out = im_open( "conversion_make_visualise", "p" );
130 int tconv = !(conv && conv->enabled && !conv->type);
131
132 /* Histogram type ... plot the histogram. Keep this old hist display
133 * method in case the goffice plotter is not available.
134 */
135 if( tconv && in->Type == IM_TYPE_HISTOGRAM &&
136 (in->Xsize == 1 || in->Ysize == 1) ) {
137 IMAGE *t[3];
138
139 if( in->Coding == IM_CODING_LABQ ) {
140 IMAGE *t = im_open_local( out, "conv:1", "p" );
141
142 if( !t || im_LabQ2Lab( in, t ) ) {
143 im_close( out );
144 return( NULL );
145 }
146
147 in = t;
148 }
149
150 if( in->Coding == IM_CODING_RAD ) {
151 IMAGE *t = im_open_local( out, "conv:1", "p" );
152
153 if( !t || im_rad2float( in, t ) ) {
154 im_close( out );
155 return( NULL );
156 }
157
158 in = t;
159 }
160
161 if( im_open_local_array( out, t, 3, "conv-1", "p" ) ||
162 im_histnorm( in, t[0] ) ||
163 im_histplot( t[0], t[1] ) ) {
164 im_close( out );
165 return( NULL );
166 }
167
168 /* Scale to a sensible size ... aim for a height of 256
169 * elements.
170 */
171 if( in->Xsize == 1 && t[1]->Xsize > 256 ) {
172 if( im_subsample( t[1], t[2], t[1]->Xsize / 256, 1 ) ) {
173 im_close( out );
174 return( NULL );
175 }
176 }
177 else if( in->Ysize == 1 && t[1]->Ysize > 256 ) {
178 if( im_subsample( t[1], t[2], 1, t[1]->Ysize / 256 ) ) {
179 im_close( out );
180 return( NULL );
181 }
182 }
183 else
184 t[2] = t[1];
185
186 in = t[2];
187 }
188
189 /* IM_TYPE_FOURIER type ... pow/log scale, good for fourier
190 * transforms.
191 */
192 if( tconv && in->Type == IM_TYPE_FOURIER ) {
193 IMAGE *t[2];
194
195 if( im_open_local_array( out, t, 2, "conv-1", "p" ) ||
196 im_abs( in, t[0] ) ||
197 im_scaleps( t[0], t[1] ) ) {
198 im_close( out );
199 return( NULL );
200 }
201
202 in = t[1];
203 }
204
205 if( im_copy( in, out ) ) {
206 im_close( out );
207 return( NULL );
208 }
209
210 return( out );
211 }
212
213 /* What we send from the notify callback to the main GUI thread.
214 */
215 typedef struct _ConversionUpdate {
216 Conversion *conv;
217
218 IMAGE *im;
219 Rect area;
220 } ConversionUpdate;
221
222 static gboolean
conversion_render_idle_cb(gpointer data)223 conversion_render_idle_cb( gpointer data )
224 {
225 ConversionUpdate *update = (ConversionUpdate *) data;
226 Conversion *conv = update->conv;
227
228 /* Must be a valid conversion, must be for the image that that
229 * conversion is still using for display.
230 */
231 if( g_slist_find( conversion_all, conv ) &&
232 imageinfo_get( FALSE, conv->display_ii ) == update->im ) {
233 #ifdef DEBUG
234 g_print( "conversion_update_dispatch: left = %d, top = %d, "
235 "width = %d, height = %d\n",
236 update->area.left, update->area.top,
237 update->area.width, update->area.height );
238 #endif /*DEBUG*/
239
240 /* We need to invalid the main image too, since those
241 * regions will have black in from the failed first calc.
242 *
243 * im_render() can't do this invalidate for us,
244 * it needs to be done from the main loop.
245 *
246 * commented out, vips_sink_screen() now does this for us.
247 *
248 im_invalidate( conv->mask ); im_invalidate( imageinfo_get( FALSE, conv->display_ii ) );
249 */
250
251 conversion_area_changed( conv, &update->area );
252 }
253 #ifdef DEBUG
254 else
255 g_print( "conversion_render_idle_cb: skipping dead update\n" );
256 #endif /*DEBUG*/
257
258 g_free( update );
259
260 return( FALSE );
261 }
262
263 /* Here from the im_render() background thread.
264 */
265 static void
conversion_render_notify_cb(IMAGE * im,Rect * area,void * client)266 conversion_render_notify_cb( IMAGE *im, Rect *area, void *client )
267 {
268 ConversionUpdate *update = g_new( ConversionUpdate, 1 );
269
270 /* Can't use CONVERSION() in this thread ... the GUI thread will check
271 * this pointer for us when it reads from the queue.
272 */
273 update->conv = (Conversion *) client;
274 update->im = im;
275 update->area = *area;
276
277 g_idle_add( conversion_render_idle_cb, update );
278 }
279
280 /* How many tiles should we ask for? A bit more than the number needed to
281 * paint the screen.
282 */
283 static int
conversion_get_default_tiles(Conversion * conv)284 conversion_get_default_tiles( Conversion *conv )
285 {
286 GdkScreen *screen = gdk_screen_get_default();
287 int width = gdk_screen_get_width( screen ) / conv->tile_size;
288 int height = gdk_screen_get_height( screen ) / conv->tile_size;
289
290 return( 2 * width * height );
291 }
292
293 /* Resize to screen coordinates and cache it.
294 */
295 static IMAGE *
conversion_make_display(Conversion * conv,IMAGE * in,IMAGE ** mask_out)296 conversion_make_display( Conversion *conv, IMAGE *in, IMAGE **mask_out )
297 {
298 IMAGE *out = im_open( "conversion_display:1", "p" );
299
300 if( !out )
301 return( NULL );
302
303 if( conv->mag < 0 ) {
304 /* Ordinary image ... use im_subsample().
305
306 FIXME ... look for pyramid TIFFs here
307
308 */
309 IMAGE *t = im_open_local( out, "conv:s", "p" );
310
311 /* Don't shrink by more than the image size (ie. to less than
312 * 1 pixel).
313 */
314 int xshrink = IM_MIN( -conv->mag, in->Xsize );
315 int yshrink = IM_MIN( -conv->mag, in->Ysize );
316
317 if( DISPLAY_THUMBNAIL_HQ ) {
318 if( !t || im_shrink( in, t, xshrink, yshrink ) ) {
319 im_close( out );
320 return( NULL );
321 }
322 }
323 else {
324 if( !t || im_subsample( in, t, xshrink, yshrink ) ) {
325 im_close( out );
326 return( NULL );
327 }
328 }
329
330 in = t;
331 }
332
333 /* Zoom, if necessary.
334 */
335 if( conv->mag > 1 ) {
336 IMAGE *t = im_open_local( out, "conv:z", "p" );
337
338 if( !t || im_zoom( in, t, conv->mag, conv->mag ) ) {
339 im_close( out );
340 return( NULL );
341 }
342
343 in = t;
344 }
345
346 /* Cache it.
347 */
348 if( conv->synchronous ) {
349 if( im_copy( in, out ) ) {
350 im_close( out );
351 return( NULL );
352 }
353 }
354 else {
355 IMAGE *mask = im_open_local( out, "conv:r", "p" );
356
357 if( im_render_priority( in, out, mask,
358 conv->tile_size, conv->tile_size,
359 conversion_get_default_tiles( conv ),
360 conv->priority,
361 conversion_render_notify_cb, conv ) ) {
362 im_close( out );
363 return( NULL );
364 }
365
366 if( mask_out )
367 *mask_out = mask;
368 }
369
370 return( out );
371 }
372
373 /* Track during lintrauc.
374 */
375 typedef struct {
376 double a, b;
377 IMAGE *in, *out;
378 } LintraInfo;
379
380 /* Define what we do for each band element type. Non-complex input, uchar
381 * output.
382 */
383 #define LOOP(IN) { \
384 IN *p = (IN *) in; \
385 PEL *q = (PEL *) out; \
386 \
387 for( x = 0; x < sz; x++ ) { \
388 double t; \
389 \
390 t = a * p[x] + b; \
391 \
392 if( t > 255 ) \
393 t = 255; \
394 else if( t < 0 ) \
395 t = 0; \
396 \
397 q[x] = t; \
398 } \
399 }
400
401 /* Complex input, uchar output.
402 */
403 #define LOOPCMPLX(IN) { \
404 IN *p = (IN *) in; \
405 PEL *q = (PEL *) out; \
406 \
407 for( x = 0; x < sz; x++ ) { \
408 double t; \
409 \
410 t = a * p[x << 1] + b; \
411 \
412 if( t > 255 ) \
413 t = 255; \
414 else if( t < 0 ) \
415 t = 0; \
416 \
417 q[x] = t; \
418 } \
419 }
420
421 /* Lintra a buffer, 1 set of scale/offset.
422 */
423 static int
lintrauc_gen(PEL * in,PEL * out,int width,IMAGE * im,LintraInfo * inf)424 lintrauc_gen( PEL *in, PEL *out, int width, IMAGE *im, LintraInfo *inf )
425 {
426 double a = inf->a;
427 double b = inf->b;
428 int sz = width * im->Bands;
429 int x;
430
431 /* Lintra all input types.
432 */
433 switch( im->BandFmt ) {
434 case IM_BANDFMT_UCHAR:
435 LOOP( unsigned char ); break;
436 case IM_BANDFMT_CHAR:
437 LOOP( signed char ); break;
438 case IM_BANDFMT_USHORT:
439 LOOP( unsigned short ); break;
440 case IM_BANDFMT_SHORT:
441 LOOP( signed short ); break;
442 case IM_BANDFMT_UINT:
443 LOOP( unsigned int ); break;
444 case IM_BANDFMT_INT:
445 LOOP( signed int ); break;
446 case IM_BANDFMT_FLOAT:
447 LOOP( float ); break;
448 case IM_BANDFMT_DOUBLE:
449 LOOP( double ); break;
450 case IM_BANDFMT_COMPLEX:
451 LOOPCMPLX( float ); break;
452 case IM_BANDFMT_DPCOMPLEX:
453 LOOPCMPLX( double ); break;
454
455 default:
456 g_assert( 0 );
457 }
458
459 return( 0 );
460 }
461
462 /* im_lintra() that writes uchar (the VIPS one writes float/double).
463 */
464 static int
im_lintrauc(double a,IMAGE * in,double b,IMAGE * out)465 im_lintrauc( double a, IMAGE *in, double b, IMAGE *out )
466 {
467 LintraInfo *inf;
468
469 if( in->Coding != IM_CODING_NONE ) {
470 im_error( "im_lintrauc", _( "not uncoded" ) );
471 return( -1 );
472 }
473
474 if( im_cp_desc( out, in ) )
475 return( -1 );
476 out->Bbits = IM_BBITS_BYTE;
477 out->BandFmt = IM_BANDFMT_UCHAR;
478
479 if( !(inf = IM_NEW( out, LintraInfo )) )
480 return( -1 );
481 inf->a = a;
482 inf->b = b;
483 inf->in = in;
484 inf->out = out;
485
486 if( im_wrapone( in, out,
487 (im_wrapone_fn) lintrauc_gen, in, inf ) )
488 return( -1 );
489
490 return( 0 );
491 }
492
493 /* Turn any IMAGE into a 1/3 band IM_BANDFMT_UCHAR ready for gdk_rgb_*().
494 */
495 static IMAGE *
conversion_make_repaint(Conversion * conv,IMAGE * in)496 conversion_make_repaint( Conversion *conv, IMAGE *in )
497 {
498 IMAGE *out = im_open( "conversion_apply:1", "p" );
499
500 /* 7 is sRGB.
501
502 this is all deprecated and unused now with vips-7.31 and later
503 tag as unused to stop gcc complaints
504
505 */
506 struct im_col_display *display __attribute__ ((unused)) =
507 im_col_displays( 7 );
508
509 /* Do we do colorimetric type conversions? Look for
510 * interpret-type-toggle.
511 */
512 int tconv = !(conv && conv->enabled && !conv->type);
513
514 if( !out )
515 return( NULL );
516
517 /* Special case: if this is a IM_CODING_LABQ and the display control
518 * bar is turned off, we can go straight to RGB for speed.
519 */
520 if( in->Coding == IM_CODING_LABQ && !(conv && conv->enabled) ) {
521 IMAGE *t = im_open_local( out, "conv:1", "p" );
522 static void *table = NULL;
523
524 /* Make sure fast LabQ2disp tables are built.
525 */
526 if( !table )
527 table = im_LabQ2disp_build_table( NULL, display );
528
529 if( !t || im_LabQ2disp_table( in, t, table ) ) {
530 im_close( out );
531 return( NULL );
532 }
533
534 in = t;
535 }
536
537 /* Get the bands right. If we have >3, drop down to 3. If we have 2,
538 * drop down to 1.
539 */
540 if( in->Coding == IM_CODING_NONE ) {
541 if( in->Bands == 2 ) {
542 IMAGE *t = im_open_local( out, "conv:1", "p" );
543
544 if( !t || im_extract_band( in, t, 0 ) ) {
545 im_close( out );
546 return( NULL );
547 }
548
549 in = t;
550 }
551 else if( in->Bands > 3 ) {
552 IMAGE *t = im_open_local( out, "conv:1", "p" );
553
554 if( !t ||
555 im_extract_bands( in, t, 0, 3 ) ) {
556 im_close( out );
557 return( NULL );
558 }
559
560 in = t;
561 }
562 }
563
564 /* Interpret the Type field for colorimetric images.
565 */
566 if( tconv &&
567 in->Bands == 3 && in->BandFmt == IM_BANDFMT_SHORT &&
568 in->Type == IM_TYPE_LABS ) {
569 IMAGE *t = im_open_local( out, "conv:1", "p" );
570
571 if( !t || im_LabS2LabQ( in, t ) ) {
572 im_close( out );
573 return( NULL );
574 }
575
576 in = t;
577 }
578
579 if( in->Coding == IM_CODING_LABQ ) {
580 IMAGE *t = im_open_local( out, "conv:1", "p" );
581
582 if( !t || im_LabQ2Lab( in, t ) ) {
583 im_close( out );
584 return( NULL );
585 }
586
587 in = t;
588 }
589
590 if( in->Coding == IM_CODING_RAD ) {
591 IMAGE *t = im_open_local( out, "conv:1", "p" );
592
593 if( !t || im_rad2float( in, t ) ) {
594 im_close( out );
595 return( NULL );
596 }
597
598 in = t;
599 }
600
601 if( in->Coding != IM_CODING_NONE ) {
602 im_close( out );
603 return( NULL );
604 }
605
606 /* One of the colorimetric types?
607 */
608 if( tconv && in->Bands == 3 &&
609 (in->Type == IM_TYPE_LCH ||
610 in->Type == IM_TYPE_YXY ||
611 in->Type == IM_TYPE_UCS ||
612 #if VIPS_MAJOR_VERSION > 7 || VIPS_MINOR_VERSION > 32
613 /* scRGB colourspace added in 7.32.
614 */
615 in->Type == VIPS_INTERPRETATION_scRGB ||
616 #endif
617 in->Type == IM_TYPE_LAB ||
618 in->Type == IM_TYPE_XYZ) ) {
619 IMAGE *t[2];
620
621 /* We need to scale/offset before we go to 8 bit to work well
622 * with HDR.
623 */
624 if( conv && conv->enabled &&
625 (conv->scale != 1.0 || conv->offset != 0.0) ) {
626 IMAGE *t = im_open_local( out, "conv:1", "p" );
627
628 if( !t || im_lintra( conv->scale, in,
629 conv->offset, t ) ) {
630 im_close( out );
631 return( NULL );
632 }
633
634 in = t;
635 }
636
637 if( in->Type == IM_TYPE_LCH ) {
638 if( im_open_local_array( out, t, 2, "conv-1", "p" ) ||
639 im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) ||
640 im_LCh2Lab( t[0], t[1] ) ) {
641 im_close( out );
642 return( NULL );
643 }
644
645 in = t[1];
646 }
647
648 #if VIPS_MAJOR_VERSION > 7 || VIPS_MINOR_VERSION > 32
649 if( in->Type == VIPS_INTERPRETATION_scRGB ) {
650 VipsImage *x;
651
652 if( vips_scRGB2sRGB( in, &x, NULL ) ) {
653 im_close( out );
654 return( NULL );
655 }
656 vips_object_local( out, x );
657
658 in = x;
659 }
660 #endif
661
662 if( in->Type == IM_TYPE_YXY ) {
663 if( im_open_local_array( out, t, 2, "conv-1", "p" ) ||
664 im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) ||
665 im_Yxy2XYZ( t[0], t[1] ) ) {
666 im_close( out );
667 return( NULL );
668 }
669
670 in = t[1];
671 }
672
673 if( in->Type == IM_TYPE_UCS ) {
674 if( im_open_local_array( out, t, 2, "conv-1", "p" ) ||
675 im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) ||
676 im_UCS2XYZ( t[0], t[1] ) ) {
677 im_close( out );
678 return( NULL );
679 }
680
681 in = t[1];
682 }
683
684 if( in->Type == IM_TYPE_LAB ) {
685 if( im_open_local_array( out, t, 2, "conv-1", "p" ) ||
686 im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) ||
687 im_Lab2XYZ( t[0], t[1] ) ) {
688 im_close( out );
689 return( NULL );
690 }
691
692 in = t[1];
693 }
694
695 if( in->Type == IM_TYPE_XYZ ) {
696 if( im_open_local_array( out, t, 2, "conv-1", "p" ) ||
697 im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) ||
698 im_XYZ2disp( t[0], t[1], display ) ) {
699 im_close( out );
700 return( NULL );
701 }
702
703 in = t[1];
704 }
705 }
706 else {
707 /* Not colorimetric. We can use a special ->uchar lintra for
708 * scale/offset. Don't scale/offset fourier or histogram
709 * images, they are presented above.
710 */
711 if( conv && conv->enabled &&
712 (!tconv || in->Type != IM_TYPE_FOURIER) &&
713 (!tconv || in->Type != IM_TYPE_HISTOGRAM) &&
714 (conv->scale != 1.0 || conv->offset != 0.0) ) {
715 IMAGE *t = im_open_local( out, "conv:1", "p" );
716
717 if( !t ||
718 im_lintrauc( conv->scale, in,
719 conv->offset, t ) ) {
720 im_close( out );
721 return( NULL );
722 }
723
724 in = t;
725 }
726 }
727
728 if( tconv &&
729 (in->Type == IM_TYPE_RGB16 || in->Type == IM_TYPE_GREY16) ) {
730 IMAGE *t[1];
731
732 if( im_open_local_array( out, t, 1, "conv-1", "p" ) ) {
733 im_close( out );
734 return( NULL );
735 }
736
737 /* im_msb() only works for the int formats.
738 */
739 if( vips_bandfmt_isint( in->BandFmt ) ) {
740 if( im_msb( in, t[0] ) ) {
741 im_close( out );
742 return( NULL );
743 }
744 }
745 else {
746 if( im_lintrauc( 1 / 256.0, in, 0.0, t[0] ) ) {
747 im_close( out );
748 return( NULL );
749 }
750 }
751
752 in = t[0];
753 }
754
755 /* Clip to uchar if not there already.
756 */
757 if( in->BandFmt != IM_BANDFMT_UCHAR ) {
758 IMAGE *t = im_open_local( out, "conv:1", "p" );
759
760 if( !t || im_clip2fmt( in, t, IM_BANDFMT_UCHAR ) ) {
761 im_close( out );
762 return( NULL );
763 }
764
765 in = t;
766 }
767
768 /* Falsecolour. Just use the green channel if we're RGB.
769 */
770 if( conv && conv->enabled && conv->falsecolour ) {
771 IMAGE *t1 = im_open_local( out, "conv:1", "p" );
772
773 if( !t1 ) {
774 im_close( out );
775 return( NULL );
776 }
777
778 if( in->Bands == 3 ) {
779 IMAGE *t2 = im_open_local( out, "conv:1", "p" );
780
781 if( im_extract_band( in, t2, 1 ) ) {
782 im_close( out );
783 return( NULL );
784 }
785
786 in = t2;
787 }
788
789 if( im_falsecolour( in, t1 ) ) {
790 im_close( out );
791 return( NULL );
792 }
793
794 in = t1;
795 }
796
797 if( im_copy( in, out ) ) {
798 im_close( out );
799 return( NULL );
800 }
801
802 return( out );
803 }
804
805 /* Controls in the display conversion bar, or the display image have changed.
806 * Remake the repaint image.
807 */
808 static void
conversion_rebuild_repaint(Conversion * conv)809 conversion_rebuild_repaint( Conversion *conv )
810 {
811 IMAGE *display_im;
812 IMAGE *new_repaint_im;
813 Imageinfo *new_repaint_ii;
814 REGION *new_ireg;
815
816 #ifdef DEBUG
817 g_print( "conversion_remake_repaint: %p\n", conv );
818 #endif /*DEBUG*/
819
820 if( conv->display_ii )
821 display_im = imageinfo_get( FALSE, conv->display_ii );
822 else
823 display_im = NULL;
824
825 /* Keep gcc quiet about annoying possible-used-before-set warnings.
826 */
827 new_repaint_ii = NULL;
828 new_ireg = NULL;
829
830 /* Make the new stuff first.
831 */
832 if( display_im ) {
833 if( !(new_repaint_im =
834 conversion_make_repaint( conv, display_im )) )
835 return;
836 if( !(new_repaint_ii = imageinfo_new( main_imageinfogroup,
837 NULL, new_repaint_im, NULL )) ) {
838 im_close( new_repaint_im );
839 return;
840 }
841 managed_sub_add( MANAGED( new_repaint_ii ),
842 MANAGED( conv->display_ii ) );
843
844 if( !(new_ireg = im_region_create( new_repaint_im )) )
845 return;
846 }
847
848 IM_FREEF( im_region_free, conv->ireg );
849 MANAGED_UNREF( conv->repaint_ii );
850
851 if( display_im ) {
852 conv->repaint_ii = new_repaint_ii;
853 MANAGED_REF( conv->repaint_ii );
854 conv->ireg = new_ireg;
855 }
856 }
857
858 /* The magnification or the visual image have changed ... remake the
859 * display image.
860 */
861 static void
conversion_rebuild_display(Conversion * conv)862 conversion_rebuild_display( Conversion *conv )
863 {
864 IMAGE *visual_im;
865 IMAGE *new_display_im;
866 Imageinfo *new_display_ii;
867 IMAGE *mask;
868 REGION *new_mreg;
869
870 #ifdef DEBUG
871 g_print( "conversion_remake_display: %p\n", conv );
872 #endif /*DEBUG*/
873
874 if( conv->visual_ii )
875 visual_im = imageinfo_get( FALSE, conv->visual_ii );
876 else
877 visual_im = NULL;
878
879 /* Keep gcc quiet about annoying possible-used-before-set warnings.
880 */
881 new_display_ii = NULL;
882 new_display_im = NULL;
883 new_mreg = NULL;
884 mask = NULL;
885
886 /* Make the new stuff first.
887 */
888 if( visual_im ) {
889 if( !(new_display_im =
890 conversion_make_display( conv, visual_im, &mask )) )
891 return;
892 if( !(new_display_ii = imageinfo_new( main_imageinfogroup,
893 NULL, new_display_im, NULL )) ) {
894 im_close( new_display_im );
895 return;
896 }
897 managed_sub_add( MANAGED( new_display_ii ),
898 MANAGED( conv->visual_ii ) );
899 if( mask &&
900 !(new_mreg = im_region_create( mask )) )
901 return;
902 }
903
904 IM_FREEF( im_region_free, conv->mreg );
905 MANAGED_UNREF( conv->display_ii );
906
907 if( visual_im ) {
908 conv->display_ii = new_display_ii;
909 conv->mask = mask;
910 conv->mreg = new_mreg;
911 MANAGED_REF( conv->display_ii );
912
913 conv->canvas.width = new_display_im->Xsize;
914 conv->canvas.height = new_display_im->Ysize;
915 }
916
917 /* Certainly need a new repaint image.
918 */
919 conversion_rebuild_repaint( conv );
920 }
921
922 /* The underlying ii has changed. Remake the visualisation image.
923 */
924 static void
conversion_rebuild_visual(Conversion * conv)925 conversion_rebuild_visual( Conversion *conv )
926 {
927 IMAGE *im = imageinfo_get( FALSE, conv->ii );
928 IMAGE *new_visual_im;
929 Imageinfo *new_visual_ii;
930 REGION *new_reg;
931
932 #ifdef DEBUG
933 g_print( "conversion_rebuild_visual: %p\n", conv );
934 #endif /*DEBUG*/
935
936 /* Keep gcc quiet about annoying possible-used-before-set warnings.
937 */
938 new_visual_im = NULL;
939 new_visual_ii = NULL;
940 new_reg = NULL;
941
942 /* Make new visualization image.
943 */
944 if( im ) {
945 if( !(new_visual_im = conversion_make_visualise( conv, im )) )
946 return;
947 if( !(new_visual_ii = imageinfo_new( main_imageinfogroup,
948 NULL, new_visual_im, NULL )) ) {
949 im_close( new_visual_im );
950 return;
951 }
952 managed_sub_add( MANAGED( new_visual_ii ),
953 MANAGED( conv->ii ) );
954
955 if( !(new_reg = im_region_create( im )) )
956 return;
957 }
958
959 /* Junk old stuff.
960 */
961 IM_FREEF( im_region_free, conv->reg );
962 MANAGED_UNREF( conv->visual_ii );
963
964 /* Install new stuff.
965 */
966 if( im ) {
967 conv->visual_ii = new_visual_ii;
968 MANAGED_REF( conv->visual_ii );
969 conv->image.width = new_visual_im->Xsize;
970 conv->image.height = new_visual_im->Ysize;
971 conv->reg = new_reg;
972 }
973
974 /* Certainly need a new display.
975 */
976 conversion_rebuild_display( conv );
977 }
978
979 /* Something has changed ... check it out.
980 */
981 static void
conversion_changed(iObject * iobject)982 conversion_changed( iObject *iobject )
983 {
984 Conversion *conv = CONVERSION( iobject );
985
986 gboolean rebuild_display = FALSE;
987 gboolean rebuild_repaint = FALSE;
988
989 #ifdef DEBUG
990 g_print( "conversion_changed: %p\n", conv );
991 #endif /*DEBUG*/
992
993 /* Need to remake the display image if mag has changed.
994 */
995 if( conv->mag != conv->display_mag ) {
996 rebuild_display = TRUE;
997 conv->display_mag = conv->mag;
998 }
999
1000 /* Need to rebuild repaint if display control bar has changed.
1001 */
1002 if( conv->changed ) {
1003 conv->changed = FALSE;
1004 rebuild_repaint = TRUE;
1005 }
1006
1007 if( rebuild_display )
1008 conversion_rebuild_display( conv );
1009 else if( rebuild_repaint )
1010 conversion_rebuild_repaint( conv );
1011
1012 IOBJECT_CLASS( parent_class )->changed( iobject );
1013 }
1014
1015 static void
conversion_class_init(ConversionClass * class)1016 conversion_class_init( ConversionClass *class )
1017 {
1018 GObjectClass *gobject_class = (GObjectClass *) class;
1019 iObjectClass *iobject_class = (iObjectClass *) class;
1020
1021 parent_class = g_type_class_peek_parent( class );
1022
1023 gobject_class->dispose = conversion_dispose;
1024 gobject_class->finalize = conversion_finalize;
1025
1026 iobject_class->changed = conversion_changed;
1027
1028 /* Create signals.
1029 */
1030 conversion_signals[SIG_AREA_CHANGED] = g_signal_new( "area_changed",
1031 G_OBJECT_CLASS_TYPE( gobject_class ),
1032 G_SIGNAL_RUN_FIRST,
1033 G_STRUCT_OFFSET( ConversionClass, area_changed ),
1034 NULL, NULL,
1035 g_cclosure_marshal_VOID__POINTER,
1036 G_TYPE_NONE, 1,
1037 G_TYPE_POINTER );
1038 conversion_signals[SIG_IMAGEINFO_CHANGED] = g_signal_new(
1039 "imageinfo_changed",
1040 G_OBJECT_CLASS_TYPE( gobject_class ),
1041 G_SIGNAL_RUN_FIRST,
1042 G_STRUCT_OFFSET( ConversionClass, imageinfo_changed ),
1043 NULL, NULL,
1044 g_cclosure_marshal_VOID__VOID,
1045 G_TYPE_NONE, 0 );
1046 }
1047
1048 static void
conversion_init(Conversion * conv)1049 conversion_init( Conversion *conv )
1050 {
1051 static const Rect emptyrect = { 0, 0, 0, 0 };
1052
1053 #ifdef DEBUG
1054 g_print( "conversion_init: " );
1055 iobject_print( IOBJECT( conv ) );
1056 #endif /*DEBUG*/
1057
1058 conv->ii = NULL;
1059 conv->changed_sid = 0;
1060 conv->area_changed_sid = 0;
1061 conv->reg = NULL;
1062 conv->synchronous = FALSE;
1063 conv->priority = 0;
1064 conv->visual_ii = NULL;
1065 conv->display_ii = NULL;
1066 conv->display_mag = 99999999;
1067 conv->repaint_ii = NULL;
1068 conv->ireg = NULL;
1069 conv->mreg = NULL;
1070
1071 /* Default tile size ... OK for image display, too big for
1072 * thumbnails.
1073 */
1074 conv->tile_size = 64;
1075
1076 conv->underlay = emptyrect;
1077 conv->image = emptyrect;
1078 conv->canvas = emptyrect;
1079 conv->visible = emptyrect;
1080 conv->mag = 1;
1081
1082 conv->changed = FALSE;
1083 conv->enabled = FALSE;
1084 conv->scale = 1.0;
1085 conv->offset = 0.0;
1086 conv->falsecolour = FALSE;
1087 conv->type = TRUE;
1088
1089 conversion_all = g_slist_prepend( conversion_all, conv );
1090 }
1091
1092 GType
conversion_get_type(void)1093 conversion_get_type( void )
1094 {
1095 static GType type = 0;
1096
1097 if( !type ) {
1098 static const GTypeInfo info = {
1099 sizeof( ConversionClass ),
1100 NULL, /* base_init */
1101 NULL, /* base_finalize */
1102 (GClassInitFunc) conversion_class_init,
1103 NULL, /* class_finalize */
1104 NULL, /* class_data */
1105 sizeof( Conversion ),
1106 32, /* n_preallocs */
1107 (GInstanceInitFunc) conversion_init,
1108 };
1109
1110 type = g_type_register_static( TYPE_MODEL,
1111 "Conversion", &info, 0 );
1112 }
1113
1114 return( type );
1115 }
1116
1117 static void
conversion_link(Conversion * conv,Imageinfo * ii)1118 conversion_link( Conversion *conv, Imageinfo *ii )
1119 {
1120 iobject_set( IOBJECT( conv ), "display_conversion", NULL );
1121 conversion_set_image( conv, ii );
1122 }
1123
1124 Conversion *
conversion_new(Imageinfo * ii)1125 conversion_new( Imageinfo *ii )
1126 {
1127 Conversion *conv;
1128
1129 conv = CONVERSION( g_object_new( TYPE_CONVERSION, NULL ) );
1130 conversion_link( conv, ii );
1131
1132 return( conv );
1133 }
1134
1135 /* Imageinfo has changed signal. The ii is the same, but the image it
1136 * represents may have changed from "p" to "r" or whatever. Assume size/etc.
1137 * stay the same.
1138 */
1139 static void
conversion_ii_changed_cb(Imageinfo * ii,Conversion * conv)1140 conversion_ii_changed_cb( Imageinfo *ii, Conversion *conv )
1141 {
1142 conversion_rebuild_visual( conv );
1143 iobject_changed( IOBJECT( conv ) );
1144 }
1145
1146 /* Something like a paint action on the ii.
1147 */
1148 static void
conversion_ii_area_changed_cb(Imageinfo * imageinfo,Rect * dirty,Conversion * conv)1149 conversion_ii_area_changed_cb( Imageinfo *imageinfo,
1150 Rect *dirty, Conversion *conv )
1151 {
1152 Rect repaint;
1153
1154 conversion_im_to_disp_rect( conv, dirty, &repaint );
1155 conversion_area_changed( conv, &repaint );
1156 }
1157
1158 /* Install a new image.
1159 */
1160 void
conversion_set_image(Conversion * conv,Imageinfo * ii)1161 conversion_set_image( Conversion *conv, Imageinfo *ii )
1162 {
1163 /* Pointer compare is safe, since we hold a ref to conv->ii and it
1164 * can't have been freed. So we can't have a new ii at the same
1165 * address as the old ii.
1166 */
1167 if( conv->ii != ii ) {
1168 IMAGE *im;
1169
1170 if( ii )
1171 im = imageinfo_get( FALSE, ii );
1172 else
1173 im = NULL;
1174
1175 /* Junk old stuff.
1176 */
1177 FREESID( conv->changed_sid, conv->ii );
1178 FREESID( conv->area_changed_sid, conv->ii );
1179 MANAGED_UNREF( conv->ii );
1180 conv->image.width = -1;
1181 conv->image.height = -1;
1182
1183 /* Install new stuff.
1184 */
1185 if( ii ) {
1186 conv->ii = ii;
1187 MANAGED_REF( conv->ii );
1188 conv->changed_sid = g_signal_connect(
1189 G_OBJECT( ii ), "changed",
1190 G_CALLBACK( conversion_ii_changed_cb ),
1191 conv );
1192 conv->area_changed_sid = g_signal_connect(
1193 G_OBJECT( ii ), "area_changed",
1194 G_CALLBACK( conversion_ii_area_changed_cb ),
1195 conv );
1196 }
1197 if( im ) {
1198 conv->underlay.width = im->Xsize;
1199 conv->underlay.height = im->Ysize;
1200 }
1201
1202 /* Make new visualization image.
1203 */
1204 conversion_rebuild_visual( conv );
1205
1206 /* Tell everyone about our new ii.
1207 */
1208 conversion_imageinfo_changed( conv );
1209 }
1210
1211 iobject_changed( IOBJECT( conv ) );
1212 }
1213
1214 double
conversion_dmag(int mag)1215 conversion_dmag( int mag )
1216 {
1217 g_assert( mag != 0 );
1218
1219 if( mag > 0 )
1220 return( mag );
1221 else
1222 return( 1.0 / (-mag) );
1223 }
1224
1225 /* Zoom in and zoom out scale factors ... a little tricky with our funny -ve
1226 * representation for subsample.
1227 */
1228 int
conversion_double(int mag)1229 conversion_double( int mag )
1230 {
1231 g_assert( mag != -1 );
1232
1233 if( mag == -3 )
1234 return( -2 );
1235 else if( mag == -2 )
1236 return( 1 );
1237 else if( mag > 0 )
1238 return( mag * 2 );
1239 else
1240 return( mag / 2 );
1241 }
1242
1243 int
conversion_halve(int mag)1244 conversion_halve( int mag )
1245 {
1246 g_assert( mag != -1 );
1247
1248 if( mag == 1 )
1249 return( -2 );
1250 else if( mag > 1 )
1251 return( mag / 2 );
1252 else
1253 return( mag * 2 );
1254 }
1255
1256 /* Convert display to image coordinates and back.
1257 */
1258 void
conversion_disp_to_im(Conversion * conv,int dx,int dy,int * ix,int * iy)1259 conversion_disp_to_im( Conversion *conv, int dx, int dy, int *ix, int *iy )
1260 {
1261 double fmag = conversion_dmag( conv->mag );
1262
1263 *ix = (int) (dx / fmag);
1264 *iy = (int) (dy / fmag);
1265 }
1266
1267 void
conversion_im_to_disp(Conversion * conv,int ix,int iy,int * dx,int * dy)1268 conversion_im_to_disp( Conversion *conv, int ix, int iy, int *dx, int *dy )
1269 {
1270 double fmag = conversion_dmag( conv->mag );
1271
1272 *dx = (int) (ix * fmag);
1273 *dy = (int) (iy * fmag);
1274 }
1275
1276 /* Same for rects.
1277 */
1278 void
conversion_disp_to_im_rect(Conversion * conv,Rect * dr,Rect * ir)1279 conversion_disp_to_im_rect( Conversion *conv, Rect *dr, Rect *ir )
1280 {
1281 double fmag = conversion_dmag( conv->mag );
1282
1283 int brx, bry;
1284 Rect out;
1285
1286 out.left = floor( dr->left / fmag );
1287 out.top = floor( dr->top / fmag );
1288 brx = ceil( IM_RECT_RIGHT( dr ) / fmag );
1289 bry = ceil( IM_RECT_BOTTOM( dr ) / fmag );
1290 out.width = brx - out.left;
1291 out.height = bry - out.top;
1292
1293 *ir = out;
1294 }
1295
1296 void
conversion_im_to_disp_rect(Conversion * conv,Rect * ir,Rect * dr)1297 conversion_im_to_disp_rect( Conversion *conv, Rect *ir, Rect *dr )
1298 {
1299 double fmag = conversion_dmag( conv->mag );
1300
1301 int brx, bry;
1302 Rect out;
1303
1304 out.left = floor( ir->left * fmag );
1305 out.top = floor( ir->top * fmag );
1306 brx = ceil( IM_RECT_RIGHT( ir ) * fmag );
1307 bry = ceil( IM_RECT_BOTTOM( ir ) * fmag );
1308 out.width = brx - out.left;
1309 out.height = bry - out.top;
1310
1311 *dr = out;
1312 }
1313
1314 void
conversion_set_mag(Conversion * conv,int mag)1315 conversion_set_mag( Conversion *conv, int mag )
1316 {
1317 int x, y;
1318
1319 /* Mag 0 means scale image to fit window. Use visible hint in
1320 * conversion to pick the mag.
1321 */
1322 if( mag == 0 ) {
1323 float xfac;
1324 float yfac;
1325 float fac;
1326
1327 /* Need to have image and visible set.
1328 */
1329 if( conv->visible.width <= 0 ||
1330 conv->image.width <= 0 || !conv->ii || !conv->ii->im )
1331 return;
1332
1333 xfac = (float) conv->visible.width / conv->image.width;
1334 yfac = (float) conv->visible.height / conv->image.height;
1335 fac = IM_MIN( xfac, yfac );
1336
1337 if( fac >= 1 )
1338 mag = (int) fac;
1339 else
1340 /* 0.999 means we don't round up on an exact fit.
1341
1342 FIXME ... yuk
1343
1344 */
1345 mag = -((int) (0.99999999 + 1.0/fac));
1346
1347 #ifdef DEBUG
1348 g_print( "conversion_set_mag: shrink to fit:\n" );
1349 g_print( " visible %dx%d, image %dx%d\n",
1350 conv->visible.width, conv->visible.height,
1351 conv->image.width, conv->image.height );
1352 g_print( " picked mag of %d\n", mag );
1353 #endif /*DEBUG*/
1354 }
1355
1356 /* Check for this-mag-will-cause-integer-overflow. Should flag an
1357 * error, but we just bail out instead.
1358 */
1359 if( mag > 0 &&
1360 ((double) conv->image.width * mag > (double) INT_MAX / 2 ||
1361 (double) conv->image.height * mag > (double) INT_MAX / 2) )
1362 return;
1363
1364 /* Will this mag result in width/height of <1? If it will, pick a
1365 * mag that most nearly gives us width/height 1.
1366 */
1367 conv->mag = mag;
1368 conversion_im_to_disp( conv,
1369 conv->image.width, conv->image.height, &x, &y );
1370 if( x <= 0 || y <= 0 ) {
1371 conv->mag = IM_MAX( -conv->image.width, -conv->image.height );
1372 if( conv->mag == -1 )
1373 conv->mag = 1;
1374 }
1375
1376 if( conv->mag != conv->display_mag )
1377 iobject_changed( IOBJECT( conv ) );
1378 }
1379
1380 void
conversion_set_synchronous(Conversion * conv,gboolean synchronous)1381 conversion_set_synchronous( Conversion *conv, gboolean synchronous )
1382 {
1383 if( conv->synchronous != synchronous ) {
1384 #ifdef DEBUG
1385 printf( "conversion_set_synchronous: %d", synchronous );
1386 iobject_print( IOBJECT( conv ) );
1387 #endif /*DEBUG*/
1388
1389 conv->synchronous = synchronous;
1390 if( conv->ii )
1391 iobject_changed( IOBJECT( conv->ii ) );
1392 }
1393 }
1394
1395 void
conversion_set_params(Conversion * conv,gboolean enabled,double scale,double offset,gboolean falsecolour,gboolean type)1396 conversion_set_params( Conversion *conv, gboolean enabled,
1397 double scale, double offset, gboolean falsecolour, gboolean type )
1398 {
1399 gboolean changed = FALSE;
1400
1401 if( conv->enabled != enabled )
1402 changed = TRUE;
1403 if( enabled )
1404 if( conv->scale != scale ||
1405 conv->offset != offset ||
1406 conv->falsecolour != falsecolour ||
1407 conv->type != type )
1408 changed = TRUE;
1409
1410 if( changed ) {
1411 conv->enabled = enabled;
1412 conv->scale = scale;
1413 conv->offset = offset;
1414 conv->falsecolour = falsecolour;
1415 conv->type = type;
1416 conv->changed = TRUE;
1417 iobject_changed( IOBJECT( conv ) );
1418 }
1419 }
1420