home *** CD-ROM | disk | FTP | other *** search
- /* pnmscale.c - read a portable anymap and scale it
- **
- ** Copyright (C) 1989, 1991 by Jef Poskanzer.
- **
- ** Permission to use, copy, modify, and distribute this software and its
- ** documentation for any purpose and without fee is hereby granted, provided
- ** that the above copyright notice appear in all copies and that both that
- ** copyright notice and this permission notice appear in supporting
- ** documentation. This software is provided "as is" without express or
- ** implied warranty.
- */
-
- #include <math.h>
- #include "pnm.h"
-
- #define SCALE 4096
- #define HALFSCALE 2048
-
- int
- main( argc, argv )
- int argc;
- char* argv[];
- {
- FILE* ifp;
- xel* xelrow;
- xel* tempxelrow;
- xel* newxelrow;
- register xel* xP;
- register xel* nxP;
- int argn, specxscale, specyscale, specxsize, specysize, specxysize;
- int rows, cols, format, newformat, rowsread, newrows, newcols, newpixels;
- register int row, col, needtoreadrow;
- xelval maxval;
- float xscale, yscale;
- long sxscale, syscale;
- register long fracrowtofill, fracrowleft;
- long* rs;
- long* gs;
- long* bs;
- char* usage = "<s> [pnmfile]\n \
- -xsize|width|-ysize|-height <s> [pnmfile]\n \
- -xscale|-yscale <s> [pnmfile]\n \
- -xscale|-xsize|-width <s> -yscale|-ysize|-height <s> [pnmfile]\n \
- -xysize <x> <y> [pnmfile]\n \
- -pixels <n> [pnmfile]";
-
- pnm_init( &argc, argv );
-
- argn = 1;
- specxscale = specyscale = specxsize = specysize = specxysize = newpixels = 0;
-
- while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
- {
- if ( pm_keymatch( argv[argn], "-xscale", 4 ) )
- {
- if ( specxscale )
- pm_error( "already specified an x scale" );
- if ( specxsize )
- pm_error(
- "only one of -xsize/-width and -xscale may be specified" );
- ++argn;
- if ( argn == argc || sscanf( argv[argn], "%f", &xscale ) != 1 )
- pm_usage( usage );
- if ( xscale <= 0.0 )
- pm_error( "x scale must be greater than 0" );
- specxscale = 1;
- }
- else if ( pm_keymatch( argv[argn], "-yscale", 4 ) )
- {
- if ( specyscale )
- pm_error( "already specified a y scale" );
- if ( specysize )
- pm_error(
- "only one of -ysize/-height and -yscale may be specified" );
- ++argn;
- if ( argn == argc || sscanf( argv[argn], "%f", &yscale ) != 1 )
- pm_usage( usage );
- if ( yscale <= 0.0 )
- pm_error( "y scale must be greater than 0" );
- specyscale = 1;
- }
- else if ( pm_keymatch( argv[argn], "-xsize", 4 ) ||
- pm_keymatch( argv[argn], "-width", 2 ) )
- {
- if ( specxsize )
- pm_error( "already specified a width" );
- if ( specxscale )
- pm_error(
- "only one of -xscale and -xsize/-width may be specified" );
- ++argn;
- if ( argn == argc || sscanf( argv[argn], "%d", &newcols ) != 1 )
- pm_usage( usage );
- if ( newcols <= 0 )
- pm_error( "new width must be greater than 0" );
- specxsize = 1;
- }
- else if ( pm_keymatch( argv[argn], "-ysize", 4 ) ||
- pm_keymatch( argv[argn], "-height", 2 ) )
- {
- if ( specysize )
- pm_error( "already specified a height" );
- if ( specyscale )
- pm_error(
- "only one of -yscale and -ysize/-height may be specified" );
- ++argn;
- if ( argn == argc || sscanf( argv[argn], "%d", &newrows ) != 1 )
- pm_usage( usage );
- if ( newrows <= 0 )
- pm_error( "new height must be greater than 0" );
- specysize = 1;
- }
- else if ( pm_keymatch( argv[argn], "-xysize", 3 ) )
- {
- if ( specxsize || specysize || specxscale || specyscale || newpixels )
- pm_error( "can't use -xysize with any other specifiers" );
- ++argn;
- if ( argn == argc || sscanf( argv[argn], "%d", &newcols ) != 1 )
- pm_usage( usage );
- ++argn;
- if ( argn == argc || sscanf( argv[argn], "%d", &newrows ) != 1 )
- pm_usage( usage );
- if ( newcols <= 0 || newrows <= 0 )
- pm_error( "new width and height must be greater than 0" );
- specxsize = 1;
- specysize = 1;
- specxysize = 1;
- }
- else if ( pm_keymatch( argv[argn], "-pixels", 2 ) )
- {
- if ( specxsize || specysize || specxscale || specyscale )
- pm_error( "can't use -pixels with any other specifiers" );
- ++argn;
- if ( argn == argc || sscanf( argv[argn], "%d", &newpixels ) != 1 )
- pm_usage( usage );
- if ( newpixels <= 0 )
- pm_error( "number of pixels must be greater than 0" );
- }
- else
- pm_usage( usage );
- ++argn;
- }
-
- if ( ! ( specxscale || specyscale || specxsize || specysize || newpixels ) )
- {
- /* No flags specified, so a single scale factor is required. */
- if ( argn == argc )
- pm_usage( usage );
- if ( sscanf( argv[argn], "%f", &xscale ) != 1 )
- pm_usage( usage );
- if ( xscale <= 0.0 )
- pm_error( "scale must be greater than 0" );
- ++argn;
- yscale = xscale;
- specxscale = specyscale = 1;
- }
-
- /* Now get input file. */
- if ( argn != argc )
- {
- ifp = pm_openr( argv[argn] );
- ++argn;
- }
- else
- ifp = stdin;
-
- if ( argn != argc )
- pm_usage( usage );
-
- pnm_pbmmaxval = PNM_MAXMAXVAL; /* use larger value for better results */
- pnm_readpnminit( ifp, &cols, &rows, &maxval, &format );
-
- /* Promote PBM files to PGM. */
- if ( PNM_FORMAT_TYPE(format) == PBM_TYPE )
- {
- newformat = PGM_TYPE;
- pm_message( "promoting from PBM to PGM" );
- }
- else
- newformat = format;
-
- /* Compute all sizes and scales. */
- if ( newpixels )
- if ( rows * cols <= newpixels )
- {
- newrows = rows;
- newcols = cols;
- xscale = yscale = 1.0;
- }
- else
- {
- xscale = yscale =
- sqrt( (float) newpixels / (float) cols / (float) rows );
- specxscale = specyscale = 1;
- }
-
- if ( specxysize )
- if ( (float) newcols / (float) newrows > (float) cols / (float) rows )
- specxsize = 0;
- else
- specysize = 0;
-
- if ( specxsize )
- xscale = (float) newcols / (float) cols;
- else if ( specxscale )
- newcols = cols * xscale + 0.999;
-
- if ( specysize )
- yscale = (float) newrows / (float) rows;
- else if ( specyscale )
- newrows = rows * yscale + 0.999;
- else
- if ( specxsize )
- {
- yscale = xscale;
- newrows = rows * yscale + 0.999;
- }
- else
- {
- yscale = 1.0;
- newrows = rows;
- }
-
- if ( ! ( specxsize || specxscale ) )
- if ( specysize )
- {
- xscale = yscale;
- newcols = cols * xscale + 0.999;
- }
- else
- {
- xscale = 1.0;
- newcols = cols;
- }
-
- sxscale = xscale * SCALE;
- syscale = yscale * SCALE;
-
- xelrow = pnm_allocrow( cols );
- if ( newrows == rows ) /* shortcut Y scaling if possible */
- tempxelrow = xelrow;
- else
- tempxelrow = pnm_allocrow( cols );
- rs = (long*) pm_allocrow( cols, sizeof(long) );
- gs = (long*) pm_allocrow( cols, sizeof(long) );
- bs = (long*) pm_allocrow( cols, sizeof(long) );
- rowsread = 0;
- fracrowleft = syscale;
- needtoreadrow = 1;
- for ( col = 0; col < cols; ++col )
- rs[col] = gs[col] = bs[col] = HALFSCALE;
- fracrowtofill = SCALE;
-
- pnm_writepnminit( stdout, newcols, newrows, maxval, newformat, 0 );
- newxelrow = pnm_allocrow( newcols );
-
- for ( row = 0; row < newrows; ++row )
- {
- /* First scale Y from xelrow into tempxelrow. */
- if ( newrows == rows ) /* shortcut Y scaling if possible */
- {
- pnm_readpnmrow( ifp, xelrow, cols, maxval, format );
- }
- else
- {
- while ( fracrowleft < fracrowtofill )
- {
- if ( needtoreadrow )
- if ( rowsread < rows )
- {
- pnm_readpnmrow( ifp, xelrow, cols, maxval, format );
- ++rowsread;
- /* needtoreadrow = 0; */
- }
- switch ( PNM_FORMAT_TYPE(format) )
- {
- case PPM_TYPE:
- for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
- {
- rs[col] += fracrowleft * PPM_GETR( *xP );
- gs[col] += fracrowleft * PPM_GETG( *xP );
- bs[col] += fracrowleft * PPM_GETB( *xP );
- }
- break;
-
- default:
- for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
- gs[col] += fracrowleft * PNM_GET1( *xP );
- break;
- }
- fracrowtofill -= fracrowleft;
- fracrowleft = syscale;
- needtoreadrow = 1;
- }
- /* Now fracrowleft is >= fracrowtofill, so we can produce a row. */
- if ( needtoreadrow )
- if ( rowsread < rows )
- {
- pnm_readpnmrow( ifp, xelrow, cols, maxval, format );
- ++rowsread;
- needtoreadrow = 0;
- }
- switch ( PNM_FORMAT_TYPE(format) )
- {
- case PPM_TYPE:
- for ( col = 0, xP = xelrow, nxP = tempxelrow;
- col < cols; ++col, ++xP, ++nxP )
- {
- register long r, g, b;
-
- r = rs[col] + fracrowtofill * PPM_GETR( *xP );
- g = gs[col] + fracrowtofill * PPM_GETG( *xP );
- b = bs[col] + fracrowtofill * PPM_GETB( *xP );
- r /= SCALE;
- if ( r > maxval ) r = maxval;
- g /= SCALE;
- if ( g > maxval ) g = maxval;
- b /= SCALE;
- if ( b > maxval ) b = maxval;
- PPM_ASSIGN( *nxP, r, g, b );
- rs[col] = gs[col] = bs[col] = HALFSCALE;
- }
- break;
-
- default:
- for ( col = 0, xP = xelrow, nxP = tempxelrow;
- col < cols; ++col, ++xP, ++nxP )
- {
- register long g;
-
- g = gs[col] + fracrowtofill * PNM_GET1( *xP );
- g /= SCALE;
- if ( g > maxval ) g = maxval;
- PNM_ASSIGN1( *nxP, g );
- gs[col] = HALFSCALE;
- }
- break;
- }
- fracrowleft -= fracrowtofill;
- if ( fracrowleft == 0 )
- {
- fracrowleft = syscale;
- needtoreadrow = 1;
- }
- fracrowtofill = SCALE;
- }
-
- /* Now scale X from tempxelrow into newxelrow and write it out. */
- if ( newcols == cols ) /* shortcut X scaling if possible */
- pnm_writepnmrow( stdout, tempxelrow, newcols, maxval, newformat, 0 );
- else
- {
- register long r, g, b;
- register long fraccoltofill, fraccolleft;
- register int needcol;
-
- nxP = newxelrow;
- fraccoltofill = SCALE;
- r = g = b = HALFSCALE;
- needcol = 0;
- for ( col = 0, xP = tempxelrow; col < cols; ++col, ++xP )
- {
- fraccolleft = sxscale;
- while ( fraccolleft >= fraccoltofill )
- {
- if ( needcol )
- {
- ++nxP;
- r = g = b = HALFSCALE;
- }
- switch ( PNM_FORMAT_TYPE(format) )
- {
- case PPM_TYPE:
- r += fraccoltofill * PPM_GETR( *xP );
- g += fraccoltofill * PPM_GETG( *xP );
- b += fraccoltofill * PPM_GETB( *xP );
- r /= SCALE;
- if ( r > maxval ) r = maxval;
- g /= SCALE;
- if ( g > maxval ) g = maxval;
- b /= SCALE;
- if ( b > maxval ) b = maxval;
- PPM_ASSIGN( *nxP, r, g, b );
- break;
-
- default:
- g += fraccoltofill * PNM_GET1( *xP );
- g /= SCALE;
- if ( g > maxval ) g = maxval;
- PNM_ASSIGN1( *nxP, g );
- break;
- }
- fraccolleft -= fraccoltofill;
- fraccoltofill = SCALE;
- needcol = 1;
- }
- if ( fraccolleft > 0 )
- {
- if ( needcol )
- {
- ++nxP;
- r = g = b = HALFSCALE;
- needcol = 0;
- }
- switch ( PNM_FORMAT_TYPE(format) )
- {
- case PPM_TYPE:
- r += fraccolleft * PPM_GETR( *xP );
- g += fraccolleft * PPM_GETG( *xP );
- b += fraccolleft * PPM_GETB( *xP );
- break;
-
- default:
- g += fraccolleft * PNM_GET1( *xP );
- break;
- }
- fraccoltofill -= fraccolleft;
- }
- }
- if ( fraccoltofill > 0 )
- {
- --xP;
- switch ( PNM_FORMAT_TYPE(format) )
- {
- case PPM_TYPE:
- r += fraccoltofill * PPM_GETR( *xP );
- g += fraccoltofill * PPM_GETG( *xP );
- b += fraccoltofill * PPM_GETB( *xP );
- break;
-
- default:
- g += fraccoltofill * PNM_GET1( *xP );
- break;
- }
- }
- if ( ! needcol )
- {
- switch ( PNM_FORMAT_TYPE(format) )
- {
- case PPM_TYPE:
- r /= SCALE;
- if ( r > maxval ) r = maxval;
- g /= SCALE;
- if ( g > maxval ) g = maxval;
- b /= SCALE;
- if ( b > maxval ) b = maxval;
- PPM_ASSIGN( *nxP, r, g, b );
- break;
-
- default:
- g /= SCALE;
- if ( g > maxval ) g = maxval;
- PNM_ASSIGN1( *nxP, g );
- break;
- }
- }
- pnm_writepnmrow( stdout, newxelrow, newcols, maxval, newformat, 0 );
- }
- }
-
- pm_close( ifp );
- pm_close( stdout );
-
- exit( 0 );
- }
-