diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2022-06-24 04:36:14 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2022-06-24 04:36:14 +0000 |
commit | 5bcb369386d617631627a4fb086ff6ab80ffa157 (patch) | |
tree | b2f6cae56a948d3897c1ed6452f870ffc8c832de /converter/other | |
parent | 5991a720c260e171c145d035014609f18cb2eaf3 (diff) | |
download | netpbm-mirror-5bcb369386d617631627a4fb086ff6ab80ffa157.tar.gz netpbm-mirror-5bcb369386d617631627a4fb086ff6ab80ffa157.tar.xz netpbm-mirror-5bcb369386d617631627a4fb086ff6ab80ffa157.zip |
Promote Development release to Advanced, version 10.99.00
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@4358 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'converter/other')
-rw-r--r-- | converter/other/Makefile | 7 | ||||
-rw-r--r-- | converter/other/exif.c | 6 | ||||
-rw-r--r-- | converter/other/pamtoqoi.c | 439 | ||||
-rw-r--r-- | converter/other/qoi.h | 101 | ||||
-rw-r--r-- | converter/other/qoitopam.c | 323 | ||||
-rw-r--r-- | converter/other/winicontopam.c | 2 |
6 files changed, 871 insertions, 7 deletions
diff --git a/converter/other/Makefile b/converter/other/Makefile index 5dfc27ec..3b3b6aa0 100644 --- a/converter/other/Makefile +++ b/converter/other/Makefile @@ -134,14 +134,15 @@ PORTBINARIES = avstopam bmptopnm fitstopnm \ gemtopnm giftopnm hdifftopam infotopam \ pamtoavs pamtodjvurle pamtofits pamtogif \ pamtohdiff pamtohtmltbl pamtompfont pamtooctaveimg \ - pamtopam pamtopdbimg pamtopfm pamtopnm pamtosrf pamtouil \ + pamtopam pamtopdbimg pamtopfm pamtopnm \ + pamtoqoi pamtosrf pamtouil \ pamtowinicon pamtoxvmini \ pbmtopgm pdbimgtopam pfmtopam \ pgmtopbm pgmtoppm ppmtopgm pnmtoddif \ pnmtopclxl pnmtorast \ - pnmtosgi pnmtosir pamtotga pnmtoxwd \ + pnmtosgi pnmtosir pamtotga pnmtoxwd qoitopam \ rasttopnm rlatopam sgitopnm sirtopnm srftopam sunicontopnm \ - winicontopam xwdtopnm yuy2topam zeisstopnm + winicontopam xwdtopnm yuy2topam zeisstopnm \ ifneq ($(DONT_HAVE_PROCESS_MGMT),Y) PORTBINARIES += pstopnm pnmtops diff --git a/converter/other/exif.c b/converter/other/exif.c index 1bfe4b2b..87236dc7 100644 --- a/converter/other/exif.c +++ b/converter/other/exif.c @@ -533,7 +533,7 @@ processDirEntry(const unsigned char * const dirEntry, for (end = byteCount; end > 0 && value[end] == ' '; --end); /* Skip "ASCII" if it is there */ - if (end >= 5 && MEMEQ(value, "ASCII", 5)) + if (end >= 5 && memeq(value, "ASCII", 5)) cursor = 5; else cursor = 0; @@ -860,12 +860,12 @@ exif_parse(const unsigned char * const exifData, if (wantTagTrace) fprintf(stderr, "Exif header %d bytes long\n",length); - if (MEMEQ(exifData + 0, "II" , 2)) { + if (memeq(exifData + 0, "II" , 2)) { if (wantTagTrace) fprintf(stderr, "Exif header in Intel order\n"); byteOrder = NORMAL; } else { - if (MEMEQ(exifData + 0, "MM", 2)) { + if (memeq(exifData + 0, "MM", 2)) { if (wantTagTrace) fprintf(stderr, "Exif header in Motorola order\n"); byteOrder = MOTOROLA; diff --git a/converter/other/pamtoqoi.c b/converter/other/pamtoqoi.c new file mode 100644 index 00000000..638efa3a --- /dev/null +++ b/converter/other/pamtoqoi.c @@ -0,0 +1,439 @@ +/* + pamtoqoi - Converts PAM to a QOI - The "Quite OK Image" format file + + This program is part of Netpbm. + + --------------------------------------------------------------------- + + QOI - The "Quite OK Image" format for fast, lossless image compression + + Encoder by Dominic Szablewski - https://phoboslab.org + + -- LICENSE: The MIT License(MIT) + + Copyright(c) 2021 Dominic Szablewski + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files(the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and / or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions : + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + For more information on the format visit: https//qoiformat.org/ . + + Modifications for Netpbm read routines by Akira F. Urushibata. + +*/ + +#include <string.h> +#include <assert.h> +#include "pam.h" +#include "nstring.h" +#include "mallocvar.h" +#include "shhopt.h" + +#include "qoi.h" + + + +struct CmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFileName; /* '-' if stdin */ +}; + + + +static void +parseCommandLine(int argc, + const char ** argv, + struct CmdlineInfo * cmdlineP ) { +/*---------------------------------------------------------------------------- + Parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +-----------------------------------------------------------------------------*/ + optEntry * option_def; + /* Instructions to pm_optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + MALLOCARRAY(option_def, 100); + + OPTENTINIT; + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (argc-1 < 1) + cmdlineP->inputFileName = "-"; + else if (argc-1 == 1) + cmdlineP->inputFileName = argv[1]; + else + pm_error("Program takes at most one argument: input file name. " + "you specified %d", argc-1); +} + + + +static void +encodeQoiHeader(qoi_Desc const qoiDesc) { + + assert (QOI_MAGIC_SIZE + 4 + 4 + 1 + 1 == QOI_HEADER_SIZE); + + fwrite (qoi_magic, QOI_MAGIC_SIZE, 1, stdout); + pm_writebiglongu(stdout, qoiDesc.width); + pm_writebiglongu(stdout, qoiDesc.height); + putchar(qoiDesc.channelCt); + putchar(qoiDesc.colorspace); + +} + +enum Tupletype {BW, BWAlpha, GRAY, GRAYAlpha, RGB, RGBAlpha, + GRAY255, GRAY255Alpha, RGB255, RGB255Alpha}; + + + +static void +createSampleMap(sample const oldMaxval, + sample ** const sampleMapP) { + + unsigned int i; + sample * sampleMap; + sample const newMaxval = 255; + + MALLOCARRAY_NOFAIL(sampleMap, oldMaxval+1); + + for (i = 0; i <= oldMaxval; ++i) + sampleMap[i] = ROUNDDIV(i * newMaxval, oldMaxval); + + *sampleMapP = sampleMap; +} + + + +static enum Tupletype +tupleTypeFmPam(const char * const pamTupleType, + sample const maxval) { + + enum Tupletype retval; + + if (streq(pamTupleType, PAM_PBM_TUPLETYPE)) { + if (maxval !=1) + pm_error("Invalid maxval (%lu) for tuple type '%s'.", + maxval, pamTupleType); + else + retval = BW; + } else if (streq(pamTupleType, PAM_PBM_ALPHA_TUPLETYPE)) { + if (maxval !=1) + pm_error("Invalid maxval (%lu) for tuple type '%s'.", + maxval, pamTupleType); + else + retval = BWAlpha; + } else if (maxval == 255) { + if (streq(pamTupleType, PAM_PPM_TUPLETYPE)) + retval = RGB255; + else if(streq(pamTupleType, PAM_PPM_ALPHA_TUPLETYPE)) + retval = RGB255Alpha; + else if(streq(pamTupleType, PAM_PGM_TUPLETYPE)) + retval = GRAY255; + else if(streq(pamTupleType, PAM_PGM_ALPHA_TUPLETYPE)) + retval = GRAY255Alpha; + else + pm_error("Don't know how to convert tuple type '%s'.", + pamTupleType); + } else { + if (streq(pamTupleType, PAM_PPM_TUPLETYPE)) + retval = RGB; + else if(streq(pamTupleType, PAM_PPM_ALPHA_TUPLETYPE)) + retval = RGBAlpha; + else if(streq(pamTupleType, PAM_PGM_TUPLETYPE)) + retval = GRAY; + else if(streq(pamTupleType, PAM_PGM_ALPHA_TUPLETYPE)) + retval = GRAYAlpha; + else + pm_error("Don't know how to convert tuple type '%s'.", + pamTupleType); + } + + return retval; +} + + + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wreturn-type" + +static unsigned int +channelCtFmTupleType(enum Tupletype const tupleType) { + + switch (tupleType) { + case RGB: + return 3; + case RGB255: + return 3; + case RGBAlpha: + return 4; + case RGB255Alpha: + return 4; + case BW: + case GRAY: + return 3; + case GRAY255: + return 3; + case BWAlpha: + case GRAYAlpha: + return 4; + case GRAY255Alpha: + return 4; + } +} +#pragma GCC diagnostic pop + + + +static qoi_Rgba +pxFmTuple(tuple const tuple0, + const sample * const sampleMap, + enum Tupletype const tupleType) { +/*---------------------------------------------------------------------------- + Convert PAM tuple to qoi rgba pixel struct +-----------------------------------------------------------------------------*/ + qoi_Rgba px; + + switch (tupleType) { + case RGB: + px.rgba.r = sampleMap[tuple0[PAM_RED_PLANE]]; + px.rgba.g = sampleMap[tuple0[PAM_GRN_PLANE]]; + px.rgba.b = sampleMap[tuple0[PAM_BLU_PLANE]]; + px.rgba.a = 255; + break; + case RGB255: + px.rgba.r = tuple0[PAM_RED_PLANE]; + px.rgba.g = tuple0[PAM_GRN_PLANE]; + px.rgba.b = tuple0[PAM_BLU_PLANE]; + px.rgba.a = 255; + break; + case RGBAlpha: + px.rgba.r = sampleMap[tuple0[PAM_RED_PLANE]]; + px.rgba.g = sampleMap[tuple0[PAM_GRN_PLANE]]; + px.rgba.b = sampleMap[tuple0[PAM_BLU_PLANE]]; + px.rgba.a = sampleMap[tuple0[PAM_TRN_PLANE]]; + break; + case RGB255Alpha: + px.rgba.r = tuple0[PAM_RED_PLANE]; + px.rgba.g = tuple0[PAM_GRN_PLANE]; + px.rgba.b = tuple0[PAM_BLU_PLANE]; + px.rgba.a = tuple0[PAM_TRN_PLANE]; + break; + case BW: + case GRAY : { + unsigned char const qoiSample = sampleMap[tuple0[0]]; + px.rgba.r = qoiSample; + px.rgba.g = qoiSample; + px.rgba.b = qoiSample; + px.rgba.a = 255; + } break; + case GRAY255: { + unsigned char const qoiSample = tuple0[0]; + px.rgba.r = qoiSample; + px.rgba.g = qoiSample; + px.rgba.b = qoiSample; + px.rgba.a = 255; + } break; + case BWAlpha: + case GRAYAlpha: { + unsigned char const qoiSample = sampleMap[tuple0[0]]; + px.rgba.r = qoiSample; + px.rgba.g = qoiSample; + px.rgba.b = qoiSample; + px.rgba.a = sampleMap[tuple0[PAM_GRAY_TRN_PLANE]]; + } break; + case GRAY255Alpha: { + unsigned char const qoiSample = tuple0[0]; + px.rgba.r = qoiSample; + px.rgba.g = qoiSample; + px.rgba.b = qoiSample; + px.rgba.a = tuple0[PAM_GRAY_TRN_PLANE]; + } break; + } + + return px; +} + + + +static void +encodeNewPixel(qoi_Rgba const px, + qoi_Rgba const pxPrev) { + + if (px.rgba.a == pxPrev.rgba.a) { + signed char const vr = px.rgba.r - pxPrev.rgba.r; + signed char const vg = px.rgba.g - pxPrev.rgba.g; + signed char const vb = px.rgba.b - pxPrev.rgba.b; + + signed char const vgR = vr - vg; + signed char const vgB = vb - vg; + + if ( + vr > -3 && vr < 2 && + vg > -3 && vg < 2 && + vb > -3 && vb < 2 + ) { + putchar(QOI_OP_DIFF | + (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2)); + } else if ( + vgR > -9 && vgR < 8 && + vg > -33 && vg < 32 && + vgB > -9 && vgB < 8 + ) { + putchar(QOI_OP_LUMA | (vg + 32)); + putchar((vgR + 8) << 4 | (vgB + 8)); + } else { + putchar(QOI_OP_RGB); + putchar(px.rgba.r); + putchar(px.rgba.g); + putchar(px.rgba.b); + } + } else { + putchar(QOI_OP_RGBA); + putchar(px.rgba.r); + putchar(px.rgba.g); + putchar(px.rgba.b); + putchar(px.rgba.a); + } +} + + + +static void +qoiEncode(FILE * const ifP, + struct pam * const inpamP) { + + tuple * tuplerow; + unsigned int row; + + qoi_Rgba index[QOI_INDEX_SIZE]; + qoi_Rgba pxPrev; + unsigned int run; + sample * sampleMap; + qoi_Desc qoiDesc; + + enum Tupletype const tupleType = + tupleTypeFmPam(inpamP->tuple_type, inpamP->maxval); + + if (inpamP->height > QOI_PIXELS_MAX / inpamP->width) + pm_error("Too many pixels for QOI: %u x %u (max is %u)", + inpamP->height, inpamP->width, QOI_PIXELS_MAX); + + qoiDesc.colorspace = QOI_SRGB; + qoiDesc.width = inpamP->width; + qoiDesc.height = inpamP->height; + qoiDesc.channelCt = channelCtFmTupleType(tupleType); + + encodeQoiHeader(qoiDesc); + + tuplerow = pnm_allocpamrow(inpamP); + + if (inpamP->maxval != 255) + createSampleMap(inpamP->maxval, &sampleMap); + + qoi_clearQoiIndex(index); + + pxPrev.rgba.r = 0; + pxPrev.rgba.g = 0; + pxPrev.rgba.b = 0; + pxPrev.rgba.a = 255; + + /* Read and convert rows. */ + for (row = 0, run = 0; row < inpamP->height; ++row) { + unsigned int col; + + pnm_readpamrow(inpamP, tuplerow); + + for (col = 0; col < inpamP->width; ++col) { + qoi_Rgba const px = pxFmTuple(tuplerow[col], sampleMap, tupleType); + + if (px.v == pxPrev.v) { + ++run; + if (run == 62) { + putchar(QOI_OP_RUN | (run - 1)); + run = 0; + } + } else { + unsigned int const indexPos = qoi_colorHash(px); + + if (run > 0) { + putchar(QOI_OP_RUN | (run - 1)); + run = 0; + } + + if (index[indexPos].v == px.v) { + putchar(QOI_OP_INDEX | indexPos); + + } else { + index[indexPos] = px; + encodeNewPixel(px, pxPrev); + } + } + pxPrev = px; + } + } + + if (run > 0) + putchar(QOI_OP_RUN | (run - 1)); + + fwrite(qoi_padding, sizeof(qoi_padding), 1, stdout); + + if (inpamP->maxval != 255) + free(sampleMap); + pnm_freepamrow(tuplerow); +} + + + +int +main(int argc, const char **argv) { + + struct CmdlineInfo cmdline; + struct pam inpam; + FILE * ifP; + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFileName); + + pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); + + qoiEncode(ifP, &inpam); + + return 0; +} + + diff --git a/converter/other/qoi.h b/converter/other/qoi.h new file mode 100644 index 00000000..52ee95e2 --- /dev/null +++ b/converter/other/qoi.h @@ -0,0 +1,101 @@ +#ifndef QOI_H_INCLUDED +#define QOI_H_INCLUDED +/* + +QOI - The "Quite OK Image" format for fast, lossless image compression + +Dominic Szablewski - https://phoboslab.org + + +-- LICENSE: The MIT License(MIT) + +Copyright(c) 2021 Dominic Szablewski + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files(the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions : +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + + +typedef enum { + QOI_SRGB = 0, + QOI_LINEAR = 1 +} qoi_Colorspace; + + +typedef struct { + unsigned int width; + unsigned int height; + unsigned int channelCt; + qoi_Colorspace colorspace; +} qoi_Desc; + + + +#define QOI_OP_INDEX 0x00 /* 00xxxxxx */ +#define QOI_OP_DIFF 0x40 /* 01xxxxxx */ +#define QOI_OP_LUMA 0x80 /* 10xxxxxx */ +#define QOI_OP_RUN 0xc0 /* 11xxxxxx */ +#define QOI_OP_RGB 0xfe /* 11111110 */ +#define QOI_OP_RGBA 0xff /* 11111111 */ + +#define QOI_MASK_2 0xc0 /* 11000000 */ + +#define QOI_HEADER_SIZE 14 + +/* 2GB is the max file size that this implementation can safely handle. We + guard against anything larger than that, assuming the worst case with 5 + bytes per pixel, rounded down to a nice clean value. 400 million pixels + ought to be enough for anybody. + */ +#define QOI_PIXELS_MAX 400000000 + +static unsigned int const qoi_pixels_max = (unsigned int) QOI_PIXELS_MAX; + +#define QOI_MAXVAL 255 + +#define QOI_INDEX_SIZE 64 + + +typedef union { + struct { unsigned char r, g, b, a; } rgba; + unsigned int v; +} qoi_Rgba; + +static __inline__ unsigned int +qoi_colorHash(qoi_Rgba const x) { + + return + (x.rgba.r*3 + x.rgba.g*5 + x.rgba.b*7 + x.rgba.a*11) % QOI_INDEX_SIZE; +} + +static __inline__ void +qoi_clearQoiIndex(qoi_Rgba * index) { + + memset(index, 0, QOI_INDEX_SIZE * sizeof(qoi_Rgba)); + +} + +#define QOI_MAGIC_SIZE 4 + +static char const qoi_magic[QOI_MAGIC_SIZE + 1] = {'q','o','i','f','\0'}; + +#define QOI_PADDING_SIZE 8 + +static unsigned char const qoi_padding[QOI_PADDING_SIZE] = {0,0,0,0,0,0,0,1}; + + +#endif diff --git a/converter/other/qoitopam.c b/converter/other/qoitopam.c new file mode 100644 index 00000000..af6817b7 --- /dev/null +++ b/converter/other/qoitopam.c @@ -0,0 +1,323 @@ +/* + qoitopam - Converts from a QOI - The "Quite OK Image" format file to PAM + + This program is part of Netpbm. + + --------------------------------------------------------------------- + + + QOI - The "Quite OK Image" format for fast, lossless image compression + + Decoder by Dominic Szablewski - https://phoboslab.org + + -- LICENSE: The MIT License(MIT) + + Copyright(c) 2021 Dominic Szablewski + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files(the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and / or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions : + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + For more information on the format visit: https//qoiformat.org/ . + + Modifications for Netpbm & PAM write routines by Akira F. Urushibata. +*/ + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#include "pm.h" +#include "pam.h" +#include "mallocvar.h" +#include "nstring.h" +#include "shhopt.h" + +#include "qoi.h" + + + +struct CmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFileName; /* '-' if stdin */ +}; + + + +static void +parseCommandLine(int argc, + const char ** argv, + struct CmdlineInfo * cmdlineP ) { +/*---------------------------------------------------------------------------- + Parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +-----------------------------------------------------------------------------*/ + optEntry * option_def; + /* Instructions to pm_optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + MALLOCARRAY(option_def, 100); + + OPTENTINIT; + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (argc-1 < 1) + cmdlineP->inputFileName = "-"; + else if (argc-1 == 1) + cmdlineP->inputFileName = argv[1]; + else + pm_error("Program takes at most one argument: input file name. " + "you specified %d", argc-1); +} + + + +static void +readAndValidateMagic(FILE * const ifP){ + + char magicBuff[QOI_MAGIC_SIZE]; + size_t charsReadCt; + + charsReadCt = fread(magicBuff, 1, QOI_MAGIC_SIZE, ifP); + + if (charsReadCt == 0) + pm_error("Input file is empty."); + else if (charsReadCt < QOI_MAGIC_SIZE || !MEMSEQ(&magicBuff, &qoi_magic)) { + assert(QOI_MAGIC_SIZE == 4); + pm_error("Invalid QOI image: does not start with magic number " + "'%c%c%c%c'", + qoi_magic[0], qoi_magic[1], qoi_magic[2], qoi_magic[3]); + } +} + + +/* The following two functions are from lib/pmfileio.c */ + +static void +abortWithReadError(FILE * const ifP) { + + if (feof(ifP)) + pm_error("Unexpected end of input file"); + else + pm_error("Error (not EOF) reading file."); +} + + + +static unsigned char +getcNofail(FILE * const ifP) { + + int c; + + c = getc(ifP); + + if (c == EOF) + abortWithReadError(ifP); + + return (unsigned char) c; +} + + + +static void +decodeQoiHeader(FILE * const ifP, + qoi_Desc * const qoiDescP) { + + unsigned long int width, height; + + readAndValidateMagic(ifP); + + pm_readbiglongu(ifP, &width); + if (width == 0) + pm_error("Invalid QOI image: width is zero"); + else + qoiDescP->width = width; + + pm_readbiglongu(ifP, &height); + if (height == 0) + pm_error("Invalid QOI image: height is zero"); + else if (height > QOI_PIXELS_MAX / width) + pm_error ("Invalid QOI image: %u x %u is more than %u pixels", + (unsigned int) width, (unsigned int) height, QOI_PIXELS_MAX); + else + qoiDescP->height = height; + + qoiDescP->channelCt = getcNofail(ifP); + if (qoiDescP->channelCt != 3 && qoiDescP->channelCt != 4) + pm_error("Invalid QOI image: channel count is %u. " + "Only 3 and 4 are valid", qoiDescP->channelCt); + + qoiDescP->colorspace = getcNofail(ifP); + if (qoiDescP->colorspace != QOI_SRGB && qoiDescP->colorspace != QOI_LINEAR) + pm_error("Invalid QOI image: colorspace code is %u. " + "Only %u (SRGB) and %u (LINEAR) are valid", + qoiDescP->colorspace, QOI_SRGB, QOI_LINEAR); +} + + + +static void +qoiDecode(FILE * const ifP, + qoi_Desc * const qoiDescP, + struct pam * const outpamP) { + + qoi_Rgba index[QOI_INDEX_SIZE]; + unsigned int row; + qoi_Rgba px; + unsigned int run; + tuple * tuplerow; + + assert(qoiDescP); + tuplerow = pnm_allocpamrow(outpamP); + + qoi_clearQoiIndex(index); + px.rgba.r = px.rgba.g = px.rgba.b = 0; + px.rgba.a = 255; + + for (row = 0, run = 0; row < outpamP->height; ++row) { + unsigned int col; + + for (col = 0; col < outpamP->width; ++col) { + if (run > 0) { + --run; + } else { + unsigned char const b1 = getcNofail(ifP); + + if (b1 == QOI_OP_RGB) { + px.rgba.r = getcNofail(ifP); + px.rgba.g = getcNofail(ifP); + px.rgba.b = getcNofail(ifP); + } else if (b1 == QOI_OP_RGBA) { + px.rgba.r = getcNofail(ifP); + px.rgba.g = getcNofail(ifP); + px.rgba.b = getcNofail(ifP); + px.rgba.a = getcNofail(ifP); + } else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) { + /* Official spec says 2 or more consecutive instances of + QOI_OP_INDEX are not allowed, but we don't check */ + px = index[b1]; + } else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) { + px.rgba.r += ((b1 >> 4) & 0x03) - 2; + px.rgba.g += ((b1 >> 2) & 0x03) - 2; + px.rgba.b += ( b1 & 0x03) - 2; + } else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) { + unsigned char const b2 = getcNofail(ifP); + unsigned char const vg = (b1 & 0x3f) - 32; + px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f); + px.rgba.g += vg; + px.rgba.b += vg - 8 + (b2 & 0x0f); + } else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) { + run = (b1 & 0x3f); + } + /* register pixel in hash lookup array */ + index[qoi_colorHash(px)] = px; + } + tuplerow[col][PAM_RED_PLANE] = px.rgba.r; + tuplerow[col][PAM_GRN_PLANE] = px.rgba.g; + tuplerow[col][PAM_BLU_PLANE] = px.rgba.b; + if (qoiDescP->channelCt == 4) + tuplerow[col][PAM_TRN_PLANE] = px.rgba.a; + } + pnm_writepamrow(outpamP, tuplerow); + } + if (run > 0) + pm_error("Invalid QOI image: %u (or more) extra pixels " + "beyond end of image.", run); + + pnm_freepamrow(tuplerow); +} + + + +static void +readAndValidatePadding(FILE * const ifP){ + + unsigned char padBuff[QOI_PADDING_SIZE]; + size_t charsReadCt; + + charsReadCt = fread(padBuff, 1, QOI_PADDING_SIZE, ifP); + + if(charsReadCt < QOI_PADDING_SIZE) { + pm_error("Invalid QOI image. Error reading final 8-byte padding. " + "Premature end of file."); + } else if (!MEMSEQ(&padBuff, &qoi_padding)) + pm_error("Invalid QOI image. Final 8-byte padding incorrect."); + else if (fgetc(ifP) != EOF) + pm_error("Invalid QOI image. " + "Extraneous bytes after final 8-byte padding."); +} + + + +int +main(int argc, const char **argv) { + + struct CmdlineInfo cmdline; + qoi_Desc qoiDesc; + struct pam outpam; + FILE * ifP; + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFileName); + + outpam.size = sizeof(struct pam); + outpam.len = PAM_STRUCT_SIZE(tuple_type); + outpam.maxval = QOI_MAXVAL; + outpam.plainformat = 0; + + decodeQoiHeader(ifP, &qoiDesc); + + outpam.depth = qoiDesc.channelCt == 3 ? 3 : 4; + outpam.width = qoiDesc.width; + outpam.height = qoiDesc.height; + outpam.format = PAM_FORMAT; + outpam.file = stdout; + + if (qoiDesc.channelCt == 3) + strcpy(outpam.tuple_type, PAM_PPM_TUPLETYPE); + else + strcpy(outpam.tuple_type, PAM_PPM_ALPHA_TUPLETYPE); + + pnm_writepaminit(&outpam); + qoiDecode(ifP, &qoiDesc, &outpam); + + readAndValidatePadding(ifP); + + return 0; +} + + diff --git a/converter/other/winicontopam.c b/converter/other/winicontopam.c index f6f89e11..bb39bf60 100644 --- a/converter/other/winicontopam.c +++ b/converter/other/winicontopam.c @@ -1273,7 +1273,7 @@ convertImage(struct File * const icoP, image = readImage(icoP, dirEntryP); - if (MEMEQ(image, pngSignature, sizeof (pngSignature))) + if (memeq(image, pngSignature, sizeof (pngSignature))) convertPng(image, ofP, dirEntryP); else convertBmp(image, ofP, dirEntryP, needHeaderDump, wantAndMaskPlane); |