// Windows Imaging Component: the codecs Windows has, and those the Store extensions add (HEIF and // AVIF, WebP, Raw Image). Only the few COM methods used are named; the rest of each vtable is padding. // The first frame of the image in bytes, as non-premultiplied RGBA (default allocator). wic_decode :: (bytes: [] u8) -> Image, ok: bool, error: string { image: Image; if bytes.count > U32_MAX return image, false, "it is too large for WIC"; // Any apartment will do for WIC; a thread already in one keeps it. initialized := CoInitializeEx(null, COINIT_MULTITHREADED); defer if initialized == S_OK || initialized == S_FALSE CoUninitialize(); factory: *IWICImagingFactory; hr := CoCreateInstance(*CLSID_WICImagingFactory, null, CLSCTX_INPROC_SERVER, *IID_IWICImagingFactory, xx *factory); if failed(hr) return image, false, hr_text("WIC could not start", hr); defer IUnknown_Release(factory); stream: *IWICStream; hr = factory.CreateStream(factory, *stream); if failed(hr) return image, false, hr_text("WIC could not make a stream", hr); defer IUnknown_Release(stream); hr = stream.InitializeFromMemory(stream, bytes.data, cast(u32) bytes.count); if failed(hr) return image, false, hr_text("WIC could not read the bytes", hr); decoder: *IWICBitmapDecoder; hr = factory.CreateDecoderFromStream(factory, stream, null, WICDecodeMetadataCacheOnDemand, *decoder); if failed(hr) { if hr == WINCODEC_ERR_COMPONENTNOTFOUND return image, false, "Windows has no codec for it (for HEIC, AVIF, WebP or RAW, install the HEIF, AV1, WebP or Raw Image Extension from the Microsoft Store)"; return image, false, hr_text("WIC cannot read it", hr); } defer IUnknown_Release(decoder); frame: *IWICBitmapFrameDecode; hr = decoder.GetFrame(decoder, 0, *frame); if failed(hr) return image, false, hr_text("WIC cannot read its first frame", hr); defer IUnknown_Release(frame); converter: *IWICFormatConverter; hr = factory.CreateFormatConverter(factory, *converter); if failed(hr) return image, false, hr_text("WIC could not make a converter", hr); defer IUnknown_Release(converter); hr = converter.Initialize(converter, frame, *GUID_WICPixelFormat32bppRGBA, WICBitmapDitherTypeNone, null, 0, WICBitmapPaletteTypeCustom); if failed(hr) return image, false, hr_text("WIC cannot convert it to RGBA", hr); w, h: u32; hr = converter.GetSize(converter, *w, *h); if failed(hr) || !w || !h return image, false, hr_text("WIC gives it no size", hr); if cast(u64) w * h * 4 > U32_MAX return image, false, tprint("it is % x %, too large to decode in one piece", w, h); size := cast(s64) w * h * 4; pixels := cast(*u8) alloc(size,, context.default_allocator); hr = converter.CopyPixels(converter, null, w * 4, cast(u32) size, pixels); if failed(hr) { free(pixels,, context.default_allocator); return image, false, hr_text("WIC could not decode it", hr); } image = .{pixels = pixels, width = cast(s32) w, height = cast(s32) h}; return image, true, ""; } #scope_file failed :: (hr: HRESULT) -> bool { return hr < 0; } hr_text :: (what: string, hr: HRESULT) -> string { return tprint("% (0x%)", what, formatInt(cast,no_check(u32) hr, base = 16)); } Ole32 :: #library,system "ole32"; CoInitializeEx :: (pvReserved: *void, dwCoInit: u32) -> HRESULT #foreign Ole32; CoUninitialize :: () #foreign Ole32; CoCreateInstance :: (rclsid: *GUID, pUnkOuter: *IUnknown, dwClsContext: u32, riid: *GUID, ppv: **void) -> HRESULT #foreign Ole32; COINIT_MULTITHREADED :: 0; CLSCTX_INPROC_SERVER :: 1; S_OK :HRESULT: 0; S_FALSE :HRESULT: 1; WINCODEC_ERR_COMPONENTNOTFOUND :HRESULT: cast,no_check(s32) 0x88982F50; WICDecodeMetadataCacheOnDemand :: 0; WICBitmapDitherTypeNone :: 0; WICBitmapPaletteTypeCustom :: 0; CLSID_WICImagingFactory := #run uid("CACAF262-9370-4615-A13B-9F5539DA4C0A"); IID_IWICImagingFactory := #run uid("EC5EC8A9-C395-4314-9C77-54D7A935FF70"); GUID_WICPixelFormat32bppRGBA := #run uid("F5C7AD2D-6A8D-43DD-A7A8-A29935261AE9"); IWICImagingFactory :: struct { #as unknown: IUnknown; #overlay (unknown.vtable) using vtable: *IWICImagingFactory_VTable; } IWICImagingFactory_VTable :: struct { #as using base: IUnknown_VTable; CreateDecoderFromFilename: *void; CreateDecoderFromStream: (this: *IWICImagingFactory, stream: *IWICStream, vendor: *GUID, options: u32, decoder: **IWICBitmapDecoder) -> HRESULT #cpp_method; CreateDecoderFromFileHandle: *void; CreateComponentInfo: *void; CreateDecoder: *void; CreateEncoder: *void; CreatePalette: *void; CreateFormatConverter: (this: *IWICImagingFactory, converter: **IWICFormatConverter) -> HRESULT #cpp_method; CreateBitmapScaler: *void; CreateBitmapClipper: *void; CreateBitmapFlipRotator: *void; CreateStream: (this: *IWICImagingFactory, stream: **IWICStream) -> HRESULT #cpp_method; } IWICStream :: struct { #as unknown: IUnknown; #overlay (unknown.vtable) using vtable: *IWICStream_VTable; } IWICStream_VTable :: struct { #as using base: IUnknown_VTable; Read, Write: *void; // ISequentialStream Seek, SetSize, CopyTo, Commit, Revert, LockRegion, UnlockRegion, Stat, Clone: *void; // IStream InitializeFromIStream: *void; InitializeFromFilename: *void; InitializeFromMemory: (this: *IWICStream, buffer: *u8, size: u32) -> HRESULT #cpp_method; } IWICBitmapDecoder :: struct { #as unknown: IUnknown; #overlay (unknown.vtable) using vtable: *IWICBitmapDecoder_VTable; } IWICBitmapDecoder_VTable :: struct { #as using base: IUnknown_VTable; QueryCapability, Initialize, GetContainerFormat, GetDecoderInfo, CopyPalette, GetMetadataQueryReader, GetPreview, GetColorContexts, GetThumbnail, GetFrameCount: *void; GetFrame: (this: *IWICBitmapDecoder, index: u32, frame: **IWICBitmapFrameDecode) -> HRESULT #cpp_method; } // Only ever passed on, to the converter. IWICBitmapFrameDecode :: struct { #as unknown: IUnknown; } IWICFormatConverter :: struct { #as unknown: IUnknown; #overlay (unknown.vtable) using vtable: *IWICFormatConverter_VTable; } IWICFormatConverter_VTable :: struct { #as using base: IUnknown_VTable; // IWICBitmapSource GetSize: (this: *IWICFormatConverter, width: *u32, height: *u32) -> HRESULT #cpp_method; GetPixelFormat: *void; GetResolution: *void; CopyPalette: *void; CopyPixels: (this: *IWICFormatConverter, rect: *void, stride: u32, size: u32, buffer: *u8) -> HRESULT #cpp_method; // IWICFormatConverter Initialize: (this: *IWICFormatConverter, source: *IWICBitmapFrameDecode, format: *GUID, dither: u32, palette: *void, alpha_threshold: float64, palette_type: u32) -> HRESULT #cpp_method; CanConvert: *void; }