diff --git a/CMakeLists.txt b/CMakeLists.txt index 99c11ef3..460c143c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -773,7 +773,7 @@ else() set(MD5_PPM_3x2_IFAST fd283664b3b49127984af0a7f118fccd) set(MD5_JPEG_420_ISLOW_ARI e986fb0a637a8d833d96e8a6d6d84ea1) set(MD5_JPEG_444_ISLOW_PROGARI 0a8f1c8f66e113c3cf635df0a475a617) - set(MD5_PPM_420M_IFAST_ARI 72b59a99bcf1de24c5b27d151bde2437) + set(MD5_PPM_420M_IFAST_ARI 57251da28a35b46eecb7177d82d10e0e) set(MD5_JPEG_420_ISLOW 9a68f56bc76e466aa7e52f415d0f4a5f) set(MD5_PPM_420M_ISLOW_2_1 9f9de8c0612f8d06869b960b05abf9c9) set(MD5_PPM_420M_ISLOW_15_8 b6875bc070720b899566cc06459b63b7) @@ -1164,7 +1164,7 @@ foreach(libtype ${TEST_LIBTYPES}) if(WITH_ARITH_DEC) # CC: RGB->YCC SAMP: h2v2 merged IDCT: ifast ENT: arith - add_bittest(djpeg 420m-ifast-ari "-fast;-ppm" + add_bittest(djpeg 420m-ifast-ari "-fast;-skip;1,20;-ppm" testout_420m_ifast_ari.ppm ${TESTIMAGES}/testimgari.jpg ${MD5_PPM_420M_IFAST_ARI}) @@ -1300,6 +1300,11 @@ endforeach() add_custom_target(testclean COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmakescripts/testclean.cmake) +configure_file(croptest.in croptest @ONLY) +add_custom_target(croptest + COMMAND echo croptest + COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/croptest) + if(WITH_TURBOJPEG) configure_file(tjbenchtest.in tjbenchtest @ONLY) configure_file(tjexampletest.in tjexampletest @ONLY) diff --git a/ChangeLog.md b/ChangeLog.md index 1dfe9e06..adb8a60b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -80,6 +80,23 @@ limit the number of allowable scans in the input file. - Both programs now accept a `-strict` argument, which can be used to treat all warnings as fatal. +10. Fixed "using JNI after critical get" errors that occurred on Android +platforms when using any of the YUV encoding/compression/decompression/decoding +methods in the TurboJPEG Java API. + +11. Fixed or worked around multiple issues with `jpeg_skip_scanlines()`: + + - Fixed segfaults or "Corrupt JPEG data: premature end of data segment" +errors in `jpeg_skip_scanlines()` that occurred when decompressing 4:2:2 or +4:2:0 JPEG images using merged (non-fancy) upsampling/color conversion (that +is, when setting `cinfo.do_fancy_upsampling` to `FALSE`.) 2.0.0[6] was a +similar fix, but it did not cover all cases. + - `jpeg_skip_scanlines()` now throws an error if two-pass color +quantization is enabled. Two-pass color quantization never worked properly +with `jpeg_skip_scanlines()`, and the issues could not readily be fixed. + - Fixed an issue whereby `jpeg_skip_scanlines()` always returned 0 when +skipping past the end of an image. + 2.0.5 ===== diff --git a/README.md b/README.md index 6df6c5ea..bbeb15a2 100644 --- a/README.md +++ b/README.md @@ -179,8 +179,8 @@ supported and which aren't. NOTE: As of this writing, extensive research has been conducted into the usefulness of DCT scaling as a means of data reduction and SmartScale as a -means of quality improvement. The reader is invited to peruse the research at - and draw his/her own conclusions, +means of quality improvement. Readers are invited to peruse the research at + and draw their own conclusions, but it is the general belief of our project that these features have not demonstrated sufficient usefulness to justify inclusion in libjpeg-turbo. diff --git a/croptest.in b/croptest.in new file mode 100755 index 00000000..7e3c2931 --- /dev/null +++ b/croptest.in @@ -0,0 +1,95 @@ +#!/bin/bash + +set -u +set -e +trap onexit INT +trap onexit TERM +trap onexit EXIT + +onexit() +{ + if [ -d $OUTDIR ]; then + rm -rf $OUTDIR + fi +} + +runme() +{ + echo \*\*\* $* + $* +} + +IMAGE=vgl_6548_0026a.bmp +WIDTH=128 +HEIGHT=95 +IMGDIR=@CMAKE_CURRENT_SOURCE_DIR@/testimages +OUTDIR=`mktemp -d /tmp/__croptest_output.XXXXXX` +EXEDIR=@CMAKE_CURRENT_BINARY_DIR@ + +if [ -d $OUTDIR ]; then + rm -rf $OUTDIR +fi +mkdir -p $OUTDIR + +exec >$EXEDIR/croptest.log + +echo "============================================================" +echo "$IMAGE ($WIDTH x $HEIGHT)" +echo "============================================================" +echo + +for PROGARG in "" -progressive; do + + cp $IMGDIR/$IMAGE $OUTDIR + basename=`basename $IMAGE .bmp` + echo "------------------------------------------------------------" + echo "Generating test images" + echo "------------------------------------------------------------" + echo + runme $EXEDIR/cjpeg $PROGARG -grayscale -outfile $OUTDIR/${basename}_GRAY.jpg $IMGDIR/${basename}.bmp + runme $EXEDIR/cjpeg $PROGARG -sample 2x2 -outfile $OUTDIR/${basename}_420.jpg $IMGDIR/${basename}.bmp + runme $EXEDIR/cjpeg $PROGARG -sample 2x1 -outfile $OUTDIR/${basename}_422.jpg $IMGDIR/${basename}.bmp + runme $EXEDIR/cjpeg $PROGARG -sample 1x2 -outfile $OUTDIR/${basename}_440.jpg $IMGDIR/${basename}.bmp + runme $EXEDIR/cjpeg $PROGARG -sample 1x1 -outfile $OUTDIR/${basename}_444.jpg $IMGDIR/${basename}.bmp + echo + + for NSARG in "" -nosmooth; do + + for COLORSARG in "" "-colors 256 -dither none -onepass"; do + + for Y in {0..16}; do + + for H in {1..16}; do + + X=$(( (Y*16)%128 )) + W=$(( WIDTH-X-7 )) + if [ $Y -le 15 ]; then + CROPSPEC="${W}x${H}+${X}+${Y}" + else + Y2=$(( HEIGHT-H )); + CROPSPEC="${W}x${H}+${X}+${Y2}" + fi + + echo "------------------------------------------------------------" + echo $PROGARG $NSARG $COLORSARG -crop $CROPSPEC + echo "------------------------------------------------------------" + echo + for samp in GRAY 420 422 440 444; do + $EXEDIR/djpeg $NSARG $COLORSARG -rgb -outfile $OUTDIR/${basename}_${samp}_full.ppm $OUTDIR/${basename}_${samp}.jpg + convert -crop $CROPSPEC $OUTDIR/${basename}_${samp}_full.ppm $OUTDIR/${basename}_${samp}_ref.ppm + runme $EXEDIR/djpeg $NSARG $COLORSARG -crop $CROPSPEC -rgb -outfile $OUTDIR/${basename}_${samp}.ppm $OUTDIR/${basename}_${samp}.jpg + runme cmp $OUTDIR/${basename}_${samp}.ppm $OUTDIR/${basename}_${samp}_ref.ppm + done + echo + + done + + done + + done + + done + +done + +echo SUCCESS! diff --git a/djpeg.c b/djpeg.c index 427cc3e2..fb015730 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-2017, 2019, D. R. Commander. + * Copyright (C) 2010-2011, 2013-2017, 2019-2020, D. R. Commander. * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -730,7 +730,12 @@ main(int argc, char **argv) dest_mgr->buffer_height); (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); } - jpeg_skip_scanlines(&cinfo, skip_end - skip_start + 1); + if ((tmp = jpeg_skip_scanlines(&cinfo, skip_end - skip_start + 1)) != + skip_end - skip_start + 1) { + fprintf(stderr, "%s: jpeg_skip_scanlines() returned %d rather than %d\n", + progname, tmp, skip_end - skip_start + 1); + exit(EXIT_FAILURE); + } while (cinfo.output_scanline < cinfo.output_height) { num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer, dest_mgr->buffer_height); @@ -766,13 +771,24 @@ main(int argc, char **argv) cinfo.output_height = tmp; /* Process data */ - jpeg_skip_scanlines(&cinfo, crop_y); + if ((tmp = jpeg_skip_scanlines(&cinfo, crop_y)) != crop_y) { + fprintf(stderr, "%s: jpeg_skip_scanlines() returned %d rather than %d\n", + progname, tmp, crop_y); + exit(EXIT_FAILURE); + } 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); + if ((tmp = + jpeg_skip_scanlines(&cinfo, + cinfo.output_height - crop_y - crop_height)) != + cinfo.output_height - crop_y - crop_height) { + fprintf(stderr, "%s: jpeg_skip_scanlines() returned %d rather than %d\n", + progname, tmp, cinfo.output_height - crop_y - crop_height); + exit(EXIT_FAILURE); + } /* Normal full-image decompress */ } else { diff --git a/jdapistd.c b/jdapistd.c index 86f2cd50..bd68bdca 100644 --- a/jdapistd.c +++ b/jdapistd.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2010, 2015-2019, D. R. Commander. + * Copyright (C) 2010, 2015-2020, D. R. Commander. * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -21,6 +21,8 @@ #include "jinclude.h" #include "jdmainct.h" #include "jdcoefct.h" +#include "jdmaster.h" +#include "jdmerge.h" #include "jdsample.h" #include "jmemsys.h" @@ -316,6 +318,8 @@ LOCAL(void) read_and_discard_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) { JDIMENSION n; + my_master_ptr master = (my_master_ptr)cinfo->master; + JSAMPARRAY scanlines = NULL; void (*color_convert) (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) = NULL; @@ -332,8 +336,13 @@ read_and_discard_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) cinfo->cquantize->color_quantize = noop_quantize; } + if (master->using_merged_upsample && cinfo->max_v_samp_factor == 2) { + my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; + scanlines = &upsample->spare_row; + } + for (n = 0; n < num_lines; n++) - jpeg_read_scanlines(cinfo, NULL, 1); + jpeg_read_scanlines(cinfo, scanlines, 1); if (color_convert) cinfo->cconvert->color_convert = color_convert; @@ -353,6 +362,12 @@ increment_simple_rowgroup_ctr(j_decompress_ptr cinfo, JDIMENSION rows) { JDIMENSION rows_left; my_main_ptr main_ptr = (my_main_ptr)cinfo->main; + my_master_ptr master = (my_master_ptr)cinfo->master; + + if (master->using_merged_upsample && cinfo->max_v_samp_factor == 2) { + read_and_discard_scanlines(cinfo, rows); + return; + } /* Increment the counter to the next row group after the skipped rows. */ main_ptr->rowgroup_ctr += rows / cinfo->max_v_samp_factor; @@ -382,21 +397,27 @@ jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) { my_main_ptr main_ptr = (my_main_ptr)cinfo->main; my_coef_ptr coef = (my_coef_ptr)cinfo->coef; + my_master_ptr master = (my_master_ptr)cinfo->master; my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample; JDIMENSION i, x; int y; JDIMENSION lines_per_iMCU_row, lines_left_in_iMCU_row, lines_after_iMCU_row; JDIMENSION lines_to_skip, lines_to_read; + /* Two-pass color quantization is not supported. */ + if (cinfo->quantize_colors && cinfo->two_pass_quantize) + ERREXIT(cinfo, JERR_NOTIMPL); + if (cinfo->global_state != DSTATE_SCANNING) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); /* Do not skip past the bottom of the image. */ if (cinfo->output_scanline + num_lines >= cinfo->output_height) { + num_lines = cinfo->output_height - cinfo->output_scanline; cinfo->output_scanline = cinfo->output_height; (*cinfo->inputctl->finish_input_pass) (cinfo); cinfo->inputctl->eoi_reached = TRUE; - return cinfo->output_height - cinfo->output_scanline; + return num_lines; } if (num_lines == 0) @@ -445,8 +466,10 @@ jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) main_ptr->buffer_full = FALSE; main_ptr->rowgroup_ctr = 0; main_ptr->context_state = CTX_PREPARE_FOR_IMCU; - upsample->next_row_out = cinfo->max_v_samp_factor; - upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline; + if (!master->using_merged_upsample) { + upsample->next_row_out = cinfo->max_v_samp_factor; + upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline; + } } /* Skipping is much simpler when context rows are not required. */ @@ -458,8 +481,10 @@ jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) cinfo->output_scanline += lines_left_in_iMCU_row; main_ptr->buffer_full = FALSE; main_ptr->rowgroup_ctr = 0; - upsample->next_row_out = cinfo->max_v_samp_factor; - upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline; + if (!master->using_merged_upsample) { + upsample->next_row_out = cinfo->max_v_samp_factor; + upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline; + } } } @@ -494,7 +519,8 @@ jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) cinfo->output_iMCU_row += lines_to_skip / lines_per_iMCU_row; increment_simple_rowgroup_ctr(cinfo, lines_to_read); } - upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline; + if (!master->using_merged_upsample) + upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline; return num_lines; } @@ -537,7 +563,8 @@ jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) * bit odd, since "rows_to_go" seems to be redundantly keeping track of * output_scanline. */ - upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline; + if (!master->using_merged_upsample) + upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline; /* Always skip the requested number of lines. */ return num_lines; diff --git a/jdmerge.c b/jdmerge.c index dff5a350..833ad675 100644 --- a/jdmerge.c +++ b/jdmerge.c @@ -5,7 +5,7 @@ * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009, 2011, 2014-2015, D. R. Commander. + * Copyright (C) 2009, 2011, 2014-2015, 2020, D. R. Commander. * Copyright (C) 2013, Linaro Limited. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -40,41 +40,13 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#include "jdmerge.h" #include "jsimd.h" #include "jconfigint.h" #ifdef UPSAMPLE_MERGING_SUPPORTED -/* Private subobject */ - -typedef struct { - struct jpeg_upsampler pub; /* public fields */ - - /* Pointer to routine to do actual upsampling/conversion of one row group */ - void (*upmethod) (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf); - - /* Private state for YCC->RGB conversion */ - int *Cr_r_tab; /* => table for Cr to R conversion */ - int *Cb_b_tab; /* => table for Cb to B conversion */ - JLONG *Cr_g_tab; /* => table for Cr to G conversion */ - JLONG *Cb_g_tab; /* => table for Cb to G conversion */ - - /* For 2:1 vertical sampling, we produce two output rows at a time. - * We need a "spare" row buffer to hold the second output row if the - * application provides just a one-row buffer; we also use the spare - * to discard the dummy last row if the image height is odd. - */ - JSAMPROW spare_row; - boolean spare_full; /* T if spare buffer is occupied */ - - JDIMENSION out_row_width; /* samples per output row */ - JDIMENSION rows_to_go; /* counts rows remaining in image */ -} my_upsampler; - -typedef my_upsampler *my_upsample_ptr; - #define SCALEBITS 16 /* speediest right-shift on some machines */ #define ONE_HALF ((JLONG)1 << (SCALEBITS - 1)) #define FIX(x) ((JLONG)((x) * (1L << SCALEBITS) + 0.5)) @@ -189,7 +161,7 @@ typedef my_upsampler *my_upsample_ptr; LOCAL(void) build_ycc_rgb_table(j_decompress_ptr cinfo) { - my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample; + my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; int i; JLONG x; SHIFT_TEMPS @@ -232,7 +204,7 @@ build_ycc_rgb_table(j_decompress_ptr cinfo) METHODDEF(void) start_pass_merged_upsample(j_decompress_ptr cinfo) { - my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample; + my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; /* Mark the spare buffer empty */ upsample->spare_full = FALSE; @@ -254,7 +226,7 @@ merged_2v_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail) /* 2:1 vertical sampling case: may need a spare row. */ { - my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample; + my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; JSAMPROW work_ptrs[2]; JDIMENSION num_rows; /* number of rows returned to caller */ @@ -305,7 +277,7 @@ merged_1v_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail) /* 1:1 vertical sampling case: much easier, never need a spare row. */ { - my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample; + my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; /* Just do the upsampling. */ (*upsample->upmethod) (cinfo, input_buf, *in_row_group_ctr, @@ -566,11 +538,11 @@ h2v2_merged_upsample_565D(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, GLOBAL(void) jinit_merged_upsampler(j_decompress_ptr cinfo) { - my_upsample_ptr upsample; + my_merged_upsample_ptr upsample; - upsample = (my_upsample_ptr) + upsample = (my_merged_upsample_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, - sizeof(my_upsampler)); + sizeof(my_merged_upsampler)); cinfo->upsample = (struct jpeg_upsampler *)upsample; upsample->pub.start_pass = start_pass_merged_upsample; upsample->pub.need_context_rows = FALSE; diff --git a/jdmerge.h b/jdmerge.h new file mode 100644 index 00000000..b583396b --- /dev/null +++ b/jdmerge.h @@ -0,0 +1,47 @@ +/* + * jdmerge.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1994-1996, Thomas G. Lane. + * libjpeg-turbo Modifications: + * Copyright (C) 2020, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + */ + +#define JPEG_INTERNALS +#include "jpeglib.h" + +#ifdef UPSAMPLE_MERGING_SUPPORTED + + +/* Private subobject */ + +typedef struct { + struct jpeg_upsampler pub; /* public fields */ + + /* Pointer to routine to do actual upsampling/conversion of one row group */ + void (*upmethod) (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, + JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf); + + /* Private state for YCC->RGB conversion */ + int *Cr_r_tab; /* => table for Cr to R conversion */ + int *Cb_b_tab; /* => table for Cb to B conversion */ + JLONG *Cr_g_tab; /* => table for Cr to G conversion */ + JLONG *Cb_g_tab; /* => table for Cb to G conversion */ + + /* For 2:1 vertical sampling, we produce two output rows at a time. + * We need a "spare" row buffer to hold the second output row if the + * application provides just a one-row buffer; we also use the spare + * to discard the dummy last row if the image height is odd. + */ + JSAMPROW spare_row; + boolean spare_full; /* T if spare buffer is occupied */ + + JDIMENSION out_row_width; /* samples per output row */ + JDIMENSION rows_to_go; /* counts rows remaining in image */ +} my_merged_upsampler; + +typedef my_merged_upsampler *my_merged_upsample_ptr; + +#endif /* UPSAMPLE_MERGING_SUPPORTED */ diff --git a/jdmrg565.c b/jdmrg565.c index d44b4abb..980a4e21 100644 --- a/jdmrg565.c +++ b/jdmrg565.c @@ -5,7 +5,7 @@ * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: * Copyright (C) 2013, Linaro Limited. - * Copyright (C) 2014-2015, 2018, D. R. Commander. + * Copyright (C) 2014-2015, 2018, 2020, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -19,7 +19,7 @@ h2v1_merged_upsample_565_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf) { - my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample; + my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; register int y, cred, cgreen, cblue; int cb, cr; register JSAMPROW outptr; @@ -90,7 +90,7 @@ h2v1_merged_upsample_565D_internal(j_decompress_ptr cinfo, JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf) { - my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample; + my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; register int y, cred, cgreen, cblue; int cb, cr; register JSAMPROW outptr; @@ -163,7 +163,7 @@ h2v2_merged_upsample_565_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf) { - my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample; + my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; register int y, cred, cgreen, cblue; int cb, cr; register JSAMPROW outptr0, outptr1; @@ -259,7 +259,7 @@ h2v2_merged_upsample_565D_internal(j_decompress_ptr cinfo, JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf) { - my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample; + my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; register int y, cred, cgreen, cblue; int cb, cr; register JSAMPROW outptr0, outptr1; diff --git a/jdmrgext.c b/jdmrgext.c index 7de36ebb..9bf4f1a3 100644 --- a/jdmrgext.c +++ b/jdmrgext.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2011, 2015, D. R. Commander. + * Copyright (C) 2011, 2015, 2020, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -25,7 +25,7 @@ h2v1_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf) { - my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample; + my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; register int y, cred, cgreen, cblue; int cb, cr; register JSAMPROW outptr; @@ -97,7 +97,7 @@ h2v2_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf) { - my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample; + my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample; register int y, cred, cgreen, cblue; int cb, cr; register JSAMPROW outptr0, outptr1; diff --git a/jquant2.c b/jquant2.c index b0fa8e9e..44efb18c 100644 --- a/jquant2.c +++ b/jquant2.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2009, 2014-2015, D. R. Commander. + * Copyright (C) 2009, 2014-2015, 2020, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -1145,7 +1145,7 @@ start_pass_2_quant(j_decompress_ptr cinfo, boolean is_pre_scan) int i; /* Only F-S dithering or no dithering is supported. */ - /* If user asks for ordered dither, give him F-S. */ + /* If user asks for ordered dither, give them F-S. */ if (cinfo->dither_mode != JDITHER_NONE) cinfo->dither_mode = JDITHER_FS; @@ -1263,7 +1263,7 @@ jinit_2pass_quantizer(j_decompress_ptr cinfo) cquantize->sv_colormap = NULL; /* Only F-S dithering or no dithering is supported. */ - /* If user asks for ordered dither, give him F-S. */ + /* If user asks for ordered dither, give them F-S. */ if (cinfo->dither_mode != JDITHER_NONE) cinfo->dither_mode = JDITHER_FS; diff --git a/libjpeg.txt b/libjpeg.txt index c50cf906..c233ecbd 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-2018, D. R. Commander. +Copyright (C) 2010, 2014-2018, 2020, D. R. Commander. Copyright (C) 2015, Google, Inc. For conditions of distribution and use, see the accompanying README.ijg file. @@ -750,7 +750,9 @@ 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 -behavior. +behavior. Two-pass color quantization is also not supported by this function. +Calling jpeg_skip_scanlines() with two-pass color quantization enabled will +result in an error. jpeg_skip_scanlines() will not allow skipping past the bottom of the image. If the value of num_lines is large enough to skip past the bottom of the image, diff --git a/turbojpeg-jni.c b/turbojpeg-jni.c index 93634501..1b728e3a 100644 --- a/turbojpeg-jni.c +++ b/turbojpeg-jni.c @@ -1,5 +1,5 @@ /* - * Copyright (C)2011-2019 D. R. Commander. All Rights Reserved. + * Copyright (C)2011-2020 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -326,9 +326,11 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFrom tjhandle handle = 0; unsigned long jpegSize = 0; jbyteArray jSrcPlanes[3] = { NULL, NULL, NULL }; - const unsigned char *srcPlanes[3]; + const unsigned char *srcPlanesTmp[3] = { NULL, NULL, NULL }; + const unsigned char *srcPlanes[3] = { NULL, NULL, NULL }; + int *srcOffsetsTmp = NULL, srcOffsets[3] = { 0, 0, 0 }; + int *srcStridesTmp = NULL, srcStrides[3] = { 0, 0, 0 }; unsigned char *jpegBuf = NULL; - int *srcOffsets = NULL, *srcStrides = NULL; int nc = (subsamp == org_libjpegturbo_turbojpeg_TJ_SAMP_GRAY ? 1 : 3), i; GET_HANDLE(); @@ -351,56 +353,49 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFrom if (ProcessSystemProperties(env) < 0) goto bailout; -#define RELEASE_ARRAYS_COMPRESSFROMYUV() { \ - SAFE_RELEASE(dst, jpegBuf); \ - for (i = 0; i < nc; i++) \ - SAFE_RELEASE(jSrcPlanes[i], srcPlanes[i]); \ - SAFE_RELEASE(jSrcStrides, srcStrides); \ - SAFE_RELEASE(jSrcOffsets, srcOffsets); \ -} + BAILIF0(srcOffsetsTmp = + (*env)->GetPrimitiveArrayCritical(env, jSrcOffsets, 0)); + for (i = 0; i < nc; i++) srcOffsets[i] = srcOffsetsTmp[i]; + SAFE_RELEASE(jSrcOffsets, srcOffsetsTmp); + + BAILIF0(srcStridesTmp = + (*env)->GetPrimitiveArrayCritical(env, jSrcStrides, 0)); + for (i = 0; i < nc; i++) srcStrides[i] = srcStridesTmp[i]; + SAFE_RELEASE(jSrcStrides, srcStridesTmp); - BAILIF0(srcOffsets = (*env)->GetPrimitiveArrayCritical(env, jSrcOffsets, 0)); - BAILIF0(srcStrides = (*env)->GetPrimitiveArrayCritical(env, jSrcStrides, 0)); for (i = 0; i < nc; i++) { int planeSize = tjPlaneSizeYUV(i, width, srcStrides[i], height, subsamp); int pw = tjPlaneWidth(i, width, subsamp); - if (planeSize < 0 || pw < 0) { - RELEASE_ARRAYS_COMPRESSFROMYUV(); + if (planeSize < 0 || pw < 0) THROW_ARG(tjGetErrorStr()); - } - if (srcOffsets[i] < 0) { - RELEASE_ARRAYS_COMPRESSFROMYUV(); + if (srcOffsets[i] < 0) THROW_ARG("Invalid argument in compressFromYUV()"); - } - if (srcStrides[i] < 0 && srcOffsets[i] - planeSize + pw < 0) { - RELEASE_ARRAYS_COMPRESSFROMYUV(); + if (srcStrides[i] < 0 && srcOffsets[i] - planeSize + pw < 0) THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary"); - } BAILIF0(jSrcPlanes[i] = (*env)->GetObjectArrayElement(env, srcobjs, i)); if ((*env)->GetArrayLength(env, jSrcPlanes[i]) < - srcOffsets[i] + planeSize) { - RELEASE_ARRAYS_COMPRESSFROMYUV(); + srcOffsets[i] + planeSize) THROW_ARG("Source plane is not large enough"); - } - BAILIF0(srcPlanes[i] = + BAILIF0(srcPlanesTmp[i] = (*env)->GetPrimitiveArrayCritical(env, jSrcPlanes[i], 0)); - srcPlanes[i] = &srcPlanes[i][srcOffsets[i]]; + srcPlanes[i] = &srcPlanesTmp[i][srcOffsets[i]]; + SAFE_RELEASE(jSrcPlanes[i], srcPlanesTmp[i]); } BAILIF0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0)); if (tjCompressFromYUVPlanes(handle, srcPlanes, width, srcStrides, height, subsamp, &jpegBuf, &jpegSize, jpegQual, flags | TJFLAG_NOREALLOC) == -1) { - RELEASE_ARRAYS_COMPRESSFROMYUV(); + SAFE_RELEASE(dst, jpegBuf); THROW_TJ(); } bailout: - RELEASE_ARRAYS_COMPRESSFROMYUV(); + SAFE_RELEASE(dst, jpegBuf); return (jint)jpegSize; } @@ -411,9 +406,12 @@ static void TJCompressor_encodeYUV { tjhandle handle = 0; jsize arraySize = 0, actualPitch; + unsigned char *srcBuf = NULL; jbyteArray jDstPlanes[3] = { NULL, NULL, NULL }; - unsigned char *srcBuf = NULL, *dstPlanes[3]; - int *dstOffsets = NULL, *dstStrides = NULL; + unsigned char *dstPlanesTmp[3] = { NULL, NULL, NULL }; + unsigned char *dstPlanes[3] = { NULL, NULL, NULL }; + int *dstOffsetsTmp = NULL, dstOffsets[3] = { 0, 0, 0 }; + int *dstStridesTmp = NULL, dstStrides[3] = { 0, 0, 0 }; int nc = (subsamp == org_libjpegturbo_turbojpeg_TJ_SAMP_GRAY ? 1 : 3), i; GET_HANDLE(); @@ -438,56 +436,49 @@ static void TJCompressor_encodeYUV if ((*env)->GetArrayLength(env, src) * srcElementSize < arraySize) THROW_ARG("Source buffer is not large enough"); -#define RELEASE_ARRAYS_ENCODEYUV() { \ - SAFE_RELEASE(src, srcBuf); \ - for (i = 0; i < nc; i++) \ - SAFE_RELEASE(jDstPlanes[i], dstPlanes[i]); \ - SAFE_RELEASE(jDstStrides, dstStrides); \ - SAFE_RELEASE(jDstOffsets, dstOffsets); \ -} + BAILIF0(dstOffsetsTmp = + (*env)->GetPrimitiveArrayCritical(env, jDstOffsets, 0)); + for (i = 0; i < nc; i++) dstOffsets[i] = dstOffsetsTmp[i]; + SAFE_RELEASE(jDstOffsets, dstOffsetsTmp); + + BAILIF0(dstStridesTmp = + (*env)->GetPrimitiveArrayCritical(env, jDstStrides, 0)); + for (i = 0; i < nc; i++) dstStrides[i] = dstStridesTmp[i]; + SAFE_RELEASE(jDstStrides, dstStridesTmp); - BAILIF0(dstOffsets = (*env)->GetPrimitiveArrayCritical(env, jDstOffsets, 0)); - BAILIF0(dstStrides = (*env)->GetPrimitiveArrayCritical(env, jDstStrides, 0)); for (i = 0; i < nc; i++) { int planeSize = tjPlaneSizeYUV(i, width, dstStrides[i], height, subsamp); int pw = tjPlaneWidth(i, width, subsamp); - if (planeSize < 0 || pw < 0) { - RELEASE_ARRAYS_ENCODEYUV(); + if (planeSize < 0 || pw < 0) THROW_ARG(tjGetErrorStr()); - } - if (dstOffsets[i] < 0) { - RELEASE_ARRAYS_ENCODEYUV(); + if (dstOffsets[i] < 0) THROW_ARG("Invalid argument in encodeYUV()"); - } - if (dstStrides[i] < 0 && dstOffsets[i] - planeSize + pw < 0) { - RELEASE_ARRAYS_ENCODEYUV(); + if (dstStrides[i] < 0 && dstOffsets[i] - planeSize + pw < 0) THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary"); - } BAILIF0(jDstPlanes[i] = (*env)->GetObjectArrayElement(env, dstobjs, i)); if ((*env)->GetArrayLength(env, jDstPlanes[i]) < - dstOffsets[i] + planeSize) { - RELEASE_ARRAYS_ENCODEYUV(); + dstOffsets[i] + planeSize) THROW_ARG("Destination plane is not large enough"); - } - BAILIF0(dstPlanes[i] = + BAILIF0(dstPlanesTmp[i] = (*env)->GetPrimitiveArrayCritical(env, jDstPlanes[i], 0)); - dstPlanes[i] = &dstPlanes[i][dstOffsets[i]]; + dstPlanes[i] = &dstPlanesTmp[i][dstOffsets[i]]; + SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]); } BAILIF0(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0)); if (tjEncodeYUVPlanes(handle, &srcBuf[y * actualPitch + x * tjPixelSize[pf]], width, pitch, height, pf, dstPlanes, dstStrides, subsamp, flags) == -1) { - RELEASE_ARRAYS_ENCODEYUV(); + SAFE_RELEASE(src, srcBuf); THROW_TJ(); } bailout: - RELEASE_ARRAYS_ENCODEYUV(); + SAFE_RELEASE(src, srcBuf); } /* TurboJPEG 1.4.x: TJCompressor::encodeYUV() byte source */ @@ -785,9 +776,12 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress jintArray jDstStrides, jint desiredHeight, jint flags) { tjhandle handle = 0; + unsigned char *jpegBuf = NULL; jbyteArray jDstPlanes[3] = { NULL, NULL, NULL }; - unsigned char *jpegBuf = NULL, *dstPlanes[3]; - int *dstOffsets = NULL, *dstStrides = NULL; + unsigned char *dstPlanesTmp[3] = { NULL, NULL, NULL }; + unsigned char *dstPlanes[3] = { NULL, NULL, NULL }; + int *dstOffsetsTmp = NULL, dstOffsets[3] = { 0, 0, 0 }; + int *dstStridesTmp = NULL, dstStrides[3] = { 0, 0, 0 }; int jpegSubsamp = -1, jpegWidth = 0, jpegHeight = 0; int nc = 0, i, width, height, scaledWidth, scaledHeight, nsf = 0; tjscalingfactor *sf; @@ -821,57 +815,50 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress if (i >= nsf) THROW_ARG("Could not scale down to desired image dimensions"); -#define RELEASE_ARRAYS_DECOMPRESSTOYUV() { \ - SAFE_RELEASE(src, jpegBuf); \ - for (i = 0; i < nc; i++) \ - SAFE_RELEASE(jDstPlanes[i], dstPlanes[i]); \ - SAFE_RELEASE(jDstStrides, dstStrides); \ - SAFE_RELEASE(jDstOffsets, dstOffsets); \ -} + BAILIF0(dstOffsetsTmp = + (*env)->GetPrimitiveArrayCritical(env, jDstOffsets, 0)); + for (i = 0; i < nc; i++) dstOffsets[i] = dstOffsetsTmp[i]; + SAFE_RELEASE(jDstOffsets, dstOffsetsTmp); + + BAILIF0(dstStridesTmp = + (*env)->GetPrimitiveArrayCritical(env, jDstStrides, 0)); + for (i = 0; i < nc; i++) dstStrides[i] = dstStridesTmp[i]; + SAFE_RELEASE(jDstStrides, dstStridesTmp); - BAILIF0(dstOffsets = (*env)->GetPrimitiveArrayCritical(env, jDstOffsets, 0)); - BAILIF0(dstStrides = (*env)->GetPrimitiveArrayCritical(env, jDstStrides, 0)); for (i = 0; i < nc; i++) { int planeSize = tjPlaneSizeYUV(i, scaledWidth, dstStrides[i], scaledHeight, jpegSubsamp); int pw = tjPlaneWidth(i, scaledWidth, jpegSubsamp); - if (planeSize < 0 || pw < 0) { - RELEASE_ARRAYS_DECOMPRESSTOYUV(); + if (planeSize < 0 || pw < 0) THROW_ARG(tjGetErrorStr()); - } - if (dstOffsets[i] < 0) { - RELEASE_ARRAYS_DECOMPRESSTOYUV(); + if (dstOffsets[i] < 0) THROW_ARG("Invalid argument in decompressToYUV()"); - } - if (dstStrides[i] < 0 && dstOffsets[i] - planeSize + pw < 0) { - RELEASE_ARRAYS_DECOMPRESSTOYUV(); + if (dstStrides[i] < 0 && dstOffsets[i] - planeSize + pw < 0) THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary"); - } BAILIF0(jDstPlanes[i] = (*env)->GetObjectArrayElement(env, dstobjs, i)); if ((*env)->GetArrayLength(env, jDstPlanes[i]) < - dstOffsets[i] + planeSize) { - RELEASE_ARRAYS_DECOMPRESSTOYUV(); + dstOffsets[i] + planeSize) THROW_ARG("Destination plane is not large enough"); - } - BAILIF0(dstPlanes[i] = + BAILIF0(dstPlanesTmp[i] = (*env)->GetPrimitiveArrayCritical(env, jDstPlanes[i], 0)); - dstPlanes[i] = &dstPlanes[i][dstOffsets[i]]; + dstPlanes[i] = &dstPlanesTmp[i][dstOffsets[i]]; + SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]); } BAILIF0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0)); if (tjDecompressToYUVPlanes(handle, jpegBuf, (unsigned long)jpegSize, dstPlanes, desiredWidth, dstStrides, desiredHeight, flags) == -1) { - RELEASE_ARRAYS_DECOMPRESSTOYUV(); + SAFE_RELEASE(src, jpegBuf); THROW_TJ(); } bailout: - RELEASE_ARRAYS_DECOMPRESSTOYUV(); + SAFE_RELEASE(src, jpegBuf); } /* TurboJPEG 1.2.x: TJDecompressor::decompressToYUV() */ @@ -920,9 +907,11 @@ static void TJDecompressor_decodeYUV tjhandle handle = 0; jsize arraySize = 0, actualPitch; jbyteArray jSrcPlanes[3] = { NULL, NULL, NULL }; - const unsigned char *srcPlanes[3]; + const unsigned char *srcPlanesTmp[3] = { NULL, NULL, NULL }; + const unsigned char *srcPlanes[3] = { NULL, NULL, NULL }; + int *srcOffsetsTmp = NULL, srcOffsets[3] = { 0, 0, 0 }; + int *srcStridesTmp = NULL, srcStrides[3] = { 0, 0, 0 }; unsigned char *dstBuf = NULL; - int *srcOffsets = NULL, *srcStrides = NULL; int nc = (subsamp == org_libjpegturbo_turbojpeg_TJ_SAMP_GRAY ? 1 : 3), i; GET_HANDLE(); @@ -946,56 +935,49 @@ static void TJDecompressor_decodeYUV if ((*env)->GetArrayLength(env, dst) * dstElementSize < arraySize) THROW_ARG("Destination buffer is not large enough"); -#define RELEASE_ARRAYS_DECODEYUV() { \ - SAFE_RELEASE(dst, dstBuf); \ - for (i = 0; i < nc; i++) \ - SAFE_RELEASE(jSrcPlanes[i], srcPlanes[i]); \ - SAFE_RELEASE(jSrcStrides, srcStrides); \ - SAFE_RELEASE(jSrcOffsets, srcOffsets); \ -} + BAILIF0(srcOffsetsTmp = + (*env)->GetPrimitiveArrayCritical(env, jSrcOffsets, 0)); + for (i = 0; i < nc; i++) srcOffsets[i] = srcOffsetsTmp[i]; + SAFE_RELEASE(jSrcOffsets, srcOffsetsTmp); + + BAILIF0(srcStridesTmp = + (*env)->GetPrimitiveArrayCritical(env, jSrcStrides, 0)); + for (i = 0; i < nc; i++) srcStrides[i] = srcStridesTmp[i]; + SAFE_RELEASE(jSrcStrides, srcStridesTmp); - BAILIF0(srcOffsets = (*env)->GetPrimitiveArrayCritical(env, jSrcOffsets, 0)); - BAILIF0(srcStrides = (*env)->GetPrimitiveArrayCritical(env, jSrcStrides, 0)); for (i = 0; i < nc; i++) { int planeSize = tjPlaneSizeYUV(i, width, srcStrides[i], height, subsamp); int pw = tjPlaneWidth(i, width, subsamp); - if (planeSize < 0 || pw < 0) { - RELEASE_ARRAYS_DECODEYUV(); + if (planeSize < 0 || pw < 0) THROW_ARG(tjGetErrorStr()); - } - if (srcOffsets[i] < 0) { - RELEASE_ARRAYS_DECODEYUV(); + if (srcOffsets[i] < 0) THROW_ARG("Invalid argument in decodeYUV()"); - } - if (srcStrides[i] < 0 && srcOffsets[i] - planeSize + pw < 0) { - RELEASE_ARRAYS_DECODEYUV(); + if (srcStrides[i] < 0 && srcOffsets[i] - planeSize + pw < 0) THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary"); - } BAILIF0(jSrcPlanes[i] = (*env)->GetObjectArrayElement(env, srcobjs, i)); if ((*env)->GetArrayLength(env, jSrcPlanes[i]) < - srcOffsets[i] + planeSize) { - RELEASE_ARRAYS_DECODEYUV(); + srcOffsets[i] + planeSize) THROW_ARG("Source plane is not large enough"); - } - BAILIF0(srcPlanes[i] = + BAILIF0(srcPlanesTmp[i] = (*env)->GetPrimitiveArrayCritical(env, jSrcPlanes[i], 0)); - srcPlanes[i] = &srcPlanes[i][srcOffsets[i]]; + srcPlanes[i] = &srcPlanesTmp[i][srcOffsets[i]]; + SAFE_RELEASE(jSrcPlanes[i], srcPlanesTmp[i]); } BAILIF0(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0)); if (tjDecodeYUVPlanes(handle, srcPlanes, srcStrides, subsamp, &dstBuf[y * actualPitch + x * tjPixelSize[pf]], width, pitch, height, pf, flags) == -1) { - RELEASE_ARRAYS_DECODEYUV(); + SAFE_RELEASE(dst, dstBuf); THROW_TJ(); } bailout: - RELEASE_ARRAYS_DECODEYUV(); + SAFE_RELEASE(dst, dstBuf); } /* TurboJPEG 1.4.x: TJDecompressor::decodeYUV() byte destination */