xref: /reactos/dll/win32/windowscodecs/pngformat.c (revision a6100169)
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