0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[C++]GDI+でJPEG保存

Last updated at Posted at 2019-11-26

WindowsのC++にてGDI+を使ってJPEG画像を保存する方法を試したので記載します。
(jpegライブラリなどを入れたりするのが面倒だったので)

ビルド環境

  • Windows 10
  • Build Tools for Visual Studio 2019
  • Visual Studio Code

ソースコード

エラー処理とか関数分割とか一切していないので、実際に使用する際には注意

# include <Windows.h>
# include <gdiplus.h>
# include <gdipluspixelformats.h>
# pragma comment(lib, "gdiplus.lib")

static int extendStride(int stride)
{
    const int STRIDE_UNIT_BYTE = 4;
    return (((stride + STRIDE_UNIT_BYTE - 1) / STRIDE_UNIT_BYTE) * STRIDE_UNIT_BYTE);
}

void main(void)
{
    int width = 300;
    int height = 300;
    int gdiStride = extendStride(width * 3);  // RGB用
    // 画像データ作成(RGB) 動作確認用
    byte* pixels = (byte*)malloc(gdiStride * height);
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            int i = (x * 3) + (y * gdiStride);
            pixels[i+0] = (byte)(x & 0xff);                 // B
            pixels[i+1] = (byte)(y & 0xff);                 // G
            pixels[i+2] = (byte)((0x80 + x + y) & 0xff);    // R
        }
    }
    // jpeg保存
    {
        // GDI+初期化
        Gdiplus::GdiplusStartupInput input;
        ULONG_PTR token;
        Gdiplus::GdiplusStartup(&token, &input, NULL);

        CLSID id;
        // EncoderのCLSIDの取得
        {
            UINT num = 0;
            UINT size = 0;
            Gdiplus::GetImageEncodersSize(&num, &size);
            Gdiplus::ImageCodecInfo *pImageCodecInfo = new Gdiplus::ImageCodecInfo[size];
            Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);
            for (UINT j = 0; j < num; ++j) {
                if (wcscmp(pImageCodecInfo[j].MimeType, L"image/jpeg") ==0) {
                    id = pImageCodecInfo[j].Clsid;
                    break;
                }
            }
            delete [] pImageCodecInfo;
        }    
        // 保存処理
        {
            Gdiplus::Bitmap image(width, height, gdiStride, PixelFormat24bppRGB, pixels);
            image.Save(L"out.jpg", &id);
        }
        // GDI+解放
        Gdiplus::GdiplusShutdown(token);
    }
    free(pixels);
}

IStreamを使って標準出力に出力するようにした例


# include <stdio.h>
# include <fcntl.h>
# include <io.h>
# include <Windows.h>
# include <gdiplus.h>
# include <gdipluspixelformats.h> // PixelFormat24bppRGB
# pragma comment(lib, "gdiplus.lib")

class FilePointerStream : IStream 
{
public:
    static BOOL CreateInstance(FILE* fp, IStream **ppStream) {
        FilePointerStream* pFileStream = new FilePointerStream(fp);
        if (!pFileStream) return FALSE;
        _setmode( _fileno(fp), _O_BINARY);
        pFileStream->QueryInterface(IID_IStream, (void **)ppStream);
        return TRUE;
    }
private:
    // コンストラクタ/デストラクタ
    FilePointerStream(FILE* fp) { 
        m_fp = fp; 
        m_nRefCnt = 0;
    }
    virtual ~FilePointerStream(void) {}

    // IUnknown
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) {
        if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ISequentialStream) || IsEqualIID(riid, IID_IStream)) {
            *ppvObject = static_cast<IStream *>(this);
            AddRef();   // 参照カウンタをインクリメント
            return S_OK;
        }
        *ppvObject = NULL;
        return E_NOINTERFACE;
    }
    virtual ULONG STDMETHODCALLTYPE AddRef(void) { return ++m_nRefCnt; }
    virtual ULONG STDMETHODCALLTYPE Release(void) { 
        if (--m_nRefCnt > 0) { return m_nRefCnt; }
        delete this;
        fprintf(stderr, "Release  delete\n");
        return 0;
    }

    // ISequentialStream
    virtual HRESULT STDMETHODCALLTYPE Read(void* pv, ULONG cb, ULONG *pcbRead) {
        size_t ret = fread(pv, cb, 1, m_fp);
        if (1 != ret) { return S_FALSE; }
        if (NULL != pcbRead) { *pcbRead = (ULONG)cb; }
        return S_OK;
    }
    virtual HRESULT STDMETHODCALLTYPE Write(const void* pv, ULONG cb, ULONG* pcbWritten) {
        size_t ret = fwrite(pv, cb, 1, m_fp);
        if (1 != ret) { return S_FALSE; }
        if (NULL != pcbWritten) { *pcbWritten = (ULONG)cb; }
        return S_OK;
    }

    // IStream
    virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition) {
        int origin = SEEK_SET;
        if (dwOrigin == STREAM_SEEK_SET) origin = SEEK_SET;
        if (dwOrigin == STREAM_SEEK_CUR) origin = SEEK_CUR;
        if (dwOrigin == STREAM_SEEK_END) origin = SEEK_END;
        if (0 != fseek(m_fp, dlibMove.LowPart, origin)) { return E_UNEXPECTED; }
        if (plibNewPosition != NULL) { plibNewPosition->LowPart = ftell(m_fp); }
        return S_OK;
    }
    virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER) { return E_NOTIMPL; }
    virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream *, ULARGE_INTEGER, ULARGE_INTEGER *, ULARGE_INTEGER *) { return E_NOTIMPL; }
    virtual HRESULT STDMETHODCALLTYPE Commit(DWORD) { return E_NOTIMPL; }
    virtual HRESULT STDMETHODCALLTYPE Revert(void) { return E_NOTIMPL; }
    virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD) { return E_NOTIMPL; }
    virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD) { return E_NOTIMPL; }
    virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG *, DWORD) {return E_NOTIMPL; }
    virtual HRESULT STDMETHODCALLTYPE Clone(IStream **) { return E_NOTIMPL; }
    long m_nRefCnt;
    FILE* m_fp;
};

static int extendStride(int stride)
{
    const int STRIDE_UNIT_BYTE = 4;
    return (((stride + STRIDE_UNIT_BYTE - 1) / STRIDE_UNIT_BYTE) * STRIDE_UNIT_BYTE);
}

void main(void)
{
    int width = 300;
    int height = 300;
    int gdiStride = extendStride(width * 3);  // RGB用
    // 画像データ作成(RGB) 動作確認用
    byte* pixels = (byte*)malloc(gdiStride * height);
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            int i = (x * 3) + (y * gdiStride);
            pixels[i+0] = (byte)(x & 0xff);                 // B
            pixels[i+1] = (byte)(y & 0xff);                 // G
            pixels[i+2] = (byte)((0x80 + x + y) & 0xff);    // R
        }
    }

    IStream* pStdoutStream;
    FilePointerStream::CreateInstance( stdout, &pStdoutStream);
    // jpeg保存
    {
        // GDI+初期化
        Gdiplus::GdiplusStartupInput input;
        ULONG_PTR token;
        Gdiplus::GdiplusStartup(&token, &input, NULL);

        CLSID id;
        // CLSIDの取得
        {
            UINT num = 0;
            UINT size = 0;
            Gdiplus::GetImageEncodersSize(&num, &size);
            Gdiplus::ImageCodecInfo *pImageCodecInfo = new Gdiplus::ImageCodecInfo[size];
            Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);
            for (UINT j = 0; j < num; ++j) {
                if (wcscmp(pImageCodecInfo[j].MimeType, L"image/jpeg") ==0) {
                    id = pImageCodecInfo[j].Clsid;
                    break;
                }
            }
            delete [] pImageCodecInfo;
        }    
        // 保存処理
        {
            Gdiplus::Bitmap image(width, height, gdiStride, PixelFormat24bppRGB, pixels);
            image.Save(pStdoutStream, &id);
            pStdoutStream->Release();
        }
        // GDI+解放
        Gdiplus::GdiplusShutdown(token);
    }
    free(pixels);
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?