如何使用Win32 Imaging API(Windows Mobile 6+)将HBITMAP保存为JPG?

时间:2011-04-01 10:03:41

标签: c++ visual-c++ windows-mobile windows-mobile-6 windows-mobile-6.5

我已经创建了将窗口截屏保存到文件的过程。它适用于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 description of image encoding

还发现其他人也有类似的问题,但没有回答:

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 +包装器的解决方案:

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 个答案:

答案 0 :(得分:3)

我有一些类似的代码适用于WinCE 6.0上的JPEG。

主要区别是:

  • 我针对ImageCodecInfo::FormatID检查ImageFormatJPEG以匹配编码器
  • 我致电SetEncoderParameters设置ENCODER_QUALITY
  • 我将源位图复制到IBitmapImage,然后使用IImage::PushIntoSink