1 /* image management ... a layer over the VIPS IMAGE type
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 
32 jobs:
33 
34 - reference counting layer ... in Managed base class, plus links to heap
35   garbage collection
36 
37 - filesystem tracking: we stat open files and signal file_changed if we see a
38   change
39 
40 - cache: several open( "fred.v" )s share a single Imageinfo, provided their
41   mtimes are all the same
42 
43 - lookup table management ... if an operation can work with pixel lookup
44   tables (found by examining a flag in the VIPS function descriptor), then
45   instead of operating on the image, the operation runs on the LUT associated
46   with that image ... Imageinfo tracks the LUTs representing delayed eval
47 
48 - dependency tracking ... an imageinfo can require several other imageinfos
49   to be open for it to work properly; we follow these dependencies, and
50   delay destroying an imageinfo until it's not required by any others
51 
52 - temp file management ... we can make temp images on disc; we unlink() these
53   temps when they're no longer needed
54 
55 - imageinfo/expr association tracking ... we track when an expr
56   receives an imageinfo as its value; the info is used to get region views
57   to display in the right image ... see expr_real_new_value()
58 
59 - paint stuff: also undo/redo buffers, each with a "*_changed" signal
60 
61  */
62 
63 /*
64 
65 more stuff:
66 
67 while we transition to vips8, also use imageinfo to wrap VipsImage
68 
69 most of the jobs above are pushed down into vips8 now ... except for
70 
71 - reference counting layer ... in Managed base class
72 
73 - filesystem tracking: we stat open files and signal file_changed if we see a
74   change
75 
76 - cache: several open( "fred.v" )s share a single Imageinfo, provided their
77   mtimes are all the same
78 
79  */
80 
81 
82 #include "ip.h"
83 
84 /*
85 #define DEBUG
86 #define DEBUG_MAKE
87 #define DEBUG_RGB
88 #define DEBUG_OPEN
89 #define DEBUG_CHECK
90  */
91 
92 static iContainerClass *imageinfogroup_parent_class = NULL;
93 
94 static void
imageinfogroup_finalize(GObject * gobject)95 imageinfogroup_finalize( GObject *gobject )
96 {
97 	Imageinfogroup *imageinfogroup = IMAGEINFOGROUP( gobject );
98 
99 	IM_FREEF( g_hash_table_destroy, imageinfogroup->filename_hash );
100 
101 	G_OBJECT_CLASS( imageinfogroup_parent_class )->finalize( gobject );
102 }
103 
104 static void
imageinfogroup_child_add(iContainer * parent,iContainer * child,int pos)105 imageinfogroup_child_add( iContainer *parent, iContainer *child, int pos )
106 {
107 	Imageinfogroup *imageinfogroup = IMAGEINFOGROUP( parent );
108 	Imageinfo *imageinfo = IMAGEINFO( child );
109 	const char *name = IOBJECT( imageinfo )->name;
110 	GSList *hits;
111 
112 	hits = (GSList *) g_hash_table_lookup( imageinfogroup->filename_hash,
113 		name );
114 	hits = g_slist_prepend( hits, imageinfo );
115 	g_hash_table_insert( imageinfogroup->filename_hash,
116 		(gpointer) name, (gpointer) hits );
117 
118 	ICONTAINER_CLASS( imageinfogroup_parent_class )->
119 		child_add( parent, child, pos );
120 }
121 
122 static void
imageinfogroup_child_remove(iContainer * parent,iContainer * child)123 imageinfogroup_child_remove( iContainer *parent, iContainer *child )
124 {
125 	Imageinfogroup *imageinfogroup = IMAGEINFOGROUP( parent );
126 	Imageinfo *imageinfo = IMAGEINFO( child );
127 	const char *name = IOBJECT( imageinfo )->name;
128 	GSList *hits;
129 
130 	hits = (GSList *) g_hash_table_lookup( imageinfogroup->filename_hash,
131 		name );
132 	g_assert( hits );
133 	hits = g_slist_remove( hits, imageinfo );
134 
135 	/* child is going away (probably), so we don't want to link hits back
136 	 * on again with child->name as the key ... if possible, look down
137 	 * hits for another name we can use instead.
138 	 */
139 	if( hits ) {
140 		const char *new_name = IOBJECT( hits->data )->name;
141 
142 		g_hash_table_replace( imageinfogroup->filename_hash,
143 			(gpointer) new_name, (gpointer) hits );
144 	}
145 	else
146 		g_hash_table_remove( imageinfogroup->filename_hash,
147 			(gpointer) name );
148 
149 	ICONTAINER_CLASS( imageinfogroup_parent_class )->
150 		child_remove( parent, child );
151 }
152 
153 static void
imageinfogroup_class_init(ImageinfogroupClass * class)154 imageinfogroup_class_init( ImageinfogroupClass *class )
155 {
156 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
157 	iContainerClass *icontainer_class = ICONTAINER_CLASS( class );
158 
159 	imageinfogroup_parent_class = g_type_class_peek_parent( class );
160 
161 	gobject_class->finalize = imageinfogroup_finalize;
162 
163 	icontainer_class->child_add = imageinfogroup_child_add;
164 	icontainer_class->child_remove = imageinfogroup_child_remove;
165 }
166 
167 static void
imageinfogroup_init(Imageinfogroup * imageinfogroup)168 imageinfogroup_init( Imageinfogroup *imageinfogroup )
169 {
170 #ifdef DEBUG
171 	printf( "imageinfogroup_init\n" );
172 #endif /*DEBUG*/
173 
174 	imageinfogroup->filename_hash =
175 		g_hash_table_new( g_str_hash, g_str_equal );
176 }
177 
178 GType
imageinfogroup_get_type(void)179 imageinfogroup_get_type( void )
180 {
181 	static GType imageinfogroup_type = 0;
182 
183 	if( !imageinfogroup_type ) {
184 		static const GTypeInfo info = {
185 			sizeof( ImageinfogroupClass ),
186 			NULL,           /* base_init */
187 			NULL,           /* base_finalize */
188 			(GClassInitFunc) imageinfogroup_class_init,
189 			NULL,           /* class_finalize */
190 			NULL,           /* class_data */
191 			sizeof( Imageinfogroup ),
192 			32,             /* n_preallocs */
193 			(GInstanceInitFunc) imageinfogroup_init,
194 		};
195 
196 		imageinfogroup_type = g_type_register_static( TYPE_ICONTAINER,
197 			"Imageinfogroup", &info, 0 );
198 	}
199 
200 	return( imageinfogroup_type );
201 }
202 
203 Imageinfogroup *
imageinfogroup_new(void)204 imageinfogroup_new( void )
205 {
206 	Imageinfogroup *imageinfogroup = IMAGEINFOGROUP(
207 		g_object_new( TYPE_IMAGEINFOGROUP, NULL ) );
208 
209 	return( imageinfogroup );
210 }
211 
212 static void *
imageinfogroup_lookup_test(Imageinfo * imageinfo,struct stat * buf)213 imageinfogroup_lookup_test( Imageinfo *imageinfo, struct stat *buf )
214 {
215 	const char *name = IOBJECT( imageinfo )->name;
216 
217 	if( name && buf->st_mtime == imageinfo->mtime )
218 		return( imageinfo );
219 
220 	return( NULL );
221 }
222 
223 /* Look up by filename ... mtimes have to match too.
224  */
225 static Imageinfo *
imageinfogroup_lookup(Imageinfogroup * imageinfogroup,const char * filename)226 imageinfogroup_lookup( Imageinfogroup *imageinfogroup, const char *filename )
227 {
228 	GSList *hits;
229 	Imageinfo *imageinfo;
230 	struct stat buf;
231 
232 	if( stat( filename, &buf ) == 0 &&
233 		(hits = (GSList *) g_hash_table_lookup(
234 			imageinfogroup->filename_hash, filename )) &&
235 		(imageinfo = IMAGEINFO( slist_map( hits,
236 			(SListMapFn) imageinfogroup_lookup_test, &buf ) )) )
237 		return( imageinfo );
238 
239 	return( NULL );
240 }
241 
242 /* Our signals.
243  */
244 enum {
245 	SIG_AREA_CHANGED,	/* Area of image has changed: update screen */
246 	SIG_AREA_PAINTED,	/* Area of image has been painted */
247 	SIG_UNDO_CHANGED,	/* Undo/redo state has changed */
248 	SIG_FILE_CHANGED,	/* Underlying file seems to have changed */
249 	SIG_INVALIDATE,		/* IMAGE* has been invalidated */
250 	SIG_LAST
251 };
252 
253 static ManagedClass *parent_class = NULL;
254 
255 static guint imageinfo_signals[SIG_LAST] = { 0 };
256 
257 #if defined(DEBUG) || defined(DEBUG_OPEN) || defined(DEBUG_RGB) || \
258 	defined(DEBUG_CHECK) || defined(DEBUG_MAKE)
259 static void
imageinfo_print(Imageinfo * imageinfo)260 imageinfo_print( Imageinfo *imageinfo )
261 {
262 	printf( " \"%s\" mtime = %d (%p)\n",
263 		IOBJECT( imageinfo )->name,
264 		(int) imageinfo->mtime,
265 		imageinfo );
266 }
267 #endif
268 
269 void *
imageinfo_area_changed(Imageinfo * imageinfo,Rect * dirty)270 imageinfo_area_changed( Imageinfo *imageinfo, Rect *dirty )
271 {
272 	g_assert( IS_IMAGEINFO( imageinfo ) );
273 
274 #ifdef DEBUG
275 	printf( "imageinfo_area_changed: "
276 		"left = %d, top = %d, width = %d, height = %d\n",
277 		dirty->left, dirty->top, dirty->width, dirty->height );
278 #endif /*DEBUG*/
279 
280 	g_signal_emit( G_OBJECT( imageinfo ),
281 		imageinfo_signals[SIG_AREA_CHANGED], 0, dirty );
282 
283 	return( NULL );
284 }
285 
286 void *
imageinfo_area_painted(Imageinfo * imageinfo,Rect * dirty)287 imageinfo_area_painted( Imageinfo *imageinfo, Rect *dirty )
288 {
289 	g_assert( IS_IMAGEINFO( imageinfo ) );
290 
291 #ifdef DEBUG
292 	printf( "imageinfo_area_painted: left = %d, top = %d, "
293 		"width = %d, height = %d\n",
294 		dirty->left, dirty->top, dirty->width, dirty->height );
295 #endif /*DEBUG*/
296 
297 	g_signal_emit( G_OBJECT( imageinfo ),
298 		imageinfo_signals[SIG_AREA_PAINTED], 0, dirty );
299 
300 	return( NULL );
301 }
302 
303 static void *
imageinfo_undo_changed(Imageinfo * imageinfo)304 imageinfo_undo_changed( Imageinfo *imageinfo )
305 {
306 	g_assert( IS_IMAGEINFO( imageinfo ) );
307 
308 	g_signal_emit( G_OBJECT( imageinfo ),
309 		imageinfo_signals[SIG_UNDO_CHANGED], 0 );
310 
311 	return( NULL );
312 }
313 
314 static void *
imageinfo_file_changed(Imageinfo * imageinfo)315 imageinfo_file_changed( Imageinfo *imageinfo )
316 {
317 	g_assert( IS_IMAGEINFO( imageinfo ) );
318 
319 #ifdef DEBUG_CHECK
320 	printf( "imageinfo_file_changed:" );
321 	imageinfo_print( imageinfo );
322 #endif /*DEBUG_CHECK*/
323 
324 	g_signal_emit( G_OBJECT( imageinfo ),
325 		imageinfo_signals[SIG_FILE_CHANGED], 0 );
326 
327 	return( NULL );
328 }
329 
330 static void *
imageinfo_invalidate(Imageinfo * imageinfo)331 imageinfo_invalidate( Imageinfo *imageinfo )
332 {
333 	g_assert( IS_IMAGEINFO( imageinfo ) );
334 
335 #ifdef DEBUG_CHECK
336 	printf( "imageinfo_invalidate:" );
337 	imageinfo_print( imageinfo );
338 #endif /*DEBUG_CHECK*/
339 
340 	g_signal_emit( G_OBJECT( imageinfo ),
341 		imageinfo_signals[SIG_INVALIDATE], 0 );
342 
343 	return( NULL );
344 }
345 
346 void
imageinfo_expr_add(Imageinfo * imageinfo,Expr * expr)347 imageinfo_expr_add( Imageinfo *imageinfo, Expr *expr )
348 {
349 #ifdef DEBUG
350 	printf( "imageinfo_expr_add: " );
351 	expr_name_print( expr );
352 	printf( "has imageinfo \"%s\" as value\n", imageinfo->im->filename );
353 #endif /*DEBUG*/
354 
355 	g_assert( !g_slist_find( imageinfo->exprs, expr ) );
356 	g_assert( !expr->imageinfo );
357 
358 	expr->imageinfo = imageinfo;
359 	imageinfo->exprs = g_slist_prepend( imageinfo->exprs, expr );
360 }
361 
362 void *
imageinfo_expr_remove(Expr * expr,Imageinfo * imageinfo)363 imageinfo_expr_remove( Expr *expr, Imageinfo *imageinfo )
364 {
365 #ifdef DEBUG
366 	printf( "imageinfo_expr_remove: " );
367 	expr_name_print( expr );
368 	printf( "has lost imageinfo \"%s\" as value\n",
369 		imageinfo->im->filename );
370 #endif /*DEBUG*/
371 
372 	g_assert( expr->imageinfo );
373 	g_assert( g_slist_find( imageinfo->exprs, expr ) );
374 	g_assert( expr->imageinfo == imageinfo );
375 
376 	expr->imageinfo = NULL;
377 	imageinfo->exprs = g_slist_remove( imageinfo->exprs, expr );
378 
379 	return( NULL );
380 }
381 
382 GSList *
imageinfo_expr_which(Imageinfo * imageinfo)383 imageinfo_expr_which( Imageinfo *imageinfo )
384 {
385 	return( imageinfo->exprs );
386 }
387 
388 /* Find the underlying image in an imageinfo.
389  */
390 IMAGE *
imageinfo_get_underlying(Imageinfo * imageinfo)391 imageinfo_get_underlying( Imageinfo *imageinfo )
392 {
393 	if( imageinfo->underlying )
394 		return( imageinfo_get_underlying( imageinfo->underlying ) );
395 	else
396 		return( imageinfo->im );
397 }
398 
399 /* Free up an undo fragment.
400  */
401 static void
imageinfo_undofragment_free(Undofragment * frag)402 imageinfo_undofragment_free( Undofragment *frag )
403 {
404 	IM_FREEF( im_close, frag->im );
405 	IM_FREE( frag );
406 }
407 
408 /* Free an undo buffer.
409  */
410 static void
imageinfo_undobuffer_free(Undobuffer * undo)411 imageinfo_undobuffer_free( Undobuffer *undo )
412 {
413 	slist_map( undo->frags,
414 		(SListMapFn) imageinfo_undofragment_free, NULL );
415 	IM_FREEF( g_slist_free, undo->frags );
416 	IM_FREE( undo );
417 }
418 
419 /* Free all undo information attached to an imageinfo.
420  */
421 static void
imageinfo_undo_free(Imageinfo * imageinfo)422 imageinfo_undo_free( Imageinfo *imageinfo )
423 {
424 	slist_map( imageinfo->redo,
425 		(SListMapFn) imageinfo_undobuffer_free, NULL );
426 	IM_FREEF( g_slist_free, imageinfo->redo );
427 	slist_map( imageinfo->undo,
428 		(SListMapFn) imageinfo_undobuffer_free, NULL );
429 	IM_FREEF( g_slist_free, imageinfo->undo );
430 	IM_FREEF( imageinfo_undobuffer_free, imageinfo->cundo );
431 }
432 
433 static void
imageinfo_dispose_eval(Imageinfo * imageinfo)434 imageinfo_dispose_eval( Imageinfo *imageinfo )
435 {
436 	imageinfo->monitored = FALSE;
437 
438 	/* Make sure any callbacks from the IMAGE stop working.
439 	 */
440 	if( imageinfo->proxy ) {
441 		imageinfo->proxy->imageinfo = NULL;
442 		imageinfo->proxy = NULL;
443 	}
444 }
445 
446 static void
imageinfo_dispose(GObject * gobject)447 imageinfo_dispose( GObject *gobject )
448 {
449 	Imageinfo *imageinfo = IMAGEINFO( gobject );
450 
451 #ifdef DEBUG_OPEN
452 	printf( "imageinfo_dispose:" );
453 	imageinfo_print( imageinfo );
454 #endif /*DEBUG_OPEN*/
455 
456 	slist_map( imageinfo->exprs,
457 		(SListMapFn) imageinfo_expr_remove, imageinfo );
458 	g_assert( !imageinfo->exprs );
459 
460 	imageinfo_dispose_eval( imageinfo );
461 
462 	IM_FREEF( g_source_remove, imageinfo->check_tid );
463 
464 	G_OBJECT_CLASS( parent_class )->dispose( gobject );
465 }
466 
467 /* Final death!
468  */
469 static void
imageinfo_finalize(GObject * gobject)470 imageinfo_finalize( GObject *gobject )
471 {
472 	Imageinfo *imageinfo = IMAGEINFO( gobject );
473 	gboolean isfile = imageinfo->im && im_isfile( imageinfo->im );
474 
475 #ifdef DEBUG_MAKE
476 	printf( "imageinfo_finalize:" );
477 	imageinfo_print( imageinfo );
478 #endif /*DEBUG_MAKE*/
479 
480 	IM_FREEF( im_close, imageinfo->im );
481 	IM_FREEF( im_close, imageinfo->mapped_im );
482 	IM_FREEF( im_close, imageinfo->identity_lut );
483 
484 	if( imageinfo->dfile &&
485 		imageinfo->delete_filename &&
486 		isfile ) {
487 #ifdef DEBUG_OPEN
488 		printf( "imageinfo_destroy: unlinking \"%s\"\n", name );
489 #endif /*DEBUG_OPEN*/
490 
491 		unlinkf( "%s", imageinfo->delete_filename );
492 		iobject_changed( IOBJECT( main_imageinfogroup ) );
493 	}
494 
495 	VIPS_FREE( imageinfo->delete_filename );
496 
497 	MANAGED_UNREF( imageinfo->underlying );
498 
499 	imageinfo_undo_free( imageinfo );
500 
501 	G_OBJECT_CLASS( parent_class )->finalize( gobject );
502 }
503 
504 /* Make an info string about an imageinfo.
505  */
506 static void
imageinfo_info(iObject * iobject,VipsBuf * buf)507 imageinfo_info( iObject *iobject, VipsBuf *buf )
508 {
509 	Imageinfo *imageinfo = IMAGEINFO( iobject );
510 
511 	vips_buf_appendi( buf, imageinfo_get( FALSE, imageinfo ) );
512 
513 	/* Don't chain up to parent->info(), we don't want all the other
514 	 * stuff, this is going to be used for a caption.
515 	 */
516 }
517 
518 static void
imageinfo_real_area_changed(Imageinfo * imageinfo,Rect * dirty)519 imageinfo_real_area_changed( Imageinfo *imageinfo, Rect *dirty )
520 {
521 }
522 
523 static void
imageinfo_real_area_painted(Imageinfo * imageinfo,Rect * dirty)524 imageinfo_real_area_painted( Imageinfo *imageinfo, Rect *dirty )
525 {
526 	/* Cache attaches to this signal and invalidates on paint. Trigger a
527 	 * repaint in turn.
528 	 */
529 	imageinfo_area_changed( imageinfo, dirty );
530 }
531 
532 static void
imageinfo_real_undo_changed(Imageinfo * imageinfo)533 imageinfo_real_undo_changed( Imageinfo *imageinfo )
534 {
535 }
536 
537 static void
imageinfo_real_file_changed(Imageinfo * imageinfo)538 imageinfo_real_file_changed( Imageinfo *imageinfo )
539 {
540 }
541 
542 static void
imageinfo_real_invalidate(Imageinfo * imageinfo)543 imageinfo_real_invalidate( Imageinfo *imageinfo )
544 {
545 }
546 
547 static void
imageinfo_class_init(ImageinfoClass * class)548 imageinfo_class_init( ImageinfoClass *class )
549 {
550 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
551 	iObjectClass *iobject_class = IOBJECT_CLASS( class );
552 	ManagedClass *managed_class = MANAGED_CLASS( class );
553 
554 	parent_class = g_type_class_peek_parent( class );
555 
556 	gobject_class->dispose = imageinfo_dispose;
557 	gobject_class->finalize = imageinfo_finalize;
558 
559 	iobject_class->info = imageinfo_info;
560 
561 	/* Timeout on unreffed images.
562 	 */
563 	managed_class->keepalive = 60.0;
564 
565 	class->area_changed = imageinfo_real_area_changed;
566 	class->area_painted = imageinfo_real_area_painted;
567 	class->undo_changed = imageinfo_real_undo_changed;
568 	class->file_changed = imageinfo_real_file_changed;
569 	class->invalidate = imageinfo_real_invalidate;
570 
571 	/* Create signals.
572 	 */
573 	imageinfo_signals[SIG_AREA_CHANGED] = g_signal_new( "area_changed",
574 		G_OBJECT_CLASS_TYPE( gobject_class ),
575 		G_SIGNAL_RUN_FIRST,
576 		G_STRUCT_OFFSET( ImageinfoClass, area_changed ),
577 		NULL, NULL,
578 		g_cclosure_marshal_VOID__POINTER,
579 		G_TYPE_NONE, 1,
580 		G_TYPE_POINTER );
581 	imageinfo_signals[SIG_AREA_PAINTED] = g_signal_new( "area_painted",
582 		G_OBJECT_CLASS_TYPE( gobject_class ),
583 		G_SIGNAL_RUN_FIRST,
584 		G_STRUCT_OFFSET( ImageinfoClass, area_painted ),
585 		NULL, NULL,
586 		g_cclosure_marshal_VOID__POINTER,
587 		G_TYPE_NONE, 1,
588 		G_TYPE_POINTER );
589 	imageinfo_signals[SIG_UNDO_CHANGED] = g_signal_new( "undo_changed",
590 		G_OBJECT_CLASS_TYPE( gobject_class ),
591 		G_SIGNAL_RUN_FIRST,
592 		G_STRUCT_OFFSET( ImageinfoClass, undo_changed ),
593 		NULL, NULL,
594 		g_cclosure_marshal_VOID__VOID,
595 		G_TYPE_NONE, 0 );
596 	imageinfo_signals[SIG_FILE_CHANGED] = g_signal_new( "file_changed",
597 		G_OBJECT_CLASS_TYPE( gobject_class ),
598 		G_SIGNAL_RUN_FIRST,
599 		G_STRUCT_OFFSET( ImageinfoClass, file_changed ),
600 		NULL, NULL,
601 		g_cclosure_marshal_VOID__VOID,
602 		G_TYPE_NONE, 0 );
603 	imageinfo_signals[SIG_INVALIDATE] = g_signal_new( "invalidate",
604 		G_OBJECT_CLASS_TYPE( gobject_class ),
605 		G_SIGNAL_RUN_FIRST,
606 		G_STRUCT_OFFSET( ImageinfoClass, invalidate ),
607 		NULL, NULL,
608 		g_cclosure_marshal_VOID__VOID,
609 		G_TYPE_NONE, 0 );
610 }
611 
612 static void
imageinfo_init(Imageinfo * imageinfo)613 imageinfo_init( Imageinfo *imageinfo )
614 {
615 #ifdef DEBUG_MAKE
616 	printf( "imageinfo_init: %p\n", imageinfo );
617 #endif /*DEBUG_MAKE*/
618 
619 	imageinfo->im = NULL;
620 	imageinfo->mapped_im = NULL;
621 	imageinfo->identity_lut = NULL;
622 	imageinfo->underlying = NULL;
623 	imageinfo->proxy = NULL;
624 
625 	imageinfo->dfile = FALSE;
626 	imageinfo->delete_filename = NULL;
627 	imageinfo->from_file = FALSE;
628 	imageinfo->mtime = 0;
629 	imageinfo->exprs = NULL;
630 	imageinfo->ok_to_paint = FALSE;
631 	imageinfo->undo = NULL;
632 	imageinfo->redo = NULL;
633 	imageinfo->cundo = NULL;
634 
635 	imageinfo->monitored = FALSE;
636 
637 	imageinfo->check_mtime = 0;
638 	imageinfo->check_tid = 0;
639 }
640 
641 GType
imageinfo_get_type(void)642 imageinfo_get_type( void )
643 {
644 	static GType type = 0;
645 
646 	if( !type ) {
647 		static const GTypeInfo info = {
648 			sizeof( ImageinfoClass ),
649 			NULL,           /* base_init */
650 			NULL,           /* base_finalize */
651 			(GClassInitFunc) imageinfo_class_init,
652 			NULL,           /* class_finalize */
653 			NULL,           /* class_data */
654 			sizeof( Imageinfo ),
655 			32,             /* n_preallocs */
656 			(GInstanceInitFunc) imageinfo_init,
657 		};
658 
659 		type = g_type_register_static( TYPE_MANAGED,
660 			"Imageinfo", &info, 0 );
661 	}
662 
663 	return( type );
664 }
665 
666 static int
imageinfo_proxy_eval(Imageinfoproxy * proxy)667 imageinfo_proxy_eval( Imageinfoproxy *proxy )
668 {
669 	Imageinfo *imageinfo = proxy->imageinfo;
670 
671 	if( imageinfo && imageinfo->im->time )
672 		if( progress_update_percent( imageinfo->im->time->percent,
673 			imageinfo->im->time->eta ) )
674 			return( -1 );
675 
676 	return( 0 );
677 }
678 
679 static int
imageinfo_proxy_invalidate(Imageinfoproxy * proxy)680 imageinfo_proxy_invalidate( Imageinfoproxy *proxy )
681 {
682 	Imageinfo *imageinfo = proxy->imageinfo;
683 
684 	if( imageinfo )
685 		imageinfo_invalidate( imageinfo );
686 
687 	return( 0 );
688 }
689 
690 static int
imageinfo_proxy_preclose(Imageinfoproxy * proxy)691 imageinfo_proxy_preclose( Imageinfoproxy *proxy )
692 {
693 	Imageinfo *imageinfo = proxy->imageinfo;
694 
695 	/* Remove everything related to progress.
696 	 */
697 	if( imageinfo )
698 		imageinfo_dispose_eval( imageinfo );
699 
700 	return( 0 );
701 }
702 
703 /* Add a proxy to track IMAGE events.
704  */
705 static void
imageinfo_proxy_add(Imageinfo * imageinfo)706 imageinfo_proxy_add( Imageinfo *imageinfo )
707 {
708 	/* Only if we're running interactively.
709 	 */
710 	if( main_option_batch )
711 		return;
712 
713 	/* Already being monitored?
714 	 */
715 	if( imageinfo->monitored )
716 		return;
717 	imageinfo->monitored = TRUE;
718 
719         /* Need a proxy on IMAGE.
720          */
721 	g_assert( !imageinfo->proxy );
722 	if( !(imageinfo->proxy = IM_NEW( imageinfo->im, Imageinfoproxy )) )
723 	if( !(imageinfo->proxy = IM_NEW( NULL, Imageinfoproxy )) )
724 		return;
725 	imageinfo->proxy->im = imageinfo->im;
726 	imageinfo->proxy->imageinfo = imageinfo;
727 
728 	(void) im_add_eval_callback( imageinfo->im,
729 		(im_callback_fn) imageinfo_proxy_eval,
730 		imageinfo->proxy, NULL );
731 
732 	(void) im_add_invalidate_callback( imageinfo->im,
733 		(im_callback_fn) imageinfo_proxy_invalidate,
734 		imageinfo->proxy, NULL );
735 
736 	/* Has to be preclose, because we want to be sure we disconnect before
737 	 * the proxy is freed on a close callback.
738 	 */
739 	(void) im_add_preclose_callback( imageinfo->im,
740 		(im_callback_fn) imageinfo_proxy_preclose,
741 		imageinfo->proxy, NULL );
742 }
743 
744 /* Make a basic imageinfo. No refs, will be destroyed on next GC. If name is
745  * NULL, make a temp name up; otherwise name needs to be unique.
746  */
747 Imageinfo *
imageinfo_new(Imageinfogroup * imageinfogroup,Heap * heap,IMAGE * im,const char * name)748 imageinfo_new( Imageinfogroup *imageinfogroup,
749 	Heap *heap, IMAGE *im, const char *name )
750 {
751 	Imageinfo *imageinfo =
752 		IMAGEINFO( g_object_new( TYPE_IMAGEINFO, NULL ) );
753 	char buf[FILENAME_MAX];
754 
755 #ifdef DEBUG_OPEN
756 	printf( "imageinfo_new: %p \"%s\"\n", imageinfo, im->filename );
757 #endif /*DEBUG_OPEN*/
758 
759 	managed_link_heap( MANAGED( imageinfo ), heap );
760 
761 	if( !name ) {
762 		if( !temp_name( buf, "v" ) )
763 			/* Will be freed on next GC.
764 			 */
765 			return( NULL );
766 
767 		name = buf;
768 	}
769 	iobject_set( IOBJECT( imageinfo ), name, NULL );
770 
771 	/* Only record the pointer when we know we will make the imageinfo
772 	 * successfully.
773 	 */
774 	imageinfo->im = im;
775 
776 	icontainer_child_add( ICONTAINER( imageinfogroup ),
777 		ICONTAINER( imageinfo ), -1 );
778 	imageinfo_proxy_add( imageinfo );
779 
780 	return( imageinfo );
781 }
782 
783 /* An image is a result of a LUT operation on an earlier imageinfo.
784  */
785 void
imageinfo_set_underlying(Imageinfo * top_imageinfo,Imageinfo * imageinfo)786 imageinfo_set_underlying( Imageinfo *top_imageinfo, Imageinfo *imageinfo )
787 {
788 	g_assert( !top_imageinfo->underlying );
789 
790 	top_imageinfo->underlying = imageinfo;
791 	MANAGED_REF( top_imageinfo->underlying );
792 }
793 
794 /* Make a temp image. Deleted on close. No refs: closed on next GC. If you
795  * want it to stick around, ref it!
796  */
797 Imageinfo *
imageinfo_new_temp(Imageinfogroup * imageinfogroup,Heap * heap,const char * name,const char * mode)798 imageinfo_new_temp( Imageinfogroup *imageinfogroup,
799 	Heap *heap, const char *name, const char *mode )
800 {
801 	IMAGE *im;
802 	char tname[FILENAME_MAX];
803 	Imageinfo *imageinfo;
804 
805 	if( !temp_name( tname, "v" ) ||
806 		!(im = im_open( tname, mode )) )
807 		return( NULL );
808 	if( !(imageinfo = imageinfo_new( imageinfogroup, heap, im, name )) ) {
809 		im_close( im );
810 		return( NULL );
811 	}
812 	imageinfo->dfile = TRUE;
813 	VIPS_SETSTR( imageinfo->delete_filename, tname );
814 
815 	return( imageinfo );
816 }
817 
818 /* Need this context during imageinfo_open_image_input().
819  */
820 typedef struct _ImageinfoOpen {
821 	Imageinfogroup *imageinfogroup;
822 	Heap *heap;
823 	const char *filename;
824 	GtkWidget *parent;
825 } ImageinfoOpen;
826 
827 /* Open for read ... returns a non-heap pointer, destroy if it goes in the
828  * heap.
829  */
830 static Imageinfo *
imageinfo_open_image_input(const char * filename,ImageinfoOpen * open)831 imageinfo_open_image_input( const char *filename, ImageinfoOpen *open )
832 {
833 	Imageinfo *imageinfo;
834 	VipsFormatClass *format;
835 
836 	if( !(format = vips_format_for_file( filename )) )
837 		return( NULL );
838 
839 	if( strcmp( VIPS_OBJECT_CLASS( format )->nickname, "vips" ) == 0 ) {
840 		IMAGE *im;
841 
842 		if( !(im = im_open( filename, "r" )) )
843 			return( NULL );
844 
845 		if( !(imageinfo = imageinfo_new( open->imageinfogroup,
846 			open->heap, im, open->filename )) ) {
847 			im_close( im );
848 			return( NULL );
849 		}
850 		MANAGED_REF( imageinfo );
851 
852 #ifdef DEBUG_OPEN
853 		printf( "imageinfo_open_image_input: opened VIPS \"%s\"\n",
854 			filename );
855 #endif /*DEBUG_OPEN*/
856 	}
857 	else {
858 		VipsFormatFlags flags =
859 			vips_format_get_flags( format, filename );
860 		const char *mode = flags & VIPS_FORMAT_PARTIAL ? "p" : "w";
861 
862 		if( !(imageinfo = imageinfo_new_temp( open->imageinfogroup,
863 			open->heap, open->filename, mode )) )
864 			return( NULL );
865 		MANAGED_REF( imageinfo );
866 		if( format->load( filename, imageinfo->im ) ||
867 			im_histlin( imageinfo->im, "im_copy %s %s",
868 				filename, imageinfo->im->filename ) ) {
869 			MANAGED_UNREF( imageinfo );
870 			return( NULL );
871 		}
872 
873 #ifdef DEBUG_OPEN
874 		printf( "imageinfo_open_image_input: opened %s \"%s\"\n",
875 			VIPS_OBJECT_CLASS( format )->nickname, filename );
876 #endif /*DEBUG_OPEN*/
877 	}
878 
879 	/* Get ready for input.
880  	 */
881 	if( im_pincheck( imageinfo->im ) )
882 		return( NULL );
883 
884 	/* The rewind will have removed everything from the IMAGE. Reattach
885 	 * progress.
886 	 */
887 	imageinfo_proxy_add( imageinfo );
888 
889 	/* Attach the original filename ... pick this up again later as a
890 	 * save default.
891 	 */
892 	if( im_meta_set_string( imageinfo->im, ORIGINAL_FILENAME, filename ) )
893 		return( NULL );
894 
895 	return( imageinfo );
896 }
897 
898 Imageinfo *
imageinfo_new_from_pixbuf(Imageinfogroup * imageinfogroup,Heap * heap,GdkPixbuf * pixbuf)899 imageinfo_new_from_pixbuf( Imageinfogroup *imageinfogroup,
900 	Heap *heap, GdkPixbuf *pixbuf )
901 {
902 	int width;
903 	int height;
904 	int bands;
905 	guchar *bytes;
906 	Imageinfo *ii;
907 	size_t vips_length;
908 
909 	width = gdk_pixbuf_get_width( pixbuf );
910 	height = gdk_pixbuf_get_height( pixbuf );
911 	bands = gdk_pixbuf_get_n_channels( pixbuf );
912 
913 	/* 2.26 and later have gdk_pixbuf_get_pixels_with_length()
914 	 * which would let us check the size, but we can't reslly use it yet.
915 	 * Another time!
916 
917 	guint length;
918 
919 	bytes = gdk_pixbuf_get_pixels_with_length( pixbuf, &length );
920 	if( vips_length != length ) {
921 		error_top( _( "Unable to create image." ) );
922 		error_sub( _( "vips expected %zd bytes, gdkpixbuf made %d" ),
923 			vips_length, length );
924 		return( NULL );
925 	}
926 
927 	 */
928 
929 	bytes = gdk_pixbuf_get_pixels( pixbuf );
930 
931 	if( !(ii = imageinfo_new_temp( imageinfogroup, heap, NULL, "t" )) )
932 		return( NULL );
933 	im_initdesc( ii->im, width, height, bands,
934 		IM_BBITS_BYTE, IM_BANDFMT_UCHAR, IM_CODING_NONE,
935 		IM_TYPE_sRGB, 1.0, 1.0, 0, 0 );
936 	if( im_setupout( ii->im ) )
937 		return( NULL );
938 	vips_length = VIPS_IMAGE_SIZEOF_LINE( ii->im ) * height;
939 	memcpy( ii->im->data, bytes, vips_length );
940 
941 	return( ii );
942 }
943 
944 /* Was this ii loaded from a file (ie. ->name contains a filename the user
945  * might recognise).
946  */
947 gboolean
imageinfo_is_from_file(Imageinfo * imageinfo)948 imageinfo_is_from_file( Imageinfo *imageinfo )
949 {
950 	return( IOBJECT( imageinfo )->name &&
951 		imageinfo->from_file );
952 }
953 
954 static gint
imageinfo_attach_check_cb(Imageinfo * imageinfo)955 imageinfo_attach_check_cb( Imageinfo *imageinfo )
956 {
957 	if( imageinfo_is_from_file( imageinfo ) &&
958 		imageinfo->check_tid ) {
959 		struct stat buf;
960 
961 		if( !stat( IOBJECT( imageinfo )->name, &buf ) &&
962 			buf.st_mtime != imageinfo->check_mtime ) {
963 			imageinfo->check_mtime = buf.st_mtime;
964 			imageinfo_file_changed( imageinfo );
965 		}
966 	}
967 
968 	return( TRUE );
969 }
970 
971 /* Start checking this file for updates, signal reload if there is one.
972  */
973 static void
imageinfo_attach_check(Imageinfo * imageinfo)974 imageinfo_attach_check( Imageinfo *imageinfo )
975 {
976 	if( imageinfo_is_from_file( imageinfo ) &&
977 		!imageinfo->check_tid ) {
978 		struct stat buf;
979 
980 		/* Need to be able to stat() to be able to track a file.
981 		 */
982 		if( stat( IOBJECT( imageinfo )->name, &buf ) )
983 			return;
984 
985 		imageinfo->mtime = buf.st_mtime;
986 		imageinfo->check_mtime = imageinfo->mtime;
987 		imageinfo->check_tid = g_timeout_add( 1000,
988 			(GSourceFunc) imageinfo_attach_check_cb, imageinfo );
989 
990 #ifdef DEBUG_CHECK
991 		printf( "imageinfo_attach_check: starting to check" );
992 		imageinfo_print( imageinfo );
993 #endif /*DEBUG_CHECK*/
994 	}
995 	else
996 		IM_FREEF( g_source_remove, imageinfo->check_tid );
997 }
998 
999 /* Open a filename for input. The filenmae can have an embedded mode.
1000  */
1001 Imageinfo *
imageinfo_new_input(Imageinfogroup * imageinfogroup,GtkWidget * parent,Heap * heap,const char * name)1002 imageinfo_new_input( Imageinfogroup *imageinfogroup, GtkWidget *parent,
1003 	Heap *heap, const char *name )
1004 {
1005 	Imageinfo *imageinfo;
1006 	ImageinfoOpen open;
1007 
1008 	if( (imageinfo = imageinfogroup_lookup( imageinfogroup, name )) ) {
1009 		/* We always make a new non-heap pointer.
1010 		 */
1011 		MANAGED_REF( imageinfo );
1012 		return( imageinfo );
1013 	}
1014 
1015 	open.imageinfogroup = imageinfogroup;
1016 	open.heap = heap;
1017 	open.filename = name;
1018 	open.parent = parent;
1019 
1020         if( !(imageinfo = (Imageinfo *) callv_string_filename(
1021 		(callv_string_fn) imageinfo_open_image_input,
1022 		name, &open, NULL, NULL )) ) {
1023 		error_top( _( "Unable to open image." ) );
1024 		error_sub( _( "Unable to open file \"%s\" as image." ),
1025 			name );
1026 		error_vips();
1027                 return( NULL );
1028         }
1029 
1030 	imageinfo->from_file = TRUE;
1031 	imageinfo_attach_check( imageinfo );
1032 
1033 	return( imageinfo );
1034 }
1035 
1036 /* Add an identity lut, if this is a LUTtable image.
1037  */
1038 static IMAGE *
imageinfo_get_identity_lut(Imageinfo * imageinfo)1039 imageinfo_get_identity_lut( Imageinfo *imageinfo )
1040 {
1041 	if( imageinfo->im->Coding == IM_CODING_NONE &&
1042 		imageinfo->im->BandFmt == IM_BANDFMT_UCHAR ) {
1043 		if( !imageinfo->identity_lut ) {
1044 			char tname[FILENAME_MAX];
1045 			IMAGE *im;
1046 
1047 			if( !temp_name( tname, "v" ) ||
1048 				!(im = im_open( tname, "p" )) )
1049 				return( NULL );
1050 			imageinfo->identity_lut = im;
1051 
1052 			if( im_identity( imageinfo->identity_lut,
1053 				imageinfo->im->Bands ) ||
1054 				im_histlin( imageinfo->identity_lut,
1055 					"im_identity %s %d",
1056 					imageinfo->identity_lut->filename,
1057 					imageinfo->im->Bands ) )
1058 				return( NULL );
1059 		}
1060 
1061 		return( imageinfo->identity_lut );
1062 	}
1063 	else
1064 		return( NULL );
1065 }
1066 
1067 static IMAGE *
imageinfo_get_mapped(Imageinfo * imageinfo)1068 imageinfo_get_mapped( Imageinfo *imageinfo )
1069 {
1070 	if( !imageinfo->mapped_im ) {
1071 		IMAGE *im = imageinfo_get_underlying( imageinfo );
1072 		IMAGE *mapped_im;
1073 		char name[FILENAME_MAX];
1074 		char *argv[4];
1075 
1076 		if( !temp_name( name, "v" ) ||
1077 			!(mapped_im = im_open( name, "p" )) )
1078 			return( NULL );
1079 		argv[0] = im->filename;
1080 		argv[1] = mapped_im->filename;
1081 		argv[2] = imageinfo->im->filename;
1082 		argv[3] = NULL;
1083 		if( im_maplut( im, mapped_im, imageinfo->im ) ||
1084 			im_updatehist( mapped_im, "im_maplut", 3, argv ) ) {
1085 			im_close( mapped_im );
1086 			error_vips_all();
1087 			return( NULL );
1088 		}
1089 		imageinfo->mapped_im = mapped_im;
1090 	}
1091 
1092 	return( imageinfo->mapped_im );
1093 }
1094 
1095 /* Get a lut ... or not!
1096  */
1097 IMAGE *
imageinfo_get(gboolean use_lut,Imageinfo * imageinfo)1098 imageinfo_get( gboolean use_lut, Imageinfo *imageinfo )
1099 {
1100 	if( !imageinfo )
1101 		return( NULL );
1102 
1103 	if( use_lut && imageinfo->underlying )
1104 		return( imageinfo->im );
1105 	if( use_lut && !imageinfo->underlying ) {
1106 		IMAGE *lut;
1107 
1108 		if( (lut = imageinfo_get_identity_lut( imageinfo )) )
1109 			return( lut );
1110 		else
1111 			return( imageinfo->im );
1112 	}
1113 	else if( !use_lut && imageinfo->underlying )
1114 		return( imageinfo_get_mapped( imageinfo ) );
1115 	else
1116 		return( imageinfo->im );
1117 }
1118 
1119 /* Do a set of II all refer to the same underlying image? Used to spot
1120  * LUTable optimisations.
1121  */
1122 gboolean
imageinfo_same_underlying(Imageinfo * imageinfo[],int n)1123 imageinfo_same_underlying( Imageinfo *imageinfo[], int n )
1124 {
1125 	int i;
1126 
1127 	if( n < 2 )
1128 		return( TRUE );
1129 	else {
1130 		IMAGE *first = imageinfo_get_underlying( imageinfo[0] );
1131 
1132 		for( i = 1; i < n; i++ )
1133 			if( imageinfo_get_underlying( imageinfo[i] ) != first )
1134 				return( FALSE );
1135 
1136 		return( TRUE );
1137 	}
1138 }
1139 
1140 /* Write to a filename.
1141  */
1142 gboolean
imageinfo_write(Imageinfo * imageinfo,const char * name)1143 imageinfo_write( Imageinfo *imageinfo, const char *name )
1144 {
1145 	IMAGE *im = imageinfo_get( FALSE, imageinfo );
1146 
1147 	if( vips_format_write( im, name ) ) {
1148 		char filename[FILENAME_MAX];
1149 		char mode[FILENAME_MAX];
1150 
1151 		im_filename_split( name, filename, mode );
1152 		error_top( _( "Unable to write to file." ) );
1153 		error_sub( _( "Error writing image to file \"%s\"." ),
1154 			filename );
1155 		error_vips();
1156 
1157 		return( FALSE );
1158 	}
1159 
1160 	return( TRUE );
1161 }
1162 
1163 static gboolean
imageinfo_make_paintable(Imageinfo * imageinfo)1164 imageinfo_make_paintable( Imageinfo *imageinfo )
1165 {
1166 	progress_begin();
1167 	if( im_rwcheck( imageinfo->im ) ) {
1168 		progress_end();
1169 		error_top( _( "Unable to paint on image." ) );
1170 		error_sub( _( "Unable to get write permission for "
1171 			"file \"%s\".\nCheck permission settings." ),
1172 			imageinfo->im->filename );
1173 		error_vips();
1174 		return( FALSE );
1175 	}
1176 	progress_end();
1177 
1178 	imageinfo->ok_to_paint = TRUE;
1179 
1180 	return( TRUE );
1181 }
1182 
1183 static void
imageinfo_check_paintable_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)1184 imageinfo_check_paintable_cb( iWindow *iwnd, void *client,
1185 	iWindowNotifyFn nfn, void *sys )
1186 {
1187 	Imageinfo *imageinfo = IMAGEINFO( client );
1188 
1189 	if( !imageinfo_make_paintable( imageinfo ) ) {
1190 		nfn( sys, IWINDOW_ERROR );
1191 		return;
1192 	}
1193 
1194 	nfn( sys, IWINDOW_YES );
1195 }
1196 
1197 /* Check painting is OK. nfn() called on "ok!". Returns FALSE if it's
1198  * not immediately obvious that we can paint.
1199  */
1200 gboolean
imageinfo_check_paintable(Imageinfo * imageinfo,GtkWidget * parent,iWindowNotifyFn nfn,void * sys)1201 imageinfo_check_paintable( Imageinfo *imageinfo, GtkWidget *parent,
1202 	iWindowNotifyFn nfn, void *sys )
1203 {
1204 	IMAGE *im = imageinfo_get( FALSE, imageinfo );
1205 
1206 	if( im &&
1207 		im_isfile( im ) &&
1208 		!imageinfo->dfile &&
1209 		!imageinfo->ok_to_paint ) {
1210 		iDialog *idlg;
1211 
1212 		idlg = box_yesno( parent,
1213 			imageinfo_check_paintable_cb,
1214 				iwindow_true_cb, imageinfo,
1215 			nfn, sys,
1216 			_( "Modify" ),
1217 			_( "Modify disc file?" ),
1218 			_( "This image is being shown directly from the "
1219 			"disc file:\n\n"
1220 			"   %s\n\n"
1221 			"If you paint on this file, it will be permanently "
1222 			"changed. If something goes wrong, you may lose work. "
1223 			"Are you sure you want to modify this file?" ),
1224 			IOBJECT( imageinfo )->name );
1225 		idialog_set_iobject( idlg, IOBJECT( imageinfo ) );
1226 
1227 		return( FALSE );
1228 	}
1229 	else if( im &&
1230 		!im_isfile( im ) &&
1231 		!imageinfo->ok_to_paint ) {
1232 		if( !imageinfo_make_paintable( imageinfo ) ) {
1233 			nfn( sys, IWINDOW_ERROR );
1234 			return( FALSE );
1235 		}
1236 	}
1237 
1238 	nfn( sys, IWINDOW_YES );
1239 
1240 	return( TRUE );
1241 }
1242 
1243 /* Try to get an Imageinfo from a symbol.
1244  */
1245 Imageinfo *
imageinfo_sym_image(Symbol * sym)1246 imageinfo_sym_image( Symbol *sym )
1247 {
1248         PElement *root = &sym->expr->root;
1249 
1250         if( sym->type == SYM_VALUE && PEISIMAGE( root ) )
1251                 return( PEGETII( root ) );
1252         else
1253                 return( NULL );
1254 }
1255 
1256 static Undofragment *
imageinfo_undofragment_new(Undobuffer * undo)1257 imageinfo_undofragment_new( Undobuffer *undo )
1258 {
1259 	Undofragment *frag = INEW( NULL, Undofragment );
1260 
1261 	frag->undo = undo;
1262 	frag->im = NULL;
1263 
1264 	return( frag );
1265 }
1266 
1267 static Undobuffer *
imageinfo_undobuffer_new(Imageinfo * imageinfo)1268 imageinfo_undobuffer_new( Imageinfo *imageinfo )
1269 {
1270 	Undobuffer *undo = INEW( NULL, Undobuffer );
1271 
1272 	undo->imageinfo = imageinfo;
1273 	undo->frags = NULL;
1274 
1275 	/* No pixels in bounding box at the moment.
1276 	 */
1277 	undo->bbox.left = 0;
1278 	undo->bbox.top = 0;
1279 	undo->bbox.width = 0;
1280 	undo->bbox.height = 0;
1281 
1282 	return( undo );
1283 }
1284 
1285 /* Grab from the image into an IMAGE buffer. Always grab to memory.
1286  */
1287 static IMAGE *
imageinfo_undo_grab_area(IMAGE * im,Rect * dirty)1288 imageinfo_undo_grab_area( IMAGE *im, Rect *dirty )
1289 {
1290 	IMAGE *save;
1291 
1292 	/* Make new image to extract to.
1293 	 */
1294 	if( !(save = im_open( "undo buffer", "t" )) )
1295 		return( NULL );
1296 
1297 	/* Try to extract from im.
1298 	 */
1299 	if( im_extract_area( im, save,
1300 		dirty->left, dirty->top, dirty->width, dirty->height ) ) {
1301 		im_close( save );
1302 		error_vips_all();
1303 		return( NULL );
1304 	}
1305 
1306 	return( save );
1307 }
1308 
1309 /* Grab into an undo fragment. Add frag to frag list on undo buffer, expand
1310  * bounding box.
1311  */
1312 static Undofragment *
imageinfo_undo_grab(Undobuffer * undo,Rect * dirty)1313 imageinfo_undo_grab( Undobuffer *undo, Rect *dirty )
1314 {
1315 	Imageinfo *imageinfo = undo->imageinfo;
1316 	Undofragment *frag = imageinfo_undofragment_new( undo );
1317 	IMAGE *im = imageinfo_get( FALSE, imageinfo );
1318 	Rect bbox;
1319 
1320 	/* Try to extract from im. Memory allocation happens at this
1321 	 * point, so we must be careful!
1322 	 */
1323 	if( !(frag->im = imageinfo_undo_grab_area( im, dirty )) ) {
1324 		imageinfo_undofragment_free( frag );
1325 		error_vips_all();
1326 		return( NULL );
1327 	}
1328 
1329 	/* Note position of this frag.
1330 	 */
1331 	frag->pos = *dirty;
1332 
1333 	/* Add frag to frag list on undo buffer.
1334 	 */
1335 	undo->frags = g_slist_prepend( undo->frags, frag );
1336 
1337 	/* Find bounding box for saved pixels.
1338 	 */
1339 	im_rect_unionrect( dirty, &undo->bbox, &bbox );
1340 	undo->bbox = bbox;
1341 
1342 	/* Return new frag.
1343 	 */
1344 	return( frag );
1345 }
1346 
1347 /* Trim the undo buffer if we have more than x items on it.
1348  */
1349 static void
imageinfo_undo_trim(Imageinfo * imageinfo)1350 imageinfo_undo_trim( Imageinfo *imageinfo )
1351 {
1352 	int max = PAINTBOX_MAX_UNDO;
1353 	int len = g_slist_length( imageinfo->undo );
1354 
1355 	if( max >= 0 && len > max ) {
1356 		GSList *l;
1357 		int i;
1358 
1359 		l = g_slist_reverse( imageinfo->undo );
1360 
1361 		for( i = 0; i < len - max; i++ ) {
1362 			Undobuffer *undo = (Undobuffer *) l->data;
1363 
1364 			imageinfo_undobuffer_free( undo );
1365 			l = g_slist_remove( l, undo );
1366 		}
1367 
1368 		imageinfo->undo = g_slist_reverse( l );
1369 	}
1370 
1371 #ifdef DEBUG
1372 	printf( "imageinfo_undo_trim: %d items in undo buffer\n",
1373 		g_slist_length( imageinfo->undo ) );
1374 #endif /*DEBUG*/
1375 }
1376 
1377 /* Mark the start or end of an undo session. Copy current undo information
1378  * to the undo buffers and NULL out the current undo pointer. Junk all redo
1379  * information: this new undo action makes all that out of date.
1380  */
1381 void
imageinfo_undo_mark(Imageinfo * imageinfo)1382 imageinfo_undo_mark( Imageinfo *imageinfo )
1383 {
1384 	/* Is there an existing undo save area?
1385 	 */
1386 	if( imageinfo->cundo ) {
1387 		/* Left over from the last undo save. Copy to undo save list
1388 		 * and get ready for new undo buffer.
1389 		 */
1390 		imageinfo->undo =
1391 			g_slist_prepend( imageinfo->undo, imageinfo->cundo );
1392 		imageinfo->cundo = NULL;
1393 	}
1394 
1395 	/* Junk all redo information.
1396 	 */
1397 	slist_map( imageinfo->redo,
1398 		(SListMapFn) imageinfo_undobuffer_free, NULL );
1399 	IM_FREEF( g_slist_free, imageinfo->redo );
1400 
1401 	/* Trim undo buffer.
1402 	 */
1403 	imageinfo_undo_trim( imageinfo );
1404 
1405 	/* Update menus.
1406 	 */
1407 	imageinfo_undo_changed( imageinfo );
1408 }
1409 
1410 /* Add to the undo buffer. If there is no undo buffer currently under
1411  * construction, make a new one. If there is an existing undo buffer, try to
1412  * grow it left/right/up/down so as to just enclose the new bounding box. We
1413  * assume that our dirty areas are not going to be disconnected. Is this
1414  * always true? No - if you move smudge or smear quickly, you can get
1415  * non-overlapping areas. However: if you do lots of little operations in more
1416  * or less the same place (surely the usual case), then this technique will be
1417  * far better.
1418  */
1419 static gboolean
imageinfo_undo_add(Imageinfo * imageinfo,Rect * dirty)1420 imageinfo_undo_add( Imageinfo *imageinfo, Rect *dirty )
1421 {
1422 	IMAGE *im = imageinfo_get( FALSE, imageinfo );
1423 	Undobuffer *undo = imageinfo->cundo;
1424 	Rect over, image, clipped;
1425 
1426 	/* Undo disabled? Do nothing.
1427 	 */
1428 	if( PAINTBOX_MAX_UNDO == 0 )
1429 		return( TRUE );
1430 
1431 	/* Clip dirty against image size.
1432 	 */
1433 	image.left = 0;
1434 	image.top = 0;
1435 	image.width = im->Xsize;
1436 	image.height = im->Ysize;
1437 	im_rect_intersectrect( &image, dirty, &clipped );
1438 
1439 	/* Is there anything left? If not, can return immediately.
1440 	 */
1441 	if( im_rect_isempty( &clipped ) )
1442 		return( TRUE );
1443 
1444 	if( !undo ) {
1445 		/* No current undo buffer ... start a new one for this action.
1446 		 */
1447 		if( !(imageinfo->cundo = undo =
1448 			imageinfo_undobuffer_new( imageinfo )) )
1449 			return( FALSE );
1450 
1451 		return( imageinfo_undo_grab( undo, &clipped ) != NULL );
1452 	}
1453 
1454 	/* Existing stuff we are to add to. Try to expand our undo
1455 	 * area to just enclose the new bounding box. We assume that
1456 	 * there is an overlap between the new and old stuff.
1457 	 */
1458 
1459 	/* Do we need to expand our saved area to the right?
1460 	 */
1461 	if( IM_RECT_RIGHT( &clipped ) > IM_RECT_RIGHT( &undo->bbox ) ) {
1462 		/* Expand to the right. Calculate the section we need
1463 		 * to add to our bounding box.
1464 		 */
1465 		over.left = IM_RECT_RIGHT( &undo->bbox );
1466 		over.top = undo->bbox.top;
1467 		over.width = IM_RECT_RIGHT( &clipped ) -
1468 			IM_RECT_RIGHT( &undo->bbox );
1469 		over.height = undo->bbox.height;
1470 
1471 		/* Grab new fragment.
1472 		 */
1473 		if( !imageinfo_undo_grab( undo, &over ) )
1474 			return( FALSE );
1475 	}
1476 
1477 	/* Do we need to expand our saved area to the left?
1478 	 */
1479 	if( undo->bbox.left > clipped.left ) {
1480 		over.left = clipped.left;
1481 		over.top = undo->bbox.top;
1482 		over.width = undo->bbox.left - clipped.left;
1483 		over.height = undo->bbox.height;
1484 
1485 		if( !imageinfo_undo_grab( undo, &over ) )
1486 			return( FALSE );
1487 	}
1488 
1489 	/* Do we need to expand our saved area upwards?
1490 	 */
1491 	if( undo->bbox.top > clipped.top ) {
1492 		over.left = undo->bbox.left;
1493 		over.top = clipped.top;
1494 		over.width = undo->bbox.width;
1495 		over.height = undo->bbox.top - clipped.top;
1496 
1497 		if( !imageinfo_undo_grab( undo, &over ) )
1498 			return( FALSE );
1499 	}
1500 
1501 	/* Do we need to expand our saved area downwards?
1502 	 */
1503 	if( IM_RECT_BOTTOM( &clipped ) > IM_RECT_BOTTOM( &undo->bbox ) ) {
1504 		over.left = undo->bbox.left;
1505 		over.top = IM_RECT_BOTTOM( &undo->bbox );
1506 		over.width = undo->bbox.width;
1507 		over.height = IM_RECT_BOTTOM( &clipped ) -
1508 			IM_RECT_BOTTOM( &undo->bbox );
1509 
1510 		if( !imageinfo_undo_grab( undo, &over ) )
1511 			return( FALSE );
1512 	}
1513 
1514 	return( TRUE );
1515 }
1516 
1517 /* Paste an undo fragment back into the image.
1518  */
1519 static void *
imageinfo_undofragment_paste(Undofragment * frag)1520 imageinfo_undofragment_paste( Undofragment *frag )
1521 {
1522 	Undobuffer *undo = frag->undo;
1523 	Imageinfo *imageinfo = undo->imageinfo;
1524 	IMAGE *im = imageinfo_get( FALSE, imageinfo );
1525 
1526 	im_insertplace( im, frag->im, frag->pos.left, frag->pos.top );
1527 	imageinfo_area_painted( imageinfo, &frag->pos );
1528 
1529 	return( NULL );
1530 }
1531 
1532 /* Paste a whole undo buffer back into the image.
1533  */
1534 static void
imageinfo_undobuffer_paste(Undobuffer * undo)1535 imageinfo_undobuffer_paste( Undobuffer *undo )
1536 {
1537 	slist_map( undo->frags,
1538 		(SListMapFn) imageinfo_undofragment_paste, NULL );
1539 }
1540 
1541 /* Undo a paint action.
1542  */
1543 gboolean
imageinfo_undo(Imageinfo * imageinfo)1544 imageinfo_undo( Imageinfo *imageinfo )
1545 {
1546 	Undobuffer *undo;
1547 
1548 	/* Find the undo action we are to perform.
1549 	 */
1550 	if( !imageinfo->undo )
1551 		return( TRUE );
1552 	undo = (Undobuffer *) imageinfo->undo->data;
1553 
1554 	/* We are going to undo the first action on the undo list. We must
1555 	 * save the area under the first undo action to the redo list. Do
1556 	 * the save, even if undo is disabled.
1557 	 */
1558 	if( !imageinfo_undo_add( imageinfo, &undo->bbox ) )
1559 		return( FALSE );
1560 
1561 	/* Add new undo area.
1562 	 */
1563 	imageinfo->redo = g_slist_prepend( imageinfo->redo, imageinfo->cundo );
1564 	imageinfo->cundo = NULL;
1565 
1566 	/* Paint undo back.
1567 	 */
1568 	imageinfo_undobuffer_paste( undo );
1569 
1570 	/* Junk the undo action we have performed.
1571 	 */
1572 	imageinfo->undo = g_slist_remove( imageinfo->undo, undo );
1573 	imageinfo_undobuffer_free( undo );
1574 
1575 	/* Trim undo buffer.
1576 	 */
1577 	imageinfo_undo_trim( imageinfo );
1578 
1579 	/* Update menus.
1580 	 */
1581 	imageinfo_undo_changed( imageinfo );
1582 
1583 	return( TRUE );
1584 }
1585 
1586 /* Redo a paint action, if possible.
1587  */
1588 gboolean
imageinfo_redo(Imageinfo * imageinfo)1589 imageinfo_redo( Imageinfo *imageinfo )
1590 {
1591 	Undobuffer *undo;
1592 
1593 	/* Find the redo action we are to perform.
1594 	 */
1595 	if( !imageinfo->redo )
1596 		return( TRUE );
1597 	undo = (Undobuffer *) imageinfo->redo->data;
1598 
1599 	/* We are going to redo the first action on the redo list. We must
1600 	 * save the area under the first redo action to the undo list. Save
1601 	 * even if undo is disabled.
1602 	 */
1603 	if( !imageinfo_undo_add( imageinfo, &undo->bbox ) )
1604 		return( FALSE );
1605 
1606 	/* Add this new buffer to the undo list.
1607 	 */
1608 	imageinfo->undo = g_slist_prepend( imageinfo->undo, imageinfo->cundo );
1609 	imageinfo->cundo = NULL;
1610 
1611 	/* Paint redo back.
1612 	 */
1613 	imageinfo_undobuffer_paste( undo );
1614 
1615 	/* We can junk the head of the undo list now.
1616 	 */
1617 	imageinfo->redo = g_slist_remove( imageinfo->redo, undo );
1618 	imageinfo_undobuffer_free( undo );
1619 
1620 	/* Trim undo buffer.
1621 	 */
1622 	imageinfo_undo_trim( imageinfo );
1623 
1624 	/* Update menus.
1625 	 */
1626 	imageinfo_undo_changed( imageinfo );
1627 
1628 	return( TRUE );
1629 }
1630 
1631 void
imageinfo_undo_clear(Imageinfo * imageinfo)1632 imageinfo_undo_clear( Imageinfo *imageinfo )
1633 {
1634 	imageinfo_undo_free( imageinfo );
1635 	imageinfo_undo_changed( imageinfo );
1636 }
1637 
1638 static int
imageinfo_draw_point_cb(IMAGE * im,int x,int y,void * a,void * b,void * c)1639 imageinfo_draw_point_cb( IMAGE *im, int x, int y, void *a, void *b, void *c )
1640 {
1641 	IMAGE *mask = (IMAGE *) a;
1642 	PEL *ink = (PEL *) b;
1643 
1644 	return( im_draw_mask( im, mask,
1645 		x - mask->Xsize / 2, y - mask->Ysize / 2, ink ) );
1646 }
1647 
1648 /* Draw a line.
1649  */
1650 gboolean
imageinfo_paint_line(Imageinfo * imageinfo,Imageinfo * ink,Imageinfo * mask,int x1,int y1,int x2,int y2)1651 imageinfo_paint_line( Imageinfo *imageinfo,
1652 	Imageinfo *ink, Imageinfo *mask,
1653 	int x1, int y1, int x2, int y2 )
1654 {
1655 	IMAGE *im = imageinfo_get( FALSE, imageinfo );
1656 	IMAGE *ink_im = imageinfo_get( FALSE, ink );
1657 	IMAGE *mask_im = imageinfo_get( FALSE, mask );
1658 	PEL *data = (PEL *) ink_im->data;
1659 	Rect dirty, p1, p2, image, clipped;
1660 
1661 	p1.width = mask_im->Xsize;
1662 	p1.height = mask_im->Ysize;
1663 	p1.left = x1 - mask_im->Xsize / 2;
1664 	p1.top = y1 - mask_im->Ysize / 2;
1665 	p2.width = mask_im->Xsize;
1666 	p2.height = mask_im->Ysize;
1667 	p2.left = x2 - mask_im->Xsize / 2;
1668 	p2.top = y2 - mask_im->Ysize / 2;
1669 	im_rect_unionrect( &p1, &p2, &dirty );
1670 
1671 	image.left = 0;
1672 	image.top = 0;
1673 	image.width = im->Xsize;
1674 	image.height = im->Ysize;
1675 	im_rect_intersectrect( &dirty, &image, &clipped );
1676 
1677 	if( im_rect_isempty( &clipped ) )
1678 		return( TRUE );
1679 
1680 	if( !imageinfo_undo_add( imageinfo, &clipped ) )
1681 		return( FALSE );
1682 
1683 	if( im_draw_line_user( im, x1, y1, x2, y2,
1684 		(VipsPlotFn) imageinfo_draw_point_cb, mask_im, data, NULL ) ) {
1685 		error_vips_all();
1686 		return( FALSE );
1687 	}
1688 
1689 	imageinfo_area_painted( imageinfo, &dirty );
1690 
1691 	return( TRUE );
1692 }
1693 
1694 /* Smudge a line.
1695  */
1696 gboolean
imageinfo_paint_smudge(Imageinfo * imageinfo,Rect * oper,int x1,int y1,int x2,int y2)1697 imageinfo_paint_smudge( Imageinfo *imageinfo,
1698 	Rect *oper, int x1, int y1, int x2, int y2 )
1699 {
1700 	IMAGE *im = imageinfo_get( FALSE, imageinfo );
1701 	Rect p1, p2, dirty;
1702 
1703 	/* Calculate bounding box for smudge.
1704 	 */
1705 	p1 = *oper;
1706 	p1.left += x1;
1707 	p1.top += y1;
1708 	p2 = *oper;
1709 	p2.left += x2;
1710 	p2.top += y2;
1711 	im_rect_unionrect( &p1, &p2, &dirty );
1712 	if( !imageinfo_undo_add( imageinfo, &dirty ) )
1713 		return( FALSE );
1714 
1715 	/* Smudge line connecting old and new points.
1716 	 */
1717 	if( im_draw_line_user( im, x1, y1, x2, y2,
1718 		(VipsPlotFn) im_smudge, oper, NULL, NULL ) ) {
1719 		error_vips_all();
1720 		return( FALSE );
1721 	}
1722 
1723 	imageinfo_area_painted( imageinfo, &dirty );
1724 
1725 	return( TRUE );
1726 }
1727 
1728 /* Flood an area.
1729  */
1730 gboolean
imageinfo_paint_flood(Imageinfo * imageinfo,Imageinfo * ink,int x,int y,gboolean blob)1731 imageinfo_paint_flood( Imageinfo *imageinfo, Imageinfo *ink,
1732 	int x, int y, gboolean blob )
1733 {
1734 	IMAGE *im = imageinfo_get( FALSE, imageinfo );
1735 	IMAGE *ink_im = imageinfo_get( FALSE, ink );
1736 	PEL *data = (PEL *) ink_im->data;
1737 	Rect dirty;
1738 	int result;
1739 
1740 	/* Save undo area. We have to save the entire image since we don't know
1741 	 * how much the flood will change :(
1742 	 */
1743 	dirty.left = 0;
1744 	dirty.top = 0;
1745 	dirty.width = im->Xsize;
1746 	dirty.height = im->Ysize;
1747 	if( !imageinfo_undo_add( imageinfo, &dirty ) )
1748 		return( FALSE );
1749 
1750 	/* Flood!
1751 	 */
1752 	if( blob )
1753 		result = im_flood_blob( im, x, y, data, &dirty );
1754 	else
1755 		result = im_flood( im, x, y, data, &dirty );
1756 	if( result ) {
1757 		error_vips_all();
1758 		return( FALSE );
1759 	}
1760 
1761 	imageinfo_area_painted( imageinfo, &dirty );
1762 
1763 	return( TRUE );
1764 }
1765 
1766 gboolean
imageinfo_paint_dropper(Imageinfo * imageinfo,Imageinfo * ink,int x,int y)1767 imageinfo_paint_dropper( Imageinfo *imageinfo, Imageinfo *ink, int x, int y )
1768 {
1769 	IMAGE *im = imageinfo_get( FALSE, imageinfo );
1770 	IMAGE *ink_im = imageinfo_get( FALSE, ink );
1771 	PEL *data = (PEL *) ink_im->data;
1772 	Rect dirty;
1773 
1774 	if( im_readpoint( im, x, y, data ) ) {
1775 		error_vips_all();
1776 		return( FALSE );
1777 	}
1778 	im_invalidate( ink_im );
1779 
1780 	dirty.left = 0;
1781 	dirty.top = 0;
1782 	dirty.width = ink_im->Xsize;
1783 	dirty.height = ink_im->Ysize;
1784 
1785 	imageinfo_area_painted( ink, &dirty );
1786 
1787 	return( TRUE );
1788 }
1789 
1790 /* Fill a rect.
1791  */
1792 gboolean
imageinfo_paint_rect(Imageinfo * imageinfo,Imageinfo * ink,Rect * area)1793 imageinfo_paint_rect( Imageinfo *imageinfo, Imageinfo *ink, Rect *area )
1794 {
1795 	IMAGE *im = imageinfo_get( FALSE, imageinfo );
1796 	IMAGE *ink_im = imageinfo_get( FALSE, ink );
1797 	PEL *data = (PEL *) ink_im->data;
1798 
1799 	if( !imageinfo_undo_add( imageinfo, area ) )
1800 		return( FALSE );
1801 
1802 	if( im_draw_rect( im,
1803 		area->left, area->top, area->width, area->height, 1, data ) ) {
1804 		error_vips_all();
1805 		return( FALSE );
1806 	}
1807 
1808 	imageinfo_area_painted( imageinfo, area );
1809 
1810 	return( TRUE );
1811 }
1812 
1813 /* Paint text into imageinfo, return width/height in tarea.
1814  */
1815 gboolean
imageinfo_paint_text(Imageinfo * imageinfo,const char * font_name,const char * text,Rect * tarea)1816 imageinfo_paint_text( Imageinfo *imageinfo,
1817 	const char *font_name, const char *text, Rect *tarea )
1818 {
1819 	IMAGE *im = imageinfo_get( FALSE, imageinfo );
1820 
1821 	if( im_text( im, text, font_name, 0, 0, get_dpi() ) ) {
1822 		error_top( _( "Unable to paint text." ) );
1823 		error_sub( _( "Unable to paint text \"%s\" in font \"%s\"." ),
1824 			text, font_name );
1825 		error_vips();
1826 
1827 		return( FALSE );
1828 	}
1829 
1830 	tarea->left = 0;
1831 	tarea->top = 0;
1832 	tarea->width = im->Xsize;
1833 	tarea->height = im->Ysize;
1834 
1835 	return( TRUE );
1836 }
1837 
1838 /* Draw a nib mask. Radius 0 means a single-pixel mask.
1839  */
1840 gboolean
imageinfo_paint_nib(Imageinfo * imageinfo,int radius)1841 imageinfo_paint_nib( Imageinfo *imageinfo, int radius )
1842 {
1843 	static PEL ink[1] = { 255 };
1844 
1845 	IMAGE *im = imageinfo_get( FALSE, imageinfo );
1846 
1847 	if( radius ) {
1848 		int r2 = radius * 2;
1849 		IMAGE *t;
1850 
1851 		if( !(t = im_open( "imageinfo_paint_nib", "p" )) ) {
1852 			error_vips();
1853 			return( FALSE );
1854 		}
1855 		if( im_black( t, 2 * (r2 + 1), 2 * (r2 + 1), 1 ) ||
1856 			im_draw_circle( t, r2, r2, r2, 1, ink ) ||
1857 			im_shrink( t, im, 2, 2 ) ) {
1858 			im_close( t );
1859 			error_vips();
1860 			return( FALSE );
1861 		}
1862 		im_close( t );
1863 	}
1864 	else {
1865 		if( im_black( im, 1, 1, 1 ) ||
1866 			im_draw_circle( im, 0, 0, 0, 1, ink ) )
1867 			return( FALSE );
1868 	}
1869 
1870 	return( TRUE );
1871 }
1872 
1873 /* Paint a mask.
1874  */
1875 gboolean
imageinfo_paint_mask(Imageinfo * imageinfo,Imageinfo * ink,Imageinfo * mask,int x,int y)1876 imageinfo_paint_mask( Imageinfo *imageinfo,
1877 	Imageinfo *ink, Imageinfo *mask, int x, int y )
1878 {
1879 	IMAGE *im = imageinfo_get( FALSE, imageinfo );
1880 	IMAGE *ink_im = imageinfo_get( FALSE, ink );
1881 	IMAGE *mask_im = imageinfo_get( FALSE, mask );
1882 	Rect dirty, image, clipped;
1883 
1884 	dirty.left = x;
1885 	dirty.top = y;
1886 	dirty.width = mask_im->Xsize;
1887 	dirty.height = mask_im->Ysize;
1888 	image.left = 0;
1889 	image.top = 0;
1890 	image.width = im->Xsize;
1891 	image.height = im->Ysize;
1892 	im_rect_intersectrect( &dirty, &image, &clipped );
1893 
1894 	if( im_rect_isempty( &clipped ) )
1895 		return( TRUE );
1896 
1897 	if( !imageinfo_undo_add( imageinfo, &clipped ) )
1898 		return( FALSE );
1899 
1900 	if( im_plotmask( im, 0, 0,
1901 		(PEL *) ink_im->data, (PEL *) mask_im->data, &dirty ) ) {
1902 		error_vips_all();
1903 		return( FALSE );
1904 	}
1905 
1906 	imageinfo_area_painted( imageinfo, &dirty );
1907 
1908 	return( TRUE );
1909 }
1910 
1911 /* Print a pixel. Output has to be parseable by imageinfo_from_text().
1912  */
1913 void
imageinfo_to_text(Imageinfo * imageinfo,VipsBuf * buf)1914 imageinfo_to_text( Imageinfo *imageinfo, VipsBuf *buf )
1915 {
1916 	IMAGE *im = imageinfo_get( FALSE, imageinfo );
1917 	PEL *p = (PEL *) im->data;
1918 	int i;
1919 
1920 #define PRINT_INT( T, I ) vips_buf_appendf( buf, "%d", ((T *)p)[I] );
1921 #define PRINT_FLOAT( T, I ) vips_buf_appendg( buf, ((T *)p)[I] );
1922 
1923 	for( i = 0; i < im->Bands; i++ ) {
1924 		if( i )
1925 			vips_buf_appends( buf, ", " );
1926 
1927 		switch( im->BandFmt ) {
1928 		case IM_BANDFMT_UCHAR:
1929 			PRINT_INT( unsigned char, i );
1930 			break;
1931 
1932 		case IM_BANDFMT_CHAR:
1933 			PRINT_INT( char, i );
1934 			break;
1935 
1936 		case IM_BANDFMT_USHORT:
1937 			PRINT_INT( unsigned short, i );
1938 			break;
1939 
1940 		case IM_BANDFMT_SHORT:
1941 			PRINT_INT( short, i );
1942 			break;
1943 
1944 		case IM_BANDFMT_UINT:
1945 			PRINT_INT( unsigned int, i );
1946 			break;
1947 
1948 		case IM_BANDFMT_INT:
1949 			PRINT_INT( int, i );
1950 			break;
1951 
1952 		case IM_BANDFMT_FLOAT:
1953 			PRINT_FLOAT( float, i );
1954 			break;
1955 
1956 		case IM_BANDFMT_COMPLEX:
1957 			vips_buf_appends( buf, "(" );
1958 			PRINT_FLOAT( float, (i << 1) );
1959 			vips_buf_appends( buf, ", " );
1960 			PRINT_FLOAT( float, (i << 1) + 1 );
1961 			vips_buf_appends( buf, ")" );
1962 			break;
1963 
1964 		case IM_BANDFMT_DOUBLE:
1965 			PRINT_FLOAT( double, i );
1966 			break;
1967 
1968 		case IM_BANDFMT_DPCOMPLEX:
1969 			vips_buf_appends( buf, "(" );
1970 			PRINT_FLOAT( double, i << 1 );
1971 			vips_buf_appends( buf, ", " );
1972 			PRINT_FLOAT( double, (i << 1) + 1 );
1973 			vips_buf_appends( buf, ")" );
1974 			break;
1975 
1976 		default:
1977 			vips_buf_appends( buf, "???" );
1978 			break;
1979 		}
1980 	}
1981 }
1982 
1983 /* Set band i to value.
1984  */
1985 static void
imageinfo_from_text_band(Imageinfo * imageinfo,int i,double re,double im)1986 imageinfo_from_text_band( Imageinfo *imageinfo, int i, double re, double im )
1987 {
1988 	IMAGE *image = imageinfo_get( FALSE, imageinfo );
1989 	PEL *p = (PEL *) image->data;
1990 	double mod = sqrt( re*re + im*im );
1991 
1992 	if( i < 0 || i >= image->Bands )
1993 		return;
1994 
1995 #define SET_INT( T, I, X ) (((T *)p)[I] = (T) IM_RINT(X))
1996 #define SET_FLOAT( T, I, X ) (((T *)p)[I] = (T) (X))
1997 
1998 	switch( image->BandFmt ) {
1999 	case IM_BANDFMT_UCHAR:
2000 		SET_INT( unsigned char, i, mod );
2001 		break;
2002 
2003 	case IM_BANDFMT_CHAR:
2004 		SET_INT( char, i, mod );
2005 		break;
2006 
2007 	case IM_BANDFMT_USHORT:
2008 		SET_INT( unsigned short, i, mod );
2009 		break;
2010 
2011 	case IM_BANDFMT_SHORT:
2012 		SET_INT( short, i, mod );
2013 		break;
2014 
2015 	case IM_BANDFMT_UINT:
2016 		SET_INT( unsigned int, i, mod );
2017 		break;
2018 
2019 	case IM_BANDFMT_INT:
2020 		SET_INT( int, i, mod );
2021 		break;
2022 
2023 	case IM_BANDFMT_FLOAT:
2024 		SET_FLOAT( float, i, mod );
2025 		break;
2026 
2027 	case IM_BANDFMT_COMPLEX:
2028 		SET_FLOAT( float, (i << 1), re );
2029 		SET_FLOAT( float, (i << 1) + 1, im );
2030 		break;
2031 
2032 	case IM_BANDFMT_DOUBLE:
2033 		SET_FLOAT( double, i, mod );
2034 		break;
2035 
2036 	case IM_BANDFMT_DPCOMPLEX:
2037 		SET_FLOAT( double, i << 1, re );
2038 		SET_FLOAT( double, (i << 1) + 1, im );
2039 		break;
2040 
2041 	default:
2042 		break;
2043 	}
2044 }
2045 
2046 /* Parse a string to an imageinfo.
2047  * Strings are from imageinfo_to_text(), ie. of the form:
2048  *
2049  *	50, 0, 0
2050  *	(12,13), (14,15)
2051  *
2052  */
2053 gboolean
imageinfo_from_text(Imageinfo * imageinfo,const char * text)2054 imageinfo_from_text( Imageinfo *imageinfo, const char *text )
2055 {
2056 	char buf[MAX_LINELENGTH];
2057 	char *p;
2058 	int i;
2059 	Rect dirty;
2060 
2061 #ifdef DEBUG_RGB
2062 	printf( "imageinfo_from_text: in: \"\%s\"\n", text );
2063 #endif /*DEBUG_RGB*/
2064 
2065 	im_strncpy( buf, text, MAX_LINELENGTH );
2066 
2067 	for( i = 0, p = buf; p += strspn( p, WHITESPACE ), *p; i++ ) {
2068 		double re, im;
2069 
2070 		if( p[0] == '(' ) {
2071 			/* Complex constant.
2072 			 */
2073 			re = g_ascii_strtod( p + 1, NULL );
2074 			p = break_token( p, "," );
2075 			im = g_ascii_strtod( p, NULL );
2076 			p = break_token( p, ")" );
2077 		}
2078 		else {
2079 			/* Real constant.
2080 			 */
2081 			re = g_ascii_strtod( p, NULL );
2082 			im = 0;
2083 		}
2084 
2085 		p = break_token( p, "," );
2086 
2087 		imageinfo_from_text_band( imageinfo, i, re, im );
2088 	}
2089 
2090 #ifdef DEBUG_RGB
2091 {
2092 	char txt[256];
2093 	VipsBuf buf = VIPS_BUF_STATIC( txt );
2094 
2095 	printf( "imageinfo_from_text: out: " );
2096 	imageinfo_to_text( imageinfo, &buf );
2097 	printf( "%s\n", vips_buf_all( &buf ) );
2098 }
2099 #endif /*DEBUG_RGB*/
2100 
2101 	dirty.left = 0;
2102 	dirty.top = 0;
2103 	dirty.width = 1;
2104 	dirty.height = 1;
2105 	imageinfo_area_painted( imageinfo, &dirty );
2106 
2107 	return( TRUE );
2108 }
2109 
2110 /* Get the image as display RGB in rgb[0-2].
2111  */
2112 void
imageinfo_to_rgb(Imageinfo * imageinfo,double * rgb)2113 imageinfo_to_rgb( Imageinfo *imageinfo, double *rgb )
2114 {
2115 	Conversion *conv;
2116 	Rect area;
2117 	PEL *p;
2118 	int i;
2119 
2120 #ifdef DEBUG_RGB
2121 {
2122 	char txt[256];
2123 	VipsBuf buf = VIPS_BUF_STATIC( txt );
2124 
2125 	printf( "imageinfo_to_rgb: in: " );
2126 	imageinfo_to_text( imageinfo, &buf );
2127 	printf( "%s\n", vips_buf_all( &buf ) );
2128 }
2129 #endif /*DEBUG_RGB*/
2130 
2131 	/* Make a temporary conv ... we hold the ref.
2132 	 */
2133 	conv = conversion_new( NULL );
2134 	conversion_set_synchronous( conv, TRUE );
2135 	conversion_set_image( conv, imageinfo );
2136 	g_object_ref( G_OBJECT( conv ) );
2137 	iobject_sink( IOBJECT( conv ) );
2138 
2139 	area.left = 0;
2140 	area.top = 0;
2141 	area.width = 1;
2142 	area.height = 1;
2143 
2144 	if( im_prepare( conv->ireg, &area ) ) {
2145 		UNREF( conv );
2146 		return;
2147 	}
2148         p = (PEL *) IM_REGION_ADDR( conv->ireg, area.left, area.top );
2149 
2150 	if( imageinfo->im->Bands < 3 )
2151 		for( i = 0; i < 3; i++ )
2152 			rgb[i] = p[0] / 255.0;
2153 	else
2154 		for( i = 0; i < 3; i++ )
2155 			rgb[i] = p[i] / 255.0;
2156 
2157 #ifdef DEBUG_RGB
2158 	printf( "imageinfo_to_rgb: out: r = %g, g = %g, b = %g\n",
2159 		rgb[0], rgb[1], rgb[2] );
2160 #endif /*DEBUG_RGB*/
2161 
2162 	UNREF( conv );
2163 }
2164 
2165 /* Try to overwrite an imageinfo with a display RGB colour.
2166  */
2167 void
imageinfo_from_rgb(Imageinfo * imageinfo,double * rgb)2168 imageinfo_from_rgb( Imageinfo *imageinfo, double *rgb )
2169 {
2170 	Imageinfogroup *imageinfogroup =
2171 		IMAGEINFOGROUP( ICONTAINER( imageinfo )->parent );
2172 	IMAGE *im = imageinfo_get( FALSE, imageinfo );
2173 	Imageinfo *in, *out;
2174 	IMAGE *t1, *t2;
2175 	int i;
2176 	Rect dirty;
2177 
2178 	/* Interchange format is sRGB.
2179 
2180 		FIXME ... should let other displays be used here, see
2181 		../scraps/calibrate.[hc]
2182 
2183 	 */
2184 	struct im_col_display *display = im_col_displays( 7 );
2185 
2186 #ifdef DEBUG_RGB
2187 	printf( "imageinfo_from_rgb: in: r = %g, g = %g, b = %g\n",
2188 		rgb[0], rgb[1], rgb[2] );
2189 #endif /*DEBUG_RGB*/
2190 
2191 	/* Make 1 pixel images for conversion.
2192 	 */
2193 	in = imageinfo_new_temp( imageinfogroup,
2194 		reduce_context->heap, NULL, "t" );
2195 	out = imageinfo_new_temp( imageinfogroup,
2196 		reduce_context->heap, NULL, "t" );
2197 	if( !in || !out )
2198 		return;
2199 	if( !(t1 = im_open_local( out->im, "imageinfo_from_rgb:1", "t" )) ||
2200 		!(t2 = im_open_local( out->im, "imageinfo_from_rgb:1", "t" )) )
2201 		return;
2202 
2203 	/* Fill in with rgb.
2204 	 */
2205 	im_initdesc( in->im, 1, 1, 3,
2206 		IM_BBITS_BYTE, IM_BANDFMT_UCHAR, IM_CODING_NONE,
2207 		IM_TYPE_sRGB, 1.0, 1.0, 0, 0 );
2208 	if( im_setupout( in->im ) )
2209 		return;
2210 	for( i = 0; i < 3; i++ )
2211 		((PEL *) in->im->data)[i] = IM_RINT( rgb[i] * 255.0 );
2212 
2213 	/* To imageinfo->type. Make sure we get a float ... except for LABQ
2214 	 * and RAD.
2215 	 */
2216 	if( im->Coding == IM_CODING_LABQ ) {
2217 		if( im_disp2Lab( in->im, t1, display ) ||
2218 			im_Lab2LabQ( t1, out->im ) )
2219 			return;
2220 	}
2221 	else if( im->Coding == IM_CODING_RAD ) {
2222 		if( im_disp2XYZ( in->im, t1, display ) ||
2223 			im_float2rad( t1, out->im ) )
2224 			return;
2225 	}
2226 	else if( im->Coding == IM_CODING_NONE ) {
2227 		switch( im->Type ) {
2228 		case IM_TYPE_XYZ:
2229 			if( im_disp2XYZ( in->im, out->im, display ) )
2230 				return;
2231 			break;
2232 
2233 		case IM_TYPE_YXY:
2234 			if( im_disp2XYZ( in->im, t1, display ) ||
2235 				im_XYZ2Yxy( t1, out->im ) )
2236 				return;
2237 			break;
2238 
2239 		case IM_TYPE_LAB:
2240 			if( im_disp2Lab( in->im, out->im, display ) )
2241 				return;
2242 			break;
2243 
2244 		case IM_TYPE_LCH:
2245 			if( im_disp2Lab( in->im, t1, display ) ||
2246 				im_Lab2LCh( t1, out->im ) )
2247 				return;
2248 			break;
2249 
2250 		case IM_TYPE_UCS:
2251 			if( im_disp2Lab( in->im, t1, display ) ||
2252 				im_Lab2LCh( t1, t2 ) ||
2253 				im_LCh2UCS( t2, out->im ) )
2254 				return;
2255 			break;
2256 
2257 		case IM_TYPE_RGB16:
2258 		case IM_TYPE_GREY16:
2259 			if( im_lintra( 1.0 / 256.0, in->im, 0.0, out->im ) )
2260 				return;
2261 			break;
2262 
2263 		case IM_TYPE_RGB:
2264 		case IM_TYPE_sRGB:
2265 		default:
2266 			if( im_clip2fmt( in->im, out->im, IM_BANDFMT_FLOAT ) )
2267 				return;
2268 			break;
2269 		}
2270 	}
2271 
2272 #define SET( TYPE, i ) ((TYPE *) im->data)[i] = ((float *) out->im->data)[i];
2273 
2274 	/* Now ... overwrite imageinfo.
2275 	 */
2276 	if( im->Coding == IM_CODING_LABQ ||
2277 		im->Coding == IM_CODING_RAD ) {
2278 		for( i = 0; i < im->Bands; i++ )
2279 			((PEL *) im->data)[i] = ((PEL *) out->im->data)[i];
2280 	}
2281 	else {
2282 		for( i = 0; i < im->Bands; i++ )
2283 			switch( im->BandFmt ) {
2284 			case IM_BANDFMT_UCHAR:
2285 				SET( unsigned char, i );
2286 				break;
2287 
2288 			case IM_BANDFMT_CHAR:
2289 				SET( signed char, i );
2290 				break;
2291 
2292 			case IM_BANDFMT_USHORT:
2293 				SET( unsigned short, i );
2294 				break;
2295 
2296 			case IM_BANDFMT_SHORT:
2297 				SET( signed short, i );
2298 				break;
2299 
2300 			case IM_BANDFMT_UINT:
2301 				SET( unsigned int, i );
2302 				break;
2303 
2304 			case IM_BANDFMT_INT:
2305 				SET( signed int, i );
2306 				break;
2307 
2308 			case IM_BANDFMT_FLOAT:
2309 				SET( float, i );
2310 				break;
2311 
2312 			case IM_BANDFMT_DOUBLE:
2313 				SET( double, i );
2314 				break;
2315 
2316 			case IM_BANDFMT_COMPLEX:
2317 				SET( float, i * 2 );
2318 				SET( float, i * 2 + 1 );
2319 				break;
2320 
2321 			case IM_BANDFMT_DPCOMPLEX:
2322 				SET( double, i * 2 );
2323 				SET( double, i * 2 + 1 );
2324 				break;
2325 
2326 			default:
2327 				g_assert( FALSE );
2328 			}
2329 	}
2330 	im_invalidate( im );
2331 
2332 #ifdef DEBUG_RGB
2333 {
2334 	char txt[256];
2335 	VipsBuf buf = VIPS_BUF_STATIC( txt );
2336 
2337 	printf( "imageinfo_from_rgb: out: " );
2338 	imageinfo_to_text( imageinfo, &buf );
2339 	printf( "%s\n", vips_buf_all( &buf ) );
2340 }
2341 #endif /*DEBUG_RGB*/
2342 
2343 	dirty.left = 0;
2344 	dirty.top = 0;
2345 	dirty.width = 1;
2346 	dirty.height = 1;
2347 	imageinfo_area_painted( imageinfo, &dirty );
2348 }
2349 
2350 /* Widgets for colour edit.
2351  */
2352 typedef struct _ColourEdit {
2353 	iDialog *idlg;
2354 
2355 	Imageinfo *imageinfo;
2356 	GtkWidget *colour_widget;
2357 } ColourEdit;
2358 
2359 /* Done button hit.
2360  */
2361 static void
imageinfo_colour_done_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)2362 imageinfo_colour_done_cb( iWindow *iwnd, void *client,
2363 	iWindowNotifyFn nfn, void *sys )
2364 {
2365 	ColourEdit *eds = (ColourEdit *) client;
2366 	Imageinfo *imageinfo = eds->imageinfo;
2367 	double rgb[4];
2368 
2369 	gtk_color_selection_get_color(
2370 		GTK_COLOR_SELECTION( eds->colour_widget ), rgb );
2371 
2372 	/* This will emit "area_painted" on our imageinfo.
2373 	 */
2374 	imageinfo_from_rgb( imageinfo, rgb );
2375 
2376 	nfn( sys, IWINDOW_YES );
2377 }
2378 
2379 /* Build the insides of colour edit.
2380  */
2381 static void
imageinfo_colour_buildedit(iDialog * idlg,GtkWidget * work,ColourEdit * eds)2382 imageinfo_colour_buildedit( iDialog *idlg, GtkWidget *work, ColourEdit *eds )
2383 {
2384 	Imageinfo *imageinfo = eds->imageinfo;
2385 	double rgb[4];
2386 
2387 	eds->colour_widget = gtk_color_selection_new();
2388 	gtk_color_selection_set_has_opacity_control(
2389 		GTK_COLOR_SELECTION( eds->colour_widget ), FALSE );
2390 	imageinfo_to_rgb( imageinfo, rgb );
2391 	gtk_color_selection_set_color(
2392 		GTK_COLOR_SELECTION( eds->colour_widget ), rgb );
2393         gtk_box_pack_start( GTK_BOX( work ),
2394 		eds->colour_widget, TRUE, TRUE, 2 );
2395 
2396         gtk_widget_show_all( work );
2397 }
2398 
2399 void
imageinfo_colour_edit(GtkWidget * parent,Imageinfo * imageinfo)2400 imageinfo_colour_edit( GtkWidget *parent, Imageinfo *imageinfo )
2401 {
2402 	ColourEdit *eds = INEW( NULL, ColourEdit );
2403 	GtkWidget *idlg;
2404 
2405 	eds->imageinfo = imageinfo;
2406 
2407 	idlg = idialog_new();
2408 	iwindow_set_title( IWINDOW( idlg ), "Edit Colour" );
2409 	idialog_set_build( IDIALOG( idlg ),
2410 		(iWindowBuildFn) imageinfo_colour_buildedit, eds, NULL, NULL );
2411 	idialog_set_callbacks( IDIALOG( idlg ),
2412 		iwindow_true_cb, NULL, idialog_free_client, eds );
2413 	idialog_add_ok( IDIALOG( idlg ),
2414 		imageinfo_colour_done_cb, "Set Colour" );
2415 	iwindow_set_parent( IWINDOW( idlg ), parent );
2416 	idialog_set_iobject( IDIALOG( idlg ), IOBJECT( imageinfo ) );
2417 	iwindow_build( IWINDOW( idlg ) );
2418 
2419 	gtk_widget_show( GTK_WIDGET( idlg ) );
2420 }
2421