Hello,
I need to draw text with a semi-transparent background that look exactly like the HUD. But I can not use DrawMultipleHUDText()
for that, because it's super slow (I need to draw a lot of text entries).
I'm now using a GeClipMap
to draw the text, then I get the clipmap's bitmap, and use BaseDraw::DrawTexture()
for the actual drawing. Still, to get the semi-transparent background of the HUD, I need a proper alpha channel. Using GeClipMap::SetPixelRGBA()
does not help, since the GeClipMap
is not able to return a BaseBitmap
with alpha channel.
The next thing I tried is using bitmap->AddChannel(true, true)
to create an alpha channel. I then iterate over all pixels in the bitmap, and set values to the alpha channel depending on the pixel values in the bitmap. The values are being set correctly, I checked that, but in the viewport there seems to be only "visible" and "invisible", no semi-transparency. Of course, I use DRAW_ALPHA::NORMAL
in the DrawTexture()
call.
The only thing that gives me semi-transparency is using DRAW_ALPHA::FROM_IMAGE
. But then the alpha is depending on the actual pixel colors in the bitmap, and I couldn't find any color that would give me the exact HUD look. It is fast, though, much faster than DrawMultipleHUDText()
.
Another problem is that the text does look almost perfectly like the HUD (close enough at least), but only on computers with retina screen. On computers with low-res screens, it looks washed out and blurred. That is, if I use DRAW_TEXTUREFLAGS::INTERPOLATION_LINEAR
. Using DRAW_TEXTUREFLAGS::INTERPOLATION_NEAREST
makes the text look broken, as if it had been scaled down. But I don't scale it, it should be drawn exactly in the right size (see me using the bitmaps width and height for the texture vertex coordinates).
Here's the code for texture creation:
BaseBitmap* MyClass::CreateTextBitmap(const String &text, const Vector &textColor, const Vector &bgColor, Float bgOpacity, Int32 margin)
{
// _clipmap is a member of MyClass
if (!_clipmap)
{
_clipmap.Assign(GeClipMap::Alloc());
if (!_clipmap)
return nullptr;
}
// Dummy draw, just to get the text dimensions
if (_clipmap->Init(0, 0, 32) != IMAGERESULT::OK)
return nullptr;
_clipmap->BeginDraw();
Int32 width = _clipmap->GetTextWidth(text) + margin * 2;
Int32 height = _clipmap->GetTextHeight() + margin * 2;
_clipmap->EndDraw();
_clipmap->Destroy();
// Begin actual drawing
if (_clipmap->Init(width, height, 32) != IMAGERESULT::OK)
return nullptr;
_clipmap->BeginDraw();
_clipmap->EndDraw();
width = _clipmap->GetBw();
height = _clipmap->GetBh();
_clipmap->BeginDraw();
// Background. Fill clipmap with a rectangle, overpainting possibly existing text
// Drawn with alpha = 255, because this alpha value just defines the transparency of the drawn color over the background.
// The actual background alpha is set later.
const Int32 backgroundColorR = (Int32)(bgColor.x * 255.0);
const Int32 backgroundColorG = (Int32)(bgColor.y * 255.0);
const Int32 backgroundColorB = (Int32)(bgColor.z * 255.0);
_clipmap->SetColor(backgroundColorR, backgroundColorG, backgroundColorB, 255);
_clipmap->FillRect(0, 0, width - 1, height - 1);
const Int32 backgroundOpacity = (Int32)(bgOpacity * 255.0);
// Text
// Drawn with alpha = 255, because this alpha value just defines the transparency of the drawn color over the background.
_clipmap->SetColor((Int32)(textColor.x * 255.0), (Int32)(textColor.y * 255.0), (Int32)(textColor.z * 255.0), 255);
_clipmap->TextAt(margin, margin, text);
// Make corner pixels transparent (looks more like HUD then)
_clipmap->SetColor(0, 0, 0, 255);
_clipmap->SetPixel(0, 0);
_clipmap->SetPixel(0, height - 1);
_clipmap->SetPixel(width - 1, 0);
_clipmap->SetPixel(width - 1, height - 1);
_clipmap->EndDraw();
// Get bitmap from clipmap
BaseBitmap *bitmap = _clipmap->GetBitmap()->GetClone();
if (!bitmap)
return nullptr;
// Iterate over pixels, set the actual alpha values
BaseBitmap *alphaBitmap = bitmap->AddChannel(true, true);
if (!alphaBitmap)
return nullptr;
alphaBitmap = bitmap->GetInternalChannel();
if (!alphaBitmap)
return nullptr;
for (Int32 y = 0; y < bitmap->GetBh(); ++y)
{
for (Int32 x = 0; x < bitmap->GetBw(); ++x)
{
UInt16 r = 0, g = 0, b = 0;
bitmap->GetPixel(x, y, &r, &g, &b);
if (r == backgroundColorR && g == backgroundColorG && b == backgroundColorB)
bitmap->SetAlphaPixel(alphaBitmap, x, y, 255 - backgroundOpacity);
else if (r == 0 && g == 0 && b == 0)
bitmap->SetAlphaPixel(alphaBitmap, x, y, 0);
else
bitmap->SetAlphaPixel(alphaBitmap, x, y, 255);
}
}
return bitmap;
}
And this is the code I use for drawing:
void MyClass::DrawRect(BaseDraw *bd, BaseBitmap *bmp, const Vector* padr4, const Vector &color, DRAW_ALPHA alphamode, DRAW_TEXTUREFLAGS flags)
{
if (!bd || !bmp || !padr4)
return;
// Vertex normal array
static const Vector vnadr[4] = {
Vector(0.0, 1.0, 0.0),
Vector(0.0, 1.0, 0.0),
Vector(0.0, 1.0, 0.0),
Vector(0.0, 1.0, 0.0)
};
// UV coordinate array
static const Vector uvadr[4] = {
Vector(0.0, 0.0, 0.0),
Vector(1.0, 0.0, 0.0),
Vector(1.0, 1.0, 0.0),
Vector(0.0, 1.0, 0.0)
};
// Color array
const Vector cadr[4] = {
color,
color,
color,
color
};
bd->DrawTexture(bmp, padr4, cadr, vnadr, uvadr, 4, alphamode, flags);
}
And here's the code that starts everything inside my SceneHook plugin's Draw() call:
// Set draw matrix to screen space, prevent drawn elements from being lit
bd->SetMatrix_Screen();
bd->SetLightList(BDRAW_SETLIGHTLIST_NOLIGHTS);
String valueText = FormatNumber(123.45, FORMAT_METER, 25);
BaseBitmap *valueTexture(CreateTextBitmap(valueText, Vector(0.9), Vector(0.25), 0.2, 2));
const Float valueTextureWidth = valueTexture->GetBw();
const Float valueTextureHeight = valueTexture->GetBh();
const Vector textPadr[4] = {
textUpperLeftCornerPos,
textUpperLeftCornerPos + Vector(valueTextureWidth, 0.0, 0.0),
textUpperLeftCornerPos + Vector(valueTextureWidth, valueTextureHeight, 0.0),
textUpperLeftCornerPos + Vector(0.0, valueTextureHeight, 0.0),
};
const Vector hudTextColor = bd->GetParameterData(BASEDRAW_HUD_TEXTCOLOR).GetVector();
DrawRect(bd, valueTexture, textPadr, hudTextColor, DRAW_ALPHA::NORMAL, DRAW_TEXTUREFLAGS::INTERPOLATION_LINEAR);
Any help would be very appreciated. As I said, the only goal here is just to have a function that draws text with semi-transparent background into the viewport that looks exactly like the HUD, just faster.
Cheers,
Frank