Blame editor/pamenlarge.c

Packit 78deda
/*=============================================================================
Packit 78deda
                             pamenlarge
Packit 78deda
===============================================================================
Packit 78deda
  By Bryan Henderson 2004.09.26.  Contributed to the public domain by its
Packit 78deda
  author.
Packit 78deda
=============================================================================*/
Packit 78deda
Packit 78deda
#include "netpbm/mallocvar.h"
Packit 78deda
#include "netpbm/pm_c_util.h"
Packit 78deda
#include "netpbm/pam.h"
Packit 78deda
#include "netpbm/pbm.h"
Packit 78deda
Packit 78deda
struct cmdlineInfo {
Packit 78deda
    /* All the information the user supplied in the command line,
Packit 78deda
       in a form easy for the program to use.
Packit 78deda
    */
Packit 78deda
    const char *inputFilespec;  
Packit 78deda
    unsigned int scaleFactor;
Packit 78deda
};
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
parseCommandLine(int                  const argc,
Packit 78deda
                 const char **        const argv,
Packit 78deda
                 struct cmdlineInfo * const cmdlineP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Note that the file spec array we return is stored in the storage that
Packit 78deda
   was passed to us as the argv array.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    if (argc-1 < 1)
Packit 78deda
        pm_error("You must specify at least one argument:  The scale factor");
Packit 78deda
    else {
Packit 78deda
        cmdlineP->scaleFactor = atoi(argv[1]);
Packit 78deda
        
Packit 78deda
        if (cmdlineP->scaleFactor < 1)
Packit 78deda
            pm_error("Scale factor must be an integer at least 1.  "
Packit 78deda
                     "You specified '%s'", argv[1]);
Packit 78deda
Packit 78deda
        if (argc-1 >= 2)
Packit 78deda
            cmdlineP->inputFilespec = argv[2];
Packit 78deda
        else
Packit 78deda
            cmdlineP->inputFilespec = "-";
Packit 78deda
    }
Packit 78deda
}        
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
makeOutputRowMap(tuple **     const outTupleRowP,
Packit 78deda
                 struct pam * const outpamP,
Packit 78deda
                 struct pam * const inpamP,
Packit 78deda
                 tuple *      const inTuplerow) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Create a tuple *outTupleRowP which is actually a row of pointers into
Packit 78deda
   inTupleRow[], so as to map input pixels to output pixels by stretching.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    tuple * newtuplerow;
Packit 78deda
    int col;
Packit 78deda
Packit 78deda
    MALLOCARRAY_NOFAIL(newtuplerow, outpamP->width);
Packit 78deda
Packit 78deda
    for (col = 0 ; col < inpamP->width; ++col) {
Packit 78deda
        unsigned int const scaleFactor = outpamP->width / inpamP->width;
Packit 78deda
        unsigned int subcol;
Packit 78deda
Packit 78deda
        for (subcol = 0; subcol < scaleFactor; ++subcol)
Packit 78deda
            newtuplerow[col * scaleFactor + subcol] = inTuplerow[col];
Packit 78deda
    }
Packit 78deda
    *outTupleRowP = newtuplerow;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
validateComputableDimensions(unsigned int const width,
Packit 78deda
                             unsigned int const height,
Packit 78deda
                             unsigned int const scaleFactor) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Make sure that multiplication for output image width and height do not
Packit 78deda
   overflow.
Packit 78deda
   See validateComputetableSize() in libpam.c
Packit 78deda
   and pbm_readpbminitrest() in libpbm2.c
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int const maxWidthHeight = INT_MAX - 2;
Packit 78deda
    unsigned int const maxScaleFactor = maxWidthHeight / MAX(height, width);
Packit 78deda
Packit 78deda
    if (scaleFactor > maxScaleFactor)
Packit 78deda
       pm_error("Scale factor '%u' too large.  "
Packit 78deda
                "The maximum for this %u x %u input image is %u.",
Packit 78deda
                scaleFactor, width, height, maxScaleFactor);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
enlargePbmRowHorizontally(struct pam *          const inpamP,
Packit 78deda
                          const unsigned char * const inrow,
Packit 78deda
                          unsigned int          const inColChars,
Packit 78deda
                          unsigned int          const outColChars,
Packit 78deda
                          unsigned int          const scaleFactor,
Packit 78deda
                          unsigned char *       const outrow) {
Packit 78deda
Packit 78deda
    static unsigned char const dbl[16] = { 
Packit 78deda
        0x00, 0x03, 0x0C, 0x0F, 0x30, 0x33, 0x3C, 0x3F, 
Packit 78deda
        0xC0, 0xC3, 0xCC, 0xCF, 0xF0, 0xF3, 0xFC, 0xFF };
Packit 78deda
Packit 78deda
    static unsigned char const trp1[8] = { 
Packit 78deda
        0x00, 0x03, 0x1C, 0x1F, 0xE0, 0xE3, 0xFC, 0xFF };
Packit 78deda
        
Packit 78deda
    static unsigned char const trp2[16] = { 
Packit 78deda
        0x00, 0x01, 0x0E, 0x0F, 0x70, 0x71, 0x7E, 0x7F,
Packit 78deda
        0x80, 0x81, 0x8E, 0x8F, 0xF0, 0xF1, 0xFE, 0xFF };
Packit 78deda
Packit 78deda
    static unsigned char const trp3[8] = { 
Packit 78deda
        0x00, 0x07, 0x38, 0x3F, 0xC0, 0xC7, 0xF8, 0xFF };
Packit 78deda
Packit 78deda
    static unsigned char const quad[4] = { 0x00, 0x0F, 0xF0, 0xFF };
Packit 78deda
Packit 78deda
    static unsigned char const quin2[8] = {
Packit 78deda
        0x00, 0x01, 0x3E, 0x3F, 0xC0, 0xC1, 0xFE, 0xFF };
Packit 78deda
Packit 78deda
    static unsigned char const quin4[8] = {
Packit 78deda
        0x00, 0x03, 0x7C, 0x7F, 0x80, 0x83, 0xFC, 0xFF };
Packit 78deda
Packit 78deda
    static unsigned int const pair[4] = { 0x0000, 0x00FF, 0xFF00, 0xFFFF };
Packit 78deda
Packit 78deda
    unsigned int colChar;
Packit 78deda
Packit 78deda
    switch (scaleFactor) {
Packit 78deda
    case 1:  break; /* outrow set to inrow */
Packit 78deda
    case 2:  /* Make outrow using prefabricated parts (same for 3, 5). */ 
Packit 78deda
        for (colChar = 0; colChar < inColChars; ++colChar) { 
Packit 78deda
            outrow[colChar*2]   = dbl[(inrow[colChar] & 0xF0) >> 4];
Packit 78deda
            outrow[colChar*2+1] = dbl[(inrow[colChar] & 0x0F) >> 0];
Packit 78deda
            /* Possible outrow overrun by one byte. */
Packit 78deda
        }
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case 3:
Packit 78deda
        for (colChar = 0; colChar < inColChars; ++colChar) { 
Packit 78deda
            outrow[colChar*3]   = trp1[(inrow[colChar] & 0xF0) >> 5];
Packit 78deda
            outrow[colChar*3+1] = trp2[(inrow[colChar] >> 2) & 0x0F];
Packit 78deda
            outrow[colChar*3+2] = trp3[(inrow[colChar] >> 0) & 0x07];
Packit 78deda
        }
Packit 78deda
        break;  
Packit 78deda
Packit 78deda
    case 4:
Packit 78deda
        for (colChar = 0; colChar < inColChars; ++colChar) {
Packit 78deda
            unsigned int i;
Packit 78deda
            for (i = 0; i < 4; ++i) 
Packit 78deda
                outrow[colChar*4+i]=
Packit 78deda
                    quad[(inrow[colChar] >> (6 - 2 * i)) & 0x03]; 
Packit 78deda
        }
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case 5:
Packit 78deda
        for (colChar = 0; colChar < inColChars; ++colChar) { 
Packit 78deda
            outrow[colChar*5]   = pair [(inrow[colChar] >> 6) & 0x03] >> 5; 
Packit 78deda
            outrow[colChar*5+1] = quin2[(inrow[colChar] >> 4) & 0x07] >> 0; 
Packit 78deda
            outrow[colChar*5+2] = quad [(inrow[colChar] >> 3) & 0x03] >> 0; 
Packit 78deda
            outrow[colChar*5+3] = quin4[(inrow[colChar] >> 1) & 0x07] >> 0;
Packit 78deda
            outrow[colChar*5+4] = pair [(inrow[colChar] >> 0) & 0x03] >> 3; 
Packit 78deda
        }
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case 6:  /* Compound of 2 and 3 */ 
Packit 78deda
        for (colChar = 0; colChar < inColChars; ++colChar) { 
Packit 78deda
            unsigned char const hi = dbl[(inrow[colChar] & 0xF0) >> 4];
Packit 78deda
            unsigned char const lo = dbl[(inrow[colChar] & 0x0F) >> 0];
Packit 78deda
Packit 78deda
            outrow[colChar*6]   = trp1[(hi & 0xF0) >> 5];
Packit 78deda
            outrow[colChar*6+1] = trp2[(hi >> 2) & 0x0F];
Packit 78deda
            outrow[colChar*6+2] = trp3[hi & 0x07];
Packit 78deda
Packit 78deda
            outrow[colChar*6+3] = trp1[(lo & 0xF0) >> 5];
Packit 78deda
            outrow[colChar*6+4] = trp2[(lo >> 2) & 0x0F];
Packit 78deda
            outrow[colChar*6+5] = trp3[lo & 0x07];
Packit 78deda
        }
Packit 78deda
        break;  
Packit 78deda
Packit 78deda
    case 7:
Packit 78deda
        for (colChar = 0; colChar < inColChars; ++colChar) { 
Packit 78deda
            uint32_t hi, lo;
Packit 78deda
Packit 78deda
            hi = inrow[colChar] >> 4;
Packit 78deda
            hi = ((((hi>>1) * 0x00082080) | (0x01 & hi)) & 0x00204081 ) * 0x7F;
Packit 78deda
            hi >>= 4;
Packit 78deda
            outrow[colChar*7]   =  (unsigned char) ( hi >> 16);
Packit 78deda
            outrow[colChar*7+1] =  (unsigned char) ((hi >>  8) & 0xFF);
Packit 78deda
            outrow[colChar*7+2] =  (unsigned char) ((hi >>  0) & 0xFF);
Packit 78deda
Packit 78deda
            lo = inrow[colChar] & 0x001F;
Packit 78deda
            lo = ((((lo>>1) * 0x02082080) | (0x01 & lo)) & 0x10204081 ) * 0x7F;
Packit 78deda
            outrow[colChar*7+3] =  (unsigned char) ((lo >> 24) & 0xFF);
Packit 78deda
            outrow[colChar*7+4] =  (unsigned char) ((lo >> 16) & 0xFF); 
Packit 78deda
            outrow[colChar*7+5] =  (unsigned char) ((lo >>  8) & 0xFF);
Packit 78deda
            outrow[colChar*7+6] =  (unsigned char) ((lo >>  0) & 0xFF);
Packit 78deda
        }
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case 8:
Packit 78deda
        for (colChar = 0; colChar < inColChars; ++colChar) { 
Packit 78deda
            unsigned int i;
Packit 78deda
            for (i = 0; i < 8; ++i) {
Packit 78deda
                outrow[colChar*8+i] =
Packit 78deda
                    ((inrow[colChar] >> (7-i)) & 0x01) *0xFF;
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case 9:
Packit 78deda
        for (colChar = 0; colChar < inColChars; ++colChar) { 
Packit 78deda
            outrow[colChar*9]   =  ((inrow[colChar] >> 7) & 0x01) * 0xFF;
Packit 78deda
            outrow[colChar*9+1] =  pair[(inrow[colChar] >> 6) & 0x03] >> 1; 
Packit 78deda
            outrow[colChar*9+2] =  pair[(inrow[colChar] >> 5) & 0x03] >> 2; 
Packit 78deda
            outrow[colChar*9+3] =  pair[(inrow[colChar] >> 4) & 0x03] >> 3; 
Packit 78deda
            outrow[colChar*9+4] =  pair[(inrow[colChar] >> 3) & 0x03] >> 4; 
Packit 78deda
            outrow[colChar*9+5] =  pair[(inrow[colChar] >> 2) & 0x03] >> 5; 
Packit 78deda
            outrow[colChar*9+6] =  pair[(inrow[colChar] >> 1) & 0x03] >> 6; 
Packit 78deda
            outrow[colChar*9+7] =  pair[(inrow[colChar] >> 0) & 0x03] >> 7; 
Packit 78deda
            outrow[colChar*9+8] =  (inrow[colChar] & 0x01) * 0xFF;
Packit 78deda
        }
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case 10:
Packit 78deda
        for (colChar = 0; colChar < inColChars; ++colChar) { 
Packit 78deda
            outrow[colChar*10]   = ((inrow[colChar] >> 7) & 0x01 ) * 0xFF;
Packit 78deda
            outrow[colChar*10+1] = pair[(inrow[colChar] >> 6) & 0x03] >> 2; 
Packit 78deda
            outrow[colChar*10+2] = pair[(inrow[colChar] >> 5) & 0x03] >> 4; 
Packit 78deda
            outrow[colChar*10+3] = pair[(inrow[colChar] >> 4) & 0x03] >> 6;
Packit 78deda
            outrow[colChar*10+4] = ((inrow[colChar] >> 4) & 0x01) * 0xFF;
Packit 78deda
            outrow[colChar*10+5] = ((inrow[colChar] >> 3) & 0x01) * 0xFF; 
Packit 78deda
            outrow[colChar*10+6] = pair[(inrow[colChar] >> 2) & 0x03] >> 2; 
Packit 78deda
            outrow[colChar*10+7] = pair[(inrow[colChar] >> 1) & 0x03] >> 4; 
Packit 78deda
            outrow[colChar*10+8] = pair[(inrow[colChar] >> 0) & 0x03] >> 6; 
Packit 78deda
            outrow[colChar*10+9] = ((inrow[colChar] >> 0) & 0x01) * 0xFF;
Packit 78deda
        }
Packit 78deda
        break;
Packit 78deda
 
Packit 78deda
Packit 78deda
    default:
Packit 78deda
        /*  Unlike the above cases, we iterate through outrow.  To compute the
Packit 78deda
            color composition of each outrow byte, we consult a single bit or
Packit 78deda
            two consecutive bits in inrow.
Packit 78deda
Packit 78deda
            Color changes never happen twice in a single outrow byte.
Packit 78deda
Packit 78deda
            This is a generalization of above routines for scale factors
Packit 78deda
            9 and 10.
Packit 78deda
Packit 78deda
            Logic works for scale factors 4, 6, 7, 8, and above (but not 5).
Packit 78deda
        */
Packit 78deda
Packit 78deda
        for (colChar = 0; colChar < outColChars; ++colChar) {
Packit 78deda
            unsigned int const mult = scaleFactor;
Packit 78deda
            unsigned int const mod = colChar % mult;
Packit 78deda
            unsigned int const bit = (mod*8)/mult;
Packit 78deda
            /* source bit position, leftmost=0 */
Packit 78deda
            unsigned int const offset = mult - (mod*8)%mult;
Packit 78deda
            /* number of outrow bits derived from the same
Packit 78deda
               "source" inrow bit, starting at and to the right
Packit 78deda
               of leftmost bit of outrow byte, inclusive
Packit 78deda
            */
Packit 78deda
Packit 78deda
            if (offset >= 8)  /* Bits in outrow byte are all 1 or 0 */
Packit 78deda
                outrow[colChar] =
Packit 78deda
                    (inrow[colChar/mult] >> (7-bit) & 0x01) * 0xFF;
Packit 78deda
            else           /* Two inrow bits influence this outrow byte */ 
Packit 78deda
                outrow[colChar] = (unsigned char)
Packit 78deda
                    (pair[inrow[colChar/mult] >> (6-bit) & 0x03] >> offset)
Packit 78deda
                    & 0xFF;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
enlargePbm(struct pam * const inpamP,
Packit 78deda
           unsigned int const scaleFactor,
Packit 78deda
           FILE *       const ofP) {
Packit 78deda
Packit 78deda
    unsigned char * inrow;
Packit 78deda
    unsigned char * outrow;
Packit 78deda
Packit 78deda
    unsigned int row;
Packit 78deda
Packit 78deda
    unsigned int const outcols = inpamP->width * scaleFactor;
Packit 78deda
    unsigned int const outrows = inpamP->height * scaleFactor;
Packit 78deda
    unsigned int const inColChars  = pbm_packed_bytes(inpamP->width);
Packit 78deda
    unsigned int const outColChars = pbm_packed_bytes(outcols);
Packit 78deda
Packit 78deda
    inrow  = pbm_allocrow_packed(inpamP->width);
Packit 78deda
    
Packit 78deda
    if (scaleFactor == 1)
Packit 78deda
        outrow = inrow;
Packit 78deda
    else  {
Packit 78deda
        /* Allow writes beyond outrow data end when scaleFactor is
Packit 78deda
           one of the special fast cases: 2, 3, 4, 5, 6, 7, 8, 9, 10.
Packit 78deda
        */
Packit 78deda
        unsigned int const rightPadding = 
Packit 78deda
            scaleFactor > 10 ? 0 : (scaleFactor - 1) * 8;  
Packit 78deda
        outrow = pbm_allocrow_packed(outcols + rightPadding);
Packit 78deda
    }
Packit 78deda
Packit 78deda
    pbm_writepbminit(ofP, outcols, outrows, 0);
Packit 78deda
    
Packit 78deda
    for (row = 0; row < inpamP->height; ++row) {
Packit 78deda
        unsigned int i;
Packit 78deda
Packit 78deda
        pbm_readpbmrow_packed(inpamP->file, inrow, inpamP->width,
Packit 78deda
                              inpamP->format);
Packit 78deda
Packit 78deda
        if (outcols % 8 > 0)           /* clean final partial byte */ 
Packit 78deda
            pbm_cleanrowend_packed(inrow, inpamP->width);
Packit 78deda
Packit 78deda
        enlargePbmRowHorizontally(inpamP, inrow, inColChars, outColChars,
Packit 78deda
                                  scaleFactor, outrow);
Packit 78deda
Packit 78deda
        for (i = 0; i < scaleFactor; ++i)  
Packit 78deda
            pbm_writepbmrow_packed(ofP, outrow, outcols, 0);
Packit 78deda
    }
Packit 78deda
    
Packit 78deda
    if (outrow != inrow)
Packit 78deda
        pbm_freerow(outrow);
Packit 78deda
Packit 78deda
    pbm_freerow(inrow);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
enlargeGeneral(struct pam * const inpamP,
Packit 78deda
               unsigned int const scaleFactor,
Packit 78deda
               FILE *       const ofP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Enlarge the input image described by *pamP.
Packit 78deda
Packit 78deda
   Assume the dimensions won't cause an arithmetic overflow.
Packit 78deda
Packit 78deda
   This works on all kinds of images, but is slower than enlargePbm on
Packit 78deda
   PBM.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    struct pam outpam; 
Packit 78deda
    tuple * tuplerow;
Packit 78deda
    tuple * newtuplerow;
Packit 78deda
    unsigned int row;
Packit 78deda
Packit 78deda
    outpam = *inpamP; 
Packit 78deda
    outpam.file   = ofP;
Packit 78deda
    outpam.width  = inpamP->width  * scaleFactor;
Packit 78deda
    outpam.height = inpamP->height * scaleFactor; 
Packit 78deda
Packit 78deda
    pnm_writepaminit(&outpam);
Packit 78deda
Packit 78deda
    tuplerow = pnm_allocpamrow(inpamP);
Packit 78deda
Packit 78deda
    makeOutputRowMap(&newtuplerow, &outpam, inpamP, tuplerow);
Packit 78deda
Packit 78deda
    for (row = 0; row < inpamP->height; ++row) {
Packit 78deda
        pnm_readpamrow(inpamP, tuplerow);
Packit 78deda
        pnm_writepamrowmult(&outpam, newtuplerow, scaleFactor);
Packit 78deda
    }
Packit 78deda
Packit 78deda
    free(newtuplerow);
Packit 78deda
Packit 78deda
    pnm_freepamrow(tuplerow);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
int
Packit 78deda
main(int           argc, 
Packit 78deda
     const char ** argv) {
Packit 78deda
Packit 78deda
    struct cmdlineInfo cmdline;
Packit 78deda
    FILE * ifP;
Packit 78deda
    struct pam inpam;
Packit 78deda
Packit 78deda
    pm_proginit(&argc, argv);
Packit 78deda
Packit 78deda
    parseCommandLine(argc, argv, &cmdline);
Packit 78deda
Packit 78deda
    ifP = pm_openr(cmdline.inputFilespec);
Packit 78deda
 
Packit 78deda
    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
Packit 78deda
    
Packit 78deda
    validateComputableDimensions(inpam.width, inpam.height,
Packit 78deda
                                 cmdline.scaleFactor); 
Packit 78deda
    
Packit 78deda
    if (PNM_FORMAT_TYPE(inpam.format) == PBM_TYPE)
Packit 78deda
        enlargePbm(&inpam, cmdline.scaleFactor, stdout);
Packit 78deda
    else
Packit 78deda
        enlargeGeneral(&inpam, cmdline.scaleFactor, stdout);
Packit 78deda
Packit 78deda
    pm_close(ifP);
Packit 78deda
    pm_close(stdout);
Packit 78deda
Packit 78deda
    return 0;
Packit 78deda
}
Packit 78deda