Re: 16-bit samples

Andreas Dilger (adilger@enel.ucalgary.ca)
Wed, 12 May 1999 17:26:44 -0600 (MDT)

Ewald writes:
> I implemented it like this in the HP backend (although it uses
> little CPU I'm open to suggestions on optimizing):
>
> static void
> hp_scale_to_16bit(int count, register unsigned char *data, int depth)
> {
> register uint tmp;
> int shift1 = 16 - depth;
> int shift2 = 2*depth - 16;
maybe int shift2 = 16 - shift1;
> if (count <= 0) return;
>
> while (count--) {
> tmp = 256*data[0] + data[1];
maybe tmp = *data << 8 | data[1];
> tmp = (tmp << shift1) + (tmp >> shift2);
and tmp = (tmp << shift1) | (tmp >> shift2);
> data[0] = (tmp >> 8) & 255U;
> data[1] = tmp & 255U;
> data += 2;
> }
> }

minor changes only, I know. The only other suggestion would be to make this
inline rather than a function, since functions have more overhead.

For those of you who care, I looked at libpng to see how they do the reversal
of this operation to keep maximum accuracy. Obviously, if you are going
back to the original bit depth, it isn't important because you simply discard
the low-order bits, but it is interesting when shifting to 8-bit data - which
will be the most likely operation for display or other data formats.

For example, the value 0x00ff is nearly 0x0100, but if you simply discard the
low byte you would get 0x00 instead of 0x01. The code to do this best is:

(dp = ptr to result, sp=ptr to MSB of 16-bit data, sp+1=LSB of 16-bit data)

/* This does a more accurate scaling of the 16-bit color value, rather
* than a simple low-byte truncation. We upshift by 8 bits so we can add
* "1/2" before rounding (i.e. 127 after upshifting).
*
* What the ideal calculation should be:
*dp = ((((uint_32)(*sp) << 8) | (uint_32)(*(sp + 1))) * 255 + 127) / 65535;

* Approximate calculation with shift/add instead of multiply/divide:
*dp = ((((uint_32)(*sp) << 8) | (uint_32)((int)(*(sp + 1)) - *sp)) + 128) >> 8;

* What we actually do to avoid extra shifting and conversion: */
*dp = *sp + ((((int)(*(sp + 1)) - *sp) > 128) ? 1 : 0);

of course the cheapest (but slightly inaccurate method) is obviously:
*dp = *sp;

Cheers, Andreas

-- 
Andreas Dilger   University of Calgary  \"If a man ate a pound of pasta and
                 Micronet Research Group \ a pound of antipasto, would they
Dept of Electrical & Computer Engineering \   cancel out, leaving him still
http://www-mddsp.enel.ucalgary.ca/People/adilger/       hungry?" -- Dogbert

--
Source code, list archive, and docs: http://www.mostang.com/sane/
To unsubscribe: echo unsubscribe sane-devel | mail majordomo@mostang.com