#include "cru/win/graphics/direct/ImageFactory.h" #include "cru/common/platform/win/Exception.h" #include "cru/common/platform/win/StreamConvert.h" #include "cru/platform/Check.h" #include "cru/win/graphics/direct/Exception.h" #include "cru/win/graphics/direct/Factory.h" #include "cru/win/graphics/direct/Image.h" #include #include namespace cru::platform::graphics::win::direct { WinImageFactory::WinImageFactory(DirectGraphicsFactory* graphics_factory) : DirectGraphicsResource(graphics_factory) { HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&wic_imaging_factory_)); ThrowIfFailed(hr); } WinImageFactory::~WinImageFactory() {} std::unique_ptr WinImageFactory::DecodeFromStream(io::Stream* stream) { auto graphics_factory = GetDirectFactory(); HRESULT hr; Microsoft::WRL::ComPtr com_stream( platform::win::ConvertStreamToComStream(stream)); Microsoft::WRL::ComPtr wic_bitmap_decoder; hr = wic_imaging_factory_->CreateDecoderFromStream( com_stream.Get(), NULL, WICDecodeMetadataCacheOnDemand, &wic_bitmap_decoder); ThrowIfFailed(hr); Microsoft::WRL::ComPtr wic_bitmap_frame_decode; hr = wic_bitmap_decoder->GetFrame(0, &wic_bitmap_frame_decode); ThrowIfFailed(hr); auto d2d_context = graphics_factory->GetDefaultD2D1DeviceContext(); Microsoft::WRL::ComPtr d2d_image; d2d_context->CreateBitmapFromWicBitmap( wic_bitmap_frame_decode.Get(), D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)), &d2d_image); return std::make_unique(graphics_factory, std::move(d2d_image)); } GUID ConvertImageFormatToGUID(ImageFormat format) { GUID format_guid; switch (format) { case ImageFormat::Png: format_guid = GUID_ContainerFormatPng; break; case ImageFormat::Jpeg: format_guid = GUID_ContainerFormatJpeg; break; case ImageFormat::Gif: format_guid = GUID_ContainerFormatGif; break; default: throw Exception(u"Unknown image format"); } return format_guid; } void WinImageFactory::EncodeToStream(IImage* image, io::Stream* stream, ImageFormat format, float quality) { auto direct_image = CheckPlatform(image, GetPlatformId()); Microsoft::WRL::ComPtr com_stream( platform::win::ConvertStreamToComStream(stream)); auto d2d_bitmap = direct_image->GetD2DBitmap(); auto size = d2d_bitmap->GetSize(); FLOAT dpi_x, dpi_y; d2d_bitmap->GetDpi(&dpi_x, &dpi_y); auto pixel_format = d2d_bitmap->GetPixelFormat(); Ensures(pixel_format.format == DXGI_FORMAT_B8G8R8A8_UNORM); Ensures(pixel_format.alphaMode == D2D1_ALPHA_MODE_PREMULTIPLIED); D2D1_MAPPED_RECT mapped_rect; ThrowIfFailed(d2d_bitmap->Map(D2D1_MAP_OPTIONS_READ, &mapped_rect)); Microsoft::WRL::ComPtr wic_bitmap; ThrowIfFailed(wic_imaging_factory_->CreateBitmapFromMemory( size.width, size.height, GUID_WICPixelFormat32bppPBGRA, mapped_rect.pitch, mapped_rect.pitch * size.height, mapped_rect.bits, &wic_bitmap)); ThrowIfFailed(d2d_bitmap->Unmap()); Microsoft::WRL::ComPtr wic_bitmap_encoder; ThrowIfFailed(wic_imaging_factory_->CreateEncoder( ConvertImageFormatToGUID(format), nullptr, &wic_bitmap_encoder)); ThrowIfFailed(wic_bitmap_encoder->Initialize(com_stream.Get(), WICBitmapEncoderNoCache)); Microsoft::WRL::ComPtr wic_bitmap_frame_encode; Microsoft::WRL::ComPtr property_bag; ThrowIfFailed(wic_bitmap_encoder->CreateNewFrame(&wic_bitmap_frame_encode, &property_bag)); if (format == ImageFormat::Jpeg) { PROPBAG2 option = {0}; option.pstrName = const_cast(L"ImageQuality"); VARIANT varValue; VariantInit(&varValue); varValue.vt = VT_R4; varValue.fltVal = quality; ThrowIfFailed(property_bag->Write(1, &option, &varValue)); } ThrowIfFailed(wic_bitmap_frame_encode->Initialize(property_bag.Get())); ThrowIfFailed(wic_bitmap_frame_encode->SetResolution(dpi_x, dpi_y)); ThrowIfFailed(wic_bitmap_frame_encode->WriteSource(wic_bitmap.Get(), NULL)); ThrowIfFailed(wic_bitmap_frame_encode->Commit()); ThrowIfFailed(wic_bitmap_encoder->Commit()); } std::unique_ptr WinImageFactory::CreateBitmap(int width, int height) { if (width <= 0) throw Exception(u"Bitmap width must be greater than 0."); if (height <= 0) throw Exception(u"Bitmap height must be greater than 0."); auto graphics_factory = GetDirectFactory(); Microsoft::WRL::ComPtr bitmap; auto d2d_context = graphics_factory->GetDefaultD2D1DeviceContext(); ThrowIfFailed(d2d_context->CreateBitmap( D2D1::SizeU(width, height), nullptr, 0, D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)), &bitmap)); return std::make_unique(graphics_factory, std::move(bitmap)); } } // namespace cru::platform::graphics::win::direct