/* * jcsample.c * * Copyright (C) 1991-1994, Thomas G. Lane. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * * This file contains downsampling routines. * * Downsampling input data is counted in "row groups". A row group * is defined to be max_v_samp_factor pixel rows of each component, * from which the downsampler produces v_samp_factor sample rows. * A single row group is processed in each call to the downsampler module. * * The downsampler is responsible for edge-expansion of its output data * to fill an integral number of DCT blocks horizontally. The source buffer * may be modified if it is helpful for this purpose (the source buffer is * allocated wide enough to correspond to the desired output width). * The caller (the prep controller) is responsible for vertical padding. * * The downsampler may request "context rows" by setting need_context_rows * during startup. In this case, the input arrays will contain at least * one row group's worth of pixels above and below the passed-in data; * the caller will create dummy rows at image top and bottom by replicating * the first or last real pixel row. * * An excellent reference for image resampling is * Digital Image Warping, George Wolberg, 1990. * Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. * * The downsampling algorithm used here is a simple average of the source * pixels covered by the output pixel. The hi-falutin sampling literature * refers to this as a "box filter". In general the characteristics of a box * filter are not very good, but for the specific cases we normally use (1:1 * and 2:1 ratios) the box is equivalent to a "triangle filter" which is not * nearly so bad. If you intend to use other sampling ratios, you'd be well * advised to improve this code. * * A simple input-smoothing capability is provided. This is mainly intended * for cleaning up color-dithered GIF input files (if you find it inadequate, * we suggest using an external filtering program such as pnmconvol). When * enabled, each input pixel P is replaced by a weighted sum of itself and its * eight neighbors. P's weight is 1-8*SF and each neighbor's weight is SF, * where SF = (smoothing_factor / 1024). * Currently, smoothing is only supported for 2h2v sampling factors. */ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" /* Pointer to routine to downsample a single component */ typedef JMETHOD ( void, downsample1_ptr, ( j_compress_ptr cinfo, jpeg_component_info * compptr, JSAMPARRAY input_data, JSAMPARRAY output_data ) ); /* Private subobject */ typedef struct { struct jpeg_downsampler pub;/* public fields */ /* Downsampling method pointers, one per component */ downsample1_ptr methods[MAX_COMPONENTS]; } my_downsampler; typedef my_downsampler * my_downsample_ptr; /* * Initialize for a downsampling pass. */ METHODDEF void start_pass_downsample( j_compress_ptr cinfo ) { /* no work for now */ } /* * Expand a component horizontally from width input_cols to width output_cols, * by duplicating the rightmost samples. */ LOCAL void expand_right_edge( JSAMPARRAY image_data, int num_rows, JDIMENSION input_cols, JDIMENSION output_cols ) { register JSAMPROW ptr; register JSAMPLE pixval; register int count; int row; int numcols = (int) ( output_cols - input_cols ); if ( numcols > 0 ) { for ( row = 0; row < num_rows; row++ ) { ptr = image_data[row] + input_cols; pixval = ptr[-1];/* don't need GETJSAMPLE() here */ for ( count = numcols; count > 0; count-- ) { *ptr++ = pixval; } } } } /* * Do downsampling for a whole row group (all components). * * In this version we simply downsample each component independently. */ METHODDEF void sep_downsample( j_compress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION in_row_index, JSAMPIMAGE output_buf, JDIMENSION out_row_group_index ) { my_downsample_ptr downsample = (my_downsample_ptr) cinfo->downsample; int ci; jpeg_component_info * compptr; JSAMPARRAY in_ptr, out_ptr; for ( ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++ ) { in_ptr = input_buf[ci] + in_row_index; out_ptr = output_buf[ci] + ( out_row_group_index * compptr->v_samp_factor ); ( *downsample->methods[ci] )( cinfo, compptr, in_ptr, out_ptr ); } } /* * Downsample pixel values of a single component. * One row group is processed per call. * This version handles arbitrary integral sampling ratios, without smoothing. * Note that this version is not actually used for customary sampling ratios. */ METHODDEF void int_downsample( j_compress_ptr cinfo, jpeg_component_info * compptr, JSAMPARRAY input_data, JSAMPARRAY output_data ) { int inrow, outrow, h_expand, v_expand, numpix, numpix2, h, v; JDIMENSION outcol, outcol_h;/* outcol_h == outcol*h_expand */ JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; JSAMPROW inptr, outptr; INT32 outvalue; h_expand = cinfo->max_h_samp_factor / compptr->h_samp_factor; v_expand = cinfo->max_v_samp_factor / compptr->v_samp_factor; numpix = h_expand * v_expand; numpix2 = numpix / 2; /* Expand input data enough to let all the output samples be generated * by the standard loop. Special-casing padded output would be more * efficient. */ expand_right_edge( input_data, cinfo->max_v_samp_factor, cinfo->image_width, output_cols * h_expand ); inrow = 0; for ( outrow = 0; outrow < compptr->v_samp_factor; outrow++ ) { outptr = output_data[outrow]; for ( outcol = 0, outcol_h = 0; outcol < output_cols; outcol++, outcol_h += h_expand ) { outvalue = 0; for ( v = 0; v < v_expand; v++ ) { inptr = input_data[inrow + v] + outcol_h; for ( h = 0; h < h_expand; h++ ) { outvalue += (INT32) GETJSAMPLE( *inptr++ ); } } *outptr++ = (JSAMPLE) ( ( outvalue + numpix2 ) / numpix ); } inrow += v_expand; } } /* * Downsample pixel values of a single component. * This version handles the special case of a full-size component, * without smoothing. */ METHODDEF void fullsize_downsample( j_compress_ptr cinfo, jpeg_component_info * compptr, JSAMPARRAY input_data, JSAMPARRAY output_data ) { /* Copy the data */ jcopy_sample_rows( input_data, 0, output_data, 0, cinfo->max_v_samp_factor, cinfo->image_width ); /* Edge-expand */ expand_right_edge( output_data, cinfo->max_v_samp_factor, cinfo->image_width, compptr->width_in_blocks * DCTSIZE ); } /* * Downsample pixel values of a single component. * This version handles the common case of 2:1 horizontal and 1:1 vertical, * without smoothing. * * A note about the "bias" calculations: when rounding fractional values to * integer, we do not want to always round 0.5 up to the next integer. * If we did that, we'd introduce a noticeable bias towards larger values. * Instead, this code is arranged so that 0.5 will be rounded up or down at * alternate pixel locations (a simple ordered dither pattern). */ METHODDEF void h2v1_downsample( j_compress_ptr cinfo, jpeg_component_info * compptr, JSAMPARRAY input_data, JSAMPARRAY output_data ) { int outrow; JDIMENSION outcol; JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; register JSAMPROW inptr, outptr; register int bias; /* Expand input data enough to let all the output samples be generated * by the standard loop. Special-casing padded output would be more * efficient. */ expand_right_edge( input_data, cinfo->max_v_samp_factor, cinfo->image_width, output_cols * 2 ); for ( outrow = 0; outrow < compptr->v_samp_factor; outrow++ ) { outptr = output_data[outrow]; inptr = input_data[outrow]; bias = 0; /* bias = 0,1,0,1,... for successive samples */ for ( outcol = 0; outcol < output_cols; outcol++ ) { *outptr++ = (JSAMPLE) ( ( GETJSAMPLE( *inptr ) + GETJSAMPLE( inptr[1] ) + bias ) >> 1 ); bias ^= 1; /* 0=>1, 1=>0 */ inptr += 2; } } } /* * Downsample pixel values of a single component. * This version handles the standard case of 2:1 horizontal and 2:1 vertical, * without smoothing. */ METHODDEF void h2v2_downsample( j_compress_ptr cinfo, jpeg_component_info * compptr, JSAMPARRAY input_data, JSAMPARRAY output_data ) { int inrow, outrow; JDIMENSION outcol; JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; register JSAMPROW inptr0, inptr1, outptr; register int bias; /* Expand input data enough to let all the output samples be generated * by the standard loop. Special-casing padded output would be more * efficient. */ expand_right_edge( input_data, cinfo->max_v_samp_factor, cinfo->image_width, output_cols * 2 ); inrow = 0; for ( outrow = 0; outrow < compptr->v_samp_factor; outrow++ ) { outptr = output_data[outrow]; inptr0 = input_data[inrow]; inptr1 = input_data[inrow + 1]; bias = 1; /* bias = 1,2,1,2,... for successive samples */ for ( outcol = 0; outcol < output_cols; outcol++ ) { *outptr++ = (JSAMPLE) ( ( GETJSAMPLE( *inptr0 ) + GETJSAMPLE( inptr0[1] ) + GETJSAMPLE( *inptr1 ) + GETJSAMPLE( inptr1[1] ) + bias ) >> 2 ); bias ^= 3; /* 1=>2, 2=>1 */ inptr0 += 2; inptr1 += 2; } inrow += 2; } } #ifdef INPUT_SMOOTHING_SUPPORTED /* * Downsample pixel values of a single component. * This version handles the standard case of 2:1 horizontal and 2:1 vertical, * with smoothing. One row of context is required. */ METHODDEF void h2v2_smooth_downsample( j_compress_ptr cinfo, jpeg_component_info * compptr, JSAMPARRAY input_data, JSAMPARRAY output_data ) { int inrow, outrow; JDIMENSION colctr; JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; register JSAMPROW inptr0, inptr1, above_ptr, below_ptr, outptr; INT32 membersum, neighsum, memberscale, neighscale; /* Expand input data enough to let all the output samples be generated * by the standard loop. Special-casing padded output would be more * efficient. */ expand_right_edge( input_data - 1, cinfo->max_v_samp_factor + 2, cinfo->image_width, output_cols * 2 ); /* We don't bother to form the individual "smoothed" input pixel values; * we can directly compute the output which is the average of the four * smoothed values. Each of the four member pixels contributes a fraction * (1-8*SF) to its own smoothed image and a fraction SF to each of the three * other smoothed pixels, therefore a total fraction (1-5*SF)/4 to the final * output. The four corner-adjacent neighbor pixels contribute a fraction * SF to just one smoothed pixel, or SF/4 to the final output; while the * eight edge-adjacent neighbors contribute SF to each of two smoothed * pixels, or SF/2 overall. In order to use integer arithmetic, these * factors are scaled by 2^16 = 65536. * Also recall that SF = smoothing_factor / 1024. */ memberscale = 16384 - cinfo->smoothing_factor * 80;/* scaled (1-5*SF)/4 */ neighscale = cinfo->smoothing_factor * 16;/* scaled SF/4 */ inrow = 0; for ( outrow = 0; outrow < compptr->v_samp_factor; outrow++ ) { outptr = output_data[outrow]; inptr0 = input_data[inrow]; inptr1 = input_data[inrow + 1]; above_ptr = input_data[inrow - 1]; below_ptr = input_data[inrow + 2]; /* Special case for first column: pretend column -1 is same as column 0 */ membersum = GETJSAMPLE( *inptr0 ) + GETJSAMPLE( inptr0[1] ) + GETJSAMPLE( *inptr1 ) + GETJSAMPLE( inptr1[1] ); neighsum = GETJSAMPLE( *above_ptr ) + GETJSAMPLE( above_ptr[1] ) + GETJSAMPLE( *below_ptr ) + GETJSAMPLE( below_ptr[1] ) + GETJSAMPLE( *inptr0 ) + GETJSAMPLE( inptr0[2] ) + GETJSAMPLE( *inptr1 ) + GETJSAMPLE( inptr1[2] ); neighsum += neighsum; neighsum += GETJSAMPLE( *above_ptr ) + GETJSAMPLE( above_ptr[2] ) + GETJSAMPLE( *below_ptr ) + GETJSAMPLE( below_ptr[2] ); membersum = membersum * memberscale + neighsum * neighscale; *outptr++ = (JSAMPLE) ( ( membersum + 32768 ) >> 16 ); inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2; for ( colctr = output_cols - 2; colctr > 0; colctr-- ) { /* sum of pixels directly mapped to this output element */ membersum = GETJSAMPLE( *inptr0 ) + GETJSAMPLE( inptr0[1] ) + GETJSAMPLE( *inptr1 ) + GETJSAMPLE( inptr1[1] ); /* sum of edge-neighbor pixels */ neighsum = GETJSAMPLE( *above_ptr ) + GETJSAMPLE( above_ptr[1] ) + GETJSAMPLE( *below_ptr ) + GETJSAMPLE( below_ptr[1] ) + GETJSAMPLE( inptr0[-1] ) + GETJSAMPLE( inptr0[2] ) + GETJSAMPLE( inptr1[-1] ) + GETJSAMPLE( inptr1[2] ); /* The edge-neighbors count twice as much as corner-neighbors */ neighsum += neighsum; /* Add in the corner-neighbors */ neighsum += GETJSAMPLE( above_ptr[-1] ) + GETJSAMPLE( above_ptr[2] ) + GETJSAMPLE( below_ptr[-1] ) + GETJSAMPLE( below_ptr[2] ); /* form final output scaled up by 2^16 */ membersum = membersum * memberscale + neighsum * neighscale; /* round, descale and output it */ *outptr++ = (JSAMPLE) ( ( membersum + 32768 ) >> 16 ); inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2; } /* Special case for last column */ membersum = GETJSAMPLE( *inptr0 ) + GETJSAMPLE( inptr0[1] ) + GETJSAMPLE( *inptr1 ) + GETJSAMPLE( inptr1[1] ); neighsum = GETJSAMPLE( *above_ptr ) + GETJSAMPLE( above_ptr[1] ) + GETJSAMPLE( *below_ptr ) + GETJSAMPLE( below_ptr[1] ) + GETJSAMPLE( inptr0[-1] ) + GETJSAMPLE( inptr0[1] ) + GETJSAMPLE( inptr1[-1] ) + GETJSAMPLE( inptr1[1] ); neighsum += neighsum; neighsum += GETJSAMPLE( above_ptr[-1] ) + GETJSAMPLE( above_ptr[1] ) + GETJSAMPLE( below_ptr[-1] ) + GETJSAMPLE( below_ptr[1] ); membersum = membersum * memberscale + neighsum * neighscale; *outptr = (JSAMPLE) ( ( membersum + 32768 ) >> 16 ); inrow += 2; } } /* * Downsample pixel values of a single component. * This version handles the special case of a full-size component, * with smoothing. One row of context is required. */ METHODDEF void fullsize_smooth_downsample( j_compress_ptr cinfo, jpeg_component_info * compptr, JSAMPARRAY input_data, JSAMPARRAY output_data ) { int outrow; JDIMENSION colctr; JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; register JSAMPROW inptr, above_ptr, below_ptr, outptr; INT32 membersum, neighsum, memberscale, neighscale; int colsum, lastcolsum, nextcolsum; /* Expand input data enough to let all the output samples be generated * by the standard loop. Special-casing padded output would be more * efficient. */ expand_right_edge( input_data - 1, cinfo->max_v_samp_factor + 2, cinfo->image_width, output_cols ); /* Each of the eight neighbor pixels contributes a fraction SF to the * smoothed pixel, while the main pixel contributes (1-8*SF). In order * to use integer arithmetic, these factors are multiplied by 2^16 = 65536. * Also recall that SF = smoothing_factor / 1024. */ memberscale = 65536L - cinfo->smoothing_factor * 512L;/* scaled 1-8*SF */ neighscale = cinfo->smoothing_factor * 64;/* scaled SF */ for ( outrow = 0; outrow < compptr->v_samp_factor; outrow++ ) { outptr = output_data[outrow]; inptr = input_data[outrow]; above_ptr = input_data[outrow - 1]; below_ptr = input_data[outrow + 1]; /* Special case for first column */ colsum = GETJSAMPLE( *above_ptr++ ) + GETJSAMPLE( *below_ptr++ ) + GETJSAMPLE( *inptr ); membersum = GETJSAMPLE( *inptr++ ); nextcolsum = GETJSAMPLE( *above_ptr ) + GETJSAMPLE( *below_ptr ) + GETJSAMPLE( *inptr ); neighsum = colsum + ( colsum - membersum ) + nextcolsum; membersum = membersum * memberscale + neighsum * neighscale; *outptr++ = (JSAMPLE) ( ( membersum + 32768 ) >> 16 ); lastcolsum = colsum; colsum = nextcolsum; for ( colctr = output_cols - 2; colctr > 0; colctr-- ) { membersum = GETJSAMPLE( *inptr++ ); above_ptr++; below_ptr++; nextcolsum = GETJSAMPLE( *above_ptr ) + GETJSAMPLE( *below_ptr ) + GETJSAMPLE( *inptr ); neighsum = lastcolsum + ( colsum - membersum ) + nextcolsum; membersum = membersum * memberscale + neighsum * neighscale; *outptr++ = (JSAMPLE) ( ( membersum + 32768 ) >> 16 ); lastcolsum = colsum; colsum = nextcolsum; } /* Special case for last column */ membersum = GETJSAMPLE( *inptr ); neighsum = lastcolsum + ( colsum - membersum ) + colsum; membersum = membersum * memberscale + neighsum * neighscale; *outptr = (JSAMPLE) ( ( membersum + 32768 ) >> 16 ); } } #endif /* INPUT_SMOOTHING_SUPPORTED */ /* * Module initialization routine for downsampling. * Note that we must select a routine for each component. */ GLOBAL void jinit_downsampler( j_compress_ptr cinfo ) { my_downsample_ptr downsample; int ci; jpeg_component_info * compptr; boolean smoothok = TRUE; downsample = (my_downsample_ptr) ( *cinfo->mem->alloc_small )( (j_common_ptr) cinfo, JPOOL_IMAGE, SIZEOF( my_downsampler ) ); cinfo->downsample = (struct jpeg_downsampler *) downsample; downsample->pub.start_pass = start_pass_downsample; downsample->pub.downsample = sep_downsample; downsample->pub.need_context_rows = FALSE; if ( cinfo->CCIR601_sampling ) { ERREXIT( cinfo, JERR_CCIR601_NOTIMPL ); } /* Verify we can handle the sampling factors, and set up method pointers */ for ( ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++ ) { if ( ( compptr->h_samp_factor == cinfo->max_h_samp_factor ) && ( compptr->v_samp_factor == cinfo->max_v_samp_factor ) ) { #ifdef INPUT_SMOOTHING_SUPPORTED if ( cinfo->smoothing_factor ) { downsample->methods[ci] = fullsize_smooth_downsample; downsample->pub.need_context_rows = TRUE; } else #endif downsample->methods[ci] = fullsize_downsample; } else if ( compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor && compptr->v_samp_factor == cinfo->max_v_samp_factor ) { smoothok = FALSE; downsample->methods[ci] = h2v1_downsample; } else if ( compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor && compptr->v_samp_factor * 2 == cinfo->max_v_samp_factor ) { #ifdef INPUT_SMOOTHING_SUPPORTED if ( cinfo->smoothing_factor ) { downsample->methods[ci] = h2v2_smooth_downsample; downsample->pub.need_context_rows = TRUE; } else #endif downsample->methods[ci] = h2v2_downsample; } else if ( ( cinfo->max_h_samp_factor % compptr->h_samp_factor ) == 0 && ( cinfo->max_v_samp_factor % compptr->v_samp_factor ) == 0 ) { smoothok = FALSE; downsample->methods[ci] = int_downsample; } else { ERREXIT( cinfo, JERR_FRACT_SAMPLE_NOTIMPL ); } } #ifdef INPUT_SMOOTHING_SUPPORTED if ( ( cinfo->smoothing_factor ) && ( !smoothok ) ) { TRACEMS( cinfo, 0, JTRC_SMOOTH_NOTIMPL ); } #endif }