1 /* load matrix from a file
2  *
3  * 5/12/11
4  * 	- from csvload.c
5  * 22/2/20
6  * 	- rewrite for source API
7  */
8 
9 /*
10 
11     This file is part of VIPS.
12 
13     VIPS is free software; you can redistribute it and/or modify
14     it under the terms of the GNU Lesser General Public License as published by
15     the Free Software Foundation; either version 2 of the License, or
16     (at your option) any later version.
17 
18     This program is distributed in the hope that it will be useful,
19     but WITHOUT ANY WARRANTY; without even the implied warranty of
20     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21     GNU Lesser General Public License for more details.
22 
23     You should have received a copy of the GNU Lesser General Public License
24     along with this program; if not, write to the Free Software
25     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26     02110-1301  USA
27 
28  */
29 
30 /*
31 
32     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
33 
34  */
35 
36 /*
37 #define DEBUG
38  */
39 
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif /*HAVE_CONFIG_H*/
43 #include <vips/intl.h>
44 
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 
49 #include <vips/vips.h>
50 #include <vips/buf.h>
51 #include <vips/internal.h>
52 
53 #include "pforeign.h"
54 
55 typedef struct _VipsForeignLoadMatrix {
56 	VipsForeignLoad parent_object;
57 
58 	/* Set by subclasses.
59 	 */
60 	VipsSource *source;
61 
62 	/* Buffered source.
63 	 */
64 	VipsSbuf *sbuf;
65 
66 	/* A line of pixels.
67 	 */
68 	double *linebuf;
69 
70 } VipsForeignLoadMatrix;
71 
72 typedef VipsForeignLoadClass VipsForeignLoadMatrixClass;
73 
74 G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadMatrix, vips_foreign_load_matrix,
75 	VIPS_TYPE_FOREIGN_LOAD );
76 
77 static void
vips_foreign_load_matrix_dispose(GObject * gobject)78 vips_foreign_load_matrix_dispose( GObject *gobject )
79 {
80 	VipsForeignLoadMatrix *matrix = (VipsForeignLoadMatrix *) gobject;
81 
82 	VIPS_UNREF( matrix->source );
83 	VIPS_UNREF( matrix->sbuf );
84 	VIPS_FREE( matrix->linebuf );
85 
86 	G_OBJECT_CLASS( vips_foreign_load_matrix_parent_class )->
87 		dispose( gobject );
88 }
89 
90 static int
vips_foreign_load_matrix_build(VipsObject * object)91 vips_foreign_load_matrix_build( VipsObject *object )
92 {
93 	VipsForeignLoadMatrix *matrix = (VipsForeignLoadMatrix *) object;
94 
95 	if( !(matrix->sbuf = vips_sbuf_new_from_source( matrix->source )) )
96 		return( -1 );
97 
98 	if( VIPS_OBJECT_CLASS( vips_foreign_load_matrix_parent_class )->
99 		build( object ) )
100 		return( -1 );
101 
102 	return( 0 );
103 }
104 
105 static VipsForeignFlags
vips_foreign_load_matrix_get_flags(VipsForeignLoad * load)106 vips_foreign_load_matrix_get_flags( VipsForeignLoad *load )
107 {
108 	return( 0 );
109 }
110 
111 /* Parse a header line. Two numbers for width and height, and two optional
112  * numbers for scale and offset.
113  *
114  * We can have scale and no offset, in which case we assume offset = 0.
115  */
116 static int
parse_matrix_header(char * line,int * width,int * height,double * scale,double * offset)117 parse_matrix_header( char *line,
118 	int *width, int *height, double *scale, double *offset )
119 {
120 	double header[4];
121 	char *p, *q;
122 	int i;
123 
124 	for( i = 0, p = line;
125 		(q = vips_break_token( p, " \t" )) &&
126 			i < 4;
127 		i++, p = q )
128 		if( vips_strtod( p, &header[i] ) ) {
129 			vips_error( "matload",
130 				_( "bad number \"%s\"" ), p );
131 			return( -1 );
132 		}
133 
134 	if( i < 4 )
135 		header[3] = 0.0;
136 	if( i < 3 )
137 		header[2] = 1.0;
138 	if( i < 2 ) {
139 		vips_error( "matload", "%s", _( "no width / height" ) );
140 		return( -1 );
141 	}
142 
143 	if( VIPS_FLOOR( header[0] ) != header[0] ||
144 		VIPS_FLOOR( header[1] ) != header[1] ) {
145 		vips_error( "mask2vips", "%s", _( "width / height not int" ) );
146 		return( -1 );
147 	}
148 
149 	/* Width / height can be 65536 for a 16-bit LUT, for example.
150 	 */
151 	*width = header[0];
152 	*height = header[1];
153 	if( *width <= 0 ||
154 		*width > 100000 ||
155 		*height <= 0 ||
156 		*height > 100000 ) {
157 		vips_error( "mask2vips",
158 			"%s", _( "width / height out of range" ) );
159 		return( -1 );
160 	}
161 	if( header[2] == 0.0 ) {
162 		vips_error( "mask2vips", "%s", _( "zero scale" ) );
163 		return( -1 );
164 	}
165 
166 	*scale = header[2];
167 	*offset = header[3];
168 
169 	return( 0 );
170 }
171 
172 static int
vips_foreign_load_matrix_header(VipsForeignLoad * load)173 vips_foreign_load_matrix_header( VipsForeignLoad *load )
174 {
175 	VipsForeignLoadMatrix *matrix = (VipsForeignLoadMatrix *) load;
176 
177 	char *line;
178 	int width;
179 	int height;
180 	double scale;
181 	double offset;
182 	int result;
183 
184 	/* Rewind.
185 	 */
186 	vips_sbuf_unbuffer( matrix->sbuf );
187 	if( vips_source_rewind( matrix->source ) )
188 		return( -1 );
189 
190 	line = vips_sbuf_get_line_copy( matrix->sbuf );
191 	result = parse_matrix_header( line, &width, &height, &scale, &offset );
192 	g_free( line );
193 	if( result )
194 		return( -1 );
195 
196 	if( vips_image_pipelinev( load->out,
197 		VIPS_DEMAND_STYLE_THINSTRIP, NULL ) )
198 		return( -1 );
199 	vips_image_init_fields( load->out,
200 		width, height, 1,
201 		VIPS_FORMAT_DOUBLE,
202 		VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 );
203 
204 	vips_image_set_double( load->out, "scale", scale );
205 	vips_image_set_double( load->out, "offset", offset );
206 
207 	VIPS_SETSTR( load->out->filename,
208 		vips_connection_filename( VIPS_CONNECTION( matrix->source ) ) );
209 
210 	if( !(matrix->linebuf = VIPS_ARRAY( NULL, width, double )) )
211 		return( -1 );
212 
213 	return( 0 );
214 }
215 
216 static int
vips_foreign_load_matrix_load(VipsForeignLoad * load)217 vips_foreign_load_matrix_load( VipsForeignLoad *load )
218 {
219 	VipsForeignLoadMatrix *matrix = (VipsForeignLoadMatrix *) load;
220 	VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
221 
222 	int x, y;
223 
224 	if( vips_image_pipelinev( load->real,
225 		VIPS_DEMAND_STYLE_THINSTRIP, NULL ) )
226 		return( -1 );
227 	vips_image_init_fields( load->real,
228 		load->out->Xsize, load->out->Ysize, 1,
229 		VIPS_FORMAT_DOUBLE,
230 		VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 );
231 
232 	for( y = 0; y < load->real->Ysize; y++ ) {
233 		char *line;
234 		char *p, *q;
235 
236 		line = vips_sbuf_get_line_copy( matrix->sbuf );
237 
238 		for( x = 0, p = line;
239 			(q = vips_break_token( p, " \t" )) &&
240 				x < load->out->Xsize;
241 			x++, p = q )
242 			if( vips_strtod( p, &matrix->linebuf[x] ) ) {
243 				vips_error( class->nickname,
244 					_( "bad number \"%s\"" ), p );
245 				g_free( line );
246 				return( -1 );
247 			}
248 
249 		g_free( line );
250 
251 		if( x != load->out->Xsize ) {
252 			vips_error( class->nickname,
253 				_( "line %d too short" ), y );
254 			return( -1 );
255 		}
256 
257 		if( vips_image_write_line( load->real, y,
258 			(VipsPel *) matrix->linebuf ) )
259 			return( -1 );
260 	}
261 
262 	return( 0 );
263 }
264 
265 static void
vips_foreign_load_matrix_class_init(VipsForeignLoadMatrixClass * class)266 vips_foreign_load_matrix_class_init( VipsForeignLoadMatrixClass *class )
267 {
268 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
269 	VipsObjectClass *object_class = (VipsObjectClass *) class;
270 	VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
271 
272 	gobject_class->dispose = vips_foreign_load_matrix_dispose;
273 
274 	object_class->nickname = "matrixload_base";
275 	object_class->description = _( "load matrix" );
276 	object_class->build = vips_foreign_load_matrix_build;
277 
278 	load_class->get_flags = vips_foreign_load_matrix_get_flags;
279 	load_class->header = vips_foreign_load_matrix_header;
280 	load_class->load = vips_foreign_load_matrix_load;
281 
282 }
283 
284 static void
vips_foreign_load_matrix_init(VipsForeignLoadMatrix * matrix)285 vips_foreign_load_matrix_init( VipsForeignLoadMatrix *matrix )
286 {
287 }
288 
289 typedef struct _VipsForeignLoadMatrixFile {
290 	VipsForeignLoadMatrix parent_object;
291 
292 	/* Filename for load.
293 	 */
294 	char *filename;
295 
296 } VipsForeignLoadMatrixFile;
297 
298 typedef VipsForeignLoadMatrixClass VipsForeignLoadMatrixFileClass;
299 
300 G_DEFINE_TYPE( VipsForeignLoadMatrixFile, vips_foreign_load_matrix_file,
301 	vips_foreign_load_matrix_get_type() );
302 
303 static VipsForeignFlags
vips_foreign_load_matrix_file_get_flags_filename(const char * filename)304 vips_foreign_load_matrix_file_get_flags_filename( const char *filename )
305 {
306 	return( 0 );
307 }
308 
309 static int
vips_foreign_load_matrix_file_build(VipsObject * object)310 vips_foreign_load_matrix_file_build( VipsObject *object )
311 {
312 	VipsForeignLoadMatrix *matrix = (VipsForeignLoadMatrix *) object;
313 	VipsForeignLoadMatrixFile *file = (VipsForeignLoadMatrixFile *) object;
314 
315 	if( file->filename )
316 		if( !(matrix->source =
317 			vips_source_new_from_file( file->filename )) )
318 			return( -1 );
319 
320 	if( VIPS_OBJECT_CLASS( vips_foreign_load_matrix_file_parent_class )->
321 		build( object ) )
322 		return( -1 );
323 
324 	return( 0 );
325 }
326 
327 static const char *vips_foreign_load_matrix_suffs[] = {
328 	".mat",
329 	NULL
330 };
331 
332 static gboolean
vips_foreign_load_matrix_file_is_a(const char * filename)333 vips_foreign_load_matrix_file_is_a( const char *filename )
334 {
335 	unsigned char line[80];
336 	guint64 bytes;
337 	int width;
338 	int height;
339 	double scale;
340 	double offset;
341 	int result;
342 
343 	if( (bytes = vips__get_bytes( filename, line, 79 )) <= 0 )
344 		return( FALSE );
345 	line[bytes] = '\0';
346 
347 	vips_error_freeze();
348 	result = parse_matrix_header( (char *) line,
349 		&width, &height, &scale, &offset );
350 	vips_error_thaw();
351 
352 	return( result == 0 );
353 }
354 
355 static void
vips_foreign_load_matrix_file_class_init(VipsForeignLoadMatrixFileClass * class)356 vips_foreign_load_matrix_file_class_init(
357 	VipsForeignLoadMatrixFileClass *class )
358 {
359 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
360 	VipsObjectClass *object_class = (VipsObjectClass *) class;
361 	VipsForeignClass *foreign_class = (VipsForeignClass *) class;
362 	VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
363 
364 	gobject_class->set_property = vips_object_set_property;
365 	gobject_class->get_property = vips_object_get_property;
366 
367 	object_class->nickname = "matrixload";
368 	object_class->build = vips_foreign_load_matrix_file_build;
369 
370 	foreign_class->suffs = vips_foreign_load_matrix_suffs;
371 
372 	load_class->is_a = vips_foreign_load_matrix_file_is_a;
373 	load_class->get_flags_filename =
374 		vips_foreign_load_matrix_file_get_flags_filename;
375 
376 	VIPS_ARG_STRING( class, "filename", 1,
377 		_( "Filename" ),
378 		_( "Filename to load from" ),
379 		VIPS_ARGUMENT_REQUIRED_INPUT,
380 		G_STRUCT_OFFSET( VipsForeignLoadMatrixFile, filename ),
381 		NULL );
382 
383 }
384 
385 static void
vips_foreign_load_matrix_file_init(VipsForeignLoadMatrixFile * file)386 vips_foreign_load_matrix_file_init( VipsForeignLoadMatrixFile *file )
387 {
388 }
389 
390 typedef struct _VipsForeignLoadMatrixSource {
391 	VipsForeignLoadMatrix parent_object;
392 
393 	VipsSource *source;
394 
395 } VipsForeignLoadMatrixSource;
396 
397 typedef VipsForeignLoadMatrixClass VipsForeignLoadMatrixSourceClass;
398 
399 G_DEFINE_TYPE( VipsForeignLoadMatrixSource, vips_foreign_load_matrix_source,
400 	vips_foreign_load_matrix_get_type() );
401 
402 static int
vips_foreign_load_matrix_source_build(VipsObject * object)403 vips_foreign_load_matrix_source_build( VipsObject *object )
404 {
405 	VipsForeignLoadMatrix *matrix = (VipsForeignLoadMatrix *) object;
406 	VipsForeignLoadMatrixSource *source =
407 		(VipsForeignLoadMatrixSource *) object;
408 
409 	if( source->source ) {
410 		matrix->source = source->source;
411 		g_object_ref( matrix->source );
412 	}
413 
414 	if( VIPS_OBJECT_CLASS( vips_foreign_load_matrix_source_parent_class )->
415 		build( object ) )
416 		return( -1 );
417 
418 	return( 0 );
419 }
420 
421 static int
vips_foreign_load_matrix_source_is_a_source(VipsSource * source)422 vips_foreign_load_matrix_source_is_a_source( VipsSource *source )
423 {
424 	unsigned char *data;
425 	size_t bytes_read;
426 	char line[80];
427 	int width;
428 	int height;
429 	double scale;
430 	double offset;
431 	int result;
432 
433 	if( (bytes_read = vips_source_sniff_at_most( source,
434 		&data, 79 )) <= 0 )
435 		return( FALSE );
436 	vips_strncpy( line, (const char *) data, 80 );
437 
438 	vips_error_freeze();
439 	result = parse_matrix_header( line,
440 		&width, &height, &scale, &offset );
441 	vips_error_thaw();
442 
443 	return( result == 0 );
444 }
445 
446 static void
vips_foreign_load_matrix_source_class_init(VipsForeignLoadMatrixFileClass * class)447 vips_foreign_load_matrix_source_class_init(
448 	VipsForeignLoadMatrixFileClass *class )
449 {
450 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
451 	VipsObjectClass *object_class = (VipsObjectClass *) class;
452 	VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
453 	VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
454 
455 	gobject_class->set_property = vips_object_set_property;
456 	gobject_class->get_property = vips_object_get_property;
457 
458 	object_class->nickname = "matrixload_source";
459 	object_class->build = vips_foreign_load_matrix_source_build;
460 
461 	operation_class->flags = VIPS_OPERATION_NOCACHE;
462 
463 	load_class->is_a_source = vips_foreign_load_matrix_source_is_a_source;
464 
465 	VIPS_ARG_OBJECT( class, "source", 1,
466 		_( "Source" ),
467 		_( "Source to load from" ),
468 		VIPS_ARGUMENT_REQUIRED_INPUT,
469 		G_STRUCT_OFFSET( VipsForeignLoadMatrixSource, source ),
470 		VIPS_TYPE_SOURCE );
471 
472 }
473 
474 static void
vips_foreign_load_matrix_source_init(VipsForeignLoadMatrixSource * source)475 vips_foreign_load_matrix_source_init( VipsForeignLoadMatrixSource *source )
476 {
477 }
478 
479 /**
480  * vips_matrixload:
481  * @filename: file to load
482  * @out: (out): output image
483  * @...: %NULL-terminated list of optional named arguments
484  *
485  * Reads a matrix from a file.
486  *
487  * Matrix files have a simple format that's supposed to be easy to create with
488  * a text editor or a spreadsheet.
489  *
490  * The first line has four numbers for width, height, scale and
491  * offset (scale and offset may be omitted, in which case they default to 1.0
492  * and 0.0). Scale must be non-zero. Width and height must be positive
493  * integers. The numbers are separated by any mixture of spaces, commas,
494  * tabs and quotation marks ("). The scale and offset fields may be
495  * floating-point, and must use '.'
496  * as a decimal separator.
497  *
498  * Subsequent lines each hold one row of matrix data, with numbers again
499  * separated by any mixture of spaces, commas,
500  * tabs and quotation marks ("). The numbers may be floating-point, and must
501  * use '.'
502  * as a decimal separator.
503  *
504  * Extra characters at the ends of lines or at the end of the file are
505  * ignored.
506  *
507  * See also: vips_matrixload().
508  *
509  * Returns: 0 on success, -1 on error.
510  */
511 int
vips_matrixload(const char * filename,VipsImage ** out,...)512 vips_matrixload( const char *filename, VipsImage **out, ... )
513 {
514 	va_list ap;
515 	int result;
516 
517 	va_start( ap, out );
518 	result = vips_call_split( "matrixload", ap, filename, out );
519 	va_end( ap );
520 
521 	return( result );
522 }
523 
524 /**
525  * vips_matrixload_source:
526  * @source: source to load
527  * @out: (out): output image
528  * @...: %NULL-terminated list of optional named arguments
529  *
530  * Exactly as vips_matrixload(), but read from a source.
531  *
532  * See also: vips_matrixload().
533  *
534  * Returns: 0 on success, -1 on error.
535  */
536 int
vips_matrixload_source(VipsSource * source,VipsImage ** out,...)537 vips_matrixload_source( VipsSource *source, VipsImage **out, ... )
538 {
539 	va_list ap;
540 	int result;
541 
542 	va_start( ap, out );
543 	result = vips_call_split( "matrixload_source", ap, source, out );
544 	va_end( ap );
545 
546 	return( result );
547 }
548 
549