From dcf9d82a9597a1c699e5d115a55954b06c33214e Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Mon, 30 Jan 2017 10:52:19 +0100 Subject: [PATCH] imageio: add limited PNM support for reading see: http://netpbm.sourceforge.net/ Only reads P5 and P6 pnm files for now. Change-Id: I2332a623f803df67455047f570f1cff9f464480a --- CMakeLists.txt | 2 + Makefile.vc | 1 + build.gradle | 1 + imageio/Android.mk | 1 + imageio/Makefile.am | 1 + imageio/image_dec.c | 5 ++ imageio/image_dec.h | 2 + imageio/pnmdec.c | 142 ++++++++++++++++++++++++++++++++++++++++++++ imageio/pnmdec.h | 37 ++++++++++++ makefile.unix | 1 + 10 files changed, 193 insertions(+) create mode 100644 imageio/pnmdec.c create mode 100644 imageio/pnmdec.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c62dd19..dcea857d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,6 +124,8 @@ if(WEBP_BUILD_CWEBP OR WEBP_BUILD_DWEBP OR ${CMAKE_CURRENT_SOURCE_DIR}/imageio/metadata.h ${CMAKE_CURRENT_SOURCE_DIR}/imageio/pngdec.c ${CMAKE_CURRENT_SOURCE_DIR}/imageio/pngdec.h + ${CMAKE_CURRENT_SOURCE_DIR}/imageio/pnmdec.c + ${CMAKE_CURRENT_SOURCE_DIR}/imageio/pnmdec.h ${CMAKE_CURRENT_SOURCE_DIR}/imageio/tiffdec.c ${CMAKE_CURRENT_SOURCE_DIR}/imageio/tiffdec.h ${CMAKE_CURRENT_SOURCE_DIR}/imageio/webpdec.c diff --git a/Makefile.vc b/Makefile.vc index ca0955ca..0d8857c0 100644 --- a/Makefile.vc +++ b/Makefile.vc @@ -263,6 +263,7 @@ IMAGEIO_DEC_OBJS = \ $(DIROBJ)\imageio\jpegdec.obj \ $(DIROBJ)\imageio\metadata.obj \ $(DIROBJ)\imageio\pngdec.obj \ + $(DIROBJ)\imageio\pnmdec.obj \ $(DIROBJ)\imageio\tiffdec.obj \ $(DIROBJ)\imageio\webpdec.obj \ $(DIROBJ)\imageio\wicdec.obj \ diff --git a/build.gradle b/build.gradle index f05575d4..1d661b50 100644 --- a/build.gradle +++ b/build.gradle @@ -288,6 +288,7 @@ model { include "jpegdec.c" include "metadata.c" include "pngdec.c" + include "pnmdec.c" include "tiffdec.c" include "webpdec.c" } diff --git a/imageio/Android.mk b/imageio/Android.mk index 2f1edc9c..e779f71e 100644 --- a/imageio/Android.mk +++ b/imageio/Android.mk @@ -25,6 +25,7 @@ LOCAL_SRC_FILES := \ jpegdec.c \ metadata.c \ pngdec.c \ + pnmdec.c \ tiffdec.c \ webpdec.c \ diff --git a/imageio/Makefile.am b/imageio/Makefile.am index d278a904..de45b00b 100644 --- a/imageio/Makefile.am +++ b/imageio/Makefile.am @@ -11,6 +11,7 @@ libimagedec_la_SOURCES = image_dec.c image_dec.h libimagedec_la_SOURCES += jpegdec.c jpegdec.h libimagedec_la_SOURCES += metadata.c metadata.h libimagedec_la_SOURCES += pngdec.c pngdec.h +libimagedec_la_SOURCES += pnmdec.c pnmdec.h libimagedec_la_SOURCES += tiffdec.c tiffdec.h libimagedec_la_SOURCES += webpdec.c webpdec.h libimagedec_la_SOURCES += wicdec.c wicdec.h diff --git a/imageio/image_dec.c b/imageio/image_dec.c index 67d47d5f..86b8ccc3 100644 --- a/imageio/image_dec.c +++ b/imageio/image_dec.c @@ -29,6 +29,10 @@ WebPInputFileFormat WebPGuessImageType(const uint8_t* const data, format = WEBP_TIFF_FORMAT; } else if (magic1 == 0x52494646 && magic2 == 0x57454250) { format = WEBP_WEBP_FORMAT; + } else if (((magic1 >> 24) & 0xff) == 'P') { + const int type = (magic1 >> 16) & 0xff; + // we only support 'P5' and 'P6' for now. + if (type >= '5' && type <= '6') format = WEBP_PNM_FORMAT; } } return format; @@ -51,6 +55,7 @@ WebPImageReader WebPGetImageReader(WebPInputFileFormat format) { case WEBP_JPEG_FORMAT: return ReadJPEG; case WEBP_TIFF_FORMAT: return ReadTIFF; case WEBP_WEBP_FORMAT: return ReadWebP; + case WEBP_PNM_FORMAT: return ReadPNM; default: return FailReader; } } diff --git a/imageio/image_dec.h b/imageio/image_dec.h index 28a24ebe..df411e19 100644 --- a/imageio/image_dec.h +++ b/imageio/image_dec.h @@ -23,6 +23,7 @@ #include "./metadata.h" #include "./jpegdec.h" #include "./pngdec.h" +#include "./pnmdec.h" #include "./tiffdec.h" #include "./webpdec.h" #include "./wicdec.h" @@ -36,6 +37,7 @@ typedef enum { WEBP_JPEG_FORMAT, WEBP_TIFF_FORMAT, WEBP_WEBP_FORMAT, + WEBP_PNM_FORMAT, WEBP_UNSUPPORTED_FORMAT } WebPInputFileFormat; diff --git a/imageio/pnmdec.c b/imageio/pnmdec.c new file mode 100644 index 00000000..dcb7b37d --- /dev/null +++ b/imageio/pnmdec.c @@ -0,0 +1,142 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// (limited) PNM decoder + +#include "./pnmdec.h" + +#include +#include +#include +#include + +#include "webp/encode.h" +#include "./imageio_util.h" + +// ----------------------------------------------------------------------------- +// PNM decoding + +#define MAX_LINE_SIZE 1024 +static const size_t kMinPNMHeaderSize = 3; + +static size_t ReadLine(const uint8_t* const data, size_t off, size_t data_size, + char out[MAX_LINE_SIZE + 1], size_t* const out_size) { + size_t i = 0; + *out_size = 0; + redo: + for (i = 0; i < MAX_LINE_SIZE && off < data_size; ++i) { + out[i] = data[off++]; + if (out[i] == '\n') break; + } + if (off < data_size) { + if (i == 0) goto redo; // empty line + if (out[0] == '#') goto redo; // skip comment + } + out[i] = 0; // safety sentinel + *out_size = i; + return off; +} + +static size_t ReadHeader(const uint8_t* const data, size_t data_size, + int* const width, int* const height, + int* const type, int* const max_value) { + size_t off = 0; + char out[MAX_LINE_SIZE + 1]; + size_t out_size; + if (width == NULL || height == NULL || type == NULL || max_value == NULL) { + return 0; + } + *width = 0; + *height = 0; + *type = -1; + *max_value = 0; + if (data == NULL || data_size < kMinPNMHeaderSize) return 0; + off = ReadLine(data, off, data_size, out, &out_size); + if (off == 0 || sscanf(out, "P%d", type) != 1) return 0; + off = ReadLine(data, off, data_size, out, &out_size); + if (off == 0 || sscanf(out, "%d %d", width, height) != 2) return 0; + off = ReadLine(data, off, data_size, out, &out_size); + if (off == 0 || sscanf(out, "%d", max_value) != 1) return 0; + return off; +} + +int ReadPNM(const uint8_t* const data, size_t data_size, + WebPPicture* const pic, int keep_alpha, + struct Metadata* const metadata) { + int ok = 0; + int width, height, type, max_value; + int i, j; + uint64_t stride; + uint8_t* rgb = NULL, *tmp_rgb; + size_t offset; + + offset = ReadHeader(data, data_size, &width, &height, + &type, &max_value); + if (offset == 0) goto End; + if (type != 5 && type != 6) { + fprintf(stderr, "Unsupported P%d PNM format.\n", type); + goto End; + } + + // Some basic validations. + if (pic == NULL) goto End; + if (width <= 0 || height <= 0 || + width > WEBP_MAX_DIMENSION || height > WEBP_MAX_DIMENSION) { + fprintf(stderr, "Invalid %dx%d dimension for PNM\n", width, height); + goto End; + } + + stride = 3LL * width; + if (stride != (size_t)stride || + !ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) { + goto End; + } + if ((type == 5 && offset + 1LL * width * height > data_size) || + (type == 6 && offset + 3LL * width * height > data_size)) { + fprintf(stderr, "Truncated PNM file (P%d).\n", type); + goto End; + } + + rgb = (uint8_t*)malloc((size_t)stride * height); + if (rgb == NULL) goto End; + + // Convert input + tmp_rgb = rgb; + for (j = 0; j < height; ++j) { + if (type == 5) { + assert(offset + width <= data_size); + for (i = 0; i < width; ++i) { + const uint8_t v = data[offset + i]; + tmp_rgb[3 * i + 0] = tmp_rgb[3 * i + 1] = tmp_rgb[3 * i + 2] = v; + } + offset += width; + } else if (type == 6) { + assert(offset + 3LL * width <= data_size); + memcpy(tmp_rgb, data + offset, 3 * width * sizeof(*data)); + offset += 3 * width; + } + tmp_rgb += stride; + } + + // WebP conversion. + pic->width = width; + pic->height = height; + ok = WebPPictureImportRGB(pic, rgb, (int)stride); + if (!ok) goto End; + + ok = 1; + End: + free((void*)rgb); + + (void)metadata; + (void)keep_alpha; + return ok; +} + +// ----------------------------------------------------------------------------- diff --git a/imageio/pnmdec.h b/imageio/pnmdec.h new file mode 100644 index 00000000..c4d5823e --- /dev/null +++ b/imageio/pnmdec.h @@ -0,0 +1,37 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// partial PNM format decoder (ppm/pgm) + +#ifndef WEBP_IMAGEIO_PNMDEC_H_ +#define WEBP_IMAGEIO_PNMDEC_H_ + +#include "webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct Metadata; +struct WebPPicture; + +// Reads a PNM file from 'data', returning the decoded output in 'pic'. +// The output is RGB or YUV depending on pic->use_argb value. +// Returns true on success. +// 'metadata' has no effect, but is kept for coherence with other signatures +// for image readers. +int ReadPNM(const uint8_t* const data, size_t data_size, + struct WebPPicture* const pic, int keep_alpha, + struct Metadata* const metadata); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_IMAGEIO_PNMDEC_H_ diff --git a/makefile.unix b/makefile.unix index 47b8c65f..69ca0828 100644 --- a/makefile.unix +++ b/makefile.unix @@ -223,6 +223,7 @@ EX_FORMAT_DEC_OBJS = \ imageio/jpegdec.o \ imageio/metadata.o \ imageio/pngdec.o \ + imageio/pnmdec.o \ imageio/tiffdec.o \ imageio/webpdec.o \