Author Topic: Tutor_19 "Circular text" (C++ VS2022 GDImage64 tutorial)  (Read 6951 times)

Patrice Terrier

  • Administrator
  • *****
  • Posts: 1987
    • zapsolution
Tutor_19 "Circular text" (C++ VS2022 GDImage64 tutorial)
« on: April 13, 2024, 07:32:58 pm »
About Tutor_19
Its purpose is to create one single or multiple circular text objects to use for logo or animaion.

It uses a new API named ZD_CreateCircularTextBitmap
using 7 parameters:
WCHAR* zText, the unicode string to create the circular text from.
WCHAR* zFont, the unicode string for the font to use (could be a .ttf private font).
long fontSize, the font size height.
long fontSyle, for example FontStyleBold.
long radius, the inner radius of the circular text.
DWORD ColrARGB, the ARGB color to use.
float step, the distance between each character.

The function returns a BITMAP handle that can be used by the ZD_DrawBitmapToCtrl API.

The AngleRotation procedure is using direct call to the GDIPLUS FLAT API to compute the correct rotation angle.

The window itself works in DWM transparent composited mode using the GDImage ZI_DwmEnable API.




« Last Edit: April 13, 2024, 08:42:04 pm by Patrice Terrier »
Patrice
(Always working with the latest Windows version available...)

Patrice Terrier

  • Administrator
  • *****
  • Posts: 1987
    • zapsolution
D2D "Circular text"
« Reply #1 on: April 14, 2024, 06:38:01 pm »
As a matter of comparison here is the code to draw a circular text using the D2D API

Code: [Select]
#include <windows.h>
#include <d2d1.h>
#include <dwrite.h>
#include <cmath>

#pragma comment(lib, "d2d1.lib")
#pragma comment(lib, "dwrite.lib")

constexpr auto IDI_ICON1     = 101;

// Global pointers for Direct2D and DirectWrite interfaces
ID2D1Factory* pD2DFactory = nullptr;
ID2D1HwndRenderTarget* pRenderTarget = nullptr;
ID2D1SolidColorBrush* pBrush = nullptr;
IDWriteFactory* pDWriteFactory = nullptr;
IDWriteTextFormat* pTextFormat = nullptr;

static void Initialize() {
    D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory);
    DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&pDWriteFactory));
}

static void CreateGraphicsResources(HWND hwnd) {
    if (!pRenderTarget) {
        RECT rc;
        GetClientRect(hwnd, &rc);
        D2D1_SIZE_U size = D2D1::SizeU(rc.right, rc.bottom);
        pD2DFactory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(hwnd, size), &pRenderTarget);

        pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(0xFF037BFA), &pBrush);

        pDWriteFactory->CreateTextFormat(
            L"Segoe GUI emoji",
            nullptr,
            DWRITE_FONT_WEIGHT_BOLD,
            DWRITE_FONT_STYLE_NORMAL,
            DWRITE_FONT_STRETCH_NORMAL,
            50.0f,
            L"", //locale
            &pTextFormat
        );

        pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
        pTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
    }
}

static void DiscardGraphicsResources() {
    if (pBrush) pBrush->Release();
    if (pRenderTarget) pRenderTarget->Release();
    if (pTextFormat) pTextFormat->Release();
    pBrush = nullptr;
    pRenderTarget = nullptr;
    pTextFormat = nullptr;
}

static void OnPaint(HWND hwnd) {
    PAINTSTRUCT ps;
    BeginPaint(hwnd, &ps);
    CreateGraphicsResources(hwnd);
    pRenderTarget->BeginDraw();
    pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
    D2D1_SIZE_F size = pRenderTarget->GetSize();

    const WCHAR* text = L"This is a Circular text using the D2D API.";
    const float radius = 200.0f;
    const D2D1_POINT_2F center = D2D1::Point2F(size.width / 2, size.height / 2);
    const size_t length = wcslen(text);
    const float angleStep = 360.0f / static_cast<float>(length);

    for (size_t i = 0; i < length; ++i) {
        WCHAR letter[2] = { text[i], 0 };

        float angle = DegreesToRadians(angleStep * i);
        D2D1_POINT_2F position = {
            center.x + cos(angle) * radius,
            center.y + sin(angle) * radius
        };

        D2D1_MATRIX_3X2_F rotation = D2D1::Matrix3x2F::Rotation(angleStep * i + 90, position);
        pRenderTarget->SetTransform(rotation);

        pRenderTarget->DrawText(
            letter,
            ARRAYSIZE(letter),
            pTextFormat,
            D2D1::RectF(position.x - 20, position.y - 20, position.x + 20, position.y + 20),
            pBrush
        );

        pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
    }

    HRESULT hr = pRenderTarget->EndDraw();
    if (FAILED(hr) || hr == D2DERR_RECREATE_TARGET) {
        DiscardGraphicsResources();
    }
    EndPaint(hwnd, &ps);
}

static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

        case WM_PAINT:
            OnPaint(hwnd);
            return 0;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) {
    Initialize();

    // Register the window class
    const wchar_t CLASS_NAME[] = L"Sample Window Class";
    WNDCLASS wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
    wc.lpszClassName = CLASS_NAME;

    RegisterClass(&wc);

    // Create the window
    HWND hwnd = CreateWindowEx(
        0,
        CLASS_NAME,
        L"Circular Text with Direct2D",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 600, 600,
        nullptr,
        nullptr,
        hInstance,
        nullptr
    );

    if (hwnd == nullptr) {
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    // Run the message loop
    MSG msg = {};
    while (GetMessage(&msg, nullptr, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    DiscardGraphicsResources();
    if (pD2DFactory) pD2DFactory->Release();
    if (pDWriteFactory) pDWriteFactory->Release();

    return 0;
}
Patrice
(Always working with the latest Windows version available...)

Patrice Terrier

  • Administrator
  • *****
  • Posts: 1987
    • zapsolution
Re: Tutor_19 "Circular text" (C++ VS2022 GDImage64 tutorial)
« Reply #2 on: April 20, 2024, 10:24:39 am »
And here is the PowerBASIC translation using the new GDImage.dll 32-bit version 7.01
Patrice
(Always working with the latest Windows version available...)