Comment enregistrer HBITMAP en format JPG à l'aide Imaging API Win32 (Windows Mobile 6+)?
J'ai créé des procédures pour enregistrer de la fenêtre de capture d'écran dans un fichier. Il travaille pour la PNG et BMP, mais pas pour JPG (et GIF).
Voici le code pour la capture de 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;
}
Et voici le code pour la sauvegarde de l'image:
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;
}
J'ai utilisé le code de Koders.com comme un exemple, et j'ai essayé de suivre MSDN description de codage de l'image quand a modifié cet exemple.
Trouvé aussi que les autres ont des problème similaire, mais avec pas de réponse:
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
J'ai aussi trouvé une solution qui utilise GDI+ wrapper:
www.ernzo.com/LibGdiplus.aspx
www.codeproject.com/KB/windows/gdiplusandwinmobile.aspx
Mais je ne peux pas utiliser cette GDI+ lib. Aussi je n'ai pas besoin de GDI+. Essayé de créer similaires d'économie de procédure, comme dans ce film, mais sans succès.
MODIFIER
Ici est fixe et la solution de travail (Grâce PhilMY pour la réponse):
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;
}
Vous devez vous connecter pour publier un commentaire.
J'ai un code qui a travaillé pour les Jpeg sur WinCE 6.0.
Les principales différences sont:
ImageCodecInfo::FormatID
contreImageFormatJPEG
pour correspondre à un codeurSetEncoderParameters
pour définirENCODER_QUALITY
IBitmapImage
ensuite utiliserIImage::PushIntoSink