#include "config.h"
#define _USE_MATH_DEFINES
#include <math.h>
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif
#include <stdlib.h>
#include <sixel.h>
#if !defined(MAX)
# define MAX(l, r) ((l) > (r) ? (l) : (r))
#endif
#if !defined(MIN)
#define MIN(l, r) ((l) < (r) ? (l) : (r))
#endif
#if 0#endif
static double
bilinear(double const d)
{
if (d < 1.0) {
return 1.0 - d;
}
return 0.0;
}
static double
welsh(double const d)
{
if (d < 1.0) {
return 1.0 - d * d;
}
return 0.0;
}
static double
bicubic(double const d)
{
if (d <= 1.0) {
return 1.0 + (d - 2.0) * d * d;
}
if (d <= 2.0) {
return 4.0 + d * (-8.0 + d * (5.0 - d));
}
return 0.0;
}
static double
sinc(double const x)
{
return sin(M_PI * x) / (M_PI * x);
}
static double
lanczos2(double const d)
{
if (d == 0.0) {
return 1.0;
}
if (d < 2.0) {
return sinc(d) * sinc(d / 2.0);
}
return 0.0;
}
static double
lanczos3(double const d)
{
if (d == 0.0) {
return 1.0;
}
if (d < 3.0) {
return sinc(d) * sinc(d / 3.0);
}
return 0.0;
}
static double
lanczos4(double const d)
{
if (d == 0.0) {
return 1.0;
}
if (d < 4.0) {
return sinc(d) * sinc(d / 4.0);
}
return 0.0;
}
static double
gaussian(double const d)
{
return exp(-2.0 * d * d) * sqrt(2.0 / M_PI);
}
static double
hanning(double const d)
{
return 0.5 + 0.5 * cos(d * M_PI);
}
static double
hamming(const double d)
{
return 0.54 + 0.46 * cos(d * M_PI);
}
static unsigned char
normalize(double x, double total)
{
int result;
result = floor(x / total);
if (result > 255) {
return 0xff;
}
if (result < 0) {
return 0x00;
}
return (unsigned char)result;
}
static void
scale_without_resampling(
unsigned char *dst,
unsigned char const *src,
int const srcw,
int const srch,
int const dstw,
int const dsth,
int const depth)
{
int w;
int h;
int x;
int y;
int i;
int pos;
for (h = 0; h < dsth; h++) {
for (w = 0; w < dstw; w++) {
x = w * srcw / dstw;
y = h * srch / dsth;
for (i = 0; i < depth; i++) {
pos = (y * srcw + x) * depth + i;
dst[(h * dstw + w) * depth + i] = src[pos];
}
}
}
}
typedef double (*resample_fn_t)(double const d);
static void
scale_with_resampling(
unsigned char *dst,
unsigned char const *src,
int const srcw,
int const srch,
int const dstw,
int const dsth,
int const depth,
resample_fn_t const f_resample,
double n)
{
int w;
int h;
int x;
int y;
int i;
int pos;
int x_first, x_last, y_first, y_last;
double center_x, center_y;
double diff_x, diff_y;
double weight;
double total;
double offsets[8];
for (h = 0; h < dsth; h++) {
for (w = 0; w < dstw; w++) {
total = 0.0;
for (i = 0; i < depth; i++) {
offsets[i] = 0;
}
if (dstw >= srcw) {
center_x = (w + 0.5) * srcw / dstw;
x_first = MAX(center_x - n, 0);
x_last = MIN(center_x + n, srcw - 1);
} else {
center_x = w + 0.5;
x_first = MAX(floor((center_x - n) * srcw / dstw), 0);
x_last = MIN(floor((center_x + n) * srcw / dstw), srcw - 1);
}
if (dsth >= srch) {
center_y = (h + 0.5) * srch / dsth;
y_first = MAX(center_y - n, 0);
y_last = MIN(center_y + n, srch - 1);
} else {
center_y = h + 0.5;
y_first = MAX(floor((center_y - n) * srch / dsth), 0);
y_last = MIN(floor((center_y + n) * srch / dsth), srch - 1);
}
for (y = y_first; y <= y_last; y++) {
for (x = x_first; x <= x_last; x++) {
if (dstw >= srcw) {
diff_x = (x + 0.5) - center_x;
} else {
diff_x = (x + 0.5) * dstw / srcw - center_x;
}
if (dsth >= srch) {
diff_y = (y + 0.5) - center_y;
} else {
diff_y = (y + 0.5) * dsth / srch - center_y;
}
weight = f_resample(fabs(diff_x)) * f_resample(fabs(diff_y));
for (i = 0; i < depth; i++) {
pos = (y * srcw + x) * depth + i;
offsets[i] += src[pos] * weight;
}
total += weight;
}
}
if (total > 0.0) {
for (i = 0; i < depth; i++) {
pos = (h * dstw + w) * depth + i;
dst[pos] = normalize(offsets[i], total);
}
}
}
}
}
SIXELAPI int
sixel_helper_scale_image(
unsigned char *dst,
unsigned char const *src,
int srcw,
int srch,
int pixelformat,
int dstw,
int dsth,
int method_for_resampling,
sixel_allocator_t *allocator)
{
int const depth = sixel_helper_compute_depth(pixelformat);
unsigned char *new_src = NULL;
int nret;
int new_pixelformat;
if (depth != 3) {
new_src = (unsigned char *)sixel_allocator_malloc(allocator, (size_t)(srcw * srch * 3));
if (new_src == NULL) {
return (-1);
}
nret = sixel_helper_normalize_pixelformat(new_src,
&new_pixelformat,
src, pixelformat,
srcw, srch);
if (nret != 0) {
sixel_allocator_free(allocator, new_src);
return (-1);
}
src = new_src;
} else {
new_pixelformat = pixelformat;
}
switch (method_for_resampling) {
case SIXEL_RES_NEAREST:
scale_without_resampling(dst, src, srcw, srch, dstw, dsth, depth);
break;
case SIXEL_RES_GAUSSIAN:
scale_with_resampling(dst, src, srcw, srch, dstw, dsth, depth,
gaussian, 1.0);
break;
case SIXEL_RES_HANNING:
scale_with_resampling(dst, src, srcw, srch, dstw, dsth, depth,
hanning, 1.0);
break;
case SIXEL_RES_HAMMING:
scale_with_resampling(dst, src, srcw, srch, dstw, dsth, depth,
hamming, 1.0);
break;
case SIXEL_RES_WELSH:
scale_with_resampling(dst, src, srcw, srch, dstw, dsth, depth,
welsh, 1.0);
break;
case SIXEL_RES_BICUBIC:
scale_with_resampling(dst, src, srcw, srch, dstw, dsth, depth,
bicubic, 2.0);
break;
case SIXEL_RES_LANCZOS2:
scale_with_resampling(dst, src, srcw, srch, dstw, dsth, depth,
lanczos2, 3.0);
break;
case SIXEL_RES_LANCZOS3:
scale_with_resampling(dst, src, srcw, srch, dstw, dsth, depth,
lanczos3, 3.0);
break;
case SIXEL_RES_LANCZOS4:
scale_with_resampling(dst, src, srcw, srch, dstw, dsth, depth,
lanczos4, 4.0);
break;
case SIXEL_RES_BILINEAR:
default:
scale_with_resampling(dst, src, srcw, srch, dstw, dsth, depth,
bilinear, 1.0);
break;
}
free(new_src);
return 0;
}