0

Having a window with gif and some elements c++ winapi(Non-MFC) using gdi+. SO I want to make transparent elements(Edit,Static,Button). I want to make transparent gray background of elements.

I have tried to handle WM_CTLCOLORSTATIC and adding style WS_EX_TRANSPARENT and m. but it didn't give correct result. But when I handle WM_CTLCOLORSTATIC with code:



hdcStatic = (HDC) wParam; 
    SetTextColor(hdcStatic, RGB(0,0,0));    
    SetBkMode (hdcStatic, TRANSPARENT);

    return (LRESULT)GetStockObject(NULL_BRUSH);

And it showed STATIC styles transparent, but I could see background of Windows 10.

Minimum code is:


#include <memory>
#include "Resource.h"
#include <vector>
#include "TESTING.h"
#include "framework.h"
#include <algorithm>
#include <windows.h>
#include <objidl.h>
#include <GdiPlus.h>
#include <gdiplusimaging.h>
#include <shlwapi.h>
#include<CommCtrl.h>

using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
#pragma comment (lib,"shlwapi.lib")
#pragma comment (lib,"Comctl32.lib")
#define TIMER_ID 101
static HFONT s_hFont = NULL;
static HWND hWnd;

static HWND hwndText;
static HWND hwndButton;
static HWND hwndLabel;
static HWND hwndCode;
static HWND hwndCode2;
static HWND hwndTime;



LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK SubWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR);

int WINAPI _WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    ULONG_PTR m_gdiplusToken;
    GdiplusStartupInput gdiplusStartupInput;
    GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);

    HMODULE hMod = GetModuleHandle(NULL);
    HRSRC hRes = FindResource(hMod, MAKEINTRESOURCEW(MY_GIF_ID), RT_RCDATA);
    if (!hRes) MessageBox(NULL, L"hRes!!", L"ERROR", 0);
    HGLOBAL hGlobal = LoadResource(hMod, hRes);
    if (!hGlobal)MessageBox(NULL, L"hGlobal!!", L"ERROR", 0);
    void* pResData = LockResource(hGlobal);
    if (!pResData) MessageBox(NULL, L"pResData!!", L"ERROR", 0);

    DWORD dwResData = SizeofResource(hMod, hRes);

    IStream* pStream = SHCreateMemStream((BYTE*)pResData, dwResData);
    if (!pStream) MessageBox(NULL, L"pStream!!", L"ERROR", 0);

    Image gif(pStream);
    pStream->Release();

    MSG msg = { 0 };
    WNDCLASS wc = { 0 };
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.hbrBackground = NULL; // <= Do not provide a background brush.
    wc.lpszClassName = L"anim_gif_player";
    if (!RegisterClass(&wc))
        return -1;
    hWnd = CreateWindow(wc.lpszClassName,
        L"",
        WS_EX_TOPMOST | WS_CLIPCHILDREN & ~WS_CAPTION & ~WS_SYSMENU,
        0, 0, 640, 480, 0, 0, hInstance, &gif);
        if (!hWnd) {
            MessageBox(0, L"SSSSSSSSSSSSSSSSSSS", L"kkkkkkkkkkkkkkkkkkkkkk", 0);
            return -2;

        }



        hwndLabel = CreateWindowEx(WS_EX_TRANSPARENT,L"STATIC",
            NULL,
            WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_CLIPSIBLINGS,
            325, 90, 440, 45,
            hWnd,
            NULL,
            NULL,
            NULL);
        SetWindowSubclass(hwndLabel, SubWndProc, 0, 0);




        const TCHAR* fontName = TEXT("Croobie");
        const long nFontSize = 24;

        HDC hdc = GetDC(hwndLabel);

        LOGFONT logFont = { 0 };
        logFont.lfHeight = -MulDiv(nFontSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
        logFont.lfWeight = FW_BOLD;
        wcscpy_s(logFont.lfFaceName, fontName);

        s_hFont = CreateFontIndirect(&logFont);








        SendMessage(hwndLabel, WM_SETFONT, (WPARAM)s_hFont, (LPARAM)MAKELONG(TRUE, 0));














    ShowWindow(hWnd, SW_SHOWMAXIMIZED);
    UpdateWindow(hWnd);



    while (GetMessage(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    //DeleteObject(wc.hbrBackground);

    return msg.wParam;
}

std::vector<unsigned int> LoadGifFrameInfo(Image* image)
{
    // I think animated gifs will always only have 1 frame dimension...
    // the "dimension" being the frame count, but I could be wrong about this
    int count = image->GetFrameDimensionsCount();
    if (count != 1)
        return std::vector<unsigned int>();

    GUID guid;
    if (image->GetFrameDimensionsList(&guid, 1) != 0)
        return std::vector<unsigned int>();
    int frame_count = image->GetFrameCount(&guid);

    auto sz = image->GetPropertyItemSize(PropertyTagFrameDelay);
    if (sz == 0)
        return std::vector<unsigned int>();

    // copy the frame delay property into the buffer backing an std::vector
    // of bytes and then get a pointer to its value, which will be an array of 
    // unsigned ints
    std::vector<unsigned char> buffer(sz);
    PropertyItem* property_item = reinterpret_cast<PropertyItem*>(&buffer[0]);
    image->GetPropertyItem(PropertyTagFrameDelay, sz, property_item);
    unsigned int* frame_delay_array = (unsigned int*)property_item[0].value;

    // copy the delay values into an std::vector while converting to milliseconds.
    std::vector<unsigned int> frame_delays(frame_count);
    std::transform(frame_delay_array, frame_delay_array + frame_count, frame_delays.begin(),
        [](unsigned int n) {return n * 10; }
    );

    return frame_delays;
}

void GenerateFrame(Bitmap* bmp, Image* gif)
{
    Graphics dest(bmp);

    SolidBrush white(Color::White);
    dest.FillRectangle(&white, 0, 0, bmp->GetWidth(), bmp->GetHeight());

    if (gif)
        dest.DrawImage(gif, 0, 0);
}

std::unique_ptr<Bitmap> CreateBackBuffer(HWND hWnd)
{
    RECT r;
    GetClientRect(hWnd, &r);
    return std::make_unique<Bitmap>(r.right - r.left, r.bottom - r.top);
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static Image* animated_gif;
    static std::unique_ptr<Bitmap> back_buffer;
    static std::vector<unsigned int> frame_delays;
    static int current_frame;

    switch (message) {
    case WM_CREATE: {
        animated_gif = reinterpret_cast<Image*>(
            reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams
            );

        if (!animated_gif || animated_gif->GetLastStatus() != 0) {
            MessageBox(hWnd, L"Unable to load animated gif", L"error", MB_ICONERROR);
            return 0;
        }

        // Create a bitmap the size of the window's clent area
        back_buffer = CreateBackBuffer(hWnd);

        // get the frame delays and thereby test that this is really an animated gif
        frame_delays = LoadGifFrameInfo(animated_gif);
        if (frame_delays.empty()) {
            MessageBox(hWnd, L"Invalid gif or not an animated gif", L"error", MB_ICONERROR);
            return 0;
        }

        current_frame = 0;
        animated_gif->SelectActiveFrame(&FrameDimensionTime, current_frame);

        GenerateFrame(back_buffer.get(), animated_gif);

        SetTimer(hWnd, TIMER_ID, frame_delays[0], nullptr);
        InvalidateRect(hWnd, nullptr, FALSE);
    }
                    break;


    case WM_TIMER: {
        KillTimer(hWnd, TIMER_ID);
        current_frame = (current_frame + 1) % frame_delays.size();
        animated_gif->SelectActiveFrame(&FrameDimensionTime, current_frame);
        GenerateFrame(back_buffer.get(), animated_gif);
        SetTimer(hWnd, TIMER_ID, frame_delays[current_frame], nullptr);
        InvalidateRect(hWnd, nullptr, FALSE);
    } break;

    case WM_PAINT: {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);

        Graphics g(hdc);
        g.DrawImage(back_buffer.get(), 0, 0);

        EndPaint(hWnd, &ps);
    } break;



    case WM_SIZE: {
        back_buffer = CreateBackBuffer(hWnd);
        GenerateFrame(back_buffer.get(), animated_gif);
    } break;

    case WM_CLOSE:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

LRESULT CALLBACK SubWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR) {
    switch (msg) {
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        RECT rc; GetClientRect(hwnd, &rc);
        SetTextColor(hdc, Color::Yellow);
        SetBkMode(hdc, TRANSPARENT);
        wchar_t buf[256] = { 0 };
        GetWindowTextW(hwnd, buf, sizeof(buf) / sizeof(*buf));
        DrawTextW(hdc, buf, wcslen(buf), &rc, DT_LEFT | DT_TOP);
        EndPaint(hwnd, &ps);
        return 0;
    }
    case WM_NCDESTROY://safely remove subclass
        RemoveWindowSubclass(hwnd, SubWndProc, 0);
        return DefSubclassProc(hwnd, msg, wParam, lParam);
    }
    return DefSubclassProc(hwnd, msg, wParam, lParam);

    }



TheGamerCoder
  • 91
  • 1
  • 11

1 Answers1

2

The WS_EX_TRANSPARENT style doesn’t mean “transparent”; it means “paint over siblings.”

The style is called “transparent” not because it makes the window transparent but because it makes transparency possible.

Please refer: Why isn't my transparent static control transparent?

The only way I've found to do this reliably is to sub-class the static control and paint the background manually.

You can catch the WM_ERASEBKGND message and paint the appropriate portion of the underlying bitmap.

Please refer:

Is it possible to make a Static control transparent?

C++ Win32 Static Control Transparent Background

Also, if you want remove grey background, you can change static control background and make it transparent.

Demon of custom static control:

#include "stdafx.h"
#include "Test_WM_CTLCOLORSTATIC.h"

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;                                // current instance
WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name
HWND hWndStatic;
// Forward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK    WndProcPanel(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.

    // Initialize global strings
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_TESTWMCTLCOLORSTATIC, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);
    // Perform application initialization:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTWMCTLCOLORSTATIC));

    MSG msg;

    // Main message loop:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TESTWMCTLCOLORSTATIC));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_TESTWMCTLCOLORSTATIC);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}


BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // Store instance handle in our global variable

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Processes messages for the main window.
//
//  WM_COMMAND  - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//
WNDPROC StaticWndProc = NULL;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_CREATE:
    {   LRESULT lRes = DefWindowProc(hWnd, message, wParam, lParam);
        hWndStatic = CreateWindowEx(0, L"Static", NULL, WS_CHILD | WS_VISIBLE | SS_LEFT, 10, 130, 200, 40, hWnd, NULL, hInst, NULL); //v2 deleted HWND
        StaticWndProc = (WNDPROC)SetWindowLong(hWndStatic, GWL_WNDPROC, (LPARAM)WndProcPanel);
        return lRes;
    }
    break;
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // Parse the menu selections:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_ERASEBKGND: 
    {    
         HBRUSH brush;   
         RECT rect;    
         brush = CreateSolidBrush(RGB(0, 255, 0));    
         SelectObject((HDC)wParam, brush);    
         GetClientRect(hWnd, &rect);   
         Rectangle((HDC)wParam, rect.left, rect.top, rect.right, rect.bottom); 
    }
    break;
    case WM_DESTROY:
        SetWindowLong(hWndStatic, GWL_WNDPROC, (LPARAM)StaticWndProc);
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}


TCHAR szText[] = _T("TestString");;
LRESULT CALLBACK WndProcPanel(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{   
        if (message == WM_PAINT)
        {
            RECT rc;
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            GetClientRect(hWnd, &rc);
            SetBkMode(hdc, TRANSPARENT);
            DrawText(hdc, szText, _tcslen(szText), &rc, DT_CENTER | DT_VCENTER);
            EndPaint(hWnd, &ps);
            return 0;
        }
        return CallWindowProc(StaticWndProc, hWnd, message, wParam, lParam); 
}

thx @IInspectable's remind, the best way to subclass controls is to use SetWindowsSubclass, see Subclassing Controls

I'll update the new code later, and I'm sorry for my shallow knowledge.

Updated:

case WM_TIMER: {
        KillTimer(hWnd, TIMER_ID);
        current_frame = (current_frame + 1) % frame_delays.size();
        animated_gif->SelectActiveFrame(&FrameDimensionTime, current_frame);
        GenerateFrame(back_buffer.get(), animated_gif);
        SetTimer(hWnd, TIMER_ID, frame_delays[current_frame], nullptr);     
        InvalidateRect(hWnd, nullptr, FALSE);
        InvalidateRect(hwndLabel, nullptr, FALSE); //Here add new code

    }

Debug Result:

3

Strive Sun
  • 5,988
  • 1
  • 9
  • 26
  • No no. I don't need in solid background. I want to delete the gray background of this STATIC. The text is written in gray bakgroung and I want to remove it – TheGamerCoder Jul 24 '19 at 09:42
  • @TheGamerCoder OK, modify the background color of the STATIC. – Strive Sun Jul 24 '19 at 09:56
  • @TheGamerCoder Or customize static controls to make the background transparent, refer to my link above – Strive Sun Jul 24 '19 at 10:05
  • @TheGamerCoder Call the `WM_PAINT` event to draw all the effects you need. – Strive Sun Jul 25 '19 at 08:51
  • That's not how you [subclass controls](https://learn.microsoft.com/en-us/windows/win32/controls/subclassing-overview#subclassing-controls-using-comctl32dll-version-6), at least that's not how we have been subclassing controls for more than a decade. Visitors will assume credibility when they see a screen name with the *MSFT* abbreviation. Don't just publish incredibly outdated code under that token of credibility. – IInspectable Jul 25 '19 at 16:14
  • @TheGamerCoder No,It actually works. Why not display text? It's because the main window is always refreshed, while the static control window is refreshed only once.Please see my updated. – Strive Sun Aug 01 '19 at 10:14
  • @TheGamerCoder You can ask a new question about how to display the text of the control on the constantly redrawing main window. – Strive Sun Aug 01 '19 at 10:20
  • Thanks. But I can do it wit writing text on frames of gif. Whatever, thanks – TheGamerCoder Aug 01 '19 at 10:54
  • @TheGamerCoder This is what I should do. If possible, you can improve your code, such as displaying transparent text on the GIF background, which is good for other members of this case to read:) – Strive Sun Aug 02 '19 at 03:01