2476 lines
66 KiB
C
2476 lines
66 KiB
C
/* dip.c
|
|
* Digital Image Processing routines
|
|
* (c) 2000-2002 Karel 'Clock' Kulhavy
|
|
* This file is a part of the Links program, released under GPL.
|
|
* This does various utilities for digital image processing including font
|
|
* rendering.
|
|
*/
|
|
|
|
#include "cfg.h"
|
|
|
|
#ifdef G
|
|
|
|
#include "links.h"
|
|
|
|
#ifdef HAVE_MATH_H
|
|
#include <math.h>
|
|
#endif /* HAVE_MATH_H */
|
|
|
|
/* #define this if you want to report missing letters to stderr.
|
|
* Leave it commented up for normal operation and releases! */
|
|
/* #define REPORT_UNKNOWN 1 */
|
|
|
|
unsigned aspect=65536; /* aspect=65536 for 320x240
|
|
* aspect=157286 for 640x200 (tall pixels)
|
|
* Defines aspect ratio of screen pixels.
|
|
* aspect=(196608*xsize+ysize<<1)/(ysize<<1);
|
|
* Default is 65536 because we assume square pixel
|
|
* when not specified otherwise. Aspect is ratio of
|
|
* the pixel height (in milimeters) to pixel width,
|
|
* multiplied by 65536. */
|
|
|
|
/* Limitation: No input image's dimension may exceed 2^(32-1-8) pixels.
|
|
*/
|
|
|
|
/* Each input byte represents 1 byte (gray). The question whether 0 is
|
|
* black or 255 is black doesn't matter.
|
|
*/
|
|
|
|
/* These constants represent contrast-enhancement and sharpen filter (which is one filter
|
|
* together) that is applied onto the letters to enhance their appearance at low height.
|
|
* They were determined by experiment for several values, interpolated, checked and tuned.
|
|
* If you change them, don't wonder if the letters start to look strange.
|
|
* The numers in the comments denote which height the line applies for.
|
|
*/
|
|
static_const float fancy_constants[64]={
|
|
(float)0,(float)3, /* 1 */
|
|
(float).1,(float)3, /* 2 */
|
|
(float).2,(float)3, /* 3 */
|
|
(float).3,(float)2.9, /* 4 */
|
|
(float).4,(float)2.7, /* 5 */
|
|
(float).4,(float)2.5, /* 6 */
|
|
(float).4,(float)2, /* 7 */
|
|
(float).5,(float)2, /* 8 */
|
|
(float).4,(float)2, /* 9 */
|
|
(float).38,(float)1.9, /* 10 */
|
|
(float).36,(float)1.8, /* 11 */
|
|
(float).33,(float)1.7, /* 12 */
|
|
(float).30,(float)1.6, /* 13 */
|
|
(float).25,(float)1.5, /* 14 */
|
|
(float).20,(float)1.5, /* 15 */
|
|
(float).15,(float)1.5, /* 16 */
|
|
(float).14,(float)1.5, /* 17 */
|
|
(float).13,(float)1.5, /* 18 */
|
|
(float).12,(float)1.5, /* 19 */
|
|
(float).12,(float)1.5, /* 20 */
|
|
(float).12,(float)1.5, /* 21 */
|
|
(float).12,(float)1.5, /* 22 */
|
|
(float).11,(float)1.5, /* 23 */
|
|
(float).10,(float)1.4, /* 24 */
|
|
(float).09,(float)1.3, /* 25 */
|
|
(float).08,(float)1.3, /* 26 */
|
|
(float).04,(float)1.2, /* 27 */
|
|
(float).04,(float)1.2, /* 28 */
|
|
(float).02,(float)1.1, /* 29 */
|
|
(float).02,(float)1.1, /* 30 */
|
|
(float).01,(float)1, /* 31 */
|
|
(float).01,(float)1 /* 32 */
|
|
};
|
|
|
|
|
|
/* This shall be hopefully reasonably fast and portable
|
|
* We assume ix is <65536. If not, the letters will be smaller in
|
|
* horizontal dimension (because of overflow) but this will not cause
|
|
* a segfault. 65536 pixels wide bitmaps are not normal and will anyway
|
|
* exhaust the memory.
|
|
*/
|
|
int compute_width(int ix, int iy, int required_height)
|
|
{
|
|
int width;
|
|
unsigned long reg;
|
|
|
|
reg=(unsigned long)aspect*(unsigned long)required_height;
|
|
|
|
if (reg>=0x1000000UL) {
|
|
/* It's big */
|
|
reg=(reg+32768)>>16;
|
|
width=(int)((reg*ix+(iy>>1))/iy);
|
|
}else{
|
|
/* It's small */
|
|
reg=(reg+128)>>8;
|
|
iy<<=8;
|
|
width=(int)((reg*ix+(iy>>1))/iy);
|
|
}
|
|
if (width<1) width=1;
|
|
return width;
|
|
}
|
|
|
|
static struct lru font_cache;
|
|
/* This is a cache for screen-ready colored bitmaps
|
|
* of lettrs and/or alpha channels for these (are the
|
|
* same size, only one byte per pixel and are used
|
|
* for letters with an image under them )
|
|
*/
|
|
|
|
#if defined(__i686__) || defined(__athlon__) || defined(__SSE2__) || defined(__x86_64__) || defined(__aarch64__) || defined(__alpha) || defined(__hppa)
|
|
/*
|
|
* On modern processors it is faster to do this in floating point.
|
|
* Do it only if we are sure that the coprocessor is present.
|
|
*/
|
|
typedef double scale_t;
|
|
#define USE_FP_SCALE
|
|
#elif defined(HAVE_LONG_LONG)
|
|
typedef unsigned long long scale_t;
|
|
#else
|
|
/*
|
|
* This may overflow, but systems without long long are very old
|
|
* and we will rather accept overflow on big images (65536 pixels)
|
|
* than slowing down the process unacceptably with possibly emulated FPU.
|
|
*/
|
|
typedef unsigned long scale_t;
|
|
#endif
|
|
|
|
/* Each input byte represents 1 byte (gray). The question whether 0 is
|
|
* black or 255 is black doesn't matter.
|
|
*/
|
|
|
|
static void add_col_gray(unsigned *my_restrict col_buf, unsigned char *my_restrict ptr, size_t line_skip, size_t n, unsigned weight)
|
|
{
|
|
for (;n;n--) {
|
|
*col_buf+=weight*(*ptr);
|
|
ptr+=line_skip;
|
|
col_buf++;
|
|
}
|
|
}
|
|
|
|
/* We assume unsigned short holds at least 16 bits. */
|
|
static void add_row_gray(unsigned *my_restrict row_buf, unsigned char *my_restrict ptr, size_t n, unsigned weight)
|
|
{
|
|
for (;n;n--) {
|
|
*row_buf+=weight**ptr;
|
|
ptr++;
|
|
row_buf++;
|
|
}
|
|
}
|
|
|
|
/* line_skip is in pixels. The column contains the whole pixels (R G B)
|
|
* We assume unsigned short holds at least 16 bits. */
|
|
#if defined(__ICC) && defined(USE_FP_SCALE)
|
|
/* ICC misoptimizes this function when inlining it */
|
|
ATTR_NOINLINE
|
|
#endif
|
|
static void add_col_color(scale_t *my_restrict col_buf,
|
|
#ifdef __GNUC__
|
|
#if __GNUC__ == 3
|
|
volatile
|
|
#endif
|
|
#endif
|
|
unsigned short *my_restrict ptr,
|
|
size_t line_skip, size_t n, ulonglong weight)
|
|
{
|
|
if (!weight) return;
|
|
#ifndef USE_FP_SCALE
|
|
if (weight < 0x10000) {
|
|
unsigned short weight_16 = (unsigned short)weight;
|
|
for (;n;n--) {
|
|
col_buf[0]+=(scale_t)((unsigned)weight_16*(unsigned)ptr[0]);
|
|
col_buf[1]+=(scale_t)((unsigned)weight_16*(unsigned)ptr[1]);
|
|
col_buf[2]+=(scale_t)((unsigned)weight_16*(unsigned)ptr[2]);
|
|
ptr+=line_skip;
|
|
col_buf+=3;
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
scale_t w = (scale_t)(longlong)weight;
|
|
for (;n;n--) {
|
|
col_buf[0]+=w*ptr[0];
|
|
col_buf[1]+=w*ptr[1];
|
|
col_buf[2]+=w*ptr[2];
|
|
ptr+=line_skip;
|
|
col_buf+=3;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* n is in pixels. pixel is 3 unsigned shorts in series */
|
|
/* We assume unsigned short holds at least 16 bits. */
|
|
static void add_row_color(scale_t *my_restrict row_buf,
|
|
#ifdef __GNUC__
|
|
#if __GNUC__ == 3
|
|
volatile
|
|
#endif
|
|
#endif
|
|
unsigned short *my_restrict ptr,
|
|
size_t n, ulonglong weight)
|
|
{
|
|
if (!weight) return;
|
|
#ifndef USE_FP_SCALE
|
|
if (weight < 0x10000) {
|
|
unsigned short weight_16 = (unsigned short)weight;
|
|
for (;n;n--) {
|
|
row_buf[0]+=(scale_t)((unsigned)weight_16*(unsigned)ptr[0]);
|
|
row_buf[1]+=(scale_t)((unsigned)weight_16*(unsigned)ptr[1]);
|
|
row_buf[2]+=(scale_t)((unsigned)weight_16*(unsigned)ptr[2]);
|
|
ptr+=3;
|
|
row_buf+=3;
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
scale_t w = (scale_t)(longlong)weight;
|
|
for (;n;n--) {
|
|
row_buf[0]+=w*ptr[0];
|
|
row_buf[1]+=w*ptr[1];
|
|
row_buf[2]+=w*ptr[2];
|
|
ptr+=3;
|
|
row_buf+=3;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We assume unsigned holds at least 32 bits */
|
|
static void emit_and_bias_col_gray(unsigned *my_restrict col_buf, unsigned char *my_restrict out,
|
|
size_t line_skip, size_t n, unsigned weight)
|
|
{
|
|
unsigned half=weight>>1;
|
|
|
|
for (;n;n--) {
|
|
*out=*col_buf/weight;
|
|
out+=line_skip;
|
|
*col_buf++=half;
|
|
}
|
|
}
|
|
|
|
/* We assume unsigned holds at least 32 bits */
|
|
static void emit_and_bias_row_gray(unsigned *my_restrict row_buf, unsigned char *my_restrict out,
|
|
size_t n, unsigned weight)
|
|
{
|
|
unsigned half=weight>>1;
|
|
|
|
for (;n;n--) {
|
|
*out++=*row_buf/weight;
|
|
*row_buf++=half;
|
|
}
|
|
}
|
|
|
|
/* We assume unsigned holds at least 32 bits */
|
|
static void bias_buf_gray(unsigned *my_restrict col_buf, size_t n, unsigned half)
|
|
{
|
|
for (;n;n--) *col_buf++=half;
|
|
}
|
|
|
|
/* We assume unsigned holds at least 32 bits */
|
|
static void bias_buf_color(scale_t *my_restrict col_buf, size_t n, scale_t half)
|
|
{
|
|
for (;n;n--) {
|
|
col_buf[0]=half;
|
|
col_buf[1]=half;
|
|
col_buf[2]=half;
|
|
col_buf+=3;
|
|
}
|
|
/* System activated */
|
|
}
|
|
|
|
#ifdef USE_FP_SCALE
|
|
#define op(x) ((x) * inv_weight)
|
|
#else
|
|
#define op(x) (sizeof(unsigned long) < sizeof(scale_t) && (x) == (unsigned long)(x) ? (unsigned long)(x) / weight : (x) / weight)
|
|
#endif
|
|
|
|
/* line skip is in pixels. Pixel is 3*unsigned short */
|
|
/* We assume unsigned holds at least 32 bits */
|
|
/* We assume unsigned short holds at least 16 bits. */
|
|
static void emit_and_bias_col_color(scale_t *my_restrict col_buf,
|
|
unsigned short *my_restrict out, size_t line_skip, size_t n, unsigned weight)
|
|
{
|
|
scale_t half=(scale_t)weight / 2;
|
|
#ifdef USE_FP_SCALE
|
|
scale_t inv_weight = (scale_t)1 / weight;
|
|
#endif
|
|
for (;n;n--) {
|
|
out[0]=(unsigned short)op(col_buf[0]);
|
|
col_buf[0]=half;
|
|
out[1]=(unsigned short)op(col_buf[1]);
|
|
col_buf[1]=half;
|
|
/* The following line is an enemy of the State and will be
|
|
* prosecuted according to the Constitution of The United States
|
|
* Cap. 20/3 ix. Sel. Bill 12/1920
|
|
* Moses 12/20 Erizea farizea 2:2:1:14
|
|
*/
|
|
out[2]=(unsigned short)op(col_buf[2]);
|
|
col_buf[2]=half;
|
|
out+=line_skip;
|
|
col_buf+=3;
|
|
}
|
|
}
|
|
|
|
/* n is in pixels. pixel is 3 unsigned shorts in series. */
|
|
/* We assume unsigned holds at least 32 bits */
|
|
/* We assume unsigned short holds at least 16 bits. */
|
|
static void emit_and_bias_row_color(scale_t *my_restrict row_buf,
|
|
unsigned short *my_restrict out, size_t n, unsigned weight)
|
|
{
|
|
scale_t half=(scale_t)weight / 2;
|
|
#ifdef USE_FP_SCALE
|
|
scale_t inv_weight = (scale_t)1 / weight;
|
|
#endif
|
|
for (;n;n--) {
|
|
out[0]=(unsigned short)op(row_buf[0]);
|
|
row_buf[0]=half;
|
|
out[1]=(unsigned short)op(row_buf[1]);
|
|
row_buf[1]=half;
|
|
out[2]=(unsigned short)op(row_buf[2]);
|
|
row_buf[2]=half;
|
|
out+=3;
|
|
row_buf+=3;
|
|
}
|
|
}
|
|
|
|
#undef op
|
|
|
|
/* For enlargement only -- does linear filtering.
|
|
* Allocates output and frees input.
|
|
* We assume unsigned holds at least 32 bits */
|
|
static void enlarge_gray_horizontal(unsigned char *in, size_t ix, size_t y, unsigned char **out, size_t ox)
|
|
{
|
|
unsigned *col_buf;
|
|
size_t total;
|
|
size_t out_pos,in_pos,in_begin,in_end;
|
|
unsigned half = (unsigned)((ox - 1) >> 1);
|
|
unsigned char *outptr;
|
|
unsigned char *inptr;
|
|
|
|
if (ox && ox * y / ox != y) overalloc();
|
|
if (ox * y > MAX_SIZE_T) overalloc();
|
|
outptr=mem_alloc(ox * y);
|
|
inptr=in;
|
|
*out=outptr;
|
|
if (ix==1) {
|
|
/* Dull copying */
|
|
for (;y;y--) {
|
|
memset(outptr,*inptr,ox);
|
|
outptr+=ox;
|
|
inptr++;
|
|
}
|
|
mem_free(in);
|
|
}else{
|
|
total=(ix-1)*(ox-1);
|
|
if (y > MAX_SIZE_T / sizeof(*col_buf)) overalloc();
|
|
col_buf=mem_alloc(y * sizeof(*col_buf));
|
|
bias_buf_gray(col_buf, y, half);
|
|
out_pos=0;
|
|
in_pos=0;
|
|
again:
|
|
in_begin=in_pos;
|
|
in_end=in_pos+ox-1;
|
|
add_col_gray(col_buf, inptr, ix, y, (unsigned)(in_end - out_pos));
|
|
add_col_gray(col_buf, inptr + 1, ix, y, (unsigned)(out_pos - in_begin));
|
|
emit_and_bias_col_gray(col_buf, outptr, ox, y, (unsigned)(ox - 1));
|
|
outptr++;
|
|
out_pos+=ix-1;
|
|
if (out_pos>in_end) {
|
|
in_pos=in_end;
|
|
inptr++;
|
|
}
|
|
if (out_pos>total) {
|
|
mem_free(in);
|
|
mem_free(col_buf);
|
|
return;
|
|
}
|
|
goto again;
|
|
}
|
|
/* Rohan, oh Rohan... */
|
|
/* ztracena zeme :) */
|
|
}
|
|
|
|
static inline longlong multiply_int(size_t a, size_t b)
|
|
{
|
|
#ifndef HAVE_LONG_LONG
|
|
volatile
|
|
#endif
|
|
longlong result = (ulonglong)a * (ulonglong)b;
|
|
#if !defined(__TINYC__) && !defined(HAVE_LONG_LONG)
|
|
if (result / a != (longlong)b) {
|
|
/*fprintf(stderr, "%lld, %lld, %d, %d\n", result / a, result, a, b);*/
|
|
overflow();
|
|
}
|
|
#endif
|
|
#ifndef HAVE_LONG_LONG
|
|
if (result == result + 1 || result == result - 1)
|
|
overflow();
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
/* For enlargement only -- does linear filtering
|
|
* Frees input and allocates output.
|
|
* We assume unsigned holds at least 32 bits
|
|
*/
|
|
static void enlarge_color_horizontal(unsigned short *in, size_t ix, size_t y,
|
|
unsigned short **outa, size_t ox)
|
|
{
|
|
#ifdef HAVE_OPENMP
|
|
int use_omp;
|
|
#endif
|
|
int n_threads;
|
|
size_t alloc_size;
|
|
scale_t *col_buf;
|
|
size_t a;
|
|
size_t skip=3*ix;
|
|
size_t oskip=3*ox;
|
|
unsigned short *out;
|
|
|
|
if (!in) {
|
|
*outa = NULL;
|
|
return;
|
|
}
|
|
|
|
if (ix==ox) {
|
|
*outa=in;
|
|
return;
|
|
}
|
|
if (ox && ox * y / ox != y) overalloc();
|
|
if (ox * y > MAX_SIZE_T / 3 / sizeof(*out)) overalloc();
|
|
out = mem_alloc_mayfail(sizeof(*out) * 3 * ox * y);
|
|
*outa=out;
|
|
if (!out) {
|
|
mem_free(in);
|
|
return;
|
|
}
|
|
if (ix==1) {
|
|
unsigned short *inp = in;
|
|
for (;y;y--,inp+=3) for (a=ox;a;a--,out+=3) {
|
|
out[0]=inp[0];
|
|
out[1]=inp[1];
|
|
out[2]=inp[2];
|
|
}
|
|
mem_free(in);
|
|
return;
|
|
}
|
|
multiply_int(ix-1,ox-1);
|
|
|
|
n_threads = omp_start();
|
|
#ifdef HAVE_OPENMP
|
|
use_omp = !OPENMP_NONATOMIC & (n_threads > 1) & (ox >= 24);
|
|
if (!use_omp)
|
|
n_threads = 1;
|
|
#endif
|
|
if (y > (MAX_SIZE_T - SMP_ALIGN + 1) / 3 / sizeof(*col_buf)) overalloc();
|
|
alloc_size = y * 3 * sizeof(*col_buf);
|
|
alloc_size = (alloc_size + SMP_ALIGN - 1) & ~(SMP_ALIGN - 1);
|
|
if (alloc_size > MAX_SIZE_T / n_threads) overalloc();
|
|
col_buf = mem_alloc_mayfail(alloc_size * n_threads);
|
|
if (!col_buf) goto skip_omp;
|
|
#ifdef HAVE_OPENMP
|
|
#pragma omp parallel default(none) firstprivate(col_buf,alloc_size,in,out,ix,ox,y,skip,oskip) if (use_omp)
|
|
#endif
|
|
{
|
|
scale_t *thread_col_buf;
|
|
ssize_t out_idx;
|
|
thread_col_buf = (scale_t *)((char *)col_buf + alloc_size * omp_get_thread_num());
|
|
bias_buf_color(thread_col_buf, y, (scale_t)(ox - 1) / 2);
|
|
#ifdef HAVE_OPENMP
|
|
#pragma omp for nowait
|
|
#endif
|
|
for (out_idx = 0; out_idx <= (ssize_t)(ox - 1); out_idx++) {
|
|
ulonglong out_pos, in_pos, in_end;
|
|
size_t in_idx;
|
|
out_pos = (ulonglong)out_idx * (ix - 1);
|
|
if (out_idx)
|
|
in_idx = (out_pos - 1) / (ox - 1);
|
|
else
|
|
in_idx = 0;
|
|
in_pos = (ulonglong)in_idx * (ox - 1);
|
|
in_end = in_pos + (ox - 1);
|
|
add_col_color(thread_col_buf, in + in_idx * 3, skip, y,
|
|
in_end - out_pos);
|
|
add_col_color(thread_col_buf, in + (in_idx + 1) * 3, skip, y,
|
|
out_pos - in_pos);
|
|
emit_and_bias_col_color(thread_col_buf, out + out_idx * 3, oskip, y, (unsigned)(ox - 1));
|
|
}
|
|
}
|
|
skip_omp:
|
|
omp_end();
|
|
|
|
mem_free(in);
|
|
if (col_buf) mem_free(col_buf);
|
|
else {
|
|
mem_free(out);
|
|
*outa = NULL;
|
|
}
|
|
}
|
|
|
|
/* Works for both enlarging and diminishing. Linear resample, no low pass.
|
|
* Automatically mem_frees the "in" and allocates "out". */
|
|
/* We assume unsigned holds at least 32 bits */
|
|
static void scale_gray_horizontal(unsigned char *in, size_t ix, size_t y, unsigned char **out, size_t ox)
|
|
{
|
|
unsigned *col_buf;
|
|
size_t total=ix * ox;
|
|
size_t out_pos, in_pos, in_begin, in_end, out_end;
|
|
unsigned char *outptr;
|
|
unsigned char *inptr;
|
|
|
|
if (ix<ox) {
|
|
enlarge_gray_horizontal(in,ix,y,out,ox);
|
|
return;
|
|
}else if (ix==ox) {
|
|
*out=in;
|
|
return;
|
|
}
|
|
if (ox && ox * y / ox != y) overalloc();
|
|
if (ox * y > MAX_SIZE_T) overalloc();
|
|
outptr=mem_alloc(ox*y);
|
|
inptr=in;
|
|
*out=outptr;
|
|
if (y > MAX_SIZE_T / sizeof(*col_buf)) overalloc();
|
|
col_buf = mem_alloc(y * sizeof(*col_buf));
|
|
bias_buf_gray(col_buf, y, (unsigned)(ix >> 1));
|
|
out_pos=0;
|
|
in_pos=0;
|
|
again:
|
|
in_begin=in_pos;
|
|
in_end=in_pos+ox;
|
|
out_end=out_pos+ix;
|
|
if (in_begin<out_pos)in_begin=out_pos;
|
|
if (in_end>out_end)in_end=out_end;
|
|
add_col_gray(col_buf, inptr, ix, y, (unsigned)(in_end - in_begin));
|
|
in_end=in_pos+ox;
|
|
if (out_end>=in_end) {
|
|
in_pos=in_end;
|
|
inptr++;
|
|
}
|
|
if (out_end<=in_end) {
|
|
emit_and_bias_col_gray(col_buf, outptr, ox, y, (unsigned)ix);
|
|
out_pos=out_pos+ix;
|
|
outptr++;
|
|
}
|
|
if (out_pos==total) {
|
|
mem_free(in);
|
|
mem_free(col_buf);
|
|
return;
|
|
}
|
|
goto again;
|
|
}
|
|
|
|
/* Works for both enlarging and diminishing. Linear resample, no low pass.
|
|
* Does only one color component.
|
|
* Frees ina and allocates outa.
|
|
* If ox*3<=ix, and display_optimize, performs optimization for LCD.
|
|
*/
|
|
static void scale_color_horizontal(unsigned short *in, size_t ix, size_t y, unsigned short **outa, size_t ox)
|
|
{
|
|
#ifdef HAVE_OPENMP
|
|
int use_omp;
|
|
#endif
|
|
int n_threads;
|
|
size_t alloc_size;
|
|
scale_t *col_buf;
|
|
size_t skip = 3 * ix;
|
|
size_t oskip = 3 * ox;
|
|
unsigned short *out;
|
|
|
|
if (!in) {
|
|
*outa = NULL;
|
|
return;
|
|
}
|
|
|
|
if (ix==ox) {
|
|
*outa=in;
|
|
return;
|
|
}
|
|
if (ix<ox) {
|
|
enlarge_color_horizontal(in,ix,y,outa,ox);
|
|
return;
|
|
}
|
|
multiply_int(ix,ox);
|
|
if (ox && ox * y / ox != y) overalloc();
|
|
if (ox * y > MAX_SIZE_T / 3 / sizeof(*out)) overalloc();
|
|
out = mem_alloc_mayfail(sizeof(*out) * 3 * ox * y);
|
|
*outa=out;
|
|
if (!out) {
|
|
mem_free(in);
|
|
return;
|
|
}
|
|
|
|
n_threads = omp_start();
|
|
#ifdef HAVE_OPENMP
|
|
use_omp = !OPENMP_NONATOMIC & (n_threads > 1) & (ox >= 24);
|
|
if (!use_omp)
|
|
n_threads = 1;
|
|
#endif
|
|
if (y > (MAX_SIZE_T - SMP_ALIGN + 1) / 3 / sizeof(*col_buf)) overalloc();
|
|
alloc_size = y * 3 * sizeof(*col_buf);
|
|
alloc_size = (alloc_size + SMP_ALIGN - 1) & ~(SMP_ALIGN - 1);
|
|
if (alloc_size > MAX_SIZE_T / n_threads) overalloc();
|
|
col_buf = mem_alloc_mayfail(alloc_size * n_threads);
|
|
if (!col_buf) goto skip_omp;
|
|
#ifdef HAVE_OPENMP
|
|
#pragma omp parallel default(none) firstprivate(col_buf,alloc_size,in,out,ix,ox,y,skip,oskip) if (use_omp)
|
|
#endif
|
|
{
|
|
scale_t *thread_col_buf;
|
|
ssize_t out_idx;
|
|
thread_col_buf = (scale_t *)((char *)col_buf + alloc_size * omp_get_thread_num());
|
|
bias_buf_color(thread_col_buf, y, (scale_t)ix / 2);
|
|
#ifdef HAVE_OPENMP
|
|
#pragma omp for nowait
|
|
#endif
|
|
for (out_idx = 0; out_idx < (ssize_t)ox; out_idx++) {
|
|
ulonglong out_pos, out_end, in_pos;
|
|
size_t in_idx;
|
|
out_pos = (ulonglong)out_idx * ix;
|
|
out_end = out_pos + ix;
|
|
in_idx = out_pos / ox;
|
|
in_pos = (ulonglong)in_idx * ox;
|
|
do {
|
|
ulonglong in_begin, in_end;
|
|
in_begin = in_pos;
|
|
in_end = in_pos + ox;
|
|
if (in_begin < out_pos) in_begin = out_pos;
|
|
if (in_end > out_end) in_end = out_end;
|
|
add_col_color(thread_col_buf, in + in_idx * 3, skip, y, in_end - in_begin);
|
|
in_idx++;
|
|
in_pos += ox;
|
|
} while (in_pos < out_end);
|
|
emit_and_bias_col_color(thread_col_buf, out + out_idx * 3, oskip, y, (unsigned)ix);
|
|
}
|
|
}
|
|
skip_omp:
|
|
omp_end();
|
|
|
|
mem_free(in);
|
|
if (col_buf) mem_free(col_buf);
|
|
else {
|
|
mem_free(out);
|
|
*outa = NULL;
|
|
}
|
|
}
|
|
|
|
/* For magnification only. Does linear filtering. */
|
|
/* We assume unsigned holds at least 32 bits */
|
|
static void enlarge_gray_vertical(unsigned char *in, size_t x, size_t iy, unsigned char **out, size_t oy)
|
|
{
|
|
unsigned *row_buf;
|
|
size_t total;
|
|
size_t out_pos,in_pos,in_begin,in_end;
|
|
unsigned half = (unsigned)((oy - 1) >> 1);
|
|
unsigned char *outptr;
|
|
unsigned char *inptr;
|
|
|
|
if (iy==1) {
|
|
if (x && x * oy / x != oy) overalloc();
|
|
if (x * oy > MAX_SIZE_T) overalloc();
|
|
outptr = mem_alloc(oy * x);
|
|
*out=outptr;
|
|
for(;oy;oy--,outptr+=x)
|
|
memcpy(outptr,in,x);
|
|
mem_free(in);
|
|
}
|
|
else if (iy==oy) {
|
|
*out=in;
|
|
}else{
|
|
if (x && x * oy / x != oy) overalloc();
|
|
if (x * oy > MAX_SIZE_T) overalloc();
|
|
outptr = mem_alloc(oy * x);
|
|
inptr=in;
|
|
*out=outptr;
|
|
total=(iy-1)*(oy-1);
|
|
if (x > MAX_SIZE_T / sizeof(*row_buf)) overalloc();
|
|
row_buf=mem_alloc(x * sizeof(*row_buf));
|
|
bias_buf_gray(row_buf, x, half);
|
|
out_pos=0;
|
|
in_pos=0;
|
|
again:
|
|
in_begin=in_pos;
|
|
in_end=in_pos+oy-1;
|
|
add_row_gray(row_buf, inptr, x, (unsigned)(in_end - out_pos));
|
|
add_row_gray(row_buf, inptr + x, x, (unsigned)(out_pos - in_begin));
|
|
emit_and_bias_row_gray(row_buf, outptr, x, (unsigned)(oy - 1));
|
|
outptr+=x;
|
|
out_pos+=iy-1;
|
|
if (out_pos>in_end) {
|
|
in_pos=in_end;
|
|
inptr+=x;
|
|
}
|
|
if (out_pos>total) {
|
|
mem_free(in);
|
|
mem_free(row_buf);
|
|
return;
|
|
}
|
|
goto again;
|
|
}
|
|
}
|
|
|
|
/* For magnification only. Does linear filtering */
|
|
/* We assume unsigned holds at least 32 bits */
|
|
static void enlarge_color_vertical(unsigned short *in, size_t x, size_t iy, unsigned short **outa, size_t oy)
|
|
{
|
|
#ifdef HAVE_OPENMP
|
|
int use_omp;
|
|
#endif
|
|
int n_threads;
|
|
size_t alloc_size;
|
|
scale_t *row_buf;
|
|
unsigned short *out;
|
|
|
|
if (!in) {
|
|
*outa = NULL;
|
|
return;
|
|
}
|
|
|
|
if (iy==oy) {
|
|
*outa=in;
|
|
return;
|
|
}
|
|
/* Rivendell */
|
|
if (x && x * oy / x != oy) overalloc();
|
|
if (x * oy > MAX_SIZE_T / 3 / sizeof(*out)) overalloc();
|
|
out = mem_alloc_mayfail(sizeof(*out) * 3 * oy * x);
|
|
*outa=out;
|
|
if (!out) {
|
|
mem_free(in);
|
|
return;
|
|
}
|
|
if (iy==1) {
|
|
for (;oy;oy--) {
|
|
memcpy(out,in,3*x*sizeof(*out));
|
|
out+=3*x;
|
|
}
|
|
mem_free(in);
|
|
return;
|
|
}
|
|
multiply_int(iy-1,oy-1);
|
|
|
|
n_threads = omp_start();
|
|
#ifdef HAVE_OPENMP
|
|
use_omp = (!OPENMP_NONATOMIC | !(x & 3)) & (n_threads > 1) & (oy >= 24);
|
|
if (!use_omp)
|
|
n_threads = 1;
|
|
#endif
|
|
if (x > (MAX_SIZE_T - SMP_ALIGN + 1) / 3 / sizeof(*row_buf)) overalloc();
|
|
alloc_size = x * 3 * sizeof(*row_buf);
|
|
alloc_size = (alloc_size + SMP_ALIGN - 1) & ~(SMP_ALIGN - 1);
|
|
if (alloc_size > MAX_SIZE_T / n_threads) overalloc();
|
|
row_buf = mem_alloc_mayfail(alloc_size * n_threads);
|
|
if (!row_buf) goto skip_omp;
|
|
#ifdef HAVE_OPENMP
|
|
#pragma omp parallel default(none) firstprivate(row_buf,alloc_size,in,out,x,iy,oy) if (use_omp)
|
|
#endif
|
|
{
|
|
scale_t *thread_row_buf;
|
|
ssize_t out_idx;
|
|
thread_row_buf = (scale_t *)((char *)row_buf + alloc_size * omp_get_thread_num());
|
|
bias_buf_color(thread_row_buf,x,(scale_t)(oy-1) / 2);
|
|
#ifdef HAVE_OPENMP
|
|
#pragma omp for nowait
|
|
#endif
|
|
for (out_idx = 0; out_idx <= (ssize_t)(oy - 1); out_idx++) {
|
|
ulonglong out_pos, in_pos, in_end;
|
|
size_t in_idx;
|
|
out_pos = (ulonglong)out_idx * (iy - 1);
|
|
if (out_idx)
|
|
in_idx = (out_pos - 1) / (oy - 1);
|
|
else
|
|
in_idx = 0;
|
|
in_pos = (ulonglong)in_idx * (oy - 1);
|
|
in_end = in_pos + oy - 1;
|
|
add_row_color(thread_row_buf, in + in_idx * 3 * x, x,
|
|
in_end - out_pos);
|
|
add_row_color(thread_row_buf, in + (in_idx + 1) * 3 * x, x,
|
|
out_pos - in_pos);
|
|
emit_and_bias_row_color(thread_row_buf, out + out_idx * 3 * x, x, (unsigned)(oy - 1));
|
|
}
|
|
}
|
|
skip_omp:
|
|
omp_end();
|
|
|
|
mem_free(in);
|
|
if (row_buf) mem_free(row_buf);
|
|
else {
|
|
mem_free(out);
|
|
*outa = NULL;
|
|
}
|
|
}
|
|
|
|
/* Both enlarges and diminishes. Linear filtering.
|
|
* Automatically allocates output and frees input.
|
|
* We assume unsigned holds at least 32 bits */
|
|
static void scale_gray_vertical(unsigned char *in, size_t x, size_t iy, unsigned char **out, size_t oy)
|
|
{
|
|
unsigned *row_buf;
|
|
size_t total=iy*oy;
|
|
size_t out_pos, in_pos, in_begin, in_end, out_end;
|
|
unsigned char *outptr;
|
|
unsigned char *inptr;
|
|
|
|
/* Snow White, Snow White... */
|
|
if (iy<oy) {
|
|
enlarge_gray_vertical(in,x,iy,out,oy);
|
|
return;
|
|
}
|
|
if (iy==oy) {
|
|
*out=in;
|
|
return;
|
|
}
|
|
if (x && x * oy / x != oy) overalloc();
|
|
if (x * oy > MAX_SIZE_T) overalloc();
|
|
outptr=mem_alloc(x*oy);
|
|
inptr=in;
|
|
*out=outptr;
|
|
if (x > MAX_SIZE_T / sizeof(*row_buf)) overalloc();
|
|
row_buf = mem_calloc(x * sizeof(*row_buf));
|
|
bias_buf_gray(row_buf, x, (unsigned)(iy >> 1));
|
|
out_pos=0;
|
|
in_pos=0;
|
|
again:
|
|
in_begin=in_pos;
|
|
in_end=in_pos+oy;
|
|
out_end=out_pos+iy;
|
|
if (in_begin<out_pos)in_begin=out_pos;
|
|
if (in_end>out_end)in_end=out_end;
|
|
add_row_gray(row_buf, inptr, x, (unsigned)(in_end - in_begin));
|
|
in_end=in_pos+oy;
|
|
if (out_end>=in_end) {
|
|
in_pos=in_end;
|
|
inptr+=x;
|
|
}
|
|
if (out_end<=in_end) {
|
|
emit_and_bias_row_gray(row_buf, outptr, x, (unsigned)iy);
|
|
out_pos=out_pos+iy;
|
|
outptr+=x;
|
|
}
|
|
if (out_pos==total) {
|
|
mem_free(in);
|
|
mem_free(row_buf);
|
|
return;
|
|
}
|
|
goto again;
|
|
}
|
|
|
|
/* Both enlarges and diminishes. Linear filtering. Sizes are
|
|
in pixels. Sizes are not in bytes. 1 pixel=3 unsigned shorts.
|
|
We assume unsigned short can hold at least 16 bits.
|
|
We assume unsigned holds at least 32 bits.
|
|
*/
|
|
static void scale_color_vertical(unsigned short *in, size_t x, size_t iy, unsigned short **outa, size_t oy)
|
|
{
|
|
#ifdef HAVE_OPENMP
|
|
int use_omp;
|
|
#endif
|
|
int n_threads;
|
|
size_t alloc_size;
|
|
scale_t *row_buf;
|
|
unsigned short *out;
|
|
|
|
if (!in) {
|
|
*outa = NULL;
|
|
return;
|
|
}
|
|
|
|
if (iy==oy) {
|
|
*outa=in;
|
|
return;
|
|
}
|
|
if (iy<oy) {
|
|
enlarge_color_vertical(in,x,iy,outa,oy);
|
|
return;
|
|
}
|
|
multiply_int(iy,oy);
|
|
if (x && x * oy / x != oy) overalloc();
|
|
if (x * oy > MAX_SIZE_T / 3 / sizeof(*out)) overalloc();
|
|
out = mem_alloc_mayfail(sizeof(*out) * 3 * oy * x);
|
|
*outa=out;
|
|
if (!out) {
|
|
mem_free(in);
|
|
return;
|
|
}
|
|
n_threads = omp_start();
|
|
#ifdef HAVE_OPENMP
|
|
use_omp = (!OPENMP_NONATOMIC | !(x & 3)) & (n_threads > 1) & (oy >= 24);
|
|
if (!use_omp)
|
|
n_threads = 1;
|
|
#endif
|
|
if (x > (MAX_SIZE_T - SMP_ALIGN + 1) / 3 / sizeof(*row_buf)) overalloc();
|
|
alloc_size = x * 3 * sizeof(*row_buf);
|
|
alloc_size = (alloc_size + SMP_ALIGN - 1) & ~(SMP_ALIGN - 1);
|
|
if (alloc_size > MAX_SIZE_T / n_threads) overalloc();
|
|
row_buf = mem_alloc_mayfail(alloc_size * n_threads);
|
|
if (!row_buf) goto skip_omp;
|
|
#ifdef HAVE_OPENMP
|
|
#pragma omp parallel default(none) firstprivate(row_buf,alloc_size,in,out,x,iy,oy) if (use_omp)
|
|
#endif
|
|
{
|
|
scale_t *thread_row_buf;
|
|
ssize_t out_idx;
|
|
thread_row_buf = (scale_t *)((char *)row_buf + alloc_size * omp_get_thread_num());
|
|
bias_buf_color(thread_row_buf,x,(scale_t)iy / 2);
|
|
#ifdef HAVE_OPENMP
|
|
#pragma omp for nowait
|
|
#endif
|
|
for (out_idx = 0; out_idx < (ssize_t)oy; out_idx++) {
|
|
ulonglong out_pos, out_end, in_pos;
|
|
size_t in_idx;
|
|
out_pos = (ulonglong)out_idx * iy;
|
|
out_end = out_pos + iy;
|
|
in_idx = out_pos / oy;
|
|
in_pos = (ulonglong)in_idx * oy;
|
|
do {
|
|
ulonglong in_begin, in_end;
|
|
in_begin = in_pos;
|
|
in_end = in_pos + oy;
|
|
if (in_begin < out_pos) in_begin = out_pos;
|
|
if (in_end > out_end) in_end = out_end;
|
|
add_row_color(thread_row_buf, in + in_idx * 3 * x, x, in_end - in_begin);
|
|
in_idx++;
|
|
in_pos += oy;
|
|
} while (in_pos < out_end);
|
|
emit_and_bias_row_color(thread_row_buf, out + out_idx * 3 * x, x, (unsigned)iy);
|
|
}
|
|
}
|
|
skip_omp:
|
|
omp_end();
|
|
|
|
mem_free(in);
|
|
if (row_buf) mem_free(row_buf);
|
|
else {
|
|
mem_free(out);
|
|
*outa = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/* Scales grayscale 8-bit map. Both enlarges and diminishes. Uses either low
|
|
* pass or bilinear filtering. Automatically mem_frees the "in".
|
|
* Automatically allocates "out".
|
|
*/
|
|
static void scale_gray(unsigned char *in, size_t ix, size_t iy, unsigned char **out, size_t ox, size_t oy)
|
|
{
|
|
unsigned char *intermediate_buffer;
|
|
|
|
if (!ix||!iy) {
|
|
if (in) mem_free(in);
|
|
if (ox && ox * oy / ox != oy) overalloc();
|
|
if (ox * oy > MAX_SIZE_T) overalloc();
|
|
*out = mem_calloc(ox * oy);
|
|
return;
|
|
}
|
|
if (ix*oy<ox*iy) {
|
|
scale_gray_vertical(in,ix,iy,&intermediate_buffer,oy);
|
|
scale_gray_horizontal(intermediate_buffer,ix,oy,out,ox);
|
|
}else{
|
|
scale_gray_horizontal(in,ix,iy,&intermediate_buffer,ox);
|
|
scale_gray_vertical(intermediate_buffer,ox,iy,out,oy);
|
|
}
|
|
}
|
|
|
|
/* To be called only when global variable display_optimize is 1 or 2.
|
|
* Performs a decimation according to this variable. Data shrink to 1/3
|
|
* and x is the smaller width.
|
|
* There must be 9*x*y unsigned shorts of data.
|
|
* x must be >=1.
|
|
* Performs realloc onto the buffer after decimation to save memory.
|
|
*/
|
|
static void decimate_3(unsigned short **data0, size_t x, size_t y)
|
|
{
|
|
unsigned short *data=*data0;
|
|
unsigned short *ahead=data;
|
|
size_t i, futuresize;
|
|
if (!data)
|
|
return;
|
|
if (x && (size_t)x * (size_t)y / (size_t)x != (size_t)y) overalloc();
|
|
if ((size_t)x * (size_t)y > MAX_SIZE_T / 3 / sizeof(**data0)) overalloc();
|
|
futuresize = x * y * 3 * sizeof(**data0);
|
|
|
|
#ifdef DEBUG
|
|
if (!(x>0&&y>0)) internal_error("zero width or height in decimate_3");
|
|
#endif /* #Ifdef DEBUG */
|
|
if (display_optimize==1) {
|
|
if (x==1) {
|
|
for (;y;y--,ahead+=9,data+=3) {
|
|
data[0]=(ahead[0]+ahead[0]+ahead[3])/3;
|
|
data[1]=(ahead[1]+ahead[4]+ahead[7])/3;
|
|
data[2]=(ahead[5]+ahead[8]+ahead[8])/3;
|
|
}
|
|
}else{
|
|
for (;y;y--) {
|
|
data[0]=(ahead[0]+ahead[0]+ahead[3])/3;
|
|
data[1]=(ahead[1]+ahead[4]+ahead[7])/3;
|
|
data[2]=(ahead[5]+ahead[8]+ahead[11])/3;
|
|
for (ahead+=9,data+=3,i=x-2;i;i--,ahead+=9,data+=3) {
|
|
data[0]=(ahead[-3]+ahead[0]+ahead[3])/3;
|
|
data[1]=(ahead[1]+ahead[4]+ahead[7])/3;
|
|
data[2]=(ahead[5]+ahead[8]+ahead[11])/3;
|
|
}
|
|
data[0]=(ahead[-3]+ahead[0]+ahead[3])/3;
|
|
data[1]=(ahead[1]+ahead[4]+ahead[7])/3;
|
|
data[2]=(ahead[5]+ahead[8]+ahead[8])/3;
|
|
ahead+=9,data+=3;
|
|
}
|
|
}
|
|
}else{
|
|
/* display_optimize==2 */
|
|
if (x==1) {
|
|
for (;y;y--,ahead+=9,data+=3) {
|
|
data[0]=(ahead[3]+ahead[6]+ahead[6])/3;
|
|
data[1]=(ahead[1]+ahead[4]+ahead[7])/3;
|
|
data[2]=(ahead[2]+ahead[2]+ahead[5])/3;
|
|
}
|
|
}else{
|
|
for (;y;y--) {
|
|
data[0]=(ahead[3]+ahead[6]+ahead[9])/3;
|
|
data[1]=(ahead[1]+ahead[4]+ahead[7])/3;
|
|
data[2]=(ahead[2]+ahead[2]+ahead[5])/3;
|
|
for (ahead+=9,data+=3,i=x-2;i;i--,ahead+=9,data+=3) {
|
|
data[0]=(ahead[3]+ahead[6]+ahead[9])/3;
|
|
data[1]=(ahead[1]+ahead[4]+ahead[7])/3;
|
|
data[2]=(ahead[-1]+ahead[2]+ahead[5])/3;
|
|
}
|
|
data[0]=(ahead[3]+ahead[6]+ahead[6])/3;
|
|
data[1]=(ahead[1]+ahead[4]+ahead[7])/3;
|
|
data[2]=(ahead[-1]+ahead[2]+ahead[5])/3;
|
|
ahead+=9,data+=3;
|
|
}
|
|
}
|
|
}
|
|
*data0=mem_realloc(*data0,futuresize);
|
|
}
|
|
|
|
/* Scales color 48-bits-per-pixel bitmap. Both enlarges and diminishes. Uses
|
|
* either low pass or bilinear filtering. The memory organization for both
|
|
* input and output are red, green, blue. All three of them are unsigned shorts 0-65535.
|
|
* Allocates output and frees input
|
|
* We assume unsigned short holds at least 16 bits.
|
|
*/
|
|
void scale_color(unsigned short *in, size_t ix, size_t iy, unsigned short **out, size_t ox, size_t oy)
|
|
{
|
|
unsigned short *intermediate_buffer;
|
|
int do_optimize;
|
|
size_t ox0 = ox;
|
|
|
|
if (!ix||!iy) {
|
|
if (in) mem_free(in);
|
|
if (ox && ox * oy / ox != oy) overalloc();
|
|
if (ox * oy > MAX_SIZE_T / 3 / sizeof(**out)) overalloc();
|
|
*out = mem_calloc_mayfail(ox * oy * sizeof(**out) * 3);
|
|
return;
|
|
}
|
|
if (display_optimize&&ox*3<=ix) {
|
|
do_optimize=1;
|
|
ox0=ox;
|
|
ox*=3;
|
|
}else do_optimize=0;
|
|
if (ix*oy<ox*iy) {
|
|
scale_color_vertical(in,ix,iy,&intermediate_buffer,oy);
|
|
scale_color_horizontal(intermediate_buffer,ix,oy,out,ox);
|
|
}else{
|
|
scale_color_horizontal(in,ix,iy,&intermediate_buffer,ox);
|
|
scale_color_vertical(intermediate_buffer,ox,iy,out,oy);
|
|
}
|
|
if (do_optimize) decimate_3(out, ox0, oy);
|
|
}
|
|
|
|
/* Fills a block with given color. length is number of pixels. pixel is a
|
|
* tribyte. 24 bits per pixel.
|
|
*/
|
|
void mix_one_color_24(unsigned char *my_restrict dest, size_t length, unsigned char r, unsigned char g, unsigned char b)
|
|
{
|
|
for (;length;length--) {
|
|
dest[0]=r;
|
|
dest[1]=g;
|
|
dest[2]=b;
|
|
dest+=3;
|
|
}
|
|
}
|
|
|
|
/* Fills a block with given color. length is number of pixels. pixel is a
|
|
* tribyte. 48 bits per pixel.
|
|
* We assume unsigned short holds at least 16 bits.
|
|
*/
|
|
void mix_one_color_48(unsigned short *my_restrict dest, size_t length, unsigned short r, unsigned short g, unsigned short b)
|
|
{
|
|
for (;length;length--) {
|
|
dest[0]=r;
|
|
dest[1]=g;
|
|
dest[2]=b;
|
|
dest+=3;
|
|
}
|
|
}
|
|
|
|
/* Mixes ink and paper of a letter, using alpha as alpha mask.
|
|
* Only mixing in photon space makes physical sense so that the input values
|
|
* must always be equivalent to photons and not to electrons!
|
|
* length is number of pixels. pixel is a tribyte
|
|
* alpha is 8-bit, rgb are all 16-bit
|
|
* We assume unsigned short holds at least 16 bits.
|
|
*/
|
|
static void mix_two_colors(unsigned short *my_restrict dest, unsigned char *my_restrict alpha,
|
|
size_t length, unsigned short r0, unsigned short g0, unsigned short b0,
|
|
unsigned short r255, unsigned short g255, unsigned short b255)
|
|
{
|
|
unsigned char mask, cmask;
|
|
|
|
for (;length;length--) {
|
|
mask=*alpha++;
|
|
if (((mask+1)&255)>=2) {
|
|
cmask=255-mask;
|
|
dest[0]=(mask*r255+cmask*r0+127)/255;
|
|
dest[1]=(mask*g255+cmask*g0+127)/255;
|
|
dest[2]=(mask*b255+cmask*b0+127)/255;
|
|
}else{
|
|
if (mask) {
|
|
dest[0]=r255;
|
|
dest[1]=g255;
|
|
dest[2]=b255;
|
|
}else{
|
|
dest[0]=r0;
|
|
dest[1]=g0;
|
|
dest[2]=b0;
|
|
}
|
|
}
|
|
dest+=3;
|
|
}
|
|
}
|
|
|
|
/* We assume unsigned short holds at least 16 bits. */
|
|
void agx_and_uc_32_to_48_table(unsigned short *my_restrict dest,
|
|
const unsigned char *my_restrict src, size_t length,
|
|
unsigned short *my_restrict table,
|
|
unsigned short rb, unsigned short gb, unsigned short bb)
|
|
{
|
|
unsigned char alpha, calpha;
|
|
unsigned short ri, gi, bi;
|
|
|
|
for (;length;length--)
|
|
{
|
|
ri=table[src[0]];
|
|
gi=table[src[1]+256];
|
|
bi=table[src[2]+512];
|
|
alpha=src[3];
|
|
src+=4;
|
|
if (((alpha+1)&255)>=2) {
|
|
calpha=255-alpha;
|
|
dest[0]=(ri*alpha+calpha*rb+127)/255;
|
|
dest[1]=(gi*alpha+calpha*gb+127)/255;
|
|
dest[2]=(bi*alpha+calpha*bb+127)/255;
|
|
} else {
|
|
if (alpha) {
|
|
dest[0]=ri;
|
|
dest[1]=gi;
|
|
dest[2]=bi;
|
|
} else {
|
|
dest[0]=rb;
|
|
dest[1]=gb;
|
|
dest[2]=bb;
|
|
}
|
|
}
|
|
dest+=3;
|
|
}
|
|
}
|
|
|
|
/* src is a block of four-bytes RGBA. All bytes are gamma corrected. length is
|
|
* number of pixels. output is input powered to the given gamma, passed into
|
|
* dest. src and dest may be identical and it will work. rb, gb, bb are 0-65535
|
|
* in linear monitor output photon space
|
|
*/
|
|
/* We assume unsigned short holds at least 16 bits. */
|
|
void agx_and_uc_32_to_48(unsigned short *my_restrict dest,
|
|
const unsigned char *my_restrict src, size_t length, float red_gamma,
|
|
float green_gamma, float blue_gamma, unsigned short rb,
|
|
unsigned short gb, unsigned short bb)
|
|
{
|
|
float_double r,g,b;
|
|
unsigned char alpha, calpha;
|
|
unsigned short ri,gi,bi;
|
|
const float_double inv_255=(float_double)(1/255.);
|
|
|
|
for (;length;length--)
|
|
{
|
|
r=src[0];
|
|
g=src[1];
|
|
b=src[2];
|
|
alpha=src[3];
|
|
src+=4;
|
|
r*=inv_255;
|
|
g*=inv_255;
|
|
b*=inv_255;
|
|
r=fd_pow(r,red_gamma);
|
|
g=fd_pow(g,green_gamma);
|
|
b=fd_pow(b,blue_gamma);
|
|
ri=(unsigned)((r*65535)+(float_double)0.5);
|
|
gi=(unsigned)((g*65535)+(float_double)0.5);
|
|
bi=(unsigned)((b*65535)+(float_double)0.5);
|
|
cmd_limit_16(ri);
|
|
cmd_limit_16(gi);
|
|
cmd_limit_16(bi);
|
|
if (((alpha+1)&255)>=2) {
|
|
calpha=255-alpha;
|
|
dest[0]=(unsigned short)((ri*alpha+calpha*rb+127U)/255U);
|
|
dest[1]=(unsigned short)((gi*alpha+calpha*gb+127U)/255U);
|
|
dest[2]=(unsigned short)((bi*alpha+calpha*bb+127U)/255U);
|
|
}else{
|
|
if (alpha) {
|
|
dest[0]=ri;
|
|
dest[1]=gi;
|
|
dest[2]=bi;
|
|
}else{
|
|
dest[0]=rb;
|
|
dest[1]=gb;
|
|
dest[2]=bb;
|
|
}
|
|
}
|
|
dest+=3;
|
|
}
|
|
}
|
|
/* src is a block of four-bytes RGBA. All bytes are gamma corrected. length is
|
|
* number of pixels. output is input powered to the given gamma, passed into
|
|
* dest. src and dest may be identical and it will work. rb, gb, bb are 0-65535
|
|
* in linear monitor output photon space. alpha 255 means full image no background.
|
|
*/
|
|
/* We assume unsigned short holds at least 16 bits. */
|
|
void agx_and_uc_64_to_48(unsigned short *my_restrict dest,
|
|
const unsigned short *my_restrict src, size_t length, float red_gamma,
|
|
float green_gamma, float blue_gamma, unsigned short rb,
|
|
unsigned short gb, unsigned short bb)
|
|
{
|
|
float_double r,g,b;
|
|
unsigned short alpha, calpha;
|
|
unsigned short ri,gi,bi;
|
|
const float_double inv_65535=(float_double)(1/65535.);
|
|
|
|
for (;length;length--)
|
|
{
|
|
r=src[0];
|
|
g=src[1];
|
|
b=src[2];
|
|
alpha=src[3];
|
|
src+=4;
|
|
r*=inv_65535;
|
|
g*=inv_65535;
|
|
b*=inv_65535;
|
|
r=fd_pow(r,red_gamma);
|
|
g=fd_pow(g,green_gamma);
|
|
b=fd_pow(b,blue_gamma);
|
|
ri=(unsigned short)(r*65535+(float_double)0.5);
|
|
gi=(unsigned short)(g*65535+(float_double)0.5);
|
|
bi=(unsigned short)(b*65535+(float_double)0.5);
|
|
cmd_limit_16(ri);
|
|
cmd_limit_16(gi);
|
|
cmd_limit_16(bi);
|
|
if (((alpha+1)&65535)>=2) {
|
|
calpha=65535-alpha;
|
|
dest[0]=(unsigned short)((ri*(unsigned)alpha+(unsigned)calpha*rb+32767U)/65535U);
|
|
dest[1]=(unsigned short)((gi*(unsigned)alpha+(unsigned)calpha*gb+32767U)/65535U);
|
|
dest[2]=(unsigned short)((bi*(unsigned)alpha+(unsigned)calpha*bb+32767U)/65535U);
|
|
}else{
|
|
if (alpha) {
|
|
dest[0]=ri;
|
|
dest[1]=gi;
|
|
dest[2]=bi;
|
|
}else{
|
|
dest[0]=rb;
|
|
dest[1]=gb;
|
|
dest[2]=bb;
|
|
}
|
|
}
|
|
dest+=3;
|
|
}
|
|
}
|
|
|
|
/* src is a block of four-bytes RGBA. All bytes are gamma corrected. length is
|
|
* number of pixels. output is input powered to the given gamma, passed into
|
|
* dest. src and dest may be identical and it will work. rb, gb, bb are 0-65535
|
|
* in linear monitor output photon space. alpha 255 means full image no background.
|
|
* We assume unsigned short holds at least 16 bits. */
|
|
void agx_and_uc_64_to_48_table(unsigned short *my_restrict dest,
|
|
const unsigned short *my_restrict src, size_t length, unsigned short *my_restrict gamma_table,
|
|
unsigned short rb, unsigned short gb, unsigned short bb)
|
|
{
|
|
unsigned short alpha, calpha;
|
|
unsigned short ri,gi,bi;
|
|
|
|
for (;length;length--)
|
|
{
|
|
ri=gamma_table[*src];
|
|
gi=gamma_table[src[1]+65536];
|
|
bi=gamma_table[src[2]+131072];
|
|
alpha=src[3];
|
|
src+=4;
|
|
if (((alpha+1)&65535)>=2) {
|
|
calpha=65535-alpha;
|
|
dest[0]=(ri*alpha+calpha*rb+32767)/65535;
|
|
dest[1]=(gi*alpha+calpha*gb+32767)/65535;
|
|
dest[2]=(bi*alpha+calpha*bb+32767)/65535;
|
|
}else{
|
|
if (alpha) {
|
|
dest[0]=ri;
|
|
dest[1]=gi;
|
|
dest[2]=bi;
|
|
}else{
|
|
dest[0]=rb;
|
|
dest[1]=gb;
|
|
dest[2]=bb;
|
|
}
|
|
}
|
|
dest+=3;
|
|
}
|
|
}
|
|
|
|
/* src is a block of three-bytes. All bytes are gamma corrected. length is
|
|
* number of triplets. output is input powered to the given gamma, passed into
|
|
* dest. src and dest may be identical and it will work.
|
|
* We assume unsigned short holds at least 16 bits. */
|
|
void agx_48_to_48(unsigned short *my_restrict dest,
|
|
const unsigned short *my_restrict src, size_t length, float red_gamma,
|
|
float green_gamma, float blue_gamma)
|
|
{
|
|
float_double a;
|
|
const float_double inv_65535=(float_double)(1/65535.);
|
|
|
|
for (;length;length--,src+=3,dest+=3)
|
|
{
|
|
a=src[0];
|
|
a*=inv_65535;
|
|
a=fd_pow(a,red_gamma);
|
|
dest[0]=(unsigned short)((a*65535)+(float_double)0.5);
|
|
cmd_limit_16(dest[0]);
|
|
a=src[1];
|
|
a*=inv_65535;
|
|
a=fd_pow(a,green_gamma);
|
|
dest[1]=(unsigned short)((a*65535)+(float_double)0.5);
|
|
cmd_limit_16(dest[1]);
|
|
a=src[2];
|
|
a*=inv_65535;
|
|
a=fd_pow(a,blue_gamma);
|
|
dest[2]=(unsigned short)((a*65535)+(float_double)0.5);
|
|
cmd_limit_16(dest[2]);
|
|
}
|
|
}
|
|
|
|
/* src is a block of three-bytes. All bytes are gamma corrected. length is
|
|
* number of triples. output is input powered to the given gamma, passed into
|
|
* dest. src and dest may be identical and it will work.
|
|
* We assume unsigned short holds at least 16 bits. */
|
|
void agx_48_to_48_table(unsigned short *my_restrict dest,
|
|
const unsigned short *my_restrict src, size_t length, unsigned short *my_restrict table)
|
|
{
|
|
for (;length;length--,src+=3,dest+=3)
|
|
{
|
|
dest[0]=table[*src];
|
|
dest[1]=table[src[1]+65536];
|
|
dest[2]=table[src[2]+131072];
|
|
}
|
|
}
|
|
|
|
/* src is a block of three-bytes. All bytes are gamma corrected. length is
|
|
* number of triples. output is input powered to the given gamma, passed into
|
|
* dest. src and dest may be identical and it will work.
|
|
* We assume unsigned short holds at least 16 bits. */
|
|
void agx_24_to_48(unsigned short *my_restrict dest, const unsigned char *my_restrict src,
|
|
size_t length, float red_gamma, float green_gamma, float
|
|
blue_gamma)
|
|
{
|
|
float_double a;
|
|
const float_double inv_255=(float_double)(1/255.);
|
|
|
|
for (;length;length--,src+=3,dest+=3)
|
|
{
|
|
a=*src;
|
|
a*=inv_255;
|
|
a=fd_pow(a,red_gamma);
|
|
dest[0]=(unsigned short)((a*65535)+(float_double)0.5);
|
|
cmd_limit_16(dest[0]);
|
|
a=src[1];
|
|
a*=inv_255;
|
|
a=fd_pow(a,green_gamma);
|
|
dest[1]=(unsigned short)((a*65535)+(float_double)0.5);
|
|
cmd_limit_16(dest[1]);
|
|
a=src[2];
|
|
a*=inv_255;
|
|
a=fd_pow(a,blue_gamma);
|
|
dest[2]=(unsigned short)((a*65535)+(float_double)0.5);
|
|
cmd_limit_16(dest[2]);
|
|
}
|
|
}
|
|
|
|
/* Allocates new gamma_table and fills it with mapping 8 bits ->
|
|
* power to user_gamma/cimg->*_gamma -> 16 bits
|
|
* We assume unsigned short holds at least 16 bits. */
|
|
void make_gamma_table(struct cached_image *cimg)
|
|
{
|
|
float_double rg=(float_double)((float_double)(user_gamma tcc_hack) / cimg->red_gamma);
|
|
float_double gg=(float_double)((float_double)(user_gamma tcc_hack) / cimg->green_gamma);
|
|
float_double bg=(float_double)((float_double)(user_gamma tcc_hack) / cimg->blue_gamma);
|
|
int a;
|
|
unsigned short *ptr_16;
|
|
unsigned short last_val;
|
|
const float_double inv_255=(float_double)(1/255.);
|
|
const float_double inv_65535=(float_double)(1/65535.);
|
|
|
|
if (cimg->buffer_bytes_per_pixel<=4) {
|
|
/* 8-bit */
|
|
ptr_16=mem_alloc(768*sizeof(*(cimg->gamma_table)));
|
|
cimg->gamma_table=ptr_16;
|
|
for (a=0;a<256;a++,ptr_16++) {
|
|
last_val = (unsigned short)(65535*fd_pow((float_double)a*inv_255,rg)+(float_double)0.5);
|
|
cmd_limit_16(last_val);
|
|
*ptr_16 = last_val;
|
|
}
|
|
for (a=0;a<256;a++,ptr_16++) {
|
|
last_val = (unsigned short)(65535*fd_pow((float_double)a*inv_255,gg)+(float_double)0.5);
|
|
cmd_limit_16(last_val);
|
|
*ptr_16 = last_val;
|
|
}
|
|
for (a=0;a<256;a++,ptr_16++) {
|
|
last_val = (unsigned short)(65535*fd_pow((float_double)a*inv_255,bg)+(float_double)0.5);
|
|
cmd_limit_16(last_val);
|
|
*ptr_16 = last_val;
|
|
}
|
|
}else{
|
|
int x_slow_fpu;
|
|
if (gamma_bits != 2) x_slow_fpu = !gamma_bits;
|
|
else x_slow_fpu = slow_fpu == 1;
|
|
|
|
last_val = 0; /* against warning */
|
|
|
|
/* 16-bit */
|
|
ptr_16=mem_alloc(196608*sizeof(*(cimg->gamma_table)));
|
|
cimg->gamma_table=ptr_16;
|
|
for (a=0;a<0x10000;a++,ptr_16++) {
|
|
if (!x_slow_fpu || !(a & 0xff)) {
|
|
last_val = (unsigned short)(65535*fd_pow((float_double)a*inv_65535,rg)+(float_double)0.5);
|
|
cmd_limit_16(last_val);
|
|
}
|
|
*ptr_16 = last_val;
|
|
}
|
|
for (a=0;a<0x10000;a++,ptr_16++) {
|
|
if (!x_slow_fpu || !(a & 0xff)) {
|
|
last_val = (unsigned short)(65535*fd_pow((float_double)a*inv_65535,gg)+(float_double)0.5);
|
|
cmd_limit_16(last_val);
|
|
}
|
|
*ptr_16 = last_val;
|
|
}
|
|
for (a=0;a<0x10000;a++,ptr_16++) {
|
|
if (!x_slow_fpu || !(a & 0xff)) {
|
|
last_val = (unsigned short)(65535*fd_pow((float_double)a*inv_65535,bg)+(float_double)0.5);
|
|
cmd_limit_16(last_val);
|
|
}
|
|
*ptr_16 = last_val;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We assume unsigned short holds at least 16 bits. */
|
|
void agx_24_to_48_table(unsigned short *my_restrict dest, const unsigned char *my_restrict src,
|
|
size_t length, unsigned short *my_restrict table)
|
|
{
|
|
for (;length;length--,src+=3,dest+=3)
|
|
{
|
|
dest[0]=table[src[0]];
|
|
dest[1]=table[src[1]+256];
|
|
dest[2]=table[src[2]+512];
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
/* Input is 0-255 (8-bit). Output is 0-255 (8-bit)*/
|
|
unsigned char ags_8_to_8(unsigned char input, float gamma)
|
|
{
|
|
const float_double inv_255=1/255.;
|
|
return 255*fd_pow((float_double)input*inv_255,gamma)+(float_double)0.5;
|
|
}
|
|
#endif
|
|
|
|
/* Input is 0-255 (8-bit). Output is 0-65535 (16-bit)*/
|
|
/* We assume unsigned short holds at least 16 bits. */
|
|
unsigned short ags_8_to_16(unsigned char input, float gamma)
|
|
{
|
|
float_double a=input;
|
|
unsigned short retval;
|
|
const float_double inv_255=(float_double)(1/255.);
|
|
|
|
a*=inv_255;
|
|
a=fd_pow(a,gamma);
|
|
a*=65535;
|
|
retval = (unsigned short)(a+(float_double)0.5);
|
|
cmd_limit_16(retval);
|
|
return retval;
|
|
}
|
|
|
|
/* Input is 0-65535 (16-bit). Output is 0-255 (8-bit)*/
|
|
/* We assume unsigned short holds at least 16 bits. */
|
|
unsigned char ags_16_to_8(unsigned short input, float gamma)
|
|
{
|
|
const float_double inv_65535=(float_double)(1/65535.);
|
|
return (unsigned char)(fd_pow((float_double)input*inv_65535,gamma)*255+(float_double)0.5);
|
|
}
|
|
|
|
/* Input is 0-65535 (16-bit). Output is 0-255 (8-bit)*/
|
|
unsigned short ags_16_to_16(unsigned short input, float gamma)
|
|
{
|
|
unsigned short retval;
|
|
const float_double inv_65535=(float_double)(1/65535.);
|
|
|
|
retval = (unsigned short)(65535*fd_pow((float_double)input*inv_65535,gamma)+(float_double)0.5);
|
|
cmd_limit_16(retval);
|
|
return retval;
|
|
}
|
|
|
|
#define FONT_NORMAL 1
|
|
#define FONT_BOLD 2
|
|
#define FONT_MONOSPACED 3
|
|
|
|
/* Returns a pointer to a structure describing the letter found or NULL
|
|
* if the letter is not found. Tries all possibilities in the style table
|
|
* before returning NULL.
|
|
*/
|
|
static struct letter *find_stored_letter(struct style *style, int letter_number)
|
|
{
|
|
int tries[3];
|
|
int try;
|
|
if (style->flags & FF_MONOSPACED) {
|
|
tries[0] = FONT_MONOSPACED;
|
|
if (style->flags & FF_BOLD) {
|
|
tries[1] = FONT_BOLD;
|
|
tries[2] = FONT_NORMAL;
|
|
} else {
|
|
tries[1] = FONT_NORMAL;
|
|
tries[2] = FONT_BOLD;
|
|
}
|
|
} else {
|
|
if (style->flags & FF_BOLD) {
|
|
tries[0] = FONT_BOLD;
|
|
tries[1] = FONT_NORMAL;
|
|
tries[2] = FONT_MONOSPACED;
|
|
} else {
|
|
tries[0] = FONT_NORMAL;
|
|
tries[1] = FONT_MONOSPACED;
|
|
tries[2] = FONT_BOLD;
|
|
}
|
|
}
|
|
for (try = 0; try < 3; try++) {
|
|
struct font *font = &font_table[tries[try]];
|
|
int start = font->begin;
|
|
int result;
|
|
#define EQ(v, key) (letter_data[start + v].code == key)
|
|
#define AB(v, key) (letter_data[start + v].code > key)
|
|
BIN_SEARCH(font->length, EQ, AB, letter_number, result);
|
|
if (result >= 0)
|
|
return letter_data + start + result;
|
|
}
|
|
return letter_data + font_table[0].begin;
|
|
}
|
|
|
|
struct read_work {
|
|
const unsigned char *pointer;
|
|
int length;
|
|
};
|
|
|
|
static void read_stored_data(png_structp png_ptr, png_bytep data, png_size_t length)
|
|
{
|
|
struct read_work *work;
|
|
|
|
length=(png_uint_32)length;
|
|
|
|
work=png_get_io_ptr(png_ptr);
|
|
if (length>(png_uint_32)work->length) png_error(png_ptr,"Ran out of input data");
|
|
memcpy(data,work->pointer,length);
|
|
work->length-=(int)length;
|
|
work->pointer+=length;
|
|
}
|
|
|
|
static void my_png_warning(png_structp a, png_const_charp b)
|
|
{
|
|
}
|
|
|
|
static jmp_buf png_error_jump;
|
|
static int png_error_jump_valid = 0;
|
|
|
|
static void my_png_error(png_structp a, png_const_charp error_string)
|
|
{
|
|
if (png_error_jump_valid)
|
|
longjmp(png_error_jump, 1);
|
|
error("Error when loading compiled-in font: %s.", error_string);
|
|
}
|
|
|
|
/* Loads width and height of the PNG (nothing is scaled). Style table is
|
|
* already incremented.
|
|
*/
|
|
static void load_metric(struct style *style, int char_number, int *x, int *y)
|
|
{
|
|
struct letter *l;
|
|
|
|
#ifdef HAVE_FREETYPE
|
|
if (style->ft_face) {
|
|
*y = style->height;
|
|
if (!freetype_load_metric_cached(style, char_number, x, *y))
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
l = find_stored_letter(style, char_number);
|
|
if (!l) {
|
|
*x = 0;
|
|
*y = 0;
|
|
} else {
|
|
*x = l->xsize;
|
|
*y = l->ysize;
|
|
}
|
|
return;
|
|
}
|
|
|
|
#ifdef PNG_USER_MEM_SUPPORTED
|
|
void *my_png_alloc(png_structp png_ptr, png_size_t size)
|
|
{
|
|
void *ptr = mem_alloc_mayfail(size);
|
|
return ptr;
|
|
}
|
|
void my_png_free(png_structp png_ptr, void *ptr)
|
|
{
|
|
if (ptr) mem_free(ptr);
|
|
}
|
|
#endif
|
|
|
|
static void dip_set_gamma(png_structp png_ptr, png_infop info_ptr)
|
|
{
|
|
png_error_jump_valid = 1;
|
|
if (!setjmp(png_error_jump)) {
|
|
double gamma;
|
|
if (png_get_gAMA(png_ptr, info_ptr, &gamma))
|
|
png_set_gamma(png_ptr, 1.0, gamma);
|
|
else
|
|
png_set_gamma(png_ptr, 1.0, sRGB_gamma);
|
|
}
|
|
png_error_jump_valid = 0;
|
|
}
|
|
|
|
/* The data tha fall out of this function express this: 0 is paper. 255 is ink. 34
|
|
* is 34/255ink+(255-34)paper. No gamma is involved in this formula, as you can see.
|
|
* The multiplications and additions take place in photon space.
|
|
*/
|
|
static void load_char(unsigned char **dest, int *x, int *y, const unsigned char *png_data, int png_length)
|
|
{
|
|
png_structp png_ptr;
|
|
png_infop info_ptr;
|
|
int y1,number_of_passes;
|
|
unsigned char **ptrs;
|
|
struct read_work work;
|
|
|
|
work.pointer = png_data;
|
|
work.length = png_length;
|
|
|
|
retry1:
|
|
#ifdef PNG_USER_MEM_SUPPORTED
|
|
png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING,
|
|
NULL, my_png_error, my_png_warning,
|
|
NULL, my_png_alloc, my_png_free);
|
|
#else
|
|
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
|
|
NULL, my_png_error, my_png_warning);
|
|
#endif
|
|
if (!png_ptr) {
|
|
if (out_of_memory(0, NULL, 0)) goto retry1;
|
|
fatal_exit("png_create_read_struct failed");
|
|
}
|
|
retry2:
|
|
info_ptr = png_create_info_struct(png_ptr);
|
|
if (!info_ptr) {
|
|
if (out_of_memory(0, NULL, 0)) goto retry2;
|
|
fatal_exit("png_create_info_struct failed");
|
|
}
|
|
png_set_read_fn(png_ptr, &work, (png_rw_ptr)read_stored_data);
|
|
png_read_info(png_ptr, info_ptr);
|
|
*x = (int)png_get_image_width(png_ptr, info_ptr);
|
|
*y = (int)png_get_image_height(png_ptr, info_ptr);
|
|
dip_set_gamma(png_ptr, info_ptr);
|
|
{
|
|
int bit_depth;
|
|
int color_type;
|
|
|
|
color_type = png_get_color_type(png_ptr, info_ptr);
|
|
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
|
|
if (color_type == PNG_COLOR_TYPE_GRAY) {
|
|
if (bit_depth < 8) {
|
|
png_set_expand(png_ptr);
|
|
}
|
|
if (bit_depth == 16) {
|
|
png_set_strip_16(png_ptr);
|
|
}
|
|
}
|
|
if (color_type == PNG_COLOR_TYPE_PALETTE) {
|
|
png_set_expand(png_ptr);
|
|
#ifdef HAVE_PNG_SET_RGB_TO_GRAY
|
|
png_set_rgb_to_gray(png_ptr, 1, -1, -1);
|
|
#else
|
|
goto end;
|
|
#endif
|
|
}
|
|
if (color_type & PNG_COLOR_MASK_ALPHA) {
|
|
png_set_strip_alpha(png_ptr);
|
|
}
|
|
if (color_type == PNG_COLOR_TYPE_RGB ||
|
|
color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
|
|
#ifdef HAVE_PNG_SET_RGB_TO_GRAY
|
|
png_set_rgb_to_gray(png_ptr, 1, -1, -1);
|
|
#else
|
|
goto end;
|
|
#endif
|
|
}
|
|
}
|
|
/* If the depth is different from 8 bits/gray, make the libpng expand
|
|
* it to 8 bit gray.
|
|
*/
|
|
number_of_passes = png_set_interlace_handling(png_ptr);
|
|
png_read_update_info(png_ptr, info_ptr);
|
|
if (*x && (unsigned)*x * (unsigned)*y / (unsigned)*x != (unsigned)*y) overalloc();
|
|
if ((unsigned)*x * (unsigned)*y > MAXINT) overalloc();
|
|
*dest = mem_alloc(*x * *y);
|
|
if ((unsigned)*y > MAXINT / sizeof(*ptrs)) overalloc();
|
|
ptrs = mem_alloc(*y * sizeof(*ptrs));
|
|
for (y1 = 0; y1 < *y; y1++) ptrs[y1] = *dest + *x * y1;
|
|
for (;number_of_passes; number_of_passes--) {
|
|
png_read_rows(png_ptr, ptrs, NULL, *y);
|
|
}
|
|
png_read_end(png_ptr, NULL);
|
|
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
|
mem_free(ptrs);
|
|
return;
|
|
#ifndef HAVE_PNG_SET_RGB_TO_GRAY
|
|
end:
|
|
if (*x && (unsigned)*x * (unsigned)*y / (unsigned)*x != (unsigned)*y) overalloc();
|
|
if ((unsigned)*x * (unsigned)*y > MAXINT) overalloc();
|
|
*dest=mem_calloc(*x * *y));
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
/* Like load_char, but we dictate the y.
|
|
*/
|
|
static void load_scaled_char(unsigned char **dest, int *x, int y, const unsigned char *png_data, int png_length, struct style *style)
|
|
{
|
|
unsigned char *interm;
|
|
unsigned char *interm2;
|
|
unsigned char *i2ptr, *dptr;
|
|
int ix, iy, y0, x0, c;
|
|
float conv0, conv1, sharpness,contrast;
|
|
|
|
load_char(&interm, &ix, &iy, png_data, png_length);
|
|
if (style->mono_space >= 0)
|
|
*x = compute_width(style->mono_space, style->mono_height, y);
|
|
else
|
|
*x = compute_width(ix, iy, y);
|
|
if (display_optimize) *x *= 3;
|
|
scale_gray(interm, ix, iy, dest, *x, y);
|
|
if (y > 32 || y <= 0) return; /* No convolution */
|
|
ix = *x + 2; /* There is one-pixel border around */
|
|
iy = y + 2;
|
|
if (ix && (unsigned)ix * (unsigned)iy / (unsigned)ix != (unsigned)iy) overalloc();
|
|
if ((unsigned)ix * (unsigned)iy > MAXINT) overalloc();
|
|
interm2 = mem_alloc(ix * iy);
|
|
i2ptr = interm2 + ix + 1;
|
|
dptr = *dest;
|
|
memset(interm2, 0, ix);
|
|
memset(interm2 + (iy - 1) * ix, 0, ix);
|
|
for (y0 = y; y0; y0--) {
|
|
i2ptr[-1] = 0;
|
|
memcpy(i2ptr, dptr, *x);
|
|
i2ptr[*x] = 0;
|
|
i2ptr += ix;
|
|
dptr += *x;
|
|
}
|
|
i2ptr = interm2 + ix + 1;
|
|
dptr = *dest;
|
|
|
|
/* Determine the sharpness and contrast */
|
|
sharpness = fancy_constants[2 * y - 2];
|
|
contrast = fancy_constants[2 * y - 1];
|
|
|
|
/* Compute the matrix constants from contrast and sharpness */
|
|
conv0 = (1 + sharpness) * contrast;
|
|
conv1 = (float)(-sharpness * (float)0.25 * contrast);
|
|
|
|
for (y0 = y; y0; y0--) {
|
|
for (x0 = *x; x0; x0--) {
|
|
/* Convolution */
|
|
c=(int)((*i2ptr * conv0) + i2ptr[-ix] * conv1 + i2ptr[-1] * conv1 + i2ptr[1] * conv1 + i2ptr[ix] * conv1 + (float)0.5);
|
|
if (c & ~255) c = c < 0 ? 0 : 255;
|
|
*dptr = (unsigned char)c;
|
|
dptr++;
|
|
i2ptr++;
|
|
}
|
|
i2ptr += 2;
|
|
}
|
|
mem_free(interm2);
|
|
}
|
|
|
|
static struct font_cache_entry *locked_color_entry = NULL;
|
|
|
|
/* Adds required entry into font_cache and returns pointer to the entry.
|
|
*/
|
|
ATTR_NOINLINE static struct font_cache_entry *supply_color_cache_entry(struct style *style, struct letter *letter, int char_number)
|
|
{
|
|
int found_x, found_y;
|
|
unsigned char *found_data;
|
|
struct font_cache_entry *neww;
|
|
unsigned short *primary_data;
|
|
unsigned short red, green, blue;
|
|
unsigned bytes_consumed;
|
|
|
|
found_y = style->height;
|
|
#ifdef HAVE_FREETYPE
|
|
if (!letter) {
|
|
if (freetype_type_character(style, char_number, &found_data, &found_x, found_y))
|
|
return NULL;
|
|
} else
|
|
#endif
|
|
{
|
|
load_scaled_char(&found_data, &found_x, found_y, letter->begin, letter->length, style);
|
|
}
|
|
|
|
neww = mem_alloc(sizeof(struct font_cache_entry));
|
|
locked_color_entry = neww;
|
|
neww->bitmap.x = found_x;
|
|
neww->bitmap.y = found_y;
|
|
neww->r0 = style->r0;
|
|
neww->g0 = style->g0;
|
|
neww->b0 = style->b0;
|
|
neww->r1 = style->r1;
|
|
neww->g1 = style->g1;
|
|
neww->b1 = style->b1;
|
|
neww->flags = style->flags;
|
|
neww->char_number = char_number;
|
|
neww->mono_space = style->mono_space;
|
|
neww->mono_height = style->mono_height;
|
|
#ifdef HAVE_FREETYPE
|
|
neww->font = freetype_get_allocated_font_name(style);
|
|
#endif
|
|
|
|
if (neww->bitmap.x && (unsigned)neww->bitmap.x * (unsigned)neww->bitmap.y / (unsigned)neww->bitmap.x != (unsigned)neww->bitmap.y) overalloc();
|
|
if ((unsigned)neww->bitmap.x * (unsigned)neww->bitmap.y > MAXINT / 3 / sizeof(*primary_data)) overalloc();
|
|
primary_data = mem_alloc(3 * neww->bitmap.x * neww->bitmap.y * sizeof(*primary_data));
|
|
|
|
/* We assume the gamma of HTML styles is in sRGB space */
|
|
round_color_sRGB_to_48(&red, &green, &blue, (style->r0 << 16) | (style->g0 << 8) | style->b0);
|
|
mix_two_colors(primary_data, found_data,
|
|
found_x * found_y,
|
|
red, green, blue,
|
|
ags_8_to_16(style->r1, (float)((float)(user_gamma tcc_hack) / (float)sRGB_gamma)),
|
|
ags_8_to_16(style->g1, (float)((float)(user_gamma tcc_hack) / (float)sRGB_gamma)),
|
|
ags_8_to_16(style->b1, (float)((float)(user_gamma tcc_hack) / (float)sRGB_gamma))
|
|
);
|
|
if (display_optimize) {
|
|
/* A correction for LCD */
|
|
neww->bitmap.x /= 3;
|
|
decimate_3(&primary_data, neww->bitmap.x, neww->bitmap.y);
|
|
}
|
|
/* We have a buffer with photons */
|
|
if (drv->get_empty_bitmap(&neww->bitmap))
|
|
goto skip_dither;
|
|
if (dither_letters)
|
|
dither(primary_data, &neww->bitmap);
|
|
else
|
|
(*round_fn)(primary_data, &neww->bitmap);
|
|
skip_dither:
|
|
mem_free(primary_data);
|
|
drv->register_bitmap(&neww->bitmap);
|
|
|
|
mem_free(found_data);
|
|
|
|
bytes_consumed = neww->bitmap.x * neww->bitmap.y * (drv->depth&7);
|
|
/* Number of bytes per pixel in passed bitmaps */
|
|
bytes_consumed += (int)sizeof(*neww);
|
|
bytes_consumed += (int)sizeof(struct lru_entry);
|
|
|
|
#ifdef HAVE_FREETYPE
|
|
if (!letter) {
|
|
lru_insert(&font_cache, neww, &freetype_cache[char_number & (sizeof_freetype_cache - 1)], bytes_consumed);
|
|
} else
|
|
#endif
|
|
{
|
|
lru_insert(&font_cache, neww, &letter->color_list, bytes_consumed);
|
|
}
|
|
|
|
return neww;
|
|
}
|
|
|
|
static int destroy_font_cache_bottom(void)
|
|
{
|
|
struct font_cache_entry *bottom;
|
|
bottom = lru_get_bottom(&font_cache);
|
|
if (!bottom) return 0;
|
|
if (bottom == locked_color_entry) return 0;
|
|
#ifdef HAVE_FREETYPE
|
|
if (bottom->font) mem_free(bottom->font);
|
|
#endif
|
|
drv->unregister_bitmap(&bottom->bitmap);
|
|
mem_free(bottom);
|
|
lru_destroy_bottom(&font_cache);
|
|
return 1;
|
|
}
|
|
|
|
/* Prunes the cache to comply with maximum size */
|
|
static int prune_font_cache(void)
|
|
{
|
|
int r = 0;
|
|
|
|
while (font_cache.bytes > (unsigned)font_cache_size) {
|
|
if (destroy_font_cache_bottom()) {
|
|
r = 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/* Prints a letter to the specified position and
|
|
* returns the width of the printed letter */
|
|
static inline int print_letter(struct graphics_device *device, int x, int y, struct style *style, int char_number)
|
|
|
|
{
|
|
int xw;
|
|
struct font_cache_entry *found;
|
|
struct font_cache_entry templat;
|
|
struct letter *letter;
|
|
|
|
templat.r0 = style->r0;
|
|
templat.r1 = style->r1;
|
|
templat.g0 = style->g0;
|
|
templat.g1 = style->g1;
|
|
templat.b0 = style->b0;
|
|
templat.b1 = style->b1;
|
|
templat.flags = style->flags;
|
|
templat.char_number = char_number;
|
|
templat.mono_space = style->mono_space;
|
|
templat.mono_height = style->mono_height;
|
|
templat.bitmap.y = style->height;
|
|
#ifdef HAVE_FREETYPE
|
|
templat.font = freetype_get_allocated_font_name(style);
|
|
#endif
|
|
|
|
#ifdef HAVE_FREETYPE
|
|
if (style->ft_face) {
|
|
found = lru_lookup(&font_cache, &templat, &freetype_cache[char_number & (sizeof_freetype_cache - 1)]);
|
|
letter = NULL;
|
|
} else
|
|
bypass_freetype:
|
|
#endif
|
|
{
|
|
/* Find a suitable letter */
|
|
letter = find_stored_letter(style, char_number);
|
|
#ifdef DEBUG
|
|
if (!letter) internal_error("print_letter could not find a letter - even not the blotch!");
|
|
#endif /* #ifdef DEBUG */
|
|
found = lru_lookup(&font_cache, &templat, &letter->color_list);
|
|
}
|
|
|
|
if (!found) {
|
|
found = supply_color_cache_entry(style, letter, char_number);
|
|
#ifdef HAVE_FREETYPE
|
|
if (!found)
|
|
goto bypass_freetype;
|
|
#endif
|
|
} else {
|
|
locked_color_entry = found;
|
|
}
|
|
#ifdef HAVE_FREETYPE
|
|
if (templat.font) mem_free(templat.font);
|
|
#endif
|
|
#if 0
|
|
drv->fill_area(device, x, y, x + found->bitmap.x, y + found->bitmap.y, drv->get_color(0xff0000));
|
|
if (drv->flush) drv->flush(device);
|
|
portable_sleep(1);
|
|
#endif
|
|
drv->draw_bitmap(device, &found->bitmap, x, y);
|
|
xw = found->bitmap.x;
|
|
if (locked_color_entry != found) internal_error("bad letter lock");
|
|
locked_color_entry = NULL;
|
|
prune_font_cache();
|
|
return xw;
|
|
}
|
|
|
|
/* Must return values that are:
|
|
* >=0
|
|
* <=height
|
|
* at least 1 apart
|
|
* Otherwise g_print_text will print nonsense (but won't segfault)
|
|
*/
|
|
static void get_line_pos(int height, int *top, int *bottom, unsigned style)
|
|
{
|
|
int thickness, baseline;
|
|
thickness=(height+15)/16;
|
|
if (style == FF_STRIKE)
|
|
baseline=height/2 - height/12;
|
|
else
|
|
baseline=height/7;
|
|
if (baseline<=0) baseline=1;
|
|
if (thickness>baseline) thickness=baseline;
|
|
*top=height-baseline;
|
|
*bottom=*top+thickness;
|
|
}
|
|
|
|
/* *width will be advanced by the width of the text */
|
|
void g_print_text(struct graphics_device *device, int x, int y, struct style *style, unsigned char *text, int *width)
|
|
{
|
|
int top_line, bottom_line, original_width, my_width;
|
|
unsigned char original_flags, line_style;
|
|
struct rect saved_clip;
|
|
|
|
if (y + style->height <= device->clip.y1 || y >= device->clip.y2)
|
|
goto o;
|
|
if (style->flags & FF_UNDERLINE || style->flags & FF_STRIKE) {
|
|
if (drv->start_draw) drv->start_draw(device);
|
|
/* Underline or strike */
|
|
if (!width) {
|
|
width = &my_width;
|
|
*width = 0;
|
|
}
|
|
original_flags = style->flags;
|
|
original_width = *width;
|
|
line_style = style->flags & FF_UNDERLINE ? FF_UNDERLINE : FF_STRIKE;
|
|
style->flags &= ~line_style;
|
|
get_line_pos(style->height, &top_line, &bottom_line, line_style);
|
|
restrict_clip_area(device, &saved_clip, 0, 0, device->size.x2, y + top_line);
|
|
g_print_text(device, x, y, style, text, width);
|
|
set_clip_area(device, &saved_clip);
|
|
if (bottom_line - top_line == 1) {
|
|
/* Line */
|
|
drv->draw_hline(device, x, y + top_line, safe_add(x, *width) - original_width, style->line_color);
|
|
} else {
|
|
/* Area */
|
|
drv->fill_area(device, x, y + top_line, safe_add(x, *width) - original_width, y + bottom_line, style->line_color);
|
|
}
|
|
if (bottom_line < style->height) {
|
|
/* Do the bottom half only if the line is above
|
|
* the bottom of the letters.
|
|
*/
|
|
*width = original_width;
|
|
restrict_clip_area(device, &saved_clip, 0, y + bottom_line, device->size.x2, device->size.y2);
|
|
g_print_text(device, x, y, style, text, width);
|
|
set_clip_area(device, &saved_clip);
|
|
}
|
|
style->flags = original_flags;
|
|
if (drv->end_draw) drv->end_draw(device);
|
|
return;
|
|
}
|
|
while (*text) {
|
|
int p;
|
|
int u;
|
|
GET_UTF_8(text, u);
|
|
/* 00-09, 0b-1f, 80, 81, 84, 86-9f ignorovat
|
|
* 0a = LF
|
|
* 82 = ' '
|
|
* 83 = nobrk
|
|
* 85 = radkovy zlom
|
|
* a0 = NBSP
|
|
* ad = soft hyphen
|
|
*/
|
|
#if 0
|
|
if ( (u>=0x00&&u<=0x09)||
|
|
(u>=0x0b&&u<=0x1f)||
|
|
u==0x80||
|
|
u==0x82||
|
|
u==0x84||
|
|
(u>=0x86&&u<=0x9f)
|
|
)continue;
|
|
if (u==0x82)u=' ';
|
|
#endif
|
|
/* stare Mikulasovo patchovani, musim to opravit -- Brain */
|
|
if (!u || u == 0xad)
|
|
continue;
|
|
if (u == 0x01 || u == 0xa0) u = ' ';
|
|
p = print_letter(device, x, y, style, u);
|
|
x += p;
|
|
if (width) {
|
|
*width = safe_add(*width, p);
|
|
continue;
|
|
}
|
|
if (x >= device->clip.x2)
|
|
return;
|
|
}
|
|
return;
|
|
o:
|
|
if (width) {
|
|
int qw = g_text_width(style, text);
|
|
*width = safe_add(*width, qw);
|
|
}
|
|
}
|
|
|
|
/* 0=equality 1=inequality */
|
|
static int compare_font_entries(void *entry, void *templat)
|
|
{
|
|
struct font_cache_entry *e1 = entry;
|
|
struct font_cache_entry *e2 = templat;
|
|
|
|
if (e1->r0 != e2->r0) return 1;
|
|
if (e1->g0 != e2->g0) return 1;
|
|
if (e1->b0 != e2->b0) return 1;
|
|
if (e1->r1 != e2->r1) return 1;
|
|
if (e1->g1 != e2->g1) return 1;
|
|
if (e1->b1 != e2->b1) return 1;
|
|
if (e1->flags != e2->flags) return 1;
|
|
if (e1->char_number != e2->char_number) return 1;
|
|
if (e1->mono_space != e2->mono_space) return 1;
|
|
if (e1->mono_space >= 0) {
|
|
if (e1->mono_height != e2->mono_height) return 1;
|
|
}
|
|
if (e1->bitmap.y != e2->bitmap.y) return 1;
|
|
#ifdef HAVE_FREETYPE
|
|
if (!e1->font != !e2->font) return 1;
|
|
if (e1->font && strcmp(cast_const_char e1->font, cast_const_char e2->font)) return 1;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/* If the cache already exists, it is destroyed and reallocated. If you call it with the same
|
|
* size argument, only a cache flush will yield.
|
|
*/
|
|
static void init_font_cache(void)
|
|
{
|
|
lru_init(&font_cache, &compare_font_entries);
|
|
}
|
|
|
|
/* Ensures there are no lru_entry objects allocated - destroys them.
|
|
* Also destroys the bitmaps asociated with them. Does not destruct the
|
|
font_cache per se.
|
|
*/
|
|
static void destroy_font_cache(void)
|
|
{
|
|
while (destroy_font_cache_bottom())
|
|
;
|
|
if (lru_get_bottom(&font_cache))
|
|
internal_error("destroy_font_cache: cache not freed due to locks");
|
|
}
|
|
|
|
/* Returns 0 in case the char is not found. */
|
|
static inline int g_get_width(struct style *style, unsigned charcode)
|
|
{
|
|
int x, y, width;
|
|
|
|
if (!charcode || charcode == 0xad) return 0;
|
|
if (charcode == 0x01 || charcode == 0xa0) charcode = ' ';
|
|
if (style->mono_space >= 0) {
|
|
x = style->mono_space;
|
|
y = style->mono_height;
|
|
} else load_metric(style, charcode, &x, &y);
|
|
if (!(x && y)) width = 0;
|
|
else width = compute_width(x, y, style->height);
|
|
return width;
|
|
}
|
|
|
|
int g_text_width(struct style *style, unsigned char *text)
|
|
{
|
|
int w = 0;
|
|
while (*text) {
|
|
int u;
|
|
int qw;
|
|
GET_UTF_8(text, u);
|
|
qw = g_get_width(style, u);
|
|
w = safe_add(w, qw);
|
|
}
|
|
return w;
|
|
}
|
|
|
|
int g_char_width(struct style *style, unsigned charcode)
|
|
{
|
|
return g_get_width(style, charcode);
|
|
}
|
|
|
|
int g_wrap_text(struct wrap_struct *w)
|
|
{
|
|
unsigned char *init_text = w->text;
|
|
while (*w->text) {
|
|
int u;
|
|
int s;
|
|
unsigned char *l_text = w->text;
|
|
if (*l_text == ' ') w->last_wrap = l_text,
|
|
w->last_wrap_obj = w->obj;
|
|
GET_UTF_8(w->text, u);
|
|
if (!u) continue;
|
|
s = g_get_width(w->style, u);
|
|
w->pos = safe_add(w->pos, s);
|
|
if (w->pos <= w->width) {
|
|
c:
|
|
if (u != 0xad || *w->text == ' ' || w->force_break) continue;
|
|
s = g_char_width(w->style, '-');
|
|
if (safe_add(w->pos, s) <= w->width || (!w->last_wrap && !w->last_wrap_obj)) {
|
|
w->last_wrap = l_text;
|
|
w->last_wrap_obj = w->obj;
|
|
continue;
|
|
}
|
|
}
|
|
if (w->force_break && !w->last_wrap && l_text != init_text) {
|
|
w->last_wrap = l_text;
|
|
w->last_wrap_obj = w->obj;
|
|
}
|
|
if (!w->last_wrap && !w->last_wrap_obj) goto c;
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void update_aspect(void)
|
|
{
|
|
aspect = (int)(65536 * bfu_aspect + 0.5);
|
|
destroy_font_cache();
|
|
}
|
|
|
|
void flush_bitmaps(int flush_font, int flush_images, int redraw_all)
|
|
{
|
|
if (flush_font)
|
|
destroy_font_cache();
|
|
if (flush_images)
|
|
gamma_stamp++;
|
|
if (redraw_all)
|
|
cls_redraw_all_terminals();
|
|
}
|
|
|
|
my_uintptr_t fontcache_info(int type)
|
|
{
|
|
switch (type) {
|
|
case CI_BYTES:
|
|
return font_cache.bytes;
|
|
case CI_FILES:
|
|
return font_cache.items;
|
|
default:
|
|
internal_error("fontcache_info: query %d", type);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int shrink_font_cache(int u)
|
|
{
|
|
int freed_something = 0;
|
|
int has_something;
|
|
if (u == SH_CHECK_QUOTA) {
|
|
freed_something = prune_font_cache();
|
|
}
|
|
if (u == SH_FREE_ALL) {
|
|
while (destroy_font_cache_bottom())
|
|
freed_something = 1;
|
|
}
|
|
if (u == SH_FREE_SOMETHING) {
|
|
freed_something = destroy_font_cache_bottom();
|
|
}
|
|
has_something = !!lru_get_bottom(&font_cache);
|
|
return (freed_something ? ST_SOMETHING_FREED : 0) |
|
|
(has_something ? 0 : ST_CACHE_EMPTY);
|
|
}
|
|
|
|
void init_dip(void)
|
|
{
|
|
init_font_cache();
|
|
update_aspect();
|
|
register_cache_upcall(shrink_font_cache, MF_GPI, cast_uchar "fontcache");
|
|
}
|
|
|
|
struct style *g_invert_style(struct style *old)
|
|
{
|
|
struct style *st;
|
|
st = mem_alloc(sizeof(struct style));
|
|
st->refcount = 1;
|
|
st->r0 = old->r1;
|
|
st->g0 = old->g1;
|
|
st->b0 = old->b1;
|
|
st->r1 = old->r0;
|
|
st->g1 = old->g0;
|
|
st->b1 = old->b0;
|
|
st->height = old->height;
|
|
st->flags = old->flags;
|
|
#ifdef HAVE_FREETYPE
|
|
st->ft_face = old->ft_face;
|
|
#endif
|
|
if (st->flags & FF_UNDERLINE || st->flags & FF_STRIKE) {
|
|
/* We have to get a foreground color for underlining and
|
|
* striking
|
|
*/
|
|
st->line_color = dip_get_color_sRGB((st->r1 << 16) | (st->g1 << 8) | (st->b1));
|
|
}
|
|
st->mono_space = old->mono_space;
|
|
st->mono_height = old->mono_height;
|
|
return st;
|
|
}
|
|
|
|
int hack_rgb(int rgb)
|
|
{
|
|
if (((drv->depth >> 3) & 0x1f) <= 4) {
|
|
int r,g,b;
|
|
r=(rgb>>16)&255;
|
|
g=(rgb>>8)&255;
|
|
b=rgb&255;
|
|
if (r == g && g == b) {
|
|
if (r < 128) return 0;
|
|
else return 0xffffff;
|
|
}
|
|
}
|
|
return rgb;
|
|
}
|
|
|
|
/* Never returns NULL. */
|
|
struct style *g_get_style_font(int fg, int bg, int size, int fflags, unsigned char *font)
|
|
{
|
|
struct style *st;
|
|
|
|
bg = hack_rgb(bg);
|
|
|
|
st = mem_alloc(sizeof(struct style));
|
|
st->refcount = 1;
|
|
st->r0 = bg >> 16;
|
|
st->g0 = (bg >> 8) & 255;
|
|
st->b0 = bg & 255;
|
|
st->r1 = fg >> 16;
|
|
st->g1 = (fg >> 8) & 255;
|
|
st->b1 = fg & 255;
|
|
if (size <= 0) size = 1;
|
|
st->height = size;
|
|
st->flags = (unsigned char)fflags;
|
|
#ifdef HAVE_FREETYPE
|
|
if (!font)
|
|
st->ft_face = freetype_flags_to_face(fflags);
|
|
else
|
|
st->ft_face = freetype_get_font(font);
|
|
#endif
|
|
if (fflags & FF_UNDERLINE || fflags & FF_STRIKE) {
|
|
/* We have to get a foreground color for underlining and
|
|
* striking
|
|
*/
|
|
st->line_color = dip_get_color_sRGB(fg);
|
|
}
|
|
if (fflags & FF_MONOSPACED)
|
|
load_metric(st, ' ', &st->mono_space, &st->mono_height);
|
|
else
|
|
st->mono_space = -1;
|
|
|
|
return st;
|
|
}
|
|
|
|
struct style *g_get_style(int fg, int bg, int size, int fflags)
|
|
{
|
|
return g_get_style_font(fg, bg, size, fflags, NULL);
|
|
}
|
|
|
|
struct style *g_clone_style(struct style *st)
|
|
{
|
|
st->refcount++;
|
|
return st;
|
|
}
|
|
|
|
void g_free_style(struct style *st)
|
|
{
|
|
if (--st->refcount) return;
|
|
#ifdef HAVE_FREETYPE
|
|
if (st->ft_face)
|
|
freetype_free_font(st->ft_face);
|
|
#endif
|
|
mem_free(st);
|
|
}
|
|
|
|
tcount gamma_stamp = 1; /* stamp counter for gamma changes */
|
|
|
|
long gamma_cache_color_1;
|
|
int gamma_cache_rgb_1;
|
|
long gamma_cache_color_2;
|
|
int gamma_cache_rgb_2;
|
|
|
|
/* IEC 61966-2-1
|
|
* Input gamma: sRGB space (directly from HTML, i. e. unrounded)
|
|
* Output: color index for graphics driver that is closest to the
|
|
* given sRGB value.
|
|
* We assume unsigned short holds at least 16 bits. */
|
|
long real_dip_get_color_sRGB(int rgb)
|
|
{
|
|
unsigned short r, g, b;
|
|
int hacked_rgb, new_rgb;
|
|
|
|
hacked_rgb = hack_rgb(rgb);
|
|
|
|
round_color_sRGB_to_48(&r, &g, &b, hacked_rgb);
|
|
r = ags_16_to_8(r, (float)(1 / (float)(display_red_gamma tcc_hack)));
|
|
g = ags_16_to_8(g, (float)(1 / (float)(display_green_gamma tcc_hack)));
|
|
b = ags_16_to_8(b, (float)(1 / (float)(display_blue_gamma tcc_hack)));
|
|
new_rgb = b | (g << 8) | (r << 16);
|
|
|
|
gamma_cache_rgb_2 = gamma_cache_rgb_1;
|
|
gamma_cache_color_2 = gamma_cache_color_1;
|
|
|
|
gamma_cache_rgb_1 = rgb;
|
|
/* The get_color takes values with gamma of display_*_gamma */
|
|
return gamma_cache_color_1 = drv->get_color(new_rgb);
|
|
}
|
|
|
|
#include "links_ic.inc"
|
|
|
|
void get_links_icon(unsigned char **data, int *width, int *height, ssize_t *skip, int pad)
|
|
{
|
|
struct bitmap b;
|
|
unsigned short *tmp1;
|
|
float g = (float)((float)(user_gamma tcc_hack) / (float)sRGB_gamma);
|
|
|
|
b.x = 48;
|
|
b.y = 48;
|
|
*width = b.x;
|
|
*height = b.y;
|
|
b.skip = b.x * (drv->depth & 7);
|
|
while (b.skip % pad) b.skip++;
|
|
*skip = b.skip;
|
|
b.data = *data = mem_alloc(b.skip * b.y);
|
|
tmp1 = mem_alloc(6 * b.y * b.x);
|
|
agx_24_to_48(tmp1, links_icon, b.x * b.y, g, g, g);
|
|
(*round_fn)(tmp1, &b);
|
|
mem_free(tmp1);
|
|
}
|
|
|
|
static inline void qb_palette(unsigned r_max, unsigned g_max, unsigned b_max, unsigned r, unsigned g, unsigned b, unsigned scale, unsigned rgb[3])
|
|
{
|
|
rgb[0] = (r * scale + r_max / 2) / r_max;
|
|
rgb[1] = (g * scale + g_max / 2) / g_max;
|
|
rgb[2] = (b * scale + b_max / 2) / b_max;
|
|
}
|
|
|
|
void q_palette(unsigned size, unsigned color, unsigned scale, unsigned rgb[3])
|
|
{
|
|
if (color >= size) {
|
|
rgb[0] = rgb[1] = rgb[2] = 0;
|
|
return;
|
|
}
|
|
switch (size) {
|
|
case 8:
|
|
qb_palette(1, 1, 1, color >> 2, (color >> 1) & 1, color & 1, scale, rgb);
|
|
break;
|
|
case 16:
|
|
qb_palette(1, 3, 1, color >> 3, (color >> 1) & 3, color & 1, scale, rgb);
|
|
break;
|
|
case 216:
|
|
qb_palette(5, 5, 5, color / 36, color / 6 % 6, color % 6, scale, rgb);
|
|
break;
|
|
case 256:
|
|
qb_palette(7, 7, 3, color >> 5, (color >> 2) & 7, color & 3, scale, rgb);
|
|
break;
|
|
case 32768:
|
|
qb_palette(31, 31, 31, color >> 10, (color >> 5) & 31, color & 31, scale, rgb);
|
|
break;
|
|
case 65536:
|
|
qb_palette(31, 63, 31, color >> 11, (color >> 5) & 63, color & 31, scale, rgb);
|
|
break;
|
|
case 16777216:
|
|
qb_palette(255, 255, 255, color >> 16, (color >> 8) & 255, color & 255, scale, rgb);
|
|
break;
|
|
default:
|
|
internal_error("q_palette: invalid size %u", size);
|
|
}
|
|
}
|
|
|
|
double rgb_distance(int r1, int g1, int b1, int r2, int g2, int b2)
|
|
{
|
|
double diff_r, diff_g, diff_b;
|
|
diff_r = (r1 - r2) * .30;
|
|
diff_r *= diff_r;
|
|
diff_g = (g1 - g2) * .59;
|
|
diff_g *= diff_g;
|
|
diff_b = (b1 - b2) * .11;
|
|
diff_b *= diff_b;
|
|
return diff_r + diff_g + diff_b;
|
|
}
|
|
|
|
#endif /* G */
|