Как сохранить HBITMAP как JPG с помощью Win32 Imaging API (Windows Mobile 6+)?
Я создал процедуры для сохранения скриншота окна в файл. Он работает для PNG и BMP, но не для JPG (и GIF). Вот код для захвата HBITMAP:
HBITMAP Signature::getScreenHBITMAP() {
// get screen rectangle
RECT windowRect;
GetWindowRect(getMainWnd(), &windowRect);
// bitmap dimensions
int bitmap_dx = windowRect.right - windowRect.left;
int bitmap_dy = windowRect.bottom - windowRect.top;
// create bitmap info header
BITMAPINFOHEADER infoHeader;
infoHeader.biSize = sizeof(infoHeader);
infoHeader.biWidth = bitmap_dx;
infoHeader.biHeight = bitmap_dy;
infoHeader.biPlanes = 1;
infoHeader.biBitCount = 24;
infoHeader.biCompression = BI_RGB;
infoHeader.biSizeImage = 0;
infoHeader.biXPelsPerMeter = 0;
infoHeader.biYPelsPerMeter = 0;
infoHeader.biClrUsed = 0;
infoHeader.biClrImportant = 0;
// dibsection information
BITMAPINFO info;
info.bmiHeader = infoHeader;
HDC winDC = GetWindowDC(getMainWnd());
HDC memDC = CreateCompatibleDC(winDC);
BYTE* memory = 0;
HBITMAP bitmap = CreateDIBSection(winDC, &info, DIB_RGB_COLORS, (void**)&memory, 0, 0);
SelectObject(memDC, bitmap);
// Copies screen upside down (as it is already upside down) - if need normal layout, change to BitBlt function call
StretchBlt(memDC, 0, 0, bitmap_dx, bitmap_dy, winDC, 0, bitmap_dy, bitmap_dx, bitmap_dy * -1, SRCCOPY);
DeleteDC(memDC);
ReleaseDC(getMainWnd(), winDC);
return bitmap;
}
а вот код для сохранения изображения:
HRESULT Imaging_SaveToFile(HBITMAP handle, LPTSTR filename, LPCTSTR format){
HRESULT res;
res = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if ((res == S_OK) || (res == S_FALSE)) {
IImagingFactory* factory=NULL;
if (CoCreateInstance(CLSID_ImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IImagingFactory, (void**)&factory) == S_OK) {
UINT count;
ImageCodecInfo* imageCodecInfo=NULL;
if (factory->GetInstalledEncoders(&count, &imageCodecInfo) == S_OK) {
// Get the particular encoder to use
LPTSTR formatString;
if (wcscmp(format, L"png") == 0) {
formatString = _T("image/png");
} else if (wcscmp(format, L"jpg") == 0) {
formatString = _T("image/jpeg");
} else if (wcscmp(format, L"gif") == 0) {
formatString = _T("image/gif");
} else if (wcscmp(format, L"bmp") == 0) {
formatString = _T("image/bmp");
} else {
CoUninitialize();
return S_FALSE;
}
CLSID encoderClassId;
if (count == 0) {
CoUninitialize();
return S_FALSE;
}
for(int i=0; i < (int)count; i++) {
if (wcscmp(imageCodecInfo[i].MimeType, formatString) == 0) {
encoderClassId= imageCodecInfo[i].Clsid;
free(imageCodecInfo);
break;
} else {
continue;
}
CoUninitialize();
return S_FALSE;
}
IImageEncoder* imageEncoder=NULL;
if (factory->CreateImageEncoderToFile(&encoderClassId, filename, &imageEncoder) == S_OK) {
IImageSink* imageSink = NULL;
res = imageEncoder->GetEncodeSink(&imageSink);
if (res != S_OK) {
CoUninitialize();
return res;
}
BITMAP bm;
GetObject (handle, sizeof(BITMAP), &bm);
ImageInfo* imageInfo = new ImageInfo();
imageInfo->Width = bm.bmWidth;
imageInfo->Height = bm.bmHeight;
imageInfo->RawDataFormat = IMGFMT_MEMORYBMP; //ImageFormatMemoryBMP;
imageInfo->Flags |= SinkFlagsTopDown | SinkFlagsFullWidth;
// Get pixel format from hBitmap
PixelFormatID pixelFormat;
int numColors = 0;
switch (bm.bmBitsPixel) {
case 1: {
pixelFormat = PixelFormat1bppIndexed;
numColors = 1;
break;
}
case 4: {
pixelFormat = PixelFormat4bppIndexed;
numColors = 16;
break;
}
case 8: {
pixelFormat = PixelFormat8bppIndexed;
numColors = 256;
break;
}
case 24: {
pixelFormat = PixelFormat24bppRGB;
break;
}
default: {
pixelFormat = PixelFormat32bppARGB;
numColors = 3; // according to MSDN 16 and 32 bpp numColors should be 3
break;
}
}
imageInfo->PixelFormat = pixelFormat;
if (pixelFormat == PixelFormat32bppARGB) imageInfo->Flags |= SinkFlagsHasAlpha;
res = imageSink->BeginSink(imageInfo, NULL);
if (res != S_OK) {
CoUninitialize();
return res;
}
ColorPalette* palette = NULL;
if (numColors > 0) {
palette = (ColorPalette*)malloc(sizeof(ColorPalette) + (numColors - 1) * sizeof(ARGB));
palette->Count = numColors;
for (int i=0; i<numColors; i++) {
int rgb = i*64;
int red = rgb & 0x00FF;
int green = (rgb >> 8) & 0x00FF;
int blue = (rgb >> 16) & 0x00FF;
palette->Entries[i] = MAKEARGB(0, red, green, blue);
}
} else {
palette = (ColorPalette*)malloc(sizeof(ColorPalette));
palette->Count = 0;
if (pixelFormat == PixelFormat32bppARGB) palette->Flags = PALFLAG_HASALPHA;
}
res = imageSink->SetPalette(palette);
if (res != S_OK) {
CoUninitialize();
return res;
}
BitmapData* bmData = new BitmapData();
bmData->Height = bm.bmHeight;
bmData->Width = bm.bmWidth;
bmData->Scan0 = bm.bmBits;
bmData->PixelFormat = pixelFormat;
UINT bitsPerLine = imageInfo->Width * bm.bmBitsPixel;
UINT bitAlignment = sizeof(LONG) * 8;
UINT bitStride = bitAlignment * (bitsPerLine / bitAlignment); // The image buffer is always padded to LONG boundaries
if ((bitsPerLine % bitAlignment) != 0) bitStride += bitAlignment; // Add a bit more for the leftover values
bmData->Stride = (bitStride / 8);
RECT rect;
rect.top = 0;
rect.bottom = bm.bmHeight;
rect.left = 0;
rect.right = bm.bmWidth;
res = imageSink->PushPixelData(&rect, bmData, TRUE);
if (res != S_OK) {
CoUninitialize();
return res;
}
res = imageSink->EndSink(S_OK);
if (res != S_OK) {
CoUninitialize();
return res;
}
imageSink->Release();
res = imageEncoder->TerminateEncoder();
if (res != S_OK) {
CoUninitialize();
return res;
}
}
}
}
CoUninitialize();
} else {
return res;
}
return res;
}
я использовал код из Koders.com в качестве примера и попытался следовать MSDN описание кодирования изображений при изменении этого примера.
найдено также, что другие имеют аналогичную проблему, но без ответ:
social.msdn.microsoft.com/Forums/en-US/windowsmobiledev/thread/1c368cc1-cc5b-419e-a7d2-2a39c90ae83d/
groups.google.com/group/microsoft.public.windowsce.embedded.vc/browse_thread/thread/8cd67e16ac29627b/9242e82721c48ace?hl=hu&pli=1
Я также нашел решение, которое использует GDI + wrapper:
www.ernzo.com/LibGdiplus.aspx
www.codeproject.com/KB/windows/gdiplusandwinmobile.aspx
но я не могу использовать этот GDI + lib. Также мне не нужен весь GDI+. Пытался создать подобную процедуру сохранения, как в этой обертке, но без успеха.
редактировать
здесь является фиксированной и рабочего раствора (спасибо PhilMY для ответа):
HRESULT Imaging_SaveToFile(HBITMAP handle, LPTSTR filename, LPCTSTR format){
HRESULT res;
res = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if ((res == S_OK) || (res == S_FALSE)) {
IImagingFactory* factory=NULL;
if (CoCreateInstance(CLSID_ImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IImagingFactory, (void**)&factory) == S_OK) {
UINT count;
ImageCodecInfo* imageCodecInfo=NULL;
if (factory->GetInstalledEncoders(&count, &imageCodecInfo) == S_OK) {
// Get the particular encoder to use
LPTSTR formatString;
if (wcscmp(format, L"png") == 0) {
formatString = _T("image/png");
} else if (wcscmp(format, L"jpg") == 0) {
formatString = _T("image/jpeg");
} else if (wcscmp(format, L"gif") == 0) {
formatString = _T("image/gif");
} else if (wcscmp(format, L"bmp") == 0) {
formatString = _T("image/bmp");
} else {
CoUninitialize();
return S_FALSE;
}
CLSID encoderClassId;
if (count == 0) {
CoUninitialize();
return S_FALSE;
}
for(int i=0; i < (int)count; i++) {
if (wcscmp(imageCodecInfo[i].MimeType, formatString) == 0) {
encoderClassId= imageCodecInfo[i].Clsid;
free(imageCodecInfo);
break;
} else {
continue;
}
CoUninitialize();
return S_FALSE;
}
IImageEncoder* imageEncoder=NULL;
if (factory->CreateImageEncoderToFile(&encoderClassId, filename, &imageEncoder) == S_OK) {
IImageSink* imageSink = NULL;
res = imageEncoder->GetEncodeSink(&imageSink);
if (res != S_OK) {
CoUninitialize();
return res;
}
BITMAP bm;
GetObject (handle, sizeof(BITMAP), &bm);
PixelFormatID pixelFormat;
switch (bm.bmBitsPixel) {
case 1: {
pixelFormat = PixelFormat1bppIndexed;
break;
}
case 4: {
pixelFormat = PixelFormat4bppIndexed;
break;
}
case 8: {
pixelFormat = PixelFormat8bppIndexed;
break;
}
case 24: {
pixelFormat = PixelFormat24bppRGB;
break;
}
default: {
pixelFormat = PixelFormat32bppARGB;
break;
}
}
BitmapData* bmData = new BitmapData();
bmData->Height = bm.bmHeight;
bmData->Width = bm.bmWidth;
bmData->Scan0 = bm.bmBits;
bmData->PixelFormat = pixelFormat;
UINT bitsPerLine = bm.bmWidth * bm.bmBitsPixel;
UINT bitAlignment = sizeof(LONG) * 8;
UINT bitStride = bitAlignment * (bitsPerLine / bitAlignment); // The image buffer is always padded to LONG boundaries
if ((bitsPerLine % bitAlignment) != 0) bitStride += bitAlignment; // Add a bit more for the leftover values
bmData->Stride = (bitStride / 8);
IBitmapImage* pBitmap;
factory->CreateBitmapFromBuffer(bmData, &pBitmap);
IImage* pImage;
pBitmap->QueryInterface(IID_IImage, (void**)&pImage);
res = pImage->PushIntoSink(imageSink);
if (res != S_OK) {
CoUninitialize();
return res;
}
pBitmap->Release();
pImage->Release();
imageSink->Release();
imageEncoder->TerminateEncoder();
imageEncoder->Release();
}
}
}
CoUninitialize();
} else {
return res;
}
return res;
}
1 ответов
у меня есть аналогичный код, который работал для JPEGs на WinCE 6.0.
основные отличия:
- проверить
ImageCodecInfo::FormatID
противImageFormatJPEG
в соответствии с энкодером - Я называю
SetEncoderParameters
установитьENCODER_QUALITY
- я копирую исходное растровое изображение в
IBitmapImage
затем использоватьIImage::PushIntoSink