385 lines
12 KiB
C
385 lines
12 KiB
C
/*-----------------------------------------------------------------------------+
|
|
| BLTPROP.C |
|
|
| |
|
|
| Emulates the 16 bit BLTPROP.ASM for WIN32 |
|
|
| |
|
|
| (C) Copyright Microsoft Corporation 1993. All rights reserved. |
|
|
| |
|
|
| Revision History |
|
|
| 21-Oct-1992 MikeTri Created |
|
|
| 09-Apr-1993 GeraintD Added error propagation
|
|
| |
|
|
+-----------------------------------------------------------------------------*/
|
|
|
|
#include <windows.h>
|
|
#include <stdlib.h>
|
|
#include "mplayer.h"
|
|
#include "bltprop.h"
|
|
|
|
|
|
/*
|
|
* 256 colour to 16 colour dithering by error propagation
|
|
*
|
|
* This function takes an 8-bit DIB using 256 colours and converts
|
|
* it to a DIB that uses only 16 distinct colours.
|
|
*
|
|
* We take a pixel and convert it to one of the 16 standard vga colours
|
|
* by taking each component and comparing it against a low and high
|
|
* threshold. Less than the low gets 0 of that component; between low
|
|
* and high gets an intensity of 128, and above the high threshold gets
|
|
* an intensity of 255 for that component. (the standard 16 colours
|
|
* have the 8 combinations of 0 or 128 for each component, and the
|
|
* 8 combinations of 0 or 255 for each component - there are no colours
|
|
* combining intensities of 255 and 128). So if any of our colours
|
|
* are above the high threshold, we use 255 for any non-0 intensity.
|
|
* We also have 2 grey levels that are picked out if all colour intensities
|
|
* are less than a given threshold.
|
|
*
|
|
* The conversion is done by building an 8-bit value with bits set to
|
|
* indicate if each component is above either of the two thresholds,
|
|
* and then using this as a palette index. We thus use an output colour
|
|
* table that contains 256 entries, though only 16 distinct colours.
|
|
*
|
|
* Having converted the pixel into the new palette index, we calculate the
|
|
* difference for each r,g,b component between the original and the final
|
|
* colour. We then add a fraction of this error to the adjacent pixels
|
|
* along, down and diagonally. These error values are added to the
|
|
* red, green and blue values for the adjacent pixels before comparing
|
|
* against the thresholds in the colour conversion process.
|
|
*/
|
|
|
|
|
|
/*
|
|
* y error propagation - this contains the error for each component that
|
|
* we wish to pass to the line below. Thus there is one entry for each
|
|
* colour component for each pixel. The same max line length is assumed
|
|
* in the win-16 version.
|
|
*/
|
|
#define MAXBITMAPWIDTH 1500
|
|
typedef struct _colour_error {
|
|
int red_error;
|
|
int green_error;
|
|
int blue_error;
|
|
} colour_error, *pcolour_error;
|
|
|
|
colour_error y_error[MAXBITMAPWIDTH];
|
|
|
|
/*
|
|
* we take the difference between the actual and desired components,
|
|
* multiply up by SCALE_UP, and then pass the result divided by SCALE_X
|
|
* to both the pixel across and below, and divided by SCALE_Z to the pixel
|
|
* diagonally across and below. (Below of course, means further down the
|
|
* DIB, and therefore higher up the screen)
|
|
*/
|
|
#define SCALE_UP 8
|
|
#define SCALE_X 32
|
|
#define SCALE_Z 64
|
|
|
|
|
|
|
|
/*
|
|
* The final pixel has the following form:
|
|
*
|
|
* bits 7x543210
|
|
* | ||||||
|
|
* | |||||+-- set iff RED > HiThresh
|
|
* | ||||+--- set iff RED > LoThresh
|
|
* | |||+---- set iff GREEN > HiThresh
|
|
* | ||+----- set iff GREEN > LoThresh
|
|
* | |+------ set iff BLUE > HiThresh
|
|
* | +------- set iff BLUE > LoThresh
|
|
* +--------- set iff all colors > GrayThresh
|
|
*/
|
|
|
|
#define RED_HITHRESH 0x01
|
|
#define RED_LOTHRESH 0x02
|
|
#define GREEN_HITHRESH 0x04
|
|
#define GREEN_LOTHRESH 0x08
|
|
#define BLUE_HITHRESH 0x10
|
|
#define BLUE_LOTHRESH 0x20
|
|
#define GRAY_THRESH 0x80
|
|
|
|
#define ALL_HITHRESH (RED_HITHRESH | GREEN_HITHRESH | BLUE_HITHRESH)
|
|
#define ALL_LOTHRESH (RED_LOTHRESH | GREEN_LOTHRESH | BLUE_LOTHRESH)
|
|
|
|
/*
|
|
* convert a palette index in the above threshold format into the
|
|
* rgb component values.
|
|
*/
|
|
RGBQUAD
|
|
ThresholdToRGB(int PalIndex)
|
|
{
|
|
RGBQUAD rgbq;
|
|
BYTE RGBVal;
|
|
|
|
/* Special case greys */
|
|
|
|
if (PalIndex == (GRAY_THRESH | ALL_LOTHRESH)) {
|
|
|
|
rgbq.rgbRed = rgbq.rgbGreen = rgbq.rgbBlue = 0xc0;
|
|
|
|
} else if (PalIndex == GRAY_THRESH) {
|
|
|
|
rgbq.rgbRed = rgbq.rgbGreen = rgbq.rgbBlue = 0x80;
|
|
|
|
} else {
|
|
|
|
rgbq.rgbRed = 0;
|
|
rgbq.rgbGreen = 0;
|
|
rgbq.rgbBlue = 0;
|
|
|
|
/*
|
|
* if any components are above hi-threshold, then
|
|
* use the high threshold for all non-zero components; otherwise
|
|
* use the low threshold for all non-zero components.
|
|
*/
|
|
if (PalIndex & ALL_HITHRESH) {
|
|
RGBVal = 0xff;
|
|
} else {
|
|
RGBVal = 0x80;
|
|
}
|
|
|
|
if (PalIndex & (RED_HITHRESH | RED_LOTHRESH)) {
|
|
rgbq.rgbRed = RGBVal;
|
|
}
|
|
|
|
if (PalIndex & (GREEN_HITHRESH | GREEN_LOTHRESH)) {
|
|
rgbq.rgbGreen = RGBVal;
|
|
}
|
|
|
|
if (PalIndex & (BLUE_HITHRESH | BLUE_LOTHRESH)) {
|
|
rgbq.rgbBlue = RGBVal;
|
|
}
|
|
}
|
|
|
|
return (rgbq);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* copy a dib from pbSrc to pbDst reducing to 16 distinct colours
|
|
*/
|
|
void FAR PASCAL BltProp(LPBITMAPINFOHEADER pbiSrc,
|
|
LPBYTE pbSrc,
|
|
UINT SrcX,
|
|
UINT SrcY,
|
|
UINT SrcXE,
|
|
UINT SrcYE,
|
|
LPBITMAPINFOHEADER pbiDst,
|
|
LPBYTE pbDst,
|
|
UINT DstX,
|
|
UINT DstY)
|
|
{
|
|
UINT count, row, column;
|
|
BYTE TempByte;
|
|
BYTE ColourTableIndex;
|
|
|
|
int RedVal;
|
|
int GreenVal;
|
|
int BlueVal;
|
|
colour_error x_error, z_error;
|
|
int scaled_error, scaled_x, scaled_z;
|
|
RGBQUAD rgbq;
|
|
|
|
LPBITMAPINFO ColourTable;
|
|
|
|
|
|
DPF2("BltProp");
|
|
|
|
|
|
|
|
|
|
/*
|
|
* clear the y_error to zero at start of bitmap
|
|
*/
|
|
for (count = 0; count < SrcXE; count++) {
|
|
y_error[count].red_error = 0;
|
|
y_error[count].green_error = 0;
|
|
y_error[count].blue_error = 0;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************\
|
|
*
|
|
* Loop through the bitmap picking up the pixel r,g,b values, adjust for
|
|
* the error propagated and then compare the components against the two
|
|
* threshold values. The resulting byte has the following form:
|
|
*
|
|
* bits 7x543210
|
|
* | ||||||
|
|
* | |||||+-- set iff RED > HiThresh
|
|
* | ||||+--- set iff RED > LoThresh
|
|
* | |||+---- set iff GREEN > HiThresh
|
|
* | ||+----- set iff GREEN > LoThresh
|
|
* | |+------ set iff BLUE > HiThresh
|
|
* | +------- set iff BLUE > LoThresh
|
|
* +--------- set iff all colors > GrayThresh
|
|
*
|
|
* This is an index into the 256-entry colour table generated below (that
|
|
* uses only 16 distinct colours).
|
|
*
|
|
* After creating the correct colour, we calculate the difference between
|
|
* this colour and the original, and propagate that error forwards and down.
|
|
*
|
|
\*****************************************************************************/
|
|
|
|
|
|
/* offset source, dest pointers by SrcX rows */
|
|
pbSrc += (SrcY * pbiSrc->biWidth) + SrcX;
|
|
pbDst += (DstY * pbiDst->biWidth) + DstX;
|
|
ColourTable = (LPBITMAPINFO)pbiSrc;
|
|
|
|
for (row=0; row < SrcYE ; row++) {
|
|
|
|
/* clear x error for start of row */
|
|
x_error.red_error = 0;
|
|
x_error.green_error = 0;
|
|
x_error.blue_error = 0;
|
|
z_error.red_error = 0;
|
|
z_error.green_error = 0;
|
|
z_error.blue_error = 0;
|
|
|
|
|
|
for (column = 0; column < SrcXE; column++) {
|
|
|
|
/* pick up the source palette index and get rgb components */
|
|
ColourTableIndex = *pbSrc++;
|
|
RedVal = ColourTable->bmiColors[ColourTableIndex].rgbRed;
|
|
GreenVal = ColourTable->bmiColors[ColourTableIndex].rgbGreen;
|
|
BlueVal = ColourTable->bmiColors[ColourTableIndex].rgbBlue;
|
|
|
|
/* add on error - x-error is propagated from
|
|
* previous column. y-error is passed down from pixel above.
|
|
* z-error is passed diagonally and has already been added
|
|
* into y-error for this pixel.
|
|
*/
|
|
RedVal += x_error.red_error + y_error[column].red_error;
|
|
GreenVal += x_error.green_error + y_error[column].green_error;
|
|
BlueVal += x_error.blue_error + y_error[column].blue_error;
|
|
|
|
/*
|
|
* As we move along the line, y_error[] for the pixels
|
|
* ahead of us contains the error to be added to the pixels
|
|
* on this row. y_error[] for the pixels we have done contains
|
|
* the error to be propagated to those pixels on the row
|
|
* below.
|
|
*
|
|
* Now that we have picked up the error for this pixel, we
|
|
* can start accumulating errors for this column on the
|
|
* row below. We start with the z_error from the previous pixel
|
|
* and then add in (later) the y_error from the current pixel.
|
|
*/
|
|
y_error[column] = z_error;
|
|
|
|
|
|
|
|
TempByte = 0x00; // Our "new" bitmap entry, once it has been munged
|
|
|
|
/*
|
|
* set threshold bits for each component based on adjusted colours
|
|
*/
|
|
|
|
if (RedVal > LoThresh) {
|
|
TempByte |= RED_LOTHRESH;
|
|
if (RedVal > HiThresh){
|
|
TempByte |= RED_HITHRESH;
|
|
}
|
|
}
|
|
if (GreenVal > LoThresh) {
|
|
TempByte |= GREEN_LOTHRESH;
|
|
if (GreenVal > HiThresh){
|
|
TempByte |= GREEN_HITHRESH;
|
|
}
|
|
}
|
|
if (BlueVal > LoThresh) {
|
|
TempByte |= BLUE_LOTHRESH;
|
|
if (BlueVal > HiThresh){
|
|
TempByte |= BLUE_HITHRESH;
|
|
}
|
|
}
|
|
|
|
/* set grey scale bit if all colours > grey threshold */
|
|
if (
|
|
(RedVal > GrayThresh)
|
|
&& (BlueVal > GrayThresh)
|
|
&& (GreenVal > GrayThresh)
|
|
) {
|
|
TempByte |= GRAY_THRESH;
|
|
}
|
|
|
|
/* we now have palette index into new colour table */
|
|
*pbDst++ = TempByte;
|
|
|
|
/*
|
|
* calculate difference for each component between
|
|
* desired colour (after error adjustment) and actual
|
|
* colour. Remember to add in to the y-error, since this
|
|
* already contains the z_error from the previous cell.
|
|
* Hold the z_error for this cell, since we can't add this
|
|
* to the next y_error until we have used it for the next cell
|
|
* on this row.
|
|
*
|
|
* do the scaling on the absolute values and then
|
|
* put the sign back in afterwards - to make sure
|
|
* we handle small negative numbers ok.
|
|
*/
|
|
rgbq = ThresholdToRGB(TempByte);
|
|
|
|
scaled_error = (RedVal - rgbq.rgbRed) * SCALE_UP;
|
|
scaled_x = abs(scaled_error) / SCALE_X;
|
|
scaled_z = abs(scaled_error) / SCALE_Z;
|
|
x_error.red_error = (scaled_error > 0) ? scaled_x : -scaled_x;
|
|
z_error.red_error = (scaled_error > 0) ? scaled_z : -scaled_z;
|
|
y_error[column].red_error += x_error.red_error;
|
|
|
|
|
|
scaled_error = (GreenVal - rgbq.rgbGreen) * SCALE_UP;
|
|
scaled_x = abs(scaled_error) / SCALE_X;
|
|
scaled_z = abs(scaled_error) / SCALE_Z;
|
|
x_error.green_error = (scaled_error > 0) ? scaled_x : -scaled_x;
|
|
z_error.green_error = (scaled_error > 0) ? scaled_z : -scaled_z;
|
|
y_error[column].green_error += x_error.green_error;
|
|
|
|
scaled_error = (BlueVal - rgbq.rgbBlue) * SCALE_UP;
|
|
scaled_x = abs(scaled_error) / SCALE_X;
|
|
scaled_z = abs(scaled_error) / SCALE_Z;
|
|
x_error.blue_error = (scaled_error > 0) ? scaled_x : -scaled_x;
|
|
z_error.blue_error = (scaled_error > 0) ? scaled_z : -scaled_z;
|
|
y_error[column].blue_error += x_error.blue_error;
|
|
|
|
|
|
}
|
|
|
|
/* advance source and dest pointers from end of rectangle to start of
|
|
* next line
|
|
*/
|
|
pbSrc += pbiSrc->biWidth - SrcXE;
|
|
pbDst += pbiDst->biWidth - SrcXE;
|
|
}
|
|
|
|
DPF2("BltProp - finished first loop");
|
|
/*****************************************************************************\
|
|
*
|
|
* This part generates a new output colour table entry that is accessed by the
|
|
* modified bitmap generated above, and updates the destination DIB colour
|
|
* table with that new entry.
|
|
*
|
|
\*****************************************************************************/
|
|
|
|
ColourTable = (LPBITMAPINFO)pbiDst;
|
|
|
|
for (count=0; count<256; count++ ) {
|
|
|
|
|
|
/* Update the original colour table within the destination DIB */
|
|
|
|
ColourTable->bmiColors[count] = ThresholdToRGB(count);
|
|
|
|
}
|
|
DPF2("BltProp - finished second loop");
|
|
}
|
|
|
|
|