OLD | NEW |
(Empty) | |
| 1 /*--------------------------------------------------------------------------- |
| 2 |
| 3 wpng - simple PNG-writing program wpng.c |
| 4 |
| 5 This program converts certain NetPBM binary files (grayscale and RGB, |
| 6 maxval = 255) to PNG. Non-interlaced PNGs are written progressively; |
| 7 interlaced PNGs are read and written in one memory-intensive blast. |
| 8 |
| 9 Thanks to Jean-loup Gailly for providing the necessary trick to read |
| 10 interactive text from the keyboard while stdin is redirected. Thanks |
| 11 to Cosmin Truta for Cygwin fixes. |
| 12 |
| 13 NOTE: includes provisional support for PNM type "8" (portable alphamap) |
| 14 images, presumed to be a 32-bit interleaved RGBA format; no pro- |
| 15 vision for possible interleaved grayscale+alpha (16-bit) format. |
| 16 THIS IS UNLIKELY TO BECOME AN OFFICIAL NETPBM ALPHA FORMAT! |
| 17 |
| 18 to do: |
| 19 - delete output file if quit before calling any writepng routines |
| 20 - process backspace with -text option under DOS/Win? (currently get ^H) |
| 21 |
| 22 --------------------------------------------------------------------------- |
| 23 |
| 24 Changelog: |
| 25 - 1.01: initial public release |
| 26 - 1.02: modified to allow abbreviated options |
| 27 - 1.03: removed extraneous character from usage screen; fixed bug in |
| 28 command-line parsing |
| 29 - 1.04: fixed DOS/OS2/Win32 detection, including partial Cygwin fix |
| 30 (see http://home.att.net/~perlspinr/diffs/GregBook_cygwin.diff) |
| 31 - 2.00: dual-licensed (added GNU GPL) |
| 32 |
| 33 [REPORTED BUG (win32 only): "contrib/gregbook/wpng.c - cmd line |
| 34 dose not work! In order to do something useful I needed to redirect |
| 35 both input and output, with cygwin and with bcc32 as well. Under |
| 36 Linux, the same wpng appears to work fine. I don't know what is |
| 37 the problem."] |
| 38 |
| 39 --------------------------------------------------------------------------- |
| 40 |
| 41 Copyright (c) 1998-2007 Greg Roelofs. All rights reserved. |
| 42 |
| 43 This software is provided "as is," without warranty of any kind, |
| 44 express or implied. In no event shall the author or contributors |
| 45 be held liable for any damages arising in any way from the use of |
| 46 this software. |
| 47 |
| 48 The contents of this file are DUAL-LICENSED. You may modify and/or |
| 49 redistribute this software according to the terms of one of the |
| 50 following two licenses (at your option): |
| 51 |
| 52 |
| 53 LICENSE 1 ("BSD-like with advertising clause"): |
| 54 |
| 55 Permission is granted to anyone to use this software for any purpose, |
| 56 including commercial applications, and to alter it and redistribute |
| 57 it freely, subject to the following restrictions: |
| 58 |
| 59 1. Redistributions of source code must retain the above copyright |
| 60 notice, disclaimer, and this list of conditions. |
| 61 2. Redistributions in binary form must reproduce the above copyright |
| 62 notice, disclaimer, and this list of conditions in the documenta- |
| 63 tion and/or other materials provided with the distribution. |
| 64 3. All advertising materials mentioning features or use of this |
| 65 software must display the following acknowledgment: |
| 66 |
| 67 This product includes software developed by Greg Roelofs |
| 68 and contributors for the book, "PNG: The Definitive Guide," |
| 69 published by O'Reilly and Associates. |
| 70 |
| 71 |
| 72 LICENSE 2 (GNU GPL v2 or later): |
| 73 |
| 74 This program is free software; you can redistribute it and/or modify |
| 75 it under the terms of the GNU General Public License as published by |
| 76 the Free Software Foundation; either version 2 of the License, or |
| 77 (at your option) any later version. |
| 78 |
| 79 This program is distributed in the hope that it will be useful, |
| 80 but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 81 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 82 GNU General Public License for more details. |
| 83 |
| 84 You should have received a copy of the GNU General Public License |
| 85 along with this program; if not, write to the Free Software Foundation, |
| 86 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 87 |
| 88 ---------------------------------------------------------------------------*/ |
| 89 |
| 90 #define PROGNAME "wpng" |
| 91 #define VERSION "2.00 of 2 June 2007" |
| 92 #define APPNAME "Simple PGM/PPM/PAM to PNG Converter" |
| 93 |
| 94 #if defined(__MSDOS__) || defined(__OS2__) |
| 95 # define DOS_OS2_W32 |
| 96 #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) |
| 97 # ifndef __GNUC__ /* treat Win32 native ports of gcc as Unix environments */ |
| 98 # define DOS_OS2_W32 |
| 99 # endif |
| 100 #endif |
| 101 |
| 102 #include <stdio.h> |
| 103 #include <stdlib.h> |
| 104 #include <string.h> |
| 105 #include <setjmp.h> /* for jmpbuf declaration in writepng.h */ |
| 106 #include <time.h> |
| 107 |
| 108 #ifdef DOS_OS2_W32 |
| 109 # include <io.h> /* for isatty(), setmode() prototypes */ |
| 110 # include <fcntl.h> /* O_BINARY for fdopen() without text translation */ |
| 111 # ifdef __EMX__ |
| 112 # ifndef getch |
| 113 # define getch() _read_kbd(0, 1, 0) /* need getche() */ |
| 114 # endif |
| 115 # else /* !__EMX__ */ |
| 116 # ifdef __GO32__ |
| 117 # include <pc.h> |
| 118 # define getch() getkey() /* GRR: need getche() */ |
| 119 # else |
| 120 # include <conio.h> /* for getche() console input */ |
| 121 # endif |
| 122 # endif /* ?__EMX__ */ |
| 123 # define FGETS(buf,len,stream) dos_kbd_gets(buf,len) |
| 124 #else |
| 125 # include <unistd.h> /* for isatty() prototype */ |
| 126 # define FGETS fgets |
| 127 #endif |
| 128 |
| 129 /* #define DEBUG : this enables the Trace() macros */ |
| 130 |
| 131 /* #define FORBID_LATIN1_CTRL : this requires the user to re-enter any |
| 132 text that includes control characters discouraged by the PNG spec; text |
| 133 that includes an escape character (27) must be re-entered regardless */ |
| 134 |
| 135 #include "writepng.h" /* typedefs, common macros, writepng prototypes */ |
| 136 |
| 137 |
| 138 |
| 139 /* local prototypes */ |
| 140 |
| 141 static int wpng_isvalid_latin1(uch *p, int len); |
| 142 static void wpng_cleanup(void); |
| 143 |
| 144 #ifdef DOS_OS2_W32 |
| 145 static char *dos_kbd_gets(char *buf, int len); |
| 146 #endif |
| 147 |
| 148 |
| 149 |
| 150 static mainprog_info wpng_info; /* lone global */ |
| 151 |
| 152 |
| 153 |
| 154 int main(int argc, char **argv) |
| 155 { |
| 156 #ifndef DOS_OS2_W32 |
| 157 FILE *keybd; |
| 158 #endif |
| 159 #ifdef sgi |
| 160 FILE *tmpfile; /* or we could just use keybd, since no overlap */ |
| 161 char tmpline[80]; |
| 162 #endif |
| 163 char *inname = NULL, outname[256]; |
| 164 char *p, pnmchar, pnmline[256]; |
| 165 char *bgstr, *textbuf = NULL; |
| 166 ulg rowbytes; |
| 167 int rc, len = 0; |
| 168 int error = 0; |
| 169 int text = FALSE; |
| 170 int maxval; |
| 171 double LUT_exponent; /* just the lookup table */ |
| 172 double CRT_exponent = 2.2; /* just the monitor */ |
| 173 double default_display_exponent; /* whole display system */ |
| 174 double default_gamma = 0.0; |
| 175 |
| 176 |
| 177 wpng_info.infile = NULL; |
| 178 wpng_info.outfile = NULL; |
| 179 wpng_info.image_data = NULL; |
| 180 wpng_info.row_pointers = NULL; |
| 181 wpng_info.filter = FALSE; |
| 182 wpng_info.interlaced = FALSE; |
| 183 wpng_info.have_bg = FALSE; |
| 184 wpng_info.have_time = FALSE; |
| 185 wpng_info.have_text = 0; |
| 186 wpng_info.gamma = 0.0; |
| 187 |
| 188 |
| 189 /* First get the default value for our display-system exponent, i.e., |
| 190 * the product of the CRT exponent and the exponent corresponding to |
| 191 * the frame-buffer's lookup table (LUT), if any. If the PNM image |
| 192 * looks correct on the user's display system, its file gamma is the |
| 193 * inverse of this value. (Note that this is not an exhaustive list |
| 194 * of LUT values--e.g., OpenStep has a lot of weird ones--but it should |
| 195 * cover 99% of the current possibilities. This section must ensure |
| 196 * that default_display_exponent is positive.) */ |
| 197 |
| 198 #if defined(NeXT) |
| 199 /* third-party utilities can modify the default LUT exponent */ |
| 200 LUT_exponent = 1.0 / 2.2; |
| 201 /* |
| 202 if (some_next_function_that_returns_gamma(&next_gamma)) |
| 203 LUT_exponent = 1.0 / next_gamma; |
| 204 */ |
| 205 #elif defined(sgi) |
| 206 LUT_exponent = 1.0 / 1.7; |
| 207 /* there doesn't seem to be any documented function to |
| 208 * get the "gamma" value, so we do it the hard way */ |
| 209 tmpfile = fopen("/etc/config/system.glGammaVal", "r"); |
| 210 if (tmpfile) { |
| 211 double sgi_gamma; |
| 212 |
| 213 fgets(tmpline, 80, tmpfile); |
| 214 fclose(tmpfile); |
| 215 sgi_gamma = atof(tmpline); |
| 216 if (sgi_gamma > 0.0) |
| 217 LUT_exponent = 1.0 / sgi_gamma; |
| 218 } |
| 219 #elif defined(Macintosh) |
| 220 LUT_exponent = 1.8 / 2.61; |
| 221 /* |
| 222 if (some_mac_function_that_returns_gamma(&mac_gamma)) |
| 223 LUT_exponent = mac_gamma / 2.61; |
| 224 */ |
| 225 #else |
| 226 LUT_exponent = 1.0; /* assume no LUT: most PCs */ |
| 227 #endif |
| 228 |
| 229 /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */ |
| 230 default_display_exponent = LUT_exponent * CRT_exponent; |
| 231 |
| 232 |
| 233 /* If the user has set the SCREEN_GAMMA environment variable as suggested |
| 234 * (somewhat imprecisely) in the libpng documentation, use that; otherwise |
| 235 * use the default value we just calculated. Either way, the user may |
| 236 * override this via a command-line option. */ |
| 237 |
| 238 if ((p = getenv("SCREEN_GAMMA")) != NULL) { |
| 239 double exponent = atof(p); |
| 240 |
| 241 if (exponent > 0.0) |
| 242 default_gamma = 1.0 / exponent; |
| 243 } |
| 244 |
| 245 if (default_gamma == 0.0) |
| 246 default_gamma = 1.0 / default_display_exponent; |
| 247 |
| 248 |
| 249 /* Now parse the command line for options and the PNM filename. */ |
| 250 |
| 251 while (*++argv && !error) { |
| 252 if (!strncmp(*argv, "-i", 2)) { |
| 253 wpng_info.interlaced = TRUE; |
| 254 } else if (!strncmp(*argv, "-time", 3)) { |
| 255 wpng_info.modtime = time(NULL); |
| 256 wpng_info.have_time = TRUE; |
| 257 } else if (!strncmp(*argv, "-text", 3)) { |
| 258 text = TRUE; |
| 259 } else if (!strncmp(*argv, "-gamma", 2)) { |
| 260 if (!*++argv) |
| 261 ++error; |
| 262 else { |
| 263 wpng_info.gamma = atof(*argv); |
| 264 if (wpng_info.gamma <= 0.0) |
| 265 ++error; |
| 266 else if (wpng_info.gamma > 1.01) |
| 267 fprintf(stderr, PROGNAME |
| 268 " warning: file gammas are usually less than 1.0\n"); |
| 269 } |
| 270 } else if (!strncmp(*argv, "-bgcolor", 4)) { |
| 271 if (!*++argv) |
| 272 ++error; |
| 273 else { |
| 274 bgstr = *argv; |
| 275 if (strlen(bgstr) != 7 || bgstr[0] != '#') |
| 276 ++error; |
| 277 else { |
| 278 unsigned r, g, b; /* this way quiets compiler warnings */ |
| 279 |
| 280 sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b); |
| 281 wpng_info.bg_red = (uch)r; |
| 282 wpng_info.bg_green = (uch)g; |
| 283 wpng_info.bg_blue = (uch)b; |
| 284 wpng_info.have_bg = TRUE; |
| 285 } |
| 286 } |
| 287 } else { |
| 288 if (**argv != '-') { |
| 289 inname = *argv; |
| 290 if (argv[1]) /* shouldn't be any more args after filename */ |
| 291 ++error; |
| 292 } else |
| 293 ++error; /* not expecting any other options */ |
| 294 } |
| 295 } |
| 296 |
| 297 |
| 298 /* open the input and output files, or register an error and abort */ |
| 299 |
| 300 if (!inname) { |
| 301 if (isatty(0)) { |
| 302 fprintf(stderr, PROGNAME |
| 303 ": must give input filename or provide image data via stdin\n"); |
| 304 ++error; |
| 305 } else { |
| 306 #ifdef DOS_OS2_W32 |
| 307 /* some buggy C libraries require BOTH setmode() and fdopen(bin) */ |
| 308 setmode(fileno(stdin), O_BINARY); |
| 309 setmode(fileno(stdout), O_BINARY); |
| 310 #endif |
| 311 if ((wpng_info.infile = fdopen(fileno(stdin), "rb")) == NULL) { |
| 312 fprintf(stderr, PROGNAME |
| 313 ": unable to reopen stdin in binary mode\n"); |
| 314 ++error; |
| 315 } else |
| 316 if ((wpng_info.outfile = fdopen(fileno(stdout), "wb")) == NULL) { |
| 317 fprintf(stderr, PROGNAME |
| 318 ": unable to reopen stdout in binary mode\n"); |
| 319 fclose(wpng_info.infile); |
| 320 ++error; |
| 321 } else |
| 322 wpng_info.filter = TRUE; |
| 323 } |
| 324 } else if ((len = strlen(inname)) > 250) { |
| 325 fprintf(stderr, PROGNAME ": input filename is too long [%d chars]\n", |
| 326 len); |
| 327 ++error; |
| 328 } else if (!(wpng_info.infile = fopen(inname, "rb"))) { |
| 329 fprintf(stderr, PROGNAME ": can't open input file [%s]\n", inname); |
| 330 ++error; |
| 331 } |
| 332 |
| 333 if (!error) { |
| 334 fgets(pnmline, 256, wpng_info.infile); |
| 335 if (pnmline[0] != 'P' || ((pnmchar = pnmline[1]) != '5' && |
| 336 pnmchar != '6' && pnmchar != '8')) |
| 337 { |
| 338 fprintf(stderr, PROGNAME |
| 339 ": input file [%s] is not a binary PGM, PPM or PAM file\n", |
| 340 inname); |
| 341 ++error; |
| 342 } else { |
| 343 wpng_info.pnmtype = (int)(pnmchar - '0'); |
| 344 if (wpng_info.pnmtype != 8) |
| 345 wpng_info.have_bg = FALSE; /* no need for bg if opaque */ |
| 346 do { |
| 347 fgets(pnmline, 256, wpng_info.infile); /* lose any comments */ |
| 348 } while (pnmline[0] == '#'); |
| 349 sscanf(pnmline, "%ld %ld", &wpng_info.width, &wpng_info.height); |
| 350 do { |
| 351 fgets(pnmline, 256, wpng_info.infile); /* more comment lines */ |
| 352 } while (pnmline[0] == '#'); |
| 353 sscanf(pnmline, "%d", &maxval); |
| 354 if (wpng_info.width <= 0L || wpng_info.height <= 0L || |
| 355 maxval != 255) |
| 356 { |
| 357 fprintf(stderr, PROGNAME |
| 358 ": only positive width/height, maxval == 255 allowed \n"); |
| 359 ++error; |
| 360 } |
| 361 wpng_info.sample_depth = 8; /* <==> maxval 255 */ |
| 362 |
| 363 if (!wpng_info.filter) { |
| 364 /* make outname from inname */ |
| 365 if ((p = strrchr(inname, '.')) == NULL || |
| 366 (p - inname) != (len - 4)) |
| 367 { |
| 368 strcpy(outname, inname); |
| 369 strcpy(outname+len, ".png"); |
| 370 } else { |
| 371 len -= 4; |
| 372 strncpy(outname, inname, len); |
| 373 strcpy(outname+len, ".png"); |
| 374 } |
| 375 /* check if outname already exists; if not, open */ |
| 376 if ((wpng_info.outfile = fopen(outname, "rb")) != NULL) { |
| 377 fprintf(stderr, PROGNAME ": output file exists [%s]\n", |
| 378 outname); |
| 379 fclose(wpng_info.outfile); |
| 380 ++error; |
| 381 } else if (!(wpng_info.outfile = fopen(outname, "wb"))) { |
| 382 fprintf(stderr, PROGNAME ": can't open output file [%s]\n", |
| 383 outname); |
| 384 ++error; |
| 385 } |
| 386 } |
| 387 } |
| 388 if (error) { |
| 389 fclose(wpng_info.infile); |
| 390 wpng_info.infile = NULL; |
| 391 if (wpng_info.filter) { |
| 392 fclose(wpng_info.outfile); |
| 393 wpng_info.outfile = NULL; |
| 394 } |
| 395 } |
| 396 } |
| 397 |
| 398 |
| 399 /* if we had any errors, print usage and die horrible death...arrr! */ |
| 400 |
| 401 if (error) { |
| 402 fprintf(stderr, "\n%s %s: %s\n", PROGNAME, VERSION, APPNAME); |
| 403 writepng_version_info(); |
| 404 fprintf(stderr, "\n" |
| 405 "Usage: %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] pnmfile\n" |
| 406 "or: ... | %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] | ...\n" |
| 407 " exp \ttransfer-function exponent (``gamma'') of the image in\n" |
| 408 "\t\t floating-point format (e.g., ``%.5f''); if image looks\n" |
| 409 "\t\t correct on given display system, image gamma is equal to\n" |
| 410 "\t\t inverse of display-system exponent, i.e., 1 / (LUT * CRT)\n" |
| 411 "\t\t (where LUT = lookup-table exponent and CRT = CRT exponent;\n" |
| 412 "\t\t first varies, second is usually 2.2, all are positive)\n" |
| 413 " bg \tdesired background color for alpha-channel images, in\n" |
| 414 "\t\t 7-character hex RGB format (e.g., ``#ff7700'' for orange:\n" |
| 415 "\t\t same as HTML colors)\n" |
| 416 " -text\tprompt interactively for text info (tEXt chunks)\n" |
| 417 " -time\tinclude a tIME chunk (last modification time)\n" |
| 418 " -interlace\twrite interlaced PNG image\n" |
| 419 "\n" |
| 420 "pnmfile or stdin must be a binary PGM (`P5'), PPM (`P6') or (extremely\n" |
| 421 "unofficial and unsupported!) PAM (`P8') file. Currently it is required\n" |
| 422 "to have maxval == 255 (i.e., no scaling). If pnmfile is specified, it\n" |
| 423 "is converted to the corresponding PNG file with the same base name but a\n" |
| 424 "``.png'' extension; files read from stdin are converted and sent to stdout.\n" |
| 425 "The conversion is progressive (low memory usage) unless interlacing is\n" |
| 426 "requested; in that case the whole image will be buffered in memory and\n" |
| 427 "written in one call.\n" |
| 428 "\n", PROGNAME, PROGNAME, default_gamma); |
| 429 exit(1); |
| 430 } |
| 431 |
| 432 |
| 433 /* prepare the text buffers for libpng's use; note that even though |
| 434 * PNG's png_text struct includes a length field, we don't have to fill |
| 435 * it out */ |
| 436 |
| 437 if (text && |
| 438 #ifndef DOS_OS2_W32 |
| 439 (keybd = fdopen(fileno(stderr), "r")) != NULL && |
| 440 #endif |
| 441 (textbuf = (char *)malloc((5 + 9)*75)) != NULL) |
| 442 { |
| 443 int i, valid, result; |
| 444 |
| 445 fprintf(stderr, |
| 446 "Enter text info (no more than 72 characters per line);\n"); |
| 447 fprintf(stderr, "to skip a field, hit the <Enter> key.\n"); |
| 448 /* note: just <Enter> leaves len == 1 */ |
| 449 |
| 450 do { |
| 451 valid = TRUE; |
| 452 p = textbuf + TEXT_TITLE_OFFSET; |
| 453 fprintf(stderr, " Title: "); |
| 454 fflush(stderr); |
| 455 if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) { |
| 456 if (p[len-1] == '\n') |
| 457 p[--len] = '\0'; |
| 458 wpng_info.title = p; |
| 459 wpng_info.have_text |= TEXT_TITLE; |
| 460 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) { |
| 461 fprintf(stderr, " " PROGNAME " warning: character code" |
| 462 " %u is %sdiscouraged by the PNG\n specification " |
| 463 "[first occurrence was at character position #%d]\n", |
| 464 (unsigned)p[result], (p[result] == 27)? "strongly " : "", |
| 465 result+1); |
| 466 fflush(stderr); |
| 467 #ifdef FORBID_LATIN1_CTRL |
| 468 wpng_info.have_text &= ~TEXT_TITLE; |
| 469 valid = FALSE; |
| 470 #else |
| 471 if (p[result] == 27) { /* escape character */ |
| 472 wpng_info.have_text &= ~TEXT_TITLE; |
| 473 valid = FALSE; |
| 474 } |
| 475 #endif |
| 476 } |
| 477 } |
| 478 } while (!valid); |
| 479 |
| 480 do { |
| 481 valid = TRUE; |
| 482 p = textbuf + TEXT_AUTHOR_OFFSET; |
| 483 fprintf(stderr, " Author: "); |
| 484 fflush(stderr); |
| 485 if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) { |
| 486 if (p[len-1] == '\n') |
| 487 p[--len] = '\0'; |
| 488 wpng_info.author = p; |
| 489 wpng_info.have_text |= TEXT_AUTHOR; |
| 490 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) { |
| 491 fprintf(stderr, " " PROGNAME " warning: character code" |
| 492 " %u is %sdiscouraged by the PNG\n specification " |
| 493 "[first occurrence was at character position #%d]\n", |
| 494 (unsigned)p[result], (p[result] == 27)? "strongly " : "", |
| 495 result+1); |
| 496 fflush(stderr); |
| 497 #ifdef FORBID_LATIN1_CTRL |
| 498 wpng_info.have_text &= ~TEXT_AUTHOR; |
| 499 valid = FALSE; |
| 500 #else |
| 501 if (p[result] == 27) { /* escape character */ |
| 502 wpng_info.have_text &= ~TEXT_AUTHOR; |
| 503 valid = FALSE; |
| 504 } |
| 505 #endif |
| 506 } |
| 507 } |
| 508 } while (!valid); |
| 509 |
| 510 do { |
| 511 valid = TRUE; |
| 512 p = textbuf + TEXT_DESC_OFFSET; |
| 513 fprintf(stderr, " Description (up to 9 lines):\n"); |
| 514 for (i = 1; i < 10; ++i) { |
| 515 fprintf(stderr, " [%d] ", i); |
| 516 fflush(stderr); |
| 517 if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) |
| 518 p += len; /* now points at NULL; char before is newline */ |
| 519 else |
| 520 break; |
| 521 } |
| 522 if ((len = p - (textbuf + TEXT_DESC_OFFSET)) > 1) { |
| 523 if (p[-1] == '\n') { |
| 524 p[-1] = '\0'; |
| 525 --len; |
| 526 } |
| 527 wpng_info.desc = textbuf + TEXT_DESC_OFFSET; |
| 528 wpng_info.have_text |= TEXT_DESC; |
| 529 p = textbuf + TEXT_DESC_OFFSET; |
| 530 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) { |
| 531 fprintf(stderr, " " PROGNAME " warning: character code" |
| 532 " %u is %sdiscouraged by the PNG\n specification " |
| 533 "[first occurrence was at character position #%d]\n", |
| 534 (unsigned)p[result], (p[result] == 27)? "strongly " : "", |
| 535 result+1); |
| 536 fflush(stderr); |
| 537 #ifdef FORBID_LATIN1_CTRL |
| 538 wpng_info.have_text &= ~TEXT_DESC; |
| 539 valid = FALSE; |
| 540 #else |
| 541 if (p[result] == 27) { /* escape character */ |
| 542 wpng_info.have_text &= ~TEXT_DESC; |
| 543 valid = FALSE; |
| 544 } |
| 545 #endif |
| 546 } |
| 547 } |
| 548 } while (!valid); |
| 549 |
| 550 do { |
| 551 valid = TRUE; |
| 552 p = textbuf + TEXT_COPY_OFFSET; |
| 553 fprintf(stderr, " Copyright: "); |
| 554 fflush(stderr); |
| 555 if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) { |
| 556 if (p[len-1] == '\n') |
| 557 p[--len] = '\0'; |
| 558 wpng_info.copyright = p; |
| 559 wpng_info.have_text |= TEXT_COPY; |
| 560 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) { |
| 561 fprintf(stderr, " " PROGNAME " warning: character code" |
| 562 " %u is %sdiscouraged by the PNG\n specification " |
| 563 "[first occurrence was at character position #%d]\n", |
| 564 (unsigned)p[result], (p[result] == 27)? "strongly " : "", |
| 565 result+1); |
| 566 fflush(stderr); |
| 567 #ifdef FORBID_LATIN1_CTRL |
| 568 wpng_info.have_text &= ~TEXT_COPY; |
| 569 valid = FALSE; |
| 570 #else |
| 571 if (p[result] == 27) { /* escape character */ |
| 572 wpng_info.have_text &= ~TEXT_COPY; |
| 573 valid = FALSE; |
| 574 } |
| 575 #endif |
| 576 } |
| 577 } |
| 578 } while (!valid); |
| 579 |
| 580 do { |
| 581 valid = TRUE; |
| 582 p = textbuf + TEXT_EMAIL_OFFSET; |
| 583 fprintf(stderr, " E-mail: "); |
| 584 fflush(stderr); |
| 585 if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) { |
| 586 if (p[len-1] == '\n') |
| 587 p[--len] = '\0'; |
| 588 wpng_info.email = p; |
| 589 wpng_info.have_text |= TEXT_EMAIL; |
| 590 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) { |
| 591 fprintf(stderr, " " PROGNAME " warning: character code" |
| 592 " %u is %sdiscouraged by the PNG\n specification " |
| 593 "[first occurrence was at character position #%d]\n", |
| 594 (unsigned)p[result], (p[result] == 27)? "strongly " : "", |
| 595 result+1); |
| 596 fflush(stderr); |
| 597 #ifdef FORBID_LATIN1_CTRL |
| 598 wpng_info.have_text &= ~TEXT_EMAIL; |
| 599 valid = FALSE; |
| 600 #else |
| 601 if (p[result] == 27) { /* escape character */ |
| 602 wpng_info.have_text &= ~TEXT_EMAIL; |
| 603 valid = FALSE; |
| 604 } |
| 605 #endif |
| 606 } |
| 607 } |
| 608 } while (!valid); |
| 609 |
| 610 do { |
| 611 valid = TRUE; |
| 612 p = textbuf + TEXT_URL_OFFSET; |
| 613 fprintf(stderr, " URL: "); |
| 614 fflush(stderr); |
| 615 if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) { |
| 616 if (p[len-1] == '\n') |
| 617 p[--len] = '\0'; |
| 618 wpng_info.url = p; |
| 619 wpng_info.have_text |= TEXT_URL; |
| 620 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) { |
| 621 fprintf(stderr, " " PROGNAME " warning: character code" |
| 622 " %u is %sdiscouraged by the PNG\n specification " |
| 623 "[first occurrence was at character position #%d]\n", |
| 624 (unsigned)p[result], (p[result] == 27)? "strongly " : "", |
| 625 result+1); |
| 626 fflush(stderr); |
| 627 #ifdef FORBID_LATIN1_CTRL |
| 628 wpng_info.have_text &= ~TEXT_URL; |
| 629 valid = FALSE; |
| 630 #else |
| 631 if (p[result] == 27) { /* escape character */ |
| 632 wpng_info.have_text &= ~TEXT_URL; |
| 633 valid = FALSE; |
| 634 } |
| 635 #endif |
| 636 } |
| 637 } |
| 638 } while (!valid); |
| 639 |
| 640 #ifndef DOS_OS2_W32 |
| 641 fclose(keybd); |
| 642 #endif |
| 643 |
| 644 } else if (text) { |
| 645 fprintf(stderr, PROGNAME ": unable to allocate memory for text\n"); |
| 646 text = FALSE; |
| 647 wpng_info.have_text = 0; |
| 648 } |
| 649 |
| 650 |
| 651 /* allocate libpng stuff, initialize transformations, write pre-IDAT data */ |
| 652 |
| 653 if ((rc = writepng_init(&wpng_info)) != 0) { |
| 654 switch (rc) { |
| 655 case 2: |
| 656 fprintf(stderr, PROGNAME |
| 657 ": libpng initialization problem (longjmp)\n"); |
| 658 break; |
| 659 case 4: |
| 660 fprintf(stderr, PROGNAME ": insufficient memory\n"); |
| 661 break; |
| 662 case 11: |
| 663 fprintf(stderr, PROGNAME |
| 664 ": internal logic error (unexpected PNM type)\n"); |
| 665 break; |
| 666 default: |
| 667 fprintf(stderr, PROGNAME |
| 668 ": unknown writepng_init() error\n"); |
| 669 break; |
| 670 } |
| 671 exit(rc); |
| 672 } |
| 673 |
| 674 |
| 675 /* free textbuf, since it's a completely local variable and all text info |
| 676 * has just been written to the PNG file */ |
| 677 |
| 678 if (text && textbuf) { |
| 679 free(textbuf); |
| 680 textbuf = NULL; |
| 681 } |
| 682 |
| 683 |
| 684 /* calculate rowbytes on basis of image type; note that this becomes much |
| 685 * more complicated if we choose to support PBM type, ASCII PNM types, or |
| 686 * 16-bit-per-sample binary data [currently not an official NetPBM type] */ |
| 687 |
| 688 if (wpng_info.pnmtype == 5) |
| 689 rowbytes = wpng_info.width; |
| 690 else if (wpng_info.pnmtype == 6) |
| 691 rowbytes = wpng_info.width * 3; |
| 692 else /* if (wpng_info.pnmtype == 8) */ |
| 693 rowbytes = wpng_info.width * 4; |
| 694 |
| 695 |
| 696 /* read and write the image, either in its entirety (if writing interlaced |
| 697 * PNG) or row by row (if non-interlaced) */ |
| 698 |
| 699 fprintf(stderr, "Encoding image data...\n"); |
| 700 fflush(stderr); |
| 701 |
| 702 if (wpng_info.interlaced) { |
| 703 long i; |
| 704 ulg bytes; |
| 705 ulg image_bytes = rowbytes * wpng_info.height; /* overflow? */ |
| 706 |
| 707 wpng_info.image_data = (uch *)malloc(image_bytes); |
| 708 wpng_info.row_pointers = (uch **)malloc(wpng_info.height*sizeof(uch *)); |
| 709 if (wpng_info.image_data == NULL || wpng_info.row_pointers == NULL) { |
| 710 fprintf(stderr, PROGNAME ": insufficient memory for image data\n"); |
| 711 writepng_cleanup(&wpng_info); |
| 712 wpng_cleanup(); |
| 713 exit(5); |
| 714 } |
| 715 for (i = 0; i < wpng_info.height; ++i) |
| 716 wpng_info.row_pointers[i] = wpng_info.image_data + i*rowbytes; |
| 717 bytes = fread(wpng_info.image_data, 1, image_bytes, wpng_info.infile); |
| 718 if (bytes != image_bytes) { |
| 719 fprintf(stderr, PROGNAME ": expected %lu bytes, got %lu bytes\n", |
| 720 image_bytes, bytes); |
| 721 fprintf(stderr, " (continuing anyway)\n"); |
| 722 } |
| 723 if (writepng_encode_image(&wpng_info) != 0) { |
| 724 fprintf(stderr, PROGNAME |
| 725 ": libpng problem (longjmp) while writing image data\n"); |
| 726 writepng_cleanup(&wpng_info); |
| 727 wpng_cleanup(); |
| 728 exit(2); |
| 729 } |
| 730 |
| 731 } else /* not interlaced: write progressively (row by row) */ { |
| 732 long j; |
| 733 ulg bytes; |
| 734 |
| 735 wpng_info.image_data = (uch *)malloc(rowbytes); |
| 736 if (wpng_info.image_data == NULL) { |
| 737 fprintf(stderr, PROGNAME ": insufficient memory for row data\n"); |
| 738 writepng_cleanup(&wpng_info); |
| 739 wpng_cleanup(); |
| 740 exit(5); |
| 741 } |
| 742 error = 0; |
| 743 for (j = wpng_info.height; j > 0L; --j) { |
| 744 bytes = fread(wpng_info.image_data, 1, rowbytes, wpng_info.infile); |
| 745 if (bytes != rowbytes) { |
| 746 fprintf(stderr, PROGNAME |
| 747 ": expected %lu bytes, got %lu bytes (row %ld)\n", rowbytes, |
| 748 bytes, wpng_info.height-j); |
| 749 ++error; |
| 750 break; |
| 751 } |
| 752 if (writepng_encode_row(&wpng_info) != 0) { |
| 753 fprintf(stderr, PROGNAME |
| 754 ": libpng problem (longjmp) while writing row %ld\n", |
| 755 wpng_info.height-j); |
| 756 ++error; |
| 757 break; |
| 758 } |
| 759 } |
| 760 if (error) { |
| 761 writepng_cleanup(&wpng_info); |
| 762 wpng_cleanup(); |
| 763 exit(2); |
| 764 } |
| 765 if (writepng_encode_finish(&wpng_info) != 0) { |
| 766 fprintf(stderr, PROGNAME ": error on final libpng call\n"); |
| 767 writepng_cleanup(&wpng_info); |
| 768 wpng_cleanup(); |
| 769 exit(2); |
| 770 } |
| 771 } |
| 772 |
| 773 |
| 774 /* OK, we're done (successfully): clean up all resources and quit */ |
| 775 |
| 776 fprintf(stderr, "Done.\n"); |
| 777 fflush(stderr); |
| 778 |
| 779 writepng_cleanup(&wpng_info); |
| 780 wpng_cleanup(); |
| 781 |
| 782 return 0; |
| 783 } |
| 784 |
| 785 |
| 786 |
| 787 |
| 788 |
| 789 static int wpng_isvalid_latin1(uch *p, int len) |
| 790 { |
| 791 int i, result = -1; |
| 792 |
| 793 for (i = 0; i < len; ++i) { |
| 794 if (p[i] == 10 || (p[i] > 31 && p[i] < 127) || p[i] > 160) |
| 795 continue; /* character is completely OK */ |
| 796 if (result < 0 || (p[result] != 27 && p[i] == 27)) |
| 797 result = i; /* mark location of first questionable one */ |
| 798 } /* or of first escape character (bad) */ |
| 799 |
| 800 return result; |
| 801 } |
| 802 |
| 803 |
| 804 |
| 805 |
| 806 |
| 807 static void wpng_cleanup(void) |
| 808 { |
| 809 if (wpng_info.outfile) { |
| 810 fclose(wpng_info.outfile); |
| 811 wpng_info.outfile = NULL; |
| 812 } |
| 813 |
| 814 if (wpng_info.infile) { |
| 815 fclose(wpng_info.infile); |
| 816 wpng_info.infile = NULL; |
| 817 } |
| 818 |
| 819 if (wpng_info.image_data) { |
| 820 free(wpng_info.image_data); |
| 821 wpng_info.image_data = NULL; |
| 822 } |
| 823 |
| 824 if (wpng_info.row_pointers) { |
| 825 free(wpng_info.row_pointers); |
| 826 wpng_info.row_pointers = NULL; |
| 827 } |
| 828 } |
| 829 |
| 830 |
| 831 |
| 832 |
| 833 #ifdef DOS_OS2_W32 |
| 834 |
| 835 static char *dos_kbd_gets(char *buf, int len) |
| 836 { |
| 837 int ch, count=0; |
| 838 |
| 839 do { |
| 840 buf[count++] = ch = getche(); |
| 841 } while (ch != '\r' && count < len-1); |
| 842 |
| 843 buf[count--] = '\0'; /* terminate string */ |
| 844 if (buf[count] == '\r') /* Enter key makes CR, so change to newline */ |
| 845 buf[count] = '\n'; |
| 846 |
| 847 fprintf(stderr, "\n"); /* Enter key does *not* cause a newline */ |
| 848 fflush(stderr); |
| 849 |
| 850 return buf; |
| 851 } |
| 852 |
| 853 #endif /* DOS_OS2_W32 */ |
OLD | NEW |