NT4/private/windows/media/mplayer2/bltprop.c
2020-09-30 17:12:29 +02:00

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");
}