Files
FireBee_Setup/sources/z-tools/trunk/zview/resample.c

314 lines
8.0 KiB
C

#include "general.h"
#include "mfdb.h"
#include "pic_load.h"
#include "zvdi/pixel.h"
#include <math.h>
#define MagickEpsilon 1.0e-6
typedef struct
{
double weight;
long pixel;
}ContributionInfo;
typedef struct
{
double (*function)( double);
double support;
} FilterInfo;
static double ( *algo)( double x) = NULL;
double algo_support = 3.0f;
static inline uint8 RoundToQuantum( double value)
{
if( value < 0.0)
return( 0);
if ( value >= 255.0)
return(( uint8) 255);
return(( uint8)( value + 0.5));
}
static double Sinc( double x)
{
if( x == 0.0)
return( 1.0);
return( sin( x) / x);
}
static double Lanczos( double x)
{
x = fabs(x);
return( x < 3.0) ? Sinc( M_PI * x) * Sinc( 1.0471975511965977461533333333333 * x) : 0.0;
}
static double Gaussian( double x)
{
return( exp((double)( -2.0 * x * x)) * sqrt( 2.0 / M_PI));
}
static double Cubic( double x)
{
if( x < -2.0)
return( 0.0);
if( x < -1.0)
return( ( 2.0 + x) * ( 2.0 + x) * ( 2.0 + x) / 6.0);
if( x < 0.0)
return( ( 4.0 + x * x * ( -6.0 - 3.0 * x)) / 6.0);
if( x < 1.0)
return( ( 4.0 + x * x * ( -6.0 + 3.0 * x)) / 6.0);
if( x < 2.0)
return( ( 2.0 - x) * ( 2.0 - x) * ( 2.0 - x) / 6.0);
return(0.0);
}
static double Blackman( double x)
{
return( 0.42 + 0.5 * cos( M_PI * ( double)x) + 0.08 * cos( 2 * M_PI * (double) x));
}
static double Triangle( double x)
{
if( x < -1.0)
return( 0.0);
if( x < 0.0)
return( 1.0 + x);
if( x < 1.0)
return( 1.0 - x);
return( 0.0);
}
static double Quadratic( double x)
{
if( x < -1.5)
return( 0.0);
if( x < -0.5)
return( 0.5 * ( x + 1.5) * ( x + 1.5));
if( x < 0.5)
return( 0.75 - x * x);
if( x < 1.5)
return( 0.5 * ( x - 1.5) * ( x - 1.5));
return( 0.0);
}
static void HorizontalFilter( MFDB *source, MFDB *destination, double x_factor, ContributionInfo *contribution)
{
long start, stop;
double center, density, scale, support;
register long i, x, y, n;
uint8 source_red, source_green, source_blue;
uint32 source_line_size, dst_line_size;
source_line_size = (( uint32)source->fd_wdwidth << 1 ) * ( uint32)source->fd_nplanes;
dst_line_size = (( uint32)destination->fd_wdwidth << 1 ) * ( uint32)destination->fd_nplanes;
scale = MAX( 1.0 / x_factor, 1.0);
support = scale * algo_support;
if( support <= 0.5)
{
/* Reduce to point sampling. */
support = ( double)( 0.5 + MagickEpsilon);
scale = 1.0;
}
scale = 1.0 / scale;
for( x = 0; x < destination->fd_w; x++)
{
center = ( double)( x + 0.5) / x_factor;
start = ( long)MAX( center - support + 0.5, 0);
stop = ( long)MIN( center + support + 0.5, ( double)source->fd_w);
density = 0.0;
for( n = 0; n < (stop-start); n++)
{
contribution[n].pixel = start + n;
contribution[n].weight = algo( scale * (( double)( start + n) - center + 0.5));
density += contribution[n].weight;
}
if(( density != 0.0) && ( density != 1.0))
{
density = 1.0 / density;
for( i = 0; i < n; i++)
contribution[i].weight *= density;
}
/* source->fd_h */
for( y = 0; y < destination->fd_h; y++)
{
register double gamma = 0.0;
register double red = 0.0;
register double green = 0.0;
register double blue = 0.0;
for( i = 0; i < n; i++)
{
getPixel( contribution[i].pixel, y, source_line_size, &source_red, &source_green, &source_blue, source->fd_addr);
red += contribution[i].weight * source_red;
green += contribution[i].weight * source_green;
blue += contribution[i].weight * source_blue;
gamma += contribution[i].weight;
}
gamma = 1.0 / ( fabs( gamma) <= MagickEpsilon ? 1.0 : gamma);
setPixel( x, y, dst_line_size, RoundToQuantum( gamma * red), RoundToQuantum( gamma * green), RoundToQuantum( gamma * blue), destination->fd_addr);
}
}
}
static void VerticalFilter( MFDB *source, MFDB *destination, double y_factor, ContributionInfo *contribution)
{
register long i, y, x, n;
long start, stop;
double center, density, scale, support;
uint8 source_red, source_green, source_blue;
uint32 source_line_size, dst_line_size;
source_line_size = (( uint32)source->fd_wdwidth << 1 ) * ( uint32)source->fd_nplanes;
dst_line_size = (( uint32)destination->fd_wdwidth << 1 ) * ( uint32)destination->fd_nplanes;
scale = MAX( 1.0 / y_factor, 1.0);
support = scale * algo_support;
if( support <= 0.5)
{
/* Reduce to point sampling. */
support = ( double)( 0.5 + MagickEpsilon);
scale = 1.0;
}
scale = 1.0 / scale;
for( y = 0; y < destination->fd_h; y++)
{
center = ( double)( y + 0.5) / y_factor;
start = ( long)MAX( center - support + 0.5, 0);
stop = ( long)MIN( center + support + 0.5, ( double) source->fd_h);
density = 0.0;
for( n = 0; n < ( stop - start); n++)
{
contribution[n].pixel = start + n;
contribution[n].weight = algo( scale * (( double)( start + n) - center + 0.5));
density += contribution[n].weight;
}
if(( density != 0.0) && ( density != 1.0))
{
/* Normalize. */
density = 1.0 / density;
for( i = 0; i < n; i++)
contribution[i].weight *= density;
}
for ( x = 0; x < destination->fd_w; x++)
{
register double gamma = 0.0;
register double red = 0.0;
register double green = 0.0;
register double blue = 0.0;
for ( i = 0; i < n; i++)
{
getPixel( x, contribution[i].pixel, source_line_size, &source_red, &source_green, &source_blue, source->fd_addr);
red += contribution[i].weight * source_red;
green += contribution[i].weight * source_green;
blue += contribution[i].weight * source_blue;
gamma += contribution[i].weight;
}
gamma = 1.0 / ( fabs( gamma) <= MagickEpsilon ? 1.0 : gamma);
setPixel( x, y, dst_line_size, RoundToQuantum( gamma * red), RoundToQuantum( gamma * green), RoundToQuantum( gamma * blue), destination->fd_addr);
}
}
}
int16 smooth_resize( MFDB *source_image, MFDB *resized_image, int resize_algo)
{
MFDB tmp_img;
double support, x_factor, x_support, y_factor, y_support;
ContributionInfo *contribution;
static const FilterInfo filters[7] =
{
{ NULL, 0},
{ Triangle, 1.0f},
{ Blackman, 1.0f},
{ Gaussian, 1.25f},
{ Quadratic, 1.5f},
{ Cubic, 2.0f},
{ Lanczos, 3.0f}
};
algo = filters[resize_algo].function;
algo_support = filters[resize_algo].support;
x_factor = (double) resized_image->fd_w / (double) source_image->fd_w;
y_factor = (double) resized_image->fd_h / (double) source_image->fd_h;
x_support = MAX( 1.0 / x_factor, 1.0) * algo_support;
y_support = MAX( 1.0 / y_factor, 1.0) * algo_support;
support = MAX( x_support, y_support);
if( support < algo_support)
support = algo_support;
contribution = ( ContributionInfo *)gmalloc( ( size_t)( 2.0 * MAX( support, 0.5) + 3) * sizeof( *contribution));
if( contribution == NULL)
return 0;
/* Resize image. */
if (( resized_image->fd_w * (( uint32) source_image->fd_h + resized_image->fd_h)) > ( resized_image->fd_h * ((uint32) source_image->fd_w + resized_image->fd_w)))
{
if( !init_mfdb( &tmp_img, resized_image->fd_w, source_image->fd_h, app.nplanes))
{
gfree(contribution);
return 0;
}
HorizontalFilter( source_image , &tmp_img, x_factor, contribution);
VerticalFilter( &tmp_img, resized_image, y_factor, contribution);
}
else
{
if( !init_mfdb( &tmp_img, source_image->fd_w, resized_image->fd_h, app.nplanes))
{
gfree(contribution);
return 0;
}
VerticalFilter( source_image, &tmp_img, y_factor, contribution);
HorizontalFilter( &tmp_img, resized_image, x_factor, contribution);
}
gfree( contribution);
gfree( tmp_img.fd_addr);
return 1;
}