mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1082 lines
28 KiB
1082 lines
28 KiB
/*
|
|
Enhanced NCSA Mosaic from Spyglass
|
|
"Guitar"
|
|
|
|
Copyright 1994 Spyglass, Inc.
|
|
All Rights Reserved
|
|
|
|
Author(s):
|
|
Eric W. Sink [email protected]
|
|
*/
|
|
|
|
|
|
#include "all.h"
|
|
|
|
struct ImageInfo iiNotLoaded =
|
|
{ 32000, // refCount;
|
|
0, // width;
|
|
0, // height;
|
|
0, // flags;
|
|
NULL, // srcURL;
|
|
NULL, // actualURL;
|
|
NULL, // hPalette;
|
|
NULL, // pbmi;
|
|
NULL, // data;
|
|
-1, // transparent;
|
|
0, // cbImgLoadCount;
|
|
NULL, // decoderObject;
|
|
0, // cbCheckSum;
|
|
NULL // pImgOtherVers;
|
|
};
|
|
struct ImageInfo iiMissing =
|
|
{ 32000, // refCount;
|
|
0, // width;
|
|
0, // height;
|
|
0, // flags;
|
|
NULL, // srcURL;
|
|
NULL, // actualURL;
|
|
NULL, // hPalette;
|
|
NULL, // pbmi;
|
|
NULL, // data;
|
|
-1, // transparent;
|
|
0, // cbImgLoadCount;
|
|
NULL, // decoderObject;
|
|
0, // cbCheckSum;
|
|
NULL // pImgOtherVers;
|
|
};
|
|
|
|
|
|
void *pCreateDitherData(int xsize)
|
|
{
|
|
int *v_rgb_mem;
|
|
|
|
v_rgb_mem = (int *) GTR_CALLOC(3*(xsize + 2), sizeof(int));
|
|
return v_rgb_mem;
|
|
}
|
|
|
|
/*
|
|
constrains colors to 6X6X6 cube we use
|
|
*/
|
|
void x_ColorConstrain(unsigned char *psrc, unsigned char *pdst, PALETTEENTRY *pe, int xsize, int transparent)
|
|
{
|
|
int r_level, g_level, b_level;
|
|
int x;
|
|
int pixel;
|
|
|
|
for (x = 0; x < xsize; x++)
|
|
{
|
|
pixel = *psrc++;
|
|
if ((transparent != -1) && (pixel == transparent))
|
|
{
|
|
pixel = TRANSPARENT_COLOR_INDEX;
|
|
}
|
|
else
|
|
{
|
|
|
|
/* adjust red to the closest available value */
|
|
r_level = (pe[pixel].peRed + (RED_LEVEL_INCR/2)) / RED_LEVEL_INCR;
|
|
if (r_level < 0)
|
|
{
|
|
r_level = 0;
|
|
}
|
|
else if (r_level >= RED_COLOR_LEVELS)
|
|
{
|
|
r_level = RED_COLOR_LEVELS - 1;
|
|
}
|
|
|
|
/* adjust green to the closest available value */
|
|
g_level = (pe[pixel].peGreen + (GREEN_LEVEL_INCR/2)) / GREEN_LEVEL_INCR;
|
|
if (g_level < 0)
|
|
{
|
|
g_level = 0;
|
|
}
|
|
else if (g_level >= GREEN_COLOR_LEVELS)
|
|
{
|
|
g_level = GREEN_COLOR_LEVELS - 1;
|
|
}
|
|
|
|
/* adjust blue to the closest available value */
|
|
b_level = (pe[pixel].peBlue + (BLUE_LEVEL_INCR/2)) / BLUE_LEVEL_INCR;
|
|
if (b_level < 0)
|
|
{
|
|
b_level = 0;
|
|
}
|
|
else if (b_level >= BLUE_COLOR_LEVELS)
|
|
{
|
|
b_level = BLUE_COLOR_LEVELS - 1;
|
|
}
|
|
|
|
/*
|
|
Having calculated new r, g, and b values (along with their errors),
|
|
now we calculate the new pixel value
|
|
*/
|
|
pixel = CUBE6COLOR(r_level*GREEN_COLOR_LEVELS*BLUE_COLOR_LEVELS + g_level*BLUE_COLOR_LEVELS + b_level);
|
|
}
|
|
*pdst++ = pixel;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Floyd-Steinberg error diffusion dithering routine
|
|
*/
|
|
void x_DitherRelative(unsigned char *pdata, PALETTEENTRY *pe, int xsize, int ysize, int transparent, int *v_rgb_mem, int yfirst, int ylast)
|
|
{
|
|
int *v_red;
|
|
int *v_grn;
|
|
int *v_blu;
|
|
int h_red;
|
|
int h_grn;
|
|
int h_blu;
|
|
int x;
|
|
int y;
|
|
int r,g,b;
|
|
int r2,g2,b2;
|
|
int r_level, g_level, b_level;
|
|
int total_error;
|
|
int padWidth;
|
|
unsigned char *p;
|
|
int xstart;
|
|
int xend;
|
|
int xinc;
|
|
int red_next_error;
|
|
int grn_next_error;
|
|
int blu_next_error;
|
|
|
|
if (xsize%4) {
|
|
padWidth = xsize + 4 - (xsize%4);
|
|
}
|
|
else {
|
|
padWidth = xsize;
|
|
}
|
|
|
|
|
|
/* Initially, the vertical errors are 0, set by calloc */
|
|
v_red = v_rgb_mem + 1;
|
|
v_grn = v_red + (xsize+2);
|
|
v_blu = v_grn + (xsize+2);
|
|
|
|
for (y=yfirst; y<= ylast; y++)
|
|
{
|
|
/* Beginning each new row, the horizontal errors are 0 */
|
|
h_red = 0;
|
|
h_grn = 0;
|
|
h_blu = 0;
|
|
red_next_error = 0;
|
|
grn_next_error = 0;
|
|
blu_next_error = 0;
|
|
|
|
p = pdata + padWidth*(ysize - y - 1); /* the DIB is stored upside down */
|
|
|
|
xstart = 0;
|
|
xend = xsize;
|
|
xinc = 1;
|
|
|
|
/*
|
|
Dithering currently only works left to right. If we wanted bidirectional
|
|
dithering, we would need to set:
|
|
xstart = xsize-1;
|
|
xend = -1;
|
|
xinc = -1;
|
|
*/
|
|
|
|
for (x = xstart; x != xend; x += xinc)
|
|
{
|
|
if ((transparent != -1) && (*p == transparent))
|
|
{
|
|
h_red = 0;
|
|
h_grn = 0;
|
|
h_blu = 0;
|
|
v_red[x] = 0;
|
|
v_grn[x] = 0;
|
|
v_blu[x] = 0;
|
|
*p = TRANSPARENT_COLOR_INDEX;
|
|
}
|
|
else
|
|
{
|
|
/* RED */
|
|
/* r is the original value adjusted with the previous vertical and horizontal errors */
|
|
r = pe[*p].peRed + (h_red + v_red[x]);
|
|
|
|
/* adjust r to the closest available value */
|
|
r_level = (r + (RED_LEVEL_INCR/2)) / RED_LEVEL_INCR;
|
|
if (r_level < 0)
|
|
{
|
|
r_level = 0;
|
|
}
|
|
else if (r_level >= RED_COLOR_LEVELS)
|
|
{
|
|
r_level = RED_COLOR_LEVELS - 1;
|
|
}
|
|
r2 = r_level * RED_LEVEL_INCR;
|
|
|
|
/* calculate the errors for the current pixel, total error is (r - r2) */
|
|
total_error = (r - r2); /* 1/16 */
|
|
v_red[x-xinc] += (total_error * 3 / 16);
|
|
v_red[x] = (total_error * 5 / 16) + red_next_error; /* from the previous column */
|
|
red_next_error = (total_error / 16);
|
|
h_red = (r - r2) - (total_error * 9 / 16);
|
|
|
|
/* GREEN */
|
|
/* g is the original value adjusted with the previous vertical and horizontal errors */
|
|
|
|
g = pe[*p].peGreen + (h_grn + v_grn[x]);
|
|
|
|
/* adjust g to the closest available value */
|
|
g_level = (g + (GREEN_LEVEL_INCR/2)) / GREEN_LEVEL_INCR;
|
|
if (g_level < 0)
|
|
{
|
|
g_level = 0;
|
|
}
|
|
else if (g_level >= GREEN_COLOR_LEVELS)
|
|
{
|
|
g_level = GREEN_COLOR_LEVELS - 1;
|
|
}
|
|
g2 = g_level * GREEN_LEVEL_INCR;
|
|
/* calculate the errors for the current pixel, total error is (g - g2) */
|
|
total_error = (g - g2); /* 1/16 */
|
|
v_grn[x-xinc] += (total_error * 3 / 16);
|
|
v_grn[x] = (total_error * 5 / 16) + grn_next_error; /* from the previous column */
|
|
grn_next_error = (total_error / 16);
|
|
h_grn = (g - g2) - (total_error * 9 / 16);
|
|
|
|
/* BLUE */
|
|
/* b is the original value adjusted with the previous vertical and horizontal errors */
|
|
b = pe[*p].peBlue + (h_blu + v_blu[x]);
|
|
|
|
/* adjust b to the closest available value */
|
|
b_level = (b + (BLUE_LEVEL_INCR/2)) / BLUE_LEVEL_INCR;
|
|
if (b_level < 0)
|
|
{
|
|
b_level = 0;
|
|
}
|
|
else if (b_level >= BLUE_COLOR_LEVELS)
|
|
{
|
|
b_level = BLUE_COLOR_LEVELS - 1;
|
|
}
|
|
b2 = b_level * BLUE_LEVEL_INCR;
|
|
|
|
/* calculate the errors for the current pixel, total error is (b - b2) */
|
|
total_error = (b - b2); /* 1/16 */
|
|
v_blu[x-xinc] += (total_error * 3 / 16);
|
|
v_blu[x] = (total_error * 5 / 16) + blu_next_error; /* from the previous column */
|
|
blu_next_error = (total_error / 16);
|
|
h_blu = (b - b2) - (total_error * 9 / 16);
|
|
|
|
/*
|
|
Having calculated new r, g, and b values (along with their errors),
|
|
now we calculate the new pixel value
|
|
*/
|
|
*p = CUBE6COLOR(r_level*GREEN_COLOR_LEVELS*BLUE_COLOR_LEVELS + g_level*BLUE_COLOR_LEVELS + b_level);
|
|
}
|
|
p++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Floyd-Steinberg error diffusion dithering routine
|
|
*/
|
|
static int x_Dither(unsigned char *pdata, PALETTEENTRY *pe, int xsize, int ysize, int transparent)
|
|
{
|
|
int *v_rgb_mem;
|
|
|
|
v_rgb_mem = (int *) pCreateDitherData(xsize);
|
|
if (!v_rgb_mem)
|
|
{
|
|
return -1;
|
|
}
|
|
x_DitherRelative(pdata,pe,xsize,ysize,transparent,v_rgb_mem,0,ysize-1);
|
|
GTR_FREE(v_rgb_mem);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
This routine loads a bitmap from resources and constructs a DIB
|
|
in DIB_RGB_COLORS format for it.
|
|
*/
|
|
static BOOL x_load_dib(struct ImageInfo *pii, int rez)
|
|
{
|
|
BITMAP bmp;
|
|
HBITMAP hBitmap;
|
|
HDC hdc;
|
|
int padwidth;
|
|
|
|
hBitmap = LoadImage( wg.hInstance, MAKEINTRESOURCE(rez),
|
|
IMAGE_BITMAP, 0, 0, 0);
|
|
if (hBitmap && GetObject(hBitmap, sizeof(BITMAP), (LPVOID) & bmp))
|
|
{
|
|
if (pii->pbmi == NULL)
|
|
{
|
|
pii->pbmi = (PBITMAPINFO) GTR_MALLOC(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
|
|
}
|
|
if (pii->pbmi)
|
|
{
|
|
pii->width = bmp.bmWidth;
|
|
pii->height = bmp.bmHeight;
|
|
pii->pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
pii->pbmi->bmiHeader.biWidth = pii->width;
|
|
pii->pbmi->bmiHeader.biHeight = pii->height;
|
|
pii->pbmi->bmiHeader.biPlanes = 1;
|
|
pii->pbmi->bmiHeader.biBitCount = 8;
|
|
pii->pbmi->bmiHeader.biCompression = BI_RGB; /* no compression */
|
|
pii->pbmi->bmiHeader.biSizeImage = 0; /* not needed when not compressed */
|
|
pii->pbmi->bmiHeader.biXPelsPerMeter = 0;
|
|
pii->pbmi->bmiHeader.biYPelsPerMeter = 0;
|
|
pii->pbmi->bmiHeader.biClrUsed = 256;
|
|
pii->pbmi->bmiHeader.biClrImportant = 0;
|
|
padwidth = ((pii->width + 3) / 4) * 4;
|
|
if (pii->data == NULL)
|
|
{
|
|
pii->data = (unsigned char *) GTR_MALLOC(padwidth * pii->height);
|
|
}
|
|
if (pii->data)
|
|
{
|
|
hdc = GetDC(NULL);
|
|
GetDIBits(hdc, hBitmap, 0, pii->height, pii->data, pii->pbmi, DIB_RGB_COLORS);
|
|
ReleaseDC(NULL, hdc);
|
|
}
|
|
}
|
|
}
|
|
DeleteObject(hBitmap);
|
|
|
|
return pii->data == NULL || pii->pbmi == NULL ? TRUE : FALSE;
|
|
}
|
|
|
|
BOOL LoadImagePlaceholders(void)
|
|
{
|
|
BOOL bResult;
|
|
|
|
bResult = x_load_dib(&iiMissing, RES_IMAGE_MISSING);
|
|
return x_load_dib(&iiNotLoaded, RES_IMAGE_NOTLOADED) || bResult;
|
|
}
|
|
|
|
static void x_destroy_dib(struct ImageInfo *pii)
|
|
{
|
|
if (pii)
|
|
{
|
|
if (pii->data)
|
|
{
|
|
GTR_FREE(pii->data);
|
|
}
|
|
if (pii->pbmi)
|
|
{
|
|
GTR_FREE(pii->pbmi);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DestroyImagePlaceholders(void)
|
|
{
|
|
x_destroy_dib(&iiMissing);
|
|
x_destroy_dib(&iiNotLoaded);
|
|
}
|
|
|
|
// Allocates and returns pbitmapinfo for 8 bit color image (depending on type
|
|
// of display
|
|
|
|
static PBITMAPINFO x_8BPIBitmap(int xsize, int ysize)
|
|
{
|
|
PBITMAPINFO pbmi;
|
|
|
|
if (wg.eColorMode == 8)
|
|
{
|
|
pbmi = (PBITMAPINFO) GTR_MALLOC(sizeof(BITMAPINFOHEADER) + 256 * sizeof(WORD));
|
|
if (!pbmi)
|
|
{
|
|
return NULL;
|
|
}
|
|
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
pbmi->bmiHeader.biWidth = xsize;
|
|
pbmi->bmiHeader.biHeight = ysize;
|
|
pbmi->bmiHeader.biPlanes = 1;
|
|
pbmi->bmiHeader.biBitCount = 8;
|
|
pbmi->bmiHeader.biCompression = BI_RGB; /* no compression */
|
|
pbmi->bmiHeader.biSizeImage = 0; /* not needed when not compressed */
|
|
pbmi->bmiHeader.biXPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biYPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biClrUsed = 256;
|
|
pbmi->bmiHeader.biClrImportant = 0;
|
|
}
|
|
else
|
|
{
|
|
pbmi = (PBITMAPINFO) GTR_MALLOC(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
|
|
if (!pbmi)
|
|
{
|
|
return NULL;
|
|
}
|
|
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
pbmi->bmiHeader.biWidth = xsize;
|
|
pbmi->bmiHeader.biHeight = ysize;
|
|
pbmi->bmiHeader.biPlanes = 1;
|
|
pbmi->bmiHeader.biBitCount = 8;
|
|
pbmi->bmiHeader.biCompression = BI_RGB; /* no compression */
|
|
pbmi->bmiHeader.biSizeImage = 0; /* not needed when not compressed */
|
|
pbmi->bmiHeader.biXPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biYPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biClrUsed = 256;
|
|
pbmi->bmiHeader.biClrImportant = 0;
|
|
}
|
|
return pbmi;
|
|
}
|
|
|
|
/*
|
|
This routine should only be used when drawing to an 8 bit palette screen.
|
|
It always creates a DIB in DIB_PAL_COLORS format, and it handles the
|
|
case of a B/W image as well as color images. B/W images are created
|
|
with their pixels set to the foreground and background colors of the
|
|
screen, in the global palette. Color images are always dithered into
|
|
the global palette, and may have a transparent color.
|
|
*/
|
|
PBITMAPINFO BIT_Make_DIB_PAL_Header(int xsize, int ysize, CONST BYTE * pdata, HPALETTE hPalette, int transparent)
|
|
{
|
|
PALETTEENTRY pe[256];
|
|
int i;
|
|
PBITMAPINFO pbmi;
|
|
WORD *pw;
|
|
|
|
if (!hPalette)
|
|
{
|
|
pbmi = (PBITMAPINFO) GTR_MALLOC(sizeof(BITMAPINFOHEADER) + 2 * sizeof(DWORD));
|
|
if (!pbmi)
|
|
{
|
|
return NULL;
|
|
}
|
|
if (xsize % 16)
|
|
{
|
|
xsize += (16 - (xsize % 16));
|
|
}
|
|
|
|
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
pbmi->bmiHeader.biWidth = xsize;
|
|
pbmi->bmiHeader.biHeight = ysize;
|
|
pbmi->bmiHeader.biPlanes = 1;
|
|
pbmi->bmiHeader.biBitCount = 1;
|
|
pbmi->bmiHeader.biCompression = BI_RGB; /* no compression */
|
|
pbmi->bmiHeader.biSizeImage = 0; /* not needed when not compressed */
|
|
pbmi->bmiHeader.biXPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biYPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biClrUsed = 2;
|
|
pbmi->bmiHeader.biClrImportant = 0;
|
|
|
|
pw = (WORD *) pbmi->bmiColors;
|
|
pw[0] = BACKGROUND_COLOR_INDEX;
|
|
pw[1] = FOREGROUND_COLOR_INDEX;
|
|
}
|
|
else
|
|
{
|
|
pbmi = x_8BPIBitmap(xsize, ysize);
|
|
if (!pbmi)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
GetPaletteEntries(hPalette, 0, 256, pe);
|
|
|
|
pw = (WORD *) pbmi->bmiColors;
|
|
|
|
if (pdata)
|
|
x_Dither((unsigned char *) pdata, pe, xsize, ysize, transparent);
|
|
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
pw[i] = i;
|
|
}
|
|
}
|
|
|
|
return pbmi;
|
|
}
|
|
|
|
/*
|
|
This routine should only be used when drawing to an 8 bit palette screen.
|
|
Also, it is only used when the pixel data is already prematched to the global
|
|
palette. Currently, the only happens for JPEG images, since they arrive in
|
|
24 bit format, they are dithered into the global palette as they are read.
|
|
*/
|
|
#ifdef FEATURE_JPEG
|
|
PBITMAPINFO BIT_Make_DIB_PAL_Header_Prematched(int xsize, int ysize, CONST BYTE * pdata)
|
|
{
|
|
int i;
|
|
PBITMAPINFO pbmi;
|
|
WORD *pw;
|
|
|
|
{
|
|
pbmi = x_8BPIBitmap(xsize, ysize);
|
|
if (!pbmi)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
pw = (WORD *) pbmi->bmiColors;
|
|
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
pw[i] = i;
|
|
}
|
|
}
|
|
|
|
return pbmi;
|
|
}
|
|
#endif /* FEATURE_JPEG */
|
|
|
|
/*
|
|
This routine is used when drawing to printers. It always creates DIBs in
|
|
DIB_RGB_COLORS format, and it will accept arbitrary 8 bit data with
|
|
an arbitrary palette. Transparent colors are handled using white.
|
|
The foreground color for B/W images is black.
|
|
*/
|
|
PBITMAPINFO BIT_Make_DIB_RGB_Header_Printer(int xsize, int ysize, CONST BYTE * pdata, HPALETTE hPalette, int transparent, unsigned int flags)
|
|
{
|
|
PALETTEENTRY pe[256];
|
|
int i;
|
|
PBITMAPINFO pbmi;
|
|
|
|
if (flags & IMG_BW)
|
|
{
|
|
pbmi = (PBITMAPINFO) GTR_MALLOC(sizeof(BITMAPINFOHEADER) + 2 * sizeof(RGBQUAD));
|
|
if (!pbmi)
|
|
{
|
|
return NULL;
|
|
}
|
|
if (xsize % 16)
|
|
{
|
|
xsize += (16 - (xsize % 16));
|
|
}
|
|
|
|
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
pbmi->bmiHeader.biWidth = xsize;
|
|
pbmi->bmiHeader.biHeight = ysize;
|
|
pbmi->bmiHeader.biPlanes = 1;
|
|
pbmi->bmiHeader.biBitCount = 1;
|
|
pbmi->bmiHeader.biCompression = BI_RGB; /* no compression */
|
|
pbmi->bmiHeader.biSizeImage = 0; /* not needed when not compressed */
|
|
pbmi->bmiHeader.biXPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biYPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biClrUsed = 2;
|
|
pbmi->bmiHeader.biClrImportant = 0;
|
|
|
|
/*
|
|
When printing, we force XBMs to be black and white
|
|
*/
|
|
pbmi->bmiColors[0].rgbRed = 255;
|
|
pbmi->bmiColors[0].rgbGreen = 255;
|
|
pbmi->bmiColors[0].rgbBlue = 255;
|
|
pbmi->bmiColors[0].rgbReserved = 0;
|
|
|
|
pbmi->bmiColors[1].rgbRed = 0;
|
|
pbmi->bmiColors[1].rgbGreen = 0;
|
|
pbmi->bmiColors[1].rgbBlue = 0;
|
|
pbmi->bmiColors[1].rgbReserved = 0;
|
|
}
|
|
else if ((flags & IMG_JPEG) && (wg.eColorMode != 8))
|
|
{
|
|
pbmi = BIT_Make_DIB_RGB_Header_24BIT(xsize, ysize, pdata);
|
|
}
|
|
else
|
|
{
|
|
pbmi = (PBITMAPINFO) GTR_MALLOC(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
|
|
if (!pbmi)
|
|
{
|
|
return NULL;
|
|
}
|
|
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
pbmi->bmiHeader.biWidth = xsize;
|
|
pbmi->bmiHeader.biHeight = ysize;
|
|
pbmi->bmiHeader.biPlanes = 1;
|
|
pbmi->bmiHeader.biBitCount = 8;
|
|
pbmi->bmiHeader.biCompression = BI_RGB; /* no compression */
|
|
pbmi->bmiHeader.biSizeImage = 0; /* not needed when not compressed */
|
|
pbmi->bmiHeader.biXPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biYPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biClrUsed = 256;
|
|
pbmi->bmiHeader.biClrImportant = 0;
|
|
|
|
|
|
GetPaletteEntries(hPalette, 0, 256, pe);
|
|
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
pbmi->bmiColors[i].rgbRed = pe[i].peRed;
|
|
pbmi->bmiColors[i].rgbGreen = pe[i].peGreen;
|
|
pbmi->bmiColors[i].rgbBlue = pe[i].peBlue;
|
|
pbmi->bmiColors[i].rgbReserved = 0;
|
|
}
|
|
|
|
if (transparent != -1)
|
|
{
|
|
/*
|
|
Paper is white
|
|
*/
|
|
pbmi->bmiColors[transparent].rgbRed = 255;
|
|
pbmi->bmiColors[transparent].rgbGreen = 255;
|
|
pbmi->bmiColors[transparent].rgbBlue = 255;
|
|
}
|
|
}
|
|
|
|
return pbmi;
|
|
}
|
|
|
|
|
|
/*
|
|
This routine is used when drawing to the nonpalette screens. It always creates
|
|
DIBs in DIB_RGB_COLORS format. For B/W images, a 2-entry palette is constructed
|
|
using the foreground and background colors for the window. For color images,
|
|
if there is a transparent color, it is modified in the palette to be the
|
|
background color for the window.
|
|
*/
|
|
PBITMAPINFO BIT_Make_DIB_RGB_Header_Screen(int xsize, int ysize, CONST BYTE * pdata, HPALETTE hPalette, int transparent, unsigned int flags)
|
|
{
|
|
PALETTEENTRY pe[256];
|
|
int i;
|
|
PBITMAPINFO pbmi;
|
|
|
|
if (flags & IMG_BW)
|
|
{
|
|
COLORREF color;
|
|
|
|
pbmi = (PBITMAPINFO) GTR_MALLOC(sizeof(BITMAPINFOHEADER) + 2 * sizeof(RGBQUAD));
|
|
if (!pbmi)
|
|
{
|
|
return NULL;
|
|
}
|
|
if (xsize % 16)
|
|
{
|
|
xsize += (16 - (xsize % 16));
|
|
}
|
|
|
|
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
pbmi->bmiHeader.biWidth = xsize;
|
|
pbmi->bmiHeader.biHeight = ysize;
|
|
pbmi->bmiHeader.biPlanes = 1;
|
|
pbmi->bmiHeader.biBitCount = 1;
|
|
pbmi->bmiHeader.biCompression = BI_RGB; /* no compression */
|
|
pbmi->bmiHeader.biSizeImage = 0; /* not needed when not compressed */
|
|
pbmi->bmiHeader.biXPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biYPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biClrUsed = 2;
|
|
pbmi->bmiHeader.biClrImportant = 0;
|
|
|
|
color = PREF_GetBackgroundColor();
|
|
pbmi->bmiColors[0].rgbRed = GetRValue(color);
|
|
pbmi->bmiColors[0].rgbGreen = GetGValue(color);
|
|
pbmi->bmiColors[0].rgbBlue = GetBValue(color);
|
|
pbmi->bmiColors[0].rgbReserved = 0;
|
|
|
|
color = PREF_GetForegroundColor();
|
|
pbmi->bmiColors[1].rgbRed = GetRValue(color);
|
|
pbmi->bmiColors[1].rgbGreen = GetGValue(color);
|
|
pbmi->bmiColors[1].rgbBlue = GetBValue(color);
|
|
pbmi->bmiColors[1].rgbReserved = 0;
|
|
}
|
|
else
|
|
{
|
|
pbmi = x_8BPIBitmap(xsize, ysize);
|
|
if (!pbmi)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
GetPaletteEntries(hPalette, 0, 256, pe);
|
|
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
pbmi->bmiColors[i].rgbRed = pe[i].peRed;
|
|
pbmi->bmiColors[i].rgbGreen = pe[i].peGreen;
|
|
pbmi->bmiColors[i].rgbBlue = pe[i].peBlue;
|
|
pbmi->bmiColors[i].rgbReserved = 0;
|
|
}
|
|
|
|
if (transparent != -1)
|
|
{
|
|
COLORREF color;
|
|
|
|
color = PREF_GetBackgroundColor();
|
|
pbmi->bmiColors[transparent].rgbRed = GetRValue(color);
|
|
pbmi->bmiColors[transparent].rgbGreen = GetGValue(color);
|
|
pbmi->bmiColors[transparent].rgbBlue = GetBValue(color);
|
|
}
|
|
}
|
|
|
|
return pbmi;
|
|
}
|
|
|
|
DWORD vga_colors[16]={RGB( 0, 0, 0), //Black
|
|
RGB(128, 0, 0), //Dark red
|
|
RGB( 0, 128, 0), //Dark green
|
|
RGB(128, 128, 0), //Dark yellow
|
|
RGB( 0, 0, 128), //Dark blue
|
|
RGB(128, 0, 128), //Dark purple
|
|
RGB( 0, 128, 128), //Dark aqua
|
|
RGB(128, 128, 128), //Dark grey
|
|
RGB(192, 192, 192), //Light grey
|
|
RGB(255, 0, 0), //Light red
|
|
RGB( 0, 255, 0), //Light green
|
|
RGB(255, 255, 0), //Light yellow
|
|
RGB( 0, 0, 255), //Light blue
|
|
RGB(255, 0, 255), //Light purple
|
|
RGB( 0, 255, 255), //Light aqua
|
|
RGB(255, 255, 255), //White
|
|
};
|
|
|
|
/*
|
|
This routine is used when drawing to the 16 color screens. It always creates
|
|
DIBs in DIB_RGB_COLORS format. It is used when an image happens to be
|
|
prematched to the VGA palette at load time, such as a JPEG which is
|
|
dithered using those 16 colors.
|
|
*/
|
|
PBITMAPINFO BIT_Make_DIB_RGB_Header_VGA(int xsize, int ysize, CONST BYTE * pdata)
|
|
{
|
|
int i;
|
|
PBITMAPINFO pbmi;
|
|
|
|
pbmi = (PBITMAPINFO) GTR_MALLOC(sizeof(BITMAPINFOHEADER) + 16 * sizeof(RGBQUAD));
|
|
if (!pbmi)
|
|
{
|
|
return NULL;
|
|
}
|
|
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
pbmi->bmiHeader.biWidth = xsize;
|
|
pbmi->bmiHeader.biHeight = ysize;
|
|
pbmi->bmiHeader.biPlanes = 1;
|
|
pbmi->bmiHeader.biBitCount = 8;
|
|
pbmi->bmiHeader.biCompression = BI_RGB; /* no compression */
|
|
pbmi->bmiHeader.biSizeImage = 0; /* not needed when not compressed */
|
|
pbmi->bmiHeader.biXPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biYPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biClrUsed = 16;
|
|
pbmi->bmiHeader.biClrImportant = 0;
|
|
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
pbmi->bmiColors[i].rgbRed = GetRValue(vga_colors[i]);
|
|
pbmi->bmiColors[i].rgbGreen = GetGValue(vga_colors[i]);
|
|
pbmi->bmiColors[i].rgbBlue = GetBValue(vga_colors[i]);
|
|
pbmi->bmiColors[i].rgbReserved = 0;
|
|
}
|
|
|
|
return pbmi;
|
|
}
|
|
|
|
/*
|
|
This routine is used when drawing to truecolor screens. It always creates
|
|
DIBs in 24 bit format. It is used for truecolor image formats like
|
|
JPEG.
|
|
*/
|
|
PBITMAPINFO BIT_Make_DIB_RGB_Header_24BIT(int xsize, int ysize, CONST BYTE * pdata)
|
|
{
|
|
PBITMAPINFO pbmi;
|
|
|
|
pbmi = (PBITMAPINFO) GTR_MALLOC(sizeof(BITMAPINFOHEADER));
|
|
if (!pbmi)
|
|
{
|
|
return NULL;
|
|
}
|
|
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
pbmi->bmiHeader.biWidth = xsize;
|
|
pbmi->bmiHeader.biHeight = ysize;
|
|
pbmi->bmiHeader.biPlanes = 1;
|
|
pbmi->bmiHeader.biBitCount = 24;
|
|
pbmi->bmiHeader.biCompression = BI_RGB; /* no compression */
|
|
pbmi->bmiHeader.biSizeImage = 0; /* not needed when not compressed */
|
|
pbmi->bmiHeader.biXPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biYPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biClrUsed = 0;
|
|
pbmi->bmiHeader.biClrImportant = 0;
|
|
|
|
return pbmi;
|
|
}
|
|
|
|
/*
|
|
This function is only used for printing. We take the DIB as stored in the
|
|
ImageInfo structure and adapt it to be in DIB_RGB_COLORS format, suitable
|
|
for printing.
|
|
*/
|
|
int Printer_StretchDIBits(HDC hdc, int XDest, int YDest, int nDestWidth, int nDestHeight,
|
|
int XSrc, int YSrc, int nSrcWidth, int nSrcHeight,
|
|
struct ImageInfo *img)
|
|
{
|
|
PBITMAPINFO pbmi;
|
|
int err;
|
|
|
|
if (wg.eColorMode == 8 && (img->flags & IMG_PREMATCHED))
|
|
{
|
|
/*
|
|
On 8 bit screens, color images are stored in DIB_PAL_COLORS
|
|
format, so we need to construct a DIB_RGB_COLORS format DIB
|
|
using the global palette.
|
|
*/
|
|
pbmi = BIT_Make_DIB_RGB_Header_Printer(img->width, img->height,
|
|
img->data, hPalGuitar, TRANSPARENT_COLOR_INDEX, img->flags);
|
|
}
|
|
else
|
|
{
|
|
if ((wg.eColorMode == 4) && (img->flags & IMG_JPEG))
|
|
{
|
|
/*
|
|
On 4-bit screens, we use the IJG dithering code to dither
|
|
directly to a VGA palette
|
|
*/
|
|
pbmi = BIT_Make_DIB_RGB_Header_VGA(img->width, img->height, img->data);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
The image should already be in DIB_RGB_COLORS format. In
|
|
anything other than 8 bit mode, all images are stored that
|
|
way. In 8 bit mode, placeholder images are
|
|
stored that way. In either case, we need to reconstruct
|
|
a new DIB_RGB_COLORS, so that we can properly handle any
|
|
transparent colors.
|
|
*/
|
|
pbmi = BIT_Make_DIB_RGB_Header_Printer(img->width, img->height,
|
|
img->data, img->hPalette, img->transparent, img->flags);
|
|
}
|
|
}
|
|
|
|
err = StretchDIBits(hdc, XDest, YDest, nDestWidth, nDestHeight, XSrc, YSrc, nSrcWidth, nSrcHeight,
|
|
img->data, pbmi, DIB_RGB_COLORS, SRCCOPY);
|
|
|
|
GTR_FREE(pbmi);
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
// Performs a StretchDIBits patching bitmap color table as necessary to
|
|
// handle dibenv. we must patch the color table if we have a transparent
|
|
// image (which implies 8-bits per pixel) or black and white (iff 2 colors
|
|
// used in bitmap)
|
|
int MyStretchDIBits(
|
|
HDC hdc, // handle of device context
|
|
int XDest, // x-coordinate of upper-left corner of dest. rect.
|
|
int YDest, // y-coordinate of upper-left corner of dest. rect.
|
|
int nDestWidth, // width of destination rectangle
|
|
int nDestHeight, // height of destination rectangle
|
|
int XSrc, // x-coordinate of upper-left corner of source rect.
|
|
int YSrc, // y-coordinate of upper-left corner of source rect.
|
|
int nSrcWidth, // width of source rectangle
|
|
int nSrcHeight, // height of source rectangle
|
|
CONST VOID *lpBits, // bitmap bits
|
|
LPBITMAPINFO lpBitsInfo, // bitmap data
|
|
UINT iUsage, // usage
|
|
DWORD dwRop, // raster operation code
|
|
PDIBENV pdibenv // DIBENV for draw
|
|
)
|
|
{
|
|
RGBQUAD rgbFg;
|
|
RGBQUAD rgbBg;
|
|
int idxSaveFg;
|
|
int idxSaveBg;
|
|
BOOL bBandW = lpBitsInfo->bmiHeader.biClrUsed == 2;
|
|
BOOL bTransparent = pdibenv->transparent >= 0;
|
|
WORD *pw;
|
|
int result;
|
|
HDC hdcbm = NULL;
|
|
HBITMAP hbitmap = NULL;
|
|
RECT rectDC;
|
|
RECT rectDoc;
|
|
RECT rectScreen;
|
|
BOOL bErase = FALSE;
|
|
int XDDest = XDest;
|
|
int YDDest = YDest;
|
|
HDC dhdc = hdc;
|
|
struct Mwin *tw = pdibenv->tw;
|
|
PBITMAPINFO pbmi = NULL;
|
|
BOOL bPalette = (wg.eColorMode == 8);
|
|
int i;
|
|
static const RGBQUAD rgbWhite = {255, 255, 255, 0};
|
|
static const RGBQUAD rgbBlack = {0, 0, 0, 0};
|
|
// These define the biggest offscreen buffer we'll try to make
|
|
#define MAX_OSBUFFER (0x100000)
|
|
int destBytes;
|
|
|
|
SetStretchBltMode(hdc, COLORONCOLOR);
|
|
if (bBandW)
|
|
{
|
|
if (bPalette)
|
|
{
|
|
pw = (WORD *) lpBitsInfo->bmiColors;
|
|
idxSaveBg = pw[0];
|
|
idxSaveFg = pw[1];
|
|
pw[0] = colorIdxBg;
|
|
pw[1] = colorIdxFg;
|
|
}
|
|
else
|
|
{
|
|
rgbBg = lpBitsInfo->bmiColors[0];
|
|
rgbFg = lpBitsInfo->bmiColors[1];
|
|
|
|
lpBitsInfo->bmiColors[0].rgbRed = GetRValue(pdibenv->colorBg);
|
|
lpBitsInfo->bmiColors[0].rgbGreen = GetGValue(pdibenv->colorBg);
|
|
lpBitsInfo->bmiColors[0].rgbBlue = GetBValue(pdibenv->colorBg);
|
|
lpBitsInfo->bmiColors[1].rgbRed = GetRValue(pdibenv->colorFg);
|
|
lpBitsInfo->bmiColors[1].rgbGreen = GetGValue(pdibenv->colorFg);
|
|
lpBitsInfo->bmiColors[1].rgbBlue = GetBValue(pdibenv->colorFg);
|
|
}
|
|
}
|
|
else if (bTransparent)
|
|
{
|
|
if (bPalette)
|
|
{
|
|
pw = (WORD *) lpBitsInfo->bmiColors;
|
|
idxSaveBg = pw[TRANSPARENT_COLOR_INDEX];
|
|
pw[TRANSPARENT_COLOR_INDEX] = pdibenv->colorIdxBg;
|
|
}
|
|
else
|
|
{
|
|
rgbBg = lpBitsInfo->bmiColors[pdibenv->transparent];
|
|
lpBitsInfo->bmiColors[pdibenv->transparent].rgbRed = GetRValue(pdibenv->colorBg);
|
|
lpBitsInfo->bmiColors[pdibenv->transparent].rgbGreen = GetGValue(pdibenv->colorBg);
|
|
lpBitsInfo->bmiColors[pdibenv->transparent].rgbBlue = GetBValue(pdibenv->colorBg);
|
|
}
|
|
}
|
|
|
|
|
|
if (bTransparent &&
|
|
pdibenv->bFancyBg &&
|
|
(pbmi = x_8BPIBitmap(lpBitsInfo->bmiHeader.biWidth, lpBitsInfo->bmiHeader.biHeight)))
|
|
{
|
|
HDC savehdc;
|
|
|
|
rectDoc.left = XDest;
|
|
rectDoc.right = XDest + nDestWidth;
|
|
rectDoc.top = YDest;
|
|
rectDoc.bottom = YDest + nDestHeight;
|
|
IntersectRect(&rectScreen, &(pdibenv->rectPaint), &rectDoc);
|
|
rectDC.left = 0;
|
|
rectDC.top = 0;
|
|
rectDC.right = rectScreen.right - rectScreen.left;
|
|
rectDC.bottom = rectScreen.bottom - rectScreen.top;
|
|
|
|
// To avoid flashing, we set up memory DC so we can always draw opaquely
|
|
// If this operation fails due to lack of memory, we explictly draw background ourselves
|
|
// We don't do this if the offscreen bitmap would be bigger than MAX_OSBUFFER
|
|
if (tw->bErase)
|
|
{
|
|
destBytes = rectDC.right*rectDC.bottom*(wg.eColorMode / 8);
|
|
if (destBytes <= MAX_OSBUFFER)
|
|
{
|
|
hdcbm = CreateCompatibleDC(hdc);
|
|
if (hdcbm)
|
|
{
|
|
hbitmap = CreateCompatibleBitmap(hdc,rectDC.right,rectDC.bottom);
|
|
GTR_RealizePalette(hdcbm);
|
|
SelectObject(hdcbm, hbitmap);
|
|
SetStretchBltMode(hdcbm, COLORONCOLOR);
|
|
}
|
|
}
|
|
if (hdcbm == NULL || hbitmap == NULL)
|
|
{
|
|
if (hdcbm) DeleteDC(hdcbm);
|
|
hdcbm = NULL;
|
|
bErase = tw->bErase;
|
|
}
|
|
}
|
|
|
|
// bErase is TRUE, iff we didn't set up memory device context
|
|
if (bErase)
|
|
{
|
|
TW_DrawBackground(tw, tw->offl, tw->offt, 0, 0, &rectScreen);
|
|
}
|
|
else if (hdcbm != NULL)
|
|
{
|
|
savehdc = tw->hdc;
|
|
|
|
tw->hdc = hdcbm;
|
|
|
|
TW_DrawBackground(tw, tw->offl, tw->offt, rectScreen.left, rectScreen.top, &rectDC);
|
|
|
|
tw->hdc = savehdc;
|
|
|
|
dhdc = hdcbm;
|
|
XDDest = XDest - rectScreen.left;
|
|
YDDest = YDest - rectScreen.top;
|
|
|
|
}
|
|
|
|
// USE SRCAND to set bits in dest that will be covered by
|
|
// opaque bits to 0 and leave bits covered by transparent bits untouched
|
|
|
|
if (bPalette)
|
|
{
|
|
pw = (WORD *) pbmi->bmiColors;
|
|
for (i = 0;i < 256; i++)
|
|
pw[i] = CUBE6COLOR(0); // <0,0,0>
|
|
pw[TRANSPARENT_COLOR_INDEX] = CUBE6COLOR(LAST_MAIN_PALETTE_COLOR); // <255,255,255>
|
|
}
|
|
else
|
|
{
|
|
for (i= 0; i < 256; i++)
|
|
{
|
|
pbmi->bmiColors[i] = rgbBlack;
|
|
}
|
|
pbmi->bmiColors[pdibenv->transparent] = rgbWhite;
|
|
}
|
|
result = StretchDIBits(dhdc,
|
|
XDDest, YDDest, nDestWidth, nDestHeight,
|
|
XSrc, YSrc, nSrcWidth, nSrcHeight,
|
|
lpBits, pbmi,
|
|
iUsage, SRCAND);
|
|
|
|
// USE SRCPAINT == OR, to copy over just the opaque bits
|
|
|
|
if (bPalette)
|
|
{
|
|
pw = (WORD *) lpBitsInfo->bmiColors;
|
|
pw[TRANSPARENT_COLOR_INDEX] = CUBE6COLOR(0);
|
|
}
|
|
else
|
|
{
|
|
lpBitsInfo->bmiColors[pdibenv->transparent] = rgbBlack;
|
|
}
|
|
result = StretchDIBits(dhdc,
|
|
XDDest, YDDest, nDestWidth, nDestHeight,
|
|
XSrc, YSrc, nSrcWidth, nSrcHeight,
|
|
lpBits, lpBitsInfo,
|
|
iUsage, SRCPAINT);
|
|
|
|
// Finally, bitblt in composited image
|
|
|
|
if (hdcbm)
|
|
{
|
|
BitBlt(hdc,rectScreen.left,rectScreen.top,rectDC.right,rectDC.bottom,hdcbm,0,0,dwRop);
|
|
DeleteObject(hbitmap);
|
|
DeleteDC(hdcbm);
|
|
}
|
|
if (pbmi) GTR_FREE(pbmi);
|
|
}
|
|
else
|
|
{
|
|
result = StretchDIBits(hdc,
|
|
XDest, YDest, nDestWidth, nDestHeight,
|
|
XSrc, YSrc, nSrcWidth, nSrcHeight,
|
|
lpBits, lpBitsInfo,
|
|
iUsage, dwRop);
|
|
}
|
|
|
|
if (bBandW)
|
|
{
|
|
if (bPalette)
|
|
{
|
|
pw[0] = idxSaveBg;
|
|
pw[1] = idxSaveFg;
|
|
}
|
|
else
|
|
{
|
|
lpBitsInfo->bmiColors[0] = rgbBg;
|
|
lpBitsInfo->bmiColors[1] = rgbFg;
|
|
}
|
|
}
|
|
else if (bTransparent)
|
|
{
|
|
if (bPalette)
|
|
{
|
|
pw[TRANSPARENT_COLOR_INDEX] = idxSaveBg;
|
|
}
|
|
else
|
|
{
|
|
lpBitsInfo->bmiColors[pdibenv->transparent] = rgbBg;
|
|
}
|
|
}
|
|
return result;
|
|
}
|