diff --git a/Makefile.am b/Makefile.am index e70ae69..a04402f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -273,6 +273,13 @@ MD5_PPM_420_ISLOW_ARI_STRIP4_56 = 0e5e44a39b94817917a1bac72903246b 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_420_ISLOW_SUBSET15_31 = 7820f883760d6f8253a85ba1202ca9af +MD5_PPM_420_ISLOW_ARI_SUBSET16_139 = c725efb67f9cc96b95525eea52f8d2a3 +MD5_PPM_420_ISLOW_PROG_SUBSET71_132 = 26eb36ccc7d1f0cb80cdabb0ac8b5d99 +MD5_PPM_420_ISLOW_ARI_SUBSET4_56 = 886c6775af22370257122f8b16207e6d +MD5_PPM_444_ISLOW_SUBSET1_6 = ffc071e50a8836ddebc442f0bed09dc2 +MD5_PPM_444_ISLOW_PROG_SUBSET13_110 = db87dc7ce26bcdc7a6b56239ce2b9d6c +MD5_PPM_444_ISLOW_ARI_SUBSET0_36 = cb57b32bd6d03e35432362f7bf184b6d MD5_JPEG_CROP = b4197f377e621c4e9b1d20471432610d endif @@ -629,6 +636,51 @@ endif rm testout_444_islow_ari.jpg endif +# Partial decode tests. These tests are designed to cover all of the possible +# code paths in jpeg_set_partial_scanline(). + +# Context rows: Yes Intra-iMCU row: Yes iMCU row prefetch: No ENT: huff + ./djpeg -dct int -subset 15,31,15,31 -ppm -outfile testout_420_islow_subset15,31.ppm $(srcdir)/testimages/$(TESTORIG) + md5/md5cmp $(MD5_PPM_420_ISLOW_SUBSET15_31) testout_420_islow_subset15,31.ppm + rm testout_420_islow_subset15,31.ppm +# Context rows: Yes Intra-iMCU row: No iMCU row prefetch: Yes ENT: arith +if WITH_ARITH_DEC + ./djpeg -dct int -subset 16,139,16,139 -ppm -outfile testout_420_islow_ari_subset16,139.ppm $(srcdir)/testimages/testimgari.jpg + md5/md5cmp $(MD5_PPM_420_ISLOW_ARI_SUBSET16_139) testout_420_islow_ari_subset16,139.ppm + rm testout_420_islow_ari_subset16,139.ppm +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 -subset 71,132,71,132 -ppm -outfile testout_420_islow_prog_subset71,132.ppm testout_420_islow_prog.jpg + md5/md5cmp $(MD5_PPM_420_ISLOW_PROG_SUBSET71_132) testout_420_islow_prog_subset71,132.ppm + rm testout_420_islow_prog_subset71,132.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 -subset 4,56,4,56 -ppm -outfile testout_420_islow_ari_subset4,56.ppm $(srcdir)/testimages/testimgari.jpg + md5/md5cmp $(MD5_PPM_420_ISLOW_ARI_SUBSET4_56) testout_420_islow_ari_subset4,56.ppm + rm testout_420_islow_ari_subset4,56.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 + ./djpeg -dct int -subset 1,6,1,6 -ppm -outfile testout_444_islow_subset1,6.ppm testout_444_islow.jpg + md5/md5cmp $(MD5_PPM_444_ISLOW_SUBSET1_6) testout_444_islow_subset1,6.ppm + rm testout_444_islow_subset1,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 -subset 13,110,13,110 -ppm -outfile testout_444_islow_prog_subset13,110.ppm testout_444_islow_prog.jpg + md5/md5cmp $(MD5_PPM_444_ISLOW_PROG_SUBSET13_110) testout_444_islow_prog_subset13,110.ppm + rm testout_444_islow_prog_subset13,110.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 -subset 0,36,0,36 -ppm -outfile testout_444_islow_ari_subset0,36.ppm testout_444_islow_ari.jpg + md5/md5cmp $(MD5_PPM_444_ISLOW_ARI_SUBSET0_36) testout_444_islow_ari_subset0,36.ppm + rm testout_444_islow_ari_subset0,36.ppm +endif + rm testout_444_islow_ari.jpg +endif + ./jpegtran -crop 120x90+20+50 -transpose -perfect -outfile testout_crop.jpg $(srcdir)/testimages/$(TESTORIG) md5/md5cmp $(MD5_JPEG_CROP) testout_crop.jpg rm testout_crop.jpg diff --git a/djpeg.1 b/djpeg.1 index 17296ab..b8d81c9 100644 --- a/djpeg.1 +++ b/djpeg.1 @@ -1,4 +1,4 @@ -.TH DJPEG 1 "26 June 2015" +.TH DJPEG 1 "17 December 2015" .SH NAME djpeg \- decompress a JPEG file to an image file .SH SYNOPSIS @@ -204,6 +204,12 @@ 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 scaled image dimensions. .TP +.BI \-subset " X0,X1,Y0,Y1" +Decode only a subset of the JPEG image between X0 and X1 and Y0 and Y1 +(inclusive.) If necessary, X0 will be shifted to the left to the start of the +nearest block. Note that if decompression scaling is being used, X0, X1, Y0, +and Y1 are relative to the scaled image dimensions. +.TP .B \-verbose Enable debug printout. More .BR \-v 's diff --git a/djpeg.c b/djpeg.c index 93567c6..d37ca12 100644 --- a/djpeg.c +++ b/djpeg.c @@ -90,8 +90,8 @@ 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 strip, skip, subset; +JDIMENSION startX, endX, startY, endY; #define INPUT_BUF_SIZE 4096 @@ -169,6 +169,7 @@ usage (void) #endif fprintf(stderr, " -skip Y0,Y1 Decode all rows except those between Y0 and Y1 (inclusive)\n"); + fprintf(stderr, " -subset X0,X1,Y0,Y1 Decode only a subset of the image (inclusive)\n"); fprintf(stderr, " -strip Y0,Y1 Decode only rows between Y0 and Y1 (inclusive)\n"); fprintf(stderr, " -verbose or -debug Emit debug output\n"); fprintf(stderr, " -version Print version information and exit\n"); @@ -197,6 +198,7 @@ parse_switches (j_decompress_ptr cinfo, int argc, char **argv, memsrc = FALSE; strip = FALSE; skip = FALSE; + subset = FALSE; cinfo->err->trace_level = 0; /* Scan command line options, adjust parameters */ @@ -392,6 +394,14 @@ parse_switches (j_decompress_ptr cinfo, int argc, char **argv, usage(); skip = TRUE; + } else if (keymatch(arg, "subset", 2)) { + if (++argn >= argc) + usage(); + if (sscanf(argv[argn], "%d,%d,%d,%d", &startX, &endX, &startY, &endY) != 4 + || startX > endX || startY > endY) + usage(); + subset = TRUE; + } else if (keymatch(arg, "targa", 1)) { /* Targa output format. */ requested_fmt = FMT_TARGA; @@ -657,8 +667,8 @@ main (int argc, char **argv) /* Start decompressor */ (void) jpeg_start_decompress(&cinfo); - /* Strip decode */ - if (strip || skip) { + /* Subset decode */ + if (strip || skip || subset) { JDIMENSION tmp; /* Check for valid endY. We cannot check this value until after @@ -671,6 +681,38 @@ main (int argc, char **argv) exit(EXIT_FAILURE); } + if (subset) { + JDIMENSION width; + + /* Check for valid endX. We cannot check this value until after + * jpeg_start_decompress() is called. Note that we have already verified + * that startX <= endX. + */ + if (endX > cinfo.output_width - 1) { + fprintf(stderr, "%s: subset %d-%d exceeds image width %d\n", progname, + startX, endX, cinfo.output_width); + exit(EXIT_FAILURE); + } + + width = endX - startX + 1; + jpeg_set_partial_scanline(&cinfo, &startX, &width); + + /* It turns out this is really hard to fix... */ + 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; + + ((ppm_dest_ptr) dest_mgr)->buffer_width = cinfo.output_width * cinfo.out_color_components; + } + /* 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. */ diff --git a/jdapistd.c b/jdapistd.c index d134242..cf9a6cc 100644 --- a/jdapistd.c +++ b/jdapistd.c @@ -140,6 +140,114 @@ output_pass_setup (j_decompress_ptr cinfo) /* + * Tells the JPEG decompressor to only decode partial lines. + * + * 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_set_partial_scanline (j_decompress_ptr cinfo, JDIMENSION* start_x, + JDIMENSION* width) +{ + int ci, align, MCU_sample_width, orig_downsampled_width; + JDIMENSION input_start_x; + jpeg_component_info* compptr; + + /* Must be called after jpeg_start_decompress before reading or skipping + * any scanlines. + */ + if (cinfo->global_state != DSTATE_SCANNING || cinfo->output_scanline != 0) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* start_x and width must form a valid subset of the output_width. */ + if (*width == 0 || *start_x + *width > cinfo->output_width) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + /* No need to do any work if the caller wants the entire width. */ + if (*width == cinfo->output_width) + return; + + /* Ensuring the proper alignment of start_x is tricky. At a minimum, it + * must align to the size of the largest IDCT block. This is to comply + * with the following implementation details: + * (1) The IDCT is performed in blocks. We will not be modifying the + * IDCT routines to inverse transform a partial block. + * (2) Upsampling and color conversion require aligned input memory, so + * we must begin these steps at the start of the first block that + * we IDCT. + * + * In practice, we actually impose a stricter alignment requirement. We + * require that start_x be a multiple of the MCU_sample_width of the + * component with the largest MCU_sample_width. This is to simplify the + * single pass decompress case, allowing us to use the same MCU_col for all + * of the components. + */ + align = 0; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + + /* We cannot rely on the MCU_sample_width stored on the compptr because + * it does not take the h_samp_factor into account on multi-scan images. + */ + MCU_sample_width = compptr->_DCT_scaled_size * compptr->h_samp_factor; + if (MCU_sample_width > align) { + align = MCU_sample_width; + } + } + + /* Set start_x to an aligned value that is less than or equal to the input + * value. + */ + input_start_x = *start_x; + *start_x = (input_start_x / align) * align; + + /* Set the width. We may recommend a few extra pixels so the right edge of + * the subset remains the same as the request. It is important that clients + * check this value after calling this function, in case their memory is not + * large enough. + */ + *width = *width + input_start_x - *start_x; + cinfo->output_width = *width; + + /* Set the first and last MCU columns that we must decompress. These values + * will be used in single scan decodes. + */ + cinfo->master->first_MCU_subset_col = (JDIMENSION) + ((long) *start_x) / + ((long) (cinfo->max_h_samp_factor * cinfo->min_DCT_scaled_size)); + cinfo->master->last_MCU_subset_col = (JDIMENSION) jdiv_round_up( + (long) (*start_x + cinfo->output_width), + (long) (cinfo->max_h_samp_factor*cinfo->min_DCT_scaled_size)) - 1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Change the downsampled_width to correspond 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) { + remove_fancy_upsample(cinfo); + } + + /* Set the first and last blocks that we must decompress. These values + * will be used in multiple scan decodes. + */ + compptr->first_subset_block = (JDIMENSION) + ((long) (*start_x * (long) compptr->h_samp_factor)) / + ((long) (cinfo->max_h_samp_factor * cinfo->min_DCT_scaled_size)); + compptr->last_subset_block = (JDIMENSION) jdiv_round_up( + (long) ((*start_x + cinfo->output_width) * + (long) compptr->h_samp_factor), + (long) (cinfo->max_h_samp_factor * cinfo->min_DCT_scaled_size)) - 1; + } +} + + +/* * Read some scanlines of data from the JPEG decompressor. * * The return value will be the number of lines actually read. diff --git a/jdcoefct.c b/jdcoefct.c index baf6bc8..49fc2b6 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, D. R. Commander. + * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -107,38 +108,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 + * subset. */ - 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_MCU_subset_col && + MCU_col_num <= cinfo->master->last_MCU_subset_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_MCU_subset_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; } } } @@ -291,11 +300,13 @@ decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) } inverse_DCT = cinfo->idct->inverse_DCT[ci]; 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] + compptr->first_subset_block; output_col = 0; - for (block_num = 0; block_num < compptr->width_in_blocks; block_num++) { + for (block_num = compptr->first_subset_block; + block_num <= compptr->last_subset_block; block_num++) { (*inverse_DCT) (cinfo, compptr, (JCOEFPTR) buffer_ptr, output_ptr, output_col); buffer_ptr++; @@ -481,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] + compptr->first_subset_block; if (first_row && block_row == 0) prev_block_row = buffer_ptr; else @@ -498,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 = compptr->first_subset_block; + block_num <= compptr->last_subset_block; 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 0e0a18b..bfe0cda 100644 --- a/jdinput.c +++ b/jdinput.c @@ -105,6 +105,12 @@ 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 block cols to decompress in multiple scan + * decodes. By default, decompress all of the columns. + */ + compptr->first_subset_block = 0; + compptr->last_subset_block = 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 42c71ba..90bcd14 100644 --- a/jdmaster.c +++ b/jdmaster.c @@ -7,6 +7,7 @@ * libjpeg-turbo Modifications: * Copyright (C) 2009-2011, 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. * @@ -579,6 +580,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 MCU cols to decompress in single scan decodes. + * By default, decompress all of the columns. + */ + cinfo->master->first_MCU_subset_col = 0; + cinfo->master->last_MCU_subset_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 diff --git a/jdsample.c b/jdsample.c index 276feae..1b47378 100644 --- a/jdsample.c +++ b/jdsample.c @@ -7,6 +7,7 @@ * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2010, 2015, 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. * @@ -360,6 +361,39 @@ h2v2_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, /* + * Correct the selected upsampling routines if fancy upsampling is no longer + * safe. + */ + +GLOBAL(void) +remove_fancy_upsample(j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample; + int ci; + jpeg_component_info * compptr; + + upsample = (my_upsample_ptr) cinfo->upsample; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) + if (upsample->methods[ci] == jsimd_h2v2_fancy_upsample || + upsample->methods[ci] == h2v2_fancy_upsample) { + if (jsimd_can_h2v2_upsample()) + upsample->methods[ci] = jsimd_h2v2_upsample; + else + upsample->methods[ci] = h2v2_upsample; + } + else if (upsample->methods[ci] == jsimd_h2v1_fancy_upsample || + upsample->methods[ci] == h2v1_fancy_upsample) { + if (jsimd_can_h2v1_upsample()) + upsample->methods[ci] = jsimd_h2v1_upsample; + else + upsample->methods[ci] = h2v1_upsample; + } +} + + +/* * Module initialization routine for upsampling. */ diff --git a/jdsample.h b/jdsample.h index da905a6..aeeffbe 100644 --- a/jdsample.h +++ b/jdsample.h @@ -3,6 +3,7 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. + * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. */ @@ -49,3 +50,6 @@ typedef struct { } my_upsampler; typedef my_upsampler * my_upsample_ptr; + +GLOBAL(void) +remove_fancy_upsample (j_decompress_ptr cinfo); diff --git a/jpegint.h b/jpegint.h index 1530e0c..342b635 100644 --- a/jpegint.h +++ b/jpegint.h @@ -6,6 +6,7 @@ * Modified 1997-2009 by Guido Vollbeding. * libjpeg-turbo Modifications: * Copyright (C) 2015, D. R. Commander + * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -150,6 +151,10 @@ 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_MCU_subset_col; + JDIMENSION last_MCU_subset_col; }; /* Input control module */ diff --git a/jpeglib.h b/jpeglib.h index 7d9c6cc..042f572 100644 --- a/jpeglib.h +++ b/jpeglib.h @@ -184,6 +184,10 @@ typedef struct { /* Private per-component storage for DCT or IDCT subsystem. */ void * dct_table; + + /* Partial decompression variables */ + JDIMENSION first_subset_block; + JDIMENSION last_subset_block; } jpeg_component_info; @@ -995,6 +999,9 @@ 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_set_partial_scanline (j_decompress_ptr cinfo, + JDIMENSION* start_x, + JDIMENSION* width); EXTERN(boolean) jpeg_finish_decompress (j_decompress_ptr cinfo); /* Replaces jpeg_read_scanlines when reading raw downsampled data. */ diff --git a/libjpeg.txt b/libjpeg.txt index d1f6417..efcebb2 100644 --- a/libjpeg.txt +++ b/libjpeg.txt @@ -730,9 +730,11 @@ 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 -------------------------------- +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 @@ -772,6 +774,47 @@ 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_set_partial_scanline (j_decompress_ptr cinfo, JDIMENSION* start_x, + JDIMENSION* width) + +This must be called after jpeg_start_decompress() and before any calls to +jpeg_read_scanlines() or jpeg_skip_scanlines(). + +This function provides application programmers with the ability to decode only +a portion of each row, 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.) + +If start_x and width do not form a valid subset of the image row, this function +will error exit. Note that we are taking a subset of the output_width, which +might be scaled. Stated again, if the client is requesting scaling, any +subsetting will be applied to the scaled dimensions. + +start_x and width are passed in as pointers because start_x must be aligned +to the IDCT block size. If the application programmer passes in an unaligned +start_x, libjpeg-turbo will choose and suggest new values of start_x and width, +such that start_x is properly aligned and that the entire requested subset is +contained in the new subset. If the client does not like this "suggestion", +it can call jpeg_set_partial_scanline() again with new values of start_x and +width. + +When providing memory to jpeg_read_scanlines() (after calling this function), +the client should provide enough memory for the suggested value of "width", +which will be equal to the updated value of cinfo->output_width. + +The output of a jpeg_set_partial_scanline() decode will be identical to the +corresponding pixels in a full image decode with one exception. "Fancy" H2V2 +and H2V1 upsampling modes use neighboring pixels to upsample, except on the +right and left edges of the image (where neighboring pixels are missing). +On subset decodes, these "fancy" upsampling modes may treat the left and right +edges of the subset as if they are the left and right edges of the image. +This means that the upsample step may be simplified. The result is that the +pixels on the left or right edge of the subset image may not be exactly +identical to the corresponding pixels in the original image. + Mechanics of usage: include files, linking, etc -----------------------------------------------