|
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 |
|