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