1 /*
2 * imgsav.c
3 *
4 * Implementation of the image save functionality.
5 *
6 * (C) 1997 Randall Hopper
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met: 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer. 2.
12 * Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29
30 /* ******************** Include Files ************** */
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <tiffio.h>
36 #include <assert.h>
37 #include <X11/X.h>
38 #include "tvdefines.h"
39 #include "imgsav.h"
40 #include "xutil.h"
41 #include "rawvideo.h"
42
43 /* ******************** Local defines ************** */
44
45 #define WRITE_BUF_SIZE (64*1024L)
46 #define TV_BITS_PER_COMP 8
47 #define TV_COMP_PER_PIX 3
48 #define TV_BYTES_PER_PIX 3
49
50 #define WRITE_PLANAR_TIFFS
51 /* NOTE: libtiff doesn't write correct Planar TIFFs when */
52 /* ROWSPERSTRIP is > 1 (e.g. height of image). If you can't */
53 /* do that, no compression advantage, so no reason to use it. */
54
55 #ifndef WRITE_PLANAR_TIFFS
56 # error This code worked for tiff3.3, but broke for tiff3.4.
57 /* NOTE: Fortunately, what we wanted was PLANAR tiffs anyway, so with */
58 /* tiff3.4, what we want now works whereas with 3.3 it didn't. */
59 #endif
60
61 /* ******************** Private variables ************** */
62 /* ******************** Forward declarations ************** */
63 /* ******************** Function Definitions ************** */
64
65 /**@BEGINFUNC**************************************************************
66
67 Prototype : static void TVIMGSAVFmtScanline24bpp(
68 TV_IMAGE *img,
69 TV_INT32 y,
70 TV_INT32 compon,
71 TV_UINT8 *dst )
72
73 Purpose : Formats the pixel data from scanline "y" in the image "img",
74 form whatever strange and surreal format it happens to be
75 in into straight 24bpp RGB format (R-G-B, respectively).
76 Only the color components specified by compon are written.
77
78 NOTE: dst is assumed to be a pointer to a buffer of
79 sufficient length ( N * img->geom.w, where N is the
80 number of components specified by compon).
81
82 Programmer : 03-Sep-97 Randall Hopper
83
84 Parameters : img - I: an image
85 y - I: which scanline to extract (and fmt cnvt if needed)
86 compon - I: desired components (mask of DoRed DoGreen DoBlue)
87 dst - O: pointer to the output buffer (size 3*width or more)
88
89 Returns : None.
90
91 Globals : None.
92
93 **@ENDFUNC*****************************************************************/
94
TVIMGSAVFmtScanline24bpp(TV_IMAGE * img,TV_INT32 y,TV_INT32 compon,TV_UINT8 * dst)95 static void TVIMGSAVFmtScanline24bpp(
96 TV_IMAGE *img,
97 TV_INT32 y,
98 TV_INT32 compon,
99 TV_UINT8 *dst )
100 {
101 static TV_BOOL S_cache_valid = FALSE;
102 static TV_INT32 S_cache_shf[3];
103 static TV_UINT32 S_cache_msk[3],
104 S_cache_pixmsk[3],
105 S_dst_rel_mask[3] = { 0xFF0000, 0x00FF00, 0x0000FF };
106 TV_UINT32 *shf = S_cache_shf,
107 *msk = S_cache_msk;
108
109 TV_UINT8 *src8 = (TV_UINT8 *) (img->buf +
110 y * img->geom.w * img->pix_geom.Bpp);
111 TV_UINT16 *src16 = (TV_UINT16 *) src8;
112 TV_UINT32 *src32 = (TV_UINT32 *) src8;
113 TV_INT32 x;
114
115 assert( (y >= 0) && (y < img->geom.h) );
116
117 if ( !S_cache_valid ||
118 !memcmp( img->pix_geom.mask, S_cache_pixmsk, sizeof(S_cache_pixmsk)) ){
119
120 memcpy( S_cache_pixmsk, img->pix_geom.mask, sizeof( S_cache_pixmsk ) );
121 XUTILGetPixelConvInfo( S_cache_pixmsk, S_dst_rel_mask,
122 S_cache_shf, S_cache_msk );
123 S_cache_valid = TRUE;
124 }
125
126 /* Format scanline buffer */
127 for ( x = 0; x < img->geom.w; x++ ) {
128 register TV_UINT32 pix;
129
130 /* Grab pixel */
131 switch ( img->pix_geom.Bpp ) {
132 case 2 : pix = *(src16++); break;
133 case 4 : pix = *(src32++); break;
134 case 3 : pix = *(src8++);
135 pix |= *(src8++) << 8;
136 pix |= *(src8++) << 16;
137 break;
138 default:
139 fprintf( stderr,
140 "TVIMGSAVFmtScanline24bpp: Unsupported Bpp %ld\n",
141 img->pix_geom.Bpp );
142 exit(1);
143 }
144
145 /* Swap bytes as needed */
146 if (( !img->pix_geom.swap_shorts ) &&
147 ( img->pix_geom.Bpp == 4 ))
148 pix = (pix >> 16) | (pix << 16);
149 if ( !img->pix_geom.swap_bytes )
150 if ( img->pix_geom.Bpp == 3 )
151 pix = ((pix & 0x00FF0000) >> 16) |
152 ((pix & 0x000000FF) << 16);
153 else
154 pix = ((pix & 0xFF000000) >> 8) |
155 ((pix & 0x00FF0000) << 8) |
156 ((pix & 0x0000FF00) >> 8) |
157 ((pix & 0x000000FF) << 8);
158
159 pix = SHIFT_AND_MASK( pix, shf[0], msk[0] ) |
160 SHIFT_AND_MASK( pix, shf[1], msk[1] ) |
161 SHIFT_AND_MASK( pix, shf[2], msk[2] );
162
163 /* Finally, got an 8-8-8 RGB 3Bpp pixel in "pix". */
164 /* Slap it in the buffer. */
165 if ( compon & DoRed )
166 *(dst++) = (pix >> 16) & 0xFF;
167 if ( compon & DoGreen )
168 *(dst++) = (pix >> 8) & 0xFF;
169 if ( compon & DoBlue )
170 *(dst++) = pix & 0xFF;
171 }
172 }
173
174
TVIMGSAVDoSaveTIFF(char filename[],TV_IMAGE * img)175 void TVIMGSAVDoSaveTIFF( char filename[], TV_IMAGE *img )
176 {
177 char errmsg[160];
178 TIFF *out;
179 TV_INT32 linebytes,
180 y,
181 pass,
182 compon;
183 TV_UINT8 *buf;
184
185 if ( img->pix_geom.type != TV_PIXELTYPE_RGB ) {
186 fprintf( stderr, "Attempt to save non-RGB data as TIFF\n" );
187 exit(1);
188 }
189
190 /* Open output file */
191 if ( (out = TIFFOpen( filename, "w" )) == NULL ) {
192 sprintf( errmsg, "Can't open output file '%s'", filename );
193 XUTILDialogPause( TVTOPLEVEL, "Error", errmsg, TV_DIALOG_TYPE_OK );
194 return;
195 }
196
197 /* Setup image format info (tags) */
198 TIFFSetField( out, TIFFTAG_IMAGEWIDTH , img->geom.w );
199 TIFFSetField( out, TIFFTAG_IMAGELENGTH , img->geom.h );
200 TIFFSetField( out, TIFFTAG_ORIENTATION , ORIENTATION_TOPLEFT );
201 TIFFSetField( out, TIFFTAG_SAMPLESPERPIXEL, TV_COMP_PER_PIX );
202 TIFFSetField( out, TIFFTAG_BITSPERSAMPLE , TV_BITS_PER_COMP );
203 TIFFSetField( out, TIFFTAG_PHOTOMETRIC , PHOTOMETRIC_RGB );
204 TIFFSetField( out, TIFFTAG_COMPRESSION , COMPRESSION_LZW );
205
206 /* Allocate a buffer to hold each scanline */
207 #ifdef WRITE_PLANAR_TIFFS
208 /* Note: w/ ROWSPERSTRIP > 1, libtiff doesn't write pixels correctly. */
209 /* Valid TIFF file, but there's some weird pixel shifting going on. */
210 TIFFSetField( out, TIFFTAG_ROWSPERSTRIP , img->geom.h );
211 TIFFSetField( out, TIFFTAG_PLANARCONFIG , PLANARCONFIG_SEPARATE );
212 linebytes = img->geom.w * 1;
213 #else
214 TIFFSetField( out, TIFFTAG_PLANARCONFIG , PLANARCONFIG_CONTIG );
215 linebytes = img->geom.w * TV_BYTES_PER_PIX;
216 #endif
217
218 if ( TIFFScanlineSize(out) != linebytes ) {
219 fprintf( stderr, "Linebytes mismatch: TIFF says %ld, we say %ld\n",
220 TIFFScanlineSize(out), linebytes );
221 exit(1);
222 }
223 if ( (buf = malloc( linebytes )) == NULL )
224 TVUTILOutOfMemory();
225
226 /* Convert & write the image data */
227 #ifdef WRITE_PLANAR_TIFFS
228 for ( pass = 0; pass < TV_COMP_PER_PIX; pass++ ) {
229 compon = ( pass == 0 ? DoRed : pass == 1 ? DoGreen : DoBlue );
230 #else
231 for ( pass = 0; pass < 1; pass++ ) {
232 compon = DoRed | DoGreen | DoBlue;
233 #endif
234 for ( y = 0; y < img->geom.h; y++ ) {
235
236 /* Format scanline */
237 TVIMGSAVFmtScanline24bpp( img, y, compon, buf );
238
239 /* And write it in TIFF */
240 if ( !TIFFWriteScanline( out, buf, y, pass ) ) {
241 fprintf( stderr, "TIFFWriteScanline() failed\n" );
242 XBell( TVDISPLAY, 100 );
243 TIFFClose( out );
244 free( buf );
245 unlink( filename );
246 return;
247 }
248 }
249 }
250
251 /* All done. Close up shop and go home */
252 TIFFClose(out);
253 free( buf );
254 }
255
256
257 void TVIMGSAVDoSavePPM( char filename[], TV_IMAGE *img )
258 {
259 static char *S_fp_buf = NULL;
260 static char *S_buf = NULL;
261 static TV_INT32 S_buf_size = 0;
262
263 char errmsg[160];
264 FILE *out;
265 TV_INT32 y;
266 TV_INT32 linebytes;
267
268 if ( img->pix_geom.type != TV_PIXELTYPE_RGB ) {
269 fprintf( stderr, "Attempt to save non-RGB data as PPM\n" );
270 exit(1);
271 }
272
273 /* Allocate a buffer for PPM writing to encourage large block writes */
274 if ( !S_fp_buf &&
275 ( (S_fp_buf = malloc( WRITE_BUF_SIZE )) == NULL ) )
276 TVUTILOutOfMemory();
277
278 /* Open output file */
279 if ( filename ) {
280 if ( (out = fopen( filename, "wb" )) == NULL ) {
281 sprintf( errmsg, "Can't open output file '%s'", filename );
282 XUTILDialogPause( TVTOPLEVEL, "Error", errmsg, TV_DIALOG_TYPE_OK );
283 return;
284 }
285 setvbuf( out, S_fp_buf, _IOFBF, WRITE_BUF_SIZE );
286 }
287 else
288 out = stdout;
289
290 /* Write binary PPM header */
291 fprintf( out, "P6\n%ld %ld\n%d\n", img->geom.w, img->geom.h,
292 (1 << TV_BITS_PER_COMP) - 1 );
293
294 linebytes = img->geom.w * TV_BYTES_PER_PIX;
295
296 if (( S_buf == NULL ) || ( S_buf_size < linebytes )) {
297 if ( (S_buf = realloc( S_buf, linebytes )) == NULL )
298 TVUTILOutOfMemory();
299 S_buf_size = MAX( S_buf_size, linebytes );
300 }
301
302 /* Now write the pixel data -- just a simple raw block of */
303 /* R G B pixel values, ordered left-to-right, top-to-bottom. */
304 for ( y = 0; y < img->geom.h; y++ ) {
305
306 /* Format scanline */
307 TVIMGSAVFmtScanline24bpp( img, y, DoRed | DoGreen | DoBlue, S_buf );
308
309 /* And write it in PPM */
310 if ( fwrite( S_buf, 3, img->geom.w, out ) != img->geom.w ) {
311 fprintf( stderr, "TVIMGSAVDoSavePPM(): Error writing scanline\n" );
312 XBell( TVDISPLAY, 100 );
313 if ( filename ) {
314 fclose( out );
315 unlink( filename );
316 }
317 return;
318 }
319 }
320
321 /* All done. Close up shop and go home */
322 if ( filename )
323 fclose(out);
324 }
325
326
327 void TVIMGSAVDoSaveYUV( char filename[], TV_IMAGE *img )
328 {
329 /* YUV isn't really a format. It's just the raw frame chunked into */
330 /* a file as-is (packed/planar, whatever H/V Y/U/V sampling, etc.) */
331
332 static char *S_fp_buf = NULL;
333
334 char errmsg[160];
335 FILE *out;
336 TV_UINT32 img_size;
337
338 if ( img->pix_geom.type != TV_PIXELTYPE_YUV ) {
339 fprintf( stderr, "Attempt to save non-YUV data as YUV\n" );
340 exit(1);
341 }
342
343 /* Allocate a buffer for YUV writing to encourage large block writes */
344 if ( !S_fp_buf &&
345 ( (S_fp_buf = malloc( WRITE_BUF_SIZE )) == NULL ) )
346 TVUTILOutOfMemory();
347
348 /* Open output file */
349 if ( filename ) {
350 if ( (out = fopen( filename, "wb" )) == NULL ) {
351 sprintf( errmsg, "Can't open output file '%s'", filename );
352 XUTILDialogPause( TVTOPLEVEL, "Error", errmsg, TV_DIALOG_TYPE_OK );
353 return;
354 }
355 setvbuf( out, S_fp_buf, _IOFBF, WRITE_BUF_SIZE );
356 }
357 else
358 out = stdout;
359
360 img_size = TVRAWVIDEOCalcImageSize( img );
361
362 if ( fwrite( img->buf, img_size, 1, out ) != 1 ) {
363 fprintf( stderr, "TVIMGSAVDoSaveYUV(): Error writing YUV file\n" );
364 XBell( TVDISPLAY, 100 );
365 fclose( out );
366 unlink( filename );
367 return;
368 }
369
370 /* All done. Close up shop and go home */
371 if ( filename )
372 fclose(out);
373 }
374
375
376
377 /**@BEGINFUNC**************************************************************
378
379 Prototype : void TVIMGSAVDoSave(
380 char filename[],
381 TV_STILL_FMT fmt,
382 TV_IMAGE *img )
383
384 Purpose : Write an image to a file (or stdout) in the
385 specified format.
386
387 Programmer : 15-Jan-98 Randall Hopper
388
389 Parameters : filename - I: filename (NULL = stdout)
390 fmt - I: format to write it in
391 img - I: image to write.
392
393 Returns : None.
394
395 Globals : None.
396
397 **@ENDFUNC*****************************************************************/
398
399 void TVIMGSAVDoSave( char filename[], TV_STILL_FMT fmt,
400 TV_IMAGE *img )
401 {
402 switch ( fmt ) {
403 case TV_STILL_FMT_TIFF :
404 if ( !filename ) {
405 fprintf( stderr, "TVIMGSAVDoSave: stdout mode not supported "
406 "for TIFF\n" );
407 exit(1);
408 }
409 TVIMGSAVDoSaveTIFF( filename, img ); break;
410
411 case TV_STILL_FMT_PPM :
412 TVIMGSAVDoSavePPM ( filename, img ); break;
413
414 case TV_STILL_FMT_YUV :
415 TVIMGSAVDoSaveYUV ( filename, img ); break;
416
417 default:
418 fprintf( stderr, "TVIMGSAVDoSave: Unsupported format %d\n", fmt );
419 exit(1);
420 }
421 }
422