The problem is that the anti-aliased area of the text will be opaque and
show as a dark halo around the letters, because the letters are not actually
anti-aliased onto the image.
I think I would need my bitmap to have an alpha channel with all of the
pixels set to transparent, except the text which would be opaque. The
anti-aliased area around each letter would have various values of
semi-transparency. This should allow the text to be anti-aliased against the
image which shows through the transparent area.
Is it possible to do alpha anti-aliasing like this in plain GDI - if not, is
there a trick to doing this?
If it really is impossible in GDI, can it be done in GDI+ ?
If your bitmap is a 32-bit (RGBA) DIBSection you can do it yourself,
by examining each pixel in memory and setting the alpha value
accordingly, i.e. based on the R, G and B values of that pixel.
Assuming all your opaque text colors are 'saturated' (at least one of
R, G or B is 255) you can calculate the alpha as max(R,G,B).
Richard.
http://www.rtrussell.co.uk/
To reply by email change 'news' to my forename.
Thanks. That would certainly help, but I think there would still be *some*
black halo around the text as it is still being anti-aliased against a black
background. After a bit of thought, if the text was to be say red, perhaps
the bitmap ought to be *completely* red. And then we would create a
secondary 8-bit greyscale bitmap (if that's possible) of the same X and Y
dimensions. This secondary bitmap would be completely black and we would
write over it in white text - this would effectively create a monochrome
mask which had grey pixels around the edges of the text. Then we could copy
the bytes from the 8-bit bitmap into the alpha bytes of the red bitmap.
Although the red bitmap would be entirely red, you'd only see the red where
the mask defined the letters - the rest would be transparent. Hopefully this
would mean that the red text would be truly anti-aliased against whatever
image was placed beneath it.
If you needed more than 1 colour of text in the bitmap, then you could just
add an extra step to the above - get the bounding rectangle of the text,
expand it by a few pixels and fill that area of the main bitmap with the
required color.
Would that be a better approach?
I guess there isn't a quick way to copy the bytes from the 8-bit bitmap to
the main bitmap's alpha bytes - it would require a byte-at-a-time copying?
I don't understand. The calculation done by the text renderer should
be:
new_pixel = old_pixel * (1-alpha) + text_color * alpha
(where alpha=0 means transparent and alpha=1 means opaque).
If you're rendering the text onto a black background, 'old_pixel' is
zero. Therefore the equation reduces to:
new_pixel = text_color * alpha
So to discover alpha (which is what you need) rearrange the equation
as:
alpha = new_pixel / text_color
How does that result in a "black halo" ?
It was more of an instintive thing on my part, but I've just done some more
searching and found this.
http://www.xsibase.com/forum/index.php?board=12;action=display;threadid=24137
The 1st reply seems to have a good explanation. I *think* you are describing
premultiplied alpha while I was thinking about what he calls "straight"
alpha.
For your scenario, you will have to take the result of drawing-text with
GDI on top of a surface with a known background (e.g: black, white)
and then you will have to manually fix-up the alpha channel based
upon the value of the colored pixels.
There is decent approximation of this in DrawThemedTextEx(DTT_COMPOSITED).
That API will do for you what was suggested earlier:
run over every pixel, and, calculated an alpha value compatible with the
colors,
so that the result is a valid premultiplied per-pixel-alpha bitmap.
At that point, you can un-premultiply, if your application so
desire/require.
[side note: if you are thinking of using the text bitmap for a texture for
DX/OpenGl,
it would be easier to set-up the blend-state of your device/context to
texture
from the bitmap, and blend that with the render-target current value].
--
This posting is provided "AS IS" with no warranties, and confers no rights.
Use of any included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm
"Stanza" <sta...@devnull.com> wrote in message
news:wa-dne2TTtai7w3X...@brightview.com...
Yeah, right. :-(
> I *think* you are describing premultiplied alpha while I was
> thinking about what he calls "straight" alpha.
Not really. My max(R,G,B) algorithm calculates the alpha values, and
the alpha values in the two cases are exactly the same! What differs
is not the alpha value, but the associated RGB values. In the
"premultiplied" case the RGB values are multiplied by the alpha value,
in the "straight" case they are not.
You are right that what ends up in the bitmap after you apply my
algorithm is equivalent to the "premultiplied" case (the RGB values as
originally rendered are 'premultiplied', and they're not altered), but
that's exactly what you need if you're going to use AlphaBlend to
combine the bitmap with your final background!
Just try my suggestion. You should find that it works correctly (with
'saturated' text colors).
-write text using cleartype
-you can't use it directly, because cleartype would require an alpha per RGB
component, which you don't have. You have 2 options: 1 is to keep the
cleartype trick, and add a shadow or halo around the text to get away with
it. Your other option is simply to turn this colored cleartype text into grey
(using RGB weighting, you'll find the weighting values on the net)
-then make the alpha the same as the level of white
This C++ code is approved.
It works perfectly on Windows 7 and Vista.
There are no black or grey halos around the printed text!
The text is not blurred.
It looks absolutely clean in any color on any background!
Although this code runs seamlessly on the latest Windows 7 it requires only
API's which exist since Windows 95!
I tested with AlphaBlend() which does !NOT! work.
The text looks really ugly!
This code does not use AlphaBlend()
struct kBmpData
{
HDC h_DC;
HBITMAP h_Bmp;
HBITMAP h_OldBmp;
DWORD* pu32_Bits;
int s32_Width;
int s32_Height;
kBmpData() // Constructor
{
memset(this, 0, sizeof(kBmpData));
}
void Destroy()
{
SelectObject(h_DC, h_OldBmp);
DeleteObject(h_Bmp);
DeleteDC(h_DC);
memset(this, 0, sizeof(kBmpData));
}
};
// The required variables:
int s32_Width, s32_Height = the dimensions of your Bitmap
DWORD u32_Left, u32_Top = the position of the text inside the bitmap
HDC = your drawing DC from GetDC(h_Wnd)
HFONT h_Font = your Font
COLORREF c_Text = your text color
WCHAR* pu16_Text = L"Hello World of transparent good old GDI";
// This function creates a 32 Bit Device Independent Bitmap (DIB) of the
desired size:
BOOL CreateAndSelectAlphaBitmap(HDC h_DC, int s32_Width, int s32_Height,
kBmpData* pk_Bmp)
{
BITMAPINFO k_Info;
memset(&k_Info, 0, sizeof(BITMAPINFOHEADER));
k_Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
k_Info.bmiHeader.biWidth = s32_Width;
k_Info.bmiHeader.biHeight = -s32_Height; // negative --> top-down DIB
k_Info.bmiHeader.biPlanes = 1;
k_Info.bmiHeader.biBitCount = 32;
k_Info.bmiHeader.biCompression = BI_RGB;
pk_Bmp->s32_Width = s32_Width;
pk_Bmp->s32_Height = s32_Height;
// on error returns NULL for h_Bmp and pu32_Bits
pk_Bmp->h_Bmp = CreateDIBSection(h_DC, &k_Info, DIB_RGB_COLORS,
(void**)&pk_Bmp->pu32_Bits, NULL, 0);
if (!pk_Bmp->h_Bmp)
return FALSE;
pk_Bmp->h_DC = CreateCompatibleDC(h_DC);
if (!pk_Bmp->h_DC)
return FALSE;
pk_Bmp->h_OldBmp = (HBITMAP)SelectObject(pk_Bmp->h_DC, pk_Bmp->h_Bmp);
if (!pk_Bmp->h_OldBmp)
return FALSE;
return TRUE;
}
You have to call CreateAndSelectAlphaBitmap() TWICE!
You need one Bitmap which holds the background over which you want to paint
the text.
This backgound must have a valid Alpha channel.
And another Bitmap which is used temporarily to paint the text.
What results in pk_Backgr can than be outputted via BitBlt() into the DC of
the window
BOOL WriteText(kBmpData* pk_Backgr, kBmpData* pk_Text, HFONT h_Font,
COLORREF c_Text,
WCHAR* p_u16Text, DWORD u32_Left, DWORD u32_Top)
{
int s32_Pixels = pk_Text->s32_Width * pk_Text->s32_Height;
// set all pixels black
memset(pk_Text->pu32_Bits, 0, s32_Pixels * 4);
HFONT h_OldFont = (HFONT) SelectObject(pk_Text->h_DC, h_Font);
if (!h_OldFont)
return FALSE;
SetBkMode (pk_Text->h_DC, TRANSPARENT);
SetTextColor(pk_Text->h_DC, 0x00FFFFFF); // ALWAYS white !!!
DWORD u32_TxtLen = (DWORD)wcslen(p_u16Text);
BOOL b_Ret = TextOutW(pk_Text->h_DC, u32_Left, u32_Top, p_u16Text,
u32_TxtLen);
SelectObject(pk_Text->h_DC, h_OldFont);
if (!b_Ret)
return FALSE;
DWORD u32_Txt_R = (c_Text) & 0xFF;
DWORD u32_Txt_G = (c_Text >> 8) & 0xFF;
DWORD u32_Txt_B = (c_Text >> 16) & 0xFF;
for (int i=0; i<s32_Pixels; i++)
{
// take the blue channel of the white text as alpha channel
DWORD u32_Alpha = pk_Text->pu32_Bits[i] & 0xFF;
if (!u32_Alpha)
continue; // At this pixel there has no text been printed
DWORD u32_Inv = 255 - u32_Alpha;
DWORD u32_Data = pk_Backgr->pu32_Bits[i];
DWORD u32_Pix_B = (u32_Data) & 0xFF;
DWORD u32_Pix_G = (u32_Data >> 8) & 0xFF;
DWORD u32_Pix_R = (u32_Data >> 16) & 0xFF;
DWORD u32_Pix_A = (u32_Data >> 24) & 0xFF;
u32_Pix_R = (u32_Inv * u32_Pix_R + u32_Alpha * u32_Txt_R) / 255;
u32_Pix_G = (u32_Inv * u32_Pix_G + u32_Alpha * u32_Txt_G) / 255;
u32_Pix_B = (u32_Inv * u32_Pix_B + u32_Alpha * u32_Txt_B) / 255;
u32_Pix_A = (u32_Inv * u32_Pix_A + u32_Alpha * 255 ) / 255;
u32_Data = u32_Pix_B | (u32_Pix_G << 8) | (u32_Pix_R << 16) | (u32_Pix_A
<< 24);
pk_Backgr->pu32_Bits[i] = u32_Data;
}
return TRUE;
}
You saved some hours of work now!
Elmü
> You saved some hours of work now!
> Elmü
No.
Read MSDN code instead from SDK.
10 times better and shorter.