|
|
|
@@ -23,117 +23,131 @@ |
|
|
|
#include <string.h> |
|
|
|
|
|
|
|
#include <windows.h> |
|
|
|
#include <gdiplus.h> |
|
|
|
|
|
|
|
#include "pipi.h" |
|
|
|
#include "pipi_internals.h" |
|
|
|
|
|
|
|
extern "C" pipi_image_t *pipi_load_gdiplus(const char *name) |
|
|
|
{ |
|
|
|
BITMAPINFO binfo; |
|
|
|
pipi_image_t *img; |
|
|
|
pipi_pixels_t *p; |
|
|
|
uint8_t *data; |
|
|
|
HBITMAP hbmap; |
|
|
|
HDC hdc; |
|
|
|
|
|
|
|
hbmap = (HBITMAP)LoadImage(NULL, name, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); |
|
|
|
if(!hbmap) |
|
|
|
size_t len; |
|
|
|
if(mbstowcs_s(&len, NULL, 0, name, _TRUNCATE) != 0) |
|
|
|
return NULL; |
|
|
|
wchar_t *wname = new wchar_t[len]; |
|
|
|
if(mbstowcs_s(NULL, wname, len, name, _TRUNCATE) != 0) |
|
|
|
{ |
|
|
|
delete[] wname; |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
hdc = CreateCompatibleDC(NULL); |
|
|
|
SelectObject(hdc, hbmap); |
|
|
|
ULONG_PTR token; |
|
|
|
Gdiplus::GdiplusStartupInput input; |
|
|
|
Gdiplus::GdiplusStartup(&token, &input, NULL); |
|
|
|
|
|
|
|
memset(&binfo, 0, sizeof(binfo)); |
|
|
|
binfo.bmiHeader.biSize = sizeof(binfo.bmiHeader); |
|
|
|
if(!GetDIBits(hdc, hbmap, 0, 0, 0, &binfo, DIB_RGB_COLORS)) |
|
|
|
Gdiplus::Bitmap *b = Gdiplus::Bitmap::FromFile(wname, 0); |
|
|
|
if(!b) |
|
|
|
{ |
|
|
|
ReleaseDC(0, hdc); |
|
|
|
DeleteObject(hbmap); |
|
|
|
delete[] wname; |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
delete[] wname; |
|
|
|
|
|
|
|
img = pipi_new(binfo.bmiHeader.biWidth, binfo.bmiHeader.biHeight); |
|
|
|
p = pipi_getpixels(img, PIPI_PIXELS_RGBA_C); |
|
|
|
data = (uint8_t *)p->pixels; |
|
|
|
Gdiplus::BitmapData bdata; |
|
|
|
Gdiplus::Rect rect(0, 0, b->GetWidth(), b->GetHeight()); |
|
|
|
|
|
|
|
binfo.bmiHeader.biBitCount = 32; |
|
|
|
binfo.bmiHeader.biCompression = BI_RGB; |
|
|
|
binfo.bmiHeader.biHeight = - abs(binfo.bmiHeader.biHeight); |
|
|
|
if(!GetDIBits(hdc, hbmap, 0, abs(binfo.bmiHeader.biHeight), data, |
|
|
|
&binfo, DIB_RGB_COLORS)) |
|
|
|
if(b->LockBits(&rect, Gdiplus::ImageLockModeRead, |
|
|
|
PixelFormat32bppARGB, &bdata) != Gdiplus::Ok) |
|
|
|
{ |
|
|
|
ReleaseDC(0, hdc); |
|
|
|
DeleteObject(hbmap); |
|
|
|
delete b; |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
/* FIXME: do we need to swap bytes? Apparently Vista doesn't need it, |
|
|
|
* but we'd need a more thorough test. */ |
|
|
|
pipi_image_t *img = pipi_new(b->GetWidth(), b->GetHeight()); |
|
|
|
pipi_pixels_t *p = pipi_getpixels(img, PIPI_PIXELS_RGBA_C); |
|
|
|
memcpy(p->pixels, bdata.Scan0, bdata.Width * bdata.Height * 4); |
|
|
|
|
|
|
|
b->UnlockBits(&bdata); |
|
|
|
delete b; |
|
|
|
|
|
|
|
img->codec_priv = NULL; |
|
|
|
|
|
|
|
img->wrap = 0; |
|
|
|
img->u8 = 1; |
|
|
|
|
|
|
|
ReleaseDC(0, hdc); |
|
|
|
DeleteObject(hbmap); |
|
|
|
|
|
|
|
return img; |
|
|
|
} |
|
|
|
|
|
|
|
extern "C" int pipi_save_gdiplus(pipi_image_t *img, const char *name) |
|
|
|
{ |
|
|
|
BITMAPINFOHEADER binfo; |
|
|
|
BITMAPFILEHEADER bfheader; |
|
|
|
uint8_t dummy[4] = { 0 }; |
|
|
|
HANDLE hfile; |
|
|
|
pipi_pixels_t *p; |
|
|
|
DWORD ret = 0; |
|
|
|
uint8_t *data; |
|
|
|
int x, y, padding; |
|
|
|
|
|
|
|
p = pipi_getpixels(img, PIPI_PIXELS_RGBA_C); |
|
|
|
data = (uint8_t *)p->pixels; |
|
|
|
|
|
|
|
padding = ((img->w * 3) + 3) / 4 * 4 - img->w * 3; |
|
|
|
|
|
|
|
memset(&binfo, 0, sizeof(binfo)); |
|
|
|
binfo.biSize = sizeof(binfo); |
|
|
|
binfo.biWidth = img->w; |
|
|
|
binfo.biHeight = img->h; |
|
|
|
binfo.biPlanes = 1; |
|
|
|
binfo.biBitCount = 24; |
|
|
|
binfo.biCompression = BI_RGB; |
|
|
|
binfo.biSizeImage = (img->w * 3 + padding) * img->h; |
|
|
|
|
|
|
|
memset(&bfheader, 0, sizeof(bfheader)); |
|
|
|
bfheader.bfType = 0x4D42; |
|
|
|
bfheader.bfOffBits = sizeof(bfheader) + sizeof(binfo); |
|
|
|
bfheader.bfSize = bfheader.bfOffBits + binfo.biSizeImage; |
|
|
|
|
|
|
|
/* We don’t even create the bitmap object, since we know exactly |
|
|
|
* what kind of file we are saving. But later, when we support |
|
|
|
* different depths and BMP options, we'll need to care about it. */ |
|
|
|
hfile = CreateFile(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, |
|
|
|
FILE_ATTRIBUTE_ARCHIVE, NULL); |
|
|
|
if(!hfile) |
|
|
|
wchar_t const *fmt; |
|
|
|
if(strstr(name, ".gif")) |
|
|
|
fmt = L"image/gif"; |
|
|
|
else if(strstr(name, ".jpeg") || strstr(name, ".jpeg")) |
|
|
|
fmt = L"image/jpeg"; |
|
|
|
else if(strstr(name, ".png")) |
|
|
|
fmt = L"image/png"; |
|
|
|
else if(strstr(name, ".tiff")) |
|
|
|
fmt = L"image/tiff"; |
|
|
|
|
|
|
|
unsigned int num = 0, size = 0; |
|
|
|
Gdiplus::GetImageEncodersSize(&num, &size); |
|
|
|
if(size == 0) |
|
|
|
return -1; |
|
|
|
Gdiplus::ImageCodecInfo *codecs |
|
|
|
= (Gdiplus::ImageCodecInfo *)(malloc(size)); |
|
|
|
if(!codecs) |
|
|
|
return -1; |
|
|
|
WriteFile(hfile, &bfheader, sizeof(bfheader), &ret, NULL); |
|
|
|
WriteFile(hfile, &binfo, sizeof(binfo), &ret, NULL); |
|
|
|
for(y = 0; y < img->h; y++) |
|
|
|
Gdiplus::GetImageEncoders(num, size, codecs); |
|
|
|
CLSID clsid; |
|
|
|
for(unsigned int i = 0; i < num; i++) |
|
|
|
{ |
|
|
|
for(x = 0; x < img->w; x++) |
|
|
|
if(wcscmp(codecs[i].MimeType, fmt) == 0) |
|
|
|
{ |
|
|
|
uint8_t tmp[3]; |
|
|
|
tmp[0] = data[4 * (img->w * (img->h - 1 - y) + x) + 0]; |
|
|
|
tmp[1] = data[4 * (img->w * (img->h - 1 - y) + x) + 1]; |
|
|
|
tmp[2] = data[4 * (img->w * (img->h - 1 - y) + x) + 2]; |
|
|
|
WriteFile(hfile, tmp, 3, &ret, NULL); |
|
|
|
clsid = codecs[i].Clsid; |
|
|
|
break; |
|
|
|
} |
|
|
|
if(padding) |
|
|
|
WriteFile(hfile, dummy, padding, &ret, NULL); |
|
|
|
} |
|
|
|
CloseHandle(hfile); |
|
|
|
|
|
|
|
size_t len; |
|
|
|
if(mbstowcs_s(&len, NULL, 0, name, _TRUNCATE) != 0) |
|
|
|
return NULL; |
|
|
|
wchar_t *wname = new wchar_t[len]; |
|
|
|
if(mbstowcs_s(NULL, wname, len, name, _TRUNCATE) != 0) |
|
|
|
{ |
|
|
|
delete[] wname; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
ULONG_PTR token; |
|
|
|
Gdiplus::GdiplusStartupInput input; |
|
|
|
Gdiplus::GdiplusStartup(&token, &input, NULL); |
|
|
|
|
|
|
|
Gdiplus::Bitmap *b = new Gdiplus::Bitmap(img->w, img->h, |
|
|
|
PixelFormat32bppARGB); |
|
|
|
|
|
|
|
Gdiplus::BitmapData bdata; |
|
|
|
Gdiplus::Rect rect(0, 0, img->w, img->h); |
|
|
|
|
|
|
|
if(b->LockBits(&rect, Gdiplus::ImageLockModeWrite, |
|
|
|
PixelFormat32bppARGB, &bdata) != Gdiplus::Ok) |
|
|
|
{ |
|
|
|
delete b; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
pipi_pixels_t *p = pipi_getpixels(img, PIPI_PIXELS_RGBA_C); |
|
|
|
memcpy(bdata.Scan0, p->pixels, bdata.Width * bdata.Height * 4); |
|
|
|
|
|
|
|
b->UnlockBits(&bdata); |
|
|
|
|
|
|
|
if(b->Save(wname, &clsid, NULL) != Gdiplus::Ok) |
|
|
|
{ |
|
|
|
delete[] wname; |
|
|
|
delete b; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
delete[] wname; |
|
|
|
delete b; |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|