Author Topic: Tutor_02 (C++ VS2022 GDImage64 tutorial)  (Read 2481 times)

Patrice Terrier

  • Administrator
  • *****
  • Posts: 1983
    • zapsolution
Tutor_02 (C++ VS2022 GDImage64 tutorial)
« 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:
Code: [Select]
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:
Code: [Select]
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.


   
* procedural programming mode, is based on direct use of the FLAT API (Windows SDK) that is the core meat of the OS.
« Last Edit: August 18, 2023, 10:32:06 pm by Patrice Terrier »
Patrice
(Always working with the latest Windows version available...)

Shao Voon Wong

  • Newbie
  • *
  • Posts: 12
Re: Tutor_02 (C++ VS2022 GDImage64 tutorial)
« Reply #1 on: June 11, 2023, 11:57:35 am »
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.

Code: [Select]
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

Patrice Terrier

  • Administrator
  • *****
  • Posts: 1983
    • zapsolution
Re: Tutor_02 (C++ VS2022 GDImage64 tutorial)
« Reply #2 on: June 11, 2023, 01:48:06 pm »
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

Code: [Select]
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);
}

Code: [Select]
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.
Code: [Select]
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

Code: [Select]
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

Code: [Select]
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.

« Last Edit: June 11, 2023, 01:53:56 pm by Patrice Terrier »
Patrice
(Always working with the latest Windows version available...)

Shao Voon Wong

  • Newbie
  • *
  • Posts: 12
Re: Tutor_02 (C++ VS2022 GDImage64 tutorial)
« Reply #3 on: June 20, 2023, 07:12:53 am »
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.

Patrice Terrier

  • Administrator
  • *****
  • Posts: 1983
    • zapsolution
Re: Tutor_02 (C++ VS2022 GDImage64 tutorial)
« Reply #4 on: June 20, 2023, 11:20:48 am »
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

« Last Edit: June 20, 2023, 08:36:37 pm by Patrice Terrier »
Patrice
(Always working with the latest Windows version available...)

Shao Voon Wong

  • Newbie
  • *
  • Posts: 12
Re: Tutor_02 (C++ VS2022 GDImage64 tutorial)
« Reply #5 on: June 20, 2023, 01:35:31 pm »
I declare my IDR_APPLE in the rc file. It crashed the GDImage64.dll.

Code: [Select]
IDR_APPLE     RCDATA   DISCARDABLE               "res\\apple.png"

Patrice Terrier

  • Administrator
  • *****
  • Posts: 1983
    • zapsolution
Re: Tutor_02 (C++ VS2022 GDImage64 tutorial)
« Reply #6 on: June 20, 2023, 02:07:11 pm »
I shall give it a try...
Patrice
(Always working with the latest Windows version available...)

Patrice Terrier

  • Administrator
  • *****
  • Posts: 1983
    • zapsolution
Re: Tutor_02 (C++ VS2022 GDImage64 tutorial)
« Reply #7 on: June 20, 2023, 07:21:50 pm »
Patrice
(Always working with the latest Windows version available...)