libvips/contrib/vdump/vdump.c
2007-08-29 16:23:50 +00:00

400 lines
9.8 KiB
C

/* This is incredibly primitive and annoying.
*
* Turn a VASARI format file into PostScript. Do simple subsampling of
* images to get the size down .. no point in sending anything much larger
* than 100x100 to the laserwriter if it's going in a document. The output
* conforms to PS-Adobe-2.0 EPSF-2.0, I think.
*
* Options:
* -s<n> Average an nxn area in the image for each pixel in the output.
* This reduces the size of the files significantly (obviously).
* Default 1.
* -l Force landscape output
* -p Force portrait output (default)
* -a Automatic choice of portrait/landscape
* Nasty: as we have to include a %%BoundingBox: line, we can't
* size the image to fit comfortably in whatever size paper this
* PostScript printer takes.
* -D Supress generation of showpage. Sometimes necessary if you
* want to include the PS file in a document.
*/
/*
This file is part of VIPS.
VIPS is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <vips/vips.h>
#include <vips/util.h>
#define USAGE "usage: [-s<scale> -alpD] vasari_format_file"
#define PROLOGUE "vdump.pro"
#define PAPER_WIDTH (8.25*72.0) /* Paper size .. A4 */
#define PAPER_HEIGHT (11.75*72.0)
#define PAPER_MARGIN (1.0*72.0) /* Margin we leave around the edge */
#define PRINT_WIDTH (PAPER_WIDTH - 2.0*PAPER_MARGIN)
#define PRINT_HEIGHT (PAPER_HEIGHT - 2.0*PAPER_MARGIN)
#define PRINT_RATIO (PRINT_WIDTH / PRINT_HEIGHT)
/* Useful: a pixel. We mmap the file, then cast the pointer to the image to
* a pointer to one of these things.
*/
struct pixel {
unsigned char p_red;
unsigned char p_green;
unsigned char p_blue;
};
/* A monochrome pixel.
*/
struct mpixel {
unsigned char p_val;
};
enum output_format {
LANDSCAPE, /* Rotated by 90 degrees */
PORTRAIT, /* Vertical */
AUTOMATIC /* Whichever fits best */
};
static const char *our_name; /* Name of this prog */
static char *file_name; /* Name of file we dump */
static int print_on = 1; /* Generate showpage */
/* Copy between two fds
*/
static void
copy_file( from, to )
FILE *from, *to;
{ int ch;
while( (ch = getc( from )) != EOF )
putc( ch, to );
}
/* Send a file to stdout. Used to transmit the prelude.
*/
static int
transmit_file( name )
char *name;
{ const char *prefix;
char buf[PATH_MAX];
FILE *fp;
if( !(prefix = im_guess_prefix( our_name, "VIPSHOME" )) )
error_exit( "VIPSHOME not defined" );
im_snprintf( buf, PATH_MAX, "%s/share/%s/%s", prefix, PACKAGE, name );
/* Send it!
*/
if( !(fp = fopen( buf, "r" )) )
error_exit( "can't find %s", name );
copy_file( fp, stdout );
fclose( fp );
return( -1 );
}
/* Encode a colour VASARI file as mono hex bytes. Scale down by a factor of
* s. We scale by averaging regions of sxs pixels .. is this the best way?
* works ok for our laserwriter anyway. We lose incomplete regions down the RH
* side and across the bottom.
*/
static void
encode_colour( im, s, data )
IMAGE *im;
int s;
struct pixel *data;
{ int p = 35;
int x, y;
int i, j;
/* Scan across and down. Make sure we chop off those incomplete
* regions on the RH side and across the bottom.
*/
for( y = 0; y <= im->Ysize - s; y += s )
for( x = 0; x <= im->Xsize - s; x += s ) {
int col = 0;
struct pixel *rs = &data[y * im->Xsize + x];
/* Now average the region. We monochromise each pixel
* and add it to the running total.
*/
for( i = 0; i < s; i++ ) {
struct pixel *d = &rs[i * im->Xsize];
for( j = 0; j < s; j++ ) {
col += (int) (d->p_red + d->p_green +
d->p_blue) / 3;
d++;
}
}
col /= s*s;
/* Output the averaged pixel.
*/
printf( "%02x", col );
if( !p-- ) {
printf( "\n" );
p = 35;
}
}
printf( "\n" );
}
/* Encode a mono VASARI file as hex bytes. Scale down by a factor of
* s. We scale by averaging regions of sxs pixels .. is this the best way?
* works ok for our laserwriter anyway. We lose incomplete regions down the RH
* side and across the bottom.
*/
static void
encode_mono( im, s, data )
IMAGE *im;
int s;
struct mpixel *data;
{ int p = 35;
int x, y;
int i, j;
/* Scan across and down. Make sure we chop off those incomplete
* regions on the RH side and across the bottom.
*/
for( y = 0; y <= im->Ysize - s; y += s )
for( x = 0; x <= im->Xsize - s; x += s ) {
int col = 0;
struct mpixel *rs = &data[y * im->Xsize + x];
/* Now average the region.
*/
for( i = 0; i < s; i++ ) {
struct mpixel *d = &rs[i * im->Xsize];
for( j = 0; j < s; j++ )
col += d++->p_val;
}
col /= s*s;
/* Output the averaged pixel.
*/
printf( "%02x", col );
if( !p-- ) { printf( "\n" ); p = 35; }
}
printf( "\n" );
}
/* Print the image. Work out the orientation, print the prologue, then call
* one of the dumps above to do the image.
*/
static void
dump( im, format, scale )
IMAGE *im;
enum output_format format;
int scale;
{ float r, width, height, xstart, ystart;
/* Fix orientation, then set our origin and output size. Four cases ..
* can any of these be combined? Perhaps not.
*/
r = (float) im->Xsize / im->Ysize;
if( format == AUTOMATIC ) {
if( im->Xsize > im->Ysize )
format = LANDSCAPE;
else
format = PORTRAIT;
}
if( format == PORTRAIT ) {
/* Is it going to be smaller than the paper vertically or
* horizontally?
*/
if( r > PRINT_RATIO ) {
/* It's too wide. We make it as large as possible
* horizontally, then center it vertically.
*/
width = PRINT_WIDTH;
height = PRINT_WIDTH / r;
xstart = PAPER_MARGIN;
ystart = (PRINT_HEIGHT - height) / 2.0 + PAPER_MARGIN;
}
else {
/* Too high. Make as large as possible vertically,
* then center it horizontally.
*/
height = PRINT_HEIGHT;
width = PRINT_HEIGHT * r;
ystart = PAPER_MARGIN;
xstart = (PRINT_WIDTH - width) / 2.0 + PAPER_MARGIN;
}
}
else {
/* Do a landscape picture. Will we run out of space
* horizontally or vertically?
*/
if( 1.0 / r < PRINT_RATIO ) {
/* Very wide indeed! Fit it horizontally, then center
* it vertically.
*/
height = PRINT_HEIGHT;
width = PRINT_HEIGHT / r;
ystart = PAPER_MARGIN;
xstart = (PRINT_WIDTH - width) / 2.0 + PAPER_MARGIN;
}
else {
/* Too tall. Make as large as possible vertically,
* then center it horizontally.
*/
width = PRINT_WIDTH;
height = PRINT_WIDTH * r;
xstart = PAPER_MARGIN;
ystart = (PRINT_HEIGHT - height) / 2.0 + PAPER_MARGIN;
}
}
/* Print header.
*/
printf( "%%!PS-Adobe-2.0 EPSF-2.0\n" );
printf( "%%%%BoundingBox: %d %d %d %d\n", (int) xstart, (int) ystart,
(int) (width + xstart), (int) (height + ystart) );
printf( "%%%%Title: %s\n", file_name );
printf( "%%%%Creator: %s\n", our_name );
/* Print prologue.
*/
transmit_file( PROLOGUE );
/* Print position, scale and rotation. Print size in pixels and call
* doimage.
*/
if( format == LANDSCAPE )
printf( "%d %d translate\n",
(int) (xstart + width), (int) ystart );
else
printf( "%d %d translate\n", (int) xstart, (int) ystart );
printf( "%d %d scale\n", (int) width, (int) height );
if( format == LANDSCAPE )
printf( "90 rotate\n" );
printf( "%d %d 8 doimage\n",
(int) (im->Xsize / scale), (int) (im->Ysize / scale) );
/* Print body of file.
*/
if( im->Bands == 3 )
encode_colour( im, scale, (struct pixel *) im->data );
else
encode_mono( im, scale, (struct mpixel *) im->data );
/* Print trailer.
*/
if( print_on )
printf( "showpage\n" );
printf( "%%%%EndDocument\n" );
}
/* Start here!
*/
int
main( argc, argv )
int argc;
char **argv;
{ int scale = 1;
enum output_format format = PORTRAIT;
IMAGE *im = NULL;
if( im_init_world( argv[0] ) )
error_exit( "unable to start VIPS" );
our_name = *argv;
/* Decode args .. just look for file names and our three options.
*/
while( --argc )
if( *argv[argc] == '-' )
switch( argv[argc][1] ) {
case 's':
if( sscanf( argv[argc] + 2,
"%d", &scale ) != 1 )
error_exit( USAGE );
break;
case 'l':
format = LANDSCAPE;
break;
case 'p':
format = PORTRAIT;
break;
case 'a':
format = AUTOMATIC;
break;
case 'D':
print_on = 0;
break;
default:
error_exit( USAGE );
break;
}
else {
/* Try to open the file. If we have previously opened,
* then flag an error.
*/
if( im != NULL )
error_exit( USAGE );
file_name = argv[argc];
if( !(im = im_open( file_name, "r" )) )
error_exit( "unable to open %s", file_name );
}
if( im == NULL ) error_exit( USAGE );
/* Check it for suitability. We can print colour
* or monochrome pictures.
*/
if( im->Coding != IM_CODING_NONE )
error_exit( "cannot print compressed pictures" );
if( !(
(im->Bands == 3 && im->Bbits == 8 &&
im->BandFmt == IM_BANDFMT_UCHAR) ||
(im->Bands == 1 && im->Bbits == 8 &&
im->BandFmt == IM_BANDFMT_UCHAR)
) )
error_exit( "can only print mono or colour images" );
if( im_incheck( im ) )
error_exit( "unable to get pixels" );
dump( im, format, scale);
return( 0 );
}