Animated Image in Win32 - c++

How can i put an animated GIF to my dialog in my native Win32 application?
I have a loading indicator, and a loading process.
Thanks :-)

Not sure if GDI+ could be considered as native win32. In case you can use it check the following example: CodeProject

You could use the Animation Control. You would have to convert your .gif to an .avi though.

Its very easy to use GdiPlus to load a variety of image formats including jpeg, gif (animated), png and so on.
This code demonstrates how to quickly load a single frame of an image into an HBITMAP :-
#include <gdiplus.h>
#pragma comment(lib,"gdiplus.lib")
using namespace Gdiplus;
HBITMAP LoadImageWithGdiPlus(LPCTSTR pszPngPath)
{
Image image(pszPngPath);
int width = image.GetWidth();
int height = image.GetHeight();
BITMAPINFO bmi;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biClrImportant = 0;
bmi.bmiHeader.biClrUsed = 0;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biHeight = height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biSize = sizeof (bmi.bmiHeader);
bmi.bmiHeader.biSizeImage = 0; //calc later
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biXPelsPerMeter = 0;
bmi.bmiHeader.biYPelsPerMeter = 0;
BYTE* pBmp = NULL;
HBITMAP hbm = CreateDIBSection(NULL,&bmi,DIB_RGB_COLORS,(void**)&pBmp,NULL,0);
HDC hdc = CreateCompatibleDC(NULL);
HGDIOBJ hobj = SelectObject(hdc,hbm);
Graphics graphics(hdc);
graphics.DrawImage(&image,0,0);
SelectObject(hdc,hobj);
DeleteDC(hdc);
return hbm;
}

Since you have a tight timeframe on this one I searched for a working example to animate gifs on win32 and I found a nice implementation on cplusplus.com.
It's called GIF View [direct link] by Juan Soulie.

It's fairly simple to implement a timer to change what's displayed. You might set up a text block, with no text in it, with a background color and just change the size. It will look like an expanding colored bar with very little overhead.

Related

glfwSetWindowIcon from an embedded resource with transparency

I'm trying to achieve what almost every application does, and that's to have an embedded icon which I can show in the title bar and taskbar.
The requirements are:
The icon must support transparency
The icon must be embedded, not a separate file on the disk
The icon file format doesn't matter; can be ico, bmp or anything else that works.
I tried to create a bmp in paint.net, but LoadImage kept returning NULL. I recreated it as a 32-bit png, then used
this tool to convert it to a 32-bit bmp, my reasoning for which being to get LoadImage working again, and this time with transparency. I'm using LoadImage because apparently LoadBitmap isn't so well-supported.
resources.rc:
100 BITMAP "icons\\icon.bmp"
code:
HBITMAP h_bmp = (HBITMAP)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(100), IMAGE_BITMAP, 0, 0, 0);
HDC dc = CreateCompatibleDC(NULL);
HBITMAP bmp_old = (HBITMAP)SelectObject(dc, h_bmp);
BITMAP bmp = {};
GetObject(h_bmp, sizeof(bmp), &bmp);
BITMAPINFO info;
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biWidth = bmp.bmWidth;
info.bmiHeader.biHeight = -bmp.bmHeight;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = bmp.bmBitsPixel;
info.bmiHeader.biCompression = BI_RGB;
info.bmiHeader.biSizeImage = ((bmp.bmWidth * bmp.bmBitsPixel + 31) / 32) * 4 * bmp.bmHeight;
std::vector<unsigned char> pixels;
pixels.resize(info.bmiHeader.biSizeImage);
GetDIBits(dc, h_bmp, 0, bmp.bmHeight, &pixels[0], &info, DIB_RGB_COLORS);
SelectObject(dc, bmp_old);
DeleteDC(dc);
GLFWimage image = { bmp.bmWidth, bmp.bmHeight, &pixels[0] };
glfwSetWindowIcon(window, 1, &image);
There were two issues with the image; firstly it was displaying upside-down, hence the negation of the height above. Secondly, the RGB channels are flipped and I have no idea how to fix it.
So, is there a simpler way to do this, perhaps without having to suffer bmp formats and needing 3rd-party tools, and if not, how can I get the colours displaying properly?
Windows (HWNDs) uses HICONs. A HICON is a handle to an icon loaded from a .ico file or an icon resource. A .ico file consists of one or more bmp and/or png files in various sizes. You should use a real icon editor to create your icon. You can find several free editors if you look around.
Once you have the ico file in your resources, load it with LoadImage(hInst, ..., IMAGE_ICON, ..., LR_SHARED) and apply it with SetClassLongPtr or WM_SETICON.
Solved as per #user253751's comment;
SetClassLongPtr(glfwGetWin32Window(window), GCLP_HICON, (LONG_PTR)LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(100)));
Much simpler than I expected!

Using StretchDIBits to copy PNG (with alpha)

Could someone help me?
How I can copy png to virtual HDC.
Transparent pixels are always black.
I tried but alpha is always black.
Seems like BITMAPINFOHEADER does not support alpha.
Thank you very much.
and sorry for my English. Peace.
(Note I need to use GDI only)
// fill background
HBRUSH hbrush = CreateSolidBrush(RGB(color.red(), color.green(), color.blue()));
::FillRect(m_virtualHDC, &wRect, hbrush);
DeleteObject(hbrush);
// load image
QImage pix;
pix.load("D:\\1.png");
// draw image with alpha
BITMAPINFO bmInfo;
ZeroMemory(&bmInfo, sizeof(BITMAPINFO));
BITMAPINFOHEADER& BMPInfoHeader = bmInfo.bmiHeader;
BMPInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
BMPInfoHeader.biWidth = pix.width();
BMPInfoHeader.biHeight = pix.height();
BMPInfoHeader.biPlanes = 1;
BMPInfoHeader.biBitCount = 32;
BMPInfoHeader.biCompression = BI_RGB;
BMPInfoHeader.biSizeImage = pix.byteCount();
BMPInfoHeader.biXPelsPerMeter = 0;
BMPInfoHeader.biYPelsPerMeter = 0;
BMPInfoHeader.biClrUsed = 0;
SetStretchBltMode(m_virtualHDC, HALFTONE);
StretchDIBits(m_virtualHDC,
targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(),
sourceRect.x(), sourceRect.y(), sourceRect.width(), sourceRect.height(),
pix.bits(), &bmInfo, DIB_RGB_COLORS, SRCCOPY);
GDI in general does not support an alpha channel.
You need to use an API that has dedicated alpha support, e. g. AlphaBlend() or GDI+.
Most of these APIs have very poor resampling quality. I suggest to use a third-party library like stb_image_resize to resample in memory and use APIs like AlphaBlend() only to display the final result to the screen, without doing further scaling.

StretchBlt only works when nHeightDest is negative

I'm trying to use StretchBlt in order to copy pixels from a memory hdc to the window hdc.
The memory hdc gets the image from an invisible window which renders a stream using openGL.
Here's my code:
BITMAPINFOHEADER createBitmapHeader(int width, int height) {
BITMAPINFOHEADER header;
header.biSize = sizeof(BITMAPINFOHEADER);
header.biWidth = width;
header.biHeight = height;
header.biPlanes = 1;
header.biBitCount = 32;
header.biCompression = BI_RGB;
header.biSizeImage = 0;
header.biXPelsPerMeter = 0;
header.biYPelsPerMeter = 0;
header.biClrUsed = 0;
header.biClrImportant = 0;
return header;
}
...
HDC memoryHdc = CreateCompatibleDC(windowHdc);
BITMAPINFO bitmapInfo;
bitmapInfo.bmiHeader = createBitmapHeader(targetDimensions.width, targetDimensions.height);
HBITMAP bitmap = CreateDIBitmap(windowHdc, &bitmapInfo.bmiHeader, CBM_INIT, offscreenBuffer, &bitmapInfo, DIB_RGB_COLORS);
SelectObject(memoryHdc, bitmap);
DeleteObject(bitmap);
SetStretchBltMode(windowHdc, COLORONCOLOR);
StretchBlt(windowHdc,
targetDimensions.x, targetDimensions.y,
targetDimensions.width, -targetDimensions.height,
memoryHdc,
sourceDimensions.x, sourceDimensions.y,
sourceDimensions.width, sourceDimensions.height,
SRCCOPY);
DeleteDC(memoryHdc);
Where windowHdc is the hdc of the window to which I want the StretchBlt to copy the pixels to, and offscreenBuffer is a void* to the pixels copied from the offscreen window in which the openGL is rendering.
This code works great, except that the image is upside down and I want it vertically flipped.
I know that this happens because:
If nHeightSrc and nHeightDest have different signs, the function
creates a mirror image of the bitmap along the y-axis
But when I remove the minus sign and both target and source heights are the same then I see no image in the window.
Just to check, I tried to put the minus on the sourceDimensions.height but that also results in no image, and the same if I try to negate the widths (both target and source).
Any idea why?
Thanks.

Editing bitmap in memory using C++

I have a two dimensional array of data that I want to display as an image.
The plan goes something like this -
Create a bitmap using CreateCompatibleBitmap (this results in a solid black bitmap and I can display this with no problems)
Edit the pixels of this bitmap to match my data
BitBlt the bitmap to the window
I think that I need a pointer to the place in memory where the pixel data begins. I've tried many different methods of doing this and googled it for 3 days and still haven't been able to even edit a single pixel.
Using a loop of SetPixel(HDC, x, y, Color) to set each pixel works but VERY slowly.
I have accomplished this in C# by locking the bitmap and editing the bits, but I am new to C++ and can't seem to figure out how to do something similar.
I have mostly been trying to use memset(p, value, length)
For "p" I have tried using the handle returned from CreateCompatibleBitmap, the DC for the bitmap, and the DC for the window. I have tried all sorts of values for the value and length.
I'm not sure if this is the right thing to use though.
I don't have to use a bitmap, that's just the only thing I know to do. Actually it would be awesome to find a way to directly change the main window's DC.
I do want to avoid libraries though. I am doing this purely for learning C++.
This took QUITE a bit of research so I'll post exactly how it is done for anyone else who may be looking.
This colors every pixel red.
hDC = BeginPaint(hWnd, &Ps);
const int
width = 400,
height = 400,
size = width * height * 3;
byte * data;
data = new byte[size];
for (int i = 0; i < size; i += 3)
{
data[i] = 0;
data[i + 1] = 0;
data[i + 2] = 255;
}
BITMAPINFOHEADER bmih;
bmih.biBitCount = 24;
bmih.biClrImportant = 0;
bmih.biClrUsed = 0;
bmih.biCompression = BI_RGB;
bmih.biWidth = width;
bmih.biHeight = height;
bmih.biPlanes = 1;
bmih.biSize = 40;
bmih.biSizeImage = size;
BITMAPINFO bmpi;
bmpi.bmiHeader = bmih;
SetDIBitsToDevice(hDC, 0, 0, width, height, 0, 0, 0, height, data, &bmpi, DIB_RGB_COLORS);
delete[] data;
memset can be used on the actually RGB information array (but you need to also know the format of the bitmap, if a pixel has 32 or 24 bits ).
From a bit of research on msdn, it seems that what you want to get is the BITMAP structure :
http://msdn.microsoft.com/en-us/library/k1sf4cx2.aspx
There you have the bmBits on which you can memset.
How to get there from your function ?
Well, CreateCompatibleBitmap returns a HBITMAP structure and it seems you can get BITMAP from HBITMAP with the following code :
BITMAP bmp;
GetObject(hBmp, sizeof(BITMAP), &bmp);
This however seems to get a you copy of the existing bitmap info, which only solves your memset problem (you can now set the bitmap information with memset, eventhou I don't see any other use for memeset besides making the bmp all white or black).
There should be a function that allows you to set the DC bites to a bitmap thou, so you should be able to use the new bitmap as a parameter.

CreateDIBSection in MFC and rendering using picture control

I'm creating a DIB Section in MFC using the call CreateDIBSection. I get a HBITMAP from the call which I pass onto another dialog in my MFC Project. In the other dialog I'm using CStatic::SetBitmap(HBITMAP) call to render the bitmap. But for some reason I'm not able to see anything. This works perfectly fine if this is done in same dialog, but I want to create bitmap in one dialog and display in another.
The code for creating the DIBSection is
//-----------------BEGINNING OF FIRST DIALOG--------------------------------------------------
LPVOID pViewBitmapBits = NULL;
BITMAPINFOHEADER BMHeaderInfo;
memset(&BMHeaderInfo, 0, sizeof(BITMAPINFOHEADER));
BMHeaderInfo.biSize = sizeof(BITMAPINFOHEADER);
BMHeaderInfo.biWidth = 800;
BMHeaderInfo.biHeight = 400;
BMHeaderInfo.biPlanes = 1;
BMHeaderInfo.biBitCount = 8;
BMHeaderInfo.biCompression = BI_RGB;
BMHeaderInfo.biSizeImage = 0;
BMHeaderInfo.biClrUsed = 0;
BMHeaderInfo.biClrImportant= 0;
BITMAPINFO BMInfo;
memset(&BMInfo, 0, sizeof(BMInfo));
BMInfo.bmiHeader = BMHeaderInfo;
BMInfo.bmiColors[0].rgbBlue=255;
HBITMAP hGlobalBitMap = CreateDIBSection(m_pParentSheet->test.m_hDC, &BMInfo, DIB_RGB_COLORS, &pViewBitmapBits, NULL, NULL);
SelectObject(m_pParentSheet->test.m_hDC, hGlobalBitMap);
//--------------------------END OF FIRST DIALOG----------------------------------
//-----------------------------BEGINNING OF SECOND DIALOG----------------------------------------
void CreateViewDlg::OnBnClickedButton2()
{
m_pic.SetBitmap(hGlobalBitMap );
}
//------------------------------------END OF SECOND DIALOG---------------------------------
Please help me with this. Is there any limitation to the usage of HBITMAP Handles?
HBITMAP hGlobalBitMap = ...
Looks like local variable hiding the global variable.