mirror of
https://git.code.sf.net/p/libpng/code.git
synced 2025-07-10 18:04:09 +02:00
239 lines
5.7 KiB
C
239 lines
5.7 KiB
C
/* Given a target range and a source range work out an expression to scale from
|
|
* the source to the target of the form:
|
|
*
|
|
* (number * mult + add)>>16
|
|
*
|
|
* The command arguments are:
|
|
*
|
|
* scale target source
|
|
*
|
|
* and the program works out a pair of numbers, mult and add, that evaluate:
|
|
*
|
|
* number * target
|
|
* round( --------------- )
|
|
* source
|
|
*
|
|
* exactly for number in the range 0..source
|
|
*/
|
|
#define _ISOC99_SOURCE 1
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
|
|
static double minerr;
|
|
static unsigned long minmult, minadd, minshift;
|
|
static long mindelta;
|
|
|
|
static int
|
|
test(unsigned long target, unsigned long source, unsigned long mult,
|
|
long add, unsigned long shift, long delta)
|
|
{
|
|
unsigned long i;
|
|
double maxerr = 0;
|
|
double rs = (double)target/source;
|
|
|
|
for (i=0; i<=source; ++i)
|
|
{
|
|
unsigned long t = i*mult+add;
|
|
double err = fabs((t >> shift) - i*rs);
|
|
|
|
if (err > minerr)
|
|
return 0;
|
|
|
|
if (err > maxerr)
|
|
maxerr = err;
|
|
}
|
|
|
|
if (maxerr < minerr)
|
|
{
|
|
minerr = maxerr;
|
|
minmult = mult;
|
|
minadd = add;
|
|
minshift = shift;
|
|
mindelta = delta;
|
|
}
|
|
|
|
return maxerr < .5;
|
|
}
|
|
|
|
static int
|
|
dotest(unsigned long target, unsigned long source, unsigned long mult,
|
|
long add, unsigned long shift, long delta, int print)
|
|
{
|
|
if (test(target, source, mult, add, shift, delta))
|
|
{
|
|
if (print & 4)
|
|
printf(" {%11lu,%6ld /* >>%lu */ }, /* %lu/%lu */\n",
|
|
mult, add, shift, target, source);
|
|
|
|
else if (print & 2)
|
|
printf(" {%11lu,%6ld,%3lu }, /* %lu/%lu */\n",
|
|
mult, add, shift, target, source);
|
|
|
|
else if (print)
|
|
printf("number * %lu/%lu = (number * %lu + %ld) >> %lu [delta %ld]\n",
|
|
target, source, mult, add, shift, delta);
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
find(unsigned long target, unsigned long source, int print, int fixshift)
|
|
{
|
|
unsigned long shift = 0;
|
|
unsigned long shiftlim = 0;
|
|
|
|
/* In the final math the sum is at most (source*mult+add) >> shift, so:
|
|
*
|
|
* source*mult+add < 1<<32
|
|
* mult < (1<<32)/source
|
|
*
|
|
* but:
|
|
*
|
|
* mult = (target<<shift)/source
|
|
*
|
|
* so:
|
|
*
|
|
* (target<<shift) < (1<<32)
|
|
*/
|
|
if (fixshift < 0)
|
|
while ((target<<shiftlim) < 0x80000000U) ++shiftlim;
|
|
|
|
else
|
|
shift = shiftlim = (unsigned long)fixshift;
|
|
|
|
minerr = 1E8;
|
|
|
|
for (; shift<=shiftlim; ++shift)
|
|
{
|
|
unsigned long mult = ((target<<shift) + (source>>1)) / source;
|
|
long delta;
|
|
long limit = 1; /* seems to be sufficient */
|
|
long add, start, end;
|
|
|
|
end = 1<<shift;
|
|
start = -end;
|
|
|
|
for (add=start; add<=end; ++add)
|
|
if (dotest(target,source,mult,add,shift,0,print))
|
|
return 1;
|
|
|
|
for (delta=1; delta<=limit; ++delta)
|
|
{
|
|
# if 0
|
|
fprintf(stderr, "%lu/%lu: shift %lu, delta %lu\n", target, source,
|
|
shift, delta);
|
|
# endif
|
|
|
|
for (add=start; add<=end; ++add)
|
|
{
|
|
if (dotest(target, source, mult-delta, add, shift, -delta, print))
|
|
return 1;
|
|
|
|
if (dotest(target, source, mult+delta, add, shift, delta, print))
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (print & 4)
|
|
printf(" {%11lu,%6ld /* >>%lu */ }, /* %lu/%lu ERROR: .5+%g*/\n",
|
|
minmult, minadd, minshift, target, source, minerr-.5);
|
|
|
|
else if (print & 2)
|
|
printf(" {%11lu,%6ld,%3lu }, /* %lu/%lu ERROR: .5+%g*/\n",
|
|
minmult, minadd, minshift, target, source, minerr-.5);
|
|
|
|
else if (print)
|
|
printf(
|
|
"number * %lu/%lu ~= (number * %lu + %ld) >> %lu +/-.5+%g [delta %ld]\n",
|
|
target, source, minmult, minadd, minshift, minerr-.5, mindelta);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
usage(const char *prog)
|
|
{
|
|
fprintf(stderr,
|
|
"usage: %s {--denominator|--maxshift|--code} target {source}\n"
|
|
" For each 'source' prints 'mult' and 'add' such that:\n\n"
|
|
" (number * mult + add) >> 16 = round(number*target/source)\n\n"
|
|
" for all integer values of number in the range 0..source.\n\n"
|
|
" --denominator: swap target and source (specify a single source first\n"
|
|
" and follow with multiple targets.)\n"
|
|
" --maxshift: find the lowest shift value that works for all the\n"
|
|
" repeated 'source' values\n"
|
|
" --code: output C code for array/structure initialization\n",
|
|
prog);
|
|
exit(1);
|
|
}
|
|
|
|
int
|
|
main(int argc, const char **argv)
|
|
{
|
|
int i, err = 0, maxshift = 0, firstsrc = 1, code = 0, denominator = 0;
|
|
unsigned long target, shift = 0;
|
|
|
|
while (argc > 1)
|
|
{
|
|
if (strcmp(argv[firstsrc], "--maxshift") == 0)
|
|
{
|
|
maxshift = 1;
|
|
++firstsrc;
|
|
}
|
|
|
|
else if (strcmp(argv[firstsrc], "--code") == 0)
|
|
{
|
|
code = 1;
|
|
++firstsrc;
|
|
}
|
|
|
|
else if (strcmp(argv[firstsrc], "--denominator") == 0)
|
|
{
|
|
denominator = 1;
|
|
++firstsrc;
|
|
}
|
|
|
|
else
|
|
break;
|
|
}
|
|
|
|
|
|
if (argc < 2+firstsrc)
|
|
usage(argv[0]);
|
|
|
|
target = strtoul(argv[firstsrc++], 0, 0);
|
|
if (target == 0) usage(argv[0]);
|
|
|
|
for (i=firstsrc; i<argc; ++i)
|
|
{
|
|
unsigned long source = strtoul(argv[i], 0, 0);
|
|
|
|
if (source == 0) usage(argv[0]);
|
|
|
|
if (!find(denominator ? source : target, denominator ? target : source,
|
|
maxshift ? 0 : 1+code, -1))
|
|
err = 1;
|
|
|
|
if (minshift > shift) shift = minshift;
|
|
}
|
|
|
|
if (maxshift) for (i=firstsrc; i<argc; ++i)
|
|
{
|
|
unsigned long source = strtoul(argv[i], 0, 0);
|
|
|
|
if (!find(denominator ? source : target, denominator ? target : source,
|
|
code ? 4 : 1, shift))
|
|
err = 1;
|
|
}
|
|
|
|
/* Just an exit code - the printout above lists the problem */
|
|
return err;
|
|
}
|