98
The shadow offset is to use a small transparent grayed shadow (blur effect), to enhance the visibility of a white text when shown hover a light background.
The ZD_DrawTextToCtrl as been superseeded by ZD_DrawTextToCtrlEx
long ZD_DrawTextToCtrl (IN HWND hCtrl, IN WCHAR* sUseText, IN long x, IN long y, IN DWORD ColrARGB,
IN WCHAR* sUseFont, IN long UseSize, IN long ObjID, IN long ZS_STYLE, IN short Use3D, IN long UseStrFormat) { // dllexport
return ZD_DrawTextToCtrlEx(hCtrl, sUseText, x, y, 0, 0, ColrARGB, sUseFont, UseSize, ObjID, ZS_STYLE, Use3D, UseStrFormat);
}
long ZD_DrawTextToCtrlEx (IN HWND hCtrl, IN WCHAR* UseText, IN long x, IN long y, IN long w, IN long h, IN DWORD ColrARGB,
IN WCHAR* UseFont, IN long UseSize, IN long ObjID, IN long ZS_STYLE, IN short Use3D, IN long UseStrFormat) { // dllexport
long nRet = 0;
if (IsWindow(hCtrl)) {
RECTF layoutRect = {0}, boundingBox = {0};
HDC hIC;
LONG_PTR graphics = 0, strFormat = 0;
long nExistItem = 0, nItem = 0;
long ObjectType = OBJECT_TEXT;
BYTE nPrivateFont = UsePrivateFont(UseFont);
nExistItem = DetectObjectItem(hCtrl, ObjectType, ObjID);
if (nExistItem > -1) { // if the object already exist { we ReUse it
nItem = nExistItem;
// We clear the previous font being used
if (g_zObj[nItem].curfont) { GdipDeleteFont(g_zObj[nItem].curfont); } // Delete the font object
if (g_zObj[nItem].privateFont == 0) { GdipDeleteFontFamily(g_zObj[nItem].fontfam); } // Delete the font family object
} else {
nItem = zGetNewItem();
}
GdipCreateStringFormat(NULL, NULL, strFormat);
if (nPrivateFont == 0) {
GdipCreateFontFamilyFromName(UseFont, 0, g_zObj[nItem].fontfam);
} else {
g_zObj[nItem].fontfam = LoadPrivateFont(UseFont); // Create a PrivateFont
}
GdipCreateFont(g_zObj[nItem].fontfam, (float) UseSize, FontStyleRegular, UnitPixel, g_zObj[nItem].curfont);
hIC = zDisplayDC();
GdipCreateFromHDC(hIC, graphics);
long codepointsFitted = 0, linesFilled = 0;
GdipMeasureString(graphics, UseText, lstrlen(UseText), g_zObj[nItem].curfont, layoutRect, strFormat, boundingBox, codepointsFitted, linesFilled);
GdipDeleteGraphics(graphics);
GdipDeleteStringFormat(strFormat);
DeleteDC(hIC);
g_zObj[nItem].hwnd = hCtrl;
g_zObj[nItem].objtype = MAKLNG(ObjectType, 0);
//wcscpy_s(g_zObj[nItem].usefont, UseFont); // WARNING 64 bytes only
// START 09-15-2017 WARNING 64 bytes only in zObj.usefont
long nLen = lstrlen(UseFont);
long nLenUsefont = strSize(g_zObj[nItem].usefont);
if (nLen > nLenUsefont) {
WCHAR PathName[MAX_PATH] = {0};
WCHAR FilName[MAX_PATH] = {0};
zSplitN(UseFont, &PathName[0], &FilName[0]);
if (nPrivateFont) {
UsePrivateFontPath(PathName, 1);
nPrivateFont = 2;
}
MoveMemory(g_zObj[nItem].usefont, FilName, min(lstrlen(FilName), nLenUsefont) * sizeof(WCHAR));
} else {
MoveMemory(g_zObj[nItem].usefont, UseFont, min(nLen, nLenUsefont) * sizeof(WCHAR));
}
// END 09-15-2017 WARNING 64 bytes only in zObj.usefont
g_zObj[nItem].usesize = UseSize;
g_zObj[nItem].argb = ColrARGB;
g_zObj[nItem].use3d = Use3D;
g_zObj[nItem].x1 = x;
g_zObj[nItem].y1 = y;
g_zObj[nItem].style = ZS_STYLE;
if (CheckStyle(ZS_STYLE, ZS_VISIBLE)) {
g_zObj[nItem].visible = TRUE;
}
if (nExistItem < 0) { // We put the new object on top of z-order
// and we keep it unchanged when it already exist
zUpdateZorder(hCtrl, nItem);
}
g_zObj[nItem].metacount = lstrlen(UseText); // 0;
MoveMemory(&g_zObj[nItem].metadata[0], UseText, g_zObj[nItem].metacount * sizeof(WCHAR));
if (w == 0) { w = (long)(boundingBox.right + 1.0f); }
if (h == 0) { h = (long)(boundingBox.bottom + 2.0f); }
g_zObj[nItem].x2 = w;
g_zObj[nItem].y2 = h;
g_zObj[nItem].id = ObjID;
g_zObj[nItem].strformat = UseStrFormat;
g_zObj[nItem].scale = 1.0f;
g_zObj[nItem].framecount = 0;
g_zObj[nItem].frametouse = 0;
g_zObj[nItem].privateFont = nPrivateFont;
nRet = -1;
}
return nRet;
}
See for Use3D
You can also use this one to change an object shadow property.
void ZD_SetObjectUse3Dshadow (IN long ObjID, IN short Use3D) { // dllexport
long nItem = zItemFromID(ObjID);
if (nItem > -1) { g_zObj[nItem].use3d = Use3D; }
}
and here is how it is rendered into the DC
long ZD_DrawTextToDC (IN HDC hDC, IN WCHAR* UseText, IN long x, IN long y, IN DWORD ColrARGB,
IN WCHAR* UseFont, IN long UseSize, IN short Use3D, IN long UseStrFormat) { // dllexport
long nRet = 0;
RECTF layoutRect = { 0 };
RECTF boundingBox = { 0 };
LONG_PTR graphics = 0, TempFont = 0, fontfam = 0, strFormat = 0;
wstring sUseText = (WCHAR*) UseText;
BYTE nPrivateFont = UsePrivateFont(UseFont);
// Create matching font
if (nPrivateFont == 0) {
GdipCreateFontFamilyFromName(UseFont, 0, fontfam);
} else {
fontfam = LoadPrivateFont(UseFont); // Create a PrivateFont
}
if (fontfam) {
GdipCreateFont(fontfam, (float) UseSize, FontStyleRegular, UnitPixel, TempFont);
if (TempFont) {
// Shadow offset use NULL if you don't want a shadow,
// use positive value to display shadow right, negative value to display shadow left
// Draw the string
GdipCreateStringFormat(0, 0, strFormat);
GdipCreateFromHDC(hDC, graphics);
sUseText = TRIM$(sUseText, $SPACE);
long codepointsFitted = 0, linesFilled = 0, nLen = (long) sUseText.length();
GdipMeasureString(graphics, (WCHAR*) sUseText.c_str(), nLen, TempFont, layoutRect, strFormat, boundingBox, codepointsFitted, linesFilled);
GdipDeleteStringFormat(strFormat);
long xWidth = roundL(boundingBox.right + 0.5f);
long yHeight = roundL(boundingBox.bottom + 1.5f);
if (Use3D) { // Fly!
RECT rb; SetRect(&rb, x, y, x + xWidth, y + yHeight);
BlurTextPlus(hDC, UseText, rb, TempFont, 4, (LONG_PTR)UseStrFormat);
}
nRet = DrawStringFormatedEx(graphics,
(WCHAR*) sUseText.c_str(),
x, y,
xWidth,
yHeight,
ColrARGB,
TextRenderingHintAntiAlias,
TempFont,
Use3D,
UseStrFormat);
GdipDeleteGraphics(graphics);
GdipDeleteFont(TempFont); // Delete the font object
}
if (nPrivateFont == 0) { GdipDeleteFontFamily(fontfam); } // Delete the font family object
}
return nRet;
}
And the BlurTextPlus() procedure that is used to create the 3D blur effect
void BlurTextPlus(IN HDC hDC, IN WCHAR* sTxt, IN RECT &rb, IN LONG_PTR UseFont, IN long TextRendering, IN LONG_PTR nStrFormat) {
BITMAP bm = {0};
long nRet = 0, P = 0, nDiv = 0, xDiv = 0, yDiv = 0, xWidth = 0, yHeight = 0, UseColor = 0;
LONG_PTR graphics = 0, img = 0, imgAttr = 0, lpCallback = 0, callbackdata = 0;
HDC hDC1 = 0, hDC2 = 0;
HBITMAP hBM1 = 0, hBM2 = 0;
float sT = 0.0f;
RECT rc;
BYTE Alpha = 0;
nDiv = 3; // 4; // Blur level
xWidth = Width(rb);
yHeight = Height(rb);
SetRect(&rc, 0, 0, xWidth, yHeight);
xDiv = (long) (xWidth / nDiv);
yDiv = (long) (yHeight / nDiv);
hDC1 = CreateCompatibleDC(hDC);
hBM1 = zCreateDIBSection(hDC1, xWidth, yHeight, 32);
HGDIOBJ oldBM1 = SelectObject(hDC1, hBM1);
UseColor = ZD_ARGB(192, 10, 10, 10);
if (GdipCreateFromHDC(hDC1, graphics) == 0) {
DrawStringFormatedEx(graphics, sTxt, 0, 0, xWidth, yHeight, UseColor, TextRendering, UseFont, 0, nStrFormat);
GdipDeleteGraphics(graphics);
}
// Copy from DC source
hDC2 = CreateCompatibleDC(hDC);
hBM2 = zCreateDIBSection(hDC2, xDiv, yDiv, 32);
HGDIOBJ oldBM2 = SelectObject(hDC2, hBM2);
// Perform reduction
if (GdipCreateFromHDC(hDC2, graphics) == 0) {
img = zBitmapToImage(hDC1);
GdipSetInterpolationMode(graphics, 2);
GdipDrawImageRectRectI(graphics, img, 0, 0, xDiv, yDiv, 0, 0, xWidth, yHeight, 2, imgAttr, lpCallback, callbackdata);
GdipDeleteGraphics(graphics);
GdipDisposeImage(img);
}
DeleteObject(hBM1);
SelectObject(hDC1, oldBM1); DeleteDC(hDC1);
// Blur from reduction
if (GdipCreateFromHDC(hDC, graphics) == 0) {
//Alpha = zGetAValue(UseColor);
Alpha = LOBYTE((UseColor) >> 24);
// Use this to setup the transparency level (changing the alpha channel)
if (Alpha < 255) {
GetObject(hBM2, sizeof(bm), &bm);
sT = (float) (min(254, Alpha) / 255.0f);
BYTE* A = (BYTE*) bm.bmBits;
long nBitCount = bm.bmWidth * bm.bmHeight * 4 + 3;
for (P = 3; P < nBitCount; P += 4) {
A[P] = (BYTE) (A[P] * sT);
}
}
img = zBitmapToImage(hDC2);
GdipSetInterpolationMode(graphics, 2);
for (P = 0; P < min(max(nDiv - 1, 1), 4); P++) {
nRet = GdipDrawImageRectRectI(graphics, img, rb.left, rb.top, xWidth, yHeight, 0, 0, xDiv, yDiv, 2, imgAttr, lpCallback, callbackdata);
}
GdipDeleteGraphics(graphics);
GdipDisposeImage(img);
}
DeleteObject(hBM2);
SelectObject(hDC2, oldBM2); DeleteDC(hDC2);
}
If you want to create the same effects that you did with the code you posted on CodeProject,
then you can use two or several overlapping text sprite objects, using a specific x,y offset.