Blame converter/other/pamtopng.c

Packit 78deda
/*
Packit 78deda
** read a PNM/PAM image and produce a Portable Network Graphics (PNG) file
Packit 78deda
**
Packit 78deda
** derived from pnmtorast.c by Jef Poskanzer and pamrgbatopng.c by Bryan
Packit 78deda
** Henderson <bryanh@giraffe-data.com> and probably some other sources
Packit 78deda
**
Packit 78deda
** Copyright (C) 1995-1998 by Alexander Lehmann <alex@hal.rhein-main.de>
Packit 78deda
**                        and Willem van Schaik <willem@schaik.com>
Packit 78deda
** Copyright (C) 1999,2001 by Greg Roelofs <newt@pobox.com>
Packit 78deda
** Copyright (C) 2015 by Willem van Schaik <willem@schaik.com>
Packit 78deda
**
Packit 78deda
** Permission to use, copy, modify, and distribute this software and its
Packit 78deda
** documentation for any purpose and without fee is hereby granted, provided
Packit 78deda
** that the above copyright notice appear in all copies and that both that
Packit 78deda
** copyright notice and this permission notice appear in supporting
Packit 78deda
** documentation.  This software is provided "as is" without express or
Packit 78deda
** implied warranty.
Packit 78deda
*/
Packit 78deda
Packit 78deda
/*
Packit 78deda
  This Netpbm program pamtopng was derived in 2015 from the Netpbm program
Packit 78deda
  Pnmtopng. This was a nearly complete rewrite with the following goals:
Packit 78deda
Packit 78deda
  - Add ability to create a PNG alpha channel from the alpha channel in a
Packit 78deda
    PAM (format P7) file.
Packit 78deda
Packit 78deda
  - Simplify the 20 year old pnmtopng code. Because of the many, many features
Packit 78deda
    that program implements and its need for backward compatibility, the code
Packit 78deda
    had become rather complex.  This program is roughly 1/3 the size of
Packit 78deda
    pnmtopng.c that it replaces.
Packit 78deda
Packit 78deda
  - In 1995 bandwith was limited and therefore filesize had to be kept
Packit 78deda
    small. The original program tried to optimize for that by applying
Packit 78deda
    many "clever tricks". Today that isn't an issue anymore, so gone 
Packit 78deda
    are filters, palettes, etc. Also, image conversions were removed,
Packit 78deda
    because those should be done with other NetPBM tools.
Packit 78deda
Packit 78deda
  - Add ability to create iTXt (international language) chunks.
Packit 78deda
*/
Packit 78deda
Packit 78deda
Packit 78deda
#include <stdio.h>
Packit 78deda
#include <stdlib.h>
Packit 78deda
#include <time.h>
Packit 78deda
#include <png.h>
Packit 78deda
/* setjmp.h needs to be included after png.h */
Packit 78deda
#include <setjmp.h>
Packit 78deda
Packit 78deda
#include "pm_c_util.h"
Packit 78deda
#include "mallocvar.h"
Packit 78deda
#include "nstring.h"
Packit 78deda
#include "shhopt.h"
Packit 78deda
#include "pam.h"
Packit 78deda
#include "pngx.h"
Packit 78deda
#include "pngtxt.h"
Packit 78deda
Packit 78deda
Packit 78deda
/* global variable */
Packit 78deda
static bool verbose;
Packit 78deda
Packit 78deda
Packit 78deda
struct CmdlineInfo {
Packit 78deda
    const char * inputFileName;
Packit 78deda
    unsigned int verbose;
Packit 78deda
    unsigned int transparencySpec;
Packit 78deda
    const char * transparency;
Packit 78deda
    unsigned int chromaSpec;
Packit 78deda
    struct pngx_chroma chroma;
Packit 78deda
    unsigned int gammaSpec;
Packit 78deda
    float gamma;
Packit 78deda
    unsigned int srgbintentSpec;
Packit 78deda
    pngx_srgbIntent srgbintent;
Packit 78deda
    unsigned int textSpec;
Packit 78deda
    const char * text;
Packit 78deda
    unsigned int ztxtSpec;
Packit 78deda
    const char * ztxt;
Packit 78deda
    unsigned int itxtSpec;
Packit 78deda
    const char * itxt;
Packit 78deda
    unsigned int backgroundSpec;
Packit 78deda
    const char * background;
Packit 78deda
    unsigned int timeSpec;
Packit 78deda
    time_t time;
Packit 78deda
};
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
parseChromaOpt(const char *         const chromaOpt,
Packit 78deda
               struct pngx_chroma * const chromaP) {
Packit 78deda
Packit 78deda
    int count;
Packit 78deda
    
Packit 78deda
    count = sscanf(chromaOpt, "%f %f %f %f %f %f %f %f",
Packit 78deda
                   &chromaP->wx, &chromaP->wy,
Packit 78deda
                   &chromaP->rx, &chromaP->ry,
Packit 78deda
                   &chromaP->gx, &chromaP->gy,
Packit 78deda
                   &chromaP->bx, &chromaP->by);
Packit 78deda
Packit 78deda
    if (count != 6)
Packit 78deda
        pm_error("Invalid syntax for the -rgb option value '%s'.  "
Packit 78deda
                 "Should be 6 floating point number: "
Packit 78deda
                 "x and y for each of white, red, green, and blue",
Packit 78deda
                 chromaOpt);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
parseSrgbintentOpt(const char *      const srgbintentOpt,
Packit 78deda
                   pngx_srgbIntent * const srgbintentP) {
Packit 78deda
    
Packit 78deda
    if (streq(srgbintentOpt, "perceptual"))
Packit 78deda
        *srgbintentP = PNGX_PERCEPTUAL;
Packit 78deda
    else if (streq(srgbintentOpt, "relativecolorimetric"))
Packit 78deda
        *srgbintentP = PNGX_RELATIVE_COLORIMETRIC;
Packit 78deda
    else if (streq(srgbintentOpt, "saturation"))
Packit 78deda
        *srgbintentP = PNGX_SATURATION;
Packit 78deda
    else if (streq(srgbintentOpt, "absolutecolorimetric"))
Packit 78deda
        *srgbintentP = PNGX_ABSOLUTE_COLORIMETRIC;
Packit 78deda
    else
Packit 78deda
        pm_error("Unrecognized sRGB intent value '%s'.  We understand "
Packit 78deda
                 "only 'perceptual', 'relativecolorimetric', "
Packit 78deda
                 "'saturation', and 'absolutecolorimetric'",
Packit 78deda
                 srgbintentOpt);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
parseTimeOpt(const char * const timeOpt,
Packit 78deda
             time_t *     const timeP) {
Packit 78deda
Packit 78deda
    struct tm brokenTime;
Packit 78deda
    int year;
Packit 78deda
    int month;
Packit 78deda
    int count;
Packit 78deda
Packit 78deda
    count = sscanf(timeOpt, "%d-%d-%d %d:%d:%d",
Packit 78deda
                   &year,
Packit 78deda
                   &month,
Packit 78deda
                   &brokenTime.tm_mday,
Packit 78deda
                   &brokenTime.tm_hour,
Packit 78deda
                   &brokenTime.tm_min,
Packit 78deda
                   &brokenTime.tm_sec);
Packit 78deda
Packit 78deda
    if (count != 6)
Packit 78deda
        pm_error("Invalid value for -time '%s'.   It should have "
Packit 78deda
                 "the form [yy]yy-mm-dd hh:mm:ss.", timeOpt);
Packit 78deda
    
Packit 78deda
    if (year < 0)
Packit 78deda
        pm_error("Year is negative in -time value '%s'", timeOpt);
Packit 78deda
    if (year > 9999)
Packit 78deda
        pm_error("Year is more than 4 digits in -time value '%s'",
Packit 78deda
                 timeOpt);
Packit 78deda
    if (month < 0)
Packit 78deda
        pm_error("Month is negative in -time value '%s'", timeOpt);
Packit 78deda
    if (month > 12)
Packit 78deda
        pm_error("Month is >12 in -time value '%s'", timeOpt);
Packit 78deda
    if (brokenTime.tm_mday < 0)
Packit 78deda
        pm_error("Day of month is negative in -time value '%s'",
Packit 78deda
                 timeOpt);
Packit 78deda
    if (brokenTime.tm_mday > 31)
Packit 78deda
        pm_error("Day of month is >31 in -time value '%s'", timeOpt);
Packit 78deda
    if (brokenTime.tm_hour < 0)
Packit 78deda
        pm_error("Hour is negative in -time value '%s'", timeOpt);
Packit 78deda
    if (brokenTime.tm_hour > 23)
Packit 78deda
        pm_error("Hour is >23 in -time value '%s'", timeOpt);
Packit 78deda
    if (brokenTime.tm_min < 0)
Packit 78deda
        pm_error("Minute is negative in -time value '%s'", timeOpt);
Packit 78deda
    if (brokenTime.tm_min > 59)
Packit 78deda
        pm_error("Minute is >59 in -time value '%s'", timeOpt);
Packit 78deda
    if (brokenTime.tm_sec < 0)
Packit 78deda
        pm_error("Second is negative in -time value '%s'", timeOpt);
Packit 78deda
    if (brokenTime.tm_sec > 59)
Packit 78deda
        pm_error("Second is >59 in -time value '%s'", timeOpt);
Packit 78deda
Packit 78deda
    brokenTime.tm_mon = month - 1;
Packit 78deda
    if (year >= 1900)
Packit 78deda
        brokenTime.tm_year = year - 1900;
Packit 78deda
    else
Packit 78deda
        brokenTime.tm_year = year;
Packit 78deda
Packit 78deda
    /* Note that mktime() considers brokeTime to be in local time.
Packit 78deda
       This is what we want, since we got it from a user.  User should
Packit 78deda
       set his local time zone to UTC if he wants absolute time.
Packit 78deda
    */
Packit 78deda
    *timeP = mktime(&brokenTime);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
parseCommandLine (int                  argc,
Packit 78deda
                  const char **        argv,
Packit 78deda
                  struct CmdlineInfo * const cmdlineP) {
Packit 78deda
    
Packit 78deda
    optEntry * option_def;
Packit 78deda
    optStruct3 opt;
Packit 78deda
    unsigned int option_def_index = 0;  /* incremented by OPTENT3 */
Packit 78deda
Packit 78deda
    const char * srgbintent;
Packit 78deda
    const char * chroma;
Packit 78deda
    const char * time;
Packit 78deda
Packit 78deda
    MALLOCARRAY(option_def, 100);
Packit 78deda
Packit 78deda
    OPTENT3(0,  "verbose",      OPT_FLAG,       NULL,
Packit 78deda
            &cmdlineP->verbose,        0);
Packit 78deda
    OPTENT3(0,  "transparency", OPT_STRING,     &cmdlineP->transparency,
Packit 78deda
            &cmdlineP->transparencySpec, 0);
Packit 78deda
    OPTENT3(0,  "chroma",       OPT_STRING,     &chroma,
Packit 78deda
            &cmdlineP->chromaSpec,     0);
Packit 78deda
    OPTENT3(0,  "gamma",        OPT_FLOAT,      &cmdlineP->gamma,
Packit 78deda
            &cmdlineP->gammaSpec,      0);
Packit 78deda
    OPTENT3(0,  "srgbintent",   OPT_STRING,     &srgbintent,
Packit 78deda
            &cmdlineP->srgbintentSpec, 0);
Packit 78deda
    OPTENT3(0,  "text",         OPT_STRING,     &cmdlineP->text,
Packit 78deda
            &cmdlineP->textSpec,       0);
Packit 78deda
    OPTENT3(0,  "ztxt",         OPT_STRING,     &cmdlineP->ztxt,
Packit 78deda
            &cmdlineP->ztxtSpec,       0);
Packit 78deda
    OPTENT3(0,  "itxt",         OPT_STRING,     &cmdlineP->itxt,
Packit 78deda
            &cmdlineP->itxtSpec,       0);
Packit 78deda
    OPTENT3(0,  "background",   OPT_STRING,     &cmdlineP->background,
Packit 78deda
            &cmdlineP->backgroundSpec, 0);
Packit 78deda
    OPTENT3(0,  "time",         OPT_STRING,        &time,
Packit 78deda
            &cmdlineP->timeSpec,       0);
Packit 78deda
Packit 78deda
    opt.opt_table = option_def;
Packit 78deda
    opt.short_allowed = false;  /* we have no short (old-fashioned) options */
Packit 78deda
    opt.allowNegNum = false;  /* we have no parms that are negative numbers */
Packit 78deda
Packit 78deda
    /* uses and sets argc, argv, and some of *cmdlineP and others */
Packit 78deda
    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
Packit 78deda
Packit 78deda
    if (cmdlineP->chromaSpec)
Packit 78deda
        parseChromaOpt(chroma, &cmdlineP->chroma);
Packit 78deda
Packit 78deda
    if (cmdlineP->srgbintentSpec)
Packit 78deda
        parseSrgbintentOpt(srgbintent, &cmdlineP->srgbintent);
Packit 78deda
Packit 78deda
    if (cmdlineP->timeSpec)
Packit 78deda
        parseTimeOpt(time, &cmdlineP->time);
Packit 78deda
    
Packit 78deda
    /* get the input-file or stdin pipe */
Packit 78deda
    if (argc-1 < 1)
Packit 78deda
        cmdlineP->inputFileName = "-";
Packit 78deda
    else if (argc-1 == 1)
Packit 78deda
        cmdlineP->inputFileName = argv[1];
Packit 78deda
    else
Packit 78deda
        pm_error("Program takes at most one argument: input file name.");
Packit 78deda
Packit 78deda
    free(option_def);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static png_byte
Packit 78deda
colorTypeFromInputType(const struct pam * const pamP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Analyse the Netpbm image for color-type and bit-depth
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    png_byte retval;
Packit 78deda
Packit 78deda
    if (pamP->depth < 1 && pamP->depth > 4)
Packit 78deda
        pm_error ("Number of color planes must be between 1 and 4 inclusive");
Packit 78deda
Packit 78deda
    if (pamP->maxval != 1 && pamP->maxval != 3 && pamP->maxval != 15 &&
Packit 78deda
        pamP->maxval != 255 && pamP->maxval != 65535)
Packit 78deda
        pm_error("The maxval of the input image is %u; "
Packit 78deda
                 "it must be 1, 3, 15, 255 or 65535", (unsigned)pamP->maxval);
Packit 78deda
Packit 78deda
    if (strneq(pamP->tuple_type, "RGB_ALPHA", 9)) {
Packit 78deda
        if (pamP->depth == 4)
Packit 78deda
            retval = PNG_COLOR_TYPE_RGB_ALPHA;
Packit 78deda
        else
Packit 78deda
            pm_error("Input tuple type is RGB_ALPHA, "
Packit 78deda
                     "but number of planes is %u instead of 4",
Packit 78deda
                pamP->depth);
Packit 78deda
    } else if (strneq(pamP->tuple_type, "RGB", 3)) {
Packit 78deda
        if (pamP->depth == 3)
Packit 78deda
            retval = PNG_COLOR_TYPE_RGB;
Packit 78deda
        else
Packit 78deda
            pm_error("Input tuple type is RGB, "
Packit 78deda
                     "but number of planes is %u instead of 3",
Packit 78deda
                     pamP->depth);
Packit 78deda
    } else if (strneq(pamP->tuple_type, "GRAYSCALE_ALPHA", 15)) {
Packit 78deda
        if (pamP->depth == 2)
Packit 78deda
            retval = PNG_COLOR_TYPE_GRAY_ALPHA;
Packit 78deda
        else
Packit 78deda
            pm_error("Input tupel type is GRAYSCALE_ALPHA, "
Packit 78deda
                     "but number of planes is %u instread of 2",
Packit 78deda
                     pamP->depth);
Packit 78deda
    } else if (strneq(pamP->tuple_type, "GRAYSCALE", 9)) {
Packit 78deda
        if (pamP->depth == 1)
Packit 78deda
            retval = PNG_COLOR_TYPE_GRAY;
Packit 78deda
        else
Packit 78deda
            pm_error("Input tuple type is GRAYSCALE, "
Packit 78deda
                     "but number of planes is %u instead of 1",
Packit 78deda
                     pamP->depth);
Packit 78deda
    } else if (strneq(pamP->tuple_type, "BLACKANDWHITE", 3)) {
Packit 78deda
        if (pamP->depth != 1)
Packit 78deda
            pm_error("Input tuple type is BLACKANDWHITE, "
Packit 78deda
                     "but number of planes is %u instead of 1",
Packit 78deda
                     pamP->depth);
Packit 78deda
        if (pamP->maxval != 1)
Packit 78deda
            pm_error("Input tuple type is BLACKANDWHITE, "
Packit 78deda
                     "but maxval is %u instead of 1", (unsigned)pamP->maxval);
Packit 78deda
Packit 78deda
        retval = PNG_COLOR_TYPE_GRAY;
Packit 78deda
    } else
Packit 78deda
        pm_error("Unrecognized tuple type: '%s'", pamP->tuple_type);
Packit 78deda
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
/*****************************************************************************
Packit 78deda
*  Subroutines that create all the (ancillary) chunks
Packit 78deda
*****************************************************************************/
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static png_color_16
Packit 78deda
parseAndScaleColor(const char * const colorString,
Packit 78deda
                   xelval       const pngMaxval) {
Packit 78deda
Packit 78deda
    png_color_16 pngColor;
Packit 78deda
Packit 78deda
    if (colorString) {
Packit 78deda
        xel const inputColor = ppm_parsecolor(colorString, PNM_OVERALLMAXVAL);
Packit 78deda
Packit 78deda
        xel scaledColor;
Packit 78deda
Packit 78deda
        /* Scale the color down to the PNG bit depth */
Packit 78deda
        PPM_DEPTH(scaledColor, inputColor, PNM_OVERALLMAXVAL, pngMaxval);
Packit 78deda
Packit 78deda
        pngColor.red   = PPM_GETR(scaledColor);
Packit 78deda
        pngColor.green = PPM_GETG(scaledColor);
Packit 78deda
        pngColor.blue  = PPM_GETB(scaledColor);
Packit 78deda
        pngColor.gray  = PNM_GET1(scaledColor);
Packit 78deda
    }
Packit 78deda
Packit 78deda
    return pngColor;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
doTrnsChunk(const struct pam * const pamP,
Packit 78deda
            struct pngx *      const pngxP,
Packit 78deda
            const char *       const trans) {
Packit 78deda
Packit 78deda
    if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY_ALPHA || 
Packit 78deda
        pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB_ALPHA)
Packit 78deda
        pm_error("Both alpha channel and transparency chunk not allowed.");
Packit 78deda
    else {
Packit 78deda
        xelval const pngMaxval = pm_bitstomaxval(pngx_bitDepth(pngxP));
Packit 78deda
        png_color_16 const pngColor = parseAndScaleColor(trans, pngMaxval);
Packit 78deda
            /* Transparency color from text format scaled from 16-bit to
Packit 78deda
               maxval.
Packit 78deda
            */
Packit 78deda
Packit 78deda
        pngx_setTrnsValue(pngxP, pngColor);
Packit 78deda
Packit 78deda
        if (verbose) {
Packit 78deda
            if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY) {
Packit 78deda
                pm_message("writing tRNS chunk with color {gray} = {%u}",
Packit 78deda
                           pngColor.gray );
Packit 78deda
            } else if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB) {
Packit 78deda
                pm_message("writing tRNS chunk with color "
Packit 78deda
                           "{red, green, blue} = {%u, %u, %u}",
Packit 78deda
                           pngColor.red, pngColor.green, pngColor.blue);
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
doChrmChunk(struct pngx *      const pngxP,
Packit 78deda
            struct pngx_chroma const chroma) {
Packit 78deda
    
Packit 78deda
    pngx_setChrm(pngxP, chroma);
Packit 78deda
Packit 78deda
    if (verbose) {
Packit 78deda
        pm_message("writing cHRM chunk { wx, wy, rx, ry, gx, gy, bx, by } = "
Packit 78deda
                   "{ %4.2f, %4.2f, %4.2f, %4.2f, "
Packit 78deda
                   "%4.2f, %4.2f, %4.2f, %4.2f }", 
Packit 78deda
                   chroma.wx, chroma.wy,
Packit 78deda
                   chroma.rx, chroma.ry,
Packit 78deda
                   chroma.gx, chroma.gy,
Packit 78deda
                   chroma.bx, chroma.by);
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void 
Packit 78deda
doGamaChunk(struct pngx *  const pngxP,
Packit 78deda
            float          const gamma) {
Packit 78deda
Packit 78deda
    pngx_setGama(pngxP, gamma);
Packit 78deda
Packit 78deda
    if (verbose) {
Packit 78deda
        pm_message("writing gAMA chunk with image gamma value %4.2f", gamma);
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
doSbitChunk(const struct pam * const pamP,
Packit 78deda
            struct pngx *      const pngxP) {
Packit 78deda
Packit 78deda
    unsigned int const pnmBitDepth = pm_maxvaltobits(pamP->maxval);
Packit 78deda
Packit 78deda
    /* create SBIT chunk in case of 1,2,4 bit deep images stored in 8 bit
Packit 78deda
       format PNG files 
Packit 78deda
    */
Packit 78deda
    if (pngx_colorType(pngxP) != PNG_COLOR_TYPE_GRAY && pnmBitDepth < 8) {
Packit 78deda
        png_color_8 sBit;
Packit 78deda
Packit 78deda
        if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB || 
Packit 78deda
            pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB_ALPHA) {
Packit 78deda
            sBit.red = sBit.green = sBit.blue = pnmBitDepth;
Packit 78deda
        } else {
Packit 78deda
            sBit.gray = pnmBitDepth;
Packit 78deda
        }
Packit 78deda
        if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB_ALPHA || 
Packit 78deda
            pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY_ALPHA) {
Packit 78deda
            sBit.alpha = pnmBitDepth;
Packit 78deda
        }
Packit 78deda
        pngx_setSbit(pngxP, sBit);
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
doSrgbChunk(struct pngx *   const pngxP,
Packit 78deda
            pngx_srgbIntent const srgbIntent) {
Packit 78deda
Packit 78deda
    pngx_setSrgb(pngxP, srgbIntent);
Packit 78deda
Packit 78deda
    if (verbose) {
Packit 78deda
        pm_message("writing sRGB chunk with intent value %s",
Packit 78deda
                   pngx_srgbIntentDesc(srgbIntent));
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
doTextChunkSet(struct pngx * const pngxP,
Packit 78deda
               const char *  const textFileName) {
Packit 78deda
Packit 78deda
    bool const ztxt = true;
Packit 78deda
    bool const itxt = false;
Packit 78deda
Packit 78deda
    FILE * tfP;
Packit 78deda
Packit 78deda
    tfP = pm_openr(textFileName);
Packit 78deda
    
Packit 78deda
    pngtxt_addChunk(pngxP, tfP, ztxt, itxt, verbose);
Packit 78deda
    
Packit 78deda
    pm_close(tfP);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
doZtxtChunkSet(struct pngx * const pngxP,
Packit 78deda
               const char *  const textFileName) {
Packit 78deda
Packit 78deda
    bool const ztxt = true;
Packit 78deda
    bool const itxt = false;
Packit 78deda
Packit 78deda
    FILE * tfP;
Packit 78deda
Packit 78deda
    tfP = pm_openr(textFileName);
Packit 78deda
    
Packit 78deda
    pngtxt_addChunk(pngxP, tfP, ztxt, itxt, verbose);
Packit 78deda
    
Packit 78deda
    pm_close(tfP);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
doItxtChunkSet(struct pngx * const pngxP,
Packit 78deda
               const char *  const textFileName) {
Packit 78deda
Packit 78deda
    bool const ztxt = true;
Packit 78deda
    bool const itxt = true;
Packit 78deda
Packit 78deda
    FILE * tfP;
Packit 78deda
Packit 78deda
    tfP = pm_openr(textFileName);
Packit 78deda
Packit 78deda
    pngtxt_addChunk(pngxP, tfP, ztxt, itxt, verbose);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
doBkgdChunk (const struct pam * const pamP,
Packit 78deda
             struct pngx *      const pngxP,
Packit 78deda
             const char *       const colorName)
Packit 78deda
{
Packit 78deda
    xelval const pngMaxval = pm_bitstomaxval(pngx_bitDepth(pngxP));
Packit 78deda
Packit 78deda
    png_color_16 const pngColor = parseAndScaleColor(colorName, pngMaxval);
Packit 78deda
        /* Background color from text format, scaled from 16-bit to maxval */
Packit 78deda
Packit 78deda
    pngx_setBkgdRgb(pngxP, pngColor);
Packit 78deda
Packit 78deda
    if (verbose) {
Packit 78deda
        if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY || 
Packit 78deda
            pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY_ALPHA) {
Packit 78deda
            pm_message("writing bKGD chunk with gray level = %u",
Packit 78deda
                       pngColor.gray);
Packit 78deda
        } else if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB || 
Packit 78deda
                   pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB_ALPHA) {
Packit 78deda
            pm_message("writing bKGD chunk with color {red, green, blue} = "
Packit 78deda
                       "{%u, %u, %u}",
Packit 78deda
                       pngColor.red, pngColor.green, pngColor.blue);
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
doTimeChunk(struct pngx * const pngxP,
Packit 78deda
            time_t        const time) {
Packit 78deda
Packit 78deda
    pngx_setTime(pngxP, time);
Packit 78deda
Packit 78deda
    if (verbose) {
Packit 78deda
        struct tm * const brokenTimeP = gmtime(&time);
Packit 78deda
Packit 78deda
        char buffer[100];
Packit 78deda
Packit 78deda
        strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", brokenTimeP);
Packit 78deda
Packit 78deda
        pm_message("Writing tIME chunk specifying datetime %s", buffer);
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
convertRaster(const struct pam * const pamP,
Packit 78deda
              const tuple *      const tuplerow,
Packit 78deda
              png_byte *         const pngRow,
Packit 78deda
              unsigned int       const bitDepth) {
Packit 78deda
Packit 78deda
    unsigned int col;
Packit 78deda
Packit 78deda
    /* An image row consists of columns x planes like gray or rgb(a) x 8 or 16
Packit 78deda
       bits.
Packit 78deda
    */
Packit 78deda
    for (col = 0; col < pamP->width; ++col) {
Packit 78deda
        unsigned int plane;
Packit 78deda
        for (plane = 0; plane < pamP->depth; ++plane) {
Packit 78deda
            if (bitDepth > 8) {
Packit 78deda
                /* Copy 2 bytes = 16 bits for one pixel */
Packit 78deda
                pngRow[2 * (pamP->depth * col + plane)] =
Packit 78deda
                    (tuplerow[col][plane] >> 8) & 0xff ;
Packit 78deda
                pngRow[2 * (pamP->depth * col + plane) + 1] =
Packit 78deda
                    tuplerow[col][plane] & 0xff ;
Packit 78deda
            } else {
Packit 78deda
                /* Copy 1 byte for one pixel. Later, a packing of 2, 4 or 8
Packit 78deda
                   pixels into a single byte can still happen.
Packit 78deda
                */
Packit 78deda
                pngRow[pamP->depth * col + plane] = tuplerow[col][plane];
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
writeRaster(const struct pam * const pamP,
Packit 78deda
            struct pngx *      const pngxP,
Packit 78deda
            int                const bitDepth) {
Packit 78deda
Packit 78deda
    tuple * tupleRow;
Packit 78deda
    png_byte * pngRow;
Packit 78deda
    unsigned int row;
Packit 78deda
Packit 78deda
    /* We process row-by-row and do not read the complete image into memory */
Packit 78deda
Packit 78deda
    tupleRow = pnm_allocpamrow(pamP);
Packit 78deda
Packit 78deda
    MALLOCARRAY(pngRow, pamP->width * 8);
Packit 78deda
        /* sufficient to store a 16-bit RGB+A row */
Packit 78deda
Packit 78deda
    if (pngRow == NULL)
Packit 78deda
        pm_error("Unable to allocate space for PNG pixel row for "
Packit 78deda
                 "%u columns", pamP->width);
Packit 78deda
    else {
Packit 78deda
        for (row = 0; row < pamP->height; ++row) {
Packit 78deda
            pnm_readpamrow(pamP, tupleRow);
Packit 78deda
Packit 78deda
            convertRaster(pamP, tupleRow, pngRow, bitDepth);
Packit 78deda
Packit 78deda
            png_write_row(pngxP->png_ptr, pngRow);
Packit 78deda
        }
Packit 78deda
        free(pngRow);
Packit 78deda
    }
Packit 78deda
    pnm_freepamrow(tupleRow);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
writePng(const struct pam * const pamP,
Packit 78deda
         FILE *             const ofP,
Packit 78deda
         struct CmdlineInfo const cmdline) {
Packit 78deda
Packit 78deda
    unsigned int const pnmBitDepth = pm_maxvaltobits(pamP->maxval);
Packit 78deda
    int const pngColorType = colorTypeFromInputType(pamP);
Packit 78deda
Packit 78deda
    struct pngx * pngxP;
Packit 78deda
    unsigned int pngBitDepth;
Packit 78deda
    png_color_8 sBit;
Packit 78deda
Packit 78deda
    pngx_create(&pngxP, PNGX_WRITE, NULL);
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
    if ((pngColorType == PNG_COLOR_TYPE_RGB ||
Packit 78deda
         pngColorType == PNG_COLOR_TYPE_RGB_ALPHA) &&
Packit 78deda
        pnmBitDepth < 8) {
Packit 78deda
Packit 78deda
        pngBitDepth = 8;
Packit 78deda
    } else
Packit 78deda
        pngBitDepth = pnmBitDepth;
Packit 78deda
Packit 78deda
    png_init_io(pngxP->png_ptr, ofP);
Packit 78deda
Packit 78deda
    pngx_setIhdr(pngxP, pamP->width, pamP->height,
Packit 78deda
                 pngBitDepth, pngColorType,
Packit 78deda
                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
Packit 78deda
                 PNG_FILTER_TYPE_BASE);
Packit 78deda
Packit 78deda
    /* Where requested, add ancillary chunks */
Packit 78deda
    if (cmdline.transparencySpec)
Packit 78deda
        doTrnsChunk(pamP, pngxP,cmdline.transparency);
Packit 78deda
Packit 78deda
    if (cmdline.chromaSpec)
Packit 78deda
        doChrmChunk(pngxP, cmdline.chroma);
Packit 78deda
Packit 78deda
    if (cmdline.gammaSpec)
Packit 78deda
        doGamaChunk(pngxP, cmdline.gamma);
Packit 78deda
Packit 78deda
    /* no iccp */
Packit 78deda
Packit 78deda
    doSbitChunk(pamP, pngxP);
Packit 78deda
Packit 78deda
    if (cmdline.srgbintentSpec)
Packit 78deda
        doSrgbChunk(pngxP, cmdline.srgbintent);
Packit 78deda
Packit 78deda
    if (cmdline.textSpec)
Packit 78deda
        doTextChunkSet(pngxP, cmdline.text);
Packit 78deda
Packit 78deda
    if (cmdline.ztxtSpec)
Packit 78deda
        doZtxtChunkSet(pngxP, cmdline.ztxt);
Packit 78deda
Packit 78deda
    if (cmdline.itxtSpec)
Packit 78deda
        doItxtChunkSet(pngxP, cmdline.itxt);
Packit 78deda
Packit 78deda
    if (cmdline.backgroundSpec)
Packit 78deda
        doBkgdChunk(pamP, pngxP, cmdline.background);
Packit 78deda
Packit 78deda
    /* no hist */
Packit 78deda
Packit 78deda
    /* no phys */
Packit 78deda
Packit 78deda
    /* no splt */
Packit 78deda
Packit 78deda
    if (cmdline.timeSpec)
Packit 78deda
        doTimeChunk(pngxP, cmdline.time);
Packit 78deda
Packit 78deda
    /* Write the ancillary chunks to PNG file */
Packit 78deda
    pngx_writeInfo(pngxP);
Packit 78deda
Packit 78deda
    if (pngColorType != PNG_COLOR_TYPE_GRAY && pnmBitDepth < 8) {
Packit 78deda
        /* Move the 1, 2, 4 bits to most significant bits */
Packit 78deda
        pngx_setShift(pngxP, sBit);
Packit 78deda
    }
Packit 78deda
    if ((pngColorType == PNG_COLOR_TYPE_GRAY) && (pnmBitDepth < 8)) {
Packit 78deda
        /* Pack multiple pixels in a byte */
Packit 78deda
        pngx_setPacking(pngxP);
Packit 78deda
    }
Packit 78deda
Packit 78deda
    writeRaster(pamP, pngxP, pnmBitDepth);
Packit 78deda
Packit 78deda
    pngx_writeEnd(pngxP);
Packit 78deda
    pngx_destroy(pngxP);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
reportInputFormat(const struct pam * const pamP) {
Packit 78deda
Packit 78deda
    const char * formatDesc;
Packit 78deda
Packit 78deda
    if (pamP->format == PBM_FORMAT || pamP->format == RPBM_FORMAT)
Packit 78deda
        formatDesc = "PBM";
Packit 78deda
    else if (pamP->format == PGM_FORMAT || pamP->format == RPGM_FORMAT)
Packit 78deda
        formatDesc = "PGM";
Packit 78deda
    else if (pamP->format == PPM_FORMAT || pamP->format == RPPM_FORMAT)
Packit 78deda
        formatDesc = "PPM";
Packit 78deda
    else if (pamP->format == PAM_FORMAT)
Packit 78deda
        formatDesc = "PAM";
Packit 78deda
    else
Packit 78deda
        formatDesc = NULL;
Packit 78deda
Packit 78deda
    if (formatDesc)
Packit 78deda
        pm_message("Input format = %s", formatDesc);
Packit 78deda
    else
Packit 78deda
        pm_message("Unrecognized input format, format code = 0x%x",
Packit 78deda
                   pamP->format);
Packit 78deda
Packit 78deda
    pm_message("Input tuple type = '%s'", pamP->tuple_type);
Packit 78deda
    pm_message("Input depth = %u", pamP->depth);
Packit 78deda
    pm_message("Input maxval = %u", (unsigned int) pamP->maxval);
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
    FILE * ifP;
Packit 78deda
    struct CmdlineInfo cmdline;
Packit 78deda
    struct pam pam;
Packit 78deda
Packit 78deda
    pm_proginit(&argc, argv);
Packit 78deda
Packit 78deda
    parseCommandLine(argc, argv, &cmdline);
Packit 78deda
Packit 78deda
    verbose = cmdline.verbose;
Packit 78deda
Packit 78deda
    ifP = pm_openr(cmdline.inputFileName);
Packit 78deda
Packit 78deda
    pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
Packit 78deda
Packit 78deda
    if (verbose)
Packit 78deda
        reportInputFormat(&pam;;
Packit 78deda
Packit 78deda
    writePng(&pam, stdout, cmdline);
Packit 78deda
Packit 78deda
    pm_close(ifP);
Packit 78deda
Packit 78deda
    return 0;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda