Search code examples
c++cwinapijpegdecoding

How to Decode JPEG Using Win32?


I have a chunk of memory containing the raw bytes of a JPEG and would like to convert it to 24-bit RGB so I can render it using StretchDIBits. I've looked around msdn but jeez... I really don't want to use any third party libraries or even C++ if I don't have to. Any suggestions? I wish there was just some function like DecodeJpeg() or something...


Solution

  • I came up with a solution that uses WIC, which doesn't seem to use C++, but I wasn't able to get the following code to compile as C, so not sure. Took A LOT of time to put the pieces together, but here it is!

    #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);
    }
    

    EDIT: By popular demand, here's a version with basic error checking and some rudimentary explanations:

    #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);
    }