home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / netpbma.zip / pnm / pnmscale.c < prev    next >
C/C++ Source or Header  |  1993-11-29  |  12KB  |  465 lines

  1. /* pnmscale.c - read a portable anymap and scale it
  2. **
  3. ** Copyright (C) 1989, 1991 by Jef Poskanzer.
  4. **
  5. ** Permission to use, copy, modify, and distribute this software and its
  6. ** documentation for any purpose and without fee is hereby granted, provided
  7. ** that the above copyright notice appear in all copies and that both that
  8. ** copyright notice and this permission notice appear in supporting
  9. ** documentation.  This software is provided "as is" without express or
  10. ** implied warranty.
  11. */
  12.  
  13. #include <math.h>
  14. #include "pnm.h"
  15.  
  16. #define SCALE 4096
  17. #define HALFSCALE 2048
  18.  
  19. int
  20. main( argc, argv )
  21.     int argc;
  22.     char* argv[];
  23.     {
  24.     FILE* ifp;
  25.     xel* xelrow;
  26.     xel* tempxelrow;
  27.     xel* newxelrow;
  28.     register xel* xP;
  29.     register xel* nxP;
  30.     int argn, specxscale, specyscale, specxsize, specysize, specxysize;
  31.     int rows, cols, format, newformat, rowsread, newrows, newcols, newpixels;
  32.     register int row, col, needtoreadrow;
  33.     xelval maxval;
  34.     float xscale, yscale;
  35.     long sxscale, syscale;
  36.     register long fracrowtofill, fracrowleft;
  37.     long* rs;
  38.     long* gs;
  39.     long* bs;
  40.     char* usage = "<s> [pnmfile]\n \
  41.             -xsize|width|-ysize|-height <s> [pnmfile]\n \
  42.             -xscale|-yscale <s> [pnmfile]\n \
  43.             -xscale|-xsize|-width <s> -yscale|-ysize|-height <s> [pnmfile]\n \
  44.             -xysize <x> <y> [pnmfile]\n \
  45.             -pixels <n> [pnmfile]";
  46.  
  47.     pnm_init( &argc, argv );
  48.  
  49.     argn = 1;
  50.     specxscale = specyscale = specxsize = specysize = specxysize = newpixels = 0;
  51.  
  52.     while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
  53.     {
  54.     if ( pm_keymatch( argv[argn], "-xscale", 4 ) )
  55.         {
  56.         if ( specxscale )
  57.         pm_error( "already specified an x scale" );
  58.         if ( specxsize )
  59.         pm_error(
  60.             "only one of -xsize/-width and -xscale may be specified" );
  61.         ++argn;
  62.         if ( argn == argc || sscanf( argv[argn], "%f", &xscale ) != 1 )
  63.         pm_usage( usage );
  64.         if ( xscale <= 0.0 )
  65.         pm_error( "x scale must be greater than 0" );
  66.         specxscale = 1;
  67.         }
  68.     else if ( pm_keymatch( argv[argn], "-yscale", 4 ) )
  69.         {
  70.         if ( specyscale )
  71.         pm_error( "already specified a y scale" );
  72.         if ( specysize )
  73.         pm_error(
  74.             "only one of -ysize/-height and -yscale may be specified" );
  75.         ++argn;
  76.         if ( argn == argc || sscanf( argv[argn], "%f", &yscale ) != 1 )
  77.         pm_usage( usage );
  78.         if ( yscale <= 0.0 )
  79.         pm_error( "y scale must be greater than 0" );
  80.         specyscale = 1;
  81.         }
  82.     else if ( pm_keymatch( argv[argn], "-xsize", 4 ) ||
  83.               pm_keymatch( argv[argn], "-width", 2 ) )
  84.         {
  85.         if ( specxsize )
  86.         pm_error( "already specified a width" );
  87.         if ( specxscale )
  88.         pm_error(
  89.             "only one of -xscale and -xsize/-width may be specified" );
  90.         ++argn;
  91.         if ( argn == argc || sscanf( argv[argn], "%d", &newcols ) != 1 )
  92.         pm_usage( usage );
  93.         if ( newcols <= 0 )
  94.         pm_error( "new width must be greater than 0" );
  95.         specxsize = 1;
  96.         }
  97.     else if ( pm_keymatch( argv[argn], "-ysize", 4 ) ||
  98.               pm_keymatch( argv[argn], "-height", 2 ) )
  99.         {
  100.         if ( specysize )
  101.         pm_error( "already specified a height" );
  102.         if ( specyscale )
  103.         pm_error(
  104.             "only one of -yscale and -ysize/-height may be specified" );
  105.         ++argn;
  106.         if ( argn == argc || sscanf( argv[argn], "%d", &newrows ) != 1 )
  107.         pm_usage( usage );
  108.         if ( newrows <= 0 )
  109.         pm_error( "new height must be greater than 0" );
  110.         specysize = 1;
  111.         }
  112.     else if ( pm_keymatch( argv[argn], "-xysize", 3 ) )
  113.         {
  114.         if ( specxsize || specysize || specxscale || specyscale || newpixels )
  115.         pm_error( "can't use -xysize with any other specifiers" );
  116.         ++argn;
  117.         if ( argn == argc || sscanf( argv[argn], "%d", &newcols ) != 1 )
  118.         pm_usage( usage );
  119.         ++argn;
  120.         if ( argn == argc || sscanf( argv[argn], "%d", &newrows ) != 1 )
  121.         pm_usage( usage );
  122.         if ( newcols <= 0 || newrows <= 0 )
  123.         pm_error( "new width and height must be greater than 0" );
  124.         specxsize = 1;
  125.         specysize = 1;
  126.         specxysize = 1;
  127.         }
  128.     else if ( pm_keymatch( argv[argn], "-pixels", 2 ) )
  129.         {
  130.         if ( specxsize || specysize || specxscale || specyscale )
  131.         pm_error( "can't use -pixels with any other specifiers" );
  132.         ++argn;
  133.         if ( argn == argc || sscanf( argv[argn], "%d", &newpixels ) != 1 )
  134.         pm_usage( usage );
  135.         if ( newpixels <= 0 )
  136.         pm_error( "number of pixels must be greater than 0" );
  137.         }
  138.     else
  139.         pm_usage( usage );
  140.     ++argn;
  141.     }
  142.  
  143.     if ( ! ( specxscale || specyscale || specxsize || specysize || newpixels ) )
  144.     {
  145.     /* No flags specified, so a single scale factor is required. */
  146.     if ( argn == argc )
  147.         pm_usage( usage );
  148.     if ( sscanf( argv[argn], "%f", &xscale ) != 1 )
  149.         pm_usage( usage );
  150.     if ( xscale <= 0.0 )
  151.         pm_error( "scale must be greater than 0" );
  152.     ++argn;
  153.     yscale = xscale;
  154.     specxscale = specyscale = 1;
  155.     }
  156.  
  157.     /* Now get input file. */
  158.     if ( argn != argc )
  159.     {
  160.     ifp = pm_openr( argv[argn] );
  161.     ++argn;
  162.     }
  163.     else
  164.     ifp = stdin;
  165.  
  166.     if ( argn != argc )
  167.     pm_usage( usage );
  168.  
  169.     pnm_pbmmaxval = PNM_MAXMAXVAL;  /* use larger value for better results */
  170.     pnm_readpnminit( ifp, &cols, &rows, &maxval, &format );
  171.  
  172.     /* Promote PBM files to PGM. */
  173.     if ( PNM_FORMAT_TYPE(format) == PBM_TYPE )
  174.     {
  175.         newformat = PGM_TYPE;
  176.     pm_message( "promoting from PBM to PGM" );
  177.     }
  178.     else
  179.         newformat = format;
  180.  
  181.     /* Compute all sizes and scales. */
  182.     if ( newpixels )
  183.     if ( rows * cols <= newpixels )
  184.         {
  185.         newrows = rows;
  186.         newcols = cols;
  187.         xscale = yscale = 1.0;
  188.         }
  189.     else
  190.         {
  191.         xscale = yscale =
  192.         sqrt( (float) newpixels / (float) cols / (float) rows );
  193.         specxscale = specyscale = 1;
  194.         }
  195.  
  196.     if ( specxysize )
  197.     if ( (float) newcols / (float) newrows > (float) cols / (float) rows )
  198.         specxsize = 0;
  199.     else
  200.         specysize = 0;
  201.     
  202.     if ( specxsize )
  203.     xscale = (float) newcols / (float) cols;
  204.     else if ( specxscale )
  205.     newcols = cols * xscale + 0.999;
  206.  
  207.     if ( specysize )
  208.     yscale = (float) newrows / (float) rows;
  209.     else if ( specyscale )
  210.     newrows = rows * yscale + 0.999;
  211.     else
  212.     if ( specxsize )
  213.         {
  214.         yscale = xscale;
  215.         newrows = rows * yscale + 0.999;
  216.         }
  217.     else
  218.         {
  219.         yscale = 1.0;
  220.         newrows = rows;
  221.         }
  222.     
  223.     if ( ! ( specxsize || specxscale ) )
  224.     if ( specysize )
  225.         {
  226.         xscale = yscale;
  227.         newcols = cols * xscale + 0.999;
  228.         }
  229.     else
  230.         {
  231.         xscale = 1.0;
  232.         newcols = cols;
  233.         }
  234.  
  235.     sxscale = xscale * SCALE;
  236.     syscale = yscale * SCALE;
  237.  
  238.     xelrow = pnm_allocrow( cols );
  239.     if ( newrows == rows )    /* shortcut Y scaling if possible */
  240.     tempxelrow = xelrow;
  241.     else
  242.     tempxelrow = pnm_allocrow( cols );
  243.     rs = (long*) pm_allocrow( cols, sizeof(long) );
  244.     gs = (long*) pm_allocrow( cols, sizeof(long) );
  245.     bs = (long*) pm_allocrow( cols, sizeof(long) );
  246.     rowsread = 0;
  247.     fracrowleft = syscale;
  248.     needtoreadrow = 1;
  249.     for ( col = 0; col < cols; ++col )
  250.     rs[col] = gs[col] = bs[col] = HALFSCALE;
  251.     fracrowtofill = SCALE;
  252.  
  253.     pnm_writepnminit( stdout, newcols, newrows, maxval, newformat, 0 );
  254.     newxelrow = pnm_allocrow( newcols );
  255.  
  256.     for ( row = 0; row < newrows; ++row )
  257.     {
  258.     /* First scale Y from xelrow into tempxelrow. */
  259.     if ( newrows == rows )    /* shortcut Y scaling if possible */
  260.         {
  261.         pnm_readpnmrow( ifp, xelrow, cols, maxval, format );
  262.         }
  263.     else
  264.         {
  265.         while ( fracrowleft < fracrowtofill )
  266.         {
  267.         if ( needtoreadrow )
  268.             if ( rowsread < rows )
  269.             {
  270.             pnm_readpnmrow( ifp, xelrow, cols, maxval, format );
  271.             ++rowsread;
  272.             /* needtoreadrow = 0; */
  273.             }
  274.                 switch ( PNM_FORMAT_TYPE(format) )
  275.                     {
  276.                     case PPM_TYPE:
  277.             for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
  278.             {
  279.             rs[col] += fracrowleft * PPM_GETR( *xP );
  280.             gs[col] += fracrowleft * PPM_GETG( *xP );
  281.             bs[col] += fracrowleft * PPM_GETB( *xP );
  282.             }
  283.                     break;
  284.  
  285.                     default:
  286.             for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
  287.             gs[col] += fracrowleft * PNM_GET1( *xP );
  288.                     break;
  289.                     }
  290.         fracrowtofill -= fracrowleft;
  291.         fracrowleft = syscale;
  292.         needtoreadrow = 1;
  293.         }
  294.         /* Now fracrowleft is >= fracrowtofill, so we can produce a row. */
  295.         if ( needtoreadrow )
  296.         if ( rowsread < rows )
  297.             {
  298.             pnm_readpnmrow( ifp, xelrow, cols, maxval, format );
  299.             ++rowsread;
  300.             needtoreadrow = 0;
  301.             }
  302.         switch ( PNM_FORMAT_TYPE(format) )
  303.         {
  304.         case PPM_TYPE:
  305.         for ( col = 0, xP = xelrow, nxP = tempxelrow;
  306.               col < cols; ++col, ++xP, ++nxP )
  307.             {
  308.             register long r, g, b;
  309.  
  310.             r = rs[col] + fracrowtofill * PPM_GETR( *xP );
  311.             g = gs[col] + fracrowtofill * PPM_GETG( *xP );
  312.             b = bs[col] + fracrowtofill * PPM_GETB( *xP );
  313.             r /= SCALE;
  314.             if ( r > maxval ) r = maxval;
  315.             g /= SCALE;
  316.             if ( g > maxval ) g = maxval;
  317.             b /= SCALE;
  318.             if ( b > maxval ) b = maxval;
  319.             PPM_ASSIGN( *nxP, r, g, b );
  320.             rs[col] = gs[col] = bs[col] = HALFSCALE;
  321.             }
  322.         break;
  323.  
  324.         default:
  325.         for ( col = 0, xP = xelrow, nxP = tempxelrow;
  326.               col < cols; ++col, ++xP, ++nxP )
  327.             {
  328.             register long g;
  329.  
  330.             g = gs[col] + fracrowtofill * PNM_GET1( *xP );
  331.             g /= SCALE;
  332.             if ( g > maxval ) g = maxval;
  333.             PNM_ASSIGN1( *nxP, g );
  334.             gs[col] = HALFSCALE;
  335.             }
  336.         break;
  337.         }
  338.         fracrowleft -= fracrowtofill;
  339.         if ( fracrowleft == 0 )
  340.         {
  341.         fracrowleft = syscale;
  342.         needtoreadrow = 1;
  343.         }
  344.         fracrowtofill = SCALE;
  345.         }
  346.  
  347.     /* Now scale X from tempxelrow into newxelrow and write it out. */
  348.     if ( newcols == cols )    /* shortcut X scaling if possible */
  349.         pnm_writepnmrow( stdout, tempxelrow, newcols, maxval, newformat, 0 );
  350.     else
  351.         {
  352.         register long r, g, b;
  353.         register long fraccoltofill, fraccolleft;
  354.         register int needcol;
  355.  
  356.         nxP = newxelrow;
  357.         fraccoltofill = SCALE;
  358.         r = g = b = HALFSCALE;
  359.         needcol = 0;
  360.         for ( col = 0, xP = tempxelrow; col < cols; ++col, ++xP )
  361.         {
  362.         fraccolleft = sxscale;
  363.         while ( fraccolleft >= fraccoltofill )
  364.             {
  365.             if ( needcol )
  366.             {
  367.             ++nxP;
  368.             r = g = b = HALFSCALE;
  369.             }
  370.             switch ( PNM_FORMAT_TYPE(format) )
  371.             {
  372.             case PPM_TYPE:
  373.             r += fraccoltofill * PPM_GETR( *xP );
  374.             g += fraccoltofill * PPM_GETG( *xP );
  375.             b += fraccoltofill * PPM_GETB( *xP );
  376.             r /= SCALE;
  377.             if ( r > maxval ) r = maxval;
  378.             g /= SCALE;
  379.             if ( g > maxval ) g = maxval;
  380.             b /= SCALE;
  381.             if ( b > maxval ) b = maxval;
  382.             PPM_ASSIGN( *nxP, r, g, b );
  383.             break;
  384.  
  385.             default:
  386.             g += fraccoltofill * PNM_GET1( *xP );
  387.             g /= SCALE;
  388.             if ( g > maxval ) g = maxval;
  389.             PNM_ASSIGN1( *nxP, g );
  390.             break;
  391.             }
  392.             fraccolleft -= fraccoltofill;
  393.             fraccoltofill = SCALE;
  394.             needcol = 1;
  395.             }
  396.         if ( fraccolleft > 0 )
  397.             {
  398.             if ( needcol )
  399.             {
  400.             ++nxP;
  401.             r = g = b = HALFSCALE;
  402.             needcol = 0;
  403.             }
  404.             switch ( PNM_FORMAT_TYPE(format) )
  405.             {
  406.             case PPM_TYPE:
  407.             r += fraccolleft * PPM_GETR( *xP );
  408.             g += fraccolleft * PPM_GETG( *xP );
  409.             b += fraccolleft * PPM_GETB( *xP );
  410.             break;
  411.  
  412.             default:
  413.             g += fraccolleft * PNM_GET1( *xP );
  414.             break;
  415.             }
  416.             fraccoltofill -= fraccolleft;
  417.             }
  418.         }
  419.         if ( fraccoltofill > 0 )
  420.         {
  421.         --xP;
  422.         switch ( PNM_FORMAT_TYPE(format) )
  423.             {
  424.             case PPM_TYPE:
  425.             r += fraccoltofill * PPM_GETR( *xP );
  426.             g += fraccoltofill * PPM_GETG( *xP );
  427.             b += fraccoltofill * PPM_GETB( *xP );
  428.             break;
  429.  
  430.             default:
  431.             g += fraccoltofill * PNM_GET1( *xP );
  432.             break;
  433.             }
  434.         }
  435.         if ( ! needcol )
  436.         {
  437.                 switch ( PNM_FORMAT_TYPE(format) )
  438.                     {
  439.                     case PPM_TYPE:
  440.             r /= SCALE;
  441.             if ( r > maxval ) r = maxval;
  442.             g /= SCALE;
  443.             if ( g > maxval ) g = maxval;
  444.             b /= SCALE;
  445.             if ( b > maxval ) b = maxval;
  446.             PPM_ASSIGN( *nxP, r, g, b );
  447.                     break;
  448.  
  449.                     default:
  450.             g /= SCALE;
  451.             if ( g > maxval ) g = maxval;
  452.             PNM_ASSIGN1( *nxP, g );
  453.                     break;
  454.                     }
  455.         }
  456.         pnm_writepnmrow( stdout, newxelrow, newcols, maxval, newformat, 0 );
  457.         }
  458.     }
  459.  
  460.     pm_close( ifp );
  461.     pm_close( stdout );
  462.  
  463.     exit( 0 );
  464.     }
  465.