如何使用 Win32 解码 JPEG?

How to Decode JPEG Using Win32?

我有一块内存包含 JPEG 的原始字节,我想将其转换为 24 位 RGB,以便我可以使用 StretchDIBits 渲染它。我环顾了 msdn,但是天哪……我真的不想使用任何第三方库,如果不需要的话,甚至不想使用 C++。有什么建议么?我希望只有一些函数,比如 DecodeJpeg() 之类的...

我想出了一个使用 WIC 的解决方案,它似乎没有使用 C++,但我无法将以下代码编译为 C,所以不确定。花了很多时间将各个部分拼凑在一起,但就是这样!

#include <Wincodec.h>
#include <stdio.h>

#pragma comment(lib, "Ole32.lib")
#pragma comment(lib, "Windowscodecs.lib")

#define MIN(A, B) (A < B ? A : B)

void
Win32DecodeJpeg(unsigned int ImageDataSize, void *ImageData, 
                unsigned int DestSize, void *Dest)
{
    static IWICImagingFactory *IWICFactory;
    if(IWICFactory == NULL)
    {
        CoInitializeEx(NULL, COINIT_MULTITHREADED);
        CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&IWICFactory));
    }

    IWICStream *Stream;
    IWICFactory->CreateStream(&Stream);
    Stream->InitializeFromMemory((unsigned char *)ImageData, ImageDataSize);

    IWICBitmapDecoder *BitmapDecoder;
    IWICFactory->CreateDecoderFromStream(Stream, NULL, WICDecodeMetadataCacheOnDemand, &BitmapDecoder);

    IWICBitmapFrameDecode *FrameDecode;
    BitmapDecoder->GetFrame(0, &FrameDecode);

    IWICFormatConverter *FormatConverter;
    IWICFactory->CreateFormatConverter(&FormatConverter);

    FormatConverter->Initialize(FrameDecode,
                                GUID_WICPixelFormat24bppBGR,
                                WICBitmapDitherTypeNone,
                                nullptr,
                                0.0f,
                                WICBitmapPaletteTypeCustom);

    IWICBitmap *Bitmap;
    IWICFactory->CreateBitmapFromSource(FormatConverter, WICBitmapCacheOnDemand, &Bitmap);

    unsigned int Width, Height;
    Bitmap->GetSize(&Width, &Height);
    WICRect Rect = {0, 0, (int)Width, (int)Height};

    IWICBitmapLock *Lock;
    Bitmap->Lock(&Rect, WICBitmapLockRead, &Lock);

    unsigned int PixelDataSize = 0;
    unsigned char *PixelData;
    Lock->GetDataPointer(&PixelDataSize, &PixelData);

    memcpy(Dest, PixelData, MIN(DestSize, PixelDataSize));

    Stream->Release();
    BitmapDecoder->Release();
    FrameDecode->Release();
    FormatConverter->Release();
    Bitmap->Release();
    Lock->Release();
}

int
main()
{
    int ImageWidth = 640;
    int ImageHeight = 480;

    int DecodedBufferSize = ImageWidth * ImageHeight * 3;
    void *DecodedBuffer = malloc(DecodedBufferSize);

    FILE *ImageFile = fopen("test.jpg", "rb");

    fseek(ImageFile, 0, SEEK_END);
    int ImageSize = ftell(ImageFile);
    rewind(ImageFile);

    void *ImageData = malloc(ImageSize);
    fread(ImageData, 1, ImageSize, ImageFile);

    fclose(ImageFile);

    Win32DecodeJpeg(ImageSize, ImageData, DecodedBufferSize, DecodedBuffer);
}

编辑:应大众需求,这里有一个具有基本错误检查和一些基本解释的版本:

#include <Wincodec.h>
#include <stdio.h>

#pragma comment(lib, "Ole32.lib")
#pragma comment(lib, "Windowscodecs.lib")

void
Win32DecodeJpeg(unsigned int ImageDataSize, void *ImageData, 
                unsigned int DestSize, void *Dest)
{
    // IWICImagingFactory is a structure containing the function pointers of the WIC API
    static IWICImagingFactory *IWICFactory;
    if(IWICFactory == NULL)
    {
        if(CoInitializeEx(NULL, COINIT_MULTITHREADED) != S_OK)
        {
            printf("failed to initialize the COM library\n");
            return;
        }

        if(CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&IWICFactory)) != S_OK)
        {
            printf("failed to create an instance of WIC\n");
            return;
        }
    }

    IWICStream *Stream;
    if(IWICFactory->CreateStream(&Stream) != S_OK)
    {
        printf("failed to create stream\n");
        return;
    }

    if(Stream->InitializeFromMemory((unsigned char *)ImageData, ImageDataSize) != S_OK)
    {
        printf("failed to initialize stream from memory\n");
        return;
    }

    IWICBitmapDecoder *BitmapDecoder;
    if(IWICFactory->CreateDecoderFromStream(Stream, NULL, WICDecodeMetadataCacheOnDemand, &BitmapDecoder) != S_OK)
    {
        printf("failed to create bitmap decoder from stream\n");
        return;
    }

    IWICBitmapFrameDecode *FrameDecode;
    // frames apply mostly to GIFs and other animated media. JPEGs just have a single frame.
    if(BitmapDecoder->GetFrame(0, &FrameDecode) != S_OK)
    {
        printf("failed to get 0th frame from frame decoder\n");
        return;
    }

    IWICFormatConverter *FormatConverter;
    if(IWICFactory->CreateFormatConverter(&FormatConverter) != S_OK)
    {
        printf("failed to create format converter\n");
        return;
    }

    // this function does not do any actual decoding
    if(FormatConverter->Initialize(FrameDecode,
                                   GUID_WICPixelFormat24bppBGR,
                                   WICBitmapDitherTypeNone,
                                   nullptr,
                                   0.0f,
                                   WICBitmapPaletteTypeCustom) != S_OK)
    {
        printf("failed to initialize format converter\n");
        return;
    }

    IWICBitmap *Bitmap;
    if(IWICFactory->CreateBitmapFromSource(FormatConverter, WICBitmapCacheOnDemand, &Bitmap) != S_OK)
    {
        printf("failed to create bitmap from format converter\n");
        return;
    }

    unsigned int Width, Height;
    if(Bitmap->GetSize(&Width, &Height) != S_OK)
    {
        printf("failed to get the size of the bitmap\n");
        return;
    }
    WICRect Rect = {0, 0, (int)Width, (int)Height};

    IWICBitmapLock *Lock;
    // this is the function that does the actual decoding. seems like they defer the decoding until it's actually needed
    if(Bitmap->Lock(&Rect, WICBitmapLockRead, &Lock) != S_OK)
    {
        printf("failed to lock bitmap\n");
        return;
    }

    unsigned int PixelDataSize = 0;
    unsigned char *PixelData;
    if(Lock->GetDataPointer(&PixelDataSize, &PixelData) != S_OK)
    {
        printf("failed to get data pointer\n");
        return;
    }

    memcpy(Dest, PixelData, DestSize < PixelDataSize ? DestSize : PixelDataSize);

    Stream->Release();
    BitmapDecoder->Release();
    FrameDecode->Release();
    FormatConverter->Release();
    Bitmap->Release();
    Lock->Release();
}

int
main()
{
    int ImageWidth = 640;
    int ImageHeight = 480;

    int DecodedBufferSize = ImageWidth * ImageHeight * 3;
    void *DecodedBuffer = malloc(DecodedBufferSize);

    FILE *ImageFile = fopen("test.jpg", "rb");

    fseek(ImageFile, 0, SEEK_END);
    int ImageSize = ftell(ImageFile);
    rewind(ImageFile);

    void *ImageData = malloc(ImageSize);
    fread(ImageData, 1, ImageSize, ImageFile);

    fclose(ImageFile);

    Win32DecodeJpeg(ImageSize, ImageData, DecodedBufferSize, DecodedBuffer);
}