pcsx-redux/third_party/clip/clip_win_wic.h
2022-02-14 21:54:37 -08:00

231 lines
5.5 KiB
C++

// Clip Library
// Copyright (c) 2020 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#pragma once
#include "clip.h"
#include <algorithm>
#include <vector>
#include <shlwapi.h>
#include <wincodec.h>
namespace clip {
namespace win {
// Successful calls to CoInitialize() (S_OK or S_FALSE) must match
// the calls to CoUninitialize().
// From: https://docs.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-couninitialize#remarks
struct coinit {
HRESULT hr;
coinit() {
hr = CoInitialize(nullptr);
}
~coinit() {
if (hr == S_OK || hr == S_FALSE)
CoUninitialize();
}
};
template<class T>
class comptr {
public:
comptr() : m_ptr(nullptr) { }
explicit comptr(T* ptr) : m_ptr(ptr) { }
comptr(const comptr&) = delete;
comptr& operator=(const comptr&) = delete;
~comptr() { reset(); }
T** operator&() { return &m_ptr; }
T* operator->() { return m_ptr; }
bool operator!() const { return !m_ptr; }
T* get() { return m_ptr; }
void reset() {
if (m_ptr) {
m_ptr->Release();
m_ptr = nullptr;
}
}
private:
T* m_ptr;
};
//////////////////////////////////////////////////////////////////////
// Encode the image as PNG format
bool write_png_on_stream(const image& img,
IStream* stream) {
const image_spec& spec = img.spec();
comptr<IWICBitmapEncoder> encoder;
HRESULT hr = CoCreateInstance(CLSID_WICPngEncoder,
nullptr, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&encoder));
if (FAILED(hr))
return false;
hr = encoder->Initialize(stream, WICBitmapEncoderNoCache);
if (FAILED(hr))
return false;
comptr<IWICBitmapFrameEncode> frame;
comptr<IPropertyBag2> options;
hr = encoder->CreateNewFrame(&frame, &options);
if (FAILED(hr))
return false;
hr = frame->Initialize(options.get());
if (FAILED(hr))
return false;
// PNG encoder (and decoder) only supports GUID_WICPixelFormat32bppBGRA for 32bpp.
// See: https://docs.microsoft.com/en-us/windows/win32/wic/-wic-codec-native-pixel-formats#png-native-codec
WICPixelFormatGUID pixelFormat = GUID_WICPixelFormat32bppBGRA;
hr = frame->SetPixelFormat(&pixelFormat);
if (FAILED(hr))
return false;
hr = frame->SetSize(spec.width, spec.height);
if (FAILED(hr))
return false;
std::vector<uint32_t> buf;
uint8_t* ptr = (uint8_t*)img.data();
int bytes_per_row = spec.bytes_per_row;
image converted;
// Convert to GUID_WICPixelFormat32bppBGRA if needed
if (!img.is_bgra8888()) {
converted = img.to_bgra8888();
ptr = (uint8_t*)img.data();
bytes_per_row = 4 * spec.width;
}
hr = frame->WritePixels(spec.height,
bytes_per_row,
bytes_per_row * spec.height,
(BYTE*)ptr);
if (FAILED(hr))
return false;
hr = frame->Commit();
if (FAILED(hr))
return false;
hr = encoder->Commit();
if (FAILED(hr))
return false;
return true;
}
HGLOBAL write_png(const image& image) {
coinit com;
comptr<IStream> stream;
HRESULT hr = CreateStreamOnHGlobal(nullptr, false, &stream);
if (FAILED(hr))
return nullptr;
bool result = write_png_on_stream(image, stream.get());
HGLOBAL handle;
hr = GetHGlobalFromStream(stream.get(), &handle);
if (result)
return handle;
GlobalFree(handle);
return nullptr;
}
//////////////////////////////////////////////////////////////////////
// Decode the clipboard data from PNG format
bool read_png(const uint8_t* buf,
const UINT len,
image* output_image,
image_spec* output_spec) {
coinit com;
comptr<IStream> stream(SHCreateMemStream(buf, len));
if (!stream)
return false;
comptr<IWICBitmapDecoder> decoder;
HRESULT hr = CoCreateInstance(CLSID_WICPngDecoder2,
nullptr, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&decoder));
if (FAILED(hr)) {
hr = CoCreateInstance(CLSID_WICPngDecoder1,
nullptr, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&decoder));
if (FAILED(hr))
return false;
}
hr = decoder->Initialize(stream.get(), WICDecodeMetadataCacheOnDemand);
if (FAILED(hr))
return false;
comptr<IWICBitmapFrameDecode> frame;
hr = decoder->GetFrame(0, &frame);
if (FAILED(hr))
return false;
WICPixelFormatGUID pixelFormat;
hr = frame->GetPixelFormat(&pixelFormat);
if (FAILED(hr))
return false;
// Only support this pixel format
// TODO add support for more pixel formats
if (pixelFormat != GUID_WICPixelFormat32bppBGRA)
return false;
UINT width = 0, height = 0;
hr = frame->GetSize(&width, &height);
if (FAILED(hr))
return false;
image_spec spec;
spec.width = width;
spec.height = height;
spec.bits_per_pixel = 32;
spec.bytes_per_row = 4 * width;
spec.red_mask = 0xff0000;
spec.green_mask = 0xff00;
spec.blue_mask = 0xff;
spec.alpha_mask = 0xff000000;
spec.red_shift = 16;
spec.green_shift = 8;
spec.blue_shift = 0;
spec.alpha_shift = 24;
if (output_spec)
*output_spec = spec;
if (output_image) {
image img(spec);
hr = frame->CopyPixels(
nullptr, // Entire bitmap
spec.bytes_per_row,
spec.bytes_per_row * spec.height,
(BYTE*)img.data());
if (FAILED(hr)) {
return false;
}
std::swap(*output_image, img);
}
return true;
}
} // namespace win
} // namespace clip