From 3ab68cf563f6edc2608c085f5c8b2d5d5c61157e Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 19 Feb 2016 18:32:10 -0600 Subject: [PATCH] libjpeg API: Partial scanline decompression This, in combination with the existing jpeg_skip_scanlines() function, provides the ability to crop the image both horizontally and vertically while decompressing (certain restrictions apply-- see libjpeg.txt.) This also cleans up the documentation of the line skipping feature and removes the "strip decompression" feature from djpeg, since the new cropping feature is a superset of it. Refer to #34 for discussion. Closes #34 --- CMakeLists.txt | 61 ++++++++++---------- Makefile.am | 39 ++++++------- djpeg.1 | 16 +++--- djpeg.c | 119 ++++++++++++++++++++++++--------------- jdapimin.c | 13 ++++- jdapistd.c | 104 ++++++++++++++++++++++++++++++++++ jdcoefct.c | 77 ++++++++++++++----------- jdinput.c | 8 ++- jdmaster.c | 36 ++++-------- jdmaster.h | 28 +++++++++ jdsample.c | 20 ++++--- jpegint.h | 10 +++- jpeglib.h | 4 +- jversion.h | 1 + libjpeg.txt | 58 ++++++++++++++++--- win/jpeg62-memsrcdst.def | 1 + win/jpeg62.def | 1 + win/jpeg7.def | 1 + win/jpeg8.def | 1 + wrppm.c | 16 +----- wrppm.h | 26 +++++++++ 21 files changed, 445 insertions(+), 195 deletions(-) create mode 100644 jdmaster.h create mode 100644 wrppm.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 91c8ac662..7f1904374 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -367,9 +367,9 @@ if(WITH_12BIT) set(MD5_PPM_420M_ISLOW_1_4 35fd59d866e44659edfa3c18db2a3edb) set(MD5_PPM_420M_ISLOW_1_8 ccaed48ac0aedefda5d4abe4013f4ad7) set(MD5_PPM_420_ISLOW_SKIP15_31 86664cd9dc956536409e44e244d20a97) - set(MD5_PPM_420_ISLOW_PROG_STRIP71_132 a5c3706bb2e59bd01786b1181f67f97c) + set(MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71 452a21656115a163029cfba5c04fa76a) set(MD5_PPM_444_ISLOW_SKIP1_6 ef63901f71ef7a75cd78253fc0914f84) - set(MD5_PPM_444_ISLOW_PROG_STRIP13_110 6d228f8d05381d8639f3d078b91b2ee0) + set(MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13 15b173fb5872d9575572fbcc1b05956f) set(MD5_JPEG_CROP cdb35ff4b4519392690ea040c56ea99c) else() set(TESTORIG testorig.jpg) @@ -421,11 +421,11 @@ else() set(MD5_BMP_420M_ISLOW_565D d1be3a3339166255e76fa50a0d70d73e) set(MD5_PPM_420_ISLOW_SKIP15_31 c4c65c1e43d7275cd50328a61e6534f0) set(MD5_PPM_420_ISLOW_ARI_SKIP16_139 087c6b123db16ac00cb88c5b590bb74a) - set(MD5_PPM_420_ISLOW_PROG_STRIP71_132 a7f2ba6ea335f03549888bed66a89fae) - set(MD5_PPM_420_ISLOW_ARI_STRIP4_56 0e5e44a39b94817917a1bac72903246b) + set(MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71 26eb36ccc7d1f0cb80cdabb0ac8b5d99) + set(MD5_PPM_420_ISLOW_ARI_CROP53x53_4_4 886c6775af22370257122f8b16207e6d) set(MD5_PPM_444_ISLOW_SKIP1_6 5606f86874cf26b8fcee1117a0a436a6) - set(MD5_PPM_444_ISLOW_PROG_STRIP13_110 40b5d9742558dca6229d7332fc2dda07) - set(MD5_PPM_444_ISLOW_ARI_STRIP0_36 9aceb5b9449c900b892a1d2fe39351b4) + set(MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13 db87dc7ce26bcdc7a6b56239ce2b9d6c) + set(MD5_PPM_444_ISLOW_ARI_CROP37x37_0_0 cb57b32bd6d03e35432362f7bf184b6d) set(MD5_JPEG_CROP b4197f377e621c4e9b1d20471432610d) endif() @@ -764,22 +764,23 @@ foreach(libtype ${TEST_LIBTYPES}) add_test(cjpeg${suffix}-420-islow-prog ${dir}cjpeg${suffix} -dct int -prog -outfile testout_420_islow_prog.jpg ${TESTIMAGES}/testorig.ppm) - add_test(djpeg${suffix}-420-islow-prog-strip71_132 - ${dir}djpeg${suffix} -dct int -strip 71,132 -ppm - -outfile testout_420_islow_strip71,132.ppm testout_420_islow_prog.jpg) - add_test(djpeg${suffix}-420-islow-prog-strip71_132-cmp - ${MD5CMP} ${MD5_PPM_420_ISLOW_PROG_STRIP71_132} - testout_420_islow_strip71,132.ppm) + add_test(djpeg${suffix}-420-islow-prog-crop62x62_71_71 + ${dir}djpeg${suffix} -dct int -crop 62x62+71+71 -ppm + -outfile testout_420_islow_prog_crop62x62,71,71.ppm + testout_420_islow_prog.jpg) + add_test(djpeg${suffix}-420-islow-prog-crop62x62_71_71-cmp + ${MD5CMP} ${MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71} + testout_420_islow_prog_crop62x62,71,71.ppm) # Context rows: Yes Intra-iMCU row: No iMCU row prefetch: No ENT: arith if(WITH_ARITH_DEC) - add_test(djpeg${suffix}-420-islow-ari-strip4_56 - ${dir}djpeg${suffix} -dct int -strip 4,56 -ppm - -outfile testout_420_islow_ari_strip4,56.ppm + add_test(djpeg${suffix}-420-islow-ari-crop53x53_4_4 + ${dir}djpeg${suffix} -dct int -crop 53x53+4+4 -ppm + -outfile testout_420_islow_ari_crop53x53,4,4.ppm ${TESTIMAGES}/testimgari.jpg) - add_test(djpeg${suffix}-420-islow-ari-strip4_56-cmp - ${MD5CMP} ${MD5_PPM_420_ISLOW_ARI_STRIP4_56} - testout_420_islow_ari_strip4,56.ppm) + add_test(djpeg${suffix}-420-islow-ari-crop53x53_4_4-cmp + ${MD5CMP} ${MD5_PPM_420_ISLOW_ARI_CROP53x53_4_4} + testout_420_islow_ari_crop53x53,4,4.ppm) endif() # Context rows: No Intra-iMCU row: Yes ENT: huff @@ -796,13 +797,13 @@ foreach(libtype ${TEST_LIBTYPES}) add_test(cjpeg${suffix}-444-islow-prog ${dir}cjpeg${suffix} -dct int -prog -sample 1x1 -outfile testout_444_islow_prog.jpg ${TESTIMAGES}/testorig.ppm) - add_test(djpeg${suffix}-444-islow-prog-strip13_110 - ${dir}djpeg${suffix} -dct int -strip 13,110 -ppm - -outfile testout_444_islow_prog_strip13,110.ppm + add_test(djpeg${suffix}-444-islow-prog-crop98x98_13_13 + ${dir}djpeg${suffix} -dct int -crop 98x98+13+13 -ppm + -outfile testout_444_islow_prog_crop98x98,13,13.ppm testout_444_islow_prog.jpg) - add_test(djpeg${suffix}-444-islow-prog_strip13_110-cmp - ${MD5CMP} ${MD5_PPM_444_ISLOW_PROG_STRIP13_110} - testout_444_islow_prog_strip13,110.ppm) + add_test(djpeg${suffix}-444-islow-prog_crop98x98_13_13-cmp + ${MD5CMP} ${MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13} + testout_444_islow_prog_crop98x98,13,13.ppm) # Context rows: No Intra-iMCU row: No ENT: arith if(WITH_ARITH_ENC) @@ -810,13 +811,13 @@ foreach(libtype ${TEST_LIBTYPES}) ${dir}cjpeg${suffix} -dct int -arithmetic -sample 1x1 -outfile testout_444_islow_ari.jpg ${TESTIMAGES}/testorig.ppm) if(WITH_ARITH_DEC) - add_test(djpeg${suffix}-444-islow-ari-strip0_36 - ${dir}djpeg${suffix} -dct int -strip 0,36 -ppm - -outfile testout_444_islow_ari_strip0,36.ppm + add_test(djpeg${suffix}-444-islow-ari-crop37x37_0_0 + ${dir}djpeg${suffix} -dct int -crop 37x37+0+0 -ppm + -outfile testout_444_islow_ari_crop37x37,0,0.ppm testout_444_islow_ari.jpg) - add_test(djpeg${suffix}-444-islow-ari-strip0_36-cmp - ${MD5CMP} ${MD5_PPM_444_ISLOW_ARI_STRIP0_36} - testout_444_islow_ari_strip0,36.ppm) + add_test(djpeg${suffix}-444-islow-ari-crop37x37_0_0-cmp + ${MD5CMP} ${MD5_PPM_444_ISLOW_ARI_CROP37x37_0_0} + testout_444_islow_ari_crop37x37,0,0.ppm) endif() endif() diff --git a/Makefile.am b/Makefile.am index 68cf8d82a..b29edde74 100644 --- a/Makefile.am +++ b/Makefile.am @@ -170,7 +170,8 @@ dist_example_DATA = example.c EXTRA_DIST = win release $(DOCS) testimages CMakeLists.txt \ sharedlib/CMakeLists.txt cmakescripts libjpeg.map.in doc doxygen.config \ doxygen-extra.css jccolext.c jdcolext.c jdcol565.c jdmrgext.c jdmrg565.c \ - jstdhuff.c jdcoefct.h jdmainct.h jdsample.h md5/CMakeLists.txt + jstdhuff.c jdcoefct.h jdmainct.h jdmaster.h jdsample.h wrppm.h \ + md5/CMakeLists.txt dist-hook: rm -rf `find $(distdir) -name .svn` @@ -213,9 +214,9 @@ MD5_PPM_420M_ISLOW_3_8 = 343d19015531b7bbe746124127244fa8 MD5_PPM_420M_ISLOW_1_4 = 35fd59d866e44659edfa3c18db2a3edb MD5_PPM_420M_ISLOW_1_8 = ccaed48ac0aedefda5d4abe4013f4ad7 MD5_PPM_420_ISLOW_SKIP15_31 = 86664cd9dc956536409e44e244d20a97 -MD5_PPM_420_ISLOW_PROG_STRIP71_132 = a5c3706bb2e59bd01786b1181f67f97c +MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71 = 452a21656115a163029cfba5c04fa76a MD5_PPM_444_ISLOW_SKIP1_6 = ef63901f71ef7a75cd78253fc0914f84 -MD5_PPM_444_ISLOW_PROG_STRIP13_110 = 6d228f8d05381d8639f3d078b91b2ee0 +MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13 = 15b173fb5872d9575572fbcc1b05956f MD5_JPEG_CROP = cdb35ff4b4519392690ea040c56ea99c else @@ -270,11 +271,11 @@ MD5_BMP_420M_ISLOW_565 = 8dc0185245353cfa32ad97027342216f MD5_BMP_420M_ISLOW_565D =d1be3a3339166255e76fa50a0d70d73e MD5_PPM_420_ISLOW_SKIP15_31 = c4c65c1e43d7275cd50328a61e6534f0 MD5_PPM_420_ISLOW_ARI_SKIP16_139 = 087c6b123db16ac00cb88c5b590bb74a -MD5_PPM_420_ISLOW_PROG_STRIP71_132 = a7f2ba6ea335f03549888bed66a89fae -MD5_PPM_420_ISLOW_ARI_STRIP4_56 = 0e5e44a39b94817917a1bac72903246b +MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71 = 26eb36ccc7d1f0cb80cdabb0ac8b5d99 +MD5_PPM_420_ISLOW_ARI_CROP53x53_4_4 = 886c6775af22370257122f8b16207e6d MD5_PPM_444_ISLOW_SKIP1_6 = 5606f86874cf26b8fcee1117a0a436a6 -MD5_PPM_444_ISLOW_PROG_STRIP13_110 = 40b5d9742558dca6229d7332fc2dda07 -MD5_PPM_444_ISLOW_ARI_STRIP0_36 = 9aceb5b9449c900b892a1d2fe39351b4 +MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13 = db87dc7ce26bcdc7a6b56239ce2b9d6c +MD5_PPM_444_ISLOW_ARI_CROP37x37_0_0 = cb57b32bd6d03e35432362f7bf184b6d MD5_JPEG_CROP = b4197f377e621c4e9b1d20471432610d endif @@ -614,14 +615,14 @@ if WITH_ARITH_DEC endif # Context rows: Yes Intra-iMCU row: No iMCU row prefetch: No ENT: prog huff ./cjpeg -dct int -prog -outfile testout_420_islow_prog.jpg $(srcdir)/testimages/testorig.ppm - ./djpeg -dct int -strip 71,132 -ppm -outfile testout_420_islow_prog_strip71,132.ppm testout_420_islow_prog.jpg - md5/md5cmp $(MD5_PPM_420_ISLOW_PROG_STRIP71_132) testout_420_islow_prog_strip71,132.ppm - rm -f testout_420_islow_prog_strip71,132.ppm testout_420_islow_prog.jpg + ./djpeg -dct int -crop 62x62+71+71 -ppm -outfile testout_420_islow_prog_crop62x62,71,71.ppm testout_420_islow_prog.jpg + md5/md5cmp $(MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71) testout_420_islow_prog_crop62x62,71,71.ppm + rm -f testout_420_islow_prog_crop62x62,71,71.ppm testout_420_islow_prog.jpg # Context rows: Yes Intra-iMCU row: No iMCU row prefetch: No ENT: arith if WITH_ARITH_DEC - ./djpeg -dct int -strip 4,56 -ppm -outfile testout_420_islow_ari_strip4,56.ppm $(srcdir)/testimages/testimgari.jpg - md5/md5cmp $(MD5_PPM_420_ISLOW_ARI_STRIP4_56) testout_420_islow_ari_strip4,56.ppm - rm -f testout_420_islow_ari_strip4,56.ppm + ./djpeg -dct int -crop 53x53+4+4 -ppm -outfile testout_420_islow_ari_crop53x53,4,4.ppm $(srcdir)/testimages/testimgari.jpg + md5/md5cmp $(MD5_PPM_420_ISLOW_ARI_CROP53x53_4_4) testout_420_islow_ari_crop53x53,4,4.ppm + rm -f testout_420_islow_ari_crop53x53,4,4.ppm endif # Context rows: No Intra-iMCU row: Yes ENT: huff ./cjpeg -dct int -sample 1x1 -outfile testout_444_islow.jpg $(srcdir)/testimages/testorig.ppm @@ -630,16 +631,16 @@ endif rm -f testout_444_islow_skip1,6.ppm testout_444_islow.jpg # Context rows: No Intra-iMCU row: No ENT: prog huff ./cjpeg -dct int -prog -sample 1x1 -outfile testout_444_islow_prog.jpg $(srcdir)/testimages/testorig.ppm - ./djpeg -dct int -strip 13,110 -ppm -outfile testout_444_islow_prog_strip13,110.ppm testout_444_islow_prog.jpg - md5/md5cmp $(MD5_PPM_444_ISLOW_PROG_STRIP13_110) testout_444_islow_prog_strip13,110.ppm - rm -f testout_444_islow_prog_strip13,110.ppm testout_444_islow_prog.jpg + ./djpeg -dct int -crop 98x98+13+13 -ppm -outfile testout_444_islow_prog_crop98x98,13,13.ppm testout_444_islow_prog.jpg + md5/md5cmp $(MD5_PPM_444_ISLOW_PROG_CROP98x98_13_13) testout_444_islow_prog_crop98x98,13,13.ppm + rm -f testout_444_islow_prog_crop98x98,13,13.ppm testout_444_islow_prog.jpg # Context rows: No Intra-iMCU row: No ENT: arith if WITH_ARITH_ENC ./cjpeg -dct int -arithmetic -sample 1x1 -outfile testout_444_islow_ari.jpg $(srcdir)/testimages/testorig.ppm if WITH_ARITH_DEC - ./djpeg -dct int -strip 0,36 -ppm -outfile testout_444_islow_ari_strip0,36.ppm testout_444_islow_ari.jpg - md5/md5cmp $(MD5_PPM_444_ISLOW_ARI_STRIP0_36) testout_444_islow_ari_strip0,36.ppm - rm -f testout_444_islow_ari_strip0,36.ppm + ./djpeg -dct int -crop 37x37+0+0 -ppm -outfile testout_444_islow_ari_crop37x37,0,0.ppm testout_444_islow_ari.jpg + md5/md5cmp $(MD5_PPM_444_ISLOW_ARI_CROP37x37_0_0) testout_444_islow_ari_crop37x37,0,0.ppm + rm -f testout_444_islow_ari_crop37x37,0,0.ppm endif rm -f testout_444_islow_ari.jpg endif diff --git a/djpeg.1 b/djpeg.1 index 293939bfa..7efde43b4 100644 --- a/djpeg.1 +++ b/djpeg.1 @@ -1,4 +1,4 @@ -.TH DJPEG 1 "17 February 2016" +.TH DJPEG 1 "18 February 2016" .SH NAME djpeg \- decompress a JPEG file to an image file .SH SYNOPSIS @@ -195,13 +195,15 @@ Load input file into memory before decompressing. This feature was implemented mainly as a way of testing the in-memory source manager (jpeg_mem_src().) .TP .BI \-skip " Y0,Y1" -Decode all rows of the JPEG image except those between Y0 and Y1 (inclusive.) -Note that if decompression scaling is being used, Y0 and Y1 are relative to the -scaled image dimensions. +Decompress all rows of the JPEG image except those between Y0 and Y1 +(inclusive.) Note that if decompression scaling is being used, then Y0 and Y1 +are relative to the scaled image dimensions. .TP -.BI \-strip " Y0,Y1" -Decode only the rows of the JPEG image between Y0 and Y1 (inclusive.) Note -that if decompression scaling is being used, Y0 and Y1 are relative to the +.BI \-crop " WxH+X+Y" +Decompress only a rectangular subregion of the image, starting at point X,Y +with width W and height H. If necessary, X will be shifted left to the nearest +iMCU boundary, and the width will be increased accordingly. Note that if +decompression scaling is being used, then X, Y, W, and H are relative to the scaled image dimensions. .TP .B \-verbose diff --git a/djpeg.c b/djpeg.c index ce83104ec..54cd525ed 100644 --- a/djpeg.c +++ b/djpeg.c @@ -5,7 +5,7 @@ * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 2013 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2010-2011, 2013-2015, D. R. Commander. + * Copyright (C) 2010-2011, 2013-2016, D. R. Commander. * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -31,6 +31,7 @@ #include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ #include "jversion.h" /* for version message */ #include "jconfigint.h" +#include "wrppm.h" #include /* to declare isprint() */ @@ -91,8 +92,9 @@ static IMAGE_FORMATS requested_fmt; static const char *progname; /* program name for error messages */ static char *outfilename; /* for -outfile switch */ boolean memsrc; /* for -memsrc switch */ -boolean strip, skip; -JDIMENSION startY, endY; +boolean skip, crop; +JDIMENSION skip_start, skip_end; +JDIMENSION crop_x, crop_y, crop_width, crop_height; #define INPUT_BUF_SIZE 4096 @@ -169,8 +171,8 @@ usage (void) fprintf(stderr, " -memsrc Load input file into memory before decompressing\n"); #endif - fprintf(stderr, " -skip Y0,Y1 Decode all rows except those between Y0 and Y1 (inclusive)\n"); - fprintf(stderr, " -strip Y0,Y1 Decode only rows between Y0 and Y1 (inclusive)\n"); + fprintf(stderr, " -skip Y0,Y1 Decompress all rows except those between Y0 and Y1 (inclusive)\n"); + fprintf(stderr, " -crop WxH+X+Y Decompress only a rectangular subregion of the image\n"); fprintf(stderr, " -verbose or -debug Emit debug output\n"); fprintf(stderr, " -version Print version information and exit\n"); exit(EXIT_FAILURE); @@ -196,8 +198,8 @@ parse_switches (j_decompress_ptr cinfo, int argc, char **argv, requested_fmt = DEFAULT_FMT; /* set default output file format */ outfilename = NULL; memsrc = FALSE; - strip = FALSE; skip = FALSE; + crop = FALSE; cinfo->err->trace_level = 0; /* Scan command line options, adjust parameters */ @@ -378,20 +380,23 @@ parse_switches (j_decompress_ptr cinfo, int argc, char **argv, &cinfo->scale_num, &cinfo->scale_denom) != 2) usage(); - } else if (keymatch(arg, "strip", 2)) { + } else if (keymatch(arg, "skip", 2)) { if (++argn >= argc) usage(); - if (sscanf(argv[argn], "%d,%d", &startY, &endY) != 2 || startY > endY) + if (sscanf(argv[argn], "%u,%u", &skip_start, &skip_end) != 2 || + skip_start > skip_end) usage(); - strip = TRUE; - + skip = TRUE; - } else if (keymatch(arg, "skip", 2)) { + } else if (keymatch(arg, "crop", 2)) { + char c; if (++argn >= argc) usage(); - if (sscanf(argv[argn], "%d,%d", &startY, &endY) != 2 || startY > endY) + if (sscanf(argv[argn], "%u%c%u+%u+%u", &crop_width, &c, &crop_height, + &crop_x, &crop_y) != 5 || + (c != 'X' && c != 'x') || crop_width < 1 || crop_height < 1) usage(); - skip = TRUE; + crop = TRUE; } else if (keymatch(arg, "targa", 1)) { /* Targa output format. */ @@ -658,54 +663,78 @@ main (int argc, char **argv) /* Start decompressor */ (void) jpeg_start_decompress(&cinfo); - /* Strip decode */ - if (strip || skip) { + /* Skip rows */ + if (skip) { JDIMENSION tmp; - /* Check for valid endY. We cannot check this value until after + /* Check for valid skip_end. We cannot check this value until after * jpeg_start_decompress() is called. Note that we have already verified - * that startY <= endY. + * that skip_start <= skip_end. */ - if (endY > cinfo.output_height - 1) { - fprintf(stderr, "%s: strip %d-%d exceeds image height %d\n", progname, - startY, endY, cinfo.output_height); + if (skip_end > cinfo.output_height - 1) { + fprintf(stderr, "%s: skip region exceeds image height %d\n", progname, + cinfo.output_height); exit(EXIT_FAILURE); } /* Write output file header. This is a hack to ensure that the destination - * manager creates an image of the proper size for the partial decode. + * manager creates an output image of the proper size. */ tmp = cinfo.output_height; - cinfo.output_height = endY - startY + 1; - if (skip) - cinfo.output_height = tmp - cinfo.output_height; + cinfo.output_height -= (skip_end - skip_start + 1); (*dest_mgr->start_output) (&cinfo, dest_mgr); cinfo.output_height = tmp; /* Process data */ - if (skip) { - while (cinfo.output_scanline < startY) { - num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer, - dest_mgr->buffer_height); - (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); - } - jpeg_skip_scanlines(&cinfo, endY - startY + 1); - while (cinfo.output_scanline < cinfo.output_height) { - num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer, - dest_mgr->buffer_height); - (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); - } - } else { - jpeg_skip_scanlines(&cinfo, startY); - while (cinfo.output_scanline <= endY) { - num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer, - dest_mgr->buffer_height); - (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); - } - jpeg_skip_scanlines(&cinfo, cinfo.output_height - endY + 1); + while (cinfo.output_scanline < skip_start) { + num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer, + dest_mgr->buffer_height); + (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); + } + jpeg_skip_scanlines(&cinfo, skip_end - skip_start + 1); + while (cinfo.output_scanline < cinfo.output_height) { + num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer, + dest_mgr->buffer_height); + (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); + } + + /* Decompress a subregion */ + } else if (crop) { + JDIMENSION tmp; + + /* Check for valid crop dimensions. We cannot check these values until + * after jpeg_start_decompress() is called. + */ + if (crop_x + crop_width > cinfo.output_width || + crop_y + crop_height > cinfo.output_height) { + fprintf(stderr, "%s: crop dimensions exceed image dimensions %d x %d\n", + progname, cinfo.output_width, cinfo.output_height); + exit(EXIT_FAILURE); + } + + jpeg_crop_scanline(&cinfo, &crop_x, &crop_width); + ((ppm_dest_ptr) dest_mgr)->buffer_width = cinfo.output_width * + cinfo.out_color_components * + sizeof(JSAMPLE); + + /* Write output file header. This is a hack to ensure that the destination + * manager creates an output image of the proper size. + */ + tmp = cinfo.output_height; + cinfo.output_height = crop_height; + (*dest_mgr->start_output) (&cinfo, dest_mgr); + cinfo.output_height = tmp; + + /* Process data */ + jpeg_skip_scanlines(&cinfo, crop_y); + while (cinfo.output_scanline < crop_y + crop_height) { + num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer, + dest_mgr->buffer_height); + (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); } + jpeg_skip_scanlines(&cinfo, cinfo.output_height - crop_y - crop_height); - /* Normal full image decode */ + /* Normal full-image decompress */ } else { /* Write output file header */ (*dest_mgr->start_output) (&cinfo, dest_mgr); diff --git a/jdapimin.c b/jdapimin.c index 7cb3949d0..f80a14667 100644 --- a/jdapimin.c +++ b/jdapimin.c @@ -3,8 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1998, Thomas G. Lane. - * It was modified by The libjpeg-turbo Project to include only code relevant - * to libjpeg-turbo. + * libjpeg-turbo Modifications: + * Copyright (C) 2016, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -22,6 +22,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#include "jdmaster.h" /* @@ -83,6 +84,14 @@ jpeg_CreateDecompress (j_decompress_ptr cinfo, int version, size_t structsize) /* OK, I'm ready */ cinfo->global_state = DSTATE_START; + + /* The master struct is used to store extension parameters, so we allocate it + * here. + */ + cinfo->master = (struct jpeg_decomp_master *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof(my_decomp_master)); + MEMZERO(cinfo->master, sizeof(my_decomp_master)); } diff --git a/jdapistd.c b/jdapistd.c index 2d00b1999..06076cb76 100644 --- a/jdapistd.c +++ b/jdapistd.c @@ -140,6 +140,110 @@ output_pass_setup (j_decompress_ptr cinfo) } +/* + * Enable partial scanline decompression + * + * Must be called after jpeg_start_decompress() and before any calls to + * jpeg_read_scanlines() or jpeg_skip_scanlines(). + * + * Refer to libjpeg.txt for more information. + */ + +GLOBAL(void) +jpeg_crop_scanline (j_decompress_ptr cinfo, JDIMENSION *xoffset, + JDIMENSION *width) +{ + int ci, align, orig_downsampled_width; + JDIMENSION input_xoffset; + boolean reinit_upsampler = FALSE; + jpeg_component_info *compptr; + + if (cinfo->global_state != DSTATE_SCANNING || cinfo->output_scanline != 0) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (!xoffset || !width) + ERREXIT(cinfo, JERR_BAD_CROP_SPEC); + + /* xoffset and width must fall within the output image dimensions. */ + if (*width == 0 || *xoffset + *width > cinfo->output_width) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + /* No need to do anything if the caller wants the entire width. */ + if (*width == cinfo->output_width) + return; + + /* Ensuring the proper alignment of xoffset is tricky. At minimum, it + * must align with an MCU boundary, because: + * + * (1) The IDCT is performed in blocks, and it is not feasible to modify + * the algorithm so that it can transform partial blocks. + * (2) Because of the SIMD extensions, any input buffer passed to the + * upsampling and color conversion routines must be aligned to the + * SIMD word size (for instance, 128-bit in the case of SSE2.) The + * easiest way to accomplish this without copying data is to ensure + * that upsampling and color conversion begin at the start of the + * first MCU column that will be inverse transformed. + * + * In practice, we actually impose a stricter alignment requirement. We + * require that xoffset be a multiple of the maximum MCU column width of all + * of the components (the "iMCU column width.") This is to simplify the + * single-pass decompression case, allowing us to use the same MCU column + * width for all of the components. + */ + align = cinfo->min_DCT_scaled_size * cinfo->max_h_samp_factor; + + /* Adjust xoffset to the nearest iMCU boundary <= the requested value */ + input_xoffset = *xoffset; + *xoffset = (input_xoffset / align) * align; + + /* Adjust the width so that the right edge of the output image is as + * requested (only the left edge is altered.) It is important that calling + * programs check this value after this function returns, so that they can + * allocate an output buffer with the appropriate size. + */ + *width = *width + input_xoffset - *xoffset; + cinfo->output_width = *width; + + /* Set the first and last iMCU columns that we must decompress. These values + * will be used in single-scan decompressions. + */ + cinfo->master->first_iMCU_col = + (JDIMENSION) (long) (*xoffset) / (long) align; + cinfo->master->last_iMCU_col = + (JDIMENSION) jdiv_round_up((long) (*xoffset + cinfo->output_width), + (long) align) - 1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Set downsampled_width to the new output width. */ + orig_downsampled_width = compptr->downsampled_width; + compptr->downsampled_width = + (JDIMENSION) jdiv_round_up((long) (cinfo->output_width * + compptr->h_samp_factor), + (long) cinfo->max_h_samp_factor); + if (compptr->downsampled_width < 2 && orig_downsampled_width >= 2) + reinit_upsampler = TRUE; + + /* Set the first and last iMCU columns that we must decompress. These + * values will be used in multi-scan decompressions. + */ + cinfo->master->first_MCU_col[ci] = + (JDIMENSION) (long) (*xoffset * compptr->h_samp_factor) / + (long) align; + cinfo->master->last_MCU_col[ci] = + (JDIMENSION) jdiv_round_up((long) ((*xoffset + cinfo->output_width) * + compptr->h_samp_factor), + (long) align) - 1; + } + + if (reinit_upsampler) { + cinfo->master->jinit_upsampler_no_alloc = TRUE; + jinit_upsampler(cinfo); + cinfo->master->jinit_upsampler_no_alloc = FALSE; + } +} + + /* * Read some scanlines of data from the JPEG decompressor. * diff --git a/jdcoefct.c b/jdcoefct.c index b291da932..1a48969b8 100644 --- a/jdcoefct.c +++ b/jdcoefct.c @@ -6,6 +6,7 @@ * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2010, 2015-2016, D. R. Commander. + * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -108,38 +109,46 @@ decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) coef->MCU_ctr = MCU_col_num; return JPEG_SUSPENDED; } - /* Determine where data should go in output_buf and do the IDCT thing. - * We skip dummy blocks at the right and bottom edges (but blkn gets - * incremented past them!). Note the inner loop relies on having - * allocated the MCU_buffer[] blocks sequentially. + + /* Only perform the IDCT on blocks that are contained within the desired + * cropping region. */ - blkn = 0; /* index of current DCT block within MCU */ - for (ci = 0; ci < cinfo->comps_in_scan; ci++) { - compptr = cinfo->cur_comp_info[ci]; - /* Don't bother to IDCT an uninteresting component. */ - if (! compptr->component_needed) { - blkn += compptr->MCU_blocks; - continue; - } - inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index]; - useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width - : compptr->last_col_width; - output_ptr = output_buf[compptr->component_index] + - yoffset * compptr->_DCT_scaled_size; - start_col = MCU_col_num * compptr->MCU_sample_width; - for (yindex = 0; yindex < compptr->MCU_height; yindex++) { - if (cinfo->input_iMCU_row < last_iMCU_row || - yoffset+yindex < compptr->last_row_height) { - output_col = start_col; - for (xindex = 0; xindex < useful_width; xindex++) { - (*inverse_DCT) (cinfo, compptr, - (JCOEFPTR) coef->MCU_buffer[blkn+xindex], - output_ptr, output_col); - output_col += compptr->_DCT_scaled_size; + if (MCU_col_num >= cinfo->master->first_iMCU_col && + MCU_col_num <= cinfo->master->last_iMCU_col) { + /* Determine where data should go in output_buf and do the IDCT thing. + * We skip dummy blocks at the right and bottom edges (but blkn gets + * incremented past them!). Note the inner loop relies on having + * allocated the MCU_buffer[] blocks sequentially. + */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) { + blkn += compptr->MCU_blocks; + continue; + } + inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index]; + useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + : compptr->last_col_width; + output_ptr = output_buf[compptr->component_index] + + yoffset * compptr->_DCT_scaled_size; + start_col = (MCU_col_num - cinfo->master->first_iMCU_col) * + compptr->MCU_sample_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + if (cinfo->input_iMCU_row < last_iMCU_row || + yoffset+yindex < compptr->last_row_height) { + output_col = start_col; + for (xindex = 0; xindex < useful_width; xindex++) { + (*inverse_DCT) (cinfo, compptr, + (JCOEFPTR) coef->MCU_buffer[blkn+xindex], + output_ptr, output_col); + output_col += compptr->_DCT_scaled_size; + } } + blkn += compptr->MCU_width; + output_ptr += compptr->_DCT_scaled_size; } - blkn += compptr->MCU_width; - output_ptr += compptr->_DCT_scaled_size; } } } @@ -294,9 +303,10 @@ decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) output_ptr = output_buf[ci]; /* Loop over all DCT blocks to be processed. */ for (block_row = 0; block_row < block_rows; block_row++) { - buffer_ptr = buffer[block_row]; + buffer_ptr = buffer[block_row] + cinfo->master->first_MCU_col[ci]; output_col = 0; - for (block_num = 0; block_num < compptr->width_in_blocks; block_num++) { + for (block_num = cinfo->master->first_MCU_col[ci]; + block_num <= cinfo->master->last_MCU_col[ci]; block_num++) { (*inverse_DCT) (cinfo, compptr, (JCOEFPTR) buffer_ptr, output_ptr, output_col); buffer_ptr++; @@ -482,7 +492,7 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) output_ptr = output_buf[ci]; /* Loop over all DCT blocks to be processed. */ for (block_row = 0; block_row < block_rows; block_row++) { - buffer_ptr = buffer[block_row]; + buffer_ptr = buffer[block_row] + cinfo->master->first_MCU_col[ci]; if (first_row && block_row == 0) prev_block_row = buffer_ptr; else @@ -499,7 +509,8 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) DC7 = DC8 = DC9 = (int) next_block_row[0][0]; output_col = 0; last_block_column = compptr->width_in_blocks - 1; - for (block_num = 0; block_num <= last_block_column; block_num++) { + for (block_num = cinfo->master->first_MCU_col[ci]; + block_num <= cinfo->master->last_MCU_col[ci]; block_num++) { /* Fetch current DCT block into workspace so we can modify it. */ jcopy_block_row(buffer_ptr, (JBLOCKROW) workspace, (JDIMENSION) 1); /* Update DC values */ diff --git a/jdinput.c b/jdinput.c index 23c4bf2d1..32a6b424e 100644 --- a/jdinput.c +++ b/jdinput.c @@ -4,7 +4,8 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2010, D. R. Commander. + * Copyright (C) 2010, 2016, D. R. Commander. + * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -105,6 +106,11 @@ initial_setup (j_decompress_ptr cinfo) compptr->height_in_blocks = (JDIMENSION) jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, (long) (cinfo->max_v_samp_factor * DCTSIZE)); + /* Set the first and last MCU columns to decompress from multi-scan images. + * By default, decompress all of the MCU columns. + */ + cinfo->master->first_MCU_col[ci] = 0; + cinfo->master->last_MCU_col[ci] = compptr->width_in_blocks - 1; /* downsampled_width and downsampled_height will also be overridden by * jdmaster.c if we are doing full decompression. The transcoder library * doesn't use these values, but the calling application might. diff --git a/jdmaster.c b/jdmaster.c index 69b4f70c5..7908849bf 100644 --- a/jdmaster.c +++ b/jdmaster.c @@ -5,8 +5,9 @@ * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 2002-2009 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, D. R. Commander. + * Copyright (C) 2009-2011, 2016, D. R. Commander. * Copyright (C) 2013, Linaro Limited. + * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -20,25 +21,7 @@ #include "jinclude.h" #include "jpeglib.h" #include "jpegcomp.h" - - -/* Private state */ - -typedef struct { - struct jpeg_decomp_master pub; /* public fields */ - - int pass_number; /* # of passes completed */ - - boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */ - - /* Saved references to initialized quantizer modules, - * in case we need to switch modes. - */ - struct jpeg_color_quantizer *quantizer_1pass; - struct jpeg_color_quantizer *quantizer_2pass; -} my_decomp_master; - -typedef my_decomp_master *my_master_ptr; +#include "jdmaster.h" /* @@ -579,6 +562,12 @@ master_selection (j_decompress_ptr cinfo) /* Initialize input side of decompressor to consume first scan. */ (*cinfo->inputctl->start_input_pass) (cinfo); + /* Set the first and last iMCU columns to decompress from single-scan images. + * By default, decompress all of the iMCU columns. + */ + cinfo->master->first_iMCU_col = 0; + cinfo->master->last_iMCU_col = cinfo->MCUs_per_row - 1; + #ifdef D_MULTISCAN_FILES_SUPPORTED /* If jpeg_start_decompress will read the whole file, initialize * progress monitoring appropriately. The input step is counted @@ -723,16 +712,13 @@ jpeg_new_colormap (j_decompress_ptr cinfo) GLOBAL(void) jinit_master_decompress (j_decompress_ptr cinfo) { - my_master_ptr master; + my_master_ptr master = (my_master_ptr) cinfo->master; - master = (my_master_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - sizeof(my_decomp_master)); - cinfo->master = (struct jpeg_decomp_master *) master; master->pub.prepare_for_output_pass = prepare_for_output_pass; master->pub.finish_output_pass = finish_output_pass; master->pub.is_dummy_pass = FALSE; + master->pub.jinit_upsampler_no_alloc = FALSE; master_selection(cinfo); } diff --git a/jdmaster.h b/jdmaster.h new file mode 100644 index 000000000..76897e282 --- /dev/null +++ b/jdmaster.h @@ -0,0 +1,28 @@ +/* + * jdmaster.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1995, Thomas G. Lane. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains the master control structure for the JPEG decompressor. + */ + +/* Private state */ + +typedef struct { + struct jpeg_decomp_master pub; /* public fields */ + + int pass_number; /* # of passes completed */ + + boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */ + + /* Saved references to initialized quantizer modules, + * in case we need to switch modes. + */ + struct jpeg_color_quantizer *quantizer_1pass; + struct jpeg_color_quantizer *quantizer_2pass; +} my_decomp_master; + +typedef my_decomp_master *my_master_ptr; diff --git a/jdsample.c b/jdsample.c index 0eb56b7a5..39b372509 100644 --- a/jdsample.c +++ b/jdsample.c @@ -7,6 +7,7 @@ * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2010, 2015-2016, D. R. Commander. * Copyright (C) 2014, MIPS Technologies, Inc., California + * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -373,13 +374,16 @@ jinit_upsampler (j_decompress_ptr cinfo) boolean need_buffer, do_fancy; int h_in_group, v_in_group, h_out_group, v_out_group; - upsample = (my_upsample_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - sizeof(my_upsampler)); - cinfo->upsample = (struct jpeg_upsampler *) upsample; - upsample->pub.start_pass = start_pass_upsample; - upsample->pub.upsample = sep_upsample; - upsample->pub.need_context_rows = FALSE; /* until we find out differently */ + if (!cinfo->master->jinit_upsampler_no_alloc) { + upsample = (my_upsample_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + sizeof(my_upsampler)); + cinfo->upsample = (struct jpeg_upsampler *) upsample; + upsample->pub.start_pass = start_pass_upsample; + upsample->pub.upsample = sep_upsample; + upsample->pub.need_context_rows = FALSE; /* until we find out differently */ + } else + upsample = (my_upsample_ptr) cinfo->upsample; if (cinfo->CCIR601_sampling) /* this isn't supported */ ERREXIT(cinfo, JERR_CCIR601_NOTIMPL); @@ -455,7 +459,7 @@ jinit_upsampler (j_decompress_ptr cinfo) upsample->v_expand[ci] = (UINT8) (v_out_group / v_in_group); } else ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL); - if (need_buffer) { + if (need_buffer && !cinfo->master->jinit_upsampler_no_alloc) { upsample->color_buf[ci] = (*cinfo->mem->alloc_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, (JDIMENSION) jround_up((long) cinfo->output_width, diff --git a/jpegint.h b/jpegint.h index 1ad07ee4a..c3b4320fd 100644 --- a/jpegint.h +++ b/jpegint.h @@ -5,7 +5,8 @@ * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 1997-2009 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2015, D. R. Commander + * Copyright (C) 2015-2016, D. R. Commander + * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -150,6 +151,13 @@ struct jpeg_decomp_master { /* State variables made visible to other modules */ boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ + + /* Partial decompression variables */ + JDIMENSION first_iMCU_col; + JDIMENSION last_iMCU_col; + JDIMENSION first_MCU_col[MAX_COMPS_IN_SCAN]; + JDIMENSION last_MCU_col[MAX_COMPS_IN_SCAN]; + boolean jinit_upsampler_no_alloc; }; /* Input control module */ diff --git a/jpeglib.h b/jpeglib.h index 01b4c3dc6..6c63f5822 100644 --- a/jpeglib.h +++ b/jpeglib.h @@ -5,7 +5,7 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * Modified 2002-2009 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, 2013-2014, D. R. Commander. + * Copyright (C) 2009-2011, 2013-2014, 2016, D. R. Commander. * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -995,6 +995,8 @@ EXTERN(JDIMENSION) jpeg_read_scanlines (j_decompress_ptr cinfo, JDIMENSION max_lines); EXTERN(JDIMENSION) jpeg_skip_scanlines (j_decompress_ptr cinfo, JDIMENSION num_lines); +EXTERN(void) jpeg_crop_scanline (j_decompress_ptr cinfo, JDIMENSION *xoffset, + JDIMENSION *width); EXTERN(boolean) jpeg_finish_decompress (j_decompress_ptr cinfo); /* Replaces jpeg_read_scanlines when reading raw downsampled data. */ diff --git a/jversion.h b/jversion.h index b5ca98037..6ce663d82 100644 --- a/jversion.h +++ b/jversion.h @@ -38,6 +38,7 @@ #define JCOPYRIGHT "Copyright (C) 2009-2016 D. R. Commander\n" \ "Copyright (C) 2011-2016 Siarhei Siamashka\n" \ "Copyright (C) 2015-2016 Matthieu Darbois\n" \ + "Copyright (C) 2015 Google, Inc.\n" \ "Copyright (C) 2013-2014 MIPS Technologies, Inc.\n" \ "Copyright (C) 2013 Linaro Limited\n" \ "Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies)\n" \ diff --git a/libjpeg.txt b/libjpeg.txt index da577ef7e..71d37c616 100644 --- a/libjpeg.txt +++ b/libjpeg.txt @@ -3,7 +3,7 @@ USING THE IJG JPEG LIBRARY This file was part of the Independent JPEG Group's software: Copyright (C) 1994-2013, Thomas G. Lane, Guido Vollbeding. libjpeg-turbo Modifications: -Copyright (C) 2010, 2014, 2015, D. R. Commander. +Copyright (C) 2010, 2014-2016, D. R. Commander. Copyright (C) 2015, Google, Inc. For conditions of distribution and use, see the accompanying README.ijg file. @@ -730,16 +730,21 @@ jpeg_abort_decompress() or jpeg_abort() if you want to reuse the object. The previous discussion of aborting compression cycles applies here too. -Skipping rows when decompressing --------------------------------- +Partial image decompression +--------------------------- -jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines); +Partial image decompression is convenient for performance-critical applications +that wish to view only a portion of a large JPEG image without decompressing +the whole thing. It it also useful in memory-constrained environments (such as +on mobile devices.) This library provides the following functions to support +partial image decompression: + +1. Skipping rows when decompressing + + jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines); This function provides application programmers with the ability to skip over -multiple rows in the JPEG image, thus decoding only a subset of the image data. -This is convenient for performance-critical applications that wish to view only -a portion of a large JPEG image without decompressing the whole thing. It it -also useful in memory-constrained environments (such as on mobile devices.) +multiple rows in the JPEG image. Suspending data sources are not supported by this function. Calling jpeg_skip_scanlines() with a suspending data source will result in undefined @@ -772,6 +777,43 @@ These issues are especially tricky for cases in which upsampling requires context rows. In the worst case, jpeg_skip_scanlines() will perform similarly to jpeg_read_scanlines() (since it will actually call jpeg_read_scanlines().) +2. Decompressing partial scanlines + + jpeg_crop_scanline (j_decompress_ptr cinfo, JDIMENSION *xoffset, + JDIMENSION *width) + +This function provides application programmers with the ability to decompress +only a portion of each row in the JPEG image. It must be called after +jpeg_start_decompress() and before any calls to jpeg_read_scanlines() or +jpeg_skip_scanlines(). + +If xoffset and width do not form a valid subset of the image row, then this +function will generate an error. Note that if the output image is scaled, then +xoffset and width are relative to the scaled image dimensions. + +xoffset and width are passed by reference because xoffset must fall on an iMCU +boundary. If it doesn't, then it will be moved left to the nearest iMCU +boundary, and width will be increased accordingly. If the calling program does +not like the adjusted values of xoffset and width, then it can call +jpeg_crop_scanline() again with new values (for instance, if it wants to move +xoffset to the nearest iMCU boundary to the right instead of to the left.) + +After calling this function, cinfo->output_width will be set to the adjusted +width. This value should be used when allocating an output buffer to pass to +jpeg_read_scanlines(). + +The output image from a partial-width decompression will be identical to the +corresponding image region from a full decode, with one exception: The "fancy" +(smooth) h2v2 (4:2:0) and h2v1 (4:2:2) upsampling algorithms fill in the +missing chroma components by averaging the chroma components from neighboring +pixels, except on the right and left edges of the image (where there are no +neighboring pixels.) When performing a partial-width decompression, these +"fancy" upsampling algorithms may treat the left and right edges of the partial +image region as if they are the left and right edges of the image, meaning that +the upsampling algorithm may be simplified. The result is that the pixels on +the left or right edge of the partial image may not be exactly identical to the +corresponding pixels in the original image. + Mechanics of usage: include files, linking, etc ----------------------------------------------- diff --git a/win/jpeg62-memsrcdst.def b/win/jpeg62-memsrcdst.def index 949d26799..6499316a0 100755 --- a/win/jpeg62-memsrcdst.def +++ b/win/jpeg62-memsrcdst.def @@ -103,3 +103,4 @@ EXPORTS jpeg_mem_dest @ 102 ; jpeg_mem_src @ 103 ; jpeg_skip_scanlines @ 104 ; + jpeg_crop_scanline @ 105 ; diff --git a/win/jpeg62.def b/win/jpeg62.def index c64df50a3..9f30b1a2f 100755 --- a/win/jpeg62.def +++ b/win/jpeg62.def @@ -101,3 +101,4 @@ EXPORTS jround_up @ 100 ; jzero_far @ 101 ; jpeg_skip_scanlines @ 102 ; + jpeg_crop_scanline @ 103 ; diff --git a/win/jpeg7.def b/win/jpeg7.def index 8daa35f9d..92463c517 100644 --- a/win/jpeg7.def +++ b/win/jpeg7.def @@ -103,3 +103,4 @@ EXPORTS jround_up @ 102 ; jzero_far @ 103 ; jpeg_skip_scanlines @ 104 ; + jpeg_crop_scanline @ 105 ; diff --git a/win/jpeg8.def b/win/jpeg8.def index 01deb69f2..19246ac9c 100644 --- a/win/jpeg8.def +++ b/win/jpeg8.def @@ -106,3 +106,4 @@ EXPORTS jround_up @ 105 ; jzero_far @ 106 ; jpeg_skip_scanlines @ 107 ; + jpeg_crop_scanline @ 108 ; diff --git a/wrppm.c b/wrppm.c index 7d13e2db7..40fbf1fc1 100644 --- a/wrppm.c +++ b/wrppm.c @@ -20,6 +20,7 @@ */ #include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ +#include "wrppm.h" #ifdef PPM_SUPPORTED @@ -62,21 +63,6 @@ */ -/* Private version of data destination object */ - -typedef struct { - struct djpeg_dest_struct pub; /* public fields */ - - /* Usually these two pointers point to the same place: */ - char *iobuffer; /* fwrite's I/O buffer */ - JSAMPROW pixrow; /* decompressor output buffer */ - size_t buffer_width; /* width of I/O buffer */ - JDIMENSION samples_per_row; /* JSAMPLEs per output row */ -} ppm_dest_struct; - -typedef ppm_dest_struct *ppm_dest_ptr; - - /* * Write some pixel data. * In this module rows_supplied will always be 1. diff --git a/wrppm.h b/wrppm.h new file mode 100644 index 000000000..aa6c5626f --- /dev/null +++ b/wrppm.h @@ -0,0 +1,26 @@ +/* + * wrppm.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1994, Thomas G. Lane. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + */ + +#ifdef PPM_SUPPORTED + +/* Private version of data destination object */ + +typedef struct { + struct djpeg_dest_struct pub; /* public fields */ + + /* Usually these two pointers point to the same place: */ + char *iobuffer; /* fwrite's I/O buffer */ + JSAMPROW pixrow; /* decompressor output buffer */ + size_t buffer_width; /* width of I/O buffer */ + JDIMENSION samples_per_row; /* JSAMPLEs per output row */ +} ppm_dest_struct; + +typedef ppm_dest_struct *ppm_dest_ptr; + +#endif