ObjReader Community
GDImage => The concept => Topic started by: Patrice Terrier on June 04, 2023, 10:03:13 pm
-
Second post of a series, translated from the "WinDev tutorial",
to explain the use of GDImage64 in procedural* programming mode with Visual Studio 2022.
About Tutor_02
This tutor introduces the use of sprites within a GDImage control.
They work like icons on the Windows desktop, and you can drag them around with the mouse.
They can also be used to build a complete graphic interface, and mimic window's child controls.
It is possible to create events to process specific messages in the GDImage control.
See in the addGDImageControl how the ZI_EventMessageEx is used to preprocess the messages in the GDImageCallBack function.
All child controls are created in the CreateControls() procedure.
The sprites are loaded from the LoadSpriteResource() procedure.
All sprites are stored along the z-order, from bottom to top, in their order of appearance.
In the CHM help file, search for: ZD_SetObjectZorder and ZD_GetObjectZorder.
Each sprite can use a label property (friendly name), it is shown in the status bar when you click on a specific object with the left mouse button
The label property can also be used to display a tooltip using the ZI_SetToolTipText API.
Sprite properties:
ZD_HIDE = 0; // FALSE
ZD_SHOW = 1; // TRUE
ZS_HIDDEN = ZD_HIDE;
ZS_VISIBLE = ZD_SHOW; // VISIBLE, same as WS_VISIBLE
ZS_SCROLL = 2; // MOVE with scroll bars, default is FIX (do not scroll)
ZS_DRAFT = 4;
ZS_INACTIVE = 2;
ZD_ORDER_TOP = 0xFFFFFF;
ZD_ORDER_BOTTOM = 0xFFFF;
ZD_Normal = 0; // Orientation unchanged (default)
ZD_Flip = 1; // Swap top / bottom
ZD_Reverse = 2; // Swap left / right
Sprite types:
OBJECT_TEXT = 1;
OBJECT_ELLIPSE = 2;
OBJECT_RECT = 3;
OBJECT_CURVE = 4;
OBJECT_ARROW = 5;
OBJECT_POLYLINE = 6;
OBJECT_BITMAP = 7;
OBJECT_BEZIER = 8;
OBJECT_POLYPOLYGON = 9;
OBJECT_TEXTBITMAP = 10;
All child controls are anchored within the main window using the ZI_SetAnchorMode API, and the appropriate ANCHOR_PROPERTY.
It is also possible to use anchor properties with the sprite ZD_SetObjectAnchorMode API.
Last but not least,
the size of the standalone binary EXE is only 22 Kb.
(http://www.objreader.com/download/images/Tutor_02.jpg)
* procedural programming mode, is based on direct use of the FLAT API (Windows SDK) that is the core meat of the OS.
-
Hi Patrice,
In line 99 of Main.cpp, I changed nShadowOffet from 1 to 10. It does not seem to change anything to the Text display.
DWORD nTextColor = ZD_ARGB(200, 250,250,255);
long nFontSize = 40;
short nShadowOffet = 10; // <--- changed from 1 to 10
WCHAR zFontToUse[] = { L"Times New Roman" };
ZD_DrawTextToCtrl(gP.hGDImage, L"GDImage Aero Glass", 20, 20, nTextColor, zFontToUse, nFontSize, ID_OBJECT_TEXT, ZS_VISIBLE, nShadowOffet, ZD_TextHorzUp);
ZD_SetObjectScroll(ID_OBJECT_TEXT, TRUE); // The TEXT sprite move with the scrollbar
-
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.
-
Hi Patrice,
I modified your Tutor 2 code to load image from resource but the image does not appear. I have attached my Tutor 2 here.
//hBitmap = ZI_CreateBitmapFromFile(Resource(L"aero.png"), bmW, bmH);
hBitmap = ZI_LoadFromResource(NULL, (WCHAR*)MAKEINTRESOURCE(IDR_APPLE));
//ZD_DrawBitmapToCtrl(gP.hGDImage, 169, 31, hBitmap, nColorARGB, ID_OBJECT_SPRITE + 1, ZS_VISIBLE);
ZD_DrawBitmapToCtrl(gP.hGDImage, 19, 19, hBitmap, nColorARGB, ID_OBJECT_SPRITE + 1, ZS_VISIBLE);
ZD_SetObjectImageLabel(ID_AERO_GLASS, L"Aero glass"); // Each sprite can use a specific label (friendly name)
ZD_SetObjectScroll(ID_AERO_GLASS, TRUE); // This sprite follow the scrollbar move.
Am I putting my resource in the wrong place? Can you check my Tutor_02.rc file?
Thanks.
-
Hello Shao Voon
I shall use WinMerge to see the code difference, and will let you know here, once done.
Added:
Here is the code for ZI_LoadFromResource
HBITMAP ZI_LoadFromResource (IN HWND hWnd, IN WCHAR* sName) { // dllexport
HBITMAP hbmReturn = 0;
HRSRC hResource = FindResource(zInstance(), (LPCWSTR) sName, RT_RCDATA);
if (hResource) {
DWORD resourceSize = SizeofResource(zInstance(), hResource);
if (resourceSize) {
HGLOBAL hGlobal = LoadResource(zInstance(), hResource);
if (hGlobal) {
LPVOID pResourceData = LockResource(hGlobal);
if (pResourceData) {
HGLOBAL hMemory = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD, resourceSize);
if (hMemory) {
LPVOID pBuffer = GlobalLock(hMemory);
if (pBuffer) {
memcpy(pBuffer, pResourceData, resourceSize);
LPSTREAM pStream = 0;
if (CreateStreamOnHGlobal(hMemory, FALSE, &pStream) == 0) {
LONG_PTR hImage;
if (GdipCreateBitmapFromStream(pStream, hImage) == 0) {
if (hWnd) {
if (zSetGdipImageHandle(hWnd, hImage)) { ZI_UpdateWindow(hWnd, TRUE); }
} else { // Return a standard bitmap
long background = 0;
GdipCreateHBITMAPFromBitmap(hImage, hbmReturn, background);
GdipDisposeImage(hImage); // Delete image
}
}
//IUnknown_Release(pStream);
ReleaseObject((PFUNKNOWN*) pStream);
}
}
GlobalUnlock(hMemory);
}
GlobalFree(hMemory);
}
FreeResource(hGlobal);
}
}
}
return hbmReturn;
}
In order to use RT_RCDATA, you must put your resource image in the correct folder location to embed it inside of your binary EXE.
RT_RCDATA is the only way to embed any RAW_DATA, including, audio, dll, png images, etc.
Example of Resource.rc file used in PowerBASIC, that should be almost the same in C/C++
PROGRAM ICON ZAP.ICO
REDMASK RCDATA DISCARDABLE REDMASK.GIF
ZMAGIC RCDATA DISCARDABLE ZMAGIE.GIF
STAR CURSOR STAR.CUR
// Manifest info for WinXP theme support
#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
#define RT_MANIFEST 24
#define CONTROL_PANEL_RESOURCE_ID 123
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "gdw.man"
See this link
https://learn.microsoft.com/en-us/windows/win32/menurc/rcdata-resource
-
I declare my IDR_APPLE in the rc file. It crashed the GDImage64.dll.
IDR_APPLE RCDATA DISCARDABLE "res\\apple.png"
-
I shall give it a try...
-
Shao Voon
Here is the solution.
http://www.objreader.com/index.php?topic=297.msg6356#msg6356