1 /* read and write masks
2  */
3 
4 /* Copyright: 1990, N. Dessipris.
5  *
6  * Author: Nicos Dessipris
7  * Written on: 29/04/1991
8  * Modified on: 10/8/1992, J.Cupitt
9  *    -	Mask reading routines no longer fail if scale and offset are missing.
10  *	Instead, they set default values of scale=1, offset=0.
11  *    - Code tidied up, better error recovery.
12  *    -	Bugs fixed in im_dup_*mask. No longer coredump.
13  *    -	Bugs fixed in im_write_*mask. Now work for non-square matricies.
14  *    -	im_copy_*mask_matrix, im_copy_matrix_*mask added: copy VIPS mask
15  *	structures into Numerical Recipies in C style matricies and vice
16  *	versa. Both structures should have been built before copy attempted.
17  *	See im_create_*mask, im_*mat_alloc. The matrix should be indexed by 0
18  *	to size-1.
19  * 9/7/93 JC
20  *    - some ANSIfication and tidies
21  *    -	im_free_*mask() now return zero, so they can be used as close
22  *	callbacks.
23  * 7/10/94 JC
24  *    - new IM_NEW(), IM_ARRAY() macros added
25  * 27/4/95 JC
26  *    -	oops! forgot to init IM_ARRAY() memory to zero
27  * 7/8/96 JC
28  *    - im_scale_dmask() rewritten
29  * 7/5/98 JC
30  *    - im_read_*mask() rewritten, now more robust
31  *    - im_write_*mask() rewritten
32  *    - new functions im_write_*mask_name()
33  * 28/7/99 JC
34  *    -	im_create_imaskv(), im_create_dmaskv() make masks and init from
35  *	varargs
36  *    - tabs allowed as column separators
37  * 9/2/05
38  *    - "," allowed as column separator ... helps CSV read
39  * 31/5/06
40  *    - use g_ascii_strtod() and friends
41  * 2006-09-08 tcv
42  *    - add im_norm_dmask()
43  * 1/9/09
44  * 	- move im_print_*mask() here
45  * 12/11/09
46  * 	- reading a float mask with im_read_imask() produced an incorrect
47  * 	  error messagge
48  * 21/10/10
49  * 	- gtk-doc
50  * 	- you can use commas to separate header fields
51  * 	- small cleanups
52  * 30/11/10
53  * 	- im_scale_dmask() normalises to 20, not 100 ... we hit the fast
54  * 	  conv path more often
55  * 24/1/10
56  * 	- oops, missing braces in write dmask removed spaces between items
57  */
58 
59 /*
60 
61     This file is part of VIPS.
62 
63     VIPS is free software; you can redistribute it and/or modify
64     it under the terms of the GNU Lesser General Public License as published by
65     the Free Software Foundation; either version 2 of the License, or
66     (at your option) any later version.
67 
68     This program is distributed in the hope that it will be useful,
69     but WITHOUT ANY WARRANTY; without even the implied warranty of
70     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
71     GNU Lesser General Public License for more details.
72 
73     You should have received a copy of the GNU Lesser General Public License
74     along with this program; if not, write to the Free Software
75     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
76     02110-1301  USA
77 
78  */
79 
80 /*
81 
82     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
83 
84  */
85 
86 #ifdef HAVE_CONFIG_H
87 #include <config.h>
88 #endif /*HAVE_CONFIG_H*/
89 #include <vips/intl.h>
90 
91 #include <stdio.h>
92 #include <stdlib.h>
93 #include <stdarg.h>
94 #include <string.h>
95 #include <math.h>
96 
97 #include <vips/vips.h>
98 #include <vips/vips7compat.h>
99 
100 /**
101  * INTMASK:
102  * @xsize: mask width
103  * @ysize: mask height
104  * @scale: mask scale factor
105  * @offset: mask offset
106  * @coeff: array of mask elements
107  * @filename: the file this mask was read from, or should be written to
108  *
109  * An integer mask.
110  *
111  * @scale lets the mask represent fractional values: for
112  * example, in integer convolution (see im_conv()) the result of the
113  * convolution is divided by @scale and then added to @offset before being
114  * written to the output image.
115  *
116  * @scale and @offset default to 1 and 0. Various functions, such as
117  * im_conv(), will fail if @scale is zero.
118  *
119  * You can read and write the matrix elements in @coeff.
120  */
121 
122 /**
123  * DOUBLEMASK:
124  * @xsize: mask width
125  * @ysize: mask height
126  * @scale: mask scale factor
127  * @offset: mask offset
128  * @coeff: array of mask elements
129  * @filename: the file this mask was read from, or should be written to
130  *
131  * A floating-point mask.
132  *
133  * As with #INTMASK, in convolution (see im_convf()) the result of the
134  * convolution is divided by @scale and then added to @offset before being
135  * written to the output image.
136  *
137  * @scale and @offset default to 1.0 and 0.0. Various functions, such as
138  * im_conv(), will fail if @scale is zero.
139  *
140  * You can read and write the matrix elements in @coeff.
141  */
142 
143 /* Size of line buffer for reading.
144  */
145 #define MAX_LINE (32768)
146 
147 /**
148  * im_free_imask:
149  * @in: mask to free
150  *
151  * Free mask structure and any attached arrays. Return zero, so we can use
152  * these functions as close callbacks.
153  *
154  * See also: im_free_dmask().
155  *
156  * Returns: zero.
157  */
158 int
im_free_imask(INTMASK * in)159 im_free_imask( INTMASK *in )
160 {
161 	if( !in )
162 		return( 0 );
163 
164 	IM_FREE( in->coeff );
165 	IM_FREE( in->filename );
166 	IM_FREE( in );
167 
168 	return( 0 );
169 }
170 
171 /**
172  * im_free_dmask:
173  * @in: mask to free
174  *
175  * Free mask structure and any attached arrays. Return zero, so we can use
176  * these functions as close callbacks.
177  *
178  * See also: im_free_dmask().
179  *
180  * Returns: zero.
181  */
182 int
im_free_dmask(DOUBLEMASK * in)183 im_free_dmask( DOUBLEMASK *in )
184 {
185 	if( !in )
186 		return( 0 );
187 
188 	IM_FREE( in->coeff );
189 	IM_FREE( in->filename );
190 	IM_FREE( in );
191 
192 	return( 0 );
193 }
194 
195 /**
196  * im_create_imask:
197  * @filename: set mask filename to this
198  * @xsize: mask width
199  * @ysize: mask height
200  *
201  * Create an empty imask. You need to loop over @coeff to set the values.
202  *
203  * See also: im_create_imaskv().
204  *
205  * Returns: The newly-allocated mask.
206  */
207 INTMASK *
im_create_imask(const char * filename,int xsize,int ysize)208 im_create_imask( const char *filename, int xsize, int ysize )
209 {
210 	INTMASK *out;
211 	int size = xsize * ysize;
212 
213 	/* Check args.
214 	 */
215 	if( xsize <= 0 || ysize <= 0 || filename == NULL ) {
216 		im_error( "im_create_imask", "%s", _( "bad arguments" ) );
217 		return( NULL );
218 	}
219 
220 	/* Allocate and initialise structure.
221 	 */
222 	if( !(out = IM_NEW( NULL, INTMASK )) )
223 		return( NULL );
224 	out->coeff = NULL;
225 	out->filename = NULL;
226 	out->scale = 1;
227 	out->offset = 0;
228 	out->xsize = 0;
229 	out->ysize = 0;
230 
231 	if( !(out->coeff = IM_ARRAY( NULL, size, int )) ) {
232 		im_free_imask( out );
233 		return( NULL );
234 	}
235 	(void) memset( (char *) out->coeff, 0, size * sizeof( int ) );
236 	if( !(out->filename = im_strdup( NULL, filename )) ) {
237 		im_free_imask( out );
238 		return( NULL );
239 	}
240 	out->xsize = xsize;
241 	out->ysize = ysize;
242 
243 	return( out );
244 }
245 
246 /**
247  * im_create_imaskv:
248  * @filename: set mask filename to this
249  * @xsize: mask width
250  * @ysize: mask height
251  * @...: values to set for the mask
252  *
253  * Create an imask and initialise it from the function parameter list.
254  *
255  * See also: im_create_imask().
256  *
257  * Returns: The newly-allocated mask.
258  */
259 INTMASK *
im_create_imaskv(const char * filename,int xsize,int ysize,...)260 im_create_imaskv( const char *filename, int xsize, int ysize, ... )
261 {
262 	va_list ap;
263 
264 	INTMASK *out;
265 	int i;
266 
267 	if( !(out = im_create_imask( filename, xsize, ysize )) )
268 		return( NULL );
269 
270 	va_start( ap, ysize );
271 	for( i = 0; i < xsize * ysize; i++ )
272 		out->coeff[i] = va_arg( ap, int );
273 	va_end( ap );
274 
275 	return( out );
276 }
277 
278 /**
279  * im_create_dmask:
280  * @filename: set mask filename to this
281  * @xsize: mask width
282  * @ysize: mask height
283  *
284  * Create an empty dmask. You need to loop over @coeff to set the values.
285  *
286  * See also: im_create_dmaskv(), im_vips2mask().
287  *
288  * Returns: The newly-allocated mask.
289  */
290 DOUBLEMASK *
im_create_dmask(const char * filename,int xsize,int ysize)291 im_create_dmask( const char *filename, int xsize, int ysize )
292 {
293 	DOUBLEMASK *out;
294 	int size = xsize * ysize;
295 
296 	/* Check args.
297 	 */
298 	if( xsize <= 0 || ysize <= 0 || filename == NULL ) {
299 		im_error( "im_create_dmask", "%s", _( "bad arguments" ) );
300 		return( NULL );
301 	}
302 
303 	/* Allocate and initialise structure.
304 	 */
305 	if( !(out = IM_NEW( NULL, DOUBLEMASK )) )
306 		return( NULL );
307 	out->coeff = NULL;
308 	out->filename = NULL;
309 	out->scale = 1.0;
310 	out->offset = 0.0;
311 	out->xsize = 0;
312 	out->ysize = 0;
313 
314 	if( !(out->coeff = IM_ARRAY( NULL, size, double )) ) {
315 		im_free_dmask( out );
316 		return( NULL );
317 	}
318 	(void) memset( (char *) out->coeff, 0, size * sizeof( double ) );
319 	if( !(out->filename = im_strdup( NULL, filename )) ) {
320 		im_free_dmask( out );
321 		return( NULL );
322 	}
323 	out->xsize = xsize;
324 	out->ysize = ysize;
325 
326 	return( out );
327 }
328 
329 /**
330  * im_create_dmaskv:
331  * @filename: set mask filename to this
332  * @xsize: mask width
333  * @ysize: mask height
334  * @...: values to set for the mask
335  *
336  * Create a dmask and initialise it from the function parameter list.
337  *
338  * See also: im_create_dmask().
339  *
340  * Returns: The newly-allocated mask.
341  */
342 DOUBLEMASK *
im_create_dmaskv(const char * filename,int xsize,int ysize,...)343 im_create_dmaskv( const char *filename, int xsize, int ysize, ... )
344 {
345 	va_list ap;
346 
347 	DOUBLEMASK *out;
348 	int i;
349 
350 	if( !(out = im_create_dmask( filename, xsize, ysize )) )
351 		return( NULL );
352 
353 	va_start( ap, ysize );
354 	for( i = 0; i < xsize * ysize; i++ )
355 		out->coeff[i] = va_arg( ap, double );
356 	va_end( ap );
357 
358 	return( out );
359 }
360 
361 /* Read a line from a file!
362  */
363 static int
get_line(FILE * fp,char * buf)364 get_line( FILE *fp, char *buf )
365 {
366 	if( !fgets( buf, MAX_LINE, fp ) ) {
367 		im_error( "read_mask", "%s", _( "unexpected EOF" ) );
368 		return( -1 );
369 	}
370 
371 	return( 0 );
372 }
373 
374 /* width, height, optional scale, optional offset.
375  */
376 static int
read_header(FILE * fp,int * xs,int * ys,double * scale,double * offset)377 read_header( FILE *fp, int *xs, int *ys, double *scale, double *offset )
378 {
379 	char buf[MAX_LINE];
380 	char *p, *q;
381 	double v[4];
382 	int i;
383 
384 	/* Read the first line: should contain size and optional
385 	 * scale + offset.
386 	 */
387 	if( get_line( fp, buf ) )
388 		return( -1 );
389 
390 	/* Read as space separated doubles. \n is in the break list because
391 	 * our line will (usually) have a trailing \n which we want to count
392 	 * as whitespace.
393 	 */
394 	p = buf;
395 	for( i = 0, p = buf;
396 		i < 4 && (q = im_break_token( p, " \";,\t\n" ));
397 		i++, p = q )
398 		v[i] = g_ascii_strtod( p, NULL );
399 
400 	if( (i != 2 && i != 4) ||
401 		ceil( v[0] ) != v[0] ||
402 		ceil( v[1] ) != v[1] ||
403 		v[0] <= 0 ||
404 		v[1] <= 0 ) {
405 		im_error( "read_header",
406 			"%s", _( "error reading matrix header" ) );
407 		return( -1 );
408 	}
409 	if( i == 4 && v[2] == 0 ) {
410 		im_error( "read_header",
411 			"%s", _( "scale should be non-zero" ) );
412 		return( -1 );
413 	}
414 
415 	*xs = v[0];
416 	*ys = v[1];
417 	if( i == 2 ) {
418 		*scale = 1.0;
419 		*offset = 0.0;
420 	}
421 	else {
422 		*scale = v[2];
423 		*offset = v[3];
424 	}
425 
426 	return( 0 );
427 }
428 
429 /**
430  * im_read_dmask:
431  * @filename: read matrix from this file
432  *
433  * Reads a matrix from a file.
434  *
435  * Matrix files have a simple format that's supposed to be easy to create with
436  * a text editor or a spreadsheet.
437  *
438  * The first line has four numbers for width, height, scale and
439  * offset (scale and offset may be omitted, in which case they default to 1.0
440  * and 0.0). Scale must be non-zero. Width and height must be positive
441  * integers. The numbers are separated by any mixture of spaces, commas,
442  * tabs and quotation marks ("). The scale and offset fields may be
443  * floating-point, and must use '.'
444  * as a decimal separator.
445  *
446  * Subsequent lines each hold one line of matrix data, with numbers again
447  * separated by any mixture of spaces, commas,
448  * tabs and quotation marks ("). The numbers may be floating-point, and must
449  * use '.'
450  * as a decimal separator.
451  *
452  * Extra characters at the ends of lines or at the end of the file are
453  * ignored.
454  *
455  * See also: im_read_imask(), im_gauss_dmask().
456  *
457  * Returns: the loaded mask on success, or NULL on error.
458  */
459 DOUBLEMASK *
im_read_dmask(const char * filename)460 im_read_dmask( const char *filename )
461 {
462 	FILE *fp;
463 	double sc, off;
464 	int xs, ys;
465 	DOUBLEMASK *out;
466 	int x, y, i;
467 	char buf[MAX_LINE];
468 
469 	if( !(fp = im__file_open_read( filename, NULL, TRUE )) )
470 		return( NULL );
471 
472 	if( read_header( fp, &xs, &ys, &sc, &off ) ) {
473 		fclose( fp );
474 		return( NULL );
475 	}
476 
477 	if( !(out = im_create_dmask( filename, xs, ys )) ) {
478 		fclose( fp );
479 		return( NULL );
480 	}
481 	out->scale = sc;
482 	out->offset = off;
483 
484 	for( i = 0, y = 0; y < ys; y++ ) {
485 		char *p;
486 
487 		if( get_line( fp, buf ) ) {
488 			im_free_dmask( out );
489 			fclose( fp );
490 			return( NULL );
491 		}
492 
493 		for( p = buf, x = 0; p && x < xs;
494 			x++, i++, p = im_break_token( p, " \t,\";" ) )
495 			out->coeff[i] = g_ascii_strtod( p, NULL );
496 	}
497 	fclose( fp );
498 
499 	return( out );
500 }
501 
502 /**
503  * im_read_imask:
504  * @filename: read matrix from this file
505  *
506  * Reads an integer matrix from a file.
507  *
508  * This function works exactly as im_read_dmask(), but the loaded matrix is
509  * checked for 'int-ness'. All coefficients must be integers, and scale and
510  * offset must be integers.
511  *
512  * See also: im_read_dmask().
513  *
514  * Returns: the loaded mask on success, or NULL on error.
515  */
516 INTMASK *
im_read_imask(const char * filename)517 im_read_imask( const char *filename )
518 {
519 	DOUBLEMASK *dmask;
520 	INTMASK *imask;
521 	int i;
522 
523 	if( !(dmask = im_read_dmask( filename )) )
524 		return( NULL );
525 
526 	if( ceil( dmask->scale ) != dmask->scale ||
527 		ceil( dmask->offset ) != dmask->offset ) {
528 		im_error( "im_read_imask",
529 			"%s", _( "scale and offset should be int" ) );
530 		im_free_dmask( dmask );
531 
532 		return( NULL );
533 	}
534 
535 	for( i = 0; i < dmask->xsize * dmask->ysize; i++ )
536 		if( ceil( dmask->coeff[i] ) != dmask->coeff[i] ) {
537 			im_error( "im_read_imask", _( "ceofficient at "
538 				"position (%d, %d) is not int" ),
539 				i % dmask->xsize,
540 				i / dmask->xsize );
541 			im_free_dmask( dmask );
542 
543 			return( NULL );
544 		}
545 
546 	if( !(imask = im_create_imask( filename,
547 		dmask->xsize, dmask->ysize )) ) {
548 		im_free_dmask( dmask );
549 		return( NULL );
550 	}
551 	imask->scale = dmask->scale;
552 	imask->offset = dmask->offset;
553 	for( i = 0; i < dmask->xsize * dmask->ysize; i++ )
554 		imask->coeff[i] = dmask->coeff[i];
555 
556 	im_free_dmask( dmask );
557 
558 	return( imask );
559 }
560 
561 /**
562  * im_scale_dmask:
563  * @in: mask to scale
564  * @filename: filename for returned mask
565  *
566  * Scale the dmask to make an imask with a maximum value of 20.
567  *
568  * See also: im_norm_dmask().
569  *
570  * Returns: the converted mask, or NULL on error.
571  */
572 INTMASK *
im_scale_dmask(DOUBLEMASK * in,const char * filename)573 im_scale_dmask( DOUBLEMASK *in, const char *filename )
574 {
575 	const int size = in->xsize * in->ysize;
576 
577 	INTMASK *out;
578 	double maxval, dsum;
579 	int i;
580 	int isum;
581 
582 	if( im_check_dmask( "im_scale_dmask", in ) ||
583 		!(out = im_create_imask( filename, in->xsize, in->ysize )) )
584 		return( NULL );
585 
586 	/* Find mask max.
587 	 */
588 	maxval = in->coeff[0];
589 	for( i = 0; i < size; i++ )
590 		if( in->coeff[i] > maxval )
591 			maxval = in->coeff[i];
592 
593 	/* Copy and scale, setting max to 20.
594 	 */
595 	for( i = 0; i < size; i++ )
596 		out->coeff[i] = IM_RINT( in->coeff[i] * 20.0 / maxval );
597 	out->offset = in->offset;
598 
599 	/* Set the scale to match the adjustment to max.
600 	 */
601 	isum = 0;
602 	dsum = 0.0;
603 	for( i = 0; i < size; i++ ) {
604 		isum += out->coeff[i];
605 		dsum += in->coeff[i];
606 	}
607 
608 	if( dsum == in->scale )
609 		out->scale = isum;
610 	else if( dsum == 0.0 )
611 		out->scale = 1.0;
612 	else
613 		out->scale = IM_RINT( in->scale * isum / dsum );
614 
615 	return( out );
616 }
617 
618 /**
619  * im_dmask2imask:
620  * @in: mask to convert
621  * @filename: filename for returned mask
622  *
623  * Make an imask from the dmask, rounding to nearest.
624  *
625  * See also: im_scale_dmask().
626  *
627  * Returns: the converted mask, or NULL on error.
628  */
629 INTMASK *
im_dmask2imask(DOUBLEMASK * in,const char * filename)630 im_dmask2imask( DOUBLEMASK *in, const char *filename )
631 {
632 	const int size = in->xsize * in->ysize;
633 
634 	INTMASK *out;
635 	int i;
636 
637 	if( im_check_dmask( "im_dmask2imask", in ) ||
638 		!(out = im_create_imask( filename, in->xsize, in->ysize )) )
639 		return( NULL );
640 
641 	for( i = 0; i < size; i++ )
642 		out->coeff[i] = IM_RINT( in->coeff[i] );
643 	out->offset = IM_RINT( in->offset );
644 	out->scale = IM_RINT( in->scale );
645 
646 	return( out );
647 }
648 
649 /**
650  * im_imask2dmask:
651  * @in: mask to convert
652  * @filename: filename for returned mask
653  *
654  * Make a dmask from the imask.
655  *
656  * See also: im_dmask2imask().
657  *
658  * Returns: the converted mask, or NULL on error.
659  */
660 DOUBLEMASK *
im_imask2dmask(INTMASK * in,const char * filename)661 im_imask2dmask( INTMASK *in, const char *filename )
662 {
663 	const int size = in->xsize * in->ysize;
664 
665 	DOUBLEMASK *out;
666 	int i;
667 
668 	if( im_check_imask( "im_imask2dmask", in ) ||
669 		!(out = im_create_dmask( filename, in->xsize, in->ysize )) )
670 		return( NULL );
671 
672 	for( i = 0; i < size; i++ )
673 		out->coeff[i] = in->coeff[i];
674 	out->offset = in->offset;
675 	out->scale = in->scale;
676 
677 	return( out );
678 }
679 
680 /**
681  * im_norm_dmask:
682  * @mask: mask to scale
683  *
684  * Normalise the dmask. Apply the scale and offset to each element to make
685  * a mask with scale 1, offset zero.
686  *
687  * See also: im_scale_dmask().
688  *
689  * Returns: 0 on success, or -1 on error.
690  */
691 void
im_norm_dmask(DOUBLEMASK * mask)692 im_norm_dmask( DOUBLEMASK *mask )
693 {
694 	const int n = mask->xsize * mask->ysize;
695 	const double scale = (mask->scale == 0) ? 0 : (1.0 / mask->scale);
696 
697 	int i;
698 
699 	if( im_check_dmask( "im_norm_dmask", mask ) ||
700 		(1.0 == scale && 0.0 == mask->offset) )
701 		return;
702 
703 	for( i = 0; i < n; i++ )
704 		mask->coeff[i] = mask->coeff[i] * scale + mask->offset;
705 
706 	mask->scale = 1.0;
707 	mask->offset = 0.0;
708 }
709 
710 /**
711  * im_dup_imask:
712  * @in: mask to duplicate
713  * @filename: filename to set for the new mask
714  *
715  * Duplicate an imask.
716  *
717  * See also: im_dup_dmask().
718  *
719  * Returns: the mask copy, or NULL on error.
720  */
721 INTMASK *
im_dup_imask(INTMASK * in,const char * filename)722 im_dup_imask( INTMASK *in, const char *filename )
723 {
724 	INTMASK *out;
725 	int i;
726 
727 	if( im_check_imask( "im_dup_imask", in ) ||
728 		!(out = im_create_imask( filename, in->xsize, in->ysize )) )
729 		return( NULL );
730 
731         out->offset = in->offset;
732 	out->scale = in->scale;
733 
734         for( i = 0; i < in->xsize * in->ysize; i++ )
735 		out->coeff[i] = in->coeff[i];
736 
737         return( out );
738 }
739 
740 /**
741  * im_dup_dmask:
742  * @in: mask to duplicate
743  * @filename: filename to set for the new mask
744  *
745  * Duplicate a dmask.
746  *
747  * See also: im_dup_imask().
748  *
749  * Returns: the mask copy, or NULL on error.
750  */
751 DOUBLEMASK *
im_dup_dmask(DOUBLEMASK * in,const char * filename)752 im_dup_dmask( DOUBLEMASK *in, const char *filename )
753 {
754 	DOUBLEMASK *out;
755 	int i;
756 
757 	if( im_check_dmask( "im_dup_dmask", in ) ||
758 		!(out = im_create_dmask( filename, in->xsize, in->ysize )) )
759 		return( NULL );
760 
761         out->offset = in->offset;
762 	out->scale = in->scale;
763 
764         for( i = 0; i < in->xsize * in->ysize; i++ )
765 		out->coeff[i] = in->coeff[i];
766 
767         return( out );
768 }
769 
770 /* Write to file.
771  */
772 static int
write_line(FILE * fp,const char * fmt,...)773 write_line( FILE *fp, const char *fmt, ... )
774 {
775 	va_list ap;
776 
777 	va_start( ap, fmt );
778 	if( !vfprintf( fp, fmt, ap ) ) {
779 		im_error( "write_mask", "%s", _( "write error, disc full?" ) );
780 		return( -1 );
781 	}
782 	va_end( ap );
783 
784 	return( 0 );
785 }
786 
787 static int
write_double(FILE * fp,double d)788 write_double( FILE *fp, double d )
789 {
790 	char buf[G_ASCII_DTOSTR_BUF_SIZE];
791 
792 	fprintf( fp, "%s", g_ascii_dtostr( buf, sizeof( buf ), d ) );
793 
794 	return( 0 );
795 }
796 
797 /**
798  * im_write_imask_name:
799  * @in: mask to write
800  * @filename: filename to write to
801  *
802  * Write an imask to a file. See im_read_dmask() for a description of the mask
803  * file format.
804  *
805  * See also: im_write_imask().
806  *
807  * Returns: 0 on success, or -1 on error.
808  */
809 int
im_write_imask_name(INTMASK * in,const char * filename)810 im_write_imask_name( INTMASK *in, const char *filename )
811 {
812 	FILE *fp;
813 	int x, y, i;
814 
815 	if( im_check_imask( "im_write_imask_name", in ) ||
816 		!(fp = im__file_open_write( filename, TRUE )) )
817 		return( -1 );
818 
819 	if( write_line( fp, "%d %d", in->xsize, in->ysize ) ) {
820 		fclose( fp );
821 		return( -1 );
822 	}
823 	if( in->scale != 1 || in->offset != 0 )
824 		write_line( fp, " %d %d", in->scale, in->offset );
825 	write_line( fp, "\n" );
826 
827 	for( i = 0, y = 0; y < in->ysize; y++ ) {
828 		for( x = 0; x < in->xsize; x++, i++ )
829 			write_line( fp, "%d ", in->coeff[i] );
830 
831 		if( write_line( fp, "\n" ) ) {
832 			fclose( fp );
833 			return( -1 );
834 		}
835 	}
836 	fclose( fp );
837 
838 	return( 0 );
839 }
840 
841 /**
842  * im_write_imask:
843  * @in: mask to write
844  *
845  * Write an imask to a file.
846  *
847  * See also: im_write_imask_name().
848  *
849  * Returns: 0 on success, or -1 on error.
850  */
851 int
im_write_imask(INTMASK * in)852 im_write_imask( INTMASK *in )
853 {
854 	if( !in->filename ) {
855 		im_error( "im_write_imask", "%s", _( "filename not set" ) );
856 		return( -1 );
857 	}
858 
859 	return( im_write_imask_name( in, in->filename ) );
860 }
861 
862 /**
863  * im_write_dmask_name:
864  * @in: mask to write
865  * @filename: filename to write to
866  *
867  * Write a dmask to a file. See im_read_dmask() for a description of the mask
868  * file format.
869  *
870  * See also: im_write_dmask().
871  *
872  * Returns: 0 on success, or -1 on error.
873  */
874 int
im_write_dmask_name(DOUBLEMASK * in,const char * filename)875 im_write_dmask_name( DOUBLEMASK *in, const char *filename )
876 {
877 	FILE *fp;
878 	int x, y, i;
879 
880 	if( im_check_dmask( "im_write_dmask_name", in ) ||
881 		!(fp = im__file_open_write( filename, TRUE )) )
882 		return( -1 );
883 
884 	if( write_line( fp, "%d %d", in->xsize, in->ysize ) ) {
885 		fclose( fp );
886 		return( -1 );
887 	}
888 	if( in->scale != 1.0 || in->offset != 0.0 ) {
889 		write_line( fp, " " );
890 		write_double( fp, in->scale );
891 		write_line( fp, " " );
892 		write_double( fp, in->offset );
893 	}
894 	write_line( fp, "\n" );
895 
896 	for( i = 0, y = 0; y < in->ysize; y++ ) {
897 		for( x = 0; x < in->xsize; x++, i++ ) {
898 			write_double( fp, in->coeff[i] );
899 			write_line( fp, " " );
900 		}
901 
902 		if( write_line( fp, "\n" ) ) {
903 			fclose( fp );
904 			return( -1 );
905 		}
906 	}
907 	fclose( fp );
908 
909 	return( 0 );
910 }
911 
912 /**
913  * im_write_dmask:
914  * @in: mask to write
915  *
916  * Write a dmask to a file. See im_read_dmask() for a description of the mask
917  * file format.
918  *
919  * See also: im_write_dmask_name().
920  *
921  * Returns: 0 on success, or -1 on error.
922  */
923 int
im_write_dmask(DOUBLEMASK * in)924 im_write_dmask( DOUBLEMASK *in )
925 {
926 	if( !in->filename ) {
927 		im_error( "im_write_dmask", "%s", _( "filename not set" ) );
928 		return( -1 );
929 	}
930 
931 	return( im_write_dmask_name( in, in->filename ) );
932 }
933 
934 /* Copy an imask into a matrix. Only used internally by matrix package for
935  * invert.
936  */
937 void
im_copy_imask_matrix(INTMASK * mask,int ** matrix)938 im_copy_imask_matrix( INTMASK *mask, int **matrix )
939 {
940 	int x, y;
941 	int *p = mask->coeff;
942 
943 	for( y = 0; y < mask->ysize; y++ )
944 		for( x = 0; x < mask->xsize; x++ )
945 			matrix[x][y] = *p++;
946 }
947 
948 
949 /* Copy a matrix into an imask.
950  */
951 void
im_copy_matrix_imask(int ** matrix,INTMASK * mask)952 im_copy_matrix_imask( int **matrix, INTMASK *mask )
953 {
954 	int x, y;
955 	int *p = mask->coeff;
956 
957 	for( y = 0; y < mask->ysize; y++ )
958 		for( x = 0; x < mask->xsize; x++ )
959 			*p++ = matrix[x][y];
960 }
961 
962 /* Copy a dmask into a matrix.
963  */
964 void
im_copy_dmask_matrix(DOUBLEMASK * mask,double ** matrix)965 im_copy_dmask_matrix( DOUBLEMASK *mask, double **matrix )
966 {
967 	int x, y;
968 	double *p = mask->coeff;
969 
970 	for( y = 0; y < mask->ysize; y++ )
971 		for( x = 0; x < mask->xsize; x++ )
972 			matrix[x][y] = *p++;
973 }
974 
975 /* Copy a matrix to a dmask.
976  */
977 void
im_copy_matrix_dmask(double ** matrix,DOUBLEMASK * mask)978 im_copy_matrix_dmask( double **matrix, DOUBLEMASK *mask )
979 {
980 	int x, y;
981 	double *p = mask->coeff;
982 
983 	for( y = 0; y < mask->ysize; y++ )
984 		for( x = 0; x < mask->xsize; x++ )
985 			*p++ = matrix[x][y];
986 }
987 
988 /**
989  * im_print_imask:
990  * @in: mask to print
991  *
992  * Print an imask to stdout.
993  *
994  * See also: im_print_dmask().
995  */
996 void
im_print_imask(INTMASK * in)997 im_print_imask( INTMASK *in )
998 {
999         int i, j, k;
1000 
1001         printf( "%s: %d %d %d %d\n",
1002 		in->filename, in->xsize, in->ysize, in->scale, in->offset );
1003 
1004         for( k = 0, j = 0; j < in->ysize; j++ ) {
1005                 for( i = 0; i < in->xsize; i++, k++ )
1006                         printf( "%d\t", in->coeff[k] );
1007 
1008                 printf( "\n" );
1009 	}
1010 }
1011 
1012 /**
1013  * im_print_dmask:
1014  * @in: mask to print
1015  *
1016  * Print a dmask to stdout.
1017  *
1018  * See also: im_print_imask().
1019  */
1020 void
im_print_dmask(DOUBLEMASK * in)1021 im_print_dmask( DOUBLEMASK *in )
1022 {
1023         int i, j, k;
1024 
1025         printf( "%s: %d %d %f %f\n",
1026 		in->filename, in->xsize, in->ysize, in->scale, in->offset );
1027 
1028         for( k = 0, j = 0; j < in->ysize; j++ ) {
1029                 for( i = 0; i < in->xsize; i++, k++ )
1030                         printf( "%f\t", in->coeff[k] );
1031 
1032                 printf( "\n" );
1033 	}
1034 }
1035 
1036 /**
1037  * im_local_dmask:
1038  * @out: image to make the mask local to
1039  * @mask: mask to local-ize
1040  *
1041  * @out takes ownership of @mask: when @out is closed, @mask will be closed
1042  * for you. If im_local_dmask() itself fails, the mask is also freed.
1043  *
1044  * See also: im_local_imask().
1045  *
1046  * Returns: the mask, or NULL on error.
1047  */
1048 DOUBLEMASK *
im_local_dmask(VipsImage * out,DOUBLEMASK * mask)1049 im_local_dmask( VipsImage *out, DOUBLEMASK *mask )
1050 {
1051 	if( im_check_dmask( "im_local_dmask", mask ) )
1052 		return( NULL );
1053 
1054 	if( im_add_close_callback( out,
1055 		(im_callback_fn) im_free_dmask, mask, NULL ) ) {
1056 		im_free_dmask( mask );
1057 		return( NULL );
1058 	}
1059 
1060 	return( mask );
1061 }
1062 
1063 /**
1064  * im_local_imask:
1065  * @out: image to make the mask local to
1066  * @mask: mask to local-ize
1067  *
1068  * @out takes ownership of @mask: when @out is closed, @mask will be closed
1069  * for you. If im_local_imask() itself fails, the mask is also freed.
1070  *
1071  * See also: im_local_dmask().
1072  *
1073  * Returns: the mask, or NULL on error.
1074  */
1075 INTMASK *
im_local_imask(VipsImage * out,INTMASK * mask)1076 im_local_imask( VipsImage *out, INTMASK *mask )
1077 {
1078 	if( im_check_imask( "im_local_dmask", mask ) )
1079 		return( NULL );
1080 
1081 	if( im_add_close_callback( out,
1082 		(im_callback_fn) im_free_imask, mask, NULL ) ) {
1083 		im_free_imask( mask );
1084 		return( NULL );
1085 	}
1086 
1087 	return( mask );
1088 }
1089