1 /* 2 * Copyright 2009 Vincent Povirk for CodeWeavers 3 * Copyright 2016 Dmitry Timoshkov 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 18 */ 19 20 #include <stdarg.h> 21 22 #define COBJMACROS 23 24 #include "windef.h" 25 #include "winbase.h" 26 #include "objbase.h" 27 #include "shlwapi.h" 28 29 #include "wincodecs_private.h" 30 31 #include "wine/debug.h" 32 33 static inline USHORT read_ushort_be(BYTE* data) 34 { 35 return data[0] << 8 | data[1]; 36 } 37 38 static inline ULONG read_ulong_be(BYTE* data) 39 { 40 return data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; 41 } 42 43 static HRESULT LoadTextMetadata(IStream *stream, const GUID *preferred_vendor, 44 DWORD persist_options, MetadataItem **items, DWORD *item_count) 45 { 46 HRESULT hr; 47 BYTE type[4]; 48 BYTE *data; 49 ULONG data_size; 50 ULONG name_len, value_len; 51 BYTE *name_end_ptr; 52 LPSTR name, value; 53 MetadataItem *result; 54 55 hr = read_png_chunk(stream, type, &data, &data_size); 56 if (FAILED(hr)) return hr; 57 58 name_end_ptr = memchr(data, 0, data_size); 59 60 name_len = name_end_ptr - data; 61 62 if (!name_end_ptr || name_len > 79) 63 { 64 free(data); 65 return E_FAIL; 66 } 67 68 value_len = data_size - name_len - 1; 69 70 result = calloc(1, sizeof(MetadataItem)); 71 name = CoTaskMemAlloc(name_len + 1); 72 value = CoTaskMemAlloc(value_len + 1); 73 if (!result || !name || !value) 74 { 75 free(data); 76 free(result); 77 CoTaskMemFree(name); 78 CoTaskMemFree(value); 79 return E_OUTOFMEMORY; 80 } 81 82 PropVariantInit(&result[0].schema); 83 PropVariantInit(&result[0].id); 84 PropVariantInit(&result[0].value); 85 86 memcpy(name, data, name_len + 1); 87 memcpy(value, name_end_ptr + 1, value_len); 88 value[value_len] = 0; 89 90 result[0].id.vt = VT_LPSTR; 91 result[0].id.pszVal = name; 92 result[0].value.vt = VT_LPSTR; 93 result[0].value.pszVal = value; 94 95 *items = result; 96 *item_count = 1; 97 98 free(data); 99 100 return S_OK; 101 } 102 103 static const MetadataHandlerVtbl TextReader_Vtbl = { 104 0, 105 &CLSID_WICPngTextMetadataReader, 106 LoadTextMetadata 107 }; 108 109 HRESULT PngTextReader_CreateInstance(REFIID iid, void** ppv) 110 { 111 return MetadataReader_Create(&TextReader_Vtbl, iid, ppv); 112 } 113 114 static HRESULT LoadGamaMetadata(IStream *stream, const GUID *preferred_vendor, 115 DWORD persist_options, MetadataItem **items, DWORD *item_count) 116 { 117 HRESULT hr; 118 BYTE type[4]; 119 BYTE *data; 120 ULONG data_size; 121 ULONG gamma; 122 LPWSTR name; 123 MetadataItem *result; 124 125 hr = read_png_chunk(stream, type, &data, &data_size); 126 if (FAILED(hr)) return hr; 127 128 if (data_size < 4) 129 { 130 free(data); 131 return E_FAIL; 132 } 133 134 gamma = read_ulong_be(data); 135 136 free(data); 137 138 result = calloc(1, sizeof(MetadataItem)); 139 SHStrDupW(L"ImageGamma", &name); 140 if (!result || !name) 141 { 142 free(result); 143 CoTaskMemFree(name); 144 return E_OUTOFMEMORY; 145 } 146 147 PropVariantInit(&result[0].schema); 148 PropVariantInit(&result[0].id); 149 PropVariantInit(&result[0].value); 150 151 result[0].id.vt = VT_LPWSTR; 152 result[0].id.pwszVal = name; 153 result[0].value.vt = VT_UI4; 154 result[0].value.ulVal = gamma; 155 156 *items = result; 157 *item_count = 1; 158 159 return S_OK; 160 } 161 162 static const MetadataHandlerVtbl GamaReader_Vtbl = { 163 0, 164 &CLSID_WICPngGamaMetadataReader, 165 LoadGamaMetadata 166 }; 167 168 HRESULT PngGamaReader_CreateInstance(REFIID iid, void** ppv) 169 { 170 return MetadataReader_Create(&GamaReader_Vtbl, iid, ppv); 171 } 172 173 static HRESULT LoadChrmMetadata(IStream *stream, const GUID *preferred_vendor, 174 DWORD persist_options, MetadataItem **items, DWORD *item_count) 175 { 176 HRESULT hr; 177 BYTE type[4]; 178 BYTE *data; 179 ULONG data_size; 180 static const WCHAR names[8][12] = { 181 L"WhitePointX", 182 L"WhitePointY", 183 L"RedX", 184 L"RedY", 185 L"GreenX", 186 L"GreenY", 187 L"BlueX", 188 L"BlueY", 189 }; 190 LPWSTR dyn_names[8] = {0}; 191 MetadataItem *result; 192 int i; 193 194 hr = read_png_chunk(stream, type, &data, &data_size); 195 if (FAILED(hr)) return hr; 196 197 if (data_size < 32) 198 { 199 free(data); 200 return E_FAIL; 201 } 202 203 result = calloc(8, sizeof(MetadataItem)); 204 for (i=0; i<8; i++) 205 { 206 SHStrDupW(names[i], &dyn_names[i]); 207 if (!dyn_names[i]) break; 208 } 209 if (!result || i < 8) 210 { 211 free(result); 212 for (i=0; i<8; i++) 213 CoTaskMemFree(dyn_names[i]); 214 free(data); 215 return E_OUTOFMEMORY; 216 } 217 218 for (i=0; i<8; i++) 219 { 220 PropVariantInit(&result[i].schema); 221 222 PropVariantInit(&result[i].id); 223 result[i].id.vt = VT_LPWSTR; 224 result[i].id.pwszVal = dyn_names[i]; 225 226 PropVariantInit(&result[i].value); 227 result[i].value.vt = VT_UI4; 228 result[i].value.ulVal = read_ulong_be(&data[i*4]); 229 } 230 231 *items = result; 232 *item_count = 8; 233 234 free(data); 235 236 return S_OK; 237 } 238 239 static const MetadataHandlerVtbl ChrmReader_Vtbl = { 240 0, 241 &CLSID_WICPngChrmMetadataReader, 242 LoadChrmMetadata 243 }; 244 245 HRESULT PngChrmReader_CreateInstance(REFIID iid, void** ppv) 246 { 247 return MetadataReader_Create(&ChrmReader_Vtbl, iid, ppv); 248 } 249 250 static HRESULT LoadHistMetadata(IStream *stream, const GUID *preferred_vendor, 251 DWORD persist_options, MetadataItem **items, DWORD *item_count) 252 { 253 HRESULT hr; 254 BYTE type[4]; 255 BYTE *data; 256 ULONG data_size, element_count, i; 257 LPWSTR name; 258 MetadataItem *result; 259 USHORT *elements; 260 261 hr = read_png_chunk(stream, type, &data, &data_size); 262 if (FAILED(hr)) return hr; 263 264 element_count = data_size / 2; 265 elements = CoTaskMemAlloc(element_count * sizeof(USHORT)); 266 if (!elements) 267 { 268 free(data); 269 return E_OUTOFMEMORY; 270 } 271 for (i = 0; i < element_count; i++) 272 elements[i] = read_ushort_be(data + i * 2); 273 274 free(data); 275 276 result = calloc(1, sizeof(MetadataItem)); 277 SHStrDupW(L"Frequencies", &name); 278 if (!result || !name) { 279 free(result); 280 CoTaskMemFree(name); 281 CoTaskMemFree(elements); 282 return E_OUTOFMEMORY; 283 } 284 285 PropVariantInit(&result[0].schema); 286 PropVariantInit(&result[0].id); 287 PropVariantInit(&result[0].value); 288 289 result[0].id.vt = VT_LPWSTR; 290 result[0].id.pwszVal = name; 291 292 result[0].value.vt = VT_UI2|VT_VECTOR; 293 result[0].value.caui.cElems = element_count; 294 result[0].value.caui.pElems = elements; 295 296 *items = result; 297 *item_count = 1; 298 299 return S_OK; 300 } 301 302 static const MetadataHandlerVtbl HistReader_Vtbl = { 303 0, 304 &CLSID_WICPngHistMetadataReader, 305 LoadHistMetadata 306 }; 307 308 HRESULT PngHistReader_CreateInstance(REFIID iid, void** ppv) 309 { 310 return MetadataReader_Create(&HistReader_Vtbl, iid, ppv); 311 } 312 313 static HRESULT LoadTimeMetadata(IStream *stream, const GUID *preferred_vendor, 314 DWORD persist_options, MetadataItem **items, DWORD *item_count) 315 { 316 HRESULT hr; 317 BYTE type[4]; 318 BYTE *data; 319 ULONG data_size, i; 320 MetadataItem *result; 321 static const WCHAR *names[6] = 322 { 323 L"Year", 324 L"Month", 325 L"Day", 326 L"Hour", 327 L"Minute", 328 L"Second", 329 }; 330 LPWSTR id_values[6] = {0}; 331 332 333 hr = read_png_chunk(stream, type, &data, &data_size); 334 if (FAILED(hr)) return hr; 335 336 if (data_size != 7) 337 { 338 free(data); 339 return E_FAIL; 340 } 341 342 result = calloc(6, sizeof(MetadataItem)); 343 for (i = 0; i < 6; i++) 344 { 345 SHStrDupW(names[i], &id_values[i]); 346 if (!id_values[i]) break; 347 } 348 if (!result || i < 6) 349 { 350 free(result); 351 for (i = 0; i < 6; i++) 352 CoTaskMemFree(id_values[i]); 353 free(data); 354 return E_OUTOFMEMORY; 355 } 356 357 for (i = 0; i < 6; i++) 358 { 359 PropVariantInit(&result[i].schema); 360 PropVariantInit(&result[i].id); 361 PropVariantInit(&result[i].value); 362 363 result[i].id.vt = VT_LPWSTR; 364 result[i].id.pwszVal = id_values[i]; 365 } 366 367 result[0].value.vt = VT_UI2; 368 result[0].value.uiVal = read_ushort_be(data); 369 result[1].value.vt = VT_UI1; 370 result[1].value.bVal = data[2]; 371 result[2].value.vt = VT_UI1; 372 result[2].value.bVal = data[3]; 373 result[3].value.vt = VT_UI1; 374 result[3].value.bVal = data[4]; 375 result[4].value.vt = VT_UI1; 376 result[4].value.bVal = data[5]; 377 result[5].value.vt = VT_UI1; 378 result[5].value.bVal = data[6]; 379 380 *items = result; 381 *item_count = 6; 382 383 free(data); 384 385 return S_OK; 386 } 387 388 static const MetadataHandlerVtbl TimeReader_Vtbl = { 389 0, 390 &CLSID_WICPngTimeMetadataReader, 391 LoadTimeMetadata 392 }; 393 394 HRESULT PngTimeReader_CreateInstance(REFIID iid, void** ppv) 395 { 396 return MetadataReader_Create(&TimeReader_Vtbl, iid, ppv); 397 } 398 399 HRESULT PngDecoder_CreateInstance(REFIID iid, void** ppv) 400 { 401 HRESULT hr; 402 struct decoder *decoder; 403 struct decoder_info decoder_info; 404 405 hr = png_decoder_create(&decoder_info, &decoder); 406 407 if (SUCCEEDED(hr)) 408 hr = CommonDecoder_CreateInstance(decoder, &decoder_info, iid, ppv); 409 410 return hr; 411 } 412 413 HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv) 414 { 415 HRESULT hr; 416 struct encoder *encoder; 417 struct encoder_info encoder_info; 418 419 hr = png_encoder_create(&encoder_info, &encoder); 420 421 if (SUCCEEDED(hr)) 422 hr = CommonEncoder_CreateInstance(encoder, &encoder_info, iid, ppv); 423 424 return hr; 425 } 426