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