mirror of
https://github.com/reactos/reactos.git
synced 2024-08-30 07:08:19 +00:00
![Katayama Hirofumi MZ](/assets/img/avatar_default.png)
- TCHAR --> WCHAR - LPTSTR --> LPWSTR - LPCTSTR --> LPCWSTR - CString --> CStringW - TEXT("...") --> L"..." - _T("...") --> L"..." - ::SendMessage( --> ::SendMessageW( - ::GetWindowText( --> ::GetWindowTextW( - ::SetWindowText( --> ::SetWindowTextW( - Replace _tcscat with StringCchCatW. - Replace _tcslen with wcslen. etc. CORE-19094
347 lines
10 KiB
C++
347 lines
10 KiB
C++
/*
|
|
* PROJECT: PAINT for ReactOS
|
|
* LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
|
|
* PURPOSE: Loading/Saving an image file with getting/setting resolution
|
|
* COPYRIGHT: Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <atlimage.h>
|
|
|
|
namespace GPDE = Gdiplus::DllExports;
|
|
|
|
class CImageDx
|
|
{
|
|
protected:
|
|
HBITMAP m_hBitmap = NULL;
|
|
|
|
public:
|
|
CImageDx()
|
|
{
|
|
_shared()->AddRef();
|
|
}
|
|
|
|
~CImageDx()
|
|
{
|
|
if (m_hBitmap)
|
|
::DeleteObject(m_hBitmap);
|
|
|
|
_shared()->Release();
|
|
}
|
|
|
|
void Attach(HBITMAP hbm)
|
|
{
|
|
if (m_hBitmap)
|
|
::DeleteObject(m_hBitmap);
|
|
m_hBitmap = hbm;
|
|
}
|
|
|
|
HBITMAP Detach()
|
|
{
|
|
HBITMAP hbmOld = m_hBitmap;
|
|
m_hBitmap = NULL;
|
|
return hbmOld;
|
|
}
|
|
|
|
BOOL GetResolution(Gdiplus::GpImage *pImage, float *pxDpi, float *pyDpi)
|
|
{
|
|
if (!get_fn(_shared()->m_GetImageHorizontalResolution, "GdipGetImageHorizontalResolution") ||
|
|
!get_fn(_shared()->m_GetImageVerticalResolution, "GdipGetImageVerticalResolution"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (pxDpi)
|
|
_shared()->m_GetImageHorizontalResolution(pImage, pxDpi);
|
|
if (pyDpi)
|
|
_shared()->m_GetImageVerticalResolution(pImage, pyDpi);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SetResolution(Gdiplus::GpBitmap *pBitmap, float xDpi, float yDpi)
|
|
{
|
|
if (!get_fn(_shared()->m_BitmapSetResolution, "GdipBitmapSetResolution"))
|
|
return FALSE;
|
|
|
|
_shared()->m_BitmapSetResolution(pBitmap, xDpi, yDpi);
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT LoadDx(LPCWSTR pszFileName, float *pxDpi, float *pyDpi) throw()
|
|
{
|
|
using namespace Gdiplus;
|
|
|
|
_shared()->AddRef();
|
|
|
|
if (!get_fn(_shared()->m_CreateBitmapFromFile, "GdipCreateBitmapFromFile") ||
|
|
!get_fn(_shared()->m_CreateHBITMAPFromBitmap, "GdipCreateHBITMAPFromBitmap") ||
|
|
!get_fn(_shared()->m_DisposeImage, "GdipDisposeImage"))
|
|
{
|
|
_shared()->Release();
|
|
return E_FAIL;
|
|
}
|
|
|
|
// create a GpBitmap object from file
|
|
GpBitmap *pBitmap = NULL;
|
|
if (_shared()->m_CreateBitmapFromFile(pszFileName, &pBitmap) != Ok)
|
|
{
|
|
_shared()->Release();
|
|
return E_FAIL;
|
|
}
|
|
|
|
// get an HBITMAP
|
|
HBITMAP hbm = NULL;
|
|
Color color(0xFF, 0xFF, 0xFF);
|
|
Status status = _shared()->m_CreateHBITMAPFromBitmap(pBitmap, &hbm, color.GetValue());
|
|
|
|
// get the resolution
|
|
if (pxDpi || pyDpi)
|
|
GetResolution((GpImage*)pBitmap, pxDpi, pyDpi);
|
|
|
|
// delete GpBitmap
|
|
_shared()->m_DisposeImage(pBitmap);
|
|
|
|
// attach it
|
|
if (status == Ok)
|
|
Attach(hbm);
|
|
|
|
_shared()->Release();
|
|
return (status == Ok ? S_OK : E_FAIL);
|
|
}
|
|
|
|
HRESULT SaveDx(LPCWSTR pszFileName, REFGUID guidFileType = GUID_NULL,
|
|
float xDpi = 0, float yDpi = 0) throw()
|
|
{
|
|
using namespace Gdiplus;
|
|
|
|
_shared()->AddRef();
|
|
|
|
if (!get_fn(_shared()->m_CreateBitmapFromHBITMAP, "GdipCreateBitmapFromHBITMAP") ||
|
|
!get_fn(_shared()->m_SaveImageToFile, "GdipSaveImageToFile") ||
|
|
!get_fn(_shared()->m_DisposeImage, "GdipDisposeImage"))
|
|
{
|
|
_shared()->Release();
|
|
return E_FAIL;
|
|
}
|
|
|
|
// create a GpBitmap from HBITMAP
|
|
GpBitmap *pBitmap = NULL;
|
|
_shared()->m_CreateBitmapFromHBITMAP(m_hBitmap, NULL, &pBitmap);
|
|
|
|
// set the resolution
|
|
SetResolution(pBitmap, xDpi, yDpi);
|
|
|
|
// Get encoders
|
|
UINT cEncoders = 0;
|
|
ImageCodecInfo* pEncoders = GetAllEncoders(cEncoders);
|
|
|
|
// if the file type is null, get the file type from extension
|
|
CLSID clsid;
|
|
if (::IsEqualGUID(guidFileType, GUID_NULL))
|
|
{
|
|
CStringW strExt(PathFindExtensionW(pszFileName));
|
|
clsid = FindCodecForExtension(strExt, pEncoders, cEncoders);
|
|
}
|
|
else
|
|
{
|
|
clsid = FindCodecForFileType(guidFileType, pEncoders, cEncoders);
|
|
}
|
|
|
|
delete[] pEncoders;
|
|
|
|
// save to file
|
|
Status status = _shared()->m_SaveImageToFile(pBitmap, pszFileName, &clsid, NULL);
|
|
|
|
// destroy GpBitmap
|
|
_shared()->m_DisposeImage(pBitmap);
|
|
|
|
_shared()->Release();
|
|
|
|
return (status == Ok ? S_OK : E_FAIL);
|
|
}
|
|
|
|
static BOOL IsExtensionSupported(PWCHAR pchDotExt)
|
|
{
|
|
_shared()->AddRef();
|
|
|
|
UINT cEncoders;
|
|
Gdiplus::ImageCodecInfo* pEncoders = GetAllEncoders(cEncoders);
|
|
|
|
CLSID clsid = FindCodecForExtension(pchDotExt, pEncoders, cEncoders);
|
|
BOOL ret = !::IsEqualGUID(clsid, CLSID_NULL);
|
|
delete[] pEncoders;
|
|
|
|
_shared()->Release();
|
|
return ret;
|
|
}
|
|
|
|
protected:
|
|
using FN_Startup = decltype(&Gdiplus::GdiplusStartup);
|
|
using FN_Shutdown = decltype(&Gdiplus::GdiplusShutdown);
|
|
using FN_GetImageHorizontalResolution = decltype(&GPDE::GdipGetImageHorizontalResolution);
|
|
using FN_GetImageVerticalResolution = decltype(&GPDE::GdipGetImageVerticalResolution);
|
|
using FN_BitmapSetResolution = decltype(&GPDE::GdipBitmapSetResolution);
|
|
using FN_CreateBitmapFromHBITMAP = decltype(&GPDE::GdipCreateBitmapFromHBITMAP);
|
|
using FN_CreateBitmapFromFile = decltype(&GPDE::GdipCreateBitmapFromFile);
|
|
using FN_CreateHBITMAPFromBitmap = decltype(&GPDE::GdipCreateHBITMAPFromBitmap);
|
|
using FN_SaveImageToFile = decltype(&GPDE::GdipSaveImageToFile);
|
|
using FN_DisposeImage = decltype(&GPDE::GdipDisposeImage);
|
|
using FN_GetImageEncodersSize = decltype(&GPDE::GdipGetImageEncodersSize);
|
|
using FN_GetImageEncoders = decltype(&GPDE::GdipGetImageEncoders);
|
|
|
|
struct SHARED
|
|
{
|
|
HINSTANCE m_hGdiPlus = NULL;
|
|
LONG m_cRefs = 0;
|
|
ULONG_PTR m_dwToken = 0;
|
|
FN_Shutdown m_Shutdown = NULL;
|
|
FN_GetImageHorizontalResolution m_GetImageHorizontalResolution = NULL;
|
|
FN_GetImageVerticalResolution m_GetImageVerticalResolution = NULL;
|
|
FN_BitmapSetResolution m_BitmapSetResolution = NULL;
|
|
FN_CreateBitmapFromHBITMAP m_CreateBitmapFromHBITMAP = NULL;
|
|
FN_CreateBitmapFromFile m_CreateBitmapFromFile = NULL;
|
|
FN_CreateHBITMAPFromBitmap m_CreateHBITMAPFromBitmap = NULL;
|
|
FN_SaveImageToFile m_SaveImageToFile = NULL;
|
|
FN_DisposeImage m_DisposeImage = NULL;
|
|
FN_GetImageEncodersSize m_GetImageEncodersSize = NULL;
|
|
FN_GetImageEncoders m_GetImageEncoders = NULL;
|
|
|
|
HINSTANCE Init()
|
|
{
|
|
if (m_hGdiPlus)
|
|
return m_hGdiPlus;
|
|
|
|
m_hGdiPlus = ::LoadLibraryW(L"gdiplus.dll");
|
|
if (!m_hGdiPlus)
|
|
return NULL;
|
|
|
|
FN_Startup Startup = (FN_Startup)GetProcAddress(m_hGdiPlus, "GdiplusStartup");
|
|
m_Shutdown = (FN_Shutdown)GetProcAddress(m_hGdiPlus, "GdiplusShutdown");
|
|
if (!Startup || !m_Shutdown)
|
|
{
|
|
::FreeLibrary(m_hGdiPlus);
|
|
m_hGdiPlus = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
|
|
Startup(&m_dwToken, &gdiplusStartupInput, NULL);
|
|
|
|
return m_hGdiPlus;
|
|
}
|
|
|
|
void Free()
|
|
{
|
|
::FreeLibrary(m_hGdiPlus);
|
|
ZeroMemory(this, sizeof(*this));
|
|
}
|
|
|
|
LONG AddRef()
|
|
{
|
|
return ++m_cRefs;
|
|
}
|
|
|
|
LONG Release()
|
|
{
|
|
if (--m_cRefs == 0)
|
|
{
|
|
Free();
|
|
return 0;
|
|
}
|
|
return m_cRefs;
|
|
}
|
|
};
|
|
|
|
static SHARED* _shared()
|
|
{
|
|
static SHARED s_shared;
|
|
return &s_shared;
|
|
}
|
|
|
|
static Gdiplus::ImageCodecInfo* GetAllEncoders(UINT& cEncoders)
|
|
{
|
|
Gdiplus::ImageCodecInfo *ret = NULL;
|
|
UINT total_size;
|
|
|
|
if (!get_fn(_shared()->m_GetImageEncodersSize, "GdipGetImageEncodersSize") ||
|
|
!get_fn(_shared()->m_GetImageEncoders, "GdipGetImageEncoders"))
|
|
{
|
|
cEncoders = 0;
|
|
return NULL;
|
|
}
|
|
|
|
_shared()->m_GetImageEncodersSize(&cEncoders, &total_size);
|
|
if (total_size == 0)
|
|
return NULL;
|
|
|
|
ret = new Gdiplus::ImageCodecInfo[total_size / sizeof(ret[0])];
|
|
if (ret == NULL)
|
|
{
|
|
cEncoders = 0;
|
|
return NULL;
|
|
}
|
|
|
|
_shared()->m_GetImageEncoders(cEncoders, total_size, ret);
|
|
|
|
return ret; // needs delete[]
|
|
}
|
|
|
|
template <typename FN_T>
|
|
static bool get_fn(FN_T& fn, const char *name)
|
|
{
|
|
if (fn)
|
|
return true;
|
|
HINSTANCE hGdiPlus = _shared()->Init();
|
|
fn = reinterpret_cast<FN_T>(::GetProcAddress(hGdiPlus, name));
|
|
return fn != NULL;
|
|
}
|
|
|
|
// CImage::FindCodecForExtension is private. We have to duplicate it at here...
|
|
static CLSID
|
|
FindCodecForExtension(LPCWSTR dotext, const Gdiplus::ImageCodecInfo *pCodecs, UINT nCodecs)
|
|
{
|
|
for (UINT i = 0; i < nCodecs; ++i)
|
|
{
|
|
CStringW strSpecs(pCodecs[i].FilenameExtension);
|
|
int ichOld = 0, ichSep;
|
|
for (;;)
|
|
{
|
|
ichSep = strSpecs.Find(L';', ichOld);
|
|
|
|
CStringW strSpec;
|
|
if (ichSep < 0)
|
|
strSpec = strSpecs.Mid(ichOld);
|
|
else
|
|
strSpec = strSpecs.Mid(ichOld, ichSep - ichOld);
|
|
|
|
int ichDot = strSpec.ReverseFind(L'.');
|
|
if (ichDot >= 0)
|
|
strSpec = strSpec.Mid(ichDot);
|
|
|
|
if (!dotext || strSpec.CompareNoCase(dotext) == 0)
|
|
return pCodecs[i].Clsid;
|
|
|
|
if (ichSep < 0)
|
|
break;
|
|
|
|
ichOld = ichSep + 1;
|
|
}
|
|
}
|
|
return CLSID_NULL;
|
|
}
|
|
|
|
// CImage::FindCodecForFileType is private. We have to duplicate it at here...
|
|
static CLSID
|
|
FindCodecForFileType(REFGUID guidFileType, const Gdiplus::ImageCodecInfo *pCodecs, UINT nCodecs)
|
|
{
|
|
for (UINT iInfo = 0; iInfo < nCodecs; ++iInfo)
|
|
{
|
|
if (::IsEqualGUID(pCodecs[iInfo].FormatID, guidFileType))
|
|
return pCodecs[iInfo].Clsid;
|
|
}
|
|
return CLSID_NULL;
|
|
}
|
|
};
|