From 62f9a1f9eaa1d74acb319bf6598fb4642e23c4ee Mon Sep 17 00:00:00 2001
From: David Maciejak <david.maciejak@gmail.com>
Date: Sat, 15 Feb 2014 18:25:13 +0800
Subject: [PATCH] Improved NETPBM support, file format detection and fixed
 test compilation

---
 wrlib/load.c            |   18 ++--
 wrlib/load_ppm.c        |  270 +++++++++++++++++++++++++++++++++++++++--------
 wrlib/tests/Makefile.am |    2 +-
 wrlib/tests/testdraw.c  |    1 +
 wrlib/tests/view.c      |    6 +-
 5 files changed, 240 insertions(+), 57 deletions(-)

diff --git a/wrlib/load.c b/wrlib/load.c
index 94ef564..061a50d 100644
--- a/wrlib/load.c
+++ b/wrlib/load.c
@@ -78,8 +78,8 @@ char **RSupportedFileFormats(void)
 
 	/* built-in */
 	tmp[i++] = "XPM";
-	/* built-in */
-	tmp[i++] = "PPM";
+	/* built-in PNM here refers to anymap format: PPM, PGM, PBM */
+	tmp[i++] = "PNM";
 #ifdef USE_TIFF
 	tmp[i++] = "TIFF";
 #endif
@@ -275,7 +275,7 @@ char *RGetImageFileFormat(const char *file)
 static WRImgFormat identFile(const char *path)
 {
 	FILE *file;
-	unsigned char buffer[32];
+	unsigned char buffer[7];
 	size_t nread;
 
 	assert(path != NULL);
@@ -295,7 +295,7 @@ static WRImgFormat identFile(const char *path)
 	RETRY( fclose(file) )
 
 	/* check for XPM */
-	if (strncmp((char *)buffer, "/* XPM */", 9) == 0)
+	if (strncmp((char *)buffer, "/* XPM", 6) == 0)
 		return IM_XPM;
 
 	/* check for TIFF */
@@ -303,14 +303,12 @@ static WRImgFormat identFile(const char *path)
 	    || (buffer[0] == 'M' && buffer[1] == 'M' && buffer[2] == 0 && buffer[3] == '*'))
 		return IM_TIFF;
 
-#ifdef USE_PNG
 	/* check for PNG */
-	if (!png_sig_cmp(buffer, 0, 8))
+	if (buffer[0] == 0x89 && buffer[1] == 'P' && buffer[2] == 'N' && buffer[3] == 'G')
 		return IM_PNG;
-#endif
 
-	/* check for raw PPM or PGM */
-	if (buffer[0] == 'P' && (buffer[1] == '5' || buffer[1] == '6'))
+	/* check for PBM or PGM or PPM */
+	if (buffer[0] == 'P' && (buffer[1] > '0' && buffer[1] < '7') && (buffer[2] == 0x0a || buffer[2] == 0x20 || buffer[2] == 0x09 || buffer[2] == 0x0d))
 		return IM_PPM;
 
 	/* check for JPEG */
@@ -318,7 +316,7 @@ static WRImgFormat identFile(const char *path)
 		return IM_JPEG;
 
 	/* check for GIF */
-	if (buffer[0] == 'G' && buffer[1] == 'I' && buffer[2] == 'F')
+	if (buffer[0] == 'G' && buffer[1] == 'I' && buffer[2] == 'F' && buffer[3] == '8')
 		return IM_GIF;
 
 	return IM_UNKNOWN;
diff --git a/wrlib/load_ppm.c b/wrlib/load_ppm.c
index 49c10db..b45d0f0 100644
--- a/wrlib/load_ppm.c
+++ b/wrlib/load_ppm.c
@@ -3,6 +3,7 @@
  * Raster graphics library
  *
  * Copyright (c) 1997-2003 Alfredo K. Kojima
+ * Copyright (c) 2014 Window Maker Team
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Library General Public
@@ -26,79 +27,248 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <limits.h>
 
 #include "wraster.h"
 #include "imgformat.h"
 
-static RImage *load_graymap(FILE *file, int w, int h, int max, int raw)
+/* fileio.c - routines to read elements based on Netpbm
+**
+** Copyright (C) 1988 by Jef Poskanzer.
+*/
+
+char pm_getc(FILE * const fileP)
+{
+	int ich;
+	char ch;
+
+	ich = getc(fileP);
+	if (ich == EOF)
+		fprintf(stderr, "EOF / read error reading a byte\n");
+	ch = (char)ich;
+
+	if (ch == '#') {
+		do {
+			ich = getc(fileP);
+			if (ich == EOF)
+				fprintf(stderr, "EOF / read error reading a byte\n");
+			ch = (char)ich;
+		} while (ch != '\n' && ch != '\r');
+	}
+	return ch;
+}
+
+unsigned char pm_getrawbyte(FILE * const file)
+{
+	int iby;
+
+	iby = getc(file);
+	if (iby == EOF)
+		fprintf(stderr, "EOF / read error reading a one-byte sample\n");
+	return (unsigned char)iby;
+}
+
+int pm_getuint(FILE * const ifP)
+{
+	char ch;
+	unsigned int i;
+
+	do {
+		ch = pm_getc(ifP);
+	} while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r');
+
+	if (ch < '0' || ch > '9')
+		fprintf(stderr, "junk in file where an unsigned integer should be\n");
+
+	i = 0;
+	do {
+		unsigned int const digitVal = ch - '0';
+
+		if (i > INT_MAX / 10) {
+			fprintf(stderr, "ASCII decimal integer in file is too large to be processed\n");
+			return -1;
+		}
+
+		i *= 10;
+
+		if (i > INT_MAX - digitVal) {
+			fprintf(stderr, "ASCII decimal integer in file is too large to be processed\n");
+			return -1;
+		}
+
+		i += digitVal;
+
+		ch = pm_getc(ifP);
+	} while (ch >= '0' && ch <= '9');
+
+	return i;
+}
+/******************************************************************************************/
+
+/* PGM: support for portable graymap ascii and binary encoding */
+static RImage *load_graymap(FILE * file, int w, int h, int max, int raw)
 {
 	RImage *image;
 	unsigned char *ptr;
-	char *buf;
 	int x, y;
 
 	image = RCreateImage(w, h, 0);
 	if (!image)
 		return NULL;
 
-	if (!raw)
+	if (raw != '2' && raw != '5')
 		return image;
 
 	if (max < 256) {
-		buf = malloc(w + 1);
-		if (!buf)
-			return NULL;
-
 		ptr = image->data;
-		for (y = 0; y < h; y++) {
-			if (!fread(buf, w, 1, file)) {
-				free(buf);
-				RErrorCode = RERR_BADIMAGEFILE;
-				return NULL;
+		if (raw == '2') {
+			int val;
+			for (y = 0; y < h; y++) {
+				for (x = 0; x < w; x++) {
+					val = pm_getuint(file);
+
+					if (val > max || val < 0) {
+						RErrorCode = RERR_BADIMAGEFILE;
+						return NULL;
+					}
+
+					val = val * 255 / max;
+					*(ptr++) = val;
+					*(ptr++) = val;
+					*(ptr++) = val;
+				}
 			}
+		} else {
+			if (raw == '5') {
+				char *buf;
+				buf = malloc(w + 1);
+				if (!buf)
+					return NULL;
+				for (y = 0; y < h; y++) {
+					if (!fread(buf, w, 1, file)) {
+						free(buf);
+						RErrorCode = RERR_BADIMAGEFILE;
+						return NULL;
+					}
 
-			for (x = 0; x < w; x++) {
-				*(ptr++) = buf[x];
-				*(ptr++) = buf[x];
-				*(ptr++) = buf[x];
+					for (x = 0; x < w; x++) {
+						*(ptr++) = buf[x];
+						*(ptr++) = buf[x];
+						*(ptr++) = buf[x];
+					}
+				}
+				free(buf);
 			}
 		}
-		free(buf);
 	}
-
 	return image;
 }
 
-static RImage *load_pixmap(FILE *file, int w, int h, int max, int raw)
+/* PPM: support for portable pixmap ascii and binary encoding */
+static RImage *load_pixmap(FILE * file, int w, int h, int max, int raw)
 {
 	RImage *image;
-	int i;
-	char buf[3];
 	unsigned char *ptr;
+	int i = 0;
 
 	image = RCreateImage(w, h, 0);
 	if (!image)
 		return NULL;
 
-	if (!raw)
+	if (raw != '3' && raw != '6')
 		return image;
 
 	ptr = image->data;
 	if (max < 256) {
-		i = 0;
+		if (raw == '3') {
+			int x, y, val;
+			for (y = 0; y < h; y++) {
+				for (x = 0; x < w; x++) {
+					for (i = 0; i < 3; i++) {
+						val = pm_getuint(file);
+
+						if (val > max || val < 0) {
+							RErrorCode = RERR_BADIMAGEFILE;
+							return NULL;
+						}
+
+						val = val * 255 / max;
+						*(ptr++) = val;
+					}
+				}
+			}
+		} else if (raw == '6') {
+			char buf[3];
+			while (i < w * h) {
+				if (fread(buf, 1, 3, file) != 3) {
+					RErrorCode = RERR_BADIMAGEFILE;
+					return NULL;
+				}
+
+				*(ptr++) = buf[0];
+				*(ptr++) = buf[1];
+				*(ptr++) = buf[2];
+				i++;
+			}
+		}
+	}
+	return image;
+}
+
+/* PBM: support for portable bitmap ascii and binary encoding */
+static RImage *load_bitmap(FILE * file, int w, int h, int max, int raw)
+{
+	RImage *image;
+	int val;
+	unsigned char *ptr;
+
+	image = RCreateImage(w, h, 0);
+	if (!image)
+		return NULL;
+
+	if (raw != '1' && raw != '4')
+		return image;
+
+	ptr = image->data;
+	if (raw == '1') {
+		int i = 0;
 		while (i < w * h) {
-			if (fread(buf, 1, 3, file) != 3) {
+			val = pm_getuint(file);
+
+			if (val > max || val < 0) {
 				RErrorCode = RERR_BADIMAGEFILE;
 				return NULL;
 			}
 
-			*(ptr++) = buf[0];
-			*(ptr++) = buf[1];
-			*(ptr++) = buf[2];
+			val = (val == 0) ? 255 : 0;
+			*(ptr++) = val;
+			*(ptr++) = val;
+			*(ptr++) = val;
 			i++;
 		}
-	}
+	} else {
+		if (raw == '4') {
+			unsigned char buf;
+			int bitshift;
+			int x, y;
 
+			for (y = 0; y < h; y++) {
+				bitshift = -1;
+				for (x = 0; x < w; x++) {
+					if (bitshift == -1) {
+						buf = pm_getrawbyte(file);
+						bitshift = 7;
+					}
+					val = (buf >> bitshift) & 1;
+					val = (val == 0) ? 255 : 0;
+					--bitshift;
+					*(ptr++) = val;
+					*(ptr++) = val;
+					*(ptr++) = val;
+				}
+			}
+		}
+	}
 	return image;
 }
 
@@ -123,8 +293,8 @@ RImage *RLoadPPM(const char *file_name)
 		return NULL;
 	}
 
-	/* only accept raw pixmaps or graymaps */
-	if (buffer[0] != 'P' || (buffer[1] != '5' && buffer[1] != '6')) {
+	/* accept bitmaps,  pixmaps or graymaps */
+	if (buffer[0] != 'P' || (buffer[1] < '1' && buffer[1] > '6')) {
 		RErrorCode = RERR_BADFORMAT;
 		fclose(file);
 		return NULL;
@@ -152,24 +322,36 @@ RImage *RLoadPPM(const char *file_name)
 		return NULL;
 	}
 
-	if (!fgets(buffer, 255, file)) {
-		RErrorCode = RERR_BADIMAGEFILE;
-		fclose(file);
-		return NULL;
+	if (type != '1' && type != '4') {
+		if (!fgets(buffer, 255, file)) {
+			RErrorCode = RERR_BADIMAGEFILE;
+			fclose(file);
+			return NULL;
+		}
+		/* get max value */
+		if (sscanf(buffer, "%i", &m) != 1 || m < 1) {
+			/* Short file */
+			RErrorCode = RERR_BADIMAGEFILE;
+			fclose(file);
+			return NULL;
+		}
+	} else {
+		m = 1;
 	}
 
-	if (sscanf(buffer, "%i", &m) != 1 || m < 1) {
-		/* Short file */
-		RErrorCode = RERR_BADIMAGEFILE;
-		fclose(file);
-		return NULL;
+	/* check portable bitmap type,  ascii = 1 and binary = 4 */
+	if (type == '1' || type == '4')
+		image = load_bitmap(file, w, h, m, type);
+	else {
+		/* check portable graymap type,  ascii = 2 and binary = 5 */
+		if (type == '2' || type == '5')
+			image = load_graymap(file, w, h, m, type);
+		else
+			/* check portable pixmap type, ascii = 3 and binary = 6 */
+		if (type == '3' || type == '6')
+			image = load_pixmap(file, w, h, m, type);
 	}
 
-	if (type == '5')
-		image = load_graymap(file, w, h, m, type == '5');
-	else if (type == '6')
-		image = load_pixmap(file, w, h, m, type == '6');
-
 	fclose(file);
 	return image;
 }
diff --git a/wrlib/tests/Makefile.am b/wrlib/tests/Makefile.am
index e447e97..76c9872 100644
--- a/wrlib/tests/Makefile.am
+++ b/wrlib/tests/Makefile.am
@@ -8,7 +8,7 @@ EXTRA_DIST = test.png tile.xpm ballot_box.xpm
 
 AM_CPPFLAGS = -I$(srcdir)/.. $(DFLAGS) @HEADER_SEARCH_PATH@
 
-LIBLIST = $(top_builddir)/wrlib/libwraster.la
+LIBLIST = $(top_builddir)/wrlib/libwraster.la @XLIBS@
 
 testdraw_SOURCES = testdraw.c
 testdraw_LDADD = $(LIBLIST)
diff --git a/wrlib/tests/testdraw.c b/wrlib/tests/testdraw.c
index 8d416bb..2b209f5 100644
--- a/wrlib/tests/testdraw.c
+++ b/wrlib/tests/testdraw.c
@@ -561,6 +561,7 @@ int main(int argc, char **argv)
 {
 	RContextAttributes attr;
 	int visualID = -1;
+	(void) argc;
 
 	ProgName = strrchr(argv[0], '/');
 	if (!ProgName)
diff --git a/wrlib/tests/view.c b/wrlib/tests/view.c
index 6b107af..52c3ade 100644
--- a/wrlib/tests/view.c
+++ b/wrlib/tests/view.c
@@ -24,8 +24,10 @@ int main(int argc, char **argv)
 	attr.colors_per_channel = 4;
 	ctx = RCreateContext(dpy, DefaultScreen(dpy), &attr);
 
-	if (argc < 2)
+	if (argc < 2) {
+		printf("using default image as none was provided\n");
 		img = RGetImageFromXPMData(ctx, image_name);
+	}
 	else
 		img = RLoadImage(ctx, argv[1], 0);
 
@@ -70,7 +72,7 @@ int main(int argc, char **argv)
 		exit(1);
 	}
 
-	printf("%ix%i\n", img->width, img->height);
+	printf("size is %ix%i\n", img->width, img->height);
 
 	win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 10, 10, img->width, img->height, 0, 0, 0);
 	XSetWindowBackgroundPixmap(dpy, win, pix);
-- 
1.7.10.4

