From 75293d1c9ec4770d532baddf986f74387bb66b32 Mon Sep 17 00:00:00 2001 From: Bert Burgemeister Date: Thu, 26 Jan 2017 13:36:32 +0100 Subject: [PATCH] Add ability to unpack JPEG --- Makefile | 6 +-- image-reader.lisp | 19 +++++--- imread.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++--- phoros.asd | 2 +- 4 files changed, 136 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index c877724..393facc 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # PHOROS -- Photogrammetric Road Survey -# Copyright (C) 2010, 2011, 2012, 2015 Bert Burgemeister +# Copyright (C) 2010, 2011, 2012, 2015, 2017 Bert Burgemeister # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -201,10 +201,10 @@ git-tag : phoros #tag name is :version string in phoros.asd git tag -a $(PHOROS_VERSION) -m "" imread : imread.c Makefile #for debugging - gcc imread.c -O2 -o imread `pkg-config --cflags --libs libpng` -lm + gcc imread.c -O2 -o imread `pkg-config --cflags --libs libpng libjpeg` -lm $(LIBIMREAD) : imread.c Makefile - gcc -fpic -shared -O2 -o imread.so `pkg-config --cflags --libs libpng` -lm imread.c + gcc -fpic -shared -O2 -o imread.so `pkg-config --cflags --libs libpng libjpeg` -lm imread.c clean : rm -rf *.fasl *.log \ diff --git a/image-reader.lisp b/image-reader.lisp index 272a700..7ef13d8 100644 --- a/image-reader.lisp +++ b/image-reader.lisp @@ -1,5 +1,5 @@ ;;; PHOROS -- Photogrammetric Road Survey -;;; Copyright (C) 2010, 2011, 2012, 2016 Bert Burgemeister +;;; Copyright (C) 2010, 2011, 2012, 2016, 2017 Bert Burgemeister ;;; ;;; This program is free software; you can redistribute it and/or modify ;;; it under the terms of the GNU General Public License as published by @@ -818,7 +818,6 @@ is a wart." ;; TODO: bayer-pattern should be applied to the unturned image (let ((blob-start (find-keyword path "PICTUREDATA_BEGIN" start)) (blob-size (find-keyword-value path "dataSize=" start)) - (huffman-table-size (* 511 (+ 1 4))) (image-height (find-keyword-value path "height=" start)) (image-width (find-keyword-value path "width=" start)) (compression-mode (find-keyword-value path "compressed=" start)) @@ -827,10 +826,10 @@ is a wart." (cffi:with-foreign-objects ((baypat :int 3) (colr-raisr :double 3) (mem-png 'mem-encode) - (compressed - :unsigned-char (- blob-size huffman-table-size)) + (compressed :unsigned-char blob-size) (uncompressed - :unsigned-char (* image-width image-height))) + ;; Too big by a factor of channels unless we have a JPEG + :unsigned-char (* image-width image-height channels))) (loop for i from 0 below (min 4 (first (array-dimensions bayer-pattern))) do (setf (cffi:mem-aref baypat :int i) (aref bayer-pattern i))) @@ -839,7 +838,7 @@ is a wart." (setf (cffi:mem-aref colr-raisr :double i) (coerce (aref color-raiser i) 'double-float))) (let ((png2mem-exit - (imread:png2mem (namestring path) blob-start (- blob-size huffman-table-size) + (imread:png2mem (namestring path) blob-start blob-size image-width image-height channels baypat compression-mode uncompressed compressed mem-png (if reversep 1 0) (if brightenp 1 0) colr-raisr))) @@ -859,6 +858,14 @@ is a wart." compression-mode path)) ((= 6 png2mem-exit) (error "Don't know how to deal with ~D-channel pixels." channels)) + ((= 71 png2mem-exit) + (error "JPEG decompression error.")) + ((= 72 png2mem-exit) + (error "JPEG discarded. It was bigger than expected.")) + ((= 73 png2mem-exit) + (error "JPEG reversing not implemented.")) + ((= 74 png2mem-exit) + (error "JPEG brightening not implemented.")) ((= 11 png2mem-exit) (error "PNG error: create_write_struct().")) ((= 12 png2mem-exit) diff --git a/imread.c b/imread.c index a16c747..9e8e4b9 100644 --- a/imread.c +++ b/imread.c @@ -1,5 +1,5 @@ /* PHOROS -- Photogrammetric Road Survey */ -/* Copyright (C) 2016 Bert Burgemeister */ +/* Copyright (C) 2016, 2017 Bert Burgemeister */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -15,12 +15,14 @@ /* with this program; if not, write to the Free Software Foundation, Inc., */ /* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include -#include -#include #include #include +#include +#include +#include + #include +#include void cplt_horiz(unsigned char *rawp, png_bytep row, int y, int x, int color, int width) @@ -227,13 +229,11 @@ uncompressed2png(struct mem_encode *mem_png, int width, int height, int channels mem_png->buffer = NULL; mem_png->size = 0; png_set_write_fn(png_ptr, mem_png, write_png_data, flush_png); - /* Write header (8 bit colour depth) */ row = malloc(channels * width * sizeof(png_byte)); if (row == NULL) { retval = 21; goto finalize; } - /* Write image data */ if (channels == 3) { png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, @@ -301,6 +301,62 @@ finalize: } int +uncompressedjpeg2png(struct mem_encode *mem_png, int width, int height, + int channels, double *color_raiser, + unsigned char *uncompressed) +{ + int retval = 0; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + void (*raise)(unsigned char *, int, double *) = raise_noop; + int y; + int i; + + for (i = 0; i < 3; i++) + if (fabs(color_raiser[i] - 1) > 0.00001) + raise = raise_color; + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png_ptr == NULL) { + retval = 11; /* Could not allocate write struct */ + goto finalize; + } + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) { + retval = 12; /* Could not allocate info struct */ + goto finalize; + } + if (setjmp(png_jmpbuf(png_ptr))) { + retval = 13; /* Error during png creation */ + goto finalize; + } + mem_png->buffer = NULL; + mem_png->size = 0; + png_set_write_fn(png_ptr, mem_png, write_png_data, flush_png); + if (channels == 3) { + for (i = 0; i < width * height; i++) + raise(uncompressed, i, color_raiser); + png_set_IHDR(png_ptr, info_ptr, width, height, + 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + } else if (channels == 1) { + png_set_IHDR(png_ptr, info_ptr, width, height, + 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + } else { + retval = 6; /* wrong number of channels */ + goto finalize; + } + png_write_info(png_ptr, info_ptr); + for (y = 0; y < height; y++) + png_write_row(png_ptr, uncompressed + y * width * channels); + png_write_end(png_ptr, NULL); +finalize: + if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, NULL); + return retval; +} + +int huffdecode(int width, int height, unsigned char *uncompressed, unsigned char hcode[static 4 * 511], unsigned char hlen[static 511], unsigned char *compressed) @@ -409,6 +465,21 @@ brighten(unsigned char *base, size_t size) base[i] = (base[i] - min) * UCHAR_MAX / range; } +struct imread_jpeg_error_mgr { + struct jpeg_error_mgr pub; + jmp_buf setjmp_buffer; +}; + +typedef struct imread_jpeg_error_mgr *imread_jpeg_error_ptr; + +METHODDEF(void) +imread_jpeg_error_exit (j_common_ptr cinfo) +{ + imread_jpeg_error_ptr myerr = (imread_jpeg_error_ptr) cinfo->err; + (*cinfo->err->output_message) (cinfo); + longjmp(myerr->setjmp_buffer, 1); +} + int png2mem(char *path, int start, int len, int width, int height, int channels, int *bayer_pattern, int compr_mode, @@ -418,6 +489,7 @@ png2mem(char *path, int start, int len, int width, int height, { FILE *in; unsigned char hlen[511], hcode[511 * 4]; + int htblsize = 511 * (1 + 4); int retval; if ((in = fopen(path, "r")) == NULL) @@ -426,7 +498,7 @@ png2mem(char *path, int start, int len, int width, int height, if (compr_mode == 1 || compr_mode == 2) { fread(hcode, sizeof(hcode[0]), 511 * 4, in); fread(hlen, sizeof(hlen[0]), 511, in); - fread(compressed, sizeof(compressed[0]), len, in); + fread(compressed, sizeof(compressed[0]), len - htblsize, in); fclose(in); if ((retval = huffdecode(width, height, uncompressed, hcode, hlen, compressed)) != 0) @@ -440,6 +512,46 @@ png2mem(char *path, int start, int len, int width, int height, uncompressed)) != 0) return retval; return 0; + } else if (compr_mode == 3) { /* JPEG */ + struct jpeg_decompress_struct cinfo; + struct imread_jpeg_error_mgr jerr; + unsigned char *next_line; + int nsamples; + + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = imread_jpeg_error_exit; + if (setjmp(jerr.setjmp_buffer)) { + jpeg_destroy_decompress(&cinfo); + return 71; /* JPEG decompression error */ + } + jpeg_create_decompress(&cinfo); + fread(compressed, sizeof(compressed[0]), len, in); + fclose(in); + jpeg_mem_src(&cinfo, compressed, len); + jpeg_read_header(&cinfo, TRUE); + if (cinfo.image_width * cinfo.image_height * cinfo.num_components > + width * height * channels) { + return 72; /* JPEG bigger than expected */ + jpeg_destroy_decompress(&cinfo); + } + jpeg_start_decompress(&cinfo); + next_line = uncompressed; + while (cinfo.output_scanline < cinfo.output_height) { + nsamples = jpeg_read_scanlines(&cinfo, (JSAMPARRAY)&next_line, 1); + next_line += nsamples * cinfo.image_width * cinfo.num_components; + } + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + if (reversep) + return 73; /* JPEG reversing not implemented */ + if (brightenp) + return 74; /* JPEG brightening not implemented */ + if ((retval = uncompressedjpeg2png( + mem_png, cinfo.image_width, cinfo.image_height, + cinfo.num_components, color_raiser, + uncompressed)) != 0) + return retval; + return 0; } else if (compr_mode == 0) { /* untested */ fread(uncompressed, sizeof(uncompressed[0]), width * height, in); fclose(in); diff --git a/phoros.asd b/phoros.asd index 1e51ec5..b2550a8 100644 --- a/phoros.asd +++ b/phoros.asd @@ -32,7 +32,7 @@ it available over a web interface." ;; There should be a corresponding git tag which marks the point this ;; version number becomes official. - "14.0.2" + "14.1.0" :licence ;goes with --licence output "Copyright (C) 2010, 2011, 2012, 2015, 2016, 2017 Bert Burgemeister -- 2.11.4.GIT