├── .gitignore ├── .gitmodules ├── FlifWICCodec.sln ├── FlifWICCodec ├── FlifWICCodec.cpp ├── FlifWICCodec.vcxproj ├── FlifWICCodec.vcxproj.filters ├── autoreg.inf ├── decode_container.cpp ├── decode_container.h ├── decode_frame.cpp ├── decode_frame.h ├── dllmain.cpp ├── dllmain.h ├── encode_container.cpp ├── encode_container.h ├── encode_frame.cpp ├── encode_frame.h ├── flif_wic_codec.def ├── flif_wic_codec.rc ├── metadata_store.cpp ├── metadata_store.h ├── pixel_converter.cpp ├── pixel_converter.h ├── resids.h ├── stopwatch.h ├── targetver.h ├── utils.h ├── uuid.h └── version.h ├── README.md └── libflif ├── libflif.vcxproj └── libflif.vcxproj.filters /.gitignore: -------------------------------------------------------------------------------- 1 | *.opendb 2 | *.db 3 | /Release 4 | /x64 5 | /FlifWICCodec/Debug 6 | /FlifWICCodec/Release 7 | /FlifWICCodec/x64 8 | /Debug 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "FLIF"] 2 | path = FLIF 3 | url=https://github.com/FLIF-hub/FLIF.git -------------------------------------------------------------------------------- /FlifWICCodec.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FlifWICCodec", "FlifWICCodec\FlifWICCodec.vcxproj", "{7BC431E9-6C4B-4E03-97B6-2DBF1AD4A3D1}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libflif", "libflif\libflif.vcxproj", "{56871ADF-ABF5-414B-8872-BCF0E200B95D}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5ED12E51-C7C7-4486-ABB2-4808F9A4EA94}" 11 | ProjectSection(SolutionItems) = preProject 12 | README.md = README.md 13 | EndProjectSection 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|x64 = Debug|x64 18 | Debug|x86 = Debug|x86 19 | Release|x64 = Release|x64 20 | Release|x86 = Release|x86 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {7BC431E9-6C4B-4E03-97B6-2DBF1AD4A3D1}.Debug|x64.ActiveCfg = Debug|x64 24 | {7BC431E9-6C4B-4E03-97B6-2DBF1AD4A3D1}.Debug|x64.Build.0 = Debug|x64 25 | {7BC431E9-6C4B-4E03-97B6-2DBF1AD4A3D1}.Debug|x86.ActiveCfg = Debug|Win32 26 | {7BC431E9-6C4B-4E03-97B6-2DBF1AD4A3D1}.Debug|x86.Build.0 = Debug|Win32 27 | {7BC431E9-6C4B-4E03-97B6-2DBF1AD4A3D1}.Release|x64.ActiveCfg = Release|x64 28 | {7BC431E9-6C4B-4E03-97B6-2DBF1AD4A3D1}.Release|x64.Build.0 = Release|x64 29 | {7BC431E9-6C4B-4E03-97B6-2DBF1AD4A3D1}.Release|x86.ActiveCfg = Release|Win32 30 | {7BC431E9-6C4B-4E03-97B6-2DBF1AD4A3D1}.Release|x86.Build.0 = Release|Win32 31 | {56871ADF-ABF5-414B-8872-BCF0E200B95D}.Debug|x64.ActiveCfg = Debug|x64 32 | {56871ADF-ABF5-414B-8872-BCF0E200B95D}.Debug|x64.Build.0 = Debug|x64 33 | {56871ADF-ABF5-414B-8872-BCF0E200B95D}.Debug|x86.ActiveCfg = Debug|Win32 34 | {56871ADF-ABF5-414B-8872-BCF0E200B95D}.Debug|x86.Build.0 = Debug|Win32 35 | {56871ADF-ABF5-414B-8872-BCF0E200B95D}.Release|x64.ActiveCfg = Release|x64 36 | {56871ADF-ABF5-414B-8872-BCF0E200B95D}.Release|x64.Build.0 = Release|x64 37 | {56871ADF-ABF5-414B-8872-BCF0E200B95D}.Release|x86.ActiveCfg = Release|Win32 38 | {56871ADF-ABF5-414B-8872-BCF0E200B95D}.Release|x86.Build.0 = Release|Win32 39 | EndGlobalSection 40 | GlobalSection(SolutionProperties) = preSolution 41 | HideSolutionNode = FALSE 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /FlifWICCodec/FlifWICCodec.cpp: -------------------------------------------------------------------------------- 1 | // FlifWICCodec.cpp : Defines the exported functions for the DLL application. 2 | // 3 | 4 | #include "stdafx.h" 5 | 6 | 7 | -------------------------------------------------------------------------------- /FlifWICCodec/FlifWICCodec.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {7BC431E9-6C4B-4E03-97B6-2DBF1AD4A3D1} 23 | Win32Proj 24 | FlifWICCodec 25 | 8.1 26 | 27 | 28 | 29 | DynamicLibrary 30 | true 31 | v140 32 | Unicode 33 | 34 | 35 | DynamicLibrary 36 | false 37 | v140 38 | true 39 | Unicode 40 | 41 | 42 | DynamicLibrary 43 | true 44 | v140 45 | Unicode 46 | 47 | 48 | DynamicLibrary 49 | false 50 | v140 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | $(SolutionDir)FLIF\src\library;$(VC_IncludePath);$(WindowsSDK_IncludePath) 75 | 76 | 77 | true 78 | $(SolutionDir)FLIF\src\library;$(VC_IncludePath);$(WindowsSDK_IncludePath) 79 | 80 | 81 | false 82 | $(SolutionDir)FLIF\src\library;$(VC_IncludePath);$(WindowsSDK_IncludePath) 83 | 84 | 85 | false 86 | $(SolutionDir)FLIF\src\library;$(VC_IncludePath);$(WindowsSDK_IncludePath) 87 | 88 | 89 | 90 | NotUsing 91 | Level3 92 | Disabled 93 | FLIF_USE_STB_IMAGE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX;WIN32;_DEBUG;_WINDOWS;_USRDLL;FLIFWICCODEC_EXPORTS;%(PreprocessorDefinitions) 94 | MultiThreadedDebug 95 | Fast 96 | 97 | 98 | Windows 99 | true 100 | flif_wic_codec.def 101 | shlwapi.lib;windowscodecs.lib;ole32.lib;OleAut32.lib;Propsys.lib 102 | 103 | 104 | true 105 | 106 | 107 | 108 | 109 | NotUsing 110 | Level3 111 | Disabled 112 | FLIF_USE_STB_IMAGE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX;_DEBUG;_WINDOWS;_USRDLL;FLIFWICCODEC_EXPORTS;%(PreprocessorDefinitions) 113 | MultiThreadedDebug 114 | Fast 115 | 116 | 117 | Windows 118 | true 119 | flif_wic_codec.def 120 | shlwapi.lib;windowscodecs.lib;ole32.lib;OleAut32.lib;Propsys.lib 121 | 122 | 123 | true 124 | 125 | 126 | 127 | 128 | Level3 129 | NotUsing 130 | Full 131 | true 132 | true 133 | FLIF_USE_STB_IMAGE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX;WIN32;NDEBUG;_WINDOWS;_USRDLL;FLIFWICCODEC_EXPORTS;%(PreprocessorDefinitions) 134 | MultiThreaded 135 | true 136 | true 137 | true 138 | Fast 139 | false 140 | 141 | 142 | Windows 143 | true 144 | true 145 | true 146 | flif_wic_codec.def 147 | shlwapi.lib;windowscodecs.lib;ole32.lib;OleAut32.lib;Propsys.lib 148 | UseLinkTimeCodeGeneration 149 | 150 | 151 | true 152 | 153 | 154 | 155 | 156 | Level3 157 | NotUsing 158 | Full 159 | true 160 | true 161 | FLIF_USE_STB_IMAGE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX;NDEBUG;_WINDOWS;_USRDLL;FLIFWICCODEC_EXPORTS;%(PreprocessorDefinitions) 162 | MultiThreaded 163 | true 164 | true 165 | Fast 166 | true 167 | false 168 | 169 | 170 | Windows 171 | true 172 | true 173 | true 174 | flif_wic_codec.def 175 | shlwapi.lib;windowscodecs.lib;ole32.lib;OleAut32.lib;Propsys.lib 176 | UseLinkTimeCodeGeneration 177 | 178 | 179 | true 180 | 181 | 182 | 183 | 184 | 185 | 186 | false 187 | 188 | 189 | false 190 | 191 | 192 | false 193 | 194 | 195 | false 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | Document 232 | 233 | 234 | 235 | 236 | 237 | {56871adf-abf5-414b-8872-bcf0e200b95d} 238 | 239 | 240 | 241 | 242 | 243 | -------------------------------------------------------------------------------- /FlifWICCodec/FlifWICCodec.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | 41 | 42 | Resource Files 43 | 44 | 45 | 46 | 47 | Header Files 48 | 49 | 50 | Header Files 51 | 52 | 53 | Header Files 54 | 55 | 56 | Header Files 57 | 58 | 59 | Header Files 60 | 61 | 62 | Header Files 63 | 64 | 65 | Header Files 66 | 67 | 68 | Header Files 69 | 70 | 71 | Header Files 72 | 73 | 74 | Header Files 75 | 76 | 77 | Header Files 78 | 79 | 80 | Header Files 81 | 82 | 83 | 84 | 85 | Source Files 86 | 87 | 88 | Source Files 89 | 90 | 91 | -------------------------------------------------------------------------------- /FlifWICCodec/autoreg.inf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peirick/FlifWICCodec/e42164e90ec300ae7396b6f06365ae0d7dcb651b/FlifWICCodec/autoreg.inf -------------------------------------------------------------------------------- /FlifWICCodec/decode_container.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "dllmain.h" 3 | #include "uuid.h" 4 | #include "utils.h" 5 | #include "decode_container.h" 6 | #include "decode_frame.h" 7 | 8 | DecodeContainer::DecodeContainer() 9 | : has_been_decoded_(false), 10 | has_only_infos_decoded_(false), 11 | last_decode_desult(0), 12 | info_(nullptr), 13 | decoder_(nullptr) 14 | { 15 | TRACE("()\n"); 16 | InitializeCriticalSection(&cs_); 17 | } 18 | 19 | DecodeContainer::~DecodeContainer() 20 | { 21 | TRACE("()\n"); 22 | if (info_) { 23 | flif_destroy_info(info_); 24 | info_ = nullptr; 25 | } 26 | if (decoder_) { 27 | flif_destroy_decoder(decoder_); 28 | decoder_ = nullptr; 29 | } 30 | } 31 | 32 | HRESULT DecodeContainer::QueryInterface(REFIID riid, void** ppvObject) { 33 | TRACE2("(%s, %p)\n", debugstr_guid(riid), ppvObject); 34 | 35 | if (ppvObject == nullptr) 36 | return E_INVALIDARG; 37 | *ppvObject = nullptr; 38 | 39 | if (!IsEqualGUID(riid, IID_IUnknown) && 40 | !IsEqualGUID(riid, IID_IWICBitmapDecoder)) 41 | return E_NOINTERFACE; 42 | this->AddRef(); 43 | *ppvObject = static_cast(this); 44 | return S_OK; 45 | } 46 | 47 | HRESULT DecodeContainer::ReadInfo(IStream* pIStream) 48 | { 49 | const int header_size_bytes = 100; 50 | uint8_t header[header_size_bytes]; 51 | HRESULT ret; 52 | ULONG read; 53 | 54 | if (FAILED(ret = pIStream->Read(header, sizeof(header), &read))) 55 | return ret; 56 | 57 | if (info_) { 58 | flif_destroy_info(info_); 59 | info_ = nullptr; 60 | } 61 | info_ = flif_read_info_from_memory(header, read); 62 | if (!info_) { 63 | return WINCODEC_ERR_BADHEADER; 64 | } 65 | 66 | // reset pIStream 67 | LARGE_INTEGER zero; 68 | zero.QuadPart = 0; 69 | pIStream->Seek(zero, STREAM_SEEK_SET, nullptr); 70 | return S_OK; 71 | } 72 | 73 | HRESULT DecodeContainer::QueryCapability(IStream* pIStream, DWORD* pdwCapability) { 74 | TRACE2("(%p, %x)\n", pIStream, pdwCapability); 75 | if (pdwCapability == nullptr) 76 | return E_INVALIDARG; 77 | 78 | HRESULT ret = ReadInfo(pIStream); 79 | if (ret == WINCODEC_ERR_BADHEADER) 80 | return WINCODEC_ERR_WRONGSTATE; // That's what Win7 jpeg codec returns. 81 | 82 | if (ret == S_OK) 83 | *pdwCapability = 84 | WICBitmapDecoderCapabilityCanDecodeSomeImages 85 | | WICBitmapDecoderCapabilityCanDecodeAllImages 86 | //| WICBitmapDecoderCapabilityCanDecodeThumbnail 87 | | WICBitmapDecoderCapabilityCanEnumerateMetadata; 88 | return ret; 89 | } 90 | 91 | HRESULT DecodeContainer::Initialize(IStream* pIStream, WICDecodeOptions cacheOptions) { 92 | TRACE2("(%p, %x)\n", pIStream, cacheOptions); 93 | 94 | if (pIStream == nullptr) 95 | return E_INVALIDARG; 96 | 97 | SectionLock l(&cs_); 98 | 99 | HRESULT ret; 100 | ret = ReadInfo(pIStream); 101 | if (FAILED(ret)) { 102 | return ret; 103 | } 104 | 105 | if (has_been_decoded_) { 106 | has_been_decoded_ = false; 107 | frames_.clear(); 108 | } 109 | // Save stream for later use 110 | return pIStream->QueryInterface(stream_.get_out_storage()); 111 | } 112 | 113 | HRESULT DecodeContainer::DecodeCached(bool decode_only_infos) 114 | { 115 | SectionLock l(&cs_); 116 | if (!has_been_decoded_ 117 | || (decode_only_infos == false && has_only_infos_decoded_)) 118 | { 119 | last_decode_desult = Decode(decode_only_infos); 120 | has_been_decoded_ = true; 121 | has_only_infos_decoded_ = decode_only_infos; 122 | } 123 | return last_decode_desult; 124 | } 125 | 126 | HRESULT DecodeContainer::Decode(bool onlyInfos) 127 | { 128 | if (stream_.get() == nullptr) 129 | return WINCODEC_ERR_NOTINITIALIZED; 130 | 131 | HRESULT ret; 132 | STATSTG stats; 133 | ret = stream_->Stat(&stats, STATFLAG_NONAME); 134 | if (FAILED(ret)) 135 | return ret; 136 | ULONG stream_size = stats.cbSize.QuadPart; 137 | TRACE1("stream_size %d\n", stream_size); 138 | ULONG bytes_read; 139 | scoped_buffer file_data(stream_size); 140 | if (file_data.get() == nullptr) 141 | return WINCODEC_ERR_OUTOFMEMORY; 142 | 143 | LARGE_INTEGER zero; 144 | zero.QuadPart = 0; 145 | ret = stream_->Seek(zero, STREAM_SEEK_SET, nullptr); 146 | if (FAILED(ret)) 147 | return ret; 148 | 149 | ret = stream_->Read(file_data.get(), stream_size, &bytes_read); 150 | TRACE1("bytes_read %d\n", bytes_read); 151 | if (FAILED(ret)) { 152 | TRACE("pIStream->Read failed\n"); 153 | return ret; 154 | } 155 | 156 | if (bytes_read != stream_size) { 157 | return WINCODEC_ERR_WRONGSTATE; 158 | } 159 | 160 | if (!decoder_) { 161 | decoder_ = flif_create_decoder(); 162 | } 163 | 164 | if (onlyInfos) { 165 | flif_decoder_set_scale(decoder_, -2); 166 | } 167 | else { 168 | flif_decoder_set_scale(decoder_, 1); 169 | } 170 | 171 | if (flif_decoder_decode_memory(decoder_, file_data.get(), bytes_read) != 0) 172 | { 173 | size_t num_images = flif_decoder_num_images(decoder_); 174 | frames_.resize(num_images); 175 | for (int i = 0; i < num_images; ++i) { 176 | FLIF_IMAGE* image = flif_decoder_get_image(decoder_, i); 177 | if (image) 178 | { 179 | if (frames_[i].get() == nullptr) { 180 | frames_[i].reset(new (std::nothrow) DecodeFrame()); 181 | if (frames_[i].get() == nullptr) 182 | return E_OUTOFMEMORY; 183 | } 184 | frames_[i]->SetFlifImage(image, num_images); 185 | } 186 | else { 187 | frames_[i].reset(nullptr); 188 | } 189 | } 190 | } 191 | else { 192 | TRACE("decode failed\n"); 193 | ret = WINCODEC_ERR_WRONGSTATE; 194 | } 195 | if (FAILED(ret)) 196 | return ret; 197 | return S_OK; 198 | } 199 | 200 | HRESULT DecodeContainer::GetContainerFormat(GUID* pguidContainerFormat) { 201 | TRACE1("(%p)\n", pguidContainerFormat); 202 | if (pguidContainerFormat == nullptr) 203 | return E_INVALIDARG; 204 | *pguidContainerFormat = GUID_ContainerFormatFLIF; 205 | return S_OK; 206 | } 207 | 208 | HRESULT DecodeContainer::InitializeFactory() 209 | { 210 | SectionLock l(&cs_); 211 | HRESULT result = S_OK; 212 | if (factory_.get() == nullptr) { 213 | result = CoCreateInstance(CLSID_WICImagingFactory, 214 | nullptr, 215 | CLSCTX_INPROC_SERVER, 216 | IID_IWICImagingFactory, 217 | (LPVOID*)factory_.get_out_storage()); 218 | } 219 | return result; 220 | } 221 | 222 | HRESULT DecodeContainer::GetDecoderInfo(IWICBitmapDecoderInfo** ppIDecoderInfo) { 223 | TRACE1("(%p)\n", ppIDecoderInfo); 224 | HRESULT result; 225 | 226 | result = InitializeFactory(); 227 | if (FAILED(result)) 228 | return result; 229 | 230 | ComPtr compInfo; 231 | result = factory_->CreateComponentInfo(CLSID_FLIFWICDecoder, compInfo.get_out_storage()); 232 | if (FAILED(result)) 233 | return result; 234 | 235 | result = compInfo->QueryInterface(IID_IWICBitmapDecoderInfo, (void**)ppIDecoderInfo); 236 | if (FAILED(result)) 237 | return result; 238 | 239 | return S_OK; 240 | } 241 | 242 | HRESULT DecodeContainer::CopyPalette(IWICPalette* pIPalette) { 243 | TRACE1("(%p)\n", pIPalette); 244 | return WINCODEC_ERR_PALETTEUNAVAILABLE; 245 | } 246 | 247 | HRESULT DecodeContainer::GetMetadataQueryReader(IWICMetadataQueryReader** ppIMetadataQueryReader) { 248 | TRACE1("(%p)\n", ppIMetadataQueryReader); 249 | if (ppIMetadataQueryReader == nullptr) 250 | return E_INVALIDARG; 251 | 252 | HRESULT result = DecodeCached(true); 253 | if (FAILED(result)) 254 | return result; 255 | 256 | return frames_[0].get()->GetMetadataQueryReader(ppIMetadataQueryReader); 257 | } 258 | 259 | HRESULT DecodeContainer::GetPreview(IWICBitmapSource** ppIBitmapSource) { 260 | TRACE1("(%p)\n", ppIBitmapSource); 261 | if (ppIBitmapSource == nullptr) 262 | return E_INVALIDARG; 263 | 264 | HRESULT result = DecodeCached(false); 265 | if (FAILED(result)) 266 | return result; 267 | 268 | *ppIBitmapSource = frames_[0].new_ref(); 269 | return S_OK; 270 | } 271 | 272 | HRESULT DecodeContainer::GetColorContexts(UINT cCount, IWICColorContext** ppIColorContexts, UINT* pcActualCount) { 273 | TRACE3("(%d, %p, %p)\n", cCount, ppIColorContexts, pcActualCount); 274 | return S_OK; 275 | } 276 | 277 | HRESULT DecodeContainer::GetThumbnail(IWICBitmapSource** ppIThumbnail) 278 | { 279 | TRACE1("(%p)\n", ppIThumbnail); 280 | if (ppIThumbnail == nullptr) 281 | return E_INVALIDARG; 282 | 283 | HRESULT result = DecodeCached(false); 284 | if (FAILED(result)) 285 | return result; 286 | 287 | *ppIThumbnail = frames_[0].new_ref(); 288 | return S_OK; 289 | } 290 | 291 | HRESULT DecodeContainer::GetFrameCount(UINT* pCount) { 292 | TRACE1("(%p)\n", pCount); 293 | if (pCount == nullptr) 294 | return E_INVALIDARG; 295 | 296 | if (info_ == nullptr) 297 | return WINCODEC_ERR_NOTINITIALIZED; 298 | 299 | *pCount = GetFrameCount(); 300 | return S_OK; 301 | } 302 | 303 | HRESULT DecodeContainer::GetFrame(UINT index, IWICBitmapFrameDecode** ppIBitmapFrame) { 304 | TRACE2("(%d, %p)\n", index, ppIBitmapFrame); 305 | if (ppIBitmapFrame == nullptr) 306 | return E_INVALIDARG; 307 | 308 | HRESULT result = DecodeCached(false); 309 | if (FAILED(result)) 310 | return result; 311 | 312 | SectionLock l(&cs_); 313 | *ppIBitmapFrame = frames_[index].new_ref(); 314 | return S_OK; 315 | } 316 | 317 | UINT DecodeContainer::GetWidth() const 318 | { 319 | return info_ ? flif_info_get_width(info_) : 0; 320 | } 321 | 322 | UINT DecodeContainer::GetHeight() const 323 | { 324 | return info_ ? flif_info_get_height(info_) : 0; 325 | } 326 | 327 | UINT DecodeContainer::GetBitDepth() const 328 | { 329 | return info_ ? flif_info_get_depth(info_) : 0; 330 | } 331 | 332 | UINT DecodeContainer::GetFrameCount() const 333 | { 334 | return info_ ? flif_info_num_images(info_) : 0; 335 | } 336 | 337 | 338 | 339 | -------------------------------------------------------------------------------- /FlifWICCodec/decode_container.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "decode_frame.h" 7 | #include "utils.h" 8 | 9 | class DecodeContainer : public ComObjectBase { 10 | public: 11 | DecodeContainer(); 12 | ~DecodeContainer(); 13 | // Inherited via IUnknown: 14 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override; 15 | ULONG STDMETHODCALLTYPE AddRef() override { return ComObjectBase::AddRef(); } 16 | ULONG STDMETHODCALLTYPE Release() override { return ComObjectBase::Release(); } 17 | // Inherited via IWICBitmapDecoder: 18 | HRESULT STDMETHODCALLTYPE QueryCapability(IStream *pIStream, DWORD *pdwCapability) override; 19 | HRESULT STDMETHODCALLTYPE Initialize(IStream *pIStream, WICDecodeOptions cacheOptions) override; 20 | HRESULT STDMETHODCALLTYPE GetContainerFormat(GUID *pguidContainerFormat) override; 21 | HRESULT STDMETHODCALLTYPE GetDecoderInfo(IWICBitmapDecoderInfo **ppIDecoderInfo) override; 22 | HRESULT STDMETHODCALLTYPE CopyPalette(IWICPalette *pIPalette)override; 23 | HRESULT STDMETHODCALLTYPE GetMetadataQueryReader(IWICMetadataQueryReader **ppIMetadataQueryReader)override; 24 | HRESULT STDMETHODCALLTYPE GetPreview(IWICBitmapSource **ppIBitmapSource)override; 25 | HRESULT STDMETHODCALLTYPE GetColorContexts(UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)override; 26 | HRESULT STDMETHODCALLTYPE GetThumbnail(IWICBitmapSource **ppIThumbnail)override; 27 | HRESULT STDMETHODCALLTYPE GetFrameCount(UINT *pCount)override; 28 | HRESULT STDMETHODCALLTYPE GetFrame(UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)override; 29 | public: 30 | UINT GetWidth() const; 31 | UINT GetHeight() const; 32 | UINT GetBitDepth() const; 33 | UINT GetFrameCount() const; 34 | private: 35 | // No copy and assign. 36 | DecodeContainer(const DecodeContainer&) = delete; 37 | void operator=(const DecodeContainer&) = delete; 38 | HRESULT InitializeFactory(); 39 | HRESULT ReadInfo(IStream * pIStream); 40 | HRESULT DecodeCached(bool onlyInfos); 41 | HRESULT Decode(bool onlyInfos); 42 | bool has_been_decoded_; 43 | bool has_only_infos_decoded_; 44 | HRESULT last_decode_desult; 45 | 46 | FLIF_INFO* info_; 47 | 48 | FLIF_DECODER* decoder_; 49 | ComPtr stream_; 50 | std::deque> frames_; 51 | ComPtr factory_; 52 | CRITICAL_SECTION cs_; 53 | }; 54 | -------------------------------------------------------------------------------- /FlifWICCodec/decode_frame.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "decode_frame.h" 7 | #include "dllmain.h" 8 | #include "uuid.h" 9 | 10 | #ifdef FLIF_DEBUG_LOGGING 11 | #include "stopwatch.h" 12 | #endif // FLIF_DEBUG_LOGGING 13 | 14 | 15 | 16 | DecodeFrame::DecodeFrame() 17 | : metadataBlockReader_(*this), 18 | image_(nullptr), 19 | totalNumberOfImages_(0) 20 | { 21 | TRACE("()\n"); 22 | InitializeCriticalSection(&cs_); 23 | } 24 | 25 | void DecodeFrame::SetFlifImage(FLIF_IMAGE * image, UINT totalNumberOfImages) 26 | { 27 | SectionLock l(&cs_); 28 | image_ = image; 29 | totalNumberOfImages_ = totalNumberOfImages; 30 | } 31 | 32 | DecodeFrame::~DecodeFrame() 33 | { 34 | TRACE("()\n"); 35 | DeleteCriticalSection(&cs_); 36 | } 37 | 38 | 39 | HRESULT DecodeFrame::QueryInterface(REFIID riid, void **ppvObject) { 40 | TRACE2("(%s, %p)\n", debugstr_guid(riid), ppvObject); 41 | 42 | if (ppvObject == nullptr) 43 | return E_INVALIDARG; 44 | *ppvObject = nullptr; 45 | 46 | if (IsEqualGUID(riid, IID_IUnknown) || 47 | IsEqualGUID(riid, IID_IWICBitmapFrameDecode) || 48 | IsEqualGUID(riid, IID_IWICBitmapSource)) 49 | { 50 | this->AddRef(); 51 | *ppvObject = static_cast(this); 52 | return S_OK; 53 | } 54 | 55 | if (IsEqualGUID(riid, IID_IWICMetadataBlockReader)) 56 | { 57 | this->AddRef(); 58 | *ppvObject = static_cast(&this->metadataBlockReader_); 59 | return S_OK; 60 | } 61 | 62 | return E_NOINTERFACE; 63 | } 64 | 65 | HRESULT DecodeFrame::GetSize(UINT *puiWidth, UINT *puiHeight) { 66 | TRACE2("(%p, %p)\n", puiWidth, puiHeight); 67 | if (puiWidth == nullptr || puiHeight == nullptr) 68 | return E_INVALIDARG; 69 | *puiWidth = GetWidth(); 70 | *puiHeight = GetHeight(); 71 | TRACE2("ret: %u x %u\n", *puiWidth, *puiHeight); 72 | return S_OK; 73 | } 74 | 75 | HRESULT DecodeFrame::GetPixelFormat(WICPixelFormatGUID *pPixelFormat) { 76 | TRACE1("(%p)\n", pPixelFormat); 77 | if (pPixelFormat == nullptr) 78 | return E_INVALIDARG; 79 | //if (nb_channels >= 4) { 80 | *pPixelFormat = GUID_WICPixelFormat32bppRGBA; 81 | return S_OK; 82 | } 83 | 84 | HRESULT DecodeFrame::GetResolution(double *pDpiX, double *pDpiY) { 85 | TRACE2("(%p, %p)\n", pDpiX, pDpiY); 86 | if (pDpiX == nullptr) 87 | return E_INVALIDARG; 88 | if (pDpiY == nullptr) 89 | return E_INVALIDARG; 90 | // Let's assume square pixels. 96dpi seems to be a reasonable default. 91 | *pDpiX = 96; 92 | *pDpiY = 96; 93 | return S_OK; 94 | } 95 | 96 | HRESULT DecodeFrame::CopyPalette(IWICPalette *pIPalette) { 97 | TRACE1("(%p)\n", pIPalette); 98 | return WINCODEC_ERR_PALETTEUNAVAILABLE; 99 | } 100 | 101 | HRESULT DecodeFrame::CopyPixels(const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer) { 102 | TRACE4("(%p, %u, %u, %p)\n", prc, cbStride, cbBufferSize, pbBuffer); 103 | if (pbBuffer == nullptr) 104 | return E_INVALIDARG; 105 | uint32_t image_width = GetWidth(); 106 | uint32_t image_height = GetHeight(); 107 | uint8_t nb_channels = 4; // flif_image_get_nb_channels(image_); 108 | 109 | WICRect rect = { 0, 0, image_width, image_height }; 110 | if (prc) 111 | rect = *prc; 112 | if (rect.Width < 0 || rect.Height < 0 || rect.X < 0 || rect.Y < 0) 113 | return E_INVALIDARG; 114 | if (rect.X + rect.Width > image_width || 115 | rect.Y + rect.Height > image_height) 116 | return E_INVALIDARG; 117 | 118 | // Divisions instead of multiplications to avoid integer overflows: 119 | if (cbStride / nb_channels < static_cast(rect.Width)) 120 | return E_INVALIDARG; 121 | if (cbBufferSize / cbStride < static_cast(rect.Height)) 122 | return WINCODEC_ERR_INSUFFICIENTBUFFER; 123 | 124 | if (rect.Width == 0 || rect.Height == 0) 125 | return S_OK; 126 | 127 | size_t buffer_size = image_width*nb_channels; 128 | scoped_buffer temp_row(buffer_size); 129 | if (temp_row.alloc_failed()) { 130 | return WINCODEC_ERR_OUTOFMEMORY; 131 | } 132 | 133 | BYTE* dst_buffer = pbBuffer; 134 | const int x_offset = rect.X * nb_channels; 135 | const int width = rect.Width * nb_channels; 136 | 137 | for (int src_y = rect.Y; src_y < rect.Y + rect.Height; ++src_y) { 138 | flif_image_read_row_RGBA8(image_, src_y, temp_row.get(), buffer_size); 139 | memcpy(dst_buffer, temp_row.get() + x_offset, width); 140 | dst_buffer += cbStride; 141 | } 142 | return S_OK; 143 | } 144 | 145 | HRESULT DecodeFrame::GetMetadataQueryReader(IWICMetadataQueryReader **ppIMetadataQueryReader) { 146 | TRACE1("(%p)\n", ppIMetadataQueryReader); 147 | if (ppIMetadataQueryReader == nullptr) 148 | return E_INVALIDARG; 149 | 150 | HRESULT result = S_OK; 151 | //Create factory 152 | result = InitializeFactory(); 153 | if (FAILED(result)) 154 | return result; 155 | 156 | return componentFactory_->CreateQueryReaderFromBlockReader(static_cast(&this->metadataBlockReader_), ppIMetadataQueryReader); 157 | } 158 | 159 | HRESULT DecodeFrame::GetColorContexts(UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount) { 160 | TRACE3("(%d, %p, %p)\n", cCount, ppIColorContexts, pcActualCount); 161 | if (pcActualCount == nullptr) 162 | return E_INVALIDARG; 163 | *pcActualCount = 0; 164 | return S_OK; 165 | } 166 | 167 | HRESULT DecodeFrame::GetThumbnail(IWICBitmapSource **ppIThumbnail) { 168 | TRACE1("(%p)\n", ppIThumbnail); 169 | return WINCODEC_ERR_CODECNOTHUMBNAIL; 170 | } 171 | 172 | 173 | 174 | HRESULT DecodeFrame::InitializeFactory() 175 | { 176 | SectionLock l(&cs_); 177 | HRESULT result = S_OK; 178 | if (factory_.get() == nullptr) 179 | { 180 | result = CoCreateInstance(CLSID_WICImagingFactory, 181 | nullptr, 182 | CLSCTX_INPROC_SERVER, 183 | IID_IWICImagingFactory, 184 | (LPVOID*)factory_.get_out_storage()); 185 | if (FAILED(result)) 186 | return result; 187 | } 188 | if (factory_.get() != nullptr && 189 | componentFactory_.get() == nullptr) 190 | { 191 | result = factory_->QueryInterface(componentFactory_.get_out_storage()); 192 | if (FAILED(result)) { 193 | return result; 194 | } 195 | } 196 | return result; 197 | } 198 | 199 | UINT DecodeFrame::GetWidth() const 200 | { 201 | return flif_image_get_width(image_); 202 | } 203 | 204 | UINT DecodeFrame::GetHeight() const 205 | { 206 | return flif_image_get_height(image_); 207 | } 208 | 209 | UINT DecodeFrame::GetDelay() const 210 | { 211 | return flif_image_get_frame_delay(image_); 212 | } 213 | 214 | HRESULT DecodeFrame::MetadataBlockReader::GetContainerFormat(GUID * pguidContainerFormat) 215 | { 216 | TRACE1("(%p)\n", pguidContainerFormat); 217 | if (pguidContainerFormat == nullptr) 218 | return E_INVALIDARG; 219 | //Return container format of Jpeg so that "Photo Metadata Policies" will work. 220 | //https://msdn.microsoft.com/en-us/library/windows/desktop/ee872003(v=vs.85).aspx 221 | *pguidContainerFormat = GUID_ContainerFormatJpeg; 222 | //*pguidContainerFormat = GUID_ContainerFormatFLIF; 223 | return S_OK; 224 | } 225 | 226 | HRESULT DecodeFrame::MetadataBlockReader::GetCount(UINT * pcCount) 227 | { 228 | TRACE1("(%p)\n", pcCount); 229 | if (pcCount == nullptr) 230 | return E_INVALIDARG; 231 | 232 | HRESULT result; 233 | 234 | //Create factory 235 | result = decodeFrame_.InitializeFactory(); 236 | if (FAILED(result)) 237 | return result; 238 | 239 | ReadAllMetadata(); 240 | *pcCount = metadataReader_.size(); 241 | return S_OK; 242 | } 243 | 244 | HRESULT DecodeFrame::MetadataBlockReader::GetReaderByIndex(UINT nIndex, IWICMetadataReader ** ppIMetadataReader) 245 | { 246 | TRACE2("(%d, %p)\n", nIndex, ppIMetadataReader); 247 | if (ppIMetadataReader == nullptr) 248 | return E_INVALIDARG; 249 | if (nIndex >= metadataReader_.size()) 250 | return E_INVALIDARG; 251 | 252 | *ppIMetadataReader = metadataReader_[nIndex].new_ref(); 253 | return S_OK; 254 | } 255 | 256 | HRESULT DecodeFrame::MetadataBlockReader::GetEnumerator(IEnumUnknown ** ppIEnumMetadata) 257 | { 258 | TRACE1("(%p)\n", ppIEnumMetadata); 259 | return E_NOTIMPL; 260 | } 261 | 262 | static 263 | inline HRESULT InitPropVariantFromUInt8(_In_ UCHAR uiVal, _Out_ PROPVARIANT *ppropvar) 264 | { 265 | ppropvar->vt = VT_UI1; 266 | ppropvar->bVal = uiVal; 267 | return S_OK; 268 | } 269 | 270 | void DecodeFrame::MetadataBlockReader::ReadAllMetadata() 271 | { 272 | TRACE("()\n"); 273 | ReadMetadata(GUID_MetadataFormatExif, "eXif"); 274 | ReadMetadata(GUID_MetadataFormatXMP, "eXmp"); 275 | ReadMetadata(GUID_MetadataFormatChunkiCCP, "iCCP"); 276 | 277 | if (decodeFrame_.totalNumberOfImages_ > 1) 278 | { 279 | ComPtr writer; 280 | 281 | if (SUCCEEDED(decodeFrame_.componentFactory_->CreateMetadataWriter( 282 | GUID_MetadataFormatIMD, 283 | nullptr, 284 | WICPersistOptionDefault, 285 | writer.get_out_storage()))) 286 | { 287 | PROPVARIANT key; 288 | PROPVARIANT value; 289 | 290 | InitPropVariantFromString(L"Left", &key); 291 | InitPropVariantFromUInt16(0, &value); 292 | writer->SetValue(nullptr, &key, &value); 293 | PropVariantClear(&key); 294 | PropVariantClear(&value); 295 | 296 | InitPropVariantFromString(L"Top", &key); 297 | InitPropVariantFromUInt16(0, &value); 298 | writer->SetValue(nullptr, &key, &value); 299 | PropVariantClear(&key); 300 | PropVariantClear(&value); 301 | 302 | InitPropVariantFromString(L"Width", &key); 303 | InitPropVariantFromUInt16(decodeFrame_.GetWidth(), &value); 304 | writer->SetValue(nullptr, &key, &value); 305 | PropVariantClear(&key); 306 | PropVariantClear(&value); 307 | 308 | InitPropVariantFromString(L"Height", &key); 309 | InitPropVariantFromUInt16(decodeFrame_.GetHeight(), &value); 310 | writer->SetValue(nullptr, &key, &value); 311 | PropVariantClear(&key); 312 | PropVariantClear(&value); 313 | 314 | // Store for later use 315 | ComPtr reader; 316 | if (SUCCEEDED(writer->QueryInterface(reader.get_out_storage()))) 317 | metadataReader_.emplace_back(reader.new_ref()); 318 | } 319 | 320 | if (SUCCEEDED(decodeFrame_.componentFactory_->CreateMetadataWriter( 321 | GUID_MetadataFormatGCE, 322 | nullptr, 323 | WICPersistOptionDefault, 324 | writer.get_out_storage()))) 325 | { 326 | PROPVARIANT key; 327 | PROPVARIANT value; 328 | 329 | InitPropVariantFromString(L"Disposal", &key); 330 | InitPropVariantFromUInt8(0, &value); 331 | writer->SetValue(nullptr, &key, &value); 332 | PropVariantClear(&key); 333 | PropVariantClear(&value); 334 | 335 | InitPropVariantFromString(L"Delay", &key); 336 | InitPropVariantFromUInt16(decodeFrame_.GetDelay() / 10, &value); 337 | writer->SetValue(nullptr, &key, &value); 338 | PropVariantClear(&key); 339 | PropVariantClear(&value); 340 | 341 | InitPropVariantFromString(L"TransparencyFlag", &key); 342 | InitPropVariantFromBoolean(FALSE, &value); 343 | writer->SetValue(nullptr, &key, &value); 344 | PropVariantClear(&key); 345 | PropVariantClear(&value); 346 | 347 | // Store for later use 348 | ComPtr reader; 349 | if (SUCCEEDED(writer->QueryInterface(reader.get_out_storage()))) 350 | metadataReader_.emplace_back(reader.new_ref()); 351 | } 352 | } 353 | } 354 | 355 | void DecodeFrame::MetadataBlockReader::ReadMetadata(GUID metadataFormat, const char* name) 356 | { 357 | TRACE("()\n"); 358 | uint8_t* data = nullptr; 359 | size_t length = 0; 360 | flif_image_get_metadata(decodeFrame_.image_, name, &data, &length); 361 | if (data && length > 0) 362 | { 363 | //Create stream 364 | ComPtr stream(SHCreateMemStream(data, length)); 365 | 366 | // Create reader of stream 367 | ComPtr reader; 368 | if (SUCCEEDED(decodeFrame_.componentFactory_->CreateMetadataReader( 369 | metadataFormat, 370 | nullptr, 371 | WICPersistOptionDefault, 372 | stream.get(), 373 | reader.get_out_storage()))) { 374 | // Store Reader for later use 375 | metadataReader_.emplace_back(reader.new_ref()); 376 | } 377 | } 378 | flif_image_free_metadata(decodeFrame_.image_, data); 379 | } 380 | 381 | 382 | -------------------------------------------------------------------------------- /FlifWICCodec/decode_frame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "utils.h" 9 | 10 | class DecodeFrame : public ComObjectBase { 11 | public: 12 | DecodeFrame(); 13 | ~DecodeFrame(); 14 | // Inherited via IUnknown: 15 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override; 16 | ULONG STDMETHODCALLTYPE AddRef() override { return ComObjectBase::AddRef(); } 17 | ULONG STDMETHODCALLTYPE Release() override { return ComObjectBase::Release(); } 18 | // Inherited via IWICBitmapSource: 19 | HRESULT STDMETHODCALLTYPE GetSize(UINT *puiWidth, UINT *puiHeight) override; 20 | HRESULT STDMETHODCALLTYPE GetPixelFormat(WICPixelFormatGUID *pPixelFormat) override; 21 | HRESULT STDMETHODCALLTYPE GetResolution(double *pDpiX, double *pDpiY) override; 22 | HRESULT STDMETHODCALLTYPE CopyPalette(IWICPalette *pIPalette) override; 23 | HRESULT STDMETHODCALLTYPE CopyPixels(const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer) override; 24 | // Inherited via IWICBitmapFrameDecode: 25 | HRESULT STDMETHODCALLTYPE GetMetadataQueryReader(IWICMetadataQueryReader **ppIMetadataQueryReader) override; 26 | HRESULT STDMETHODCALLTYPE GetColorContexts(UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount) override; 27 | HRESULT STDMETHODCALLTYPE GetThumbnail(IWICBitmapSource **ppIThumbnail) override; 28 | 29 | void SetFlifImage(FLIF_IMAGE* image, UINT totalNumberImages); 30 | private: 31 | 32 | class MetadataBlockReader : public IWICMetadataBlockReader { 33 | public: 34 | MetadataBlockReader(DecodeFrame& decodeFrame) : decodeFrame_(decodeFrame) {} 35 | // Inherited via IUnknown: 36 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override { return decodeFrame_.QueryInterface(riid, ppvObject); }; 37 | ULONG STDMETHODCALLTYPE AddRef() override { return decodeFrame_.AddRef(); } 38 | ULONG STDMETHODCALLTYPE Release() override { return decodeFrame_.Release(); } 39 | // Inherited via IWICMetadataBlockReader 40 | HRESULT STDMETHODCALLTYPE GetContainerFormat(GUID * pguidContainerFormat) override; 41 | HRESULT STDMETHODCALLTYPE GetCount(UINT * pcCount) override; 42 | HRESULT STDMETHODCALLTYPE GetReaderByIndex(UINT nIndex, IWICMetadataReader ** ppIMetadataReader) override; 43 | HRESULT STDMETHODCALLTYPE GetEnumerator(IEnumUnknown ** ppIEnumMetadata) override; 44 | private: 45 | void ReadAllMetadata(); 46 | void ReadMetadata(GUID metadataFormat, const char* name); 47 | 48 | DecodeFrame& decodeFrame_; 49 | std::deque> metadataReader_; 50 | }; 51 | 52 | // No copy and assign. 53 | DecodeFrame(const DecodeFrame&) = delete; 54 | void operator=(const DecodeFrame&) = delete; 55 | HRESULT InitializeFactory(); 56 | private: 57 | UINT GetWidth() const; 58 | UINT GetHeight() const; 59 | UINT GetDelay() const; 60 | 61 | CRITICAL_SECTION cs_; 62 | ComPtr factory_; 63 | ComPtr componentFactory_; 64 | MetadataBlockReader metadataBlockReader_; 65 | FLIF_IMAGE* image_; 66 | UINT totalNumberOfImages_; 67 | }; 68 | -------------------------------------------------------------------------------- /FlifWICCodec/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #define INITGUID 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "dllmain.h" 11 | #include "decode_container.h" 12 | #include "encode_container.h" 13 | #include "metadata_store.h" 14 | #include "utils.h" 15 | #include "uuid.h" 16 | 17 | // Logging in debug mode. 18 | #ifdef FLIF_DEBUG_LOGGING 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | static CRITICAL_SECTION debug_file_section; 27 | 28 | void MAIN_debug_printf(const char* prefix, const char* func, const char* fmt, ...) { 29 | va_list ap; 30 | va_start(ap, fmt); 31 | EnterCriticalSection(&debug_file_section); 32 | fprintf(stdout, "%s:%s ", prefix, func); 33 | fflush(stdout); 34 | vfprintf(stdout, fmt, ap); 35 | fflush(stdout); 36 | LeaveCriticalSection(&debug_file_section); 37 | va_end(ap); 38 | } 39 | 40 | static void init_logging() { 41 | InitializeCriticalSection(&debug_file_section); 42 | 43 | WCHAR path[MAX_PATH]; 44 | DWORD path_size = MAX_PATH; 45 | DWORD err; 46 | if ((err = SHRegGetValueW(HKEY_LOCAL_MACHINE, L"Software\\FLIF\\FLIF Codec", 47 | L"DebugPath", SRRF_RT_REG_SZ, NULL, path, &path_size)) != ERROR_SUCCESS) { 48 | StringCchCopyW(path, MAX_PATH, L"C:\\DebugOut"); 49 | } 50 | 51 | WCHAR filename[MAX_PATH]; 52 | StringCchPrintfW(filename, MAX_PATH, L"%s\\flif-stdout.txt", path); 53 | _wfreopen(filename, L"a", stdout); 54 | StringCchPrintfW(filename, MAX_PATH, L"%s\\flif-stderr.txt", path); 55 | _wfreopen(filename, L"a", stderr); 56 | } 57 | 58 | // Returns a pointer to a string representation of a GUID. The results are 59 | // stored in 32 static buffer - the 33rd call to this methods will overwrite 60 | // the first result. 61 | char *debugstr_guid(REFGUID guid) 62 | { 63 | static char guidbuf[32][128]; 64 | static int pos = 0; 65 | pos %= 32; 66 | StringCchPrintfA(guidbuf[pos], 128, 67 | "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", 68 | guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], 69 | guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], 70 | guid.Data4[6], guid.Data4[7]); 71 | return guidbuf[pos++]; 72 | } 73 | 74 | WCHAR *debugstr_var(REFPROPVARIANT var) 75 | { 76 | static WCHAR buf[32][128]; 77 | static int pos = 0; 78 | pos %= 32; 79 | HRESULT result = PropVariantToString(var, buf[pos], ARRAYSIZE(buf[pos])); 80 | if (SUCCEEDED(result) || result == STRSAFE_E_INSUFFICIENT_BUFFER) 81 | { 82 | return buf[pos++]; 83 | } 84 | else { 85 | return L"error"; 86 | } 87 | } 88 | #endif 89 | 90 | // Object and server locks counters 91 | LONG volatile MAIN_nObjects = 0; 92 | LONG volatile MAIN_nServerLocks = 0; 93 | HINSTANCE MAIN_hSelf; 94 | 95 | 96 | // Class factory 97 | 98 | typedef HRESULT(*ObjectConstructor)(IUnknown** ppvObject); 99 | 100 | // A default constructor. Creates and instance of T. T should be a subclass of 101 | // IUnknown with a parameter-less constructor. 102 | template 103 | HRESULT CreateComObject(IUnknown** output) { 104 | T* result = new (std::nothrow) T(); 105 | if (result == nullptr) 106 | return E_OUTOFMEMORY; 107 | *output = static_cast(result); 108 | return S_OK; 109 | } 110 | 111 | class MyClassFactory : public ComObjectBase 112 | { 113 | public: 114 | MyClassFactory(ObjectConstructor ctor); 115 | // IUnknown: 116 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override; 117 | ULONG STDMETHODCALLTYPE AddRef() override { return ComObjectBase::AddRef(); } 118 | ULONG STDMETHODCALLTYPE Release() override { return ComObjectBase::Release(); } 119 | // IClassFactory: 120 | HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppvObject) override; 121 | HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock) override; 122 | 123 | private: 124 | volatile LONG ref_count_; 125 | ObjectConstructor ctor_; 126 | }; 127 | 128 | MyClassFactory::MyClassFactory(ObjectConstructor ctor) { 129 | InterlockedIncrement(&MAIN_nObjects); 130 | ref_count_ = 0; 131 | ctor_ = ctor; 132 | } 133 | 134 | HRESULT MyClassFactory::QueryInterface(REFIID riid, void **ppvObject) 135 | { 136 | if (ppvObject == nullptr) 137 | return E_INVALIDARG; 138 | *ppvObject = nullptr; 139 | 140 | if (!IsEqualGUID(riid, IID_IUnknown) && !IsEqualGUID(riid, IID_IClassFactory)) 141 | return E_NOINTERFACE; 142 | this->AddRef(); 143 | *ppvObject = static_cast(this); 144 | return S_OK; 145 | } 146 | 147 | HRESULT MyClassFactory::CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppvObject) 148 | { 149 | IUnknown* output; 150 | HRESULT ret; 151 | TRACE3("(%p, %s, %p)\n", pUnkOuter, debugstr_guid(riid), ppvObject); 152 | 153 | if (ppvObject == nullptr) 154 | return E_INVALIDARG; 155 | *ppvObject = nullptr; 156 | 157 | if (pUnkOuter != nullptr) 158 | return CLASS_E_NOAGGREGATION; 159 | 160 | ret = ctor_(&output); 161 | if (FAILED(ret)) 162 | return ret; 163 | ret = output->QueryInterface(riid, ppvObject); 164 | output->Release(); 165 | if (FAILED(ret)) { 166 | *ppvObject = nullptr; 167 | } 168 | TRACE1("ret=%08x\n", ret); 169 | return ret; 170 | } 171 | 172 | HRESULT MyClassFactory::LockServer(BOOL fLock) 173 | { 174 | if (fLock) 175 | InterlockedIncrement(&MAIN_nServerLocks); 176 | else 177 | InterlockedDecrement(&MAIN_nServerLocks); 178 | return S_OK; 179 | } 180 | 181 | typedef HRESULT(WINAPI *RegInstallFuncA)(HMODULE hm, LPCSTR pszSection, const STRTABLEA* pstTable); 182 | typedef void (STDAPICALLTYPE *SHChangeNotifyFunc)(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID dwItem2); 183 | 184 | 185 | static HRESULT RegisterServer(BOOL fInstall) { 186 | // Manual loading of advpack not to load it when DLL used in normal opertion. 187 | HMODULE hAdvPack = LoadLibraryExW(L"advpack.dll", nullptr, LOAD_WITH_ALTERED_SEARCH_PATH); 188 | if (hAdvPack == nullptr) { 189 | TRACE("Couldn't load advpack.dll\n"); 190 | return E_UNEXPECTED; 191 | } 192 | 193 | // Note: RegInstallA/W not available on Windows XP with MSIE6. 194 | // Using ANSI version doesn't matter, as the "unicodeness" of the _MOD_PATH 195 | // depends only on the "unicodeness" of the *.inf file, while other 196 | // substitutions are ASCII. 197 | RegInstallFuncA pRegInstallA = (RegInstallFuncA)GetProcAddress(hAdvPack, "RegInstall"); 198 | if (!pRegInstallA) { 199 | TRACE("Couldn't find RegInstall in advpack.dll\n"); 200 | return E_UNEXPECTED; 201 | } 202 | 203 | STRENTRYA entries[1] = { 204 | { "PhotoDir", "Windows Photo Viewer" } 205 | }; 206 | STRTABLEA strings; 207 | strings.cEntries = sizeof(entries) / sizeof(entries[0]); 208 | strings.pse = entries; 209 | if (LOWORD(GetVersion()) == 0x0006) 210 | entries[0].pszValue = "Windows Photo Gallery"; 211 | 212 | LPCSTR section; 213 | if (LOBYTE(GetVersion()) < 6) { 214 | section = (fInstall ? "PrevistaInstall" : "PrevistaUninstall"); 215 | } 216 | else { 217 | section = (fInstall ? "DefaultInstall" : "DefaultUninstall"); 218 | } 219 | TRACE3("Registering install=%d (using %ws) v=%x\n", fInstall, section, GetVersion()); 220 | if (FAILED(pRegInstallA(MAIN_hSelf, section, &strings))) 221 | return E_UNEXPECTED; 222 | 223 | // Invalidate caches. 224 | HMODULE hShell32 = LoadLibraryExW(L"shell32.dll", nullptr, LOAD_WITH_ALTERED_SEARCH_PATH); 225 | if (!hShell32) { 226 | TRACE("Couldn't load shell32.dll\n"); 227 | return E_UNEXPECTED; 228 | } 229 | SHChangeNotifyFunc pSHChangeNotify = (SHChangeNotifyFunc)GetProcAddress(hShell32, "SHChangeNotify"); 230 | if (pSHChangeNotify) 231 | pSHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr); 232 | return S_OK; 233 | } 234 | 235 | STDAPI DllRegisterServer() { 236 | return RegisterServer(TRUE); 237 | } 238 | 239 | STDAPI DllUnregisterServer() { 240 | return RegisterServer(FALSE); 241 | } 242 | 243 | _Check_return_ 244 | STDAPI DllGetClassObject(_In_ REFCLSID clsid, _In_ REFIID iid, _Outptr_ LPVOID FAR* ppv) 245 | { 246 | if (ppv == nullptr) 247 | return E_INVALIDARG; 248 | *ppv = nullptr; 249 | TRACE3("(%s, %s, %p)\n", debugstr_guid(clsid), debugstr_guid(iid), ppv); 250 | if (!IsEqualGUID(iid, IID_IClassFactory)) 251 | return E_INVALIDARG; 252 | 253 | if (IsEqualGUID(clsid, CLSID_FLIFWICDecoder)) { 254 | *ppv = (LPVOID)(new (std::nothrow) MyClassFactory(CreateComObject)); 255 | } 256 | else if (IsEqualGUID(clsid, CLSID_FLIFWICEncoder)) { 257 | *ppv = (LPVOID)(new (std::nothrow) MyClassFactory(CreateComObject)); 258 | } 259 | else if (IsEqualGUID(clsid, GUID_FLIFPropertyStore)) { 260 | *ppv = (LPVOID)(new (std::nothrow) MyClassFactory(CreateComObject)); 261 | } 262 | else { 263 | return CLASS_E_CLASSNOTAVAILABLE; 264 | } 265 | 266 | if (*ppv == nullptr) 267 | return E_OUTOFMEMORY; 268 | return S_OK; 269 | } 270 | 271 | STDAPI DllCanUnloadNow() { 272 | if (MAIN_nObjects == 0 && MAIN_nServerLocks == 0) 273 | return S_OK; 274 | else 275 | return S_FALSE; 276 | } 277 | 278 | BOOL APIENTRY DllMain(HMODULE hModule, 279 | DWORD ul_reason_for_call, 280 | LPVOID lpReserved) 281 | { 282 | if (ul_reason_for_call == DLL_PROCESS_ATTACH) { 283 | DisableThreadLibraryCalls(hModule); 284 | MAIN_hSelf = hModule; 285 | #ifdef FLIF_DEBUG_LOGGING 286 | init_logging(); 287 | #endif 288 | } 289 | TRACE1("(%d)\n", ul_reason_for_call); 290 | return TRUE; 291 | } 292 | -------------------------------------------------------------------------------- /FlifWICCodec/dllmain.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef _DEBUG 4 | #define FLIF_DEBUG_LOGGING 5 | #endif // DEBUG 6 | 7 | #ifdef FLIF_DEBUG_LOGGING 8 | // debug functions 9 | void MAIN_debug_printf(_In_z_ const char* prefix, _In_z_ const char* func, _In_z_ _Printf_format_string_ const char* fmt, ...); 10 | #define TRACE(fmt) MAIN_debug_printf("trace", __FUNCTION__, fmt) 11 | #define TRACE1(fmt, a) MAIN_debug_printf("trace", __FUNCTION__, fmt, a) 12 | #define TRACE2(fmt, a, b) MAIN_debug_printf("trace", __FUNCTION__, fmt, a, b) 13 | #define TRACE3(fmt, a, b, c) MAIN_debug_printf("trace", __FUNCTION__, fmt, a, b, c) 14 | #define TRACE4(fmt, a, b, c, d) MAIN_debug_printf("trace", __FUNCTION__, fmt, a, b, c, d) 15 | 16 | char *debugstr_guid(REFGUID guid); 17 | WCHAR *debugstr_var(REFPROPVARIANT var); 18 | #else 19 | #define TRACE(fmt) 20 | #define TRACE1(fmt, a) 21 | #define TRACE2(fmt, a, b) 22 | #define TRACE3(fmt, a, b, c) 23 | #define TRACE4(fmt, a, b, c, d) 24 | #endif 25 | 26 | // Number of COM objects created. 27 | extern LONG volatile MAIN_nObjects; -------------------------------------------------------------------------------- /FlifWICCodec/encode_container.cpp: -------------------------------------------------------------------------------- 1 | #include "encode_container.h" 2 | #include "encode_frame.h" 3 | #include "uuid.h" 4 | #include "pixel_converter.h" 5 | 6 | EncodeContainer::EncodeContainer() 7 | : current_frame_(nullptr), encoder_(nullptr), pIStream_(nullptr) 8 | { 9 | TRACE("()\n"); 10 | InitializeCriticalSection(&cs_); 11 | } 12 | 13 | EncodeContainer::~EncodeContainer() 14 | { 15 | TRACE("()\n"); 16 | DeleteCriticalSection(&cs_); 17 | if (encoder_) { 18 | flif_destroy_encoder(encoder_); 19 | encoder_ = nullptr; 20 | } 21 | } 22 | 23 | HRESULT EncodeContainer::QueryInterface(REFIID riid, void ** ppvObject) 24 | { 25 | TRACE2("(%s, %p)\n", debugstr_guid(riid), ppvObject); 26 | 27 | if (ppvObject == nullptr) 28 | return E_INVALIDARG; 29 | *ppvObject = nullptr; 30 | 31 | if (!IsEqualGUID(riid, IID_IUnknown) && !IsEqualGUID(riid, IID_IWICBitmapEncoder)) 32 | return E_NOINTERFACE; 33 | this->AddRef(); 34 | *ppvObject = static_cast(this); 35 | return S_OK; 36 | } 37 | 38 | HRESULT EncodeContainer::Initialize(IStream * pIStream, WICBitmapEncoderCacheOption cacheOptions) 39 | { 40 | TRACE2("(%p, %x)\n", pIStream, cacheOptions); 41 | if (pIStream == nullptr) 42 | return E_INVALIDARG; 43 | pIStream_ = pIStream; 44 | 45 | //reset encoder 46 | if (encoder_) { 47 | flif_destroy_encoder(encoder_); 48 | encoder_ = nullptr; 49 | } 50 | encoder_ = flif_create_encoder(); 51 | return S_OK; 52 | } 53 | 54 | HRESULT EncodeContainer::GetContainerFormat(GUID * pguidContainerFormat) 55 | { 56 | TRACE1("(%p)\n", pguidContainerFormat); 57 | if (pguidContainerFormat == nullptr) 58 | return E_INVALIDARG; 59 | *pguidContainerFormat = GUID_ContainerFormatFLIF; 60 | return S_OK; 61 | } 62 | 63 | HRESULT EncodeContainer::InitializeFactory() 64 | { 65 | SectionLock l(&cs_); 66 | HRESULT result = S_OK; 67 | if (factory_.get() == nullptr) { 68 | result = CoCreateInstance(CLSID_WICImagingFactory, 69 | nullptr, 70 | CLSCTX_INPROC_SERVER, 71 | IID_IWICImagingFactory, 72 | (LPVOID*)factory_.get_out_storage()); 73 | } 74 | return result; 75 | } 76 | 77 | HRESULT EncodeContainer::GetEncoderInfo(IWICBitmapEncoderInfo ** ppIEncoderInfo) 78 | { 79 | TRACE1("(%p)\n", ppIEncoderInfo); 80 | HRESULT result; 81 | 82 | result = InitializeFactory(); 83 | if (FAILED(result)) 84 | return result; 85 | 86 | ComPtr compInfo; 87 | result = factory_->CreateComponentInfo(CLSID_FLIFWICEncoder, compInfo.get_out_storage()); 88 | if (FAILED(result)) 89 | return result; 90 | 91 | result = compInfo->QueryInterface(IID_IWICBitmapEncoderInfo, (void**)ppIEncoderInfo); 92 | if (FAILED(result)) 93 | return result; 94 | 95 | return S_OK; 96 | } 97 | 98 | HRESULT EncodeContainer::SetColorContexts(UINT cCount, IWICColorContext ** ppIColorContext) 99 | { 100 | TRACE2("(%d, %p)\n", cCount, ppIColorContext); 101 | return WINCODEC_ERR_UNSUPPORTEDOPERATION;; 102 | } 103 | 104 | HRESULT EncodeContainer::SetPalette(IWICPalette * pIPalette) 105 | { 106 | TRACE1("(%p)\n", pIPalette); 107 | return WINCODEC_ERR_UNSUPPORTEDOPERATION; 108 | } 109 | 110 | HRESULT EncodeContainer::SetThumbnail(IWICBitmapSource * pIThumbnail) 111 | { 112 | TRACE1("(%p)\n", pIThumbnail); 113 | return WINCODEC_ERR_UNSUPPORTEDOPERATION; 114 | } 115 | 116 | HRESULT EncodeContainer::SetPreview(IWICBitmapSource * pIPreview) 117 | { 118 | TRACE1("(%p)\n", pIPreview); 119 | return WINCODEC_ERR_UNSUPPORTEDOPERATION; 120 | } 121 | 122 | HRESULT EncodeContainer::CreateNewFrame(IWICBitmapFrameEncode ** ppIFrameEncode, IPropertyBag2 ** ppIEncoderOptions) 123 | { 124 | TRACE2("(%p, %p)\n", ppIFrameEncode, ppIEncoderOptions); 125 | if (ppIFrameEncode == nullptr) 126 | return E_INVALIDARG; 127 | 128 | if (encoder_ == nullptr) { 129 | return WINCODEC_ERR_NOTINITIALIZED; 130 | } 131 | 132 | SectionLock l(&cs_); 133 | 134 | ComPtr output; 135 | output.reset(new (std::nothrow) EncodeFrame(this)); 136 | *ppIFrameEncode = output.new_ref(); 137 | return S_OK; 138 | } 139 | 140 | HRESULT EncodeContainer::Commit(void) 141 | { 142 | TRACE("()\n"); 143 | if (encoder_ == nullptr) { 144 | return WINCODEC_ERR_NOTINITIALIZED; 145 | } 146 | 147 | if (current_frame_.get()) 148 | { 149 | uint8_t* buffer = nullptr; 150 | size_t buffer_size = 0; 151 | if (flif_encoder_encode_memory(encoder_, reinterpret_cast(&buffer), &buffer_size) != 0) { 152 | ULONG written = 0; 153 | do { 154 | pIStream_->Write(buffer, buffer_size, &written); 155 | buffer_size -= written; 156 | buffer += written; 157 | } while (buffer_size > 0); 158 | } 159 | } 160 | return S_OK; 161 | } 162 | 163 | HRESULT EncodeContainer::GetMetadataQueryWriter(IWICMetadataQueryWriter ** ppIMetadataQueryWriter) 164 | { 165 | TRACE1("(%p)\n", ppIMetadataQueryWriter); 166 | if (ppIMetadataQueryWriter == nullptr) 167 | return E_INVALIDARG; 168 | return E_NOTIMPL; 169 | } 170 | 171 | HRESULT EncodeContainer::AddImage(std::shared_ptr frame, AnimationInformation animation_information, std::deque> metadata) 172 | { 173 | if (encoder_ == nullptr) { 174 | return WINCODEC_ERR_NOTINITIALIZED; 175 | } 176 | 177 | //Merge current frame 178 | if (current_frame_.get()) { 179 | if (current_frame_->NumberComponents != frame->NumberComponents) { 180 | return WINCODEC_ERR_INTERNALERROR; 181 | } 182 | 183 | if (animation_information.TransparencyFlag && frame->NumberComponents == 4) { 184 | //Must be RGBA and Alpha channel has only values 0 or FF 185 | assert(frame->NumberComponents == 4); 186 | for (UINT i = 0; i < frame->Height; ++i) { 187 | BYTE* srcrow = frame->Buffer + i * frame->Stride; 188 | BYTE* destrow = current_frame_->Buffer + (i + animation_information.Top) * current_frame_->Stride; 189 | BYTE* destrowstart = destrow + animation_information.Left * current_frame_->NumberComponents; 190 | CopyAllButTransparentPixelRGBA8(frame->Width, srcrow, destrowstart); 191 | } 192 | } 193 | else { 194 | //Must be RGB or Gray 195 | assert(frame->NumberComponents < 4); 196 | for (UINT i = 0; i < frame->Height; ++i) { 197 | BYTE* srcrow = frame->Buffer + i * frame->Stride; 198 | BYTE* destrow = current_frame_->Buffer + (i + animation_information.Top) * current_frame_->Stride; 199 | BYTE* destrowstart = destrow + animation_information.Left * current_frame_->NumberComponents; 200 | memcpy(destrowstart, srcrow, frame->Width*frame->NumberComponents); 201 | } 202 | } 203 | } 204 | else 205 | { 206 | current_frame_ = frame; 207 | } 208 | 209 | //Write and encode rows 210 | FLIF_IMAGE* image = nullptr; 211 | if (current_frame_->NumberComponents == 1) { 212 | image = flif_import_image_GRAY(current_frame_->Width, current_frame_->Height, current_frame_->Buffer, current_frame_->Stride); 213 | } 214 | else if (current_frame_->NumberComponents == 3) { 215 | image = flif_import_image_RGB(current_frame_->Width, current_frame_->Height, current_frame_->Buffer, current_frame_->Stride); 216 | } 217 | else if (current_frame_->NumberComponents == 4) { 218 | image = flif_import_image_RGBA(current_frame_->Width, current_frame_->Height, current_frame_->Buffer, current_frame_->Stride); 219 | } 220 | 221 | if (image == nullptr) 222 | return WINCODEC_ERR_INTERNALERROR; 223 | 224 | //Animation 225 | if (animation_information.Delay > 0) { 226 | flif_image_set_frame_delay(image, animation_information.Delay); 227 | } 228 | 229 | //Metadata 230 | if (metadata.size() > 0) { 231 | for (int i = 0; i < metadata.size(); ++i) { 232 | flif_image_set_metadata(image, metadata[i]->Chunkname.c_str(), metadata[i]->Buffer, metadata[i]->BufferSize); 233 | } 234 | } 235 | 236 | flif_encoder_add_image(encoder_, image); 237 | flif_destroy_image(image); 238 | 239 | return S_OK; 240 | } 241 | -------------------------------------------------------------------------------- /FlifWICCodec/encode_container.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "utils.h" 10 | 11 | struct AnimationInformation { 12 | uint32_t Left; 13 | uint32_t Top; 14 | uint8_t Disposal; 15 | bool TransparencyFlag; 16 | uint32_t Delay; 17 | }; 18 | 19 | class EncodeContainer : public ComObjectBase { 20 | public: 21 | EncodeContainer(); 22 | ~EncodeContainer(); 23 | // IUnknown: 24 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override; 25 | ULONG STDMETHODCALLTYPE AddRef() override { return ComObjectBase::AddRef(); } 26 | ULONG STDMETHODCALLTYPE Release() override { return ComObjectBase::Release(); } 27 | // Inherited via IWICBitmapEncoder 28 | HRESULT STDMETHODCALLTYPE Initialize(IStream * pIStream, WICBitmapEncoderCacheOption cacheOption) override; 29 | HRESULT STDMETHODCALLTYPE GetContainerFormat(GUID* pguidContainerFormat) override; 30 | HRESULT STDMETHODCALLTYPE GetEncoderInfo(IWICBitmapEncoderInfo ** ppIEncoderInfo) override; 31 | HRESULT STDMETHODCALLTYPE SetColorContexts(UINT cCount, IWICColorContext ** ppIColorContext) override; 32 | HRESULT STDMETHODCALLTYPE SetPalette(IWICPalette * pIPalette) override; 33 | HRESULT STDMETHODCALLTYPE SetThumbnail(IWICBitmapSource * pIThumbnail) override; 34 | HRESULT STDMETHODCALLTYPE SetPreview(IWICBitmapSource * pIPreview) override; 35 | HRESULT STDMETHODCALLTYPE CreateNewFrame(IWICBitmapFrameEncode ** ppIFrameEncode, IPropertyBag2 ** ppIEncoderOptions) override; 36 | HRESULT STDMETHODCALLTYPE Commit(void) override; 37 | HRESULT STDMETHODCALLTYPE GetMetadataQueryWriter(IWICMetadataQueryWriter ** ppIMetadataQueryWriter) override; 38 | public: 39 | HRESULT AddImage(std::shared_ptr frame, AnimationInformation animationInformation, std::deque> metadata); 40 | private: 41 | // No copy and assign. 42 | EncodeContainer(const EncodeContainer&) = delete; 43 | void operator=(const EncodeContainer&) = delete; 44 | HRESULT InitializeFactory(); 45 | 46 | std::shared_ptr current_frame_; 47 | FLIF_ENCODER* encoder_; 48 | IStream* pIStream_; 49 | ComPtr factory_; 50 | CRITICAL_SECTION cs_; 51 | 52 | }; 53 | -------------------------------------------------------------------------------- /FlifWICCodec/encode_frame.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "encode_frame.h" 3 | #include "uuid.h" 4 | 5 | EncodeFrame::EncodeFrame(EncodeContainer* container) 6 | : container_(container), frame_(nullptr), metadataBlockWriter_(*this) 7 | { 8 | TRACE("()\n"); 9 | container_->AddRef(); 10 | InitializeCriticalSection(&cs_); 11 | } 12 | 13 | EncodeFrame::~EncodeFrame() 14 | { 15 | TRACE("()\n"); 16 | container_->Release(); 17 | DeleteCriticalSection(&cs_); 18 | } 19 | 20 | 21 | HRESULT EncodeFrame::QueryInterface(REFIID riid, void ** ppvObject) 22 | { 23 | TRACE2("(%s, %p)\n", debugstr_guid(riid), ppvObject); 24 | 25 | if (ppvObject == nullptr) 26 | return E_INVALIDARG; 27 | *ppvObject = nullptr; 28 | 29 | if (IsEqualGUID(riid, IID_IUnknown) || 30 | IsEqualGUID(riid, IID_IWICBitmapFrameEncode)) 31 | { 32 | this->AddRef(); 33 | *ppvObject = static_cast(this); 34 | return S_OK; 35 | } 36 | 37 | // Multiple inheritence needs explicit cast 38 | if (IsEqualGUID(riid, IID_IWICMetadataBlockWriter) || 39 | IsEqualGUID(riid, IID_IWICMetadataBlockReader)) 40 | { 41 | this->AddRef(); 42 | *ppvObject = static_cast(&this->metadataBlockWriter_); 43 | return S_OK; 44 | } 45 | return E_NOINTERFACE; 46 | } 47 | 48 | HRESULT EncodeFrame::Initialize(IPropertyBag2 * pIEncoderOptions) 49 | { 50 | TRACE1("(%p)\n", pIEncoderOptions); 51 | return S_OK; 52 | } 53 | 54 | HRESULT EncodeFrame::SetSize(UINT uiWidth, UINT uiHeight) 55 | { 56 | TRACE2("(%d, %d)\n", uiWidth, uiHeight); 57 | return S_OK; 58 | } 59 | 60 | HRESULT EncodeFrame::SetResolution(double dpiX, double dpiY) 61 | { 62 | TRACE2("(%f, %f)\n", dpiX, dpiY); 63 | return S_OK; 64 | } 65 | 66 | HRESULT EncodeFrame::SetPixelFormat(WICPixelFormatGUID * pPixelFormat) 67 | { 68 | TRACE1("(%p)\n", pPixelFormat); 69 | if (pPixelFormat == nullptr) 70 | return E_INVALIDARG; 71 | 72 | //supported nativly 73 | if (*pPixelFormat == GUID_WICPixelFormat32bppRGBA || 74 | *pPixelFormat == GUID_WICPixelFormat24bppRGB || 75 | *pPixelFormat == GUID_WICPixelFormat8bppGray) 76 | { 77 | return S_OK; 78 | } 79 | 80 | //supported through converter 81 | if (*pPixelFormat == GUID_WICPixelFormatBlackWhite || 82 | *pPixelFormat == GUID_WICPixelFormat2bppGray || 83 | *pPixelFormat == GUID_WICPixelFormat4bppGray || 84 | *pPixelFormat == GUID_WICPixelFormat32bppBGRA || 85 | *pPixelFormat == GUID_WICPixelFormat16bppBGRA5551 || 86 | *pPixelFormat == GUID_WICPixelFormat16bppBGR555 || 87 | *pPixelFormat == GUID_WICPixelFormat16bppBGR565 || 88 | *pPixelFormat == GUID_WICPixelFormat24bppBGR || 89 | *pPixelFormat == GUID_WICPixelFormat32bppRGB || 90 | *pPixelFormat == GUID_WICPixelFormat32bppBGR || 91 | *pPixelFormat == GUID_WICPixelFormat1bppIndexed || 92 | *pPixelFormat == GUID_WICPixelFormat2bppIndexed || 93 | *pPixelFormat == GUID_WICPixelFormat4bppIndexed || 94 | *pPixelFormat == GUID_WICPixelFormat8bppIndexed) 95 | { 96 | return S_OK; 97 | } 98 | 99 | *pPixelFormat = GUID_WICPixelFormatUndefined; 100 | return WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT; 101 | } 102 | 103 | HRESULT EncodeFrame::SetColorContexts(UINT cCount, IWICColorContext ** ppIColorContext) 104 | { 105 | TRACE2("(%d, %p)\n", cCount, ppIColorContext); 106 | return E_NOTIMPL; 107 | } 108 | 109 | HRESULT EncodeFrame::SetPalette(IWICPalette * pIPalette) 110 | { 111 | TRACE1("(%p)\n", pIPalette); 112 | return E_NOTIMPL; 113 | } 114 | 115 | HRESULT EncodeFrame::SetThumbnail(IWICBitmapSource * pIThumbnail) 116 | { 117 | TRACE1("(%p)\n", pIThumbnail); 118 | return E_NOTIMPL; 119 | } 120 | 121 | HRESULT EncodeFrame::WritePixels(UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE * pbPixels) 122 | { 123 | TRACE4("(%d, %d, %d, %p)\n", lineCount, cbStride, cbBufferSize, pbPixels); 124 | return E_NOTIMPL; 125 | } 126 | 127 | HRESULT EncodeFrame::WriteSource(IWICBitmapSource* pIBitmapSource, WICRect * prc) 128 | { 129 | TRACE2("(%p, %p)\n", pIBitmapSource, prc); 130 | if (pIBitmapSource == nullptr) 131 | return E_INVALIDARG; 132 | 133 | HRESULT result = S_OK; 134 | 135 | //For GIFs get animation information 136 | animation_information_ = {}; 137 | ComPtr decodeframe; 138 | if (SUCCEEDED(pIBitmapSource->QueryInterface(decodeframe.get_out_storage()))) { 139 | ComPtr metadataReader; 140 | if (SUCCEEDED(decodeframe->GetMetadataQueryReader(metadataReader.get_out_storage()))) 141 | { 142 | PROPVARIANT propValue; 143 | PropVariantInit(&propValue); 144 | 145 | if (SUCCEEDED(metadataReader->GetMetadataByName(L"/imgdesc/Left", &propValue))) { 146 | if (propValue.vt == VT_UI2) { 147 | animation_information_.Left = propValue.uiVal; 148 | } 149 | } 150 | PropVariantClear(&propValue); 151 | 152 | if (SUCCEEDED(metadataReader->GetMetadataByName(L"/imgdesc/Top", &propValue))) { 153 | if (propValue.vt == VT_UI2) { 154 | animation_information_.Top = propValue.uiVal; 155 | } 156 | } 157 | PropVariantClear(&propValue); 158 | 159 | if (SUCCEEDED(metadataReader->GetMetadataByName(L"/grctlext/Disposal", &propValue))) { 160 | if (propValue.vt == VT_UI1) { 161 | animation_information_.Disposal = propValue.bVal; 162 | } 163 | } 164 | PropVariantClear(&propValue); 165 | 166 | if (SUCCEEDED(metadataReader->GetMetadataByName(L"/grctlext/Delay", &propValue))) { 167 | if (propValue.vt == VT_UI2) { 168 | // From 10th ms to ms 169 | animation_information_.Delay = propValue.uiVal * 10; 170 | } 171 | } 172 | PropVariantClear(&propValue); 173 | 174 | if (SUCCEEDED(metadataReader->GetMetadataByName(L"/grctlext/TransparencyFlag", &propValue))) { 175 | if (propValue.vt == VT_BOOL) { 176 | animation_information_.TransparencyFlag = propValue.boolVal; 177 | } 178 | } 179 | PropVariantClear(&propValue); 180 | } 181 | } 182 | 183 | //Check Rect 184 | UINT image_width = 0; 185 | UINT image_height = 0; 186 | result = pIBitmapSource->GetSize(&image_width, &image_height); 187 | if (FAILED(result)) { 188 | return result; 189 | } 190 | WICRect rect = { 0, 0, image_width, image_height }; 191 | if (prc) 192 | rect = *prc; 193 | if (rect.Width < 0 || rect.Height < 0 || rect.X < 0 || rect.Y < 0) 194 | return E_INVALIDARG; 195 | if (rect.X + rect.Width > image_width || 196 | rect.Y + rect.Height > image_height) 197 | return E_INVALIDARG; 198 | 199 | //Convert Image 200 | WICPixelFormatGUID source_pixel_format = { 0 }; 201 | result = pIBitmapSource->GetPixelFormat(&source_pixel_format); 202 | if (FAILED(result)) { 203 | return result; 204 | } 205 | IWICBitmapSource* source_image = pIBitmapSource; 206 | ComPtr converter; 207 | if (source_pixel_format != GUID_WICPixelFormat32bppRGBA && 208 | source_pixel_format != GUID_WICPixelFormat24bppRGB && 209 | source_pixel_format != GUID_WICPixelFormat8bppGray) 210 | { 211 | //Create factory 212 | result = InitializeFactory(); 213 | if (FAILED(result)) { 214 | return result; 215 | } 216 | 217 | //Set destination pixelformat 218 | WICPixelFormatGUID dest_pixel_format = { 0 }; 219 | if (source_pixel_format == GUID_WICPixelFormatBlackWhite || 220 | source_pixel_format == GUID_WICPixelFormat2bppGray || 221 | source_pixel_format == GUID_WICPixelFormat4bppGray) 222 | { 223 | dest_pixel_format = GUID_WICPixelFormat8bppGray; 224 | } 225 | else if (source_pixel_format == GUID_WICPixelFormat32bppBGRA || 226 | source_pixel_format == GUID_WICPixelFormat16bppBGRA5551) 227 | { 228 | dest_pixel_format = GUID_WICPixelFormat32bppRGBA; 229 | } 230 | else if ( 231 | source_pixel_format == GUID_WICPixelFormat16bppBGR555 || 232 | source_pixel_format == GUID_WICPixelFormat16bppBGR565 || 233 | source_pixel_format == GUID_WICPixelFormat24bppBGR || 234 | source_pixel_format == GUID_WICPixelFormat32bppBGR || 235 | source_pixel_format == GUID_WICPixelFormat32bppRGB) 236 | { 237 | dest_pixel_format = GUID_WICPixelFormat24bppRGB; 238 | } 239 | else if (source_pixel_format == GUID_WICPixelFormat1bppIndexed || 240 | source_pixel_format == GUID_WICPixelFormat2bppIndexed || 241 | source_pixel_format == GUID_WICPixelFormat4bppIndexed || 242 | source_pixel_format == GUID_WICPixelFormat8bppIndexed) 243 | { 244 | //Set destination pixelformat from palette info 245 | ComPtr palette; 246 | result = factory_->CreatePalette(palette.get_out_storage()); 247 | if (FAILED(result)) { 248 | return result; 249 | } 250 | result = pIBitmapSource->CopyPalette(palette.get()); 251 | if (FAILED(result)) { 252 | return result; 253 | } 254 | dest_pixel_format = GUID_WICPixelFormat24bppRGB; 255 | BOOL HasAlpha = false; 256 | result = palette->HasAlpha(&HasAlpha); 257 | if (FAILED(result)) { 258 | return result; 259 | } 260 | if (HasAlpha) { 261 | dest_pixel_format = GUID_WICPixelFormat32bppRGBA; 262 | } 263 | else 264 | { 265 | BOOL is_grayscale = false; 266 | result = palette->IsGrayscale(&is_grayscale); 267 | if (FAILED(result)) { 268 | return result; 269 | } 270 | BOOL is_blackwhite = false; 271 | result = palette->IsBlackWhite(&is_blackwhite); 272 | if (FAILED(result)) { 273 | return result; 274 | } 275 | if (is_grayscale || is_blackwhite) { 276 | dest_pixel_format = GUID_WICPixelFormat8bppGray; 277 | } 278 | } 279 | } 280 | else 281 | { 282 | return WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT; 283 | } 284 | 285 | //Create Converter 286 | result = factory_->CreateFormatConverter(converter.get_out_storage()); 287 | if (FAILED(result)) { 288 | return result; 289 | } 290 | BOOL can_convert = false; 291 | result = converter->CanConvert(source_pixel_format, dest_pixel_format, &can_convert); 292 | if (FAILED(result)) 293 | { 294 | return result; 295 | } 296 | if (!can_convert) { 297 | return WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT; 298 | } 299 | result = converter->Initialize( 300 | pIBitmapSource, dest_pixel_format, 301 | WICBitmapDitherTypeNone, nullptr, 0.f, 302 | WICBitmapPaletteTypeMedianCut); 303 | if (FAILED(result)) { 304 | return result; 305 | } 306 | source_image = converter.get(); 307 | } 308 | 309 | //Create raw frame 310 | result = source_image->GetPixelFormat(&source_pixel_format); 311 | if (FAILED(result)) { 312 | return result; 313 | } 314 | assert(source_pixel_format == GUID_WICPixelFormat32bppRGBA || 315 | source_pixel_format == GUID_WICPixelFormat24bppRGB || 316 | source_pixel_format == GUID_WICPixelFormat8bppGray); 317 | 318 | if (source_pixel_format == GUID_WICPixelFormat32bppRGBA) 319 | { 320 | uint32_t stride = rect.Width * 4; 321 | frame_.reset(new RawFrame(rect.Width, rect.Height, 4, stride)); 322 | } 323 | else if (source_pixel_format == GUID_WICPixelFormat24bppRGB) 324 | { 325 | uint32_t stride = 4 * ((24 * (UINT)rect.Width + 31) / 32); 326 | frame_.reset(new RawFrame(rect.Width, rect.Height, 3, stride)); 327 | } 328 | else if (source_pixel_format == GUID_WICPixelFormat8bppGray) 329 | { 330 | uint32_t stride = rect.Width; 331 | frame_.reset(new RawFrame(rect.Width, rect.Height, 1, stride)); 332 | } 333 | else 334 | { 335 | return WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT; 336 | } 337 | if (frame_->Buffer == nullptr) 338 | { 339 | frame_.reset(); 340 | return E_OUTOFMEMORY; 341 | } 342 | 343 | //Read Source Pixel 344 | result = source_image->CopyPixels(&rect, frame_->Stride, frame_->BufferSize, frame_->Buffer); 345 | if (FAILED(result)) 346 | { 347 | frame_.reset(); 348 | return result; 349 | } 350 | 351 | 352 | return S_OK; 353 | 354 | } 355 | 356 | HRESULT EncodeFrame::Commit(void) 357 | { 358 | TRACE("()\n"); 359 | if (!frame_.get()) 360 | return WINCODEC_ERR_NOTINITIALIZED; 361 | 362 | HRESULT result = S_OK; 363 | 364 | std::deque> metadatas; 365 | result = metadataBlockWriter_.GetMetadatas(metadatas); 366 | if (FAILED(result)) 367 | return result; 368 | 369 | result = container_->AddImage(frame_, animation_information_, metadatas); 370 | frame_.reset(); 371 | return result; 372 | } 373 | 374 | HRESULT EncodeFrame::InitializeFactory() 375 | { 376 | SectionLock l(&cs_); 377 | HRESULT result = S_OK; 378 | 379 | if (factory_.get() == nullptr) 380 | { 381 | result = CoCreateInstance(CLSID_WICImagingFactory, 382 | nullptr, 383 | CLSCTX_INPROC_SERVER, 384 | IID_IWICImagingFactory, 385 | (LPVOID*)factory_.get_out_storage()); 386 | if (FAILED(result)) 387 | return result; 388 | 389 | } 390 | 391 | if (factory_.get() != nullptr && 392 | componentFactory_.get() == nullptr) 393 | { 394 | result = factory_->QueryInterface(componentFactory_.get_out_storage()); 395 | if (FAILED(result)) { 396 | return result; 397 | } 398 | } 399 | 400 | return result; 401 | } 402 | 403 | HRESULT EncodeFrame::GetMetadataQueryWriter(IWICMetadataQueryWriter ** ppIMetadataQueryWriter) 404 | { 405 | TRACE1("(%p)\n", ppIMetadataQueryWriter); 406 | if (ppIMetadataQueryWriter == nullptr) 407 | return E_INVALIDARG; 408 | 409 | HRESULT result; 410 | 411 | //Create factory 412 | result = InitializeFactory(); 413 | if (FAILED(result)) 414 | return result; 415 | 416 | return componentFactory_->CreateQueryWriterFromBlockWriter(static_cast(&this->metadataBlockWriter_), ppIMetadataQueryWriter); 417 | } 418 | 419 | HRESULT EncodeFrame::MetadataBlockWriter::GetContainerFormat(GUID * pguidContainerFormat) 420 | { 421 | TRACE1("(%p)\n", pguidContainerFormat); 422 | if (pguidContainerFormat == nullptr) 423 | return E_INVALIDARG; 424 | *pguidContainerFormat = GUID_ContainerFormatFLIF; 425 | return S_OK; 426 | } 427 | 428 | HRESULT EncodeFrame::MetadataBlockWriter::GetCount(UINT * pcCount) 429 | { 430 | TRACE1("(%p)\n", pcCount); 431 | if (pcCount == nullptr) 432 | return E_INVALIDARG; 433 | *pcCount = metadataWriter_.size(); 434 | return S_OK; 435 | } 436 | 437 | HRESULT EncodeFrame::MetadataBlockWriter::GetEnumerator(IEnumUnknown ** ppIEnumMetadata) 438 | { 439 | TRACE1("(%p)\n", ppIEnumMetadata); 440 | return E_NOTIMPL; 441 | } 442 | 443 | HRESULT EncodeFrame::MetadataBlockWriter::InitializeFromBlockReader(IWICMetadataBlockReader * pIMDBlockReader) 444 | { 445 | TRACE1("(%p)\n", pIMDBlockReader); 446 | if (pIMDBlockReader == nullptr) 447 | return E_INVALIDARG; 448 | 449 | HRESULT result; 450 | 451 | UINT blockCount = 0; 452 | result = pIMDBlockReader->GetCount(&blockCount); 453 | if (FAILED(result)) 454 | return result; 455 | 456 | if (blockCount > 0) 457 | { 458 | //Create factory 459 | result = encodeFrame_.InitializeFactory(); 460 | if (FAILED(result)) 461 | return result; 462 | 463 | for (UINT i = 0; i < blockCount; ++i) 464 | { 465 | IWICMetadataReader* metadataReader; 466 | if (SUCCEEDED(pIMDBlockReader->GetReaderByIndex(i, &metadataReader))) 467 | { 468 | ComPtr metadataWriter; 469 | if (SUCCEEDED(encodeFrame_.componentFactory_->CreateMetadataWriterFromReader(metadataReader, NULL, metadataWriter.get_out_storage()))) 470 | { 471 | metadataWriter_.emplace_back(metadataWriter.new_ref()); 472 | } 473 | } 474 | } 475 | } 476 | return S_OK; 477 | } 478 | 479 | HRESULT EncodeFrame::MetadataBlockWriter::GetReaderByIndex(UINT nIndex, IWICMetadataReader ** ppIMetadataReader) 480 | { 481 | TRACE2("(%d, %p)\n", nIndex, ppIMetadataReader); 482 | if (ppIMetadataReader == nullptr) 483 | return E_INVALIDARG; 484 | if (nIndex >= metadataWriter_.size()) 485 | return E_INVALIDARG; 486 | *ppIMetadataReader = static_cast(metadataWriter_[nIndex].new_ref()); 487 | return S_OK; 488 | } 489 | 490 | HRESULT EncodeFrame::MetadataBlockWriter::GetWriterByIndex(UINT nIndex, IWICMetadataWriter ** ppIMetadataWriter) 491 | { 492 | TRACE2("(%d, %p)\n", nIndex, ppIMetadataWriter); 493 | if (ppIMetadataWriter == nullptr) 494 | return E_INVALIDARG; 495 | if (nIndex >= metadataWriter_.size()) 496 | return E_INVALIDARG; 497 | *ppIMetadataWriter = metadataWriter_[nIndex].new_ref(); 498 | return S_OK; 499 | } 500 | 501 | HRESULT EncodeFrame::MetadataBlockWriter::AddWriter(IWICMetadataWriter * pIMetadataWriter) 502 | { 503 | TRACE1("(%p)\n", pIMetadataWriter); 504 | if (pIMetadataWriter == nullptr) 505 | return E_INVALIDARG; 506 | metadataWriter_.emplace_back(pIMetadataWriter); 507 | return S_OK; 508 | } 509 | 510 | HRESULT EncodeFrame::MetadataBlockWriter::SetWriterByIndex(UINT nIndex, IWICMetadataWriter * pIMetadataWriter) 511 | { 512 | TRACE2("(%d, %p)\n", nIndex, pIMetadataWriter); 513 | if (pIMetadataWriter == nullptr) 514 | return E_INVALIDARG; 515 | if (nIndex >= metadataWriter_.size()) 516 | return E_INVALIDARG; 517 | metadataWriter_[nIndex].reset(pIMetadataWriter); 518 | return S_OK; 519 | } 520 | 521 | HRESULT EncodeFrame::MetadataBlockWriter::RemoveWriterByIndex(UINT nIndex) 522 | { 523 | TRACE1("(%d)\n", nIndex); 524 | if (nIndex >= metadataWriter_.size()) 525 | return E_INVALIDARG; 526 | //metadataWriter_.erase(metadataWriter_.begin() + nIndex); 527 | return S_OK; 528 | } 529 | 530 | 531 | static 532 | HRESULT SaveMetadata(IWICMetadataReader* reader, 533 | const std::string metadataName, 534 | std::deque>& metadatas) 535 | { 536 | ComPtr persistStream; 537 | if (SUCCEEDED(reader->QueryInterface(persistStream.get_out_storage()))) 538 | { 539 | IStream* stream = SHCreateMemStream(nullptr, 0); 540 | if (SUCCEEDED(persistStream->SaveEx(stream, WICMetadataCreationAllowUnknown | WICPersistOptionDefault, FALSE))) { 541 | // Allocates enough memeory for the content. 542 | STATSTG ssStreamData = {}; 543 | if (SUCCEEDED(stream->Stat(&ssStreamData, STATFLAG_NONAME))) { 544 | SIZE_T cbSize = ssStreamData.cbSize.LowPart; 545 | std::shared_ptr metadata(new (std::nothrow) Metadata(metadataName, cbSize)); 546 | if (!metadata->Buffer) 547 | return E_OUTOFMEMORY; 548 | 549 | // Copies the content from the stream to the buffer. 550 | LARGE_INTEGER position; 551 | position.QuadPart = 0; 552 | if (SUCCEEDED((stream->Seek(position, STREAM_SEEK_SET, NULL)))) { 553 | ULONG cbRead; 554 | if (SUCCEEDED(stream->Read(metadata->Buffer, cbSize, &cbRead))) { 555 | metadatas.emplace_back(metadata); 556 | } 557 | } 558 | } 559 | } 560 | } 561 | return S_OK; 562 | } 563 | 564 | static 565 | void ReadMetadataReqursive(IWICMetadataReader* reader, std::deque>& metadatas) 566 | { 567 | HRESULT result = S_OK; 568 | 569 | GUID metadataFormat; 570 | if (SUCCEEDED(reader->GetMetadataFormat(&metadataFormat))) 571 | { 572 | if (IsEqualGUID(metadataFormat, GUID_MetadataFormatExif)) 573 | { 574 | SaveMetadata(reader, "eXif", metadatas); 575 | return; 576 | } 577 | else if (IsEqualGUID(metadataFormat, GUID_MetadataFormatXMP)) { 578 | SaveMetadata(reader, "eXmp", metadatas); 579 | return; 580 | } 581 | else if (IsEqualGUID(metadataFormat, GUID_MetadataFormatChunkiCCP)) 582 | { 583 | SaveMetadata(reader, "iCCP", metadatas); 584 | return; 585 | } 586 | } 587 | 588 | UINT count = 0; 589 | result = reader->GetCount(&count); 590 | if (FAILED(result)) 591 | return; 592 | for (UINT i = 0; i < count; ++i) 593 | { 594 | PROPVARIANT id, value; 595 | 596 | PropVariantInit(&id); 597 | PropVariantInit(&value); 598 | result = reader->GetValueByIndex(i, nullptr, &id, &value); 599 | if (SUCCEEDED(result)) { 600 | if (VT_UNKNOWN == value.vt) 601 | { 602 | ComPtr subReader; 603 | result = value.punkVal->QueryInterface(subReader.get_out_storage()); 604 | if (SUCCEEDED(result)) 605 | { 606 | ReadMetadataReqursive(subReader.get(), metadatas); 607 | } 608 | } 609 | PropVariantClear(&id); 610 | PropVariantClear(&value); 611 | } 612 | } 613 | } 614 | 615 | HRESULT EncodeFrame::MetadataBlockWriter::GetMetadatas(std::deque>& metadatas) 616 | { 617 | TRACE("()\n"); 618 | for (int i = 0; i < metadataWriter_.size(); ++i) 619 | { 620 | ReadMetadataReqursive(metadataWriter_[i].get(), metadatas); 621 | } 622 | return S_OK; 623 | } 624 | -------------------------------------------------------------------------------- /FlifWICCodec/encode_frame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "utils.h" 8 | #include "encode_container.h" 9 | 10 | class EncodeFrame : public ComObjectBase { 11 | public: 12 | explicit EncodeFrame(EncodeContainer* container); 13 | ~EncodeFrame(); 14 | 15 | // Inherited via IUnknown: 16 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override; 17 | ULONG STDMETHODCALLTYPE AddRef() { return ComObjectBase::AddRef(); } 18 | ULONG STDMETHODCALLTYPE Release() { return ComObjectBase::Release(); } 19 | // Inherited via IWICBitmapFrameEncode 20 | HRESULT STDMETHODCALLTYPE Initialize(IPropertyBag2 * pIEncoderOptions) override; 21 | HRESULT STDMETHODCALLTYPE SetSize(UINT uiWidth, UINT uiHeight) override; 22 | HRESULT STDMETHODCALLTYPE SetResolution(double dpiX, double dpiY) override; 23 | HRESULT STDMETHODCALLTYPE SetPixelFormat(WICPixelFormatGUID * pPixelFormat) override; 24 | HRESULT STDMETHODCALLTYPE SetColorContexts(UINT cCount, IWICColorContext ** ppIColorContext) override; 25 | HRESULT STDMETHODCALLTYPE SetPalette(IWICPalette * pIPalette) override; 26 | HRESULT STDMETHODCALLTYPE SetThumbnail(IWICBitmapSource * pIThumbnail) override; 27 | HRESULT STDMETHODCALLTYPE WritePixels(UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE * pbPixels) override; 28 | HRESULT STDMETHODCALLTYPE WriteSource(IWICBitmapSource * pIBitmapSource, WICRect * prc) override; 29 | HRESULT STDMETHODCALLTYPE Commit(void) override; 30 | HRESULT STDMETHODCALLTYPE GetMetadataQueryWriter(IWICMetadataQueryWriter ** ppIMetadataQueryWriter) override; 31 | 32 | private: 33 | class MetadataBlockWriter : public IWICMetadataBlockWriter { 34 | public: 35 | MetadataBlockWriter(EncodeFrame& encodeFrame) : encodeFrame_(encodeFrame) {} 36 | // Inherited via IUnknown: 37 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override { return encodeFrame_.QueryInterface(riid, ppvObject); }; 38 | ULONG STDMETHODCALLTYPE AddRef() override { return encodeFrame_.AddRef(); } 39 | ULONG STDMETHODCALLTYPE Release() override { return encodeFrame_.Release(); } 40 | // Inherited via IWICMetadataBlockWriter 41 | HRESULT STDMETHODCALLTYPE GetContainerFormat(GUID * pguidContainerFormat) override; 42 | HRESULT STDMETHODCALLTYPE GetCount(UINT * pcCount) override; 43 | HRESULT STDMETHODCALLTYPE GetReaderByIndex(UINT nIndex, IWICMetadataReader ** ppIMetadataReader) override; 44 | HRESULT STDMETHODCALLTYPE GetEnumerator(IEnumUnknown ** ppIEnumMetadata) override; 45 | HRESULT STDMETHODCALLTYPE InitializeFromBlockReader(IWICMetadataBlockReader * pIMDBlockReader) override; 46 | HRESULT STDMETHODCALLTYPE GetWriterByIndex(UINT nIndex, IWICMetadataWriter ** ppIMetadataWriter) override; 47 | HRESULT STDMETHODCALLTYPE AddWriter(IWICMetadataWriter * pIMetadataWriter) override; 48 | HRESULT STDMETHODCALLTYPE SetWriterByIndex(UINT nIndex, IWICMetadataWriter * pIMetadataWriter) override; 49 | HRESULT STDMETHODCALLTYPE RemoveWriterByIndex(UINT nIndex) override; 50 | HRESULT GetMetadatas(std::deque>& metadatas); 51 | private: 52 | EncodeFrame& encodeFrame_; 53 | std::deque> metadataWriter_; 54 | }; 55 | 56 | // No copy and assign. 57 | EncodeFrame(const EncodeFrame&) = delete; 58 | void operator=(const EncodeFrame&) = delete; 59 | HRESULT InitializeFactory(); 60 | 61 | EncodeContainer* container_; 62 | std::shared_ptr frame_; 63 | AnimationInformation animation_information_; 64 | MetadataBlockWriter metadataBlockWriter_; 65 | ComPtr factory_; 66 | ComPtr componentFactory_; 67 | CRITICAL_SECTION cs_; 68 | }; 69 | 70 | -------------------------------------------------------------------------------- /FlifWICCodec/flif_wic_codec.def: -------------------------------------------------------------------------------- 1 | LIBRARY FLIFWICCodec 2 | EXPORTS 3 | DllCanUnloadNow PRIVATE 4 | DllGetClassObject PRIVATE 5 | DllRegisterServer PRIVATE 6 | DllUnregisterServer PRIVATE 7 | -------------------------------------------------------------------------------- /FlifWICCodec/flif_wic_codec.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peirick/FlifWICCodec/e42164e90ec300ae7396b6f06365ae0d7dcb651b/FlifWICCodec/flif_wic_codec.rc -------------------------------------------------------------------------------- /FlifWICCodec/metadata_store.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "metadata_store.h" 4 | #include "decode_frame.h" 5 | 6 | 7 | struct SupportedMetadata { 8 | SupportedMetadata(const PROPERTYKEY& key, LPCWSTR photo_metadata_policy, LPCWSTR xmp_metadata_query, bool is_writable) 9 | : key(key), 10 | photoMetadataPolicy(photo_metadata_policy), 11 | xmpMetadataQuery(xmp_metadata_query), 12 | isWritable(is_writable) 13 | { 14 | } 15 | 16 | const PROPERTYKEY& key; 17 | LPCWSTR photoMetadataPolicy; 18 | LPCWSTR xmpMetadataQuery; 19 | //LPCWSTR exifMetadataQuery; 20 | const bool isWritable; 21 | }; 22 | 23 | static const 24 | SupportedMetadata supported_metadatas[] = { 25 | // Using "Photo Metadata Policies" of Jpeg container. 26 | // See also DecodeFrame::MetadataBlockReader::GetContainerFormat 27 | { PKEY_Title, L"System.Title", L"/xmp/dc:title", false }, 28 | { PKEY_Copyright, L"System.Copyright", L"/xmp/dc:rights", false }, 29 | { PKEY_Author, L"System.Author", L"/xmp/dc:creator", false }, 30 | { PKEY_Subject, L"System.Subject", L"/ifd/{ushort=40095} ", false }, 31 | { PKEY_Image_BitDepth, nullptr, nullptr, false }, 32 | { PKEY_Image_HorizontalSize, nullptr, nullptr, false }, 33 | { PKEY_Image_VerticalSize, nullptr, nullptr, false }, 34 | { PKEY_Image_Dimensions, nullptr, nullptr, false }, 35 | { PKEY_Rating, L"System.Rating", L"/xmp/xmp:Rating", false }, 36 | { PKEY_Photo_CameraModel, L"System.Photo.CameraModel", L"/xmp/tiff:Model", false }, 37 | { PKEY_Photo_CameraManufacturer, L"System.Photo.CameraManufacturer", L"/xmp/tiff:Make", false }, 38 | { PKEY_Photo_CameraSerialNumber, L"System.Photo.CameraSerialNumber", L"/xmp/MicrosoftPhoto:CameraSerialNumber", false }, 39 | 40 | { PKEY_Photo_Aperture, L"System.Photo.Aperture", L"/xmp/exif:ApertureValue", false }, 41 | { PKEY_Photo_Brightness, L"System.Photo.Brightness", L"/xmp/exif:BrightnessValue", false }, 42 | { PKEY_Photo_Contrast, L"System.Photo.Contrast", L"/xmp/exif:Contrast", false }, 43 | { PKEY_Photo_DateTaken, L"System.Photo.DateTaken", L"/xmp/xmp:CreateDate", false }, 44 | { PKEY_Photo_DigitalZoom, L"System.Photo.DigitalZoom", L"/xmp/exif:DigitalZoomRatio", false }, 45 | { PKEY_Photo_EXIFVersion, L"System.Photo.EXIFVersion", L"/xmp/exif:ExifVersion", false }, 46 | { PKEY_Photo_ExposureBias, L"System.Photo.ExposureBias", L"/xmp/exif:ExposureBiasValue", false }, 47 | { PKEY_Photo_ExposureTime, L"System.Photo.ExposureTime", L"/xmp/exif:ExposureTime", false }, 48 | 49 | { PKEY_GPS_Altitude, L"System.GPS.Altitude", L"/xmp/exif:GPSAltitude", false }, 50 | { PKEY_GPS_Latitude, L"System.GPS.Latitude", L"/xmp/exif:GPSLatitude", false }, 51 | { PKEY_GPS_Longitude, L"System.GPS.Longitude" , L"/xmp/exif:GPSLongitude", false }, 52 | }; 53 | 54 | MetadataStore::MetadataStore() 55 | : initializeWithStream_(*this) 56 | , propertyStoreCapabilities_(*this) 57 | { 58 | TRACE("()\n"); 59 | } 60 | 61 | MetadataStore::~MetadataStore() 62 | { 63 | TRACE("()\n"); 64 | } 65 | 66 | HRESULT MetadataStore::QueryInterface(REFIID riid, void ** ppvObject) 67 | { 68 | TRACE2("(%s, %p)\n", debugstr_guid(riid), ppvObject); 69 | 70 | if (ppvObject == nullptr) 71 | return E_INVALIDARG; 72 | *ppvObject = nullptr; 73 | 74 | if (IsEqualGUID(riid, IID_IUnknown) || 75 | IsEqualGUID(riid, IID_IPropertyStore)) 76 | { 77 | this->AddRef(); 78 | *ppvObject = static_cast(this); 79 | return S_OK; 80 | } 81 | 82 | if (IsEqualGUID(riid, IID_IInitializeWithStream)) 83 | { 84 | this->AddRef(); 85 | *ppvObject = static_cast(&this->initializeWithStream_); 86 | return S_OK; 87 | } 88 | 89 | if (IsEqualGUID(riid, IID_IPropertyStoreCapabilities)) 90 | { 91 | this->AddRef(); 92 | *ppvObject = static_cast(&this->propertyStoreCapabilities_); 93 | return S_OK; 94 | } 95 | 96 | return E_NOINTERFACE; 97 | } 98 | 99 | HRESULT MetadataStore::GetCount(DWORD * cProps) 100 | { 101 | TRACE1("(%p)\n", cProps); 102 | return propertyStoreCache_.get() ? propertyStoreCache_->GetCount(cProps) : E_UNEXPECTED; 103 | } 104 | 105 | HRESULT MetadataStore::GetAt(DWORD iProp, PROPERTYKEY * pkey) 106 | { 107 | TRACE2("(%d %p)\n", iProp, pkey); 108 | return propertyStoreCache_.get() ? propertyStoreCache_->GetAt(iProp, pkey) : E_UNEXPECTED; 109 | } 110 | 111 | HRESULT MetadataStore::GetValue(REFPROPERTYKEY key, PROPVARIANT * pv) 112 | { 113 | TRACE2("(%s, %p)\n", debugstr_guid(key.fmtid), pv); 114 | return propertyStoreCache_.get() ? propertyStoreCache_->GetValue(key, pv) : E_UNEXPECTED; 115 | } 116 | 117 | HRESULT MetadataStore::SetValue(REFPROPERTYKEY key, REFPROPVARIANT propvar) 118 | { 119 | TRACE2("(%s, %ls)\n", debugstr_guid(key.fmtid), debugstr_var(propvar)); 120 | return propertyStoreCache_.get() ? propertyStoreCache_->SetValue(key, propvar) : E_UNEXPECTED; 121 | } 122 | 123 | HRESULT MetadataStore::Commit(void) 124 | { 125 | TRACE("()\n"); 126 | return S_OK; 127 | } 128 | 129 | HRESULT MetadataStore::InitializeWithStream::Initialize(IStream * pstream, DWORD grfMode) 130 | { 131 | TRACE2("(%p, %d)\n", pstream, grfMode); 132 | HRESULT result; 133 | result = PSCreateMemoryPropertyStore(IID_PPV_ARGS(metadataStore_.propertyStoreCache_.get_out_storage())); 134 | if (FAILED(result)) 135 | return result; 136 | 137 | DecodeContainer container; 138 | result = container.Initialize(pstream, WICDecodeMetadataCacheOnDemand); 139 | if (FAILED(result)) 140 | return result; 141 | 142 | PROPVARIANT propvar; 143 | PropVariantInit(&propvar); 144 | 145 | // bitdepth 146 | InitPropVariantFromUInt32(container.GetBitDepth(), &propvar); 147 | metadataStore_.propertyStoreCache_->SetValueAndState(PKEY_Image_BitDepth, &propvar, PSC_NORMAL); 148 | PropVariantClear(&propvar); 149 | 150 | // width 151 | ULONG width = container.GetWidth(); 152 | InitPropVariantFromUInt32(width, &propvar); 153 | metadataStore_.propertyStoreCache_->SetValueAndState(PKEY_Image_HorizontalSize, &propvar, PSC_NORMAL); 154 | PropVariantClear(&propvar); 155 | 156 | // height 157 | ULONG height = container.GetHeight(); 158 | InitPropVariantFromUInt32(height, &propvar); 159 | metadataStore_.propertyStoreCache_->SetValueAndState(PKEY_Image_VerticalSize, &propvar, PSC_NORMAL); 160 | PropVariantClear(&propvar); 161 | 162 | // dimensions 163 | WCHAR buffer[64] = {}; 164 | swprintf_s(buffer, L"%u \u00D7 %u", width, height); 165 | InitPropVariantFromString(buffer, &propvar); 166 | metadataStore_.propertyStoreCache_->SetValueAndState(PKEY_Image_Dimensions, &propvar, PSC_NORMAL); 167 | PropVariantClear(&propvar); 168 | 169 | 170 | ComPtr queryReader; 171 | result = container.GetMetadataQueryReader(queryReader.get_out_storage()); 172 | if (FAILED(result)) 173 | return result; 174 | for (const SupportedMetadata& supportedMetadata : supported_metadatas) 175 | { 176 | if (supportedMetadata.photoMetadataPolicy) 177 | { 178 | if (SUCCEEDED(queryReader->GetMetadataByName(supportedMetadata.photoMetadataPolicy, &propvar)) 179 | && (propvar.vt != VT_EMPTY)) 180 | { 181 | result = metadataStore_.propertyStoreCache_->SetValueAndState(supportedMetadata.key, &propvar, PSC_NORMAL); 182 | } 183 | PropVariantClear(&propvar); 184 | } 185 | } 186 | return S_OK; 187 | } 188 | 189 | HRESULT MetadataStore::PropertyStoreCapabilities::IsPropertyWritable(REFPROPERTYKEY key) 190 | { 191 | TRACE1("(%s)\n", debugstr_guid(key.fmtid)); 192 | for (const SupportedMetadata& supportedMetadata : supported_metadatas) 193 | { 194 | if (IsEqualGUID(supportedMetadata.key.fmtid, key.fmtid)) 195 | { 196 | return supportedMetadata.isWritable 197 | ? S_OK 198 | : S_FALSE; 199 | } 200 | } 201 | return S_FALSE; 202 | } 203 | -------------------------------------------------------------------------------- /FlifWICCodec/metadata_store.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "decode_container.h" 9 | #include "utils.h" 10 | 11 | class MetadataStore : public ComObjectBase { 12 | public: 13 | explicit MetadataStore(); 14 | ~MetadataStore(); 15 | // Inherited via IUnknown: 16 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override; 17 | ULONG STDMETHODCALLTYPE AddRef() override { return ComObjectBase::AddRef(); } 18 | ULONG STDMETHODCALLTYPE Release() override { return ComObjectBase::Release(); } 19 | // Inherited via ComObjectBase 20 | HRESULT STDMETHODCALLTYPE GetCount(DWORD * cProps) override; 21 | HRESULT STDMETHODCALLTYPE GetAt(DWORD iProp, PROPERTYKEY * pkey) override; 22 | HRESULT STDMETHODCALLTYPE GetValue(REFPROPERTYKEY key, PROPVARIANT * pv) override; 23 | HRESULT STDMETHODCALLTYPE SetValue(REFPROPERTYKEY key, REFPROPVARIANT propvar) override; 24 | HRESULT STDMETHODCALLTYPE Commit(void) override; 25 | private: 26 | class InitializeWithStream : public IInitializeWithStream 27 | { 28 | public: 29 | InitializeWithStream(MetadataStore& metadataStore) : metadataStore_(metadataStore) { InitializeCriticalSection(&cs_); } 30 | ~InitializeWithStream() { DeleteCriticalSection(&cs_); } 31 | // Inherited via IUnknown: 32 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override { return metadataStore_.QueryInterface(riid, ppvObject); }; 33 | ULONG STDMETHODCALLTYPE AddRef() override { return metadataStore_.AddRef(); } 34 | ULONG STDMETHODCALLTYPE Release() override { return metadataStore_.Release(); } 35 | // Inherited via IInitializeWithStream 36 | HRESULT STDMETHODCALLTYPE Initialize(IStream * pstream, DWORD grfMode) override; 37 | private: 38 | MetadataStore& metadataStore_; 39 | CRITICAL_SECTION cs_; 40 | }; 41 | 42 | class PropertyStoreCapabilities : public IPropertyStoreCapabilities 43 | { 44 | public: 45 | PropertyStoreCapabilities(MetadataStore& metadataStore) : metadataStore_(metadataStore) {} 46 | // Inherited via IUnknown: 47 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override { return metadataStore_.QueryInterface(riid, ppvObject); }; 48 | ULONG STDMETHODCALLTYPE AddRef() override { return metadataStore_.AddRef(); } 49 | ULONG STDMETHODCALLTYPE Release() override { return metadataStore_.Release(); } 50 | // Inherited via IPropertyStoreCapabilities 51 | virtual HRESULT STDMETHODCALLTYPE IsPropertyWritable(REFPROPERTYKEY key) override; 52 | private: 53 | MetadataStore& metadataStore_; 54 | }; 55 | 56 | ComPtr propertyStoreCache_; 57 | InitializeWithStream initializeWithStream_; 58 | PropertyStoreCapabilities propertyStoreCapabilities_; 59 | }; -------------------------------------------------------------------------------- /FlifWICCodec/pixel_converter.cpp: -------------------------------------------------------------------------------- 1 | #include "pixel_converter.h" 2 | 3 | #pragma pack(push, 1) 4 | struct RGBA { 5 | unsigned char r, g, b, a; 6 | }; 7 | #pragma pack(pop) 8 | 9 | void CopyAllButTransparentPixelRGBA8(size_t width, const void* sourceRow, const void* destRow) { 10 | RGBA* src = (RGBA*)sourceRow; 11 | RGBA* dst = (RGBA*)destRow; 12 | for (size_t i = 0; i < width; ++i) { 13 | const RGBA temp = src[i]; 14 | //Don't copy fully transparent pixel 15 | if (temp.a == 0) 16 | continue; 17 | dst[i].r = temp.r; 18 | dst[i].g = temp.g; 19 | dst[i].b = temp.b; 20 | dst[i].a = temp.a; 21 | } 22 | } -------------------------------------------------------------------------------- /FlifWICCodec/pixel_converter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void CopyAllButTransparentPixelRGBA8(size_t width, const void* sourceRow, const void* destRow); -------------------------------------------------------------------------------- /FlifWICCodec/resids.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Used by flif_wic_codec.rc 3 | 4 | #pragma once 5 | 6 | #define IDS_MUI_FILE_TYPE_NAME 1025 // Referenced from the registry - file type. 7 | -------------------------------------------------------------------------------- /FlifWICCodec/stopwatch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef LARGE_INTEGER Stopwatch; 6 | 7 | static inline double StopwatchReadAndReset(Stopwatch* watch) { 8 | const LARGE_INTEGER old_value = *watch; 9 | LARGE_INTEGER freq; 10 | if (!QueryPerformanceCounter(watch)) 11 | return 0.0; 12 | if (!QueryPerformanceFrequency(&freq)) 13 | return 0.0; 14 | if (freq.QuadPart == 0) 15 | return 0.0; 16 | return (watch->QuadPart - old_value.QuadPart) / (double)freq.QuadPart; 17 | } 18 | -------------------------------------------------------------------------------- /FlifWICCodec/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /FlifWICCodec/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "dllmain.h" 8 | 9 | struct RawFrame { 10 | const uint32_t Width; 11 | const uint32_t Height; 12 | const uint32_t NumberComponents; 13 | const uint32_t Stride; 14 | const size_t BufferSize; 15 | uint8_t* const Buffer; 16 | 17 | RawFrame(uint32_t width, uint32_t height, uint32_t numberComponents, uint32_t stride) 18 | : Width(width), Height(height), NumberComponents(numberComponents), Stride(stride), 19 | BufferSize(stride*height), Buffer((uint8_t*)CoTaskMemAlloc(BufferSize)) 20 | { 21 | } 22 | ~RawFrame() 23 | { 24 | if (Buffer) { 25 | CoTaskMemFree(Buffer); 26 | } 27 | } 28 | private: 29 | // No copy and assign. 30 | RawFrame(const RawFrame&) = delete; 31 | void operator=(const RawFrame&) = delete; 32 | }; 33 | 34 | struct Metadata { 35 | std::string Chunkname; 36 | const size_t BufferSize; 37 | uint8_t* const Buffer; 38 | Metadata(std::string chunkname, size_t bufferSize) 39 | : Chunkname(chunkname), BufferSize(bufferSize), Buffer((uint8_t*)CoTaskMemAlloc(BufferSize)) 40 | { 41 | } 42 | ~Metadata() 43 | { 44 | if (Buffer) { 45 | CoTaskMemFree(Buffer); 46 | } 47 | } 48 | private: 49 | // No copy and assign. 50 | Metadata(const Metadata&) = delete; 51 | void operator=(const Metadata&) = delete; 52 | }; 53 | 54 | 55 | class SectionLock { 56 | public: 57 | SectionLock(CRITICAL_SECTION* cs) :cs_(cs) { EnterCriticalSection(cs_); } 58 | ~SectionLock() { LeaveCriticalSection(cs_); } 59 | private: 60 | CRITICAL_SECTION* cs_; 61 | // No copy and assign. 62 | SectionLock(const SectionLock&) = delete; 63 | void operator=(const SectionLock&) = delete; 64 | }; 65 | 66 | // A wrapper around a pointer to a COM object that does a Release on 67 | // descruction. T should be a subclass of IUnknown. 68 | template 69 | class ComPtr { 70 | public: 71 | ComPtr() :ptr_(NULL) { } 72 | // ptr should be already AddRef'ed for this reference. 73 | ComPtr(T* ptr) :ptr_(ptr) { } 74 | ~ComPtr() { if (ptr_) ptr_->Release(); } 75 | 76 | T* get() { return ptr_; } 77 | T* new_ref() { ptr_->AddRef(); return ptr_; } 78 | // new_ptr should be already AddRef'ed for this new reference. 79 | void reset(T* new_ptr) { if (ptr_ != NULL) ptr_->Release(); ptr_ = new_ptr; } 80 | // Allows to pass the the pointer as an 'out' parameter. If a non-NULL value 81 | // is written to it, it should be a valid pointer and already AddRef'ed for 82 | // this new reference. 83 | T** get_out_storage() { reset(NULL); return &ptr_; } 84 | T* operator->() { return ptr_; } 85 | T& operator*() { return *ptr_; } 86 | private: 87 | T* ptr_; 88 | 89 | // No copy and assign. 90 | ComPtr(const ComPtr&) = delete; 91 | void operator=(const ComPtr&) = delete; 92 | }; 93 | 94 | // Implements handling of object's and DLL's COM reference counts. 95 | // T should be a subinterface of IUnknown. Templating used to avoid unnecessary 96 | // mulitple inheritance. 97 | template 98 | class ComObjectBase : public T { 99 | public: 100 | ComObjectBase() { 101 | TRACE1("(%p)\n", this); 102 | InterlockedIncrement(&MAIN_nObjects); 103 | ref_count_ = 1; 104 | } 105 | virtual ~ComObjectBase() { 106 | TRACE1("(%p)\n", this); 107 | InterlockedDecrement(&MAIN_nObjects); 108 | } 109 | 110 | // IUnknown methods: 111 | virtual ULONG STDMETHODCALLTYPE AddRef() override { 112 | return InterlockedIncrement(&ref_count_); 113 | } 114 | 115 | virtual ULONG STDMETHODCALLTYPE Release() override { 116 | ULONG ret = InterlockedDecrement(&ref_count_); 117 | if (ret == 0) 118 | delete this; 119 | return ret; 120 | } 121 | protected: 122 | volatile ULONG ref_count_; 123 | }; 124 | 125 | // Can't use e.g., auto_ptr because it's using delete and not delete[]. 126 | class scoped_buffer { 127 | public: 128 | scoped_buffer(SIZE_T size) { ptr_ = (BYTE*)CoTaskMemAlloc(size); } 129 | ~scoped_buffer() { CoTaskMemFree(ptr_); } 130 | 131 | // Did the allocation succeed. 132 | bool alloc_failed() { return ptr_ == NULL; } 133 | BYTE* get() { assert(ptr_ != NULL); return ptr_; } 134 | private: 135 | // No copy and assign. 136 | scoped_buffer(const scoped_buffer&) = delete; 137 | void operator=(const scoped_buffer&) = delete; 138 | BYTE* ptr_; 139 | }; 140 | 141 | -------------------------------------------------------------------------------- /FlifWICCodec/uuid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Class ID of the decoder class (DecodeContainer) 4 | // {688C2007-3185-4BB1-8075-00BEE4657DE2} 5 | DEFINE_GUID(CLSID_FLIFWICDecoder, 6 | 0x688c2007, 0x3185, 0x4bb1, 0x80, 0x75, 0x0, 0xbe, 0xe4, 0x65, 0x7d, 0xe2); 7 | 8 | // GUID of the FLIF container format. 9 | // {CB7F4B31-8638-41B2-A7B6-E5CB230DD56B} 10 | DEFINE_GUID(GUID_ContainerFormatFLIF, 11 | 0xcb7f4b31, 0x8638, 0x41b2, 0xa7, 0xb6, 0xe5, 0xcb, 0x23, 0xd, 0xd5, 0x6b); 12 | 13 | // GUID used as the vendor of this DLL. 14 | // {DD10E737-3366-4703-B75F-F9C66DFED5EE} 15 | DEFINE_GUID(GUID_FLIFCodecVendor, 16 | 0xdd10e737, 0x3366, 0x4703, 0xb7, 0x5f, 0xf9, 0xc6, 0x6d, 0xfe, 0xd5, 0xee); 17 | 18 | // GUID for IPropertyStore implementation. 19 | // {30186A1B-E4D3-4781-B3A3-2F54E271ED5A} 20 | DEFINE_GUID(GUID_FLIFPropertyStore, 21 | 0x30186a1b, 0xe4d3, 0x4781, 0xb3, 0xa3, 0x2f, 0x54, 0xe2, 0x71, 0xed, 0x5a); 22 | 23 | // Class ID of the encoder class (EncodeContainer) 24 | // {9C21723C-6748-4BCA-AB32-C2BB7EE02471} 25 | DEFINE_GUID(CLSID_FLIFWICEncoder, 26 | 0x9c21723c, 0x6748, 0x4bca, 0xab, 0x32, 0xc2, 0xbb, 0x7e, 0xe0, 0x24, 0x71); 27 | -------------------------------------------------------------------------------- /FlifWICCodec/version.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PRODUCT_NAME "FLIF Codec for Windows" 4 | #define PRODUCT_COMPANY "Private open source build" 5 | 6 | #define FILE_VERSION_MAJOR 0 7 | #define FILE_VERSION_MINOR 1 8 | #define FILE_VERSION_MAJOR_STR "0" 9 | #define FILE_VERSION_MINOR_STR "1" 10 | #define PRODUCT_VERSION_MAJOR 0 11 | #define PRODUCT_VERSION_MINOR 1 12 | #define PRODUCT_VERSION_MAJOR_STR "0" 13 | #define PRODUCT_VERSION_MINOR_STR "1" 14 | 15 | #define FILE_VERSION_BUILD 0 16 | #define FILE_VERSION_BUILD_STR "0" 17 | #define PRODUCT_VERSION_BUILD 0 18 | #define PRODUCT_VERSION_BUILD_STR "0" 19 | 20 | // Builds with a set build id are considered non-private. 21 | #if FILE_VERSION_BUILD 22 | #define VER_PRIVATE 0 23 | #else 24 | #define VER_PRIVATE VS_FF_PRIVATEBUILD 25 | #endif 26 | 27 | #ifdef _DEBUG 28 | #define VER_DEBUG VS_FF_DEBUG 29 | #else 30 | #define VER_DEBUG 0 31 | #endif 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build status](https://ci.appveyor.com/api/projects/status/1nr6guwkvyxui3rt?svg=true)](https://ci.appveyor.com/project/peirick/flifwiccodec) 2 | 3 | # FLIF Windows Codec 4 | This plugin allows to **decode** and **encode** [FLIF](http://flif.com) files in Windows aplications using the Windows Imaging Component (WIC) API. That allows e.g., to see the files 5 | in Windows PhotoViewer and Windows Explorer. 6 | 7 | ## Build Instructions 8 | 1. Open Visual Studio 2015 and open FlifWICCodec.sln 9 | 2. Compile 10 | 11 | ## Installation 12 | 1. open an administrative command prompt 13 | 2. navigate to folder with the FlifWICCodec.dll 14 | 3. execute: 15 | ``` 16 | regsvr32 FlifWICCodec.dll 17 | ``` 18 | 19 | ## Uninstall 20 | 1. open an administrative command prompt 21 | 2. navigate to folder with the FlifWICCodec.dll 22 | 3. execute: 23 | ``` 24 | regsvr32 -u FlifWICCodec.dll 25 | ``` 26 | 27 | ## Used Repositories 28 | * [https://github.com/FLIF-hub/FLIF](https://github.com/FLIF-hub/FLIF) 29 | -------------------------------------------------------------------------------- /libflif/libflif.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | false 89 | false 90 | false 91 | false 92 | 93 | 94 | true 95 | true 96 | true 97 | true 98 | 99 | 100 | Default 101 | Default 102 | Default 103 | Default 104 | true 105 | true 106 | true 107 | true 108 | 109 | 110 | Default 111 | Default 112 | Default 113 | Default 114 | true 115 | true 116 | true 117 | true 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | {56871ADF-ABF5-414B-8872-BCF0E200B95D} 126 | Win32Proj 127 | libflif 128 | 8.1 129 | libflif 130 | 131 | 132 | 133 | StaticLibrary 134 | true 135 | v140 136 | Unicode 137 | 138 | 139 | StaticLibrary 140 | false 141 | v140 142 | true 143 | Unicode 144 | 145 | 146 | StaticLibrary 147 | true 148 | v140 149 | Unicode 150 | 151 | 152 | StaticLibrary 153 | false 154 | v140 155 | true 156 | Unicode 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | NotUsing 183 | Level3 184 | Disabled 185 | FLIF_USE_STB_IMAGE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) 186 | true 187 | MultiThreadedDebug 188 | Fast 189 | 190 | 191 | Windows 192 | 193 | 194 | 195 | 196 | NotUsing 197 | Level3 198 | Disabled 199 | FLIF_USE_STB_IMAGE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX;_DEBUG;_LIB;%(PreprocessorDefinitions) 200 | true 201 | MultiThreadedDebug 202 | Fast 203 | %(AdditionalOptions) 204 | 205 | 206 | Windows 207 | 208 | 209 | 210 | 211 | Level3 212 | NotUsing 213 | Full 214 | true 215 | true 216 | FLIF_USE_STB_IMAGE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) 217 | true 218 | MultiThreaded 219 | true 220 | Fast 221 | false 222 | true 223 | true 224 | 225 | 226 | Windows 227 | true 228 | true 229 | 230 | 231 | 232 | 233 | Level3 234 | NotUsing 235 | Full 236 | true 237 | true 238 | FLIF_USE_STB_IMAGE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX;NDEBUG;_LIB;%(PreprocessorDefinitions) 239 | true 240 | MultiThreaded 241 | true 242 | Fast 243 | false 244 | true 245 | true 246 | /Qpar-report:1 /Qvec-report:1 %(AdditionalOptions) 247 | 248 | 249 | Windows 250 | true 251 | true 252 | 253 | 254 | 255 | 256 | 257 | -------------------------------------------------------------------------------- /libflif/libflif.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {45d6fccd-46d8-4d0c-8e9c-59bde0f3fa56} 6 | 7 | 8 | {0e8f55d4-ac57-4cb7-97f6-35914f60c803} 9 | 10 | 11 | {e7722c4e-9787-4ac4-b175-b2505df06706} 12 | 13 | 14 | {6be9adf2-782a-493e-ad12-3955c989587e} 15 | 16 | 17 | {5aa93bd9-1709-4937-a867-7a5169a68230} 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | transform 31 | 32 | 33 | transform 34 | 35 | 36 | transform 37 | 38 | 39 | transform 40 | 41 | 42 | transform 43 | 44 | 45 | transform 46 | 47 | 48 | transform 49 | 50 | 51 | transform 52 | 53 | 54 | transform 55 | 56 | 57 | transform 58 | 59 | 60 | transform 61 | 62 | 63 | transform 64 | 65 | 66 | transform 67 | 68 | 69 | maniac 70 | 71 | 72 | maniac 73 | 74 | 75 | maniac 76 | 77 | 78 | maniac 79 | 80 | 81 | maniac 82 | 83 | 84 | maniac 85 | 86 | 87 | maniac 88 | 89 | 90 | maniac 91 | 92 | 93 | maniac 94 | 95 | 96 | image 97 | 98 | 99 | image 100 | 101 | 102 | image 103 | 104 | 105 | image 106 | 107 | 108 | image 109 | 110 | 111 | image 112 | 113 | 114 | image 115 | 116 | 117 | image 118 | 119 | 120 | image 121 | 122 | 123 | library 124 | 125 | 126 | library 127 | 128 | 129 | library 130 | 131 | 132 | library 133 | 134 | 135 | library 136 | 137 | 138 | library 139 | 140 | 141 | library 142 | 143 | 144 | library 145 | 146 | 147 | extern 148 | 149 | 150 | extern 151 | 152 | 153 | extern 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | transform 163 | 164 | 165 | maniac 166 | 167 | 168 | maniac 169 | 170 | 171 | maniac 172 | 173 | 174 | image 175 | 176 | 177 | image 178 | 179 | 180 | image 181 | 182 | 183 | image 184 | 185 | 186 | image 187 | 188 | 189 | image 190 | 191 | 192 | image 193 | 194 | 195 | image 196 | 197 | 198 | library 199 | 200 | 201 | library 202 | 203 | 204 | library 205 | 206 | 207 | library 208 | 209 | 210 | extern 211 | 212 | 213 | --------------------------------------------------------------------------------