Sandboxie/Sandboxie/apps/control/AnimatedBitmap.cpp

355 lines
8.3 KiB
C++

/*
* Copyright 2004-2020 Sandboxie Holdings, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
// Animated Bitmap
//---------------------------------------------------------------------------
#include "stdafx.h"
#include "AnimatedBitmap.h"
#include "apps/common/MyGdi.h"
#include <objbase.h>
#include <gdiplus.h>
//---------------------------------------------------------------------------
// MyImage class
//---------------------------------------------------------------------------
class CMyImage : public Gdiplus::Image
{
IStream *m_pStream;
public:
CMyImage()
: Image() { nativeImage = NULL; m_pStream = NULL; }
~CMyImage()
{
if (m_pStream)
m_pStream->Release();
}
bool SetImage(const WCHAR *rsrcName)
{
m_pStream = (IStream *)MyGdi_CreateResourceStream(rsrcName);
if (! m_pStream)
return false;
lastResult = Gdiplus::DllExports::GdipLoadImageFromStream(
m_pStream, &nativeImage);
return (lastResult == 0 ? true : false);
}
};
//---------------------------------------------------------------------------
// Message Map
//---------------------------------------------------------------------------
BEGIN_MESSAGE_MAP(CAnimatedBitmap, CStatic)
ON_WM_PAINT()
ON_WM_TIMER()
END_MESSAGE_MAP()
//---------------------------------------------------------------------------
// Constructor
//---------------------------------------------------------------------------
CAnimatedBitmap::CAnimatedBitmap()
: CStatic()
{
m_image = NULL;
m_property = NULL;
m_frame_count = 0;
m_frame_index = -1;
}
//---------------------------------------------------------------------------
// Destructor
//---------------------------------------------------------------------------
CAnimatedBitmap::~CAnimatedBitmap()
{
SetImage(NULL);
}
//---------------------------------------------------------------------------
// Init
//---------------------------------------------------------------------------
bool CAnimatedBitmap::Init(HWND hwndEdit)
{
if (hwndEdit && SubclassWindow(hwndEdit)) {
ModifyStyleEx(WS_EX_LAYOUTRTL, 0);
return true;
} else
return false;
}
//---------------------------------------------------------------------------
// Init
//---------------------------------------------------------------------------
bool CAnimatedBitmap::Init(CDialog *dlg, UINT id)
{
HWND hwndEdit;
dlg->GetDlgItem(id, &hwndEdit);
return Init(hwndEdit);
}
//---------------------------------------------------------------------------
// SetImage
//---------------------------------------------------------------------------
bool CAnimatedBitmap::SetImage(const WCHAR *name)
{
//
// delete resources for old image
//
CMyImage *image = (CMyImage *)m_image;
if (image) {
delete image;
m_image = NULL;
}
if (m_property) {
free(m_property);
m_property = NULL;
}
m_frame_count = 0;
m_frame_index = -1;
//
// load image file for new image
//
if (! name)
return false;
image = new CMyImage();
if (! image)
return false;
if (! image->SetImage(name)) {
delete image;
return false;
}
//
// get frame data from the image
//
UINT dim_count = image->GetFrameDimensionsCount();
GUID *dim_ids = (GUID *)malloc(sizeof(GUID) * dim_count);
if (! dim_ids) {
delete image;
return false;
}
if (image->GetFrameDimensionsList(dim_ids, dim_count) == 0) {
m_guid = dim_ids[0];
int frame_count = image->GetFrameCount(&m_guid);
UINT item_size = 0;
Gdiplus::PropertyItem *property = NULL;
if (frame_count > 1)
item_size = image->GetPropertyItemSize(PropertyTagFrameDelay);
if (item_size)
property = (Gdiplus::PropertyItem *)malloc(item_size);
if (property) {
if (image->GetPropertyItem(
PropertyTagFrameDelay, item_size, property) == 0) {
free(dim_ids);
m_image = image;
m_property = property;
m_frame_count = frame_count;
return true;
}
}
}
free(dim_ids);
delete image;
return false;
}
//---------------------------------------------------------------------------
// Animate
//---------------------------------------------------------------------------
void CAnimatedBitmap::Animate(bool enable)
{
if (! m_image)
enable = false;
if (enable && m_frame_index == -1) {
SetTimer(26880, 10, NULL);
m_frame_index = 0;
m_frame_ticks = 0;
} else if ((! enable) && m_frame_index != -1) {
KillTimer(26880);
m_frame_index = -1;
}
}
//---------------------------------------------------------------------------
// DrawFrame
//---------------------------------------------------------------------------
void CAnimatedBitmap::DrawFrame(CDC *dc)
{
//
// create an off-screen buffer to paint into
//
CRect rc;
GetClientRect(&rc);
CBitmap mem_bmp;
mem_bmp.CreateCompatibleBitmap(dc, rc.Width(), rc.Height());
CDC mem_dc;
mem_dc.CreateCompatibleDC(dc);
CBitmap *old_bmp = mem_dc.SelectObject(&mem_bmp);
//
// paint the image into the off-screen buffer
//
CMyImage *image = (CMyImage *)m_image;
const ULONG width = image->GetWidth();
const ULONG height = image->GetHeight();
Gdiplus::Graphics graphics(mem_dc.m_hDC);
graphics.DrawImage(image, 0, 0, width, height);
//
// parent window can paint into off-screen buffer
//
CAnimatedBitmap_NM nm;
nm.hwndFrom = m_hWnd;
nm.idFrom = GetDlgCtrlID();
nm.code = NM_CUSTOMDRAW;
nm.dc = &mem_dc;
GetParent()->SendMessage(WM_NOTIFY, nm.idFrom, (LPARAM)&nm);
//
// copy off-screen buffer into our window and release it
//
dc->BitBlt(0, 0, width, height, &mem_dc, 0, 0, SRCCOPY);
mem_dc.SelectObject(old_bmp);
}
//---------------------------------------------------------------------------
// OnPaint
//---------------------------------------------------------------------------
void CAnimatedBitmap::OnPaint()
{
if (IsWindowVisible()) {
PAINTSTRUCT ps;
CDC *dc = BeginPaint(&ps);
DrawFrame(dc);
EndPaint(&ps);
}
}
//---------------------------------------------------------------------------
// OnTimer
//---------------------------------------------------------------------------
void CAnimatedBitmap::OnTimer(UINT_PTR nIDEvent)
{
if (nIDEvent == 26880 && m_image && m_frame_index != -1) {
CMyImage *image = (CMyImage *)m_image;
GUID page_guid = Gdiplus::FrameDimensionTime;
image->SelectActiveFrame(&m_guid, m_frame_index);
ULONG ticks_now = GetTickCount();
if (m_frame_ticks == 0) {
Gdiplus::PropertyItem *property =
(Gdiplus::PropertyItem *)m_property;
int frame_time = ((long *)property->value)[m_frame_index];
m_frame_ticks = ticks_now + frame_time * 10;
if (IsWindowVisible()) {
CDC *dc = GetDC();
if (dc) {
DrawFrame(dc);
ReleaseDC(dc);
}
}
} else {
if (m_frame_ticks <= ticks_now) {
m_frame_ticks = 0;
++m_frame_index;
if (m_frame_index == m_frame_count)
m_frame_index = 0;
}
}
}
CStatic::OnTimer(nIDEvent);
}