xref: /reactos/dll/win32/windowscodecs/converter.c (revision e08ae510)
1 /*
2  * Copyright 2009 Vincent Povirk
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 "config.h"
21 
22 #include <stdarg.h>
23 #include <math.h>
24 
25 #define COBJMACROS
26 
27 #include "windef.h"
28 #include "winbase.h"
29 #include "objbase.h"
30 
31 #include "wincodecs_private.h"
32 
33 #include "wine/heap.h"
34 #include "wine/debug.h"
35 
36 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
37 
38 struct FormatConverter;
39 
40 enum pixelformat {
41     format_1bppIndexed,
42     format_2bppIndexed,
43     format_4bppIndexed,
44     format_8bppIndexed,
45     format_BlackWhite,
46     format_2bppGray,
47     format_4bppGray,
48     format_8bppGray,
49     format_16bppGray,
50     format_16bppBGR555,
51     format_16bppBGR565,
52     format_16bppBGRA5551,
53     format_24bppBGR,
54     format_24bppRGB,
55     format_32bppGrayFloat,
56     format_32bppBGR,
57     format_32bppRGB,
58     format_32bppBGRA,
59     format_32bppRGBA,
60     format_32bppPBGRA,
61     format_32bppPRGBA,
62     format_48bppRGB,
63     format_64bppRGBA,
64     format_32bppCMYK,
65 };
66 
67 typedef HRESULT (*copyfunc)(struct FormatConverter *This, const WICRect *prc,
68     UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer, enum pixelformat source_format);
69 
70 struct pixelformatinfo {
71     enum pixelformat format;
72     const WICPixelFormatGUID *guid;
73     copyfunc copy_function;
74 };
75 
76 typedef struct FormatConverter {
77     IWICFormatConverter IWICFormatConverter_iface;
78     LONG ref;
79     IWICBitmapSource *source;
80     const struct pixelformatinfo *dst_format, *src_format;
81     WICBitmapDitherType dither;
82     double alpha_threshold;
83     IWICPalette *palette;
84     CRITICAL_SECTION lock; /* must be held when initialized */
85 } FormatConverter;
86 
87 /* https://www.w3.org/Graphics/Color/srgb */
88 #ifndef __REACTOS__
89 static inline float from_sRGB_component(float f)
90 {
91     if (f <= 0.04045f) return f / 12.92f;
92     return powf((f + 0.055f) / 1.055f, 2.4f);
93 }
94 #endif
95 
96 static inline float to_sRGB_component(float f)
97 {
98     if (f <= 0.0031308f) return 12.92f * f;
99     return 1.055f * powf(f, 1.0f/2.4f) - 0.055f;
100 }
101 
102 #if 0 /* FIXME: enable once needed */
103 static void from_sRGB(BYTE *bgr)
104 {
105     float r, g, b;
106 
107     r = bgr[2] / 255.0f;
108     g = bgr[1] / 255.0f;
109     b = bgr[0] / 255.0f;
110 
111     r = from_sRGB_component(r);
112     g = from_sRGB_component(g);
113     b = from_sRGB_component(b);
114 
115     bgr[2] = (BYTE)(r * 255.0f);
116     bgr[1] = (BYTE)(g * 255.0f);
117     bgr[0] = (BYTE)(b * 255.0f);
118 }
119 
120 static void to_sRGB(BYTE *bgr)
121 {
122     float r, g, b;
123 
124     r = bgr[2] / 255.0f;
125     g = bgr[1] / 255.0f;
126     b = bgr[0] / 255.0f;
127 
128     r = to_sRGB_component(r);
129     g = to_sRGB_component(g);
130     b = to_sRGB_component(b);
131 
132     bgr[2] = (BYTE)(r * 255.0f);
133     bgr[1] = (BYTE)(g * 255.0f);
134     bgr[0] = (BYTE)(b * 255.0f);
135 }
136 #endif
137 
138 static inline FormatConverter *impl_from_IWICFormatConverter(IWICFormatConverter *iface)
139 {
140     return CONTAINING_RECORD(iface, FormatConverter, IWICFormatConverter_iface);
141 }
142 
143 static HRESULT copypixels_to_32bppBGRA(struct FormatConverter *This, const WICRect *prc,
144     UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer, enum pixelformat source_format)
145 {
146     switch (source_format)
147     {
148     case format_1bppIndexed:
149     case format_BlackWhite:
150         if (prc)
151         {
152             HRESULT res;
153             INT x, y;
154             BYTE *srcdata;
155             UINT srcstride, srcdatasize;
156             const BYTE *srcrow;
157             const BYTE *srcbyte;
158             BYTE *dstrow;
159             DWORD *dstpixel;
160             WICColor colors[2];
161             IWICPalette *palette;
162             UINT actualcolors;
163 
164             res = PaletteImpl_Create(&palette);
165             if (FAILED(res)) return res;
166 
167             if (source_format == format_1bppIndexed)
168                 res = IWICBitmapSource_CopyPalette(This->source, palette);
169             else
170                 res = IWICPalette_InitializePredefined(palette, WICBitmapPaletteTypeFixedBW, FALSE);
171 
172             if (SUCCEEDED(res))
173                 res = IWICPalette_GetColors(palette, 2, colors, &actualcolors);
174 
175             IWICPalette_Release(palette);
176             if (FAILED(res)) return res;
177 
178             srcstride = (prc->Width+7)/8;
179             srcdatasize = srcstride * prc->Height;
180 
181             srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
182             if (!srcdata) return E_OUTOFMEMORY;
183 
184             res = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
185 
186             if (SUCCEEDED(res))
187             {
188                 srcrow = srcdata;
189                 dstrow = pbBuffer;
190                 for (y=0; y<prc->Height; y++) {
191                     srcbyte = srcrow;
192                     dstpixel=(DWORD*)dstrow;
193                     for (x=0; x<prc->Width; x+=8) {
194                         BYTE srcval;
195                         srcval=*srcbyte++;
196                         *dstpixel++ = colors[srcval>>7&1];
197                         if (x+1 < prc->Width) *dstpixel++ = colors[srcval>>6&1];
198                         if (x+2 < prc->Width) *dstpixel++ = colors[srcval>>5&1];
199                         if (x+3 < prc->Width) *dstpixel++ = colors[srcval>>4&1];
200                         if (x+4 < prc->Width) *dstpixel++ = colors[srcval>>3&1];
201                         if (x+5 < prc->Width) *dstpixel++ = colors[srcval>>2&1];
202                         if (x+6 < prc->Width) *dstpixel++ = colors[srcval>>1&1];
203                         if (x+7 < prc->Width) *dstpixel++ = colors[srcval&1];
204                     }
205                     srcrow += srcstride;
206                     dstrow += cbStride;
207                 }
208             }
209 
210             HeapFree(GetProcessHeap(), 0, srcdata);
211 
212             return res;
213         }
214         return S_OK;
215     case format_2bppIndexed:
216     case format_2bppGray:
217         if (prc)
218         {
219             HRESULT res;
220             INT x, y;
221             BYTE *srcdata;
222             UINT srcstride, srcdatasize;
223             const BYTE *srcrow;
224             const BYTE *srcbyte;
225             BYTE *dstrow;
226             DWORD *dstpixel;
227             WICColor colors[4];
228             IWICPalette *palette;
229             UINT actualcolors;
230 
231             res = PaletteImpl_Create(&palette);
232             if (FAILED(res)) return res;
233 
234             if (source_format == format_2bppIndexed)
235                 res = IWICBitmapSource_CopyPalette(This->source, palette);
236             else
237                 res = IWICPalette_InitializePredefined(palette, WICBitmapPaletteTypeFixedGray4, FALSE);
238 
239             if (SUCCEEDED(res))
240                 res = IWICPalette_GetColors(palette, 4, colors, &actualcolors);
241 
242             IWICPalette_Release(palette);
243             if (FAILED(res)) return res;
244 
245             srcstride = (prc->Width+3)/4;
246             srcdatasize = srcstride * prc->Height;
247 
248             srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
249             if (!srcdata) return E_OUTOFMEMORY;
250 
251             res = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
252 
253             if (SUCCEEDED(res))
254             {
255                 srcrow = srcdata;
256                 dstrow = pbBuffer;
257                 for (y=0; y<prc->Height; y++) {
258                     srcbyte = srcrow;
259                     dstpixel=(DWORD*)dstrow;
260                     for (x=0; x<prc->Width; x+=4) {
261                         BYTE srcval;
262                         srcval=*srcbyte++;
263                         *dstpixel++ = colors[srcval>>6];
264                         if (x+1 < prc->Width) *dstpixel++ = colors[srcval>>4&0x3];
265                         if (x+2 < prc->Width) *dstpixel++ = colors[srcval>>2&0x3];
266                         if (x+3 < prc->Width) *dstpixel++ = colors[srcval&0x3];
267                     }
268                     srcrow += srcstride;
269                     dstrow += cbStride;
270                 }
271             }
272 
273             HeapFree(GetProcessHeap(), 0, srcdata);
274 
275             return res;
276         }
277         return S_OK;
278     case format_4bppIndexed:
279     case format_4bppGray:
280         if (prc)
281         {
282             HRESULT res;
283             INT x, y;
284             BYTE *srcdata;
285             UINT srcstride, srcdatasize;
286             const BYTE *srcrow;
287             const BYTE *srcbyte;
288             BYTE *dstrow;
289             DWORD *dstpixel;
290             WICColor colors[16];
291             IWICPalette *palette;
292             UINT actualcolors;
293 
294             res = PaletteImpl_Create(&palette);
295             if (FAILED(res)) return res;
296 
297             if (source_format == format_4bppIndexed)
298                 res = IWICBitmapSource_CopyPalette(This->source, palette);
299             else
300                 res = IWICPalette_InitializePredefined(palette, WICBitmapPaletteTypeFixedGray16, FALSE);
301 
302             if (SUCCEEDED(res))
303                 res = IWICPalette_GetColors(palette, 16, colors, &actualcolors);
304 
305             IWICPalette_Release(palette);
306             if (FAILED(res)) return res;
307 
308             srcstride = (prc->Width+1)/2;
309             srcdatasize = srcstride * prc->Height;
310 
311             srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
312             if (!srcdata) return E_OUTOFMEMORY;
313 
314             res = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
315 
316             if (SUCCEEDED(res))
317             {
318                 srcrow = srcdata;
319                 dstrow = pbBuffer;
320                 for (y=0; y<prc->Height; y++) {
321                     srcbyte = srcrow;
322                     dstpixel=(DWORD*)dstrow;
323                     for (x=0; x<prc->Width; x+=2) {
324                         BYTE srcval;
325                         srcval=*srcbyte++;
326                         *dstpixel++ = colors[srcval>>4];
327                         if (x+1 < prc->Width) *dstpixel++ = colors[srcval&0xf];
328                     }
329                     srcrow += srcstride;
330                     dstrow += cbStride;
331                 }
332             }
333 
334             HeapFree(GetProcessHeap(), 0, srcdata);
335 
336             return res;
337         }
338         return S_OK;
339     case format_8bppGray:
340         if (prc)
341         {
342             HRESULT res;
343             INT x, y;
344             BYTE *srcdata;
345             UINT srcstride, srcdatasize;
346             const BYTE *srcrow;
347             const BYTE *srcbyte;
348             BYTE *dstrow;
349             DWORD *dstpixel;
350 
351             srcstride = prc->Width;
352             srcdatasize = srcstride * prc->Height;
353 
354             srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
355             if (!srcdata) return E_OUTOFMEMORY;
356 
357             res = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
358 
359             if (SUCCEEDED(res))
360             {
361                 srcrow = srcdata;
362                 dstrow = pbBuffer;
363                 for (y=0; y<prc->Height; y++) {
364                     srcbyte = srcrow;
365                     dstpixel=(DWORD*)dstrow;
366                     for (x=0; x<prc->Width; x++)
367                     {
368                         *dstpixel++ = 0xff000000|(*srcbyte<<16)|(*srcbyte<<8)|*srcbyte;
369                         srcbyte++;
370                     }
371                     srcrow += srcstride;
372                     dstrow += cbStride;
373                 }
374             }
375 
376             HeapFree(GetProcessHeap(), 0, srcdata);
377 
378             return res;
379         }
380         return S_OK;
381     case format_8bppIndexed:
382         if (prc)
383         {
384             HRESULT res;
385             INT x, y;
386             BYTE *srcdata;
387             UINT srcstride, srcdatasize;
388             const BYTE *srcrow;
389             const BYTE *srcbyte;
390             BYTE *dstrow;
391             DWORD *dstpixel;
392             WICColor colors[256];
393             IWICPalette *palette;
394             UINT actualcolors;
395 
396             res = PaletteImpl_Create(&palette);
397             if (FAILED(res)) return res;
398 
399             res = IWICBitmapSource_CopyPalette(This->source, palette);
400             if (SUCCEEDED(res))
401                 res = IWICPalette_GetColors(palette, 256, colors, &actualcolors);
402 
403             IWICPalette_Release(palette);
404 
405             if (FAILED(res)) return res;
406 
407             srcstride = prc->Width;
408             srcdatasize = srcstride * prc->Height;
409 
410             srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
411             if (!srcdata) return E_OUTOFMEMORY;
412 
413             res = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
414 
415             if (SUCCEEDED(res))
416             {
417                 srcrow = srcdata;
418                 dstrow = pbBuffer;
419                 for (y=0; y<prc->Height; y++) {
420                     srcbyte = srcrow;
421                     dstpixel=(DWORD*)dstrow;
422                     for (x=0; x<prc->Width; x++)
423                         *dstpixel++ = colors[*srcbyte++];
424                     srcrow += srcstride;
425                     dstrow += cbStride;
426                 }
427             }
428 
429             HeapFree(GetProcessHeap(), 0, srcdata);
430 
431             return res;
432         }
433         return S_OK;
434     case format_16bppGray:
435         if (prc)
436         {
437             HRESULT res;
438             INT x, y;
439             BYTE *srcdata;
440             UINT srcstride, srcdatasize;
441             const BYTE *srcrow;
442             const BYTE *srcbyte;
443             BYTE *dstrow;
444             DWORD *dstpixel;
445 
446             srcstride = prc->Width * 2;
447             srcdatasize = srcstride * prc->Height;
448 
449             srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
450             if (!srcdata) return E_OUTOFMEMORY;
451 
452             res = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
453 
454             if (SUCCEEDED(res))
455             {
456                 srcrow = srcdata;
457                 dstrow = pbBuffer;
458                 for (y=0; y<prc->Height; y++) {
459                     srcbyte = srcrow;
460                     dstpixel=(DWORD*)dstrow;
461                     for (x=0; x<prc->Width; x++)
462                     {
463                         *dstpixel++ = 0xff000000|(*srcbyte<<16)|(*srcbyte<<8)|*srcbyte;
464                         srcbyte+=2;
465                     }
466                     srcrow += srcstride;
467                     dstrow += cbStride;
468                 }
469             }
470 
471             HeapFree(GetProcessHeap(), 0, srcdata);
472 
473             return res;
474         }
475         return S_OK;
476     case format_16bppBGR555:
477         if (prc)
478         {
479             HRESULT res;
480             INT x, y;
481             BYTE *srcdata;
482             UINT srcstride, srcdatasize;
483             const BYTE *srcrow;
484             const WORD *srcpixel;
485             BYTE *dstrow;
486             DWORD *dstpixel;
487 
488             srcstride = 2 * prc->Width;
489             srcdatasize = srcstride * prc->Height;
490 
491             srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
492             if (!srcdata) return E_OUTOFMEMORY;
493 
494             res = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
495 
496             if (SUCCEEDED(res))
497             {
498                 srcrow = srcdata;
499                 dstrow = pbBuffer;
500                 for (y=0; y<prc->Height; y++) {
501                     srcpixel=(const WORD*)srcrow;
502                     dstpixel=(DWORD*)dstrow;
503                     for (x=0; x<prc->Width; x++) {
504                         WORD srcval;
505                         srcval=*srcpixel++;
506                         *dstpixel++=0xff000000 | /* constant 255 alpha */
507                                     ((srcval << 9) & 0xf80000) | /* r */
508                                     ((srcval << 4) & 0x070000) | /* r - 3 bits */
509                                     ((srcval << 6) & 0x00f800) | /* g */
510                                     ((srcval << 1) & 0x000700) | /* g - 3 bits */
511                                     ((srcval << 3) & 0x0000f8) | /* b */
512                                     ((srcval >> 2) & 0x000007);  /* b - 3 bits */
513                     }
514                     srcrow += srcstride;
515                     dstrow += cbStride;
516                 }
517             }
518 
519             HeapFree(GetProcessHeap(), 0, srcdata);
520 
521             return res;
522         }
523         return S_OK;
524     case format_16bppBGR565:
525         if (prc)
526         {
527             HRESULT res;
528             INT x, y;
529             BYTE *srcdata;
530             UINT srcstride, srcdatasize;
531             const BYTE *srcrow;
532             const WORD *srcpixel;
533             BYTE *dstrow;
534             DWORD *dstpixel;
535 
536             srcstride = 2 * prc->Width;
537             srcdatasize = srcstride * prc->Height;
538 
539             srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
540             if (!srcdata) return E_OUTOFMEMORY;
541 
542             res = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
543 
544             if (SUCCEEDED(res))
545             {
546                 srcrow = srcdata;
547                 dstrow = pbBuffer;
548                 for (y=0; y<prc->Height; y++) {
549                     srcpixel=(const WORD*)srcrow;
550                     dstpixel=(DWORD*)dstrow;
551                     for (x=0; x<prc->Width; x++) {
552                         WORD srcval;
553                         srcval=*srcpixel++;
554                         *dstpixel++=0xff000000 | /* constant 255 alpha */
555                                     ((srcval << 8) & 0xf80000) | /* r */
556                                     ((srcval << 3) & 0x070000) | /* r - 3 bits */
557                                     ((srcval << 5) & 0x00fc00) | /* g */
558                                     ((srcval >> 1) & 0x000300) | /* g - 2 bits */
559                                     ((srcval << 3) & 0x0000f8) | /* b */
560                                     ((srcval >> 2) & 0x000007);  /* b - 3 bits */
561                     }
562                     srcrow += srcstride;
563                     dstrow += cbStride;
564                 }
565             }
566 
567             HeapFree(GetProcessHeap(), 0, srcdata);
568 
569             return res;
570         }
571         return S_OK;
572     case format_16bppBGRA5551:
573         if (prc)
574         {
575             HRESULT res;
576             INT x, y;
577             BYTE *srcdata;
578             UINT srcstride, srcdatasize;
579             const BYTE *srcrow;
580             const WORD *srcpixel;
581             BYTE *dstrow;
582             DWORD *dstpixel;
583 
584             srcstride = 2 * prc->Width;
585             srcdatasize = srcstride * prc->Height;
586 
587             srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
588             if (!srcdata) return E_OUTOFMEMORY;
589 
590             res = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
591 
592             if (SUCCEEDED(res))
593             {
594                 srcrow = srcdata;
595                 dstrow = pbBuffer;
596                 for (y=0; y<prc->Height; y++) {
597                     srcpixel=(const WORD*)srcrow;
598                     dstpixel=(DWORD*)dstrow;
599                     for (x=0; x<prc->Width; x++) {
600                         WORD srcval;
601                         srcval=*srcpixel++;
602                         *dstpixel++=((srcval & 0x8000) ? 0xff000000 : 0) | /* alpha */
603                                     ((srcval << 9) & 0xf80000) | /* r */
604                                     ((srcval << 4) & 0x070000) | /* r - 3 bits */
605                                     ((srcval << 6) & 0x00f800) | /* g */
606                                     ((srcval << 1) & 0x000700) | /* g - 3 bits */
607                                     ((srcval << 3) & 0x0000f8) | /* b */
608                                     ((srcval >> 2) & 0x000007);  /* b - 3 bits */
609                     }
610                     srcrow += srcstride;
611                     dstrow += cbStride;
612                 }
613             }
614 
615             HeapFree(GetProcessHeap(), 0, srcdata);
616 
617             return res;
618         }
619         return S_OK;
620     case format_24bppBGR:
621         if (prc)
622         {
623             HRESULT res;
624             INT x, y;
625             BYTE *srcdata;
626             UINT srcstride, srcdatasize;
627             const BYTE *srcrow;
628             const BYTE *srcpixel;
629             BYTE *dstrow;
630             BYTE *dstpixel;
631 
632             srcstride = 3 * prc->Width;
633             srcdatasize = srcstride * prc->Height;
634 
635             srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
636             if (!srcdata) return E_OUTOFMEMORY;
637 
638             res = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
639 
640             if (SUCCEEDED(res))
641             {
642                 srcrow = srcdata;
643                 dstrow = pbBuffer;
644                 for (y=0; y<prc->Height; y++) {
645                     srcpixel=srcrow;
646                     dstpixel=dstrow;
647                     for (x=0; x<prc->Width; x++) {
648                         *dstpixel++=*srcpixel++; /* blue */
649                         *dstpixel++=*srcpixel++; /* green */
650                         *dstpixel++=*srcpixel++; /* red */
651                         *dstpixel++=255; /* alpha */
652                     }
653                     srcrow += srcstride;
654                     dstrow += cbStride;
655                 }
656             }
657 
658             HeapFree(GetProcessHeap(), 0, srcdata);
659 
660             return res;
661         }
662         return S_OK;
663     case format_24bppRGB:
664         if (prc)
665         {
666             HRESULT res;
667             INT x, y;
668             BYTE *srcdata;
669             UINT srcstride, srcdatasize;
670             const BYTE *srcrow;
671             const BYTE *srcpixel;
672             BYTE *dstrow;
673             BYTE *dstpixel;
674             BYTE tmppixel[3];
675 
676             srcstride = 3 * prc->Width;
677             srcdatasize = srcstride * prc->Height;
678 
679             srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
680             if (!srcdata) return E_OUTOFMEMORY;
681 
682             res = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
683 
684             if (SUCCEEDED(res))
685             {
686                 srcrow = srcdata;
687                 dstrow = pbBuffer;
688                 for (y=0; y<prc->Height; y++) {
689                     srcpixel=srcrow;
690                     dstpixel=dstrow;
691                     for (x=0; x<prc->Width; x++) {
692                         tmppixel[0]=*srcpixel++; /* red */
693                         tmppixel[1]=*srcpixel++; /* green */
694                         tmppixel[2]=*srcpixel++; /* blue */
695 
696                         *dstpixel++=tmppixel[2]; /* blue */
697                         *dstpixel++=tmppixel[1]; /* green */
698                         *dstpixel++=tmppixel[0]; /* red */
699                         *dstpixel++=255; /* alpha */
700                     }
701                     srcrow += srcstride;
702                     dstrow += cbStride;
703                 }
704             }
705 
706             HeapFree(GetProcessHeap(), 0, srcdata);
707 
708             return res;
709         }
710         return S_OK;
711     case format_32bppBGR:
712         if (prc)
713         {
714             HRESULT res;
715             INT x, y;
716 
717             res = IWICBitmapSource_CopyPixels(This->source, prc, cbStride, cbBufferSize, pbBuffer);
718             if (FAILED(res)) return res;
719 
720             /* set all alpha values to 255 */
721             for (y=0; y<prc->Height; y++)
722                 for (x=0; x<prc->Width; x++)
723                     pbBuffer[cbStride*y+4*x+3] = 0xff;
724         }
725         return S_OK;
726     case format_32bppBGRA:
727         if (prc)
728             return IWICBitmapSource_CopyPixels(This->source, prc, cbStride, cbBufferSize, pbBuffer);
729         return S_OK;
730     case format_32bppPBGRA:
731         if (prc)
732         {
733             HRESULT res;
734             INT x, y;
735 
736             res = IWICBitmapSource_CopyPixels(This->source, prc, cbStride, cbBufferSize, pbBuffer);
737             if (FAILED(res)) return res;
738 
739             for (y=0; y<prc->Height; y++)
740                 for (x=0; x<prc->Width; x++)
741                 {
742                     BYTE alpha = pbBuffer[cbStride*y+4*x+3];
743                     if (alpha != 0 && alpha != 255)
744                     {
745                         pbBuffer[cbStride*y+4*x] = pbBuffer[cbStride*y+4*x] * 255 / alpha;
746                         pbBuffer[cbStride*y+4*x+1] = pbBuffer[cbStride*y+4*x+1] * 255 / alpha;
747                         pbBuffer[cbStride*y+4*x+2] = pbBuffer[cbStride*y+4*x+2] * 255 / alpha;
748                     }
749                 }
750         }
751         return S_OK;
752     case format_48bppRGB:
753         if (prc)
754         {
755             HRESULT res;
756             INT x, y;
757             BYTE *srcdata;
758             UINT srcstride, srcdatasize;
759             const BYTE *srcrow;
760             const BYTE *srcpixel;
761             BYTE *dstrow;
762             DWORD *dstpixel;
763 
764             srcstride = 6 * prc->Width;
765             srcdatasize = srcstride * prc->Height;
766 
767             srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
768             if (!srcdata) return E_OUTOFMEMORY;
769 
770             res = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
771 
772             if (SUCCEEDED(res))
773             {
774                 srcrow = srcdata;
775                 dstrow = pbBuffer;
776                 for (y=0; y<prc->Height; y++) {
777                     srcpixel=srcrow;
778                     dstpixel=(DWORD*)dstrow;
779                     for (x=0; x<prc->Width; x++) {
780                         BYTE red, green, blue;
781                         red = *srcpixel++; srcpixel++;
782                         green = *srcpixel++; srcpixel++;
783                         blue = *srcpixel++; srcpixel++;
784                         *dstpixel++=0xff000000|red<<16|green<<8|blue;
785                     }
786                     srcrow += srcstride;
787                     dstrow += cbStride;
788                 }
789             }
790 
791             HeapFree(GetProcessHeap(), 0, srcdata);
792 
793             return res;
794         }
795         return S_OK;
796     case format_64bppRGBA:
797         if (prc)
798         {
799             HRESULT res;
800             INT x, y;
801             BYTE *srcdata;
802             UINT srcstride, srcdatasize;
803             const BYTE *srcrow;
804             const BYTE *srcpixel;
805             BYTE *dstrow;
806             DWORD *dstpixel;
807 
808             srcstride = 8 * prc->Width;
809             srcdatasize = srcstride * prc->Height;
810 
811             srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
812             if (!srcdata) return E_OUTOFMEMORY;
813 
814             res = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
815 
816             if (SUCCEEDED(res))
817             {
818                 srcrow = srcdata;
819                 dstrow = pbBuffer;
820                 for (y=0; y<prc->Height; y++) {
821                     srcpixel=srcrow;
822                     dstpixel=(DWORD*)dstrow;
823                     for (x=0; x<prc->Width; x++) {
824                         BYTE red, green, blue, alpha;
825                         red = *srcpixel++; srcpixel++;
826                         green = *srcpixel++; srcpixel++;
827                         blue = *srcpixel++; srcpixel++;
828                         alpha = *srcpixel++; srcpixel++;
829                         *dstpixel++=alpha<<24|red<<16|green<<8|blue;
830                     }
831                     srcrow += srcstride;
832                     dstrow += cbStride;
833                 }
834             }
835 
836             HeapFree(GetProcessHeap(), 0, srcdata);
837 
838             return res;
839         }
840         return S_OK;
841     case format_32bppCMYK:
842         if (prc)
843         {
844             HRESULT res;
845             UINT x, y;
846 
847             res = IWICBitmapSource_CopyPixels(This->source, prc, cbStride, cbBufferSize, pbBuffer);
848             if (FAILED(res)) return res;
849 
850             for (y=0; y<prc->Height; y++)
851                 for (x=0; x<prc->Width; x++)
852                 {
853                     BYTE *pixel = pbBuffer+cbStride*y+4*x;
854                     BYTE c=pixel[0], m=pixel[1], y=pixel[2], k=pixel[3];
855                     pixel[0] = (255-y)*(255-k)/255; /* blue */
856                     pixel[1] = (255-m)*(255-k)/255; /* green */
857                     pixel[2] = (255-c)*(255-k)/255; /* red */
858                     pixel[3] = 255; /* alpha */
859                 }
860         }
861         return S_OK;
862     default:
863         return WINCODEC_ERR_UNSUPPORTEDOPERATION;
864     }
865 }
866 
867 static HRESULT copypixels_to_32bppRGBA(struct FormatConverter *This, const WICRect *prc,
868     UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer, enum pixelformat source_format)
869 {
870     HRESULT hr;
871 
872     switch (source_format)
873     {
874     case format_32bppRGB:
875     case format_32bppRGBA:
876     case format_32bppPRGBA:
877         if (prc)
878             return IWICBitmapSource_CopyPixels(This->source, prc, cbStride, cbBufferSize, pbBuffer);
879         return S_OK;
880     default:
881         hr = copypixels_to_32bppBGRA(This, prc, cbStride, cbBufferSize, pbBuffer, source_format);
882         if (SUCCEEDED(hr) && prc)
883               reverse_bgr8(4, pbBuffer, prc->Width, prc->Height, cbStride);
884         return hr;
885     }
886 }
887 
888 static HRESULT copypixels_to_32bppBGR(struct FormatConverter *This, const WICRect *prc,
889     UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer, enum pixelformat source_format)
890 {
891     switch (source_format)
892     {
893     case format_32bppBGR:
894     case format_32bppBGRA:
895     case format_32bppPBGRA:
896         if (prc)
897             return IWICBitmapSource_CopyPixels(This->source, prc, cbStride, cbBufferSize, pbBuffer);
898         return S_OK;
899     default:
900         return copypixels_to_32bppBGRA(This, prc, cbStride, cbBufferSize, pbBuffer, source_format);
901     }
902 }
903 
904 static HRESULT copypixels_to_32bppRGB(struct FormatConverter *This, const WICRect *prc,
905     UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer, enum pixelformat source_format)
906 {
907     switch (source_format)
908     {
909     case format_32bppRGB:
910     case format_32bppRGBA:
911     case format_32bppPRGBA:
912         if (prc)
913             return IWICBitmapSource_CopyPixels(This->source, prc, cbStride, cbBufferSize, pbBuffer);
914         return S_OK;
915     default:
916         return copypixels_to_32bppRGBA(This, prc, cbStride, cbBufferSize, pbBuffer, source_format);
917     }
918 }
919 
920 static HRESULT copypixels_to_32bppPBGRA(struct FormatConverter *This, const WICRect *prc,
921     UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer, enum pixelformat source_format)
922 {
923     HRESULT hr;
924 
925     switch (source_format)
926     {
927     case format_32bppPBGRA:
928         if (prc)
929             return IWICBitmapSource_CopyPixels(This->source, prc, cbStride, cbBufferSize, pbBuffer);
930         return S_OK;
931     default:
932         hr = copypixels_to_32bppBGRA(This, prc, cbStride, cbBufferSize, pbBuffer, source_format);
933         if (SUCCEEDED(hr) && prc)
934         {
935             INT x, y;
936 
937             for (y=0; y<prc->Height; y++)
938                 for (x=0; x<prc->Width; x++)
939                 {
940                     BYTE alpha = pbBuffer[cbStride*y+4*x+3];
941                     if (alpha != 255)
942                     {
943                         pbBuffer[cbStride*y+4*x] = pbBuffer[cbStride*y+4*x] * alpha / 255;
944                         pbBuffer[cbStride*y+4*x+1] = pbBuffer[cbStride*y+4*x+1] * alpha / 255;
945                         pbBuffer[cbStride*y+4*x+2] = pbBuffer[cbStride*y+4*x+2] * alpha / 255;
946                     }
947                 }
948         }
949         return hr;
950     }
951 }
952 
953 static HRESULT copypixels_to_32bppPRGBA(struct FormatConverter *This, const WICRect *prc,
954     UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer, enum pixelformat source_format)
955 {
956     HRESULT hr;
957 
958     switch (source_format)
959     {
960     case format_32bppPRGBA:
961         if (prc)
962             return IWICBitmapSource_CopyPixels(This->source, prc, cbStride, cbBufferSize, pbBuffer);
963         return S_OK;
964     default:
965         hr = copypixels_to_32bppRGBA(This, prc, cbStride, cbBufferSize, pbBuffer, source_format);
966         if (SUCCEEDED(hr) && prc)
967         {
968             INT x, y;
969 
970             for (y=0; y<prc->Height; y++)
971                 for (x=0; x<prc->Width; x++)
972                 {
973                     BYTE alpha = pbBuffer[cbStride*y+4*x+3];
974                     if (alpha != 255)
975                     {
976                         pbBuffer[cbStride*y+4*x] = pbBuffer[cbStride*y+4*x] * alpha / 255;
977                         pbBuffer[cbStride*y+4*x+1] = pbBuffer[cbStride*y+4*x+1] * alpha / 255;
978                         pbBuffer[cbStride*y+4*x+2] = pbBuffer[cbStride*y+4*x+2] * alpha / 255;
979                     }
980                 }
981         }
982         return hr;
983     }
984 }
985 
986 static HRESULT copypixels_to_24bppBGR(struct FormatConverter *This, const WICRect *prc,
987     UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer, enum pixelformat source_format)
988 {
989     HRESULT hr;
990 
991     switch (source_format)
992     {
993     case format_24bppBGR:
994     case format_24bppRGB:
995         if (prc)
996         {
997             hr = IWICBitmapSource_CopyPixels(This->source, prc, cbStride, cbBufferSize, pbBuffer);
998             if (SUCCEEDED(hr) && source_format == format_24bppRGB)
999               reverse_bgr8(3, pbBuffer, prc->Width, prc->Height, cbStride);
1000             return hr;
1001         }
1002         return S_OK;
1003     case format_32bppBGR:
1004     case format_32bppBGRA:
1005     case format_32bppPBGRA:
1006         if (prc)
1007         {
1008             HRESULT res;
1009             INT x, y;
1010             BYTE *srcdata;
1011             UINT srcstride, srcdatasize;
1012             const BYTE *srcrow;
1013             const BYTE *srcpixel;
1014             BYTE *dstrow;
1015             BYTE *dstpixel;
1016 
1017             srcstride = 4 * prc->Width;
1018             srcdatasize = srcstride * prc->Height;
1019 
1020             srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
1021             if (!srcdata) return E_OUTOFMEMORY;
1022 
1023             res = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
1024 
1025             if (SUCCEEDED(res))
1026             {
1027                 srcrow = srcdata;
1028                 dstrow = pbBuffer;
1029                 for (y=0; y<prc->Height; y++) {
1030                     srcpixel=srcrow;
1031                     dstpixel=dstrow;
1032                     for (x=0; x<prc->Width; x++) {
1033                         *dstpixel++=*srcpixel++; /* blue */
1034                         *dstpixel++=*srcpixel++; /* green */
1035                         *dstpixel++=*srcpixel++; /* red */
1036                         srcpixel++; /* alpha */
1037                     }
1038                     srcrow += srcstride;
1039                     dstrow += cbStride;
1040                 }
1041             }
1042 
1043             HeapFree(GetProcessHeap(), 0, srcdata);
1044 
1045             return res;
1046         }
1047         return S_OK;
1048 
1049     case format_32bppGrayFloat:
1050         if (prc)
1051         {
1052             BYTE *srcdata;
1053             UINT srcstride, srcdatasize;
1054 
1055             srcstride = 4 * prc->Width;
1056             srcdatasize = srcstride * prc->Height;
1057 
1058             srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
1059             if (!srcdata) return E_OUTOFMEMORY;
1060 
1061             hr = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
1062 
1063             if (SUCCEEDED(hr))
1064             {
1065                 INT x, y;
1066                 BYTE *src = srcdata, *dst = pbBuffer;
1067 
1068                 for (y = 0; y < prc->Height; y++)
1069                 {
1070                     float *gray_float = (float *)src;
1071                     BYTE *bgr = dst;
1072 
1073                     for (x = 0; x < prc->Width; x++)
1074                     {
1075                         BYTE gray = (BYTE)floorf(to_sRGB_component(gray_float[x]) * 255.0f + 0.51f);
1076                         *bgr++ = gray;
1077                         *bgr++ = gray;
1078                         *bgr++ = gray;
1079                     }
1080                     src += srcstride;
1081                     dst += cbStride;
1082                 }
1083             }
1084 
1085             HeapFree(GetProcessHeap(), 0, srcdata);
1086 
1087             return hr;
1088         }
1089         return S_OK;
1090 
1091     case format_32bppCMYK:
1092         if (prc)
1093         {
1094             BYTE *srcdata;
1095             UINT srcstride, srcdatasize;
1096 
1097             srcstride = 4 * prc->Width;
1098             srcdatasize = srcstride * prc->Height;
1099 
1100             srcdata = heap_alloc(srcdatasize);
1101             if (!srcdata) return E_OUTOFMEMORY;
1102 
1103             hr = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
1104             if (SUCCEEDED(hr))
1105             {
1106                 INT x, y;
1107                 BYTE *src = srcdata, *dst = pbBuffer;
1108 
1109                 for (y = 0; y < prc->Height; y++)
1110                 {
1111                     BYTE *cmyk = src;
1112                     BYTE *bgr = dst;
1113 
1114                     for (x = 0; x < prc->Width; x++)
1115                     {
1116                         BYTE c = cmyk[0], m = cmyk[1], y = cmyk[2], k = cmyk[3];
1117                         bgr[0] = (255 - y) * (255 - k) / 255; /* B */
1118                         bgr[1] = (255 - m) * (255 - k) / 255; /* G */
1119                         bgr[2] = (255 - c) * (255 - k) / 255; /* R */
1120                         cmyk += 4;
1121                         bgr += 3;
1122                     }
1123                     src += srcstride;
1124                     dst += cbStride;
1125                 }
1126             }
1127 
1128             heap_free(srcdata);
1129             return hr;
1130         }
1131         return S_OK;
1132 
1133     default:
1134         FIXME("Unimplemented conversion path!\n");
1135         return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1136     }
1137 }
1138 
1139 static HRESULT copypixels_to_24bppRGB(struct FormatConverter *This, const WICRect *prc,
1140     UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer, enum pixelformat source_format)
1141 {
1142     HRESULT hr;
1143 
1144     switch (source_format)
1145     {
1146     case format_24bppBGR:
1147     case format_24bppRGB:
1148         if (prc)
1149         {
1150             hr = IWICBitmapSource_CopyPixels(This->source, prc, cbStride, cbBufferSize, pbBuffer);
1151             if (SUCCEEDED(hr) && source_format == format_24bppBGR)
1152               reverse_bgr8(3, pbBuffer, prc->Width, prc->Height, cbStride);
1153             return hr;
1154         }
1155         return S_OK;
1156     case format_32bppBGR:
1157     case format_32bppBGRA:
1158     case format_32bppPBGRA:
1159         if (prc)
1160         {
1161             HRESULT res;
1162             INT x, y;
1163             BYTE *srcdata;
1164             UINT srcstride, srcdatasize;
1165             const BYTE *srcrow;
1166             const BYTE *srcpixel;
1167             BYTE *dstrow;
1168             BYTE *dstpixel;
1169             BYTE tmppixel[3];
1170 
1171             srcstride = 4 * prc->Width;
1172             srcdatasize = srcstride * prc->Height;
1173 
1174             srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
1175             if (!srcdata) return E_OUTOFMEMORY;
1176 
1177             res = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
1178 
1179             if (SUCCEEDED(res))
1180             {
1181                 srcrow = srcdata;
1182                 dstrow = pbBuffer;
1183                 for (y=0; y<prc->Height; y++) {
1184                     srcpixel=srcrow;
1185                     dstpixel=dstrow;
1186                     for (x=0; x<prc->Width; x++) {
1187                         tmppixel[0]=*srcpixel++; /* blue */
1188                         tmppixel[1]=*srcpixel++; /* green */
1189                         tmppixel[2]=*srcpixel++; /* red */
1190                         srcpixel++; /* alpha */
1191 
1192                         *dstpixel++=tmppixel[2]; /* red */
1193                         *dstpixel++=tmppixel[1]; /* green */
1194                         *dstpixel++=tmppixel[0]; /* blue */
1195                     }
1196                     srcrow += srcstride;
1197                     dstrow += cbStride;
1198                 }
1199             }
1200 
1201             HeapFree(GetProcessHeap(), 0, srcdata);
1202 
1203             return res;
1204         }
1205         return S_OK;
1206     default:
1207         FIXME("Unimplemented conversion path!\n");
1208         return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1209     }
1210 }
1211 
1212 static HRESULT copypixels_to_32bppGrayFloat(struct FormatConverter *This, const WICRect *prc,
1213     UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer, enum pixelformat source_format)
1214 {
1215     HRESULT hr;
1216 
1217     switch (source_format)
1218     {
1219     case format_32bppBGR:
1220     case format_32bppBGRA:
1221     case format_32bppPBGRA:
1222     case format_32bppGrayFloat:
1223         if (prc)
1224         {
1225             hr = IWICBitmapSource_CopyPixels(This->source, prc, cbStride, cbBufferSize, pbBuffer);
1226             break;
1227         }
1228         return S_OK;
1229 
1230     default:
1231         hr = copypixels_to_32bppBGRA(This, prc, cbStride, cbBufferSize, pbBuffer, source_format);
1232         break;
1233     }
1234 
1235     if (SUCCEEDED(hr) && prc && source_format != format_32bppGrayFloat)
1236     {
1237         INT x, y;
1238         BYTE *p = pbBuffer;
1239 
1240         for (y = 0; y < prc->Height; y++)
1241         {
1242             BYTE *bgr = p;
1243             for (x = 0; x < prc->Width; x++)
1244             {
1245                 float gray = (bgr[2] * 0.2126f + bgr[1] * 0.7152f + bgr[0] * 0.0722f) / 255.0f;
1246                 *(float *)bgr = gray;
1247                 bgr += 4;
1248             }
1249             p += cbStride;
1250         }
1251     }
1252     return hr;
1253 }
1254 
1255 static HRESULT copypixels_to_8bppGray(struct FormatConverter *This, const WICRect *prc,
1256     UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer, enum pixelformat source_format)
1257 {
1258     HRESULT hr;
1259     BYTE *srcdata;
1260     UINT srcstride, srcdatasize;
1261 
1262     if (source_format == format_8bppGray)
1263     {
1264         if (prc)
1265             return IWICBitmapSource_CopyPixels(This->source, prc, cbStride, cbBufferSize, pbBuffer);
1266 
1267         return S_OK;
1268     }
1269 
1270     if (source_format == format_32bppGrayFloat)
1271     {
1272         hr = S_OK;
1273 
1274         if (prc)
1275         {
1276             srcstride = 4 * prc->Width;
1277             srcdatasize = srcstride * prc->Height;
1278 
1279             srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
1280             if (!srcdata) return E_OUTOFMEMORY;
1281 
1282             hr = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
1283             if (SUCCEEDED(hr))
1284             {
1285                 INT x, y;
1286                 BYTE *src = srcdata, *dst = pbBuffer;
1287 
1288                 for (y=0; y < prc->Height; y++)
1289                 {
1290                     float *srcpixel = (float*)src;
1291                     BYTE *dstpixel = dst;
1292 
1293                     for (x=0; x < prc->Width; x++)
1294                         *dstpixel++ = (BYTE)floorf(to_sRGB_component(*srcpixel++) * 255.0f + 0.51f);
1295 
1296                     src += srcstride;
1297                     dst += cbStride;
1298                 }
1299             }
1300 
1301             HeapFree(GetProcessHeap(), 0, srcdata);
1302         }
1303 
1304         return hr;
1305     }
1306 
1307     srcstride = 3 * prc->Width;
1308     srcdatasize = srcstride * prc->Height;
1309 
1310     srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
1311     if (!srcdata) return E_OUTOFMEMORY;
1312 
1313     hr = copypixels_to_24bppBGR(This, prc, srcstride, srcdatasize, srcdata, source_format);
1314     if (SUCCEEDED(hr) && prc)
1315     {
1316         INT x, y;
1317         BYTE *src = srcdata, *dst = pbBuffer;
1318 
1319         for (y = 0; y < prc->Height; y++)
1320         {
1321             BYTE *bgr = src;
1322 
1323             for (x = 0; x < prc->Width; x++)
1324             {
1325                 float gray = (bgr[2] * 0.2126f + bgr[1] * 0.7152f + bgr[0] * 0.0722f) / 255.0f;
1326 
1327                 gray = to_sRGB_component(gray) * 255.0f;
1328                 dst[x] = (BYTE)floorf(gray + 0.51f);
1329                 bgr += 3;
1330             }
1331             src += srcstride;
1332             dst += cbStride;
1333         }
1334     }
1335 
1336     HeapFree(GetProcessHeap(), 0, srcdata);
1337     return hr;
1338 }
1339 
1340 static UINT rgb_to_palette_index(BYTE r, BYTE g, BYTE b, WICColor *colors, UINT count)
1341 {
1342     UINT best_diff, best_index, i;
1343 
1344     best_diff = ~0;
1345     best_index = 0;
1346 
1347     for (i = 0; i < count; i++)
1348     {
1349         BYTE pal_r, pal_g, pal_b;
1350         DWORD diff_r, diff_g, diff_b, diff;
1351 
1352         pal_r = colors[i] >> 16;
1353         pal_g = colors[i] >> 8;
1354         pal_b = colors[i];
1355 
1356         diff_r = r - pal_r;
1357         diff_g = g - pal_g;
1358         diff_b = b - pal_b;
1359 
1360         diff = diff_r * diff_r + diff_g * diff_g + diff_b * diff_b;
1361         if (diff == 0) return i;
1362 
1363         if (diff < best_diff)
1364         {
1365             best_diff = diff;
1366             best_index = i;
1367         }
1368     }
1369 
1370     return best_index;
1371 }
1372 
1373 static HRESULT copypixels_to_8bppIndexed(struct FormatConverter *This, const WICRect *prc,
1374     UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer, enum pixelformat source_format)
1375 {
1376     HRESULT hr;
1377     BYTE *srcdata;
1378     WICColor colors[256];
1379     UINT srcstride, srcdatasize, count;
1380 
1381     if (source_format == format_8bppIndexed)
1382     {
1383         if (prc)
1384             return IWICBitmapSource_CopyPixels(This->source, prc, cbStride, cbBufferSize, pbBuffer);
1385 
1386         return S_OK;
1387     }
1388 
1389     if (!This->palette) return WINCODEC_ERR_WRONGSTATE;
1390 
1391     hr = IWICPalette_GetColors(This->palette, 256, colors, &count);
1392     if (hr != S_OK) return hr;
1393 
1394     srcstride = 3 * prc->Width;
1395     srcdatasize = srcstride * prc->Height;
1396 
1397     srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
1398     if (!srcdata) return E_OUTOFMEMORY;
1399 
1400     hr = copypixels_to_24bppBGR(This, prc, srcstride, srcdatasize, srcdata, source_format);
1401     if (SUCCEEDED(hr) && prc)
1402     {
1403         INT x, y;
1404         BYTE *src = srcdata, *dst = pbBuffer;
1405 
1406         for (y = 0; y < prc->Height; y++)
1407         {
1408             BYTE *bgr = src;
1409 
1410             for (x = 0; x < prc->Width; x++)
1411             {
1412                 dst[x] = rgb_to_palette_index(bgr[2], bgr[1], bgr[0], colors, count);
1413                 bgr += 3;
1414             }
1415             src += srcstride;
1416             dst += cbStride;
1417         }
1418     }
1419 
1420     HeapFree(GetProcessHeap(), 0, srcdata);
1421     return hr;
1422 }
1423 
1424 static const struct pixelformatinfo supported_formats[] = {
1425     {format_1bppIndexed, &GUID_WICPixelFormat1bppIndexed, NULL},
1426     {format_2bppIndexed, &GUID_WICPixelFormat2bppIndexed, NULL},
1427     {format_4bppIndexed, &GUID_WICPixelFormat4bppIndexed, NULL},
1428     {format_8bppIndexed, &GUID_WICPixelFormat8bppIndexed, copypixels_to_8bppIndexed},
1429     {format_BlackWhite, &GUID_WICPixelFormatBlackWhite, NULL},
1430     {format_2bppGray, &GUID_WICPixelFormat2bppGray, NULL},
1431     {format_4bppGray, &GUID_WICPixelFormat4bppGray, NULL},
1432     {format_8bppGray, &GUID_WICPixelFormat8bppGray, copypixels_to_8bppGray},
1433     {format_16bppGray, &GUID_WICPixelFormat16bppGray, NULL},
1434     {format_16bppBGR555, &GUID_WICPixelFormat16bppBGR555, NULL},
1435     {format_16bppBGR565, &GUID_WICPixelFormat16bppBGR565, NULL},
1436     {format_16bppBGRA5551, &GUID_WICPixelFormat16bppBGRA5551, NULL},
1437     {format_24bppBGR, &GUID_WICPixelFormat24bppBGR, copypixels_to_24bppBGR},
1438     {format_24bppRGB, &GUID_WICPixelFormat24bppRGB, copypixels_to_24bppRGB},
1439     {format_32bppGrayFloat, &GUID_WICPixelFormat32bppGrayFloat, copypixels_to_32bppGrayFloat},
1440     {format_32bppBGR, &GUID_WICPixelFormat32bppBGR, copypixels_to_32bppBGR},
1441     {format_32bppRGB, &GUID_WICPixelFormat32bppRGB, copypixels_to_32bppRGB},
1442     {format_32bppBGRA, &GUID_WICPixelFormat32bppBGRA, copypixels_to_32bppBGRA},
1443     {format_32bppRGBA, &GUID_WICPixelFormat32bppRGBA, copypixels_to_32bppRGBA},
1444     {format_32bppPBGRA, &GUID_WICPixelFormat32bppPBGRA, copypixels_to_32bppPBGRA},
1445     {format_32bppPRGBA, &GUID_WICPixelFormat32bppPRGBA, copypixels_to_32bppPRGBA},
1446     {format_48bppRGB, &GUID_WICPixelFormat48bppRGB, NULL},
1447     {format_64bppRGBA, &GUID_WICPixelFormat64bppRGBA, NULL},
1448     {format_32bppCMYK, &GUID_WICPixelFormat32bppCMYK, NULL},
1449     {0}
1450 };
1451 
1452 static const struct pixelformatinfo *get_formatinfo(const WICPixelFormatGUID *format)
1453 {
1454     UINT i;
1455 
1456     for (i=0; supported_formats[i].guid; i++)
1457         if (IsEqualGUID(supported_formats[i].guid, format)) return &supported_formats[i];
1458 
1459     return NULL;
1460 }
1461 
1462 static HRESULT WINAPI FormatConverter_QueryInterface(IWICFormatConverter *iface, REFIID iid,
1463     void **ppv)
1464 {
1465     FormatConverter *This = impl_from_IWICFormatConverter(iface);
1466     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1467 
1468     if (!ppv) return E_INVALIDARG;
1469 
1470     if (IsEqualIID(&IID_IUnknown, iid) ||
1471         IsEqualIID(&IID_IWICBitmapSource, iid) ||
1472         IsEqualIID(&IID_IWICFormatConverter, iid))
1473     {
1474         *ppv = &This->IWICFormatConverter_iface;
1475     }
1476     else
1477     {
1478         *ppv = NULL;
1479         return E_NOINTERFACE;
1480     }
1481 
1482     IUnknown_AddRef((IUnknown*)*ppv);
1483     return S_OK;
1484 }
1485 
1486 static ULONG WINAPI FormatConverter_AddRef(IWICFormatConverter *iface)
1487 {
1488     FormatConverter *This = impl_from_IWICFormatConverter(iface);
1489     ULONG ref = InterlockedIncrement(&This->ref);
1490 
1491     TRACE("(%p) refcount=%u\n", iface, ref);
1492 
1493     return ref;
1494 }
1495 
1496 static ULONG WINAPI FormatConverter_Release(IWICFormatConverter *iface)
1497 {
1498     FormatConverter *This = impl_from_IWICFormatConverter(iface);
1499     ULONG ref = InterlockedDecrement(&This->ref);
1500 
1501     TRACE("(%p) refcount=%u\n", iface, ref);
1502 
1503     if (ref == 0)
1504     {
1505         This->lock.DebugInfo->Spare[0] = 0;
1506         DeleteCriticalSection(&This->lock);
1507         if (This->source) IWICBitmapSource_Release(This->source);
1508         if (This->palette) IWICPalette_Release(This->palette);
1509         HeapFree(GetProcessHeap(), 0, This);
1510     }
1511 
1512     return ref;
1513 }
1514 
1515 static HRESULT WINAPI FormatConverter_GetSize(IWICFormatConverter *iface,
1516     UINT *puiWidth, UINT *puiHeight)
1517 {
1518     FormatConverter *This = impl_from_IWICFormatConverter(iface);
1519 
1520     TRACE("(%p,%p,%p)\n", iface, puiWidth, puiHeight);
1521 
1522     if (This->source)
1523         return IWICBitmapSource_GetSize(This->source, puiWidth, puiHeight);
1524     else
1525         return WINCODEC_ERR_NOTINITIALIZED;
1526 }
1527 
1528 static HRESULT WINAPI FormatConverter_GetPixelFormat(IWICFormatConverter *iface,
1529     WICPixelFormatGUID *pPixelFormat)
1530 {
1531     FormatConverter *This = impl_from_IWICFormatConverter(iface);
1532 
1533     TRACE("(%p,%p)\n", iface, pPixelFormat);
1534 
1535     if (This->source)
1536         memcpy(pPixelFormat, This->dst_format->guid, sizeof(GUID));
1537     else
1538         return WINCODEC_ERR_NOTINITIALIZED;
1539 
1540     return S_OK;
1541 }
1542 
1543 static HRESULT WINAPI FormatConverter_GetResolution(IWICFormatConverter *iface,
1544     double *pDpiX, double *pDpiY)
1545 {
1546     FormatConverter *This = impl_from_IWICFormatConverter(iface);
1547 
1548     TRACE("(%p,%p,%p)\n", iface, pDpiX, pDpiY);
1549 
1550     if (This->source)
1551         return IWICBitmapSource_GetResolution(This->source, pDpiX, pDpiY);
1552     else
1553         return WINCODEC_ERR_NOTINITIALIZED;
1554 }
1555 
1556 static HRESULT WINAPI FormatConverter_CopyPalette(IWICFormatConverter *iface,
1557     IWICPalette *palette)
1558 {
1559     FormatConverter *This = impl_from_IWICFormatConverter(iface);
1560 
1561     TRACE("(%p,%p)\n", iface, palette);
1562 
1563     if (!palette) return E_INVALIDARG;
1564     if (!This->source) return WINCODEC_ERR_WRONGSTATE;
1565 
1566     if (!This->palette)
1567     {
1568         HRESULT hr;
1569         UINT bpp;
1570 
1571         hr = get_pixelformat_bpp(This->dst_format->guid, &bpp);
1572         if (hr != S_OK) return hr;
1573         if (bpp <= 8) return WINCODEC_ERR_WRONGSTATE;
1574         return IWICBitmapSource_CopyPalette(This->source, palette);
1575     }
1576 
1577     return IWICPalette_InitializeFromPalette(palette, This->palette);
1578 }
1579 
1580 static HRESULT WINAPI FormatConverter_CopyPixels(IWICFormatConverter *iface,
1581     const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
1582 {
1583     FormatConverter *This = impl_from_IWICFormatConverter(iface);
1584     WICRect rc;
1585     HRESULT hr;
1586     TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
1587 
1588     if (This->source)
1589     {
1590         if (!prc)
1591         {
1592             UINT width, height;
1593             hr = IWICBitmapSource_GetSize(This->source, &width, &height);
1594             if (FAILED(hr)) return hr;
1595             rc.X = 0;
1596             rc.Y = 0;
1597             rc.Width = width;
1598             rc.Height = height;
1599             prc = &rc;
1600         }
1601 
1602         return This->dst_format->copy_function(This, prc, cbStride, cbBufferSize,
1603             pbBuffer, This->src_format->format);
1604     }
1605     else
1606         return WINCODEC_ERR_WRONGSTATE;
1607 }
1608 
1609 static HRESULT WINAPI FormatConverter_Initialize(IWICFormatConverter *iface,
1610     IWICBitmapSource *source, REFWICPixelFormatGUID dstFormat, WICBitmapDitherType dither,
1611     IWICPalette *palette, double alpha_threshold, WICBitmapPaletteType palette_type)
1612 {
1613     FormatConverter *This = impl_from_IWICFormatConverter(iface);
1614     const struct pixelformatinfo *srcinfo, *dstinfo;
1615     GUID srcFormat;
1616     HRESULT res;
1617 
1618     TRACE("(%p,%p,%s,%u,%p,%0.3f,%u)\n", iface, source, debugstr_guid(dstFormat),
1619         dither, palette, alpha_threshold, palette_type);
1620 
1621     if (!palette)
1622     {
1623         UINT bpp;
1624         res = get_pixelformat_bpp(dstFormat, &bpp);
1625         if (res != S_OK) return res;
1626 
1627         res = PaletteImpl_Create(&palette);
1628         if (res != S_OK) return res;
1629 
1630         switch (palette_type)
1631         {
1632         case WICBitmapPaletteTypeCustom:
1633             IWICPalette_Release(palette);
1634             palette = NULL;
1635             if (bpp <= 8) return E_INVALIDARG;
1636             break;
1637 
1638         case WICBitmapPaletteTypeMedianCut:
1639         {
1640             if (bpp <= 8)
1641                 res = IWICPalette_InitializeFromBitmap(palette, source, 1 << bpp, FALSE);
1642             break;
1643         }
1644 
1645         default:
1646             if (bpp <= 8)
1647                 res = IWICPalette_InitializePredefined(palette, palette_type, FALSE);
1648             break;
1649         }
1650 
1651         if (res != S_OK)
1652         {
1653             IWICPalette_Release(palette);
1654             return res;
1655         }
1656     }
1657     else
1658         IWICPalette_AddRef(palette);
1659 
1660     EnterCriticalSection(&This->lock);
1661 
1662     if (This->source)
1663     {
1664         res = WINCODEC_ERR_WRONGSTATE;
1665         goto end;
1666     }
1667 
1668     res = IWICBitmapSource_GetPixelFormat(source, &srcFormat);
1669     if (FAILED(res)) goto end;
1670 
1671     srcinfo = get_formatinfo(&srcFormat);
1672     if (!srcinfo)
1673     {
1674         res = WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT;
1675         FIXME("Unsupported source format %s\n", debugstr_guid(&srcFormat));
1676         goto end;
1677     }
1678 
1679     dstinfo = get_formatinfo(dstFormat);
1680     if (!dstinfo)
1681     {
1682         res = WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT;
1683         FIXME("Unsupported destination format %s\n", debugstr_guid(dstFormat));
1684         goto end;
1685     }
1686 
1687     if (dstinfo->copy_function)
1688     {
1689         IWICBitmapSource_AddRef(source);
1690         This->src_format = srcinfo;
1691         This->dst_format = dstinfo;
1692         This->dither = dither;
1693         This->alpha_threshold = alpha_threshold;
1694         This->palette = palette;
1695         This->source = source;
1696     }
1697     else
1698     {
1699         FIXME("Unsupported conversion %s -> %s\n", debugstr_guid(&srcFormat), debugstr_guid(dstFormat));
1700         res = WINCODEC_ERR_UNSUPPORTEDOPERATION;
1701     }
1702 
1703 end:
1704 
1705     LeaveCriticalSection(&This->lock);
1706 
1707     if (res != S_OK && palette)
1708         IWICPalette_Release(palette);
1709 
1710     return res;
1711 }
1712 
1713 static HRESULT WINAPI FormatConverter_CanConvert(IWICFormatConverter *iface,
1714     REFWICPixelFormatGUID srcPixelFormat, REFWICPixelFormatGUID dstPixelFormat,
1715     BOOL *pfCanConvert)
1716 {
1717     FormatConverter *This = impl_from_IWICFormatConverter(iface);
1718     const struct pixelformatinfo *srcinfo, *dstinfo;
1719 
1720     TRACE("(%p,%s,%s,%p)\n", iface, debugstr_guid(srcPixelFormat),
1721         debugstr_guid(dstPixelFormat), pfCanConvert);
1722 
1723     srcinfo = get_formatinfo(srcPixelFormat);
1724     if (!srcinfo)
1725     {
1726         FIXME("Unsupported source format %s\n", debugstr_guid(srcPixelFormat));
1727         return WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT;
1728     }
1729 
1730     dstinfo = get_formatinfo(dstPixelFormat);
1731     if (!dstinfo)
1732     {
1733         FIXME("Unsupported destination format %s\n", debugstr_guid(dstPixelFormat));
1734         return WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT;
1735     }
1736 
1737     if (dstinfo->copy_function &&
1738         SUCCEEDED(dstinfo->copy_function(This, NULL, 0, 0, NULL, dstinfo->format)))
1739         *pfCanConvert = TRUE;
1740     else
1741     {
1742         FIXME("Unsupported conversion %s -> %s\n", debugstr_guid(srcPixelFormat), debugstr_guid(dstPixelFormat));
1743         *pfCanConvert = FALSE;
1744     }
1745 
1746     return S_OK;
1747 }
1748 
1749 static const IWICFormatConverterVtbl FormatConverter_Vtbl = {
1750     FormatConverter_QueryInterface,
1751     FormatConverter_AddRef,
1752     FormatConverter_Release,
1753     FormatConverter_GetSize,
1754     FormatConverter_GetPixelFormat,
1755     FormatConverter_GetResolution,
1756     FormatConverter_CopyPalette,
1757     FormatConverter_CopyPixels,
1758     FormatConverter_Initialize,
1759     FormatConverter_CanConvert
1760 };
1761 
1762 HRESULT FormatConverter_CreateInstance(REFIID iid, void** ppv)
1763 {
1764     FormatConverter *This;
1765     HRESULT ret;
1766 
1767     TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
1768 
1769     *ppv = NULL;
1770 
1771     This = HeapAlloc(GetProcessHeap(), 0, sizeof(FormatConverter));
1772     if (!This) return E_OUTOFMEMORY;
1773 
1774     This->IWICFormatConverter_iface.lpVtbl = &FormatConverter_Vtbl;
1775     This->ref = 1;
1776     This->source = NULL;
1777     This->palette = NULL;
1778     InitializeCriticalSection(&This->lock);
1779     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": FormatConverter.lock");
1780 
1781     ret = IWICFormatConverter_QueryInterface(&This->IWICFormatConverter_iface, iid, ppv);
1782     IWICFormatConverter_Release(&This->IWICFormatConverter_iface);
1783 
1784     return ret;
1785 }
1786