395 lines
12 KiB
C++
395 lines
12 KiB
C++
/******************************Module*Header*******************************\
|
|
* Module Name: perpixel.c
|
|
*
|
|
* Copyright (c) 1997 Microsoft Corporation
|
|
*
|
|
\**************************************************************************/
|
|
|
|
extern "C" {
|
|
#include <windows.h>
|
|
|
|
#include <stdio.h>
|
|
#include <commdlg.h>
|
|
|
|
#include <wndstuff.h>
|
|
};
|
|
|
|
class PERPIXELDRAW
|
|
{
|
|
private:
|
|
ULONG cPixels;
|
|
HBITMAP hbmDraw;
|
|
HBITMAP hbmPerPixel;
|
|
RGBQUAD* pDrawBits;
|
|
RGBQUAD* pPerPixelBits;
|
|
|
|
public:
|
|
PERPIXELDRAW(HDC hdcScreen, LONG cx, LONG cy);
|
|
~PERPIXELDRAW();
|
|
VOID vConvertGrayScaleToPerPixelAlpha(COLORREF color);
|
|
VOID vConvertColorToPerPixelAlpha(ULONG alpha);
|
|
|
|
HBITMAP hbmDrawBitmap() { return(hbmDraw); }
|
|
HBITMAP hbmPerPixelBitmap() { return(hbmPerPixel); }
|
|
};
|
|
|
|
/******************************Public*Routine******************************\
|
|
* PERPIXELDRAW::PERPIXELDRAW
|
|
*
|
|
* Contains all the state required to allow GDI to do per-pixel alpha drawing.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
PERPIXELDRAW::PERPIXELDRAW(
|
|
HDC hdcScreen,
|
|
LONG width,
|
|
LONG height)
|
|
{
|
|
HBITMAP hbmShape;
|
|
BITMAP bmShape;
|
|
BITMAPINFO bmi;
|
|
RGBQUAD* pRGB;
|
|
ULONG i;
|
|
|
|
cPixels = width * height;
|
|
|
|
RtlZeroMemory(&bmi, sizeof(bmi));
|
|
|
|
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
bmi.bmiHeader.biWidth = width;
|
|
bmi.bmiHeader.biHeight = height;
|
|
bmi.bmiHeader.biPlanes = 1;
|
|
bmi.bmiHeader.biBitCount = 32;
|
|
bmi.bmiHeader.biCompression = BI_RGB;
|
|
|
|
hbmDraw = CreateDIBSection(hdcScreen,
|
|
&bmi,
|
|
DIB_RGB_COLORS,
|
|
(VOID**) &pDrawBits,
|
|
NULL,
|
|
0);
|
|
|
|
hbmPerPixel = CreateDIBSection(hdcScreen,
|
|
&bmi,
|
|
DIB_RGB_COLORS,
|
|
(VOID**) &pPerPixelBits,
|
|
NULL,
|
|
0);
|
|
|
|
// Initialize the alpha of the 'draw bits' bitmap to a unique pattern.
|
|
// This way we'll know what pixels have been drawn to the next time
|
|
// 'vConvertColorToPerPixelAlpha' is called:
|
|
|
|
if (pDrawBits)
|
|
{
|
|
for (pRGB = pDrawBits, i = cPixels; i != 0; pRGB++, i--)
|
|
{
|
|
pRGB->rgbReserved = 0xaa;
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* PERPIXELDRAW::~PERPIXELDRAW
|
|
*
|
|
\**************************************************************************/
|
|
|
|
PERPIXELDRAW::~PERPIXELDRAW()
|
|
{
|
|
DeleteObject(hbmDraw);
|
|
DeleteObject(hbmPerPixel);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID PERPIXELDRAW::vConvertGrayScaleToPerPixelAlpha
|
|
*
|
|
* Takes any pixels that were drawn into the 'draw' bitmap, interprets them
|
|
* as gray-scale, and updates the corresponding pixels in the resultant
|
|
* 'per-pixel' bitmap using the specified color and an alpha based on the
|
|
* drawn pixel's intensity.
|
|
*
|
|
* This routine is intended specifically to allow anti-aliased drawing of
|
|
* text into the resultant 32-bpp RGBA bitmap. If you draw to the 'draw'
|
|
* bitmap using white anti-aliased text, this will convert that text to
|
|
* the appropriate color and alpha value.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID PERPIXELDRAW::vConvertGrayScaleToPerPixelAlpha(
|
|
COLORREF color)
|
|
{
|
|
RGBQUAD* pDraw;
|
|
RGBQUAD* pPerPixel;
|
|
ULONG i;
|
|
ULONG ulAlpha;
|
|
ULONG ulRed;
|
|
ULONG ulGreen;
|
|
ULONG ulBlue;
|
|
ULONG ulDelta;
|
|
|
|
if ((pDrawBits) && (pPerPixelBits))
|
|
{
|
|
pDraw = pDrawBits;
|
|
pPerPixel = pPerPixelBits;
|
|
|
|
ulRed = GetRValue(color);
|
|
ulGreen = GetGValue(color);
|
|
ulBlue = GetBValue(color);
|
|
|
|
for (i = cPixels; i != 0; i--, pDraw++, pPerPixel++)
|
|
{
|
|
if (pDraw->rgbReserved != 0xaa)
|
|
{
|
|
// The alpha value is simply the intensity of the color.
|
|
// We assume the color is grey-scale, meaning that red,
|
|
// green, and blue are equivalent, so we can simply
|
|
// take red:
|
|
|
|
ulDelta = 255 - pPerPixel->rgbReserved;
|
|
ulAlpha = pPerPixel->rgbReserved
|
|
+ ((ulDelta * pDraw->rgbRed) + 128) / 255;
|
|
|
|
// ulAlpha = pDraw->rgbRed; // !!!
|
|
|
|
// Now set the color, remembering that it must be
|
|
// pre-multiplied with the alpha:
|
|
|
|
pPerPixel->rgbReserved = ulAlpha;
|
|
pPerPixel->rgbRed = ((ulRed * ulAlpha) + 128) / 255;
|
|
pPerPixel->rgbGreen = ((ulGreen * ulAlpha) + 128) / 255;
|
|
pPerPixel->rgbBlue = ((ulBlue * ulAlpha) + 128) / 255;
|
|
|
|
// Finally, reset the 'draw' pixel:
|
|
|
|
pDraw->rgbReserved = 0xaa;
|
|
pDraw->rgbRed = 0;
|
|
pDraw->rgbGreen = 0;
|
|
pDraw->rgbBlue = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID PERPIXELDRAW::vConvertColorToPerPixelAlpha
|
|
*
|
|
* Takes any pixels that were drawn by GDI into the 'draw' bitmap, and
|
|
* copies them to the 'per-pixel' bitmap, along with a specified alpha.
|
|
*
|
|
* This routine is intended specifically to allow GDI to draw to the
|
|
* resultant 32-bpp RGBA bitmap with an arbitrary alpha value for all
|
|
* drawing.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID PERPIXELDRAW::vConvertColorToPerPixelAlpha(
|
|
ULONG alpha)
|
|
{
|
|
RGBQUAD* pDraw;
|
|
RGBQUAD* pPerPixel;
|
|
ULONG i;
|
|
|
|
if ((pDrawBits) && (pPerPixelBits))
|
|
{
|
|
pDraw = pDrawBits;
|
|
pPerPixel = pPerPixelBits;
|
|
|
|
for (i = cPixels; i != 0; i--, pDraw++, pPerPixel++)
|
|
{
|
|
if (pDraw->rgbReserved != 0xaa)
|
|
{
|
|
// Ah ha, this pixel was drawn over. Add it to the 'per-pixel'
|
|
// buffer:
|
|
|
|
pPerPixel->rgbReserved = alpha;
|
|
pPerPixel->rgbRed = ((pDraw->rgbRed * alpha) + 128) / 255;
|
|
pPerPixel->rgbGreen = ((pDraw->rgbGreen * alpha) + 128) / 255;
|
|
pPerPixel->rgbBlue = ((pDraw->rgbBlue * alpha) + 128) / 255;
|
|
|
|
// Finally, reset the 'draw' pixel:
|
|
|
|
pDraw->rgbReserved = 0xaa;
|
|
pDraw->rgbRed = 0;
|
|
pDraw->rgbGreen = 0;
|
|
pDraw->rgbBlue = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" VOID vPerPixelAlpha(
|
|
HWND hwnd)
|
|
{
|
|
LOGFONT logfont;
|
|
HFONT hfont;
|
|
HDC hdcScreen;
|
|
HDC hdc;
|
|
CHAR achAliased[] = "Aliased text.";
|
|
CHAR achAntialiased[] = "Antialiased text.";
|
|
BLENDFUNCTION blend;
|
|
RECT rclSrc;
|
|
HBRUSH hbrush;
|
|
SIZE siz;
|
|
POINT ptlSrc;
|
|
|
|
hdcScreen = GetDC(hwnd);
|
|
|
|
PERPIXELDRAW draw(hdcScreen, 600, 400);
|
|
|
|
hdc = CreateCompatibleDC(hdcScreen);
|
|
|
|
SelectObject(hdc, draw.hbmDrawBitmap());
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// First, draw opaque aliased text:
|
|
|
|
RtlZeroMemory(&logfont, sizeof(logfont));
|
|
|
|
logfont.lfHeight = 100;
|
|
|
|
hfont = CreateFontIndirect(&logfont);
|
|
SelectObject(hdc, hfont);
|
|
|
|
// We want red transparent text:
|
|
|
|
SetTextColor(hdc, RGB(255, 0, 0));
|
|
SetBkMode(hdc, TRANSPARENT);
|
|
|
|
ExtTextOut(hdc, 0, 0, 0, NULL, achAliased, strlen(achAliased), NULL);
|
|
DeleteObject(hfont);
|
|
|
|
// Make what we've drawn opaque by using an alpha of 255:
|
|
|
|
draw.vConvertColorToPerPixelAlpha(255);
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Next, draw opaque anti-aliased text:
|
|
|
|
RtlZeroMemory(&logfont, sizeof(logfont));
|
|
|
|
logfont.lfHeight = 100;
|
|
logfont.lfQuality = ANTIALIASED_QUALITY;
|
|
|
|
hfont = CreateFontIndirect(&logfont);
|
|
SelectObject(hdc, hfont);
|
|
|
|
// For 'vConvertGrayScaleToPerPixelAlpha', we must draw in black and white.
|
|
// Switch to opaque text mode for a better result:
|
|
|
|
SetTextColor(hdc, RGB(255, 255, 255));
|
|
SetBkColor(hdc, RGB(0, 0, 0));
|
|
SetBkMode(hdc, OPAQUE);
|
|
|
|
ExtTextOut(hdc, 0, 100, 0, NULL, achAntialiased, strlen(achAntialiased), NULL);
|
|
DeleteObject(hfont);
|
|
|
|
// Note that this is where we choose the color -- it will be red:
|
|
|
|
draw.vConvertGrayScaleToPerPixelAlpha(RGB(255, 0, 0));
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Now, draw opaque aliased text with an alpha background rectangle:
|
|
|
|
// First, draw the background rectangle. We'll use a red brush with an
|
|
// alpha of ~40%:
|
|
|
|
hbrush = CreateSolidBrush(RGB(128, 0, 0));
|
|
SelectObject(hdc, hbrush);
|
|
PatBlt(hdc, 0, 200, 1024, 100, PATCOPY);
|
|
DeleteObject(hbrush);
|
|
|
|
draw.vConvertColorToPerPixelAlpha(0x60);
|
|
|
|
// Now, draw the text:
|
|
|
|
RtlZeroMemory(&logfont, sizeof(logfont));
|
|
|
|
logfont.lfHeight = 100;
|
|
hfont = CreateFontIndirect(&logfont);
|
|
SelectObject(hdc, hfont);
|
|
SetTextColor(hdc, RGB(255, 0, 0));
|
|
SetBkMode(hdc, TRANSPARENT);
|
|
|
|
ExtTextOut(hdc, 0, 200, 0, NULL, achAliased, strlen(achAliased), NULL);
|
|
DeleteObject(hfont);
|
|
|
|
// Make what we've drawn opaque by using an alpha of 255:
|
|
|
|
draw.vConvertColorToPerPixelAlpha(255);
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Now, draw opaque antialiased text with an alpha background rectangle:
|
|
|
|
// First, draw the background rectangle. We'll use a red brush with an
|
|
// alpha of ~40%:
|
|
|
|
hbrush = CreateSolidBrush(RGB(128, 0, 0));
|
|
SelectObject(hdc, hbrush);
|
|
PatBlt(hdc, 0, 300, 1024, 100, PATCOPY);
|
|
DeleteObject(hbrush);
|
|
|
|
draw.vConvertColorToPerPixelAlpha(0x60);
|
|
|
|
// Now, draw the text:
|
|
|
|
RtlZeroMemory(&logfont, sizeof(logfont));
|
|
|
|
logfont.lfHeight = 100;
|
|
logfont.lfQuality = ANTIALIASED_QUALITY;
|
|
hfont = CreateFontIndirect(&logfont);
|
|
SelectObject(hdc, hfont);
|
|
|
|
// For 'vConvertGrayScaleToPerPixelAlpha', we must draw in black and white.
|
|
// We have to use 'transparent' text mode, otherwise we'll overwrite the
|
|
// background color we just drew:
|
|
|
|
SetTextColor(hdc, RGB(255, 255, 255));
|
|
SetBkMode(hdc, TRANSPARENT);
|
|
ExtTextOut(hdc, 0, 300, 0, NULL, achAntialiased, strlen(achAntialiased), NULL);
|
|
DeleteObject(hfont);
|
|
|
|
// Note that this is where we choose the color -- it will be red:
|
|
|
|
draw.vConvertGrayScaleToPerPixelAlpha(RGB(255, 0, 0));
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Finally, convert everything to a per-pixel sprite:
|
|
|
|
SelectObject(hdc, draw.hbmPerPixelBitmap());
|
|
|
|
blend.BlendOp = AC_SRC_OVER;
|
|
blend.BlendFlags = 0;
|
|
blend.SourceConstantAlpha = 0xff; // No constant alpha
|
|
blend.AlphaFormat = AC_SRC_ALPHA; // There's a per-pixel alpha
|
|
|
|
vCreateSprite(hdcScreen, 0, 0);
|
|
|
|
ptlSrc.x = 0;
|
|
ptlSrc.y = 0;
|
|
siz.cx = 600;
|
|
siz.cy = 400;
|
|
|
|
blend.BlendOp = AC_SRC_OVER;
|
|
blend.BlendFlags = 0;
|
|
blend.SourceConstantAlpha = 0x80; // !!! 0xff; // No constant alpha
|
|
blend.AlphaFormat = AC_SRC_ALPHA; // There's a per-pixel alpha
|
|
|
|
if (!UpdateLayeredWindow(ghSprite,
|
|
hdcScreen,
|
|
&gptlSprite,
|
|
&siz,
|
|
hdc,
|
|
&ptlSrc,
|
|
0,
|
|
&blend,
|
|
ULW_ALPHA))
|
|
{
|
|
MessageBox(0, "UpdateSprite failed", "Uh oh", MB_OK);
|
|
}
|
|
|
|
ReleaseDC(hwnd, hdcScreen);
|
|
|
|
DeleteObject(hdc);
|
|
}
|
|
|