mmdevapi: Query MemoryWineUnixFuncs virtual memory and store the resulting handle.
[wine.git] / dlls / gdiplus / image.c
blob3b9ce3c14f12d4be7b8595ca3d33c48d1d955ae9
1 /*
2 * Copyright (C) 2007 Google (Evan Stade)
3 * Copyright (C) 2012,2016 Dmitry Timoshkov
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.
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.
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
20 #include <stdarg.h>
21 #include <assert.h>
23 #define NONAMELESSUNION
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "wingdi.h"
30 #define COBJMACROS
31 #include "objbase.h"
32 #include "olectl.h"
33 #include "ole2.h"
35 #include "initguid.h"
36 #include "wincodec.h"
37 #include "gdiplus.h"
38 #include "gdiplus_private.h"
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
43 HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT, IWICImagingFactory**);
45 #define PIXELFORMATBPP(x) ((x) ? ((x) >> 8) & 255 : 24)
46 #define WMF_PLACEABLE_KEY 0x9ac6cdd7
48 static const struct
50 const WICPixelFormatGUID *wic_format;
51 PixelFormat gdip_format;
52 /* predefined palette type to use for pixel format conversions */
53 WICBitmapPaletteType palette_type;
54 } pixel_formats[] =
56 { &GUID_WICPixelFormat1bppIndexed, PixelFormat1bppIndexed, WICBitmapPaletteTypeFixedBW },
57 { &GUID_WICPixelFormatBlackWhite, PixelFormat1bppIndexed, WICBitmapPaletteTypeFixedBW },
58 { &GUID_WICPixelFormat4bppIndexed, PixelFormat4bppIndexed, WICBitmapPaletteTypeFixedHalftone8 },
59 { &GUID_WICPixelFormat8bppIndexed, PixelFormat8bppIndexed, WICBitmapPaletteTypeFixedHalftone256 },
60 { &GUID_WICPixelFormat8bppGray, PixelFormat8bppIndexed, WICBitmapPaletteTypeFixedGray256 },
61 { &GUID_WICPixelFormat16bppBGR555, PixelFormat16bppRGB555, 0 },
62 { &GUID_WICPixelFormat24bppBGR, PixelFormat24bppRGB, 0 },
63 { &GUID_WICPixelFormat32bppBGR, PixelFormat32bppRGB, 0 },
64 { &GUID_WICPixelFormat32bppBGRA, PixelFormat32bppARGB, 0 },
65 { &GUID_WICPixelFormat32bppPBGRA, PixelFormat32bppPARGB, 0 },
66 { NULL }
69 static ColorPalette *get_palette(IWICBitmapFrameDecode *frame, WICBitmapPaletteType palette_type)
71 HRESULT hr;
72 IWICImagingFactory *factory;
73 IWICPalette *wic_palette;
74 ColorPalette *palette = NULL;
76 hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory);
77 if (hr != S_OK) return NULL;
79 hr = IWICImagingFactory_CreatePalette(factory, &wic_palette);
80 if (hr == S_OK)
82 hr = WINCODEC_ERR_PALETTEUNAVAILABLE;
83 if (frame)
84 hr = IWICBitmapFrameDecode_CopyPalette(frame, wic_palette);
85 if (hr != S_OK && palette_type != 0)
87 TRACE("using predefined palette %#x\n", palette_type);
88 hr = IWICPalette_InitializePredefined(wic_palette, palette_type, FALSE);
90 if (hr == S_OK)
92 WICBitmapPaletteType type;
93 BOOL alpha;
94 UINT count;
96 IWICPalette_GetColorCount(wic_palette, &count);
97 palette = heap_alloc(2 * sizeof(UINT) + count * sizeof(ARGB));
98 IWICPalette_GetColors(wic_palette, count, (UINT *)palette->Entries, &palette->Count);
100 IWICPalette_GetType(wic_palette, &type);
101 switch(type) {
102 case WICBitmapPaletteTypeFixedGray4:
103 case WICBitmapPaletteTypeFixedGray16:
104 case WICBitmapPaletteTypeFixedGray256:
105 palette->Flags = PaletteFlagsGrayScale;
106 break;
107 case WICBitmapPaletteTypeFixedHalftone8:
108 case WICBitmapPaletteTypeFixedHalftone27:
109 case WICBitmapPaletteTypeFixedHalftone64:
110 case WICBitmapPaletteTypeFixedHalftone125:
111 case WICBitmapPaletteTypeFixedHalftone216:
112 case WICBitmapPaletteTypeFixedHalftone252:
113 case WICBitmapPaletteTypeFixedHalftone256:
114 palette->Flags = PaletteFlagsHalftone;
115 break;
116 default:
117 palette->Flags = 0;
119 IWICPalette_HasAlpha(wic_palette, &alpha);
120 if(alpha)
121 palette->Flags |= PaletteFlagsHasAlpha;
123 IWICPalette_Release(wic_palette);
125 IWICImagingFactory_Release(factory);
126 return palette;
129 static HRESULT set_palette(IWICBitmapFrameEncode *frame, ColorPalette *palette)
131 HRESULT hr;
132 IWICImagingFactory *factory;
133 IWICPalette *wic_palette;
135 hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory);
136 if (FAILED(hr))
137 return hr;
139 hr = IWICImagingFactory_CreatePalette(factory, &wic_palette);
140 IWICImagingFactory_Release(factory);
141 if (SUCCEEDED(hr))
143 hr = IWICPalette_InitializeCustom(wic_palette, (UINT *)palette->Entries, palette->Count);
145 if (SUCCEEDED(hr))
146 hr = IWICBitmapFrameEncode_SetPalette(frame, wic_palette);
148 IWICPalette_Release(wic_palette);
151 return hr;
154 GpStatus WINGDIPAPI GdipBitmapApplyEffect(GpBitmap* bitmap, CGpEffect* effect,
155 RECT* roi, BOOL useAuxData, VOID** auxData, INT* auxDataSize)
157 FIXME("(%p %p %p %d %p %p): stub\n", bitmap, effect, roi, useAuxData, auxData, auxDataSize);
159 * Note: According to Jose Roca's GDI+ docs, this function is not
160 * implemented in Windows's GDI+.
162 return NotImplemented;
165 GpStatus WINGDIPAPI GdipBitmapCreateApplyEffect(GpBitmap** inputBitmaps,
166 INT numInputs, CGpEffect* effect, RECT* roi, RECT* outputRect,
167 GpBitmap** outputBitmap, BOOL useAuxData, VOID** auxData, INT* auxDataSize)
169 FIXME("(%p %d %p %p %p %p %d %p %p): stub\n", inputBitmaps, numInputs, effect, roi, outputRect, outputBitmap, useAuxData, auxData, auxDataSize);
171 * Note: According to Jose Roca's GDI+ docs, this function is not
172 * implemented in Windows's GDI+.
174 return NotImplemented;
177 static inline void getpixel_1bppIndexed(BYTE *index, const BYTE *row, UINT x)
179 *index = (row[x/8]>>(7-x%8)) & 1;
182 static inline void getpixel_4bppIndexed(BYTE *index, const BYTE *row, UINT x)
184 if (x & 1)
185 *index = row[x/2]&0xf;
186 else
187 *index = row[x/2]>>4;
190 static inline void getpixel_8bppIndexed(BYTE *index, const BYTE *row, UINT x)
192 *index = row[x];
195 static inline void getpixel_16bppGrayScale(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
196 const BYTE *row, UINT x)
198 *r = *g = *b = row[x*2+1];
199 *a = 255;
202 static inline void getpixel_16bppRGB555(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
203 const BYTE *row, UINT x)
205 WORD pixel = *((const WORD*)(row)+x);
206 *r = (pixel>>7&0xf8)|(pixel>>12&0x7);
207 *g = (pixel>>2&0xf8)|(pixel>>6&0x7);
208 *b = (pixel<<3&0xf8)|(pixel>>2&0x7);
209 *a = 255;
212 static inline void getpixel_16bppRGB565(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
213 const BYTE *row, UINT x)
215 WORD pixel = *((const WORD*)(row)+x);
216 *r = (pixel>>8&0xf8)|(pixel>>13&0x7);
217 *g = (pixel>>3&0xfc)|(pixel>>9&0x3);
218 *b = (pixel<<3&0xf8)|(pixel>>2&0x7);
219 *a = 255;
222 static inline void getpixel_16bppARGB1555(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
223 const BYTE *row, UINT x)
225 WORD pixel = *((const WORD*)(row)+x);
226 *r = (pixel>>7&0xf8)|(pixel>>12&0x7);
227 *g = (pixel>>2&0xf8)|(pixel>>6&0x7);
228 *b = (pixel<<3&0xf8)|(pixel>>2&0x7);
229 if ((pixel&0x8000) == 0x8000)
230 *a = 255;
231 else
232 *a = 0;
235 static inline void getpixel_24bppRGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
236 const BYTE *row, UINT x)
238 *r = row[x*3+2];
239 *g = row[x*3+1];
240 *b = row[x*3];
241 *a = 255;
244 static inline void getpixel_32bppRGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
245 const BYTE *row, UINT x)
247 *r = row[x*4+2];
248 *g = row[x*4+1];
249 *b = row[x*4];
250 *a = 255;
253 static inline void getpixel_32bppARGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
254 const BYTE *row, UINT x)
256 *r = row[x*4+2];
257 *g = row[x*4+1];
258 *b = row[x*4];
259 *a = row[x*4+3];
262 static inline void getpixel_32bppPARGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
263 const BYTE *row, UINT x)
265 *a = row[x*4+3];
266 if (*a == 0)
268 *r = row[x*4+2];
269 *g = row[x*4+1];
270 *b = row[x*4];
272 else
274 DWORD scaled_q = (255 << 15) / *a;
275 *r = (row[x*4+2] > *a) ? 0xff : (row[x*4+2] * scaled_q) >> 15;
276 *g = (row[x*4+1] > *a) ? 0xff : (row[x*4+1] * scaled_q) >> 15;
277 *b = (row[x*4] > *a) ? 0xff : (row[x*4] * scaled_q) >> 15;
281 static inline void getpixel_48bppRGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
282 const BYTE *row, UINT x)
284 *r = row[x*6+5];
285 *g = row[x*6+3];
286 *b = row[x*6+1];
287 *a = 255;
290 static inline void getpixel_64bppARGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
291 const BYTE *row, UINT x)
293 *r = row[x*8+5];
294 *g = row[x*8+3];
295 *b = row[x*8+1];
296 *a = row[x*8+7];
299 static inline void getpixel_64bppPARGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
300 const BYTE *row, UINT x)
302 *a = row[x*8+7];
303 if (*a == 0)
304 *r = *g = *b = 0;
305 else
307 *r = row[x*8+5] * 255 / *a;
308 *g = row[x*8+3] * 255 / *a;
309 *b = row[x*8+1] * 255 / *a;
313 GpStatus WINGDIPAPI GdipBitmapGetPixel(GpBitmap* bitmap, INT x, INT y,
314 ARGB *color)
316 BYTE r, g, b, a;
317 BYTE index;
318 BYTE *row;
320 if(!bitmap || !color ||
321 x < 0 || y < 0 || x >= bitmap->width || y >= bitmap->height)
322 return InvalidParameter;
324 row = bitmap->bits+bitmap->stride*y;
326 switch (bitmap->format)
328 case PixelFormat1bppIndexed:
329 getpixel_1bppIndexed(&index,row,x);
330 break;
331 case PixelFormat4bppIndexed:
332 getpixel_4bppIndexed(&index,row,x);
333 break;
334 case PixelFormat8bppIndexed:
335 getpixel_8bppIndexed(&index,row,x);
336 break;
337 case PixelFormat16bppGrayScale:
338 getpixel_16bppGrayScale(&r,&g,&b,&a,row,x);
339 break;
340 case PixelFormat16bppRGB555:
341 getpixel_16bppRGB555(&r,&g,&b,&a,row,x);
342 break;
343 case PixelFormat16bppRGB565:
344 getpixel_16bppRGB565(&r,&g,&b,&a,row,x);
345 break;
346 case PixelFormat16bppARGB1555:
347 getpixel_16bppARGB1555(&r,&g,&b,&a,row,x);
348 break;
349 case PixelFormat24bppRGB:
350 getpixel_24bppRGB(&r,&g,&b,&a,row,x);
351 break;
352 case PixelFormat32bppRGB:
353 getpixel_32bppRGB(&r,&g,&b,&a,row,x);
354 break;
355 case PixelFormat32bppARGB:
356 getpixel_32bppARGB(&r,&g,&b,&a,row,x);
357 break;
358 case PixelFormat32bppPARGB:
359 getpixel_32bppPARGB(&r,&g,&b,&a,row,x);
360 break;
361 case PixelFormat48bppRGB:
362 getpixel_48bppRGB(&r,&g,&b,&a,row,x);
363 break;
364 case PixelFormat64bppARGB:
365 getpixel_64bppARGB(&r,&g,&b,&a,row,x);
366 break;
367 case PixelFormat64bppPARGB:
368 getpixel_64bppPARGB(&r,&g,&b,&a,row,x);
369 break;
370 default:
371 FIXME("not implemented for format 0x%x\n", bitmap->format);
372 return NotImplemented;
375 if (bitmap->format & PixelFormatIndexed)
376 *color = bitmap->image.palette->Entries[index];
377 else
378 *color = a<<24|r<<16|g<<8|b;
380 return Ok;
383 static unsigned int absdiff(unsigned int x, unsigned int y)
385 return x > y ? x - y : y - x;
388 static inline UINT get_palette_index(BYTE r, BYTE g, BYTE b, BYTE a, ColorPalette *palette)
390 BYTE index = 0;
391 int best_distance = 0x7fff;
392 int distance;
393 UINT i;
395 if (!palette) return 0;
396 /* This algorithm scans entire palette,
397 computes difference from desired color (all color components have equal weight)
398 and returns the index of color with least difference.
400 Note: Maybe it could be replaced with a better algorithm for better image quality
401 and performance, though better algorithm would probably need some pre-built lookup
402 tables and thus may actually be slower if this method is called only few times per
403 every image.
405 for(i=0;i<palette->Count;i++) {
406 ARGB color=palette->Entries[i];
407 distance=absdiff(b, color & 0xff) + absdiff(g, color>>8 & 0xff) + absdiff(r, color>>16 & 0xff) + absdiff(a, color>>24 & 0xff);
408 if (distance<best_distance) {
409 best_distance=distance;
410 index=i;
413 return index;
416 static inline void setpixel_8bppIndexed(BYTE r, BYTE g, BYTE b, BYTE a,
417 BYTE *row, UINT x, ColorPalette *palette)
419 BYTE index = get_palette_index(r,g,b,a,palette);
420 row[x]=index;
423 static inline void setpixel_1bppIndexed(BYTE r, BYTE g, BYTE b, BYTE a,
424 BYTE *row, UINT x, ColorPalette *palette)
426 row[x/8] = (row[x/8] & ~(1<<(7-x%8))) | (get_palette_index(r,g,b,a,palette)<<(7-x%8));
429 static inline void setpixel_4bppIndexed(BYTE r, BYTE g, BYTE b, BYTE a,
430 BYTE *row, UINT x, ColorPalette *palette)
432 if (x & 1)
433 row[x/2] = (row[x/2] & 0xf0) | get_palette_index(r,g,b,a,palette);
434 else
435 row[x/2] = (row[x/2] & 0x0f) | get_palette_index(r,g,b,a,palette)<<4;
438 static inline void setpixel_16bppGrayScale(BYTE r, BYTE g, BYTE b, BYTE a,
439 BYTE *row, UINT x)
441 *((WORD*)(row)+x) = (r+g+b)*85;
444 static inline void setpixel_16bppRGB555(BYTE r, BYTE g, BYTE b, BYTE a,
445 BYTE *row, UINT x)
447 *((WORD*)(row)+x) = (r<<7&0x7c00)|
448 (g<<2&0x03e0)|
449 (b>>3&0x001f);
452 static inline void setpixel_16bppRGB565(BYTE r, BYTE g, BYTE b, BYTE a,
453 BYTE *row, UINT x)
455 *((WORD*)(row)+x) = (r<<8&0xf800)|
456 (g<<3&0x07e0)|
457 (b>>3&0x001f);
460 static inline void setpixel_16bppARGB1555(BYTE r, BYTE g, BYTE b, BYTE a,
461 BYTE *row, UINT x)
463 *((WORD*)(row)+x) = (a<<8&0x8000)|
464 (r<<7&0x7c00)|
465 (g<<2&0x03e0)|
466 (b>>3&0x001f);
469 static inline void setpixel_24bppRGB(BYTE r, BYTE g, BYTE b, BYTE a,
470 BYTE *row, UINT x)
472 row[x*3+2] = r;
473 row[x*3+1] = g;
474 row[x*3] = b;
477 static inline void setpixel_32bppRGB(BYTE r, BYTE g, BYTE b, BYTE a,
478 BYTE *row, UINT x)
480 *((DWORD*)(row)+x) = (r<<16)|(g<<8)|b;
483 static inline void setpixel_32bppARGB(BYTE r, BYTE g, BYTE b, BYTE a,
484 BYTE *row, UINT x)
486 *((DWORD*)(row)+x) = (a<<24)|(r<<16)|(g<<8)|b;
489 static inline void setpixel_32bppPARGB(BYTE r, BYTE g, BYTE b, BYTE a,
490 BYTE *row, UINT x)
492 r = (r * a + 127) / 255;
493 g = (g * a + 127) / 255;
494 b = (b * a + 127) / 255;
495 *((DWORD*)(row)+x) = (a<<24)|(r<<16)|(g<<8)|b;
498 static inline void setpixel_48bppRGB(BYTE r, BYTE g, BYTE b, BYTE a,
499 BYTE *row, UINT x)
501 row[x*6+5] = row[x*6+4] = r;
502 row[x*6+3] = row[x*6+2] = g;
503 row[x*6+1] = row[x*6] = b;
506 static inline void setpixel_64bppARGB(BYTE r, BYTE g, BYTE b, BYTE a,
507 BYTE *row, UINT x)
509 UINT64 a64=a, r64=r, g64=g, b64=b;
510 *((UINT64*)(row)+x) = (a64<<56)|(a64<<48)|(r64<<40)|(r64<<32)|(g64<<24)|(g64<<16)|(b64<<8)|b64;
513 static inline void setpixel_64bppPARGB(BYTE r, BYTE g, BYTE b, BYTE a,
514 BYTE *row, UINT x)
516 UINT64 a64, r64, g64, b64;
517 a64 = a * 257;
518 r64 = r * a / 255;
519 g64 = g * a / 255;
520 b64 = b * a / 255;
521 *((UINT64*)(row)+x) = (a64<<48)|(r64<<32)|(g64<<16)|b64;
524 GpStatus WINGDIPAPI GdipBitmapSetPixel(GpBitmap* bitmap, INT x, INT y,
525 ARGB color)
527 BYTE a, r, g, b;
528 BYTE *row;
530 if(!bitmap || x < 0 || y < 0 || x >= bitmap->width || y >= bitmap->height)
531 return InvalidParameter;
533 a = color>>24;
534 r = color>>16;
535 g = color>>8;
536 b = color;
538 row = bitmap->bits + bitmap->stride * y;
540 switch (bitmap->format)
542 case PixelFormat16bppGrayScale:
543 setpixel_16bppGrayScale(r,g,b,a,row,x);
544 break;
545 case PixelFormat16bppRGB555:
546 setpixel_16bppRGB555(r,g,b,a,row,x);
547 break;
548 case PixelFormat16bppRGB565:
549 setpixel_16bppRGB565(r,g,b,a,row,x);
550 break;
551 case PixelFormat16bppARGB1555:
552 setpixel_16bppARGB1555(r,g,b,a,row,x);
553 break;
554 case PixelFormat24bppRGB:
555 setpixel_24bppRGB(r,g,b,a,row,x);
556 break;
557 case PixelFormat32bppRGB:
558 setpixel_32bppRGB(r,g,b,a,row,x);
559 break;
560 case PixelFormat32bppARGB:
561 setpixel_32bppARGB(r,g,b,a,row,x);
562 break;
563 case PixelFormat32bppPARGB:
564 setpixel_32bppPARGB(r,g,b,a,row,x);
565 break;
566 case PixelFormat48bppRGB:
567 setpixel_48bppRGB(r,g,b,a,row,x);
568 break;
569 case PixelFormat64bppARGB:
570 setpixel_64bppARGB(r,g,b,a,row,x);
571 break;
572 case PixelFormat64bppPARGB:
573 setpixel_64bppPARGB(r,g,b,a,row,x);
574 break;
575 case PixelFormat8bppIndexed:
576 setpixel_8bppIndexed(r,g,b,a,row,x,bitmap->image.palette);
577 break;
578 case PixelFormat4bppIndexed:
579 setpixel_4bppIndexed(r,g,b,a,row,x,bitmap->image.palette);
580 break;
581 case PixelFormat1bppIndexed:
582 setpixel_1bppIndexed(r,g,b,a,row,x,bitmap->image.palette);
583 break;
584 default:
585 FIXME("not implemented for format 0x%x\n", bitmap->format);
586 return NotImplemented;
589 return Ok;
592 GpStatus convert_pixels(INT width, INT height,
593 INT dst_stride, BYTE *dst_bits, PixelFormat dst_format,
594 ColorPalette *dst_palette,
595 INT src_stride, const BYTE *src_bits, PixelFormat src_format,
596 ColorPalette *src_palette)
598 INT x, y;
600 if (src_format == dst_format ||
601 (dst_format == PixelFormat32bppRGB && PIXELFORMATBPP(src_format) == 32))
603 UINT widthbytes = PIXELFORMATBPP(src_format) * width / 8;
604 for (y=0; y<height; y++)
605 memcpy(dst_bits+dst_stride*y, src_bits+src_stride*y, widthbytes);
606 return Ok;
609 #define convert_indexed_to_rgb(getpixel_function, setpixel_function) do { \
610 for (y=0; y<height; y++) \
611 for (x=0; x<width; x++) { \
612 BYTE index; \
613 ARGB argb; \
614 BYTE *color = (BYTE *)&argb; \
615 getpixel_function(&index, src_bits+src_stride*y, x); \
616 argb = (src_palette && index < src_palette->Count) ? src_palette->Entries[index] : 0; \
617 setpixel_function(color[2], color[1], color[0], color[3], dst_bits+dst_stride*y, x); \
619 return Ok; \
620 } while (0);
622 #define convert_rgb_to_rgb(getpixel_function, setpixel_function) do { \
623 for (y=0; y<height; y++) \
624 for (x=0; x<width; x++) { \
625 BYTE r, g, b, a; \
626 getpixel_function(&r, &g, &b, &a, src_bits+src_stride*y, x); \
627 setpixel_function(r, g, b, a, dst_bits+dst_stride*y, x); \
629 return Ok; \
630 } while (0);
632 #define convert_rgb_to_indexed(getpixel_function, setpixel_function) do { \
633 for (y=0; y<height; y++) \
634 for (x=0; x<width; x++) { \
635 BYTE r, g, b, a; \
636 getpixel_function(&r, &g, &b, &a, src_bits+src_stride*y, x); \
637 setpixel_function(r, g, b, a, dst_bits+dst_stride*y, x, dst_palette); \
639 return Ok; \
640 } while (0);
642 switch (src_format)
644 case PixelFormat1bppIndexed:
645 switch (dst_format)
647 case PixelFormat16bppGrayScale:
648 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_16bppGrayScale);
649 case PixelFormat16bppRGB555:
650 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_16bppRGB555);
651 case PixelFormat16bppRGB565:
652 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_16bppRGB565);
653 case PixelFormat16bppARGB1555:
654 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_16bppARGB1555);
655 case PixelFormat24bppRGB:
656 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_24bppRGB);
657 case PixelFormat32bppRGB:
658 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_32bppRGB);
659 case PixelFormat32bppARGB:
660 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_32bppARGB);
661 case PixelFormat32bppPARGB:
662 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_32bppPARGB);
663 case PixelFormat48bppRGB:
664 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_48bppRGB);
665 case PixelFormat64bppARGB:
666 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_64bppARGB);
667 default:
668 break;
670 break;
671 case PixelFormat4bppIndexed:
672 switch (dst_format)
674 case PixelFormat16bppGrayScale:
675 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_16bppGrayScale);
676 case PixelFormat16bppRGB555:
677 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_16bppRGB555);
678 case PixelFormat16bppRGB565:
679 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_16bppRGB565);
680 case PixelFormat16bppARGB1555:
681 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_16bppARGB1555);
682 case PixelFormat24bppRGB:
683 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_24bppRGB);
684 case PixelFormat32bppRGB:
685 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_32bppRGB);
686 case PixelFormat32bppARGB:
687 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_32bppARGB);
688 case PixelFormat32bppPARGB:
689 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_32bppPARGB);
690 case PixelFormat48bppRGB:
691 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_48bppRGB);
692 case PixelFormat64bppARGB:
693 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_64bppARGB);
694 default:
695 break;
697 break;
698 case PixelFormat8bppIndexed:
699 switch (dst_format)
701 case PixelFormat16bppGrayScale:
702 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_16bppGrayScale);
703 case PixelFormat16bppRGB555:
704 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_16bppRGB555);
705 case PixelFormat16bppRGB565:
706 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_16bppRGB565);
707 case PixelFormat16bppARGB1555:
708 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_16bppARGB1555);
709 case PixelFormat24bppRGB:
710 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_24bppRGB);
711 case PixelFormat32bppRGB:
712 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_32bppRGB);
713 case PixelFormat32bppARGB:
714 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_32bppARGB);
715 case PixelFormat32bppPARGB:
716 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_32bppPARGB);
717 case PixelFormat48bppRGB:
718 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_48bppRGB);
719 case PixelFormat64bppARGB:
720 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_64bppARGB);
721 default:
722 break;
724 break;
725 case PixelFormat16bppGrayScale:
726 switch (dst_format)
728 case PixelFormat1bppIndexed:
729 convert_rgb_to_indexed(getpixel_16bppGrayScale, setpixel_1bppIndexed);
730 case PixelFormat4bppIndexed:
731 convert_rgb_to_indexed(getpixel_16bppGrayScale, setpixel_4bppIndexed);
732 case PixelFormat8bppIndexed:
733 convert_rgb_to_indexed(getpixel_16bppGrayScale, setpixel_8bppIndexed);
734 case PixelFormat16bppRGB555:
735 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_16bppRGB555);
736 case PixelFormat16bppRGB565:
737 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_16bppRGB565);
738 case PixelFormat16bppARGB1555:
739 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_16bppARGB1555);
740 case PixelFormat24bppRGB:
741 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_24bppRGB);
742 case PixelFormat32bppRGB:
743 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_32bppRGB);
744 case PixelFormat32bppARGB:
745 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_32bppARGB);
746 case PixelFormat32bppPARGB:
747 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_32bppPARGB);
748 case PixelFormat48bppRGB:
749 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_48bppRGB);
750 case PixelFormat64bppARGB:
751 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_64bppARGB);
752 default:
753 break;
755 break;
756 case PixelFormat16bppRGB555:
757 switch (dst_format)
759 case PixelFormat1bppIndexed:
760 convert_rgb_to_indexed(getpixel_16bppRGB555, setpixel_1bppIndexed);
761 case PixelFormat4bppIndexed:
762 convert_rgb_to_indexed(getpixel_16bppRGB555, setpixel_4bppIndexed);
763 case PixelFormat8bppIndexed:
764 convert_rgb_to_indexed(getpixel_16bppRGB555, setpixel_8bppIndexed);
765 case PixelFormat16bppGrayScale:
766 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_16bppGrayScale);
767 case PixelFormat16bppRGB565:
768 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_16bppRGB565);
769 case PixelFormat16bppARGB1555:
770 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_16bppARGB1555);
771 case PixelFormat24bppRGB:
772 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_24bppRGB);
773 case PixelFormat32bppRGB:
774 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_32bppRGB);
775 case PixelFormat32bppARGB:
776 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_32bppARGB);
777 case PixelFormat32bppPARGB:
778 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_32bppPARGB);
779 case PixelFormat48bppRGB:
780 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_48bppRGB);
781 case PixelFormat64bppARGB:
782 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_64bppARGB);
783 default:
784 break;
786 break;
787 case PixelFormat16bppRGB565:
788 switch (dst_format)
790 case PixelFormat1bppIndexed:
791 convert_rgb_to_indexed(getpixel_16bppRGB565, setpixel_1bppIndexed);
792 case PixelFormat4bppIndexed:
793 convert_rgb_to_indexed(getpixel_16bppRGB565, setpixel_4bppIndexed);
794 case PixelFormat8bppIndexed:
795 convert_rgb_to_indexed(getpixel_16bppRGB565, setpixel_8bppIndexed);
796 case PixelFormat16bppGrayScale:
797 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_16bppGrayScale);
798 case PixelFormat16bppRGB555:
799 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_16bppRGB555);
800 case PixelFormat16bppARGB1555:
801 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_16bppARGB1555);
802 case PixelFormat24bppRGB:
803 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_24bppRGB);
804 case PixelFormat32bppRGB:
805 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_32bppRGB);
806 case PixelFormat32bppARGB:
807 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_32bppARGB);
808 case PixelFormat32bppPARGB:
809 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_32bppPARGB);
810 case PixelFormat48bppRGB:
811 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_48bppRGB);
812 case PixelFormat64bppARGB:
813 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_64bppARGB);
814 default:
815 break;
817 break;
818 case PixelFormat16bppARGB1555:
819 switch (dst_format)
821 case PixelFormat1bppIndexed:
822 convert_rgb_to_indexed(getpixel_16bppARGB1555, setpixel_1bppIndexed);
823 case PixelFormat4bppIndexed:
824 convert_rgb_to_indexed(getpixel_16bppARGB1555, setpixel_4bppIndexed);
825 case PixelFormat8bppIndexed:
826 convert_rgb_to_indexed(getpixel_16bppARGB1555, setpixel_8bppIndexed);
827 case PixelFormat16bppGrayScale:
828 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_16bppGrayScale);
829 case PixelFormat16bppRGB555:
830 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_16bppRGB555);
831 case PixelFormat16bppRGB565:
832 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_16bppRGB565);
833 case PixelFormat24bppRGB:
834 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_24bppRGB);
835 case PixelFormat32bppRGB:
836 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_32bppRGB);
837 case PixelFormat32bppARGB:
838 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_32bppARGB);
839 case PixelFormat32bppPARGB:
840 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_32bppPARGB);
841 case PixelFormat48bppRGB:
842 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_48bppRGB);
843 case PixelFormat64bppARGB:
844 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_64bppARGB);
845 default:
846 break;
848 break;
849 case PixelFormat24bppRGB:
850 switch (dst_format)
852 case PixelFormat1bppIndexed:
853 convert_rgb_to_indexed(getpixel_24bppRGB, setpixel_1bppIndexed);
854 case PixelFormat4bppIndexed:
855 convert_rgb_to_indexed(getpixel_24bppRGB, setpixel_4bppIndexed);
856 case PixelFormat8bppIndexed:
857 convert_rgb_to_indexed(getpixel_24bppRGB, setpixel_8bppIndexed);
858 case PixelFormat16bppGrayScale:
859 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_16bppGrayScale);
860 case PixelFormat16bppRGB555:
861 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_16bppRGB555);
862 case PixelFormat16bppRGB565:
863 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_16bppRGB565);
864 case PixelFormat16bppARGB1555:
865 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_16bppARGB1555);
866 case PixelFormat32bppRGB:
867 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_32bppRGB);
868 case PixelFormat32bppARGB:
869 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_32bppARGB);
870 case PixelFormat32bppPARGB:
871 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_32bppPARGB);
872 case PixelFormat48bppRGB:
873 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_48bppRGB);
874 case PixelFormat64bppARGB:
875 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_64bppARGB);
876 default:
877 break;
879 break;
880 case PixelFormat32bppRGB:
881 switch (dst_format)
883 case PixelFormat1bppIndexed:
884 convert_rgb_to_indexed(getpixel_32bppRGB, setpixel_1bppIndexed);
885 case PixelFormat4bppIndexed:
886 convert_rgb_to_indexed(getpixel_32bppRGB, setpixel_4bppIndexed);
887 case PixelFormat8bppIndexed:
888 convert_rgb_to_indexed(getpixel_32bppRGB, setpixel_8bppIndexed);
889 case PixelFormat16bppGrayScale:
890 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_16bppGrayScale);
891 case PixelFormat16bppRGB555:
892 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_16bppRGB555);
893 case PixelFormat16bppRGB565:
894 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_16bppRGB565);
895 case PixelFormat16bppARGB1555:
896 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_16bppARGB1555);
897 case PixelFormat24bppRGB:
898 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_24bppRGB);
899 case PixelFormat32bppARGB:
900 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_32bppARGB);
901 case PixelFormat32bppPARGB:
902 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_32bppPARGB);
903 case PixelFormat48bppRGB:
904 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_48bppRGB);
905 case PixelFormat64bppARGB:
906 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_64bppARGB);
907 default:
908 break;
910 break;
911 case PixelFormat32bppARGB:
912 switch (dst_format)
914 case PixelFormat1bppIndexed:
915 convert_rgb_to_indexed(getpixel_32bppARGB, setpixel_1bppIndexed);
916 case PixelFormat4bppIndexed:
917 convert_rgb_to_indexed(getpixel_32bppARGB, setpixel_4bppIndexed);
918 case PixelFormat8bppIndexed:
919 convert_rgb_to_indexed(getpixel_32bppARGB, setpixel_8bppIndexed);
920 case PixelFormat16bppGrayScale:
921 convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_16bppGrayScale);
922 case PixelFormat16bppRGB555:
923 convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_16bppRGB555);
924 case PixelFormat16bppRGB565:
925 convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_16bppRGB565);
926 case PixelFormat16bppARGB1555:
927 convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_16bppARGB1555);
928 case PixelFormat24bppRGB:
929 convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_24bppRGB);
930 case PixelFormat32bppPARGB:
931 convert_32bppARGB_to_32bppPARGB(width, height, dst_bits, dst_stride, src_bits, src_stride);
932 return Ok;
933 case PixelFormat48bppRGB:
934 convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_48bppRGB);
935 case PixelFormat64bppARGB:
936 convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_64bppARGB);
937 default:
938 break;
940 break;
941 case PixelFormat32bppPARGB:
942 switch (dst_format)
944 case PixelFormat1bppIndexed:
945 convert_rgb_to_indexed(getpixel_32bppPARGB, setpixel_1bppIndexed);
946 case PixelFormat4bppIndexed:
947 convert_rgb_to_indexed(getpixel_32bppPARGB, setpixel_4bppIndexed);
948 case PixelFormat8bppIndexed:
949 convert_rgb_to_indexed(getpixel_32bppPARGB, setpixel_8bppIndexed);
950 case PixelFormat16bppGrayScale:
951 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_16bppGrayScale);
952 case PixelFormat16bppRGB555:
953 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_16bppRGB555);
954 case PixelFormat16bppRGB565:
955 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_16bppRGB565);
956 case PixelFormat16bppARGB1555:
957 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_16bppARGB1555);
958 case PixelFormat24bppRGB:
959 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_24bppRGB);
960 case PixelFormat32bppRGB:
961 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_32bppRGB);
962 case PixelFormat32bppARGB:
963 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_32bppARGB);
964 case PixelFormat48bppRGB:
965 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_48bppRGB);
966 case PixelFormat64bppARGB:
967 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_64bppARGB);
968 default:
969 break;
971 break;
972 case PixelFormat48bppRGB:
973 switch (dst_format)
975 case PixelFormat1bppIndexed:
976 convert_rgb_to_indexed(getpixel_48bppRGB, setpixel_1bppIndexed);
977 case PixelFormat4bppIndexed:
978 convert_rgb_to_indexed(getpixel_48bppRGB, setpixel_4bppIndexed);
979 case PixelFormat8bppIndexed:
980 convert_rgb_to_indexed(getpixel_48bppRGB, setpixel_8bppIndexed);
981 case PixelFormat16bppGrayScale:
982 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_16bppGrayScale);
983 case PixelFormat16bppRGB555:
984 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_16bppRGB555);
985 case PixelFormat16bppRGB565:
986 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_16bppRGB565);
987 case PixelFormat16bppARGB1555:
988 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_16bppARGB1555);
989 case PixelFormat24bppRGB:
990 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_24bppRGB);
991 case PixelFormat32bppRGB:
992 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_32bppRGB);
993 case PixelFormat32bppARGB:
994 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_32bppARGB);
995 case PixelFormat32bppPARGB:
996 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_32bppPARGB);
997 case PixelFormat64bppARGB:
998 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_64bppARGB);
999 default:
1000 break;
1002 break;
1003 case PixelFormat64bppARGB:
1004 switch (dst_format)
1006 case PixelFormat1bppIndexed:
1007 convert_rgb_to_indexed(getpixel_64bppARGB, setpixel_1bppIndexed);
1008 case PixelFormat4bppIndexed:
1009 convert_rgb_to_indexed(getpixel_64bppARGB, setpixel_4bppIndexed);
1010 case PixelFormat8bppIndexed:
1011 convert_rgb_to_indexed(getpixel_64bppARGB, setpixel_8bppIndexed);
1012 case PixelFormat16bppGrayScale:
1013 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_16bppGrayScale);
1014 case PixelFormat16bppRGB555:
1015 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_16bppRGB555);
1016 case PixelFormat16bppRGB565:
1017 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_16bppRGB565);
1018 case PixelFormat16bppARGB1555:
1019 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_16bppARGB1555);
1020 case PixelFormat24bppRGB:
1021 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_24bppRGB);
1022 case PixelFormat32bppRGB:
1023 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_32bppRGB);
1024 case PixelFormat32bppARGB:
1025 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_32bppARGB);
1026 case PixelFormat32bppPARGB:
1027 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_32bppPARGB);
1028 case PixelFormat48bppRGB:
1029 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_48bppRGB);
1030 default:
1031 break;
1033 break;
1034 case PixelFormat64bppPARGB:
1035 switch (dst_format)
1037 case PixelFormat1bppIndexed:
1038 convert_rgb_to_indexed(getpixel_64bppPARGB, setpixel_1bppIndexed);
1039 case PixelFormat4bppIndexed:
1040 convert_rgb_to_indexed(getpixel_64bppPARGB, setpixel_4bppIndexed);
1041 case PixelFormat8bppIndexed:
1042 convert_rgb_to_indexed(getpixel_64bppPARGB, setpixel_8bppIndexed);
1043 case PixelFormat16bppGrayScale:
1044 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_16bppGrayScale);
1045 case PixelFormat16bppRGB555:
1046 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_16bppRGB555);
1047 case PixelFormat16bppRGB565:
1048 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_16bppRGB565);
1049 case PixelFormat16bppARGB1555:
1050 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_16bppARGB1555);
1051 case PixelFormat24bppRGB:
1052 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_24bppRGB);
1053 case PixelFormat32bppRGB:
1054 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_32bppRGB);
1055 case PixelFormat32bppARGB:
1056 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_32bppARGB);
1057 case PixelFormat32bppPARGB:
1058 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_32bppPARGB);
1059 case PixelFormat48bppRGB:
1060 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_48bppRGB);
1061 case PixelFormat64bppARGB:
1062 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_64bppARGB);
1063 default:
1064 break;
1066 break;
1067 default:
1068 break;
1071 #undef convert_indexed_to_rgb
1072 #undef convert_rgb_to_rgb
1074 return NotImplemented;
1077 /* This function returns a pointer to an array of pixels that represents the
1078 * bitmap. The *entire* bitmap is locked according to the lock mode specified by
1079 * flags. It is correct behavior that a user who calls this function with write
1080 * privileges can write to the whole bitmap (not just the area in rect).
1082 * FIXME: only used portion of format is bits per pixel. */
1083 GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
1084 UINT flags, PixelFormat format, BitmapData* lockeddata)
1086 INT bitspp = PIXELFORMATBPP(format);
1087 GpRect act_rect; /* actual rect to be used */
1088 GpStatus stat;
1090 TRACE("%p %p %d 0x%x %p\n", bitmap, rect, flags, format, lockeddata);
1092 if(!lockeddata || !bitmap)
1093 return InvalidParameter;
1094 if(!image_lock(&bitmap->image))
1095 return ObjectBusy;
1097 if(rect){
1098 if(rect->X < 0 || rect->Y < 0 || (rect->X + rect->Width > bitmap->width) ||
1099 (rect->Y + rect->Height > bitmap->height) || !flags)
1101 image_unlock(&bitmap->image);
1102 return InvalidParameter;
1105 act_rect = *rect;
1107 else{
1108 act_rect.X = act_rect.Y = 0;
1109 act_rect.Width = bitmap->width;
1110 act_rect.Height = bitmap->height;
1113 if(bitmap->lockmode)
1115 WARN("bitmap is already locked and cannot be locked again\n");
1116 image_unlock(&bitmap->image);
1117 return WrongState;
1120 if (bitmap->bits && bitmap->format == format && !(flags & ImageLockModeUserInputBuf))
1122 /* no conversion is necessary; just use the bits directly */
1123 lockeddata->Width = act_rect.Width;
1124 lockeddata->Height = act_rect.Height;
1125 lockeddata->PixelFormat = format;
1126 lockeddata->Reserved = flags;
1127 lockeddata->Stride = bitmap->stride;
1128 lockeddata->Scan0 = bitmap->bits + (bitspp / 8) * act_rect.X +
1129 bitmap->stride * act_rect.Y;
1131 bitmap->lockmode = flags | ImageLockModeRead;
1133 image_unlock(&bitmap->image);
1134 return Ok;
1137 /* Make sure we can convert to the requested format. */
1138 if (flags & ImageLockModeRead)
1140 stat = convert_pixels(0, 0, 0, NULL, format, NULL, 0, NULL, bitmap->format, NULL);
1141 if (stat == NotImplemented)
1143 FIXME("cannot read bitmap from %x to %x\n", bitmap->format, format);
1144 image_unlock(&bitmap->image);
1145 return NotImplemented;
1149 /* If we're opening for writing, make sure we'll be able to write back in
1150 * the original format. */
1151 if (flags & ImageLockModeWrite)
1153 stat = convert_pixels(0, 0, 0, NULL, bitmap->format, NULL, 0, NULL, format, NULL);
1154 if (stat == NotImplemented)
1156 FIXME("cannot write bitmap from %x to %x\n", format, bitmap->format);
1157 image_unlock(&bitmap->image);
1158 return NotImplemented;
1162 lockeddata->Width = act_rect.Width;
1163 lockeddata->Height = act_rect.Height;
1164 lockeddata->PixelFormat = format;
1165 lockeddata->Reserved = flags;
1167 if(!(flags & ImageLockModeUserInputBuf))
1169 lockeddata->Stride = (((act_rect.Width * bitspp + 7) / 8) + 3) & ~3;
1171 bitmap->bitmapbits = heap_alloc_zero(lockeddata->Stride * act_rect.Height);
1173 if (!bitmap->bitmapbits)
1175 image_unlock(&bitmap->image);
1176 return OutOfMemory;
1179 lockeddata->Scan0 = bitmap->bitmapbits;
1182 if (flags & ImageLockModeRead)
1184 static BOOL fixme = FALSE;
1186 if (!fixme && (PIXELFORMATBPP(bitmap->format) * act_rect.X) % 8 != 0)
1188 FIXME("Cannot copy rows that don't start at a whole byte.\n");
1189 fixme = TRUE;
1192 stat = convert_pixels(act_rect.Width, act_rect.Height,
1193 lockeddata->Stride, lockeddata->Scan0, format, bitmap->image.palette,
1194 bitmap->stride,
1195 bitmap->bits + bitmap->stride * act_rect.Y + PIXELFORMATBPP(bitmap->format) * act_rect.X / 8,
1196 bitmap->format, bitmap->image.palette);
1198 if (stat != Ok)
1200 heap_free(bitmap->bitmapbits);
1201 bitmap->bitmapbits = NULL;
1202 image_unlock(&bitmap->image);
1203 return stat;
1207 bitmap->lockmode = flags | ImageLockModeRead;
1208 bitmap->lockx = act_rect.X;
1209 bitmap->locky = act_rect.Y;
1211 image_unlock(&bitmap->image);
1212 return Ok;
1215 GpStatus WINGDIPAPI GdipBitmapSetResolution(GpBitmap* bitmap, REAL xdpi, REAL ydpi)
1217 TRACE("(%p, %.2f, %.2f)\n", bitmap, xdpi, ydpi);
1219 if (!bitmap || xdpi == 0.0 || ydpi == 0.0)
1220 return InvalidParameter;
1222 bitmap->image.xres = xdpi;
1223 bitmap->image.yres = ydpi;
1225 return Ok;
1228 GpStatus WINGDIPAPI GdipBitmapUnlockBits(GpBitmap* bitmap,
1229 BitmapData* lockeddata)
1231 GpStatus stat;
1232 static BOOL fixme = FALSE;
1234 TRACE("(%p,%p)\n", bitmap, lockeddata);
1236 if(!bitmap || !lockeddata)
1237 return InvalidParameter;
1238 if(!image_lock(&bitmap->image))
1239 return ObjectBusy;
1241 if(!bitmap->lockmode)
1243 image_unlock(&bitmap->image);
1244 return WrongState;
1247 if(!(lockeddata->Reserved & ImageLockModeWrite)){
1248 bitmap->lockmode = 0;
1249 heap_free(bitmap->bitmapbits);
1250 bitmap->bitmapbits = NULL;
1251 image_unlock(&bitmap->image);
1252 return Ok;
1255 if (!bitmap->bitmapbits && !(lockeddata->Reserved & ImageLockModeUserInputBuf))
1257 /* we passed a direct reference; no need to do anything */
1258 bitmap->lockmode = 0;
1259 image_unlock(&bitmap->image);
1260 return Ok;
1263 if (!fixme && (PIXELFORMATBPP(bitmap->format) * bitmap->lockx) % 8 != 0)
1265 FIXME("Cannot copy rows that don't start at a whole byte.\n");
1266 fixme = TRUE;
1269 /* FIXME: Pass src_palette generated from lockeddata->PixelFormat. */
1270 stat = convert_pixels(lockeddata->Width, lockeddata->Height,
1271 bitmap->stride,
1272 bitmap->bits + bitmap->stride * bitmap->locky + PIXELFORMATBPP(bitmap->format) * bitmap->lockx / 8,
1273 bitmap->format, bitmap->image.palette,
1274 lockeddata->Stride, lockeddata->Scan0, lockeddata->PixelFormat, NULL);
1276 if (stat != Ok)
1278 ERR("failed to convert pixels; this should never happen\n");
1281 heap_free(bitmap->bitmapbits);
1282 bitmap->bitmapbits = NULL;
1283 bitmap->lockmode = 0;
1285 image_unlock(&bitmap->image);
1286 return stat;
1289 GpStatus WINGDIPAPI GdipCloneBitmapArea(REAL x, REAL y, REAL width, REAL height,
1290 PixelFormat format, GpBitmap* srcBitmap, GpBitmap** dstBitmap)
1292 Rect area;
1293 GpStatus stat;
1295 TRACE("(%f,%f,%f,%f,0x%x,%p,%p)\n", x, y, width, height, format, srcBitmap, dstBitmap);
1297 if (!srcBitmap || !dstBitmap || srcBitmap->image.type != ImageTypeBitmap ||
1298 x < 0 || y < 0 ||
1299 x + width > srcBitmap->width || y + height > srcBitmap->height)
1301 TRACE("<-- InvalidParameter\n");
1302 return InvalidParameter;
1305 if (format == PixelFormatDontCare)
1306 format = srcBitmap->format;
1308 area.X = gdip_round(x);
1309 area.Y = gdip_round(y);
1310 area.Width = gdip_round(width);
1311 area.Height = gdip_round(height);
1313 stat = GdipCreateBitmapFromScan0(area.Width, area.Height, 0, format, NULL, dstBitmap);
1314 if (stat == Ok)
1316 stat = convert_pixels(area.Width, area.Height, (*dstBitmap)->stride, (*dstBitmap)->bits, (*dstBitmap)->format,
1317 (*dstBitmap)->image.palette, srcBitmap->stride,
1318 srcBitmap->bits + srcBitmap->stride * area.Y + PIXELFORMATBPP(srcBitmap->format) * area.X / 8,
1319 srcBitmap->format, srcBitmap->image.palette);
1321 if (stat == Ok && srcBitmap->image.palette)
1323 ColorPalette *src_palette, *dst_palette;
1325 src_palette = srcBitmap->image.palette;
1327 dst_palette = heap_alloc_zero(sizeof(UINT) * 2 + sizeof(ARGB) * src_palette->Count);
1329 if (dst_palette)
1331 dst_palette->Flags = src_palette->Flags;
1332 dst_palette->Count = src_palette->Count;
1333 memcpy(dst_palette->Entries, src_palette->Entries, sizeof(ARGB) * src_palette->Count);
1335 heap_free((*dstBitmap)->image.palette);
1336 (*dstBitmap)->image.palette = dst_palette;
1338 else
1339 stat = OutOfMemory;
1342 if (stat != Ok)
1343 GdipDisposeImage(&(*dstBitmap)->image);
1346 if (stat != Ok)
1347 *dstBitmap = NULL;
1349 return stat;
1352 GpStatus WINGDIPAPI GdipCloneBitmapAreaI(INT x, INT y, INT width, INT height,
1353 PixelFormat format, GpBitmap* srcBitmap, GpBitmap** dstBitmap)
1355 TRACE("(%i,%i,%i,%i,0x%x,%p,%p)\n", x, y, width, height, format, srcBitmap, dstBitmap);
1357 return GdipCloneBitmapArea(x, y, width, height, format, srcBitmap, dstBitmap);
1360 GpStatus WINGDIPAPI GdipCloneImage(GpImage *image, GpImage **cloneImage)
1362 TRACE("%p, %p\n", image, cloneImage);
1364 if (!image || !cloneImage)
1365 return InvalidParameter;
1367 if (image->type == ImageTypeBitmap)
1369 GpBitmap *bitmap = (GpBitmap *)image;
1371 return GdipCloneBitmapAreaI(0, 0, bitmap->width, bitmap->height,
1372 bitmap->format, bitmap, (GpBitmap **)cloneImage);
1374 else if (image->type == ImageTypeMetafile && ((GpMetafile*)image)->hemf)
1376 GpMetafile *result, *metafile;
1378 metafile = (GpMetafile*)image;
1380 result = heap_alloc_zero(sizeof(*result));
1381 if (!result)
1382 return OutOfMemory;
1384 result->image.type = ImageTypeMetafile;
1385 result->image.format = image->format;
1386 result->image.flags = image->flags;
1387 result->image.frame_count = 1;
1388 result->image.xres = image->xres;
1389 result->image.yres = image->yres;
1390 result->bounds = metafile->bounds;
1391 result->unit = metafile->unit;
1392 result->metafile_type = metafile->metafile_type;
1393 result->hemf = CopyEnhMetaFileW(metafile->hemf, NULL);
1394 list_init(&result->containers);
1396 if (!result->hemf)
1398 heap_free(result);
1399 return OutOfMemory;
1402 *cloneImage = &result->image;
1403 return Ok;
1405 else
1407 WARN("GpImage with no image data (metafile in wrong state?)\n");
1408 return InvalidParameter;
1412 GpStatus WINGDIPAPI GdipCreateBitmapFromFile(GDIPCONST WCHAR* filename,
1413 GpBitmap **bitmap)
1415 GpStatus stat;
1416 IStream *stream;
1418 TRACE("(%s) %p\n", debugstr_w(filename), bitmap);
1420 if(!filename || !bitmap)
1421 return InvalidParameter;
1423 *bitmap = NULL;
1425 stat = GdipCreateStreamOnFile(filename, GENERIC_READ, &stream);
1427 if(stat != Ok)
1428 return stat;
1430 stat = GdipCreateBitmapFromStream(stream, bitmap);
1432 IStream_Release(stream);
1434 return stat;
1437 GpStatus WINGDIPAPI GdipCreateBitmapFromGdiDib(GDIPCONST BITMAPINFO* info,
1438 VOID *bits, GpBitmap **bitmap)
1440 DWORD height, stride;
1441 HBITMAP hbm;
1442 void *bmbits;
1443 GpStatus status;
1445 TRACE("(%p, %p, %p)\n", info, bits, bitmap);
1447 if (!info || !bits || !bitmap)
1448 return InvalidParameter;
1450 hbm = CreateDIBSection(0, info, DIB_RGB_COLORS, &bmbits, NULL, 0);
1451 if (!hbm)
1452 return InvalidParameter;
1454 height = abs(info->bmiHeader.biHeight);
1455 stride = ((info->bmiHeader.biWidth * info->bmiHeader.biBitCount + 31) >> 3) & ~3;
1456 TRACE("height %lu, stride %lu, image size %lu\n", height, stride, height * stride);
1458 memcpy(bmbits, bits, height * stride);
1460 status = GdipCreateBitmapFromHBITMAP(hbm, NULL, bitmap);
1461 DeleteObject(hbm);
1463 return status;
1466 /* FIXME: no icm */
1467 GpStatus WINGDIPAPI GdipCreateBitmapFromFileICM(GDIPCONST WCHAR* filename,
1468 GpBitmap **bitmap)
1470 TRACE("(%s) %p\n", debugstr_w(filename), bitmap);
1472 return GdipCreateBitmapFromFile(filename, bitmap);
1475 GpStatus WINGDIPAPI GdipCreateBitmapFromResource(HINSTANCE hInstance,
1476 GDIPCONST WCHAR* lpBitmapName, GpBitmap** bitmap)
1478 HBITMAP hbm;
1479 GpStatus stat = InvalidParameter;
1481 TRACE("%p (%s) %p\n", hInstance, debugstr_w(lpBitmapName), bitmap);
1483 if(!lpBitmapName || !bitmap)
1484 return InvalidParameter;
1486 /* load DIB */
1487 hbm = LoadImageW(hInstance, lpBitmapName, IMAGE_BITMAP, 0, 0,
1488 LR_CREATEDIBSECTION);
1490 if(hbm){
1491 stat = GdipCreateBitmapFromHBITMAP(hbm, NULL, bitmap);
1492 DeleteObject(hbm);
1495 return stat;
1498 static inline DWORD blend_argb_no_bkgnd_alpha(DWORD src, DWORD bkgnd)
1500 BYTE b = (BYTE)src;
1501 BYTE g = (BYTE)(src >> 8);
1502 BYTE r = (BYTE)(src >> 16);
1503 DWORD alpha = (BYTE)(src >> 24);
1504 return ((b + ((BYTE)bkgnd * (255 - alpha) + 127) / 255) |
1505 (g + ((BYTE)(bkgnd >> 8) * (255 - alpha) + 127) / 255) << 8 |
1506 (r + ((BYTE)(bkgnd >> 16) * (255 - alpha) + 127) / 255) << 16 |
1507 (alpha << 24));
1510 GpStatus WINGDIPAPI GdipCreateHBITMAPFromBitmap(GpBitmap* bitmap,
1511 HBITMAP* hbmReturn, ARGB background)
1513 GpStatus stat;
1514 HBITMAP result;
1515 UINT width, height;
1516 BITMAPINFOHEADER bih;
1517 LPBYTE bits;
1519 TRACE("(%p,%p,%lx)\n", bitmap, hbmReturn, background);
1521 if (!bitmap || !hbmReturn) return InvalidParameter;
1522 if (!image_lock(&bitmap->image)) return ObjectBusy;
1524 GdipGetImageWidth(&bitmap->image, &width);
1525 GdipGetImageHeight(&bitmap->image, &height);
1527 bih.biSize = sizeof(bih);
1528 bih.biWidth = width;
1529 bih.biHeight = height;
1530 bih.biPlanes = 1;
1531 bih.biBitCount = 32;
1532 bih.biCompression = BI_RGB;
1533 bih.biSizeImage = 0;
1534 bih.biXPelsPerMeter = 0;
1535 bih.biYPelsPerMeter = 0;
1536 bih.biClrUsed = 0;
1537 bih.biClrImportant = 0;
1539 result = CreateDIBSection(0, (BITMAPINFO*)&bih, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
1540 if (!result)
1542 image_unlock(&bitmap->image);
1543 return GenericError;
1546 stat = convert_pixels(width, height, -width*4,
1547 bits + (width * 4 * (height - 1)), PixelFormat32bppPARGB, bitmap->image.palette,
1548 bitmap->stride, bitmap->bits, bitmap->format, bitmap->image.palette);
1549 if (stat != Ok)
1551 DeleteObject(result);
1552 image_unlock(&bitmap->image);
1553 return stat;
1556 if (background & 0xffffff)
1558 DWORD *ptr;
1559 UINT i;
1560 for (ptr = (DWORD*)bits, i = 0; i < width * height; ptr++, i++)
1562 if ((*ptr & 0xff000000) == 0xff000000) continue;
1563 *ptr = blend_argb_no_bkgnd_alpha(*ptr, background);
1567 *hbmReturn = result;
1568 image_unlock(&bitmap->image);
1569 return Ok;
1572 GpStatus WINGDIPAPI GdipCreateBitmapFromGraphics(INT width, INT height,
1573 GpGraphics* target, GpBitmap** bitmap)
1575 GpStatus ret;
1577 TRACE("(%d, %d, %p, %p)\n", width, height, target, bitmap);
1579 if(!target || !bitmap)
1580 return InvalidParameter;
1582 ret = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppPARGB,
1583 NULL, bitmap);
1585 if (ret == Ok)
1587 GdipGetDpiX(target, &(*bitmap)->image.xres);
1588 GdipGetDpiY(target, &(*bitmap)->image.yres);
1591 return ret;
1594 GpStatus WINGDIPAPI GdipCreateBitmapFromHICON(HICON hicon, GpBitmap** bitmap)
1596 GpStatus stat;
1597 ICONINFO iinfo;
1598 BITMAP bm;
1599 int ret;
1600 UINT width, height, stride;
1601 GpRect rect;
1602 BitmapData lockeddata;
1603 HDC screendc;
1604 BOOL has_alpha;
1605 int x, y;
1606 BITMAPINFOHEADER bih;
1607 DWORD *src;
1608 BYTE *dst_row;
1609 DWORD *dst;
1611 TRACE("%p, %p\n", hicon, bitmap);
1613 if(!bitmap || !GetIconInfo(hicon, &iinfo))
1614 return InvalidParameter;
1616 /* get the size of the icon */
1617 ret = GetObjectA(iinfo.hbmColor ? iinfo.hbmColor : iinfo.hbmMask, sizeof(bm), &bm);
1618 if (ret == 0) {
1619 DeleteObject(iinfo.hbmColor);
1620 DeleteObject(iinfo.hbmMask);
1621 return GenericError;
1624 width = bm.bmWidth;
1625 height = iinfo.hbmColor ? abs(bm.bmHeight) : abs(bm.bmHeight) / 2;
1626 stride = width * 4;
1628 stat = GdipCreateBitmapFromScan0(width, height, stride, PixelFormat32bppARGB, NULL, bitmap);
1629 if (stat != Ok) {
1630 DeleteObject(iinfo.hbmColor);
1631 DeleteObject(iinfo.hbmMask);
1632 return stat;
1635 rect.X = 0;
1636 rect.Y = 0;
1637 rect.Width = width;
1638 rect.Height = height;
1640 stat = GdipBitmapLockBits(*bitmap, &rect, ImageLockModeWrite, PixelFormat32bppARGB, &lockeddata);
1641 if (stat != Ok) {
1642 DeleteObject(iinfo.hbmColor);
1643 DeleteObject(iinfo.hbmMask);
1644 GdipDisposeImage(&(*bitmap)->image);
1645 return stat;
1648 bih.biSize = sizeof(bih);
1649 bih.biWidth = width;
1650 bih.biHeight = iinfo.hbmColor ? -height: -height * 2;
1651 bih.biPlanes = 1;
1652 bih.biBitCount = 32;
1653 bih.biCompression = BI_RGB;
1654 bih.biSizeImage = 0;
1655 bih.biXPelsPerMeter = 0;
1656 bih.biYPelsPerMeter = 0;
1657 bih.biClrUsed = 0;
1658 bih.biClrImportant = 0;
1660 screendc = CreateCompatibleDC(0);
1661 if (iinfo.hbmColor)
1663 GetDIBits(screendc, iinfo.hbmColor, 0, height, lockeddata.Scan0, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
1665 if (bm.bmBitsPixel == 32)
1667 has_alpha = FALSE;
1669 /* If any pixel has a non-zero alpha, ignore hbmMask */
1670 src = (DWORD*)lockeddata.Scan0;
1671 for (x=0; x<width && !has_alpha; x++)
1672 for (y=0; y<height && !has_alpha; y++)
1673 if ((*src++ & 0xff000000) != 0)
1674 has_alpha = TRUE;
1676 else has_alpha = FALSE;
1678 else
1680 GetDIBits(screendc, iinfo.hbmMask, 0, height, lockeddata.Scan0, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
1681 has_alpha = FALSE;
1684 if (!has_alpha)
1686 if (iinfo.hbmMask)
1688 BYTE *bits = heap_alloc(height * stride);
1690 /* read alpha data from the mask */
1691 if (iinfo.hbmColor)
1692 GetDIBits(screendc, iinfo.hbmMask, 0, height, bits, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
1693 else
1694 GetDIBits(screendc, iinfo.hbmMask, height, height, bits, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
1696 src = (DWORD*)bits;
1697 dst_row = lockeddata.Scan0;
1698 for (y=0; y<height; y++)
1700 dst = (DWORD*)dst_row;
1701 for (x=0; x<height; x++)
1703 DWORD src_value = *src++;
1704 if (src_value)
1705 *dst++ = 0;
1706 else
1707 *dst++ |= 0xff000000;
1709 dst_row += lockeddata.Stride;
1712 heap_free(bits);
1714 else
1716 /* set constant alpha of 255 */
1717 dst_row = lockeddata.Scan0;
1718 for (y=0; y<height; y++)
1720 dst = (DWORD*)dst_row;
1721 for (x=0; x<height; x++)
1722 *dst++ |= 0xff000000;
1723 dst_row += lockeddata.Stride;
1728 DeleteDC(screendc);
1730 DeleteObject(iinfo.hbmColor);
1731 DeleteObject(iinfo.hbmMask);
1733 GdipBitmapUnlockBits(*bitmap, &lockeddata);
1735 return Ok;
1738 static void generate_halftone_palette(ARGB *entries, UINT count)
1740 static const BYTE halftone_values[6]={0x00,0x33,0x66,0x99,0xcc,0xff};
1741 UINT i;
1743 for (i=0; i<8 && i<count; i++)
1745 entries[i] = 0xff000000;
1746 if (i&1) entries[i] |= 0x800000;
1747 if (i&2) entries[i] |= 0x8000;
1748 if (i&4) entries[i] |= 0x80;
1751 if (8 < count)
1752 entries[i] = 0xffc0c0c0;
1754 for (i=9; i<16 && i<count; i++)
1756 entries[i] = 0xff000000;
1757 if (i&1) entries[i] |= 0xff0000;
1758 if (i&2) entries[i] |= 0xff00;
1759 if (i&4) entries[i] |= 0xff;
1762 for (i=16; i<40 && i<count; i++)
1764 entries[i] = 0;
1767 for (i=40; i<256 && i<count; i++)
1769 entries[i] = 0xff000000;
1770 entries[i] |= halftone_values[(i-40)%6];
1771 entries[i] |= halftone_values[((i-40)/6)%6] << 8;
1772 entries[i] |= halftone_values[((i-40)/36)%6] << 16;
1776 static GpStatus get_screen_resolution(REAL *xres, REAL *yres)
1778 HDC screendc = CreateCompatibleDC(0);
1780 if (!screendc) return GenericError;
1782 *xres = (REAL)GetDeviceCaps(screendc, LOGPIXELSX);
1783 *yres = (REAL)GetDeviceCaps(screendc, LOGPIXELSY);
1785 DeleteDC(screendc);
1787 return Ok;
1790 GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width, INT height, INT stride,
1791 PixelFormat format, BYTE* scan0, GpBitmap** bitmap)
1793 HBITMAP hbitmap=NULL;
1794 INT row_size, dib_stride;
1795 BYTE *bits=NULL, *own_bits=NULL;
1796 REAL xres, yres;
1797 GpStatus stat;
1799 TRACE("%d %d %d 0x%x %p %p\n", width, height, stride, format, scan0, bitmap);
1801 if (!bitmap) return InvalidParameter;
1803 if(width <= 0 || height <= 0 || (scan0 && (stride % 4))){
1804 *bitmap = NULL;
1805 return InvalidParameter;
1808 if(scan0 && !stride)
1809 return InvalidParameter;
1811 stat = get_screen_resolution(&xres, &yres);
1812 if (stat != Ok) return stat;
1814 row_size = (width * PIXELFORMATBPP(format)+7) / 8;
1815 dib_stride = (row_size + 3) & ~3;
1817 if(stride == 0)
1818 stride = dib_stride;
1820 if (format & PixelFormatGDI && !(format & (PixelFormatAlpha|PixelFormatIndexed)) && !scan0)
1822 char bmibuf[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
1823 BITMAPINFO *pbmi = (BITMAPINFO *)bmibuf;
1825 pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1826 pbmi->bmiHeader.biWidth = width;
1827 pbmi->bmiHeader.biHeight = -height;
1828 pbmi->bmiHeader.biPlanes = 1;
1829 /* FIXME: use the rest of the data from format */
1830 pbmi->bmiHeader.biBitCount = PIXELFORMATBPP(format);
1831 pbmi->bmiHeader.biCompression = BI_RGB;
1832 pbmi->bmiHeader.biSizeImage = 0;
1833 pbmi->bmiHeader.biXPelsPerMeter = 0;
1834 pbmi->bmiHeader.biYPelsPerMeter = 0;
1835 pbmi->bmiHeader.biClrUsed = 0;
1836 pbmi->bmiHeader.biClrImportant = 0;
1838 hbitmap = CreateDIBSection(0, pbmi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
1840 if (!hbitmap) return GenericError;
1842 stride = dib_stride;
1844 else
1846 /* Not a GDI format; don't try to make an HBITMAP. */
1847 if (scan0)
1848 bits = scan0;
1849 else
1851 INT size = abs(stride) * height;
1853 own_bits = bits = heap_alloc_zero(size);
1854 if (!own_bits) return OutOfMemory;
1856 if (stride < 0)
1857 bits += stride * (1 - height);
1861 *bitmap = heap_alloc_zero(sizeof(GpBitmap));
1862 if(!*bitmap)
1864 DeleteObject(hbitmap);
1865 heap_free(own_bits);
1866 return OutOfMemory;
1869 (*bitmap)->image.type = ImageTypeBitmap;
1870 memcpy(&(*bitmap)->image.format, &ImageFormatMemoryBMP, sizeof(GUID));
1871 (*bitmap)->image.flags = ImageFlagsNone;
1872 (*bitmap)->image.frame_count = 1;
1873 (*bitmap)->image.current_frame = 0;
1874 (*bitmap)->image.palette = NULL;
1875 (*bitmap)->image.xres = xres;
1876 (*bitmap)->image.yres = yres;
1877 (*bitmap)->width = width;
1878 (*bitmap)->height = height;
1879 (*bitmap)->format = format;
1880 (*bitmap)->image.decoder = NULL;
1881 (*bitmap)->image.encoder = NULL;
1882 (*bitmap)->hbitmap = hbitmap;
1883 (*bitmap)->hdc = NULL;
1884 (*bitmap)->bits = bits;
1885 (*bitmap)->stride = stride;
1886 (*bitmap)->own_bits = own_bits;
1887 (*bitmap)->metadata_reader = NULL;
1888 (*bitmap)->prop_count = 0;
1889 (*bitmap)->prop_item = NULL;
1891 /* set format-related flags */
1892 if (format & (PixelFormatAlpha|PixelFormatPAlpha|PixelFormatIndexed))
1893 (*bitmap)->image.flags |= ImageFlagsHasAlpha;
1895 if (format == PixelFormat1bppIndexed ||
1896 format == PixelFormat4bppIndexed ||
1897 format == PixelFormat8bppIndexed)
1899 (*bitmap)->image.palette = heap_alloc_zero(sizeof(UINT) * 2 + sizeof(ARGB) * (1 << PIXELFORMATBPP(format)));
1901 if (!(*bitmap)->image.palette)
1903 GdipDisposeImage(&(*bitmap)->image);
1904 *bitmap = NULL;
1905 return OutOfMemory;
1908 (*bitmap)->image.palette->Count = 1 << PIXELFORMATBPP(format);
1910 if (format == PixelFormat1bppIndexed)
1912 (*bitmap)->image.palette->Flags = PaletteFlagsGrayScale;
1913 (*bitmap)->image.palette->Entries[0] = 0xff000000;
1914 (*bitmap)->image.palette->Entries[1] = 0xffffffff;
1916 else
1918 if (format == PixelFormat8bppIndexed)
1919 (*bitmap)->image.palette->Flags = PaletteFlagsHalftone;
1921 generate_halftone_palette((*bitmap)->image.palette->Entries,
1922 (*bitmap)->image.palette->Count);
1926 TRACE("<-- %p\n", *bitmap);
1928 return Ok;
1931 GpStatus WINGDIPAPI GdipCreateBitmapFromStream(IStream* stream,
1932 GpBitmap **bitmap)
1934 GpStatus stat;
1936 TRACE("%p %p\n", stream, bitmap);
1938 stat = GdipLoadImageFromStream(stream, (GpImage**) bitmap);
1940 if(stat != Ok)
1941 return stat;
1943 if((*bitmap)->image.type != ImageTypeBitmap){
1944 GdipDisposeImage(&(*bitmap)->image);
1945 *bitmap = NULL;
1946 return GenericError; /* FIXME: what error to return? */
1949 return Ok;
1952 /* FIXME: no icm */
1953 GpStatus WINGDIPAPI GdipCreateBitmapFromStreamICM(IStream* stream,
1954 GpBitmap **bitmap)
1956 TRACE("%p %p\n", stream, bitmap);
1958 return GdipCreateBitmapFromStream(stream, bitmap);
1961 GpStatus WINGDIPAPI GdipCreateCachedBitmap(GpBitmap *bitmap, GpGraphics *graphics,
1962 GpCachedBitmap **cachedbmp)
1964 GpStatus stat;
1966 TRACE("%p %p %p\n", bitmap, graphics, cachedbmp);
1968 if(!bitmap || !graphics || !cachedbmp)
1969 return InvalidParameter;
1971 *cachedbmp = heap_alloc_zero(sizeof(GpCachedBitmap));
1972 if(!*cachedbmp)
1973 return OutOfMemory;
1975 stat = GdipCloneImage(&(bitmap->image), &(*cachedbmp)->image);
1976 if(stat != Ok){
1977 heap_free(*cachedbmp);
1978 return stat;
1981 return Ok;
1984 GpStatus WINGDIPAPI GdipCreateHICONFromBitmap(GpBitmap *bitmap, HICON *hicon)
1986 GpStatus stat;
1987 BitmapData lockeddata;
1988 ULONG andstride, xorstride, bitssize;
1989 LPBYTE andbits, xorbits, androw, xorrow, srcrow;
1990 UINT x, y;
1992 TRACE("(%p, %p)\n", bitmap, hicon);
1994 if (!bitmap || !hicon)
1995 return InvalidParameter;
1997 stat = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead,
1998 PixelFormat32bppPARGB, &lockeddata);
1999 if (stat == Ok)
2001 andstride = ((lockeddata.Width+31)/32)*4;
2002 xorstride = lockeddata.Width*4;
2003 bitssize = (andstride + xorstride) * lockeddata.Height;
2005 andbits = heap_alloc_zero(bitssize);
2007 if (andbits)
2009 xorbits = andbits + andstride * lockeddata.Height;
2011 for (y=0; y<lockeddata.Height; y++)
2013 srcrow = ((LPBYTE)lockeddata.Scan0) + lockeddata.Stride * y;
2015 androw = andbits + andstride * y;
2016 for (x=0; x<lockeddata.Width; x++)
2017 if (srcrow[3+4*x] >= 128)
2018 androw[x/8] |= 1 << (7-x%8);
2020 xorrow = xorbits + xorstride * y;
2021 memcpy(xorrow, srcrow, xorstride);
2024 *hicon = CreateIcon(NULL, lockeddata.Width, lockeddata.Height, 1, 32,
2025 andbits, xorbits);
2027 heap_free(andbits);
2029 else
2030 stat = OutOfMemory;
2032 GdipBitmapUnlockBits(bitmap, &lockeddata);
2035 return stat;
2038 GpStatus WINGDIPAPI GdipDeleteCachedBitmap(GpCachedBitmap *cachedbmp)
2040 TRACE("%p\n", cachedbmp);
2042 if(!cachedbmp)
2043 return InvalidParameter;
2045 GdipDisposeImage(cachedbmp->image);
2046 heap_free(cachedbmp);
2048 return Ok;
2051 GpStatus WINGDIPAPI GdipDrawCachedBitmap(GpGraphics *graphics,
2052 GpCachedBitmap *cachedbmp, INT x, INT y)
2054 TRACE("%p %p %d %d\n", graphics, cachedbmp, x, y);
2056 if(!graphics || !cachedbmp)
2057 return InvalidParameter;
2059 return GdipDrawImage(graphics, cachedbmp->image, (REAL)x, (REAL)y);
2062 /* Internal utility function: Replace the image data of dst with that of src,
2063 * and free src. */
2064 static void move_bitmap(GpBitmap *dst, GpBitmap *src, BOOL clobber_palette)
2066 assert(src->image.type == ImageTypeBitmap);
2067 assert(dst->image.type == ImageTypeBitmap);
2069 heap_free(dst->bitmapbits);
2070 heap_free(dst->own_bits);
2071 DeleteDC(dst->hdc);
2072 DeleteObject(dst->hbitmap);
2074 if (clobber_palette)
2076 heap_free(dst->image.palette);
2077 dst->image.palette = src->image.palette;
2079 else
2080 heap_free(src->image.palette);
2082 dst->image.xres = src->image.xres;
2083 dst->image.yres = src->image.yres;
2084 dst->width = src->width;
2085 dst->height = src->height;
2086 dst->format = src->format;
2087 dst->hbitmap = src->hbitmap;
2088 dst->hdc = src->hdc;
2089 dst->bits = src->bits;
2090 dst->stride = src->stride;
2091 dst->own_bits = src->own_bits;
2092 if (dst->metadata_reader)
2093 IWICMetadataReader_Release(dst->metadata_reader);
2094 dst->metadata_reader = src->metadata_reader;
2095 heap_free(dst->prop_item);
2096 dst->prop_item = src->prop_item;
2097 dst->prop_count = src->prop_count;
2098 if (dst->image.decoder)
2099 IWICBitmapDecoder_Release(dst->image.decoder);
2100 dst->image.decoder = src->image.decoder;
2101 terminate_encoder_wic(&dst->image); /* terminate active encoder before overwriting with src */
2102 dst->image.encoder = src->image.encoder;
2103 dst->image.frame_count = src->image.frame_count;
2104 dst->image.current_frame = src->image.current_frame;
2105 dst->image.format = src->image.format;
2107 src->image.type = ~0;
2108 heap_free(src);
2111 static GpStatus free_image_data(GpImage *image)
2113 if(!image)
2114 return InvalidParameter;
2116 if (image->type == ImageTypeBitmap)
2118 heap_free(((GpBitmap*)image)->bitmapbits);
2119 heap_free(((GpBitmap*)image)->own_bits);
2120 DeleteDC(((GpBitmap*)image)->hdc);
2121 DeleteObject(((GpBitmap*)image)->hbitmap);
2122 if (((GpBitmap*)image)->metadata_reader)
2123 IWICMetadataReader_Release(((GpBitmap*)image)->metadata_reader);
2124 heap_free(((GpBitmap*)image)->prop_item);
2126 else if (image->type == ImageTypeMetafile)
2127 METAFILE_Free((GpMetafile *)image);
2128 else
2130 WARN("invalid image: %p\n", image);
2131 return ObjectBusy;
2133 if (image->decoder)
2134 IWICBitmapDecoder_Release(image->decoder);
2135 terminate_encoder_wic(image);
2136 heap_free(image->palette);
2138 return Ok;
2141 GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image)
2143 GpStatus status;
2145 TRACE("%p\n", image);
2147 status = free_image_data(image);
2148 if (status != Ok) return status;
2149 image->type = ~0;
2150 heap_free(image);
2152 return Ok;
2155 GpStatus WINGDIPAPI GdipFindFirstImageItem(GpImage *image, ImageItemData* item)
2157 static int calls;
2159 TRACE("(%p,%p)\n", image, item);
2161 if(!image || !item)
2162 return InvalidParameter;
2164 if (!(calls++))
2165 FIXME("not implemented\n");
2167 return NotImplemented;
2170 GpStatus WINGDIPAPI GdipGetImageItemData(GpImage *image, ImageItemData *item)
2172 static int calls;
2174 TRACE("(%p,%p)\n", image, item);
2176 if (!(calls++))
2177 FIXME("not implemented\n");
2179 return NotImplemented;
2182 GpStatus WINGDIPAPI GdipGetImageBounds(GpImage *image, GpRectF *srcRect,
2183 GpUnit *srcUnit)
2185 TRACE("%p %p %p\n", image, srcRect, srcUnit);
2187 if(!image || !srcRect || !srcUnit)
2188 return InvalidParameter;
2189 if(image->type == ImageTypeMetafile){
2190 *srcRect = ((GpMetafile*)image)->bounds;
2191 *srcUnit = ((GpMetafile*)image)->unit;
2193 else if(image->type == ImageTypeBitmap){
2194 srcRect->X = srcRect->Y = 0.0;
2195 srcRect->Width = (REAL) ((GpBitmap*)image)->width;
2196 srcRect->Height = (REAL) ((GpBitmap*)image)->height;
2197 *srcUnit = UnitPixel;
2199 else{
2200 WARN("GpImage with no image data\n");
2201 return InvalidParameter;
2204 TRACE("returning (%f, %f) (%f, %f) unit type %d\n", srcRect->X, srcRect->Y,
2205 srcRect->Width, srcRect->Height, *srcUnit);
2207 return Ok;
2210 GpStatus WINGDIPAPI GdipGetImageDimension(GpImage *image, REAL *width,
2211 REAL *height)
2213 TRACE("%p %p %p\n", image, width, height);
2215 if(!image || !height || !width)
2216 return InvalidParameter;
2218 if(image->type == ImageTypeMetafile){
2219 *height = units_to_pixels(((GpMetafile*)image)->bounds.Height, ((GpMetafile*)image)->unit,
2220 image->yres, ((GpMetafile*)image)->printer_display);
2221 *width = units_to_pixels(((GpMetafile*)image)->bounds.Width, ((GpMetafile*)image)->unit,
2222 image->xres, ((GpMetafile*)image)->printer_display);
2224 else if(image->type == ImageTypeBitmap){
2225 *height = ((GpBitmap*)image)->height;
2226 *width = ((GpBitmap*)image)->width;
2228 else{
2229 WARN("GpImage with no image data\n");
2230 return InvalidParameter;
2233 TRACE("returning (%f, %f)\n", *height, *width);
2234 return Ok;
2237 GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image,
2238 GpGraphics **graphics)
2240 HDC hdc;
2241 GpStatus stat;
2243 TRACE("%p %p\n", image, graphics);
2245 if(!image || !graphics)
2246 return InvalidParameter;
2248 if (image->type == ImageTypeBitmap && ((GpBitmap*)image)->hbitmap)
2250 hdc = ((GpBitmap*)image)->hdc;
2252 if(!hdc){
2253 hdc = CreateCompatibleDC(0);
2254 SelectObject(hdc, ((GpBitmap*)image)->hbitmap);
2255 ((GpBitmap*)image)->hdc = hdc;
2258 stat = GdipCreateFromHDC(hdc, graphics);
2260 if (stat == Ok)
2262 (*graphics)->image = image;
2263 (*graphics)->image_type = image->type;
2264 (*graphics)->xres = image->xres;
2265 (*graphics)->yres = image->yres;
2268 else if (image->type == ImageTypeMetafile)
2269 stat = METAFILE_GetGraphicsContext((GpMetafile*)image, graphics);
2270 else
2271 stat = graphics_from_image(image, graphics);
2273 return stat;
2276 GpStatus WINGDIPAPI GdipGetImageHeight(GpImage *image, UINT *height)
2278 TRACE("%p %p\n", image, height);
2280 if(!image || !height)
2281 return InvalidParameter;
2283 if(image->type == ImageTypeMetafile)
2284 *height = units_to_pixels(((GpMetafile*)image)->bounds.Height, ((GpMetafile*)image)->unit,
2285 image->yres, ((GpMetafile*)image)->printer_display);
2286 else if(image->type == ImageTypeBitmap)
2287 *height = ((GpBitmap*)image)->height;
2288 else
2290 WARN("GpImage with no image data\n");
2291 return InvalidParameter;
2294 TRACE("returning %d\n", *height);
2296 return Ok;
2299 GpStatus WINGDIPAPI GdipGetImageHorizontalResolution(GpImage *image, REAL *res)
2301 if(!image || !res)
2302 return InvalidParameter;
2304 *res = image->xres;
2306 TRACE("(%p) <-- %0.2f\n", image, *res);
2308 return Ok;
2311 GpStatus WINGDIPAPI GdipGetImagePaletteSize(GpImage *image, INT *size)
2313 TRACE("%p %p\n", image, size);
2315 if(!image || !size)
2316 return InvalidParameter;
2318 if (image->type == ImageTypeMetafile)
2320 *size = 0;
2321 return GenericError;
2324 if (!image->palette || image->palette->Count == 0)
2325 *size = sizeof(ColorPalette);
2326 else
2327 *size = sizeof(UINT)*2 + sizeof(ARGB)*image->palette->Count;
2329 TRACE("<-- %u\n", *size);
2331 return Ok;
2334 /* FIXME: test this function for non-bitmap types */
2335 GpStatus WINGDIPAPI GdipGetImagePixelFormat(GpImage *image, PixelFormat *format)
2337 TRACE("%p %p\n", image, format);
2339 if(!image || !format)
2340 return InvalidParameter;
2342 if(image->type != ImageTypeBitmap)
2343 *format = PixelFormat24bppRGB;
2344 else
2345 *format = ((GpBitmap*) image)->format;
2347 return Ok;
2350 GpStatus WINGDIPAPI GdipGetImageRawFormat(GpImage *image, GUID *format)
2352 TRACE("(%p, %p)\n", image, format);
2354 if(!image || !format)
2355 return InvalidParameter;
2357 memcpy(format, &image->format, sizeof(GUID));
2359 return Ok;
2362 GpStatus WINGDIPAPI GdipGetImageType(GpImage *image, ImageType *type)
2364 TRACE("%p %p\n", image, type);
2366 if(!image || !type)
2367 return InvalidParameter;
2369 *type = image->type;
2371 return Ok;
2374 GpStatus WINGDIPAPI GdipGetImageVerticalResolution(GpImage *image, REAL *res)
2376 if(!image || !res)
2377 return InvalidParameter;
2379 *res = image->yres;
2381 TRACE("(%p) <-- %0.2f\n", image, *res);
2383 return Ok;
2386 GpStatus WINGDIPAPI GdipGetImageWidth(GpImage *image, UINT *width)
2388 TRACE("%p %p\n", image, width);
2390 if(!image || !width)
2391 return InvalidParameter;
2393 if(image->type == ImageTypeMetafile)
2394 *width = units_to_pixels(((GpMetafile*)image)->bounds.Width, ((GpMetafile*)image)->unit,
2395 image->xres, ((GpMetafile*)image)->printer_display);
2396 else if(image->type == ImageTypeBitmap)
2397 *width = ((GpBitmap*)image)->width;
2398 else
2400 WARN("GpImage with no image data\n");
2401 return InvalidParameter;
2404 TRACE("returning %d\n", *width);
2406 return Ok;
2409 GpStatus WINGDIPAPI GdipGetPropertyCount(GpImage *image, UINT *num)
2411 TRACE("(%p, %p)\n", image, num);
2413 if (!image || !num) return InvalidParameter;
2415 *num = 0;
2417 if (image->type == ImageTypeBitmap)
2419 if (((GpBitmap *)image)->prop_item)
2421 *num = ((GpBitmap *)image)->prop_count;
2422 return Ok;
2425 if (((GpBitmap *)image)->metadata_reader)
2426 IWICMetadataReader_GetCount(((GpBitmap *)image)->metadata_reader, num);
2429 return Ok;
2432 GpStatus WINGDIPAPI GdipGetPropertyIdList(GpImage *image, UINT num, PROPID *list)
2434 HRESULT hr;
2435 IWICMetadataReader *reader;
2436 IWICEnumMetadataItem *enumerator;
2437 UINT prop_count, i;
2438 ULONG items_returned;
2440 TRACE("(%p, %u, %p)\n", image, num, list);
2442 if (!image || !list) return InvalidParameter;
2444 if (image->type != ImageTypeBitmap)
2446 FIXME("Not implemented for type %d\n", image->type);
2447 return NotImplemented;
2450 if (((GpBitmap *)image)->prop_item)
2452 if (num != ((GpBitmap *)image)->prop_count) return InvalidParameter;
2454 for (i = 0; i < num; i++)
2456 list[i] = ((GpBitmap *)image)->prop_item[i].id;
2459 return Ok;
2462 reader = ((GpBitmap *)image)->metadata_reader;
2463 if (!reader)
2465 if (num != 0) return InvalidParameter;
2466 return Ok;
2469 hr = IWICMetadataReader_GetCount(reader, &prop_count);
2470 if (FAILED(hr)) return hresult_to_status(hr);
2472 if (num != prop_count) return InvalidParameter;
2474 hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
2475 if (FAILED(hr)) return hresult_to_status(hr);
2477 IWICEnumMetadataItem_Reset(enumerator);
2479 for (i = 0; i < num; i++)
2481 PROPVARIANT id;
2483 hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, NULL, &items_returned);
2484 if (hr != S_OK) break;
2486 if (id.vt != VT_UI2)
2488 FIXME("not supported propvariant type for id: %u\n", id.vt);
2489 list[i] = 0;
2490 continue;
2492 list[i] = id.uiVal;
2495 IWICEnumMetadataItem_Release(enumerator);
2497 return hr == S_OK ? Ok : hresult_to_status(hr);
2500 static UINT propvariant_size(PROPVARIANT *value)
2502 switch (value->vt & ~VT_VECTOR)
2504 case VT_EMPTY:
2505 return 0;
2506 case VT_I1:
2507 case VT_UI1:
2508 if (!(value->vt & VT_VECTOR)) return 1;
2509 return value->caub.cElems;
2510 case VT_I2:
2511 case VT_UI2:
2512 if (!(value->vt & VT_VECTOR)) return 2;
2513 return value->caui.cElems * 2;
2514 case VT_I4:
2515 case VT_UI4:
2516 case VT_R4:
2517 if (!(value->vt & VT_VECTOR)) return 4;
2518 return value->caul.cElems * 4;
2519 case VT_I8:
2520 case VT_UI8:
2521 case VT_R8:
2522 if (!(value->vt & VT_VECTOR)) return 8;
2523 return value->cauh.cElems * 8;
2524 case VT_LPSTR:
2525 return value->pszVal ? strlen(value->pszVal) + 1 : 0;
2526 case VT_BLOB:
2527 return value->blob.cbSize;
2528 default:
2529 FIXME("not supported variant type %d\n", value->vt);
2530 return 0;
2534 GpStatus WINGDIPAPI GdipGetPropertyItemSize(GpImage *image, PROPID propid, UINT *size)
2536 HRESULT hr;
2537 IWICMetadataReader *reader;
2538 PROPVARIANT id, value;
2540 TRACE("(%p,%#lx,%p)\n", image, propid, size);
2542 if (!size || !image) return InvalidParameter;
2544 if (image->type != ImageTypeBitmap)
2546 FIXME("Not implemented for type %d\n", image->type);
2547 return NotImplemented;
2550 if (((GpBitmap *)image)->prop_item)
2552 UINT i;
2554 for (i = 0; i < ((GpBitmap *)image)->prop_count; i++)
2556 if (propid == ((GpBitmap *)image)->prop_item[i].id)
2558 *size = sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length;
2559 return Ok;
2563 return PropertyNotFound;
2566 reader = ((GpBitmap *)image)->metadata_reader;
2567 if (!reader) return PropertyNotFound;
2569 id.vt = VT_UI2;
2570 id.uiVal = propid;
2571 hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
2572 if (FAILED(hr)) return PropertyNotFound;
2574 *size = propvariant_size(&value);
2575 if (*size) *size += sizeof(PropertyItem);
2576 PropVariantClear(&value);
2578 return Ok;
2581 #ifndef PropertyTagTypeSByte
2582 #define PropertyTagTypeSByte 6
2583 #define PropertyTagTypeSShort 8
2584 #define PropertyTagTypeFloat 11
2585 #define PropertyTagTypeDouble 12
2586 #endif
2588 static UINT vt_to_itemtype(UINT vt)
2590 static const struct
2592 UINT vt, type;
2593 } vt2type[] =
2595 { VT_I1, PropertyTagTypeSByte },
2596 { VT_UI1, PropertyTagTypeByte },
2597 { VT_I2, PropertyTagTypeSShort },
2598 { VT_UI2, PropertyTagTypeShort },
2599 { VT_I4, PropertyTagTypeSLONG },
2600 { VT_UI4, PropertyTagTypeLong },
2601 { VT_I8, PropertyTagTypeSRational },
2602 { VT_UI8, PropertyTagTypeRational },
2603 { VT_R4, PropertyTagTypeFloat },
2604 { VT_R8, PropertyTagTypeDouble },
2605 { VT_LPSTR, PropertyTagTypeASCII },
2606 { VT_BLOB, PropertyTagTypeUndefined }
2608 UINT i;
2609 for (i = 0; i < ARRAY_SIZE(vt2type); i++)
2611 if (vt2type[i].vt == vt) return vt2type[i].type;
2613 FIXME("not supported variant type %u\n", vt);
2614 return 0;
2617 static GpStatus propvariant_to_item(PROPVARIANT *value, PropertyItem *item,
2618 UINT size, PROPID id)
2620 UINT item_size, item_type;
2622 item_size = propvariant_size(value);
2623 if (size != item_size + sizeof(PropertyItem)) return InvalidParameter;
2625 item_type = vt_to_itemtype(value->vt & ~VT_VECTOR);
2626 if (!item_type) return InvalidParameter;
2628 item->value = item + 1;
2630 switch (value->vt & ~VT_VECTOR)
2632 case VT_I1:
2633 case VT_UI1:
2634 if (!(value->vt & VT_VECTOR))
2635 *(BYTE *)item->value = value->bVal;
2636 else
2637 memcpy(item->value, value->caub.pElems, item_size);
2638 break;
2639 case VT_I2:
2640 case VT_UI2:
2641 if (!(value->vt & VT_VECTOR))
2642 *(USHORT *)item->value = value->uiVal;
2643 else
2644 memcpy(item->value, value->caui.pElems, item_size);
2645 break;
2646 case VT_I4:
2647 case VT_UI4:
2648 case VT_R4:
2649 if (!(value->vt & VT_VECTOR))
2650 *(ULONG *)item->value = value->ulVal;
2651 else
2652 memcpy(item->value, value->caul.pElems, item_size);
2653 break;
2654 case VT_I8:
2655 case VT_UI8:
2656 case VT_R8:
2657 if (!(value->vt & VT_VECTOR))
2658 *(ULONGLONG *)item->value = value->uhVal.QuadPart;
2659 else
2660 memcpy(item->value, value->cauh.pElems, item_size);
2661 break;
2662 case VT_LPSTR:
2663 memcpy(item->value, value->pszVal, item_size);
2664 break;
2665 case VT_BLOB:
2666 memcpy(item->value, value->blob.pBlobData, item_size);
2667 break;
2668 default:
2669 FIXME("not supported variant type %d\n", value->vt);
2670 return InvalidParameter;
2673 item->length = item_size;
2674 item->type = item_type;
2675 item->id = id;
2677 return Ok;
2680 GpStatus WINGDIPAPI GdipGetPropertyItem(GpImage *image, PROPID propid, UINT size,
2681 PropertyItem *buffer)
2683 GpStatus stat;
2684 HRESULT hr;
2685 IWICMetadataReader *reader;
2686 PROPVARIANT id, value;
2688 TRACE("(%p,%#lx,%u,%p)\n", image, propid, size, buffer);
2690 if (!image || !buffer) return InvalidParameter;
2692 if (image->type != ImageTypeBitmap)
2694 FIXME("Not implemented for type %d\n", image->type);
2695 return NotImplemented;
2698 if (((GpBitmap *)image)->prop_item)
2700 UINT i;
2702 for (i = 0; i < ((GpBitmap *)image)->prop_count; i++)
2704 if (propid == ((GpBitmap *)image)->prop_item[i].id)
2706 if (size != sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length)
2707 return InvalidParameter;
2709 *buffer = ((GpBitmap *)image)->prop_item[i];
2710 buffer->value = buffer + 1;
2711 memcpy(buffer->value, ((GpBitmap *)image)->prop_item[i].value, buffer->length);
2712 return Ok;
2716 return PropertyNotFound;
2719 reader = ((GpBitmap *)image)->metadata_reader;
2720 if (!reader) return PropertyNotFound;
2722 id.vt = VT_UI2;
2723 id.uiVal = propid;
2724 hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
2725 if (FAILED(hr)) return PropertyNotFound;
2727 stat = propvariant_to_item(&value, buffer, size, propid);
2728 PropVariantClear(&value);
2730 return stat;
2733 GpStatus WINGDIPAPI GdipGetPropertySize(GpImage *image, UINT *size, UINT *count)
2735 HRESULT hr;
2736 IWICMetadataReader *reader;
2737 IWICEnumMetadataItem *enumerator;
2738 UINT prop_count, prop_size, i;
2739 PROPVARIANT id, value;
2741 TRACE("(%p,%p,%p)\n", image, size, count);
2743 if (!image || !size || !count) return InvalidParameter;
2745 if (image->type != ImageTypeBitmap)
2747 FIXME("Not implemented for type %d\n", image->type);
2748 return NotImplemented;
2751 if (((GpBitmap *)image)->prop_item)
2753 *count = ((GpBitmap *)image)->prop_count;
2754 *size = 0;
2756 for (i = 0; i < ((GpBitmap *)image)->prop_count; i++)
2758 *size += sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length;
2761 return Ok;
2764 reader = ((GpBitmap *)image)->metadata_reader;
2765 if (!reader) return PropertyNotFound;
2767 hr = IWICMetadataReader_GetCount(reader, &prop_count);
2768 if (FAILED(hr)) return hresult_to_status(hr);
2770 hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
2771 if (FAILED(hr)) return hresult_to_status(hr);
2773 IWICEnumMetadataItem_Reset(enumerator);
2775 prop_size = 0;
2777 PropVariantInit(&id);
2778 PropVariantInit(&value);
2780 for (i = 0; i < prop_count; i++)
2782 ULONG items_returned;
2783 UINT item_size;
2785 hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, &value, &items_returned);
2786 if (hr != S_OK) break;
2788 item_size = propvariant_size(&value);
2789 if (item_size) prop_size += sizeof(PropertyItem) + item_size;
2791 PropVariantClear(&id);
2792 PropVariantClear(&value);
2795 IWICEnumMetadataItem_Release(enumerator);
2797 if (hr != S_OK) return PropertyNotFound;
2799 *count = prop_count;
2800 *size = prop_size;
2801 return Ok;
2804 GpStatus WINGDIPAPI GdipGetAllPropertyItems(GpImage *image, UINT size,
2805 UINT count, PropertyItem *buf)
2807 GpStatus status;
2808 HRESULT hr;
2809 IWICMetadataReader *reader;
2810 IWICEnumMetadataItem *enumerator;
2811 UINT prop_count, prop_size, i;
2812 PROPVARIANT id, value;
2813 char *item_value;
2815 TRACE("(%p,%u,%u,%p)\n", image, size, count, buf);
2817 if (!image || !buf) return InvalidParameter;
2819 if (image->type != ImageTypeBitmap)
2821 FIXME("Not implemented for type %d\n", image->type);
2822 return NotImplemented;
2825 status = GdipGetPropertySize(image, &prop_size, &prop_count);
2826 if (status != Ok) return status;
2828 if (prop_count != count || prop_size != size) return InvalidParameter;
2830 if (((GpBitmap *)image)->prop_item)
2832 memcpy(buf, ((GpBitmap *)image)->prop_item, prop_size);
2834 item_value = (char *)(buf + prop_count);
2836 for (i = 0; i < prop_count; i++)
2838 buf[i].value = item_value;
2839 item_value += buf[i].length;
2842 return Ok;
2845 reader = ((GpBitmap *)image)->metadata_reader;
2846 if (!reader) return PropertyNotFound;
2848 hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
2849 if (FAILED(hr)) return hresult_to_status(hr);
2851 IWICEnumMetadataItem_Reset(enumerator);
2853 item_value = (char *)(buf + prop_count);
2855 PropVariantInit(&id);
2856 PropVariantInit(&value);
2858 for (i = 0; i < prop_count; i++)
2860 PropertyItem *item;
2861 ULONG items_returned;
2862 UINT item_size;
2864 hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, &value, &items_returned);
2865 if (hr != S_OK) break;
2867 if (id.vt != VT_UI2)
2869 FIXME("not supported propvariant type for id: %u\n", id.vt);
2870 continue;
2873 item_size = propvariant_size(&value);
2874 if (item_size)
2876 item = heap_alloc(item_size + sizeof(*item));
2878 propvariant_to_item(&value, item, item_size + sizeof(*item), id.uiVal);
2879 buf[i].id = item->id;
2880 buf[i].type = item->type;
2881 buf[i].length = item_size;
2882 buf[i].value = item_value;
2883 memcpy(item_value, item->value, item_size);
2884 item_value += item_size;
2886 heap_free(item);
2889 PropVariantClear(&id);
2890 PropVariantClear(&value);
2893 IWICEnumMetadataItem_Release(enumerator);
2895 if (hr != S_OK) return PropertyNotFound;
2897 return Ok;
2900 struct image_format_dimension
2902 const GUID *format;
2903 const GUID *dimension;
2906 static const struct image_format_dimension image_format_dimensions[] =
2908 {&ImageFormatGIF, &FrameDimensionTime},
2909 {&ImageFormatIcon, &FrameDimensionResolution},
2910 {NULL}
2913 GpStatus WINGDIPAPI GdipImageGetFrameCount(GpImage *image,
2914 GDIPCONST GUID* dimensionID, UINT* count)
2916 TRACE("(%p,%s,%p)\n", image, debugstr_guid(dimensionID), count);
2918 if(!image || !count)
2919 return InvalidParameter;
2921 if (!dimensionID ||
2922 IsEqualGUID(dimensionID, &image->format) ||
2923 IsEqualGUID(dimensionID, &FrameDimensionPage) ||
2924 IsEqualGUID(dimensionID, &FrameDimensionTime))
2926 *count = image->frame_count;
2927 return Ok;
2930 return InvalidParameter;
2933 GpStatus WINGDIPAPI GdipImageGetFrameDimensionsCount(GpImage *image,
2934 UINT* count)
2936 TRACE("(%p, %p)\n", image, count);
2938 /* Native gdiplus 1.1 does not yet support multiple frame dimensions. */
2940 if(!image || !count)
2941 return InvalidParameter;
2943 *count = 1;
2945 return Ok;
2948 GpStatus WINGDIPAPI GdipImageGetFrameDimensionsList(GpImage* image,
2949 GUID* dimensionIDs, UINT count)
2951 int i;
2952 const GUID *result=NULL;
2954 TRACE("(%p,%p,%u)\n", image, dimensionIDs, count);
2956 if(!image || !dimensionIDs || count != 1)
2957 return InvalidParameter;
2959 for (i=0; image_format_dimensions[i].format; i++)
2961 if (IsEqualGUID(&image->format, image_format_dimensions[i].format))
2963 result = image_format_dimensions[i].dimension;
2964 break;
2968 if (!result)
2969 result = &FrameDimensionPage;
2971 memcpy(dimensionIDs, result, sizeof(GUID));
2973 return Ok;
2976 GpStatus WINGDIPAPI GdipLoadImageFromFile(GDIPCONST WCHAR* filename,
2977 GpImage **image)
2979 GpStatus stat;
2980 IStream *stream;
2982 TRACE("(%s) %p\n", debugstr_w(filename), image);
2984 if (!filename || !image)
2985 return InvalidParameter;
2987 *image = NULL;
2989 stat = GdipCreateStreamOnFile(filename, GENERIC_READ, &stream);
2991 if (stat != Ok)
2992 return stat;
2994 stat = GdipLoadImageFromStream(stream, image);
2996 IStream_Release(stream);
2998 return stat;
3001 /* FIXME: no icm handling */
3002 GpStatus WINGDIPAPI GdipLoadImageFromFileICM(GDIPCONST WCHAR* filename,GpImage **image)
3004 TRACE("(%s) %p\n", debugstr_w(filename), image);
3006 return GdipLoadImageFromFile(filename, image);
3009 static void add_property(GpBitmap *bitmap, PropertyItem *item)
3011 UINT prop_size, prop_count;
3012 PropertyItem *prop_item;
3014 if (bitmap->prop_item == NULL)
3016 prop_size = prop_count = 0;
3017 prop_item = heap_alloc_zero(item->length + sizeof(PropertyItem));
3018 if (!prop_item) return;
3020 else
3022 UINT i;
3023 char *item_value;
3025 GdipGetPropertySize(&bitmap->image, &prop_size, &prop_count);
3027 prop_item = heap_alloc_zero(prop_size + item->length + sizeof(PropertyItem));
3028 if (!prop_item) return;
3029 memcpy(prop_item, bitmap->prop_item, sizeof(PropertyItem) * bitmap->prop_count);
3030 prop_size -= sizeof(PropertyItem) * bitmap->prop_count;
3031 memcpy(prop_item + prop_count + 1, bitmap->prop_item + prop_count, prop_size);
3033 item_value = (char *)(prop_item + prop_count + 1);
3035 for (i = 0; i < prop_count; i++)
3037 prop_item[i].value = item_value;
3038 item_value += prop_item[i].length;
3042 prop_item[prop_count].id = item->id;
3043 prop_item[prop_count].type = item->type;
3044 prop_item[prop_count].length = item->length;
3045 prop_item[prop_count].value = (char *)(prop_item + prop_count + 1) + prop_size;
3046 memcpy(prop_item[prop_count].value, item->value, item->length);
3048 heap_free(bitmap->prop_item);
3049 bitmap->prop_item = prop_item;
3050 bitmap->prop_count++;
3053 static BOOL get_bool_property(IWICMetadataReader *reader, const GUID *guid, const WCHAR *prop_name)
3055 HRESULT hr;
3056 GUID format;
3057 PROPVARIANT id, value;
3058 BOOL ret = FALSE;
3060 hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
3061 if (FAILED(hr) || !IsEqualGUID(&format, guid)) return FALSE;
3063 PropVariantInit(&id);
3064 PropVariantInit(&value);
3066 id.vt = VT_LPWSTR;
3067 id.pwszVal = CoTaskMemAlloc((lstrlenW(prop_name) + 1) * sizeof(WCHAR));
3068 if (!id.pwszVal) return FALSE;
3069 lstrcpyW(id.pwszVal, prop_name);
3070 hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
3071 if (hr == S_OK && value.vt == VT_BOOL)
3072 ret = value.boolVal;
3074 PropVariantClear(&id);
3075 PropVariantClear(&value);
3077 return ret;
3080 static PropertyItem *get_property(IWICMetadataReader *reader, const GUID *guid, const WCHAR *prop_name)
3082 HRESULT hr;
3083 GUID format;
3084 PROPVARIANT id, value;
3085 PropertyItem *item = NULL;
3087 hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
3088 if (FAILED(hr) || !IsEqualGUID(&format, guid)) return NULL;
3090 PropVariantInit(&id);
3091 PropVariantInit(&value);
3093 id.vt = VT_LPWSTR;
3094 id.pwszVal = CoTaskMemAlloc((lstrlenW(prop_name) + 1) * sizeof(WCHAR));
3095 if (!id.pwszVal) return NULL;
3096 lstrcpyW(id.pwszVal, prop_name);
3097 hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
3098 if (hr == S_OK)
3100 UINT item_size = propvariant_size(&value);
3101 if (item_size)
3103 item_size += sizeof(*item);
3104 item = heap_alloc_zero(item_size);
3105 if (propvariant_to_item(&value, item, item_size, 0) != Ok)
3107 heap_free(item);
3108 item = NULL;
3113 PropVariantClear(&id);
3114 PropVariantClear(&value);
3116 return item;
3119 static PropertyItem *get_gif_comment(IWICMetadataReader *reader)
3121 PropertyItem *comment;
3123 comment = get_property(reader, &GUID_MetadataFormatGifComment, L"TextEntry");
3124 if (comment)
3125 comment->id = PropertyTagExifUserComment;
3127 return comment;
3130 static PropertyItem *get_gif_loopcount(IWICMetadataReader *reader)
3132 PropertyItem *appext = NULL, *appdata = NULL, *loop = NULL;
3134 appext = get_property(reader, &GUID_MetadataFormatAPE, L"Application");
3135 if (appext)
3137 if (appext->type == PropertyTagTypeByte && appext->length == 11 &&
3138 (!memcmp(appext->value, "NETSCAPE2.0", 11) || !memcmp(appext->value, "ANIMEXTS1.0", 11)))
3140 appdata = get_property(reader, &GUID_MetadataFormatAPE, L"Data");
3141 if (appdata)
3143 if (appdata->type == PropertyTagTypeByte && appdata->length == 4)
3145 BYTE *data = appdata->value;
3146 if (data[0] == 3 && data[1] == 1)
3148 loop = heap_alloc_zero(sizeof(*loop) + sizeof(SHORT));
3149 if (loop)
3151 loop->type = PropertyTagTypeShort;
3152 loop->id = PropertyTagLoopCount;
3153 loop->length = sizeof(SHORT);
3154 loop->value = loop + 1;
3155 *(SHORT *)loop->value = data[2] | (data[3] << 8);
3163 heap_free(appext);
3164 heap_free(appdata);
3166 return loop;
3169 static PropertyItem *get_gif_background(IWICMetadataReader *reader)
3171 PropertyItem *background;
3173 background = get_property(reader, &GUID_MetadataFormatLSD, L"BackgroundColorIndex");
3174 if (background)
3175 background->id = PropertyTagIndexBackground;
3177 return background;
3180 static PropertyItem *get_gif_palette(IWICBitmapDecoder *decoder, IWICMetadataReader *reader)
3182 HRESULT hr;
3183 IWICImagingFactory *factory;
3184 IWICPalette *palette;
3185 UINT count = 0;
3186 WICColor colors[256];
3188 if (!get_bool_property(reader, &GUID_MetadataFormatLSD, L"GlobalColorTableFlag"))
3189 return NULL;
3191 hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory);
3192 if (hr != S_OK) return NULL;
3194 hr = IWICImagingFactory_CreatePalette(factory, &palette);
3195 if (hr == S_OK)
3197 hr = IWICBitmapDecoder_CopyPalette(decoder, palette);
3198 if (hr == S_OK)
3199 IWICPalette_GetColors(palette, 256, colors, &count);
3201 IWICPalette_Release(palette);
3204 IWICImagingFactory_Release(factory);
3206 if (count)
3208 PropertyItem *pal;
3209 UINT i;
3210 BYTE *rgb;
3212 pal = heap_alloc_zero(sizeof(*pal) + count * 3);
3213 if (!pal) return NULL;
3214 pal->type = PropertyTagTypeByte;
3215 pal->id = PropertyTagGlobalPalette;
3216 pal->value = pal + 1;
3217 pal->length = count * 3;
3219 rgb = pal->value;
3221 for (i = 0; i < count; i++)
3223 rgb[i*3] = (colors[i] >> 16) & 0xff;
3224 rgb[i*3 + 1] = (colors[i] >> 8) & 0xff;
3225 rgb[i*3 + 2] = colors[i] & 0xff;
3228 return pal;
3231 return NULL;
3234 static PropertyItem *get_gif_transparent_idx(IWICMetadataReader *reader)
3236 PropertyItem *index = NULL;
3238 if (get_bool_property(reader, &GUID_MetadataFormatGCE, L"TransparencyFlag"))
3240 index = get_property(reader, &GUID_MetadataFormatGCE, L"TransparentColorIndex");
3241 if (index)
3242 index->id = PropertyTagIndexTransparent;
3244 return index;
3247 static LONG get_gif_frame_property(IWICBitmapFrameDecode *frame, const GUID *format, const WCHAR *property)
3249 HRESULT hr;
3250 IWICMetadataBlockReader *block_reader;
3251 IWICMetadataReader *reader;
3252 UINT block_count, i;
3253 PropertyItem *prop;
3254 LONG value = 0;
3256 hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader);
3257 if (hr == S_OK)
3259 hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
3260 if (hr == S_OK)
3262 for (i = 0; i < block_count; i++)
3264 hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
3265 if (hr == S_OK)
3267 prop = get_property(reader, format, property);
3268 if (prop)
3270 if (prop->type == PropertyTagTypeByte && prop->length == 1)
3271 value = *(BYTE *)prop->value;
3272 else if (prop->type == PropertyTagTypeShort && prop->length == 2)
3273 value = *(SHORT *)prop->value;
3275 heap_free(prop);
3277 IWICMetadataReader_Release(reader);
3281 IWICMetadataBlockReader_Release(block_reader);
3284 return value;
3287 static void gif_metadata_reader(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT active_frame)
3289 HRESULT hr;
3290 IWICBitmapFrameDecode *frame;
3291 IWICMetadataBlockReader *block_reader;
3292 IWICMetadataReader *reader;
3293 UINT frame_count, block_count, i;
3294 PropertyItem *delay = NULL, *comment = NULL, *background = NULL;
3295 PropertyItem *transparent_idx = NULL, *loop = NULL, *palette = NULL;
3297 IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
3298 if (frame_count > 1)
3300 delay = heap_alloc_zero(sizeof(*delay) + frame_count * sizeof(LONG));
3301 if (delay)
3303 LONG *value;
3305 delay->type = PropertyTagTypeLong;
3306 delay->id = PropertyTagFrameDelay;
3307 delay->length = frame_count * sizeof(LONG);
3308 delay->value = delay + 1;
3310 value = delay->value;
3312 for (i = 0; i < frame_count; i++)
3314 hr = IWICBitmapDecoder_GetFrame(decoder, i, &frame);
3315 if (hr == S_OK)
3317 value[i] = get_gif_frame_property(frame, &GUID_MetadataFormatGCE, L"Delay");
3318 IWICBitmapFrameDecode_Release(frame);
3320 else value[i] = 0;
3325 hr = IWICBitmapDecoder_QueryInterface(decoder, &IID_IWICMetadataBlockReader, (void **)&block_reader);
3326 if (hr == S_OK)
3328 hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
3329 if (hr == S_OK)
3331 for (i = 0; i < block_count; i++)
3333 hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
3334 if (hr == S_OK)
3336 if (!comment)
3337 comment = get_gif_comment(reader);
3339 if (frame_count > 1 && !loop)
3340 loop = get_gif_loopcount(reader);
3342 if (!background)
3343 background = get_gif_background(reader);
3345 if (!palette)
3346 palette = get_gif_palette(decoder, reader);
3348 IWICMetadataReader_Release(reader);
3352 IWICMetadataBlockReader_Release(block_reader);
3355 if (frame_count > 1 && !loop)
3357 loop = heap_alloc_zero(sizeof(*loop) + sizeof(SHORT));
3358 if (loop)
3360 loop->type = PropertyTagTypeShort;
3361 loop->id = PropertyTagLoopCount;
3362 loop->length = sizeof(SHORT);
3363 loop->value = loop + 1;
3364 *(SHORT *)loop->value = 1;
3368 if (delay) add_property(bitmap, delay);
3369 if (comment) add_property(bitmap, comment);
3370 if (loop) add_property(bitmap, loop);
3371 if (palette) add_property(bitmap, palette);
3372 if (background) add_property(bitmap, background);
3374 heap_free(delay);
3375 heap_free(comment);
3376 heap_free(loop);
3377 heap_free(palette);
3378 heap_free(background);
3380 /* Win7 gdiplus always returns transparent color index from frame 0 */
3381 hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
3382 if (hr != S_OK) return;
3384 hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader);
3385 if (hr == S_OK)
3387 hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
3388 if (hr == S_OK)
3390 for (i = 0; i < block_count; i++)
3392 hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
3393 if (hr == S_OK)
3395 if (!transparent_idx)
3396 transparent_idx = get_gif_transparent_idx(reader);
3398 IWICMetadataReader_Release(reader);
3402 IWICMetadataBlockReader_Release(block_reader);
3405 if (transparent_idx) add_property(bitmap, transparent_idx);
3406 heap_free(transparent_idx);
3408 IWICBitmapFrameDecode_Release(frame);
3411 static PropertyItem* create_prop(PROPID propid, PROPVARIANT* value)
3413 PropertyItem *item = NULL;
3414 UINT item_size = propvariant_size(value);
3416 if (item_size)
3418 item_size += sizeof(*item);
3419 item = heap_alloc_zero(item_size);
3420 if (propvariant_to_item(value, item, item_size, propid) != Ok)
3422 heap_free(item);
3423 item = NULL;
3427 return item;
3430 static ULONG get_ulong_by_index(IWICMetadataReader* reader, ULONG index)
3432 PROPVARIANT value;
3433 HRESULT hr;
3434 ULONG result=0;
3436 hr = IWICMetadataReader_GetValueByIndex(reader, index, NULL, NULL, &value);
3437 if (SUCCEEDED(hr))
3439 switch (value.vt)
3441 case VT_UI4:
3442 result = value.ulVal;
3443 break;
3444 default:
3445 ERR("unhandled case %u\n", value.vt);
3446 break;
3448 PropVariantClear(&value);
3450 return result;
3453 static void png_metadata_reader(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT active_frame)
3455 HRESULT hr;
3456 IWICBitmapFrameDecode *frame;
3457 IWICMetadataBlockReader *block_reader;
3458 IWICMetadataReader *reader;
3459 UINT block_count, i, j;
3460 struct keyword_info {
3461 const char* name;
3462 PROPID propid;
3463 BOOL seen;
3464 } keywords[] = {
3465 { "Title", PropertyTagImageTitle },
3466 { "Author", PropertyTagArtist },
3467 { "Description", PropertyTagImageDescription },
3468 { "Copyright", PropertyTagCopyright },
3469 { "Software", PropertyTagSoftwareUsed },
3470 { "Source", PropertyTagEquipModel },
3471 { "Comment", PropertyTagExifUserComment },
3473 BOOL seen_gamma=FALSE, seen_whitepoint=FALSE, seen_chrm=FALSE;
3475 hr = IWICBitmapDecoder_GetFrame(decoder, active_frame, &frame);
3476 if (hr != S_OK) return;
3478 hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader);
3479 if (hr == S_OK)
3481 hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
3482 if (hr == S_OK)
3484 for (i = 0; i < block_count; i++)
3486 hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
3487 if (hr == S_OK)
3489 GUID format;
3491 hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
3492 if (SUCCEEDED(hr) && IsEqualGUID(&GUID_MetadataFormatChunktEXt, &format))
3494 PROPVARIANT name, value;
3495 PropertyItem* item;
3497 hr = IWICMetadataReader_GetValueByIndex(reader, 0, NULL, &name, &value);
3499 if (SUCCEEDED(hr))
3501 if (name.vt == VT_LPSTR)
3503 for (j = 0; j < ARRAY_SIZE(keywords); j++)
3504 if (!strcmp(keywords[j].name, name.pszVal))
3505 break;
3506 if (j < ARRAY_SIZE(keywords) && !keywords[j].seen)
3508 keywords[j].seen = TRUE;
3509 item = create_prop(keywords[j].propid, &value);
3510 if (item)
3511 add_property(bitmap, item);
3512 heap_free(item);
3516 PropVariantClear(&name);
3517 PropVariantClear(&value);
3520 else if (SUCCEEDED(hr) && IsEqualGUID(&GUID_MetadataFormatChunkgAMA, &format))
3522 PropertyItem* item;
3524 if (!seen_gamma)
3526 item = heap_alloc_zero(sizeof(PropertyItem) + sizeof(ULONG) * 2);
3527 if (item)
3529 ULONG *rational;
3530 item->length = sizeof(ULONG) * 2;
3531 item->type = PropertyTagTypeRational;
3532 item->id = PropertyTagGamma;
3533 rational = item->value = item + 1;
3534 rational[0] = 100000;
3535 rational[1] = get_ulong_by_index(reader, 0);
3536 add_property(bitmap, item);
3537 seen_gamma = TRUE;
3538 heap_free(item);
3542 else if (SUCCEEDED(hr) && IsEqualGUID(&GUID_MetadataFormatChunkcHRM, &format))
3544 PropertyItem* item;
3546 if (!seen_whitepoint)
3548 item = GdipAlloc(sizeof(PropertyItem) + sizeof(ULONG) * 4);
3549 if (item)
3551 ULONG *rational;
3552 item->length = sizeof(ULONG) * 4;
3553 item->type = PropertyTagTypeRational;
3554 item->id = PropertyTagWhitePoint;
3555 rational = item->value = item + 1;
3556 rational[0] = get_ulong_by_index(reader, 0);
3557 rational[1] = 100000;
3558 rational[2] = get_ulong_by_index(reader, 1);
3559 rational[3] = 100000;
3560 add_property(bitmap, item);
3561 seen_whitepoint = TRUE;
3562 GdipFree(item);
3565 if (!seen_chrm)
3567 item = GdipAlloc(sizeof(PropertyItem) + sizeof(ULONG) * 12);
3568 if (item)
3570 ULONG *rational;
3571 item->length = sizeof(ULONG) * 12;
3572 item->type = PropertyTagTypeRational;
3573 item->id = PropertyTagPrimaryChromaticities;
3574 rational = item->value = item + 1;
3575 rational[0] = get_ulong_by_index(reader, 2);
3576 rational[1] = 100000;
3577 rational[2] = get_ulong_by_index(reader, 3);
3578 rational[3] = 100000;
3579 rational[4] = get_ulong_by_index(reader, 4);
3580 rational[5] = 100000;
3581 rational[6] = get_ulong_by_index(reader, 5);
3582 rational[7] = 100000;
3583 rational[8] = get_ulong_by_index(reader, 6);
3584 rational[9] = 100000;
3585 rational[10] = get_ulong_by_index(reader, 7);
3586 rational[11] = 100000;
3587 add_property(bitmap, item);
3588 seen_chrm = TRUE;
3589 GdipFree(item);
3594 IWICMetadataReader_Release(reader);
3598 IWICMetadataBlockReader_Release(block_reader);
3601 IWICBitmapFrameDecode_Release(frame);
3604 static GpStatus initialize_decoder_wic(IStream *stream, REFGUID container, IWICBitmapDecoder **decoder)
3606 IWICImagingFactory *factory;
3607 HRESULT hr;
3609 TRACE("%p,%s\n", stream, wine_dbgstr_guid(container));
3611 hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory);
3612 if (FAILED(hr)) return hresult_to_status(hr);
3613 hr = IWICImagingFactory_CreateDecoder(factory, container, NULL, decoder);
3614 IWICImagingFactory_Release(factory);
3615 if (FAILED(hr)) return hresult_to_status(hr);
3617 hr = IWICBitmapDecoder_Initialize(*decoder, stream, WICDecodeMetadataCacheOnLoad);
3618 if (FAILED(hr))
3620 IWICBitmapDecoder_Release(*decoder);
3621 return hresult_to_status(hr);
3623 return Ok;
3626 typedef void (*metadata_reader_func)(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT frame);
3628 static GpStatus decode_frame_wic(IWICBitmapDecoder *decoder, BOOL force_conversion,
3629 UINT active_frame, metadata_reader_func metadata_reader, GpImage **image)
3631 GpStatus status=Ok;
3632 GpBitmap *bitmap;
3633 HRESULT hr;
3634 IWICBitmapFrameDecode *frame;
3635 IWICBitmapSource *source=NULL;
3636 IWICMetadataBlockReader *block_reader;
3637 WICPixelFormatGUID wic_format;
3638 PixelFormat gdip_format=0;
3639 ColorPalette *palette = NULL;
3640 WICBitmapPaletteType palette_type = WICBitmapPaletteTypeFixedHalftone256;
3641 int i;
3642 UINT width, height, frame_count;
3643 BitmapData lockeddata;
3644 WICRect wrc;
3646 TRACE("%p,%u,%p\n", decoder, active_frame, image);
3648 IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
3649 hr = IWICBitmapDecoder_GetFrame(decoder, active_frame, &frame);
3650 if (SUCCEEDED(hr)) /* got frame */
3652 hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &wic_format);
3654 if (SUCCEEDED(hr))
3656 if (!force_conversion)
3658 for (i=0; pixel_formats[i].wic_format; i++)
3660 if (IsEqualGUID(&wic_format, pixel_formats[i].wic_format))
3662 source = (IWICBitmapSource*)frame;
3663 IWICBitmapSource_AddRef(source);
3664 gdip_format = pixel_formats[i].gdip_format;
3665 palette_type = pixel_formats[i].palette_type;
3666 break;
3670 if (!source)
3672 /* unknown format; fall back on 32bppARGB */
3673 hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, (IWICBitmapSource*)frame, &source);
3674 gdip_format = PixelFormat32bppARGB;
3676 TRACE("%s => %#x\n", wine_dbgstr_guid(&wic_format), gdip_format);
3679 if (SUCCEEDED(hr)) /* got source */
3681 hr = IWICBitmapSource_GetSize(source, &width, &height);
3683 if (SUCCEEDED(hr))
3684 status = GdipCreateBitmapFromScan0(width, height, 0, gdip_format,
3685 NULL, &bitmap);
3687 if (SUCCEEDED(hr) && status == Ok) /* created bitmap */
3689 status = GdipBitmapLockBits(bitmap, NULL, ImageLockModeWrite,
3690 gdip_format, &lockeddata);
3691 if (status == Ok) /* locked bitmap */
3693 wrc.X = 0;
3694 wrc.Width = width;
3695 wrc.Height = 1;
3696 for (i=0; i<height; i++)
3698 wrc.Y = i;
3699 hr = IWICBitmapSource_CopyPixels(source, &wrc, abs(lockeddata.Stride),
3700 abs(lockeddata.Stride), (BYTE*)lockeddata.Scan0+lockeddata.Stride*i);
3701 if (FAILED(hr)) break;
3704 GdipBitmapUnlockBits(bitmap, &lockeddata);
3707 if (SUCCEEDED(hr) && status == Ok)
3708 *image = &bitmap->image;
3709 else
3711 *image = NULL;
3712 GdipDisposeImage(&bitmap->image);
3715 if (SUCCEEDED(hr) && status == Ok)
3717 double dpix, dpiy;
3718 hr = IWICBitmapSource_GetResolution(source, &dpix, &dpiy);
3719 if (SUCCEEDED(hr))
3721 bitmap->image.xres = dpix;
3722 bitmap->image.yres = dpiy;
3724 hr = S_OK;
3728 IWICBitmapSource_Release(source);
3731 if (SUCCEEDED(hr) && status == Ok) {
3732 bitmap->metadata_reader = NULL;
3734 if (metadata_reader)
3735 metadata_reader(bitmap, decoder, active_frame);
3736 else if (IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader) == S_OK)
3738 UINT block_count = 0;
3739 if (IWICMetadataBlockReader_GetCount(block_reader, &block_count) == S_OK && block_count)
3740 IWICMetadataBlockReader_GetReaderByIndex(block_reader, 0, &bitmap->metadata_reader);
3741 IWICMetadataBlockReader_Release(block_reader);
3744 palette = get_palette(frame, palette_type);
3745 IWICBitmapFrameDecode_Release(frame);
3749 if (FAILED(hr) && status == Ok) status = hresult_to_status(hr);
3751 if (status == Ok)
3753 /* Native GDI+ used to be smarter, but since Win7 it just sets these flags. */
3754 bitmap->image.flags |= ImageFlagsReadOnly|ImageFlagsHasRealPixelSize|ImageFlagsHasRealDPI;
3755 if (IsEqualGUID(&wic_format, &GUID_WICPixelFormat2bppGray) ||
3756 IsEqualGUID(&wic_format, &GUID_WICPixelFormat4bppGray) ||
3757 IsEqualGUID(&wic_format, &GUID_WICPixelFormat8bppGray) ||
3758 IsEqualGUID(&wic_format, &GUID_WICPixelFormat16bppGray))
3759 bitmap->image.flags |= ImageFlagsColorSpaceGRAY;
3760 else
3761 bitmap->image.flags |= ImageFlagsColorSpaceRGB;
3762 bitmap->image.frame_count = frame_count;
3763 bitmap->image.current_frame = active_frame;
3764 bitmap->image.decoder = decoder;
3765 IWICBitmapDecoder_AddRef(decoder);
3766 if (palette)
3768 heap_free(bitmap->image.palette);
3769 bitmap->image.palette = palette;
3771 else
3773 if (IsEqualGUID(&wic_format, &GUID_WICPixelFormatBlackWhite))
3774 bitmap->image.palette->Flags = 0;
3776 TRACE("=> %p\n", *image);
3779 return status;
3782 static GpStatus decode_image_wic(IStream *stream, REFGUID container,
3783 metadata_reader_func metadata_reader, GpImage **image)
3785 IWICBitmapDecoder *decoder;
3786 GpStatus status;
3788 status = initialize_decoder_wic(stream, container, &decoder);
3789 if(status != Ok)
3790 return status;
3792 status = decode_frame_wic(decoder, FALSE, 0, metadata_reader, image);
3793 IWICBitmapDecoder_Release(decoder);
3794 return status;
3797 static GpStatus select_frame_wic(GpImage *image, UINT active_frame)
3799 size_t obj_size, body_offset;
3800 GpImage *new_image;
3801 GpStatus status;
3803 status = decode_frame_wic(image->decoder, FALSE, active_frame, NULL, &new_image);
3804 if(status != Ok)
3805 return status;
3807 if (image->type == ImageTypeBitmap)
3808 obj_size = sizeof(GpBitmap);
3809 else if (image->type == ImageTypeMetafile)
3810 obj_size = sizeof(GpMetafile);
3811 else
3813 ERR("unknown image type: %d\n", image->type);
3814 GdipDisposeImage(new_image);
3815 return GenericError;
3818 memcpy(&new_image->format, &image->format, sizeof(GUID));
3819 new_image->encoder = image->encoder;
3820 image->encoder = NULL;
3821 free_image_data(image);
3822 memcpy(image, new_image, FIELD_OFFSET(GpImage, lock));
3823 body_offset = RTL_SIZEOF_THROUGH_FIELD(GpImage, lock);
3824 memcpy((char *)image + body_offset, (char *)new_image + body_offset, obj_size - body_offset);
3825 new_image->type = ~0;
3826 heap_free(new_image);
3827 return Ok;
3830 static HRESULT get_gif_frame_rect(IWICBitmapFrameDecode *frame,
3831 UINT *left, UINT *top, UINT *width, UINT *height)
3833 *left = get_gif_frame_property(frame, &GUID_MetadataFormatIMD, L"Left");
3834 *top = get_gif_frame_property(frame, &GUID_MetadataFormatIMD, L"Top");
3836 return IWICBitmapFrameDecode_GetSize(frame, width, height);
3839 static HRESULT blit_gif_frame(GpBitmap *bitmap, IWICBitmapFrameDecode *frame, BOOL first_frame)
3841 UINT i, j, left, top, width, height;
3842 IWICBitmapSource *source;
3843 BYTE *new_bits;
3844 HRESULT hr;
3846 hr = get_gif_frame_rect(frame, &left, &top, &width, &height);
3847 if(FAILED(hr))
3848 return hr;
3850 hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, (IWICBitmapSource*)frame, &source);
3851 if(FAILED(hr))
3852 return hr;
3854 new_bits = heap_alloc_zero(width*height*4);
3855 if(!new_bits)
3856 return E_OUTOFMEMORY;
3858 hr = IWICBitmapSource_CopyPixels(source, NULL, width*4, width*height*4, new_bits);
3859 IWICBitmapSource_Release(source);
3860 if(FAILED(hr)) {
3861 heap_free(new_bits);
3862 return hr;
3865 for(i=0; i<height && i+top<bitmap->height; i++) {
3866 for(j=0; j<width && j+left<bitmap->width; j++) {
3867 DWORD *src = (DWORD*)(new_bits+i*width*4+j*4);
3868 DWORD *dst = (DWORD*)(bitmap->bits+(i+top)*bitmap->stride+(j+left)*4);
3870 if(first_frame || *src>>24 != 0)
3871 *dst = *src;
3874 heap_free(new_bits);
3875 return hr;
3878 static DWORD get_gif_background_color(GpBitmap *bitmap)
3880 BYTE bgcolor_idx = 0;
3881 UINT i;
3883 for(i=0; i<bitmap->prop_count; i++) {
3884 if(bitmap->prop_item[i].id == PropertyTagIndexBackground)
3885 bgcolor_idx = *(BYTE*)bitmap->prop_item[i].value;
3886 else if(bitmap->prop_item[i].id == PropertyTagIndexTransparent)
3887 return 0;
3890 for(i=0; i<bitmap->prop_count; i++) {
3891 if(bitmap->prop_item[i].id == PropertyTagGlobalPalette) {
3892 if(bitmap->prop_item[i].length/3 > bgcolor_idx) {
3893 BYTE *color = ((BYTE*)bitmap->prop_item[i].value)+bgcolor_idx*3;
3894 return color[2] + (color[1]<<8) + (color[0]<<16) + (0xffu<<24);
3896 break;
3900 FIXME("can't get gif background color\n");
3901 return 0xffffffff;
3904 static GpStatus select_frame_gif(GpImage* image, UINT active_frame)
3906 GpBitmap *bitmap = (GpBitmap*)image;
3907 IWICBitmapFrameDecode *frame;
3908 int cur_frame=0, disposal;
3909 BOOL bgcolor_set = FALSE;
3910 DWORD bgcolor = 0;
3911 HRESULT hr;
3913 if(active_frame > image->current_frame) {
3914 hr = IWICBitmapDecoder_GetFrame(bitmap->image.decoder, image->current_frame, &frame);
3915 if(FAILED(hr))
3916 return hresult_to_status(hr);
3917 disposal = get_gif_frame_property(frame, &GUID_MetadataFormatGCE, L"Disposal");
3918 IWICBitmapFrameDecode_Release(frame);
3920 if(disposal == GIF_DISPOSE_RESTORE_TO_BKGND)
3921 cur_frame = image->current_frame;
3922 else if(disposal != GIF_DISPOSE_RESTORE_TO_PREV)
3923 cur_frame = image->current_frame+1;
3926 while(cur_frame != active_frame) {
3927 hr = IWICBitmapDecoder_GetFrame(bitmap->image.decoder, cur_frame, &frame);
3928 if(FAILED(hr))
3929 return hresult_to_status(hr);
3930 disposal = get_gif_frame_property(frame, &GUID_MetadataFormatGCE, L"Disposal");
3932 if(disposal==GIF_DISPOSE_UNSPECIFIED || disposal==GIF_DISPOSE_DO_NOT_DISPOSE) {
3933 hr = blit_gif_frame(bitmap, frame, cur_frame==0);
3934 if(FAILED(hr))
3935 return hresult_to_status(hr);
3936 }else if(disposal == GIF_DISPOSE_RESTORE_TO_BKGND) {
3937 UINT left, top, width, height, i, j;
3939 if(!bgcolor_set) {
3940 bgcolor = get_gif_background_color(bitmap);
3941 bgcolor_set = TRUE;
3944 hr = get_gif_frame_rect(frame, &left, &top, &width, &height);
3945 if(FAILED(hr))
3946 return hresult_to_status(hr);
3947 for(i=top; i<top+height && i<bitmap->height; i++) {
3948 DWORD *bits = (DWORD*)(bitmap->bits+i*bitmap->stride);
3949 for(j=left; j<left+width && j<bitmap->width; j++)
3950 bits[j] = bgcolor;
3954 IWICBitmapFrameDecode_Release(frame);
3955 cur_frame++;
3958 hr = IWICBitmapDecoder_GetFrame(bitmap->image.decoder, active_frame, &frame);
3959 if(FAILED(hr))
3960 return hresult_to_status(hr);
3962 hr = blit_gif_frame(bitmap, frame, cur_frame==0);
3963 IWICBitmapFrameDecode_Release(frame);
3964 if(FAILED(hr))
3965 return hresult_to_status(hr);
3967 image->current_frame = active_frame;
3968 return Ok;
3971 static GpStatus decode_image_icon(IStream* stream, GpImage **image)
3973 return decode_image_wic(stream, &GUID_ContainerFormatIco, NULL, image);
3976 static GpStatus decode_image_bmp(IStream* stream, GpImage **image)
3978 GpStatus status;
3979 GpBitmap* bitmap;
3981 status = decode_image_wic(stream, &GUID_ContainerFormatBmp, NULL, image);
3983 bitmap = (GpBitmap*)*image;
3985 if (status == Ok && bitmap->format == PixelFormat32bppARGB)
3987 /* WIC supports bmp files with alpha, but gdiplus does not */
3988 bitmap->format = PixelFormat32bppRGB;
3991 return status;
3994 static GpStatus decode_image_jpeg(IStream* stream, GpImage **image)
3996 return decode_image_wic(stream, &GUID_ContainerFormatJpeg, NULL, image);
3999 static BOOL has_png_transparency_chunk(IStream *pIStream)
4001 LARGE_INTEGER seek;
4002 BOOL has_tRNS = FALSE;
4003 HRESULT hr;
4004 BYTE header[8];
4006 seek.QuadPart = 8;
4009 ULARGE_INTEGER chunk_start;
4010 ULONG bytesread, chunk_size;
4012 hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, &chunk_start);
4013 if (FAILED(hr)) break;
4015 hr = IStream_Read(pIStream, header, 8, &bytesread);
4016 if (FAILED(hr) || bytesread < 8) break;
4018 chunk_size = (header[0] << 24) | (header[1] << 16) | (header[2] << 8) | header[3];
4019 if (!memcmp(&header[4], "tRNS", 4))
4021 has_tRNS = TRUE;
4022 break;
4025 seek.QuadPart = chunk_start.QuadPart + chunk_size + 12; /* skip data and CRC */
4026 } while (memcmp(&header[4], "IDAT", 4) && memcmp(&header[4], "IEND", 4));
4028 TRACE("has_tRNS = %d\n", has_tRNS);
4029 return has_tRNS;
4032 static GpStatus decode_image_png(IStream* stream, GpImage **image)
4034 IWICBitmapDecoder *decoder;
4035 IWICBitmapFrameDecode *frame;
4036 GpStatus status;
4037 HRESULT hr;
4038 GUID format;
4039 BOOL force_conversion = FALSE;
4041 status = initialize_decoder_wic(stream, &GUID_ContainerFormatPng, &decoder);
4042 if (status != Ok)
4043 return status;
4045 hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
4046 if (hr == S_OK)
4048 hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &format);
4049 if (hr == S_OK)
4051 if (IsEqualGUID(&format, &GUID_WICPixelFormat8bppGray))
4052 force_conversion = TRUE;
4053 else if ((IsEqualGUID(&format, &GUID_WICPixelFormat8bppIndexed) ||
4054 IsEqualGUID(&format, &GUID_WICPixelFormat4bppIndexed) ||
4055 IsEqualGUID(&format, &GUID_WICPixelFormat2bppIndexed) ||
4056 IsEqualGUID(&format, &GUID_WICPixelFormat1bppIndexed) ||
4057 IsEqualGUID(&format, &GUID_WICPixelFormat24bppBGR)) &&
4058 has_png_transparency_chunk(stream))
4059 force_conversion = TRUE;
4061 status = decode_frame_wic(decoder, force_conversion, 0, png_metadata_reader, image);
4063 else
4064 status = hresult_to_status(hr);
4066 IWICBitmapFrameDecode_Release(frame);
4068 else
4069 status = hresult_to_status(hr);
4071 IWICBitmapDecoder_Release(decoder);
4072 return status;
4075 static GpStatus decode_image_gif(IStream* stream, GpImage **image)
4077 IWICBitmapDecoder *decoder;
4078 UINT frame_count;
4079 GpStatus status;
4080 HRESULT hr;
4082 status = initialize_decoder_wic(stream, &GUID_ContainerFormatGif, &decoder);
4083 if(status != Ok)
4084 return status;
4086 hr = IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
4087 if(FAILED(hr))
4088 return hresult_to_status(hr);
4090 status = decode_frame_wic(decoder, frame_count > 1, 0, gif_metadata_reader, image);
4091 IWICBitmapDecoder_Release(decoder);
4092 if(status != Ok)
4093 return status;
4095 if(frame_count > 1) {
4096 heap_free((*image)->palette);
4097 (*image)->palette = NULL;
4099 return Ok;
4102 static GpStatus decode_image_tiff(IStream* stream, GpImage **image)
4104 return decode_image_wic(stream, &GUID_ContainerFormatTiff, NULL, image);
4107 C_ASSERT(offsetof(WmfPlaceableFileHeader, Key) == 0);
4109 static GpStatus load_wmf(IStream *stream, GpMetafile **metafile)
4111 WmfPlaceableFileHeader pfh;
4112 BOOL is_placeable = FALSE;
4113 LARGE_INTEGER seek;
4114 GpStatus status;
4115 METAHEADER mh;
4116 HMETAFILE hmf;
4117 HRESULT hr;
4118 ULONG size;
4119 void *buf;
4121 hr = IStream_Read(stream, &mh, sizeof(mh), &size);
4122 if (hr != S_OK || size != sizeof(mh))
4123 return GenericError;
4125 /* detect whether stream starts with a WmfPlaceablefileheader */
4126 if (*(UINT32 *)&mh == WMF_PLACEABLE_KEY)
4128 seek.QuadPart = 0;
4129 hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
4130 if (FAILED(hr)) return hresult_to_status(hr);
4132 hr = IStream_Read(stream, &pfh, sizeof(pfh), &size);
4133 if (hr != S_OK || size != sizeof(pfh))
4134 return GenericError;
4136 hr = IStream_Read(stream, &mh, sizeof(mh), &size);
4137 if (hr != S_OK || size != sizeof(mh))
4138 return GenericError;
4140 is_placeable = TRUE;
4143 seek.QuadPart = is_placeable ? sizeof(pfh) : 0;
4144 hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
4145 if (FAILED(hr)) return hresult_to_status(hr);
4147 buf = heap_alloc(mh.mtSize * 2);
4148 if (!buf) return OutOfMemory;
4150 hr = IStream_Read(stream, buf, mh.mtSize * 2, &size);
4151 if (hr != S_OK || size != mh.mtSize * 2)
4153 heap_free(buf);
4154 return GenericError;
4157 hmf = SetMetaFileBitsEx(mh.mtSize * 2, buf);
4158 heap_free(buf);
4159 if (!hmf)
4160 return GenericError;
4162 status = GdipCreateMetafileFromWmf(hmf, TRUE, is_placeable ? &pfh : NULL, metafile);
4163 if (status != Ok)
4164 DeleteMetaFile(hmf);
4165 return status;
4168 static GpStatus decode_image_wmf(IStream *stream, GpImage **image)
4170 GpMetafile *metafile;
4171 GpStatus status;
4173 TRACE("%p %p\n", stream, image);
4175 if (!stream || !image)
4176 return InvalidParameter;
4178 status = load_wmf(stream, &metafile);
4179 if (status != Ok)
4181 TRACE("Could not load metafile\n");
4182 return status;
4185 *image = (GpImage *)metafile;
4186 TRACE("<-- %p\n", *image);
4188 return Ok;
4191 static GpStatus load_emf(IStream *stream, GpMetafile **metafile)
4193 LARGE_INTEGER seek;
4194 ENHMETAHEADER emh;
4195 HENHMETAFILE hemf;
4196 GpStatus status;
4197 HRESULT hr;
4198 ULONG size;
4199 void *buf;
4201 hr = IStream_Read(stream, &emh, sizeof(emh), &size);
4202 if (hr != S_OK || size != sizeof(emh) || emh.dSignature != ENHMETA_SIGNATURE)
4203 return GenericError;
4205 seek.QuadPart = 0;
4206 hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
4207 if (FAILED(hr)) return hresult_to_status(hr);
4209 buf = heap_alloc(emh.nBytes);
4210 if (!buf) return OutOfMemory;
4212 hr = IStream_Read(stream, buf, emh.nBytes, &size);
4213 if (hr != S_OK || size != emh.nBytes)
4215 heap_free(buf);
4216 return GenericError;
4219 hemf = SetEnhMetaFileBits(emh.nBytes, buf);
4220 heap_free(buf);
4221 if (!hemf)
4222 return GenericError;
4224 status = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
4225 if (status != Ok)
4226 DeleteEnhMetaFile(hemf);
4227 return status;
4230 static GpStatus decode_image_emf(IStream *stream, GpImage **image)
4232 GpMetafile *metafile;
4233 GpStatus status;
4235 TRACE("%p %p\n", stream, image);
4237 if (!stream || !image)
4238 return InvalidParameter;
4240 status = load_emf(stream, &metafile);
4241 if (status != Ok)
4243 TRACE("Could not load metafile\n");
4244 return status;
4247 *image = (GpImage *)metafile;
4248 TRACE("<-- %p\n", *image);
4250 return Ok;
4253 typedef GpStatus (*encode_image_func)(GpImage *image, IStream* stream,
4254 GDIPCONST EncoderParameters* params);
4256 typedef GpStatus (*decode_image_func)(IStream *stream, GpImage **image);
4258 typedef GpStatus (*select_image_func)(GpImage *image, UINT active_frame);
4260 typedef struct image_codec {
4261 ImageCodecInfo info;
4262 encode_image_func encode_func;
4263 decode_image_func decode_func;
4264 select_image_func select_func;
4265 } image_codec;
4267 typedef enum {
4268 BMP,
4269 JPEG,
4270 GIF,
4271 TIFF,
4272 EMF,
4273 WMF,
4274 PNG,
4275 ICO,
4276 NUM_CODECS
4277 } ImageFormat;
4279 static const struct image_codec codecs[NUM_CODECS];
4281 static GpStatus get_decoder_info(IStream* stream, const struct image_codec **result)
4283 BYTE signature[8];
4284 const BYTE *pattern, *mask;
4285 LARGE_INTEGER seek;
4286 HRESULT hr;
4287 ULONG bytesread;
4288 int i;
4289 DWORD j, sig;
4291 /* seek to the start of the stream */
4292 seek.QuadPart = 0;
4293 hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
4294 if (FAILED(hr)) return hresult_to_status(hr);
4296 /* read the first 8 bytes */
4297 /* FIXME: This assumes all codecs have signatures <= 8 bytes in length */
4298 hr = IStream_Read(stream, signature, 8, &bytesread);
4299 if (FAILED(hr)) return hresult_to_status(hr);
4300 if (hr == S_FALSE || bytesread == 0) return GenericError;
4302 for (i = 0; i < NUM_CODECS; i++) {
4303 if ((codecs[i].info.Flags & ImageCodecFlagsDecoder) &&
4304 bytesread >= codecs[i].info.SigSize)
4306 for (sig=0; sig<codecs[i].info.SigCount; sig++)
4308 pattern = &codecs[i].info.SigPattern[codecs[i].info.SigSize*sig];
4309 mask = &codecs[i].info.SigMask[codecs[i].info.SigSize*sig];
4310 for (j=0; j<codecs[i].info.SigSize; j++)
4311 if ((signature[j] & mask[j]) != pattern[j])
4312 break;
4313 if (j == codecs[i].info.SigSize)
4315 *result = &codecs[i];
4316 return Ok;
4322 TRACE("no match for %lu byte signature %x %x %x %x %x %x %x %x\n", bytesread,
4323 signature[0],signature[1],signature[2],signature[3],
4324 signature[4],signature[5],signature[6],signature[7]);
4326 return GenericError;
4329 static GpStatus get_decoder_info_from_image(GpImage *image, const struct image_codec **result)
4331 int i;
4333 for (i = 0; i < NUM_CODECS; i++) {
4334 if ((codecs[i].info.Flags & ImageCodecFlagsDecoder) &&
4335 IsEqualIID(&codecs[i].info.FormatID, &image->format))
4337 *result = &codecs[i];
4338 return Ok;
4342 TRACE("no match for format: %s\n", wine_dbgstr_guid(&image->format));
4343 return GenericError;
4346 GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image, GDIPCONST GUID *dimensionID,
4347 UINT frame)
4349 GpStatus stat;
4350 const struct image_codec *codec = NULL;
4352 TRACE("(%p,%s,%u)\n", image, debugstr_guid(dimensionID), frame);
4354 if (!image || !dimensionID)
4355 return InvalidParameter;
4356 if(!image_lock(image))
4357 return ObjectBusy;
4359 if (frame >= image->frame_count)
4361 WARN("requested frame %u, but image has only %u\n", frame, image->frame_count);
4362 image_unlock(image);
4363 return InvalidParameter;
4366 if (image->type != ImageTypeBitmap && image->type != ImageTypeMetafile)
4368 WARN("invalid image type %d\n", image->type);
4369 image_unlock(image);
4370 return InvalidParameter;
4373 if (image->current_frame == frame)
4375 image_unlock(image);
4376 return Ok;
4379 if (!image->decoder)
4381 TRACE("image doesn't have an associated decoder\n");
4382 image_unlock(image);
4383 return Ok;
4386 /* choose an appropriate image decoder */
4387 stat = get_decoder_info_from_image(image, &codec);
4388 if (stat != Ok)
4390 WARN("can't find decoder info\n");
4391 image_unlock(image);
4392 return stat;
4395 stat = codec->select_func(image, frame);
4396 image_unlock(image);
4397 return stat;
4400 GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream *stream, GpImage **image)
4402 GpStatus stat;
4403 LARGE_INTEGER seek;
4404 HRESULT hr;
4405 const struct image_codec *codec=NULL;
4407 TRACE("%p %p\n", stream, image);
4409 if (!stream || !image)
4410 return InvalidParameter;
4412 /* choose an appropriate image decoder */
4413 stat = get_decoder_info(stream, &codec);
4414 if (stat != Ok) return stat;
4416 /* seek to the start of the stream */
4417 seek.QuadPart = 0;
4418 hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
4419 if (FAILED(hr)) return hresult_to_status(hr);
4421 /* call on the image decoder to do the real work */
4422 stat = codec->decode_func(stream, image);
4424 /* take note of the original data format */
4425 if (stat == Ok)
4427 memcpy(&(*image)->format, &codec->info.FormatID, sizeof(GUID));
4428 return Ok;
4431 return stat;
4434 /* FIXME: no ICM */
4435 GpStatus WINGDIPAPI GdipLoadImageFromStreamICM(IStream* stream, GpImage **image)
4437 TRACE("%p %p\n", stream, image);
4439 return GdipLoadImageFromStream(stream, image);
4442 GpStatus WINGDIPAPI GdipRemovePropertyItem(GpImage *image, PROPID propId)
4444 static int calls;
4446 TRACE("(%p,%lu)\n", image, propId);
4448 if(!image)
4449 return InvalidParameter;
4451 if(!(calls++))
4452 FIXME("not implemented\n");
4454 return NotImplemented;
4457 GpStatus WINGDIPAPI GdipSetPropertyItem(GpImage *image, GDIPCONST PropertyItem* item)
4459 static int calls;
4461 if (!image || !item) return InvalidParameter;
4463 TRACE("(%p,%p:%#lx,%u,%lu,%p)\n", image, item, item->id, item->type, item->length, item->value);
4465 if(!(calls++))
4466 FIXME("not implemented\n");
4468 return Ok;
4471 GpStatus WINGDIPAPI GdipSaveImageToFile(GpImage *image, GDIPCONST WCHAR* filename,
4472 GDIPCONST CLSID *clsidEncoder,
4473 GDIPCONST EncoderParameters *encoderParams)
4475 GpStatus stat;
4476 IStream *stream;
4478 TRACE("%p (%s) %p %p\n", image, debugstr_w(filename), clsidEncoder, encoderParams);
4480 if (!image || !filename|| !clsidEncoder)
4481 return InvalidParameter;
4483 /* this might release an old file stream held by the encoder so we can re-create it below */
4484 terminate_encoder_wic(image);
4486 stat = GdipCreateStreamOnFile(filename, GENERIC_WRITE, &stream);
4487 if (stat != Ok)
4488 return GenericError;
4490 stat = GdipSaveImageToStream(image, stream, clsidEncoder, encoderParams);
4492 IStream_Release(stream);
4493 return stat;
4496 /*************************************************************************
4497 * Encoding functions -
4498 * These functions encode an image in different image file formats.
4501 static GpStatus initialize_encoder_wic(IStream *stream, REFGUID container, GpImage *image)
4503 IWICImagingFactory *factory;
4504 HRESULT hr;
4506 TRACE("%p,%s\n", stream, wine_dbgstr_guid(container));
4508 terminate_encoder_wic(image); /* terminate previous encoder if it exists */
4510 hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory);
4511 if (FAILED(hr)) return hresult_to_status(hr);
4512 hr = IWICImagingFactory_CreateEncoder(factory, container, NULL, &image->encoder);
4513 IWICImagingFactory_Release(factory);
4514 if (FAILED(hr))
4516 image->encoder = NULL;
4517 return hresult_to_status(hr);
4520 hr = IWICBitmapEncoder_Initialize(image->encoder, stream, WICBitmapEncoderNoCache);
4521 if (FAILED(hr))
4523 IWICBitmapEncoder_Release(image->encoder);
4524 image->encoder = NULL;
4525 return hresult_to_status(hr);
4527 return Ok;
4530 GpStatus terminate_encoder_wic(GpImage *image)
4532 if (!image->encoder)
4533 return Ok;
4534 else
4536 HRESULT hr = IWICBitmapEncoder_Commit(image->encoder);
4537 IWICBitmapEncoder_Release(image->encoder);
4538 image->encoder = NULL;
4539 return hresult_to_status(hr);
4543 static GpStatus encode_frame_wic(IWICBitmapEncoder *encoder, GpImage *image)
4545 GpStatus stat;
4546 GpBitmap *bitmap;
4547 IWICBitmapFrameEncode *frameencode;
4548 IPropertyBag2 *encoderoptions;
4549 GUID container_format;
4550 HRESULT hr;
4551 UINT width, height;
4552 PixelFormat gdipformat=0;
4553 const WICPixelFormatGUID *desired_wicformat;
4554 WICPixelFormatGUID wicformat;
4555 GpRect rc;
4556 BitmapData lockeddata;
4557 UINT i;
4559 if (image->type != ImageTypeBitmap)
4560 return GenericError;
4562 bitmap = (GpBitmap*)image;
4564 GdipGetImageWidth(image, &width);
4565 GdipGetImageHeight(image, &height);
4567 rc.X = 0;
4568 rc.Y = 0;
4569 rc.Width = width;
4570 rc.Height = height;
4572 hr = IWICBitmapEncoder_CreateNewFrame(encoder, &frameencode, &encoderoptions);
4574 if (SUCCEEDED(hr)) /* created frame */
4576 hr = IWICBitmapEncoder_GetContainerFormat(encoder, &container_format);
4577 if (SUCCEEDED(hr) && IsEqualGUID(&container_format, &GUID_ContainerFormatPng))
4579 /* disable PNG filters for faster encoding */
4580 PROPBAG2 filter_option = { .pstrName = (LPOLESTR) L"FilterOption" };
4581 VARIANT filter_value;
4582 VariantInit(&filter_value);
4583 V_VT(&filter_value) = VT_UI1;
4584 V_UI1(&filter_value) = WICPngFilterNone;
4585 hr = IPropertyBag2_Write(encoderoptions, 1, &filter_option, &filter_value);
4588 if (SUCCEEDED(hr))
4589 hr = IWICBitmapFrameEncode_Initialize(frameencode, encoderoptions);
4591 if (SUCCEEDED(hr))
4592 hr = IWICBitmapFrameEncode_SetSize(frameencode, width, height);
4594 if (SUCCEEDED(hr))
4595 hr = IWICBitmapFrameEncode_SetResolution(frameencode, image->xres, image->yres);
4597 if (SUCCEEDED(hr))
4599 for (i=0; pixel_formats[i].wic_format; i++)
4601 if (pixel_formats[i].gdip_format == bitmap->format)
4603 desired_wicformat = pixel_formats[i].wic_format;
4604 gdipformat = bitmap->format;
4605 break;
4608 if (!gdipformat)
4610 desired_wicformat = &GUID_WICPixelFormat32bppBGRA;
4611 gdipformat = PixelFormat32bppARGB;
4614 memcpy(&wicformat, desired_wicformat, sizeof(GUID));
4615 hr = IWICBitmapFrameEncode_SetPixelFormat(frameencode, &wicformat);
4618 if (SUCCEEDED(hr) && !IsEqualGUID(desired_wicformat, &wicformat))
4620 /* Encoder doesn't support this bitmap's format. */
4621 gdipformat = 0;
4622 for (i=0; pixel_formats[i].wic_format; i++)
4624 if (IsEqualGUID(&wicformat, pixel_formats[i].wic_format))
4626 gdipformat = pixel_formats[i].gdip_format;
4627 break;
4630 if (!gdipformat)
4632 ERR("Cannot support encoder format %s\n", debugstr_guid(&wicformat));
4633 hr = E_FAIL;
4637 if (SUCCEEDED(hr) && IsIndexedPixelFormat(gdipformat) && image->palette)
4638 hr = set_palette(frameencode, image->palette);
4640 if (SUCCEEDED(hr))
4642 stat = GdipBitmapLockBits(bitmap, &rc, ImageLockModeRead, gdipformat,
4643 &lockeddata);
4645 if (stat == Ok)
4647 UINT row_size = (lockeddata.Width * PIXELFORMATBPP(gdipformat) + 7)/8;
4648 BYTE *row;
4650 /* write one row at a time in case stride is negative */
4651 row = lockeddata.Scan0;
4652 for (i=0; i<lockeddata.Height; i++)
4654 hr = IWICBitmapFrameEncode_WritePixels(frameencode, 1, row_size, row_size, row);
4655 if (FAILED(hr)) break;
4656 row += lockeddata.Stride;
4659 GdipBitmapUnlockBits(bitmap, &lockeddata);
4661 else
4662 hr = E_FAIL;
4665 if (SUCCEEDED(hr))
4666 hr = IWICBitmapFrameEncode_Commit(frameencode);
4668 IWICBitmapFrameEncode_Release(frameencode);
4669 IPropertyBag2_Release(encoderoptions);
4672 return hresult_to_status(hr);
4675 static BOOL has_encoder_param_long(GDIPCONST EncoderParameters *params, GUID param_guid, ULONG val)
4677 int param_idx, value_idx;
4679 if (!params)
4680 return FALSE;
4682 for (param_idx = 0; param_idx < params->Count; param_idx++)
4684 EncoderParameter param = params->Parameter[param_idx];
4685 if (param.Type == EncoderParameterValueTypeLong && IsEqualCLSID(&param.Guid, &param_guid))
4687 ULONG *value_array = (ULONG*) param.Value;
4688 for (value_idx = 0; value_idx < param.NumberOfValues; value_idx++)
4690 if (value_array[value_idx] == val)
4691 return TRUE;
4695 return FALSE;
4698 static GpStatus encode_image_wic(GpImage *image, IStream *stream,
4699 REFGUID container, GDIPCONST EncoderParameters *params)
4701 GpStatus status, terminate_status;
4703 if (image->type != ImageTypeBitmap)
4704 return GenericError;
4706 status = initialize_encoder_wic(stream, container, image);
4708 if (status == Ok)
4709 status = encode_frame_wic(image->encoder, image);
4711 if (!has_encoder_param_long(params, EncoderSaveFlag, EncoderValueMultiFrame))
4713 /* always try to terminate, but if something already failed earlier, keep the old status. */
4714 terminate_status = terminate_encoder_wic(image);
4715 if (status == Ok)
4716 status = terminate_status;
4719 return status;
4722 static GpStatus encode_image_BMP(GpImage *image, IStream* stream,
4723 GDIPCONST EncoderParameters* params)
4725 return encode_image_wic(image, stream, &GUID_ContainerFormatBmp, params);
4728 static GpStatus encode_image_tiff(GpImage *image, IStream* stream,
4729 GDIPCONST EncoderParameters* params)
4731 return encode_image_wic(image, stream, &GUID_ContainerFormatTiff, params);
4734 GpStatus encode_image_png(GpImage *image, IStream* stream,
4735 GDIPCONST EncoderParameters* params)
4737 return encode_image_wic(image, stream, &GUID_ContainerFormatPng, params);
4740 static GpStatus encode_image_jpeg(GpImage *image, IStream* stream,
4741 GDIPCONST EncoderParameters* params)
4743 return encode_image_wic(image, stream, &GUID_ContainerFormatJpeg, params);
4746 static GpStatus encode_image_gif(GpImage *image, IStream* stream,
4747 GDIPCONST EncoderParameters* params)
4749 return encode_image_wic(image, stream, &GUID_ContainerFormatGif, params);
4752 /*****************************************************************************
4753 * GdipSaveImageToStream [GDIPLUS.@]
4755 GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream,
4756 GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
4758 GpStatus stat;
4759 encode_image_func encode_image;
4760 int i;
4762 TRACE("%p, %p, %s, %p\n", image, stream, wine_dbgstr_guid(clsid), params);
4764 if(!image || !stream)
4765 return InvalidParameter;
4767 /* select correct encoder */
4768 encode_image = NULL;
4769 for (i = 0; i < NUM_CODECS; i++) {
4770 if ((codecs[i].info.Flags & ImageCodecFlagsEncoder) &&
4771 IsEqualCLSID(clsid, &codecs[i].info.Clsid))
4772 encode_image = codecs[i].encode_func;
4774 if (encode_image == NULL)
4775 return UnknownImageFormat;
4777 stat = encode_image(image, stream, params);
4779 return stat;
4782 /*****************************************************************************
4783 * GdipSaveAdd [GDIPLUS.@]
4785 * Like GdipSaveAddImage(), but encode the currently active frame of the given image into the file
4786 * or stream that is currently being encoded.
4788 GpStatus WINGDIPAPI GdipSaveAdd(GpImage *image, GDIPCONST EncoderParameters *params)
4790 return GdipSaveAddImage(image, image, params);
4793 /*****************************************************************************
4794 * GdipSaveAddImage [GDIPLUS.@]
4796 * Encode the currently active frame of additional_image into the file or stream that is currently
4797 * being encoded by the image given in the image parameter. The first frame of a multi-frame image
4798 * must be encoded using the normal GdipSaveImageToStream() or GdipSaveImageToFile() functions,
4799 * but with the "MultiFrame" encoding parameter set. The multi-frame encoding process must be
4800 * finished after adding the last frame by calling GdipSaveAdd() with the "Flush" encoding parameter
4801 * set.
4803 GpStatus WINGDIPAPI GdipSaveAddImage(GpImage *image, GpImage *additional_image,
4804 GDIPCONST EncoderParameters *params)
4806 TRACE("%p, %p, %p\n", image, additional_image, params);
4808 if (!image || !additional_image || !params)
4809 return InvalidParameter;
4811 if (!image->encoder)
4812 return Win32Error;
4814 if (has_encoder_param_long(params, EncoderSaveFlag, EncoderValueFlush))
4815 return terminate_encoder_wic(image);
4816 else if (has_encoder_param_long(params, EncoderSaveFlag, EncoderValueFrameDimensionPage))
4817 return encode_frame_wic(image->encoder, additional_image);
4818 else
4819 return InvalidParameter;
4822 /*****************************************************************************
4823 * GdipGetImagePalette [GDIPLUS.@]
4825 GpStatus WINGDIPAPI GdipGetImagePalette(GpImage *image, ColorPalette *palette, INT size)
4827 INT count;
4829 TRACE("(%p,%p,%i)\n", image, palette, size);
4831 if (!image || !palette)
4832 return InvalidParameter;
4834 count = image->palette ? image->palette->Count : 0;
4836 if (size < (sizeof(UINT)*2+sizeof(ARGB)*count))
4838 TRACE("<-- InsufficientBuffer\n");
4839 return InsufficientBuffer;
4842 if (image->palette)
4844 palette->Flags = image->palette->Flags;
4845 palette->Count = image->palette->Count;
4846 memcpy(palette->Entries, image->palette->Entries, sizeof(ARGB)*image->palette->Count);
4848 else
4850 palette->Flags = 0;
4851 palette->Count = 0;
4853 return Ok;
4856 /*****************************************************************************
4857 * GdipSetImagePalette [GDIPLUS.@]
4859 GpStatus WINGDIPAPI GdipSetImagePalette(GpImage *image,
4860 GDIPCONST ColorPalette *palette)
4862 ColorPalette *new_palette;
4864 TRACE("(%p,%p)\n", image, palette);
4866 if(!image || !palette || palette->Count > 256)
4867 return InvalidParameter;
4869 new_palette = heap_alloc_zero(2 * sizeof(UINT) + palette->Count * sizeof(ARGB));
4870 if (!new_palette) return OutOfMemory;
4872 heap_free(image->palette);
4873 image->palette = new_palette;
4874 image->palette->Flags = palette->Flags;
4875 image->palette->Count = palette->Count;
4876 memcpy(image->palette->Entries, palette->Entries, sizeof(ARGB)*palette->Count);
4878 return Ok;
4881 /*************************************************************************
4882 * Encoders -
4883 * Structures that represent which formats we support for encoding.
4886 /* ImageCodecInfo creation routines taken from libgdiplus */
4887 static const WCHAR bmp_codecname[] = L"Built-in BMP";
4888 static const WCHAR bmp_extension[] = L"*.BMP;*.DIB;*.RLE";
4889 static const WCHAR bmp_mimetype[] = L"image/bmp";
4890 static const WCHAR bmp_format[] = L"BMP";
4891 static const BYTE bmp_sig_pattern[] = { 0x42, 0x4D };
4892 static const BYTE bmp_sig_mask[] = { 0xFF, 0xFF };
4894 static const WCHAR jpeg_codecname[] = L"Built-in JPEG";
4895 static const WCHAR jpeg_extension[] = L"*.JPG;*.JPEG;*.JPE;*.JFIF";
4896 static const WCHAR jpeg_mimetype[] = L"image/jpeg";
4897 static const WCHAR jpeg_format[] = L"JPEG";
4898 static const BYTE jpeg_sig_pattern[] = { 0xFF, 0xD8 };
4899 static const BYTE jpeg_sig_mask[] = { 0xFF, 0xFF };
4901 static const WCHAR gif_codecname[] = L"Built-in GIF";
4902 static const WCHAR gif_extension[] = L"*.GIF";
4903 static const WCHAR gif_mimetype[] = L"image/gif";
4904 static const WCHAR gif_format[] = L"GIF";
4905 static const BYTE gif_sig_pattern[12] = "GIF87aGIF89a";
4906 static const BYTE gif_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
4908 static const WCHAR tiff_codecname[] = L"Built-in TIFF";
4909 static const WCHAR tiff_extension[] = L"*.TIFF;*.TIF";
4910 static const WCHAR tiff_mimetype[] = L"image/tiff";
4911 static const WCHAR tiff_format[] = L"TIFF";
4912 static const BYTE tiff_sig_pattern[] = {0x49,0x49,42,0,0x4d,0x4d,0,42};
4913 static const BYTE tiff_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
4915 static const WCHAR emf_codecname[] = L"Built-in EMF";
4916 static const WCHAR emf_extension[] = L"*.EMF";
4917 static const WCHAR emf_mimetype[] = L"image/x-emf";
4918 static const WCHAR emf_format[] = L"EMF";
4919 static const BYTE emf_sig_pattern[] = { 0x01, 0x00, 0x00, 0x00 };
4920 static const BYTE emf_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF };
4922 static const WCHAR wmf_codecname[] = L"Built-in WMF";
4923 static const WCHAR wmf_extension[] = L"*.WMF";
4924 static const WCHAR wmf_mimetype[] = L"image/x-wmf";
4925 static const WCHAR wmf_format[] = L"WMF";
4926 static const BYTE wmf_sig_pattern[] = { 0xd7, 0xcd };
4927 static const BYTE wmf_sig_mask[] = { 0xFF, 0xFF };
4929 static const WCHAR png_codecname[] = L"Built-in PNG";
4930 static const WCHAR png_extension[] = L"*.PNG";
4931 static const WCHAR png_mimetype[] = L"image/png";
4932 static const WCHAR png_format[] = L"PNG";
4933 static const BYTE png_sig_pattern[] = { 137, 80, 78, 71, 13, 10, 26, 10, };
4934 static const BYTE png_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
4936 static const WCHAR ico_codecname[] = L"Built-in ICO";
4937 static const WCHAR ico_extension[] = L"*.ICO";
4938 static const WCHAR ico_mimetype[] = L"image/x-icon";
4939 static const WCHAR ico_format[] = L"ICO";
4940 static const BYTE ico_sig_pattern[] = { 0x00, 0x00, 0x01, 0x00 };
4941 static const BYTE ico_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF };
4943 static const struct image_codec codecs[NUM_CODECS] = {
4945 { /* BMP */
4946 /* Clsid */ { 0x557cf400, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4947 /* FormatID */ { 0xb96b3cabU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4948 /* CodecName */ bmp_codecname,
4949 /* DllName */ NULL,
4950 /* FormatDescription */ bmp_format,
4951 /* FilenameExtension */ bmp_extension,
4952 /* MimeType */ bmp_mimetype,
4953 /* Flags */ ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4954 /* Version */ 1,
4955 /* SigCount */ 1,
4956 /* SigSize */ 2,
4957 /* SigPattern */ bmp_sig_pattern,
4958 /* SigMask */ bmp_sig_mask,
4960 encode_image_BMP,
4961 decode_image_bmp,
4962 select_frame_wic
4965 { /* JPEG */
4966 /* Clsid */ { 0x557cf401, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4967 /* FormatID */ { 0xb96b3caeU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4968 /* CodecName */ jpeg_codecname,
4969 /* DllName */ NULL,
4970 /* FormatDescription */ jpeg_format,
4971 /* FilenameExtension */ jpeg_extension,
4972 /* MimeType */ jpeg_mimetype,
4973 /* Flags */ ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4974 /* Version */ 1,
4975 /* SigCount */ 1,
4976 /* SigSize */ 2,
4977 /* SigPattern */ jpeg_sig_pattern,
4978 /* SigMask */ jpeg_sig_mask,
4980 encode_image_jpeg,
4981 decode_image_jpeg,
4982 select_frame_wic
4985 { /* GIF */
4986 /* Clsid */ { 0x557cf402, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4987 /* FormatID */ { 0xb96b3cb0U, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4988 /* CodecName */ gif_codecname,
4989 /* DllName */ NULL,
4990 /* FormatDescription */ gif_format,
4991 /* FilenameExtension */ gif_extension,
4992 /* MimeType */ gif_mimetype,
4993 /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsEncoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4994 /* Version */ 1,
4995 /* SigCount */ 2,
4996 /* SigSize */ 6,
4997 /* SigPattern */ gif_sig_pattern,
4998 /* SigMask */ gif_sig_mask,
5000 encode_image_gif,
5001 decode_image_gif,
5002 select_frame_gif
5005 { /* TIFF */
5006 /* Clsid */ { 0x557cf405, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
5007 /* FormatID */ { 0xb96b3cb1U, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
5008 /* CodecName */ tiff_codecname,
5009 /* DllName */ NULL,
5010 /* FormatDescription */ tiff_format,
5011 /* FilenameExtension */ tiff_extension,
5012 /* MimeType */ tiff_mimetype,
5013 /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsEncoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
5014 /* Version */ 1,
5015 /* SigCount */ 2,
5016 /* SigSize */ 4,
5017 /* SigPattern */ tiff_sig_pattern,
5018 /* SigMask */ tiff_sig_mask,
5020 encode_image_tiff,
5021 decode_image_tiff,
5022 select_frame_wic
5025 { /* EMF */
5026 /* Clsid */ { 0x557cf403, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
5027 /* FormatID */ { 0xb96b3cacU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
5028 /* CodecName */ emf_codecname,
5029 /* DllName */ NULL,
5030 /* FormatDescription */ emf_format,
5031 /* FilenameExtension */ emf_extension,
5032 /* MimeType */ emf_mimetype,
5033 /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsSupportVector | ImageCodecFlagsBuiltin,
5034 /* Version */ 1,
5035 /* SigCount */ 1,
5036 /* SigSize */ 4,
5037 /* SigPattern */ emf_sig_pattern,
5038 /* SigMask */ emf_sig_mask,
5040 NULL,
5041 decode_image_emf,
5042 NULL
5045 { /* WMF */
5046 /* Clsid */ { 0x557cf404, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
5047 /* FormatID */ { 0xb96b3cadU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
5048 /* CodecName */ wmf_codecname,
5049 /* DllName */ NULL,
5050 /* FormatDescription */ wmf_format,
5051 /* FilenameExtension */ wmf_extension,
5052 /* MimeType */ wmf_mimetype,
5053 /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsSupportVector | ImageCodecFlagsBuiltin,
5054 /* Version */ 1,
5055 /* SigCount */ 1,
5056 /* SigSize */ 2,
5057 /* SigPattern */ wmf_sig_pattern,
5058 /* SigMask */ wmf_sig_mask,
5060 NULL,
5061 decode_image_wmf,
5062 NULL
5065 { /* PNG */
5066 /* Clsid */ { 0x557cf406, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
5067 /* FormatID */ { 0xb96b3cafU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
5068 /* CodecName */ png_codecname,
5069 /* DllName */ NULL,
5070 /* FormatDescription */ png_format,
5071 /* FilenameExtension */ png_extension,
5072 /* MimeType */ png_mimetype,
5073 /* Flags */ ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
5074 /* Version */ 1,
5075 /* SigCount */ 1,
5076 /* SigSize */ 8,
5077 /* SigPattern */ png_sig_pattern,
5078 /* SigMask */ png_sig_mask,
5080 encode_image_png,
5081 decode_image_png,
5082 select_frame_wic
5085 { /* ICO */
5086 /* Clsid */ { 0x557cf407, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
5087 /* FormatID */ { 0xb96b3cabU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
5088 /* CodecName */ ico_codecname,
5089 /* DllName */ NULL,
5090 /* FormatDescription */ ico_format,
5091 /* FilenameExtension */ ico_extension,
5092 /* MimeType */ ico_mimetype,
5093 /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
5094 /* Version */ 1,
5095 /* SigCount */ 1,
5096 /* SigSize */ 4,
5097 /* SigPattern */ ico_sig_pattern,
5098 /* SigMask */ ico_sig_mask,
5100 NULL,
5101 decode_image_icon,
5102 select_frame_wic
5106 /*****************************************************************************
5107 * GdipGetImageDecodersSize [GDIPLUS.@]
5109 GpStatus WINGDIPAPI GdipGetImageDecodersSize(UINT *numDecoders, UINT *size)
5111 int decoder_count=0;
5112 int i;
5113 TRACE("%p %p\n", numDecoders, size);
5115 if (!numDecoders || !size)
5116 return InvalidParameter;
5118 for (i=0; i<NUM_CODECS; i++)
5120 if (codecs[i].info.Flags & ImageCodecFlagsDecoder)
5121 decoder_count++;
5124 *numDecoders = decoder_count;
5125 *size = decoder_count * sizeof(ImageCodecInfo);
5127 return Ok;
5130 /*****************************************************************************
5131 * GdipGetImageDecoders [GDIPLUS.@]
5133 GpStatus WINGDIPAPI GdipGetImageDecoders(UINT numDecoders, UINT size, ImageCodecInfo *decoders)
5135 int i, decoder_count=0;
5136 TRACE("%u %u %p\n", numDecoders, size, decoders);
5138 if (!decoders ||
5139 size != numDecoders * sizeof(ImageCodecInfo))
5140 return GenericError;
5142 for (i=0; i<NUM_CODECS; i++)
5144 if (codecs[i].info.Flags & ImageCodecFlagsDecoder)
5146 if (decoder_count == numDecoders) return GenericError;
5147 memcpy(&decoders[decoder_count], &codecs[i].info, sizeof(ImageCodecInfo));
5148 decoder_count++;
5152 if (decoder_count < numDecoders) return GenericError;
5154 return Ok;
5157 /*****************************************************************************
5158 * GdipGetImageEncodersSize [GDIPLUS.@]
5160 GpStatus WINGDIPAPI GdipGetImageEncodersSize(UINT *numEncoders, UINT *size)
5162 int encoder_count=0;
5163 int i;
5164 TRACE("%p %p\n", numEncoders, size);
5166 if (!numEncoders || !size)
5167 return InvalidParameter;
5169 for (i=0; i<NUM_CODECS; i++)
5171 if (codecs[i].info.Flags & ImageCodecFlagsEncoder)
5172 encoder_count++;
5175 *numEncoders = encoder_count;
5176 *size = encoder_count * sizeof(ImageCodecInfo);
5178 return Ok;
5181 /*****************************************************************************
5182 * GdipGetImageEncoders [GDIPLUS.@]
5184 GpStatus WINGDIPAPI GdipGetImageEncoders(UINT numEncoders, UINT size, ImageCodecInfo *encoders)
5186 int i, encoder_count=0;
5187 TRACE("%u %u %p\n", numEncoders, size, encoders);
5189 if (!encoders ||
5190 size != numEncoders * sizeof(ImageCodecInfo))
5191 return GenericError;
5193 for (i=0; i<NUM_CODECS; i++)
5195 if (codecs[i].info.Flags & ImageCodecFlagsEncoder)
5197 if (encoder_count == numEncoders) return GenericError;
5198 memcpy(&encoders[encoder_count], &codecs[i].info, sizeof(ImageCodecInfo));
5199 encoder_count++;
5203 if (encoder_count < numEncoders) return GenericError;
5205 return Ok;
5208 GpStatus WINGDIPAPI GdipGetEncoderParameterListSize(GpImage *image,
5209 GDIPCONST CLSID* clsidEncoder, UINT *size)
5211 static int calls;
5213 TRACE("(%p,%s,%p)\n", image, debugstr_guid(clsidEncoder), size);
5215 if(!(calls++))
5216 FIXME("not implemented\n");
5218 *size = 0;
5220 return NotImplemented;
5223 static PixelFormat get_16bpp_format(HBITMAP hbm)
5225 BITMAPV4HEADER bmh;
5226 HDC hdc;
5227 PixelFormat result;
5229 hdc = CreateCompatibleDC(NULL);
5231 memset(&bmh, 0, sizeof(bmh));
5232 bmh.bV4Size = sizeof(bmh);
5233 bmh.bV4Width = 1;
5234 bmh.bV4Height = 1;
5235 bmh.bV4V4Compression = BI_BITFIELDS;
5236 bmh.bV4BitCount = 16;
5238 GetDIBits(hdc, hbm, 0, 0, NULL, (BITMAPINFO*)&bmh, DIB_RGB_COLORS);
5240 if (bmh.bV4RedMask == 0x7c00 &&
5241 bmh.bV4GreenMask == 0x3e0 &&
5242 bmh.bV4BlueMask == 0x1f)
5244 result = PixelFormat16bppRGB555;
5246 else if (bmh.bV4RedMask == 0xf800 &&
5247 bmh.bV4GreenMask == 0x7e0 &&
5248 bmh.bV4BlueMask == 0x1f)
5250 result = PixelFormat16bppRGB565;
5252 else
5254 FIXME("unrecognized bitfields %lx,%lx,%lx\n", bmh.bV4RedMask,
5255 bmh.bV4GreenMask, bmh.bV4BlueMask);
5256 result = PixelFormatUndefined;
5259 DeleteDC(hdc);
5261 return result;
5264 /*****************************************************************************
5265 * GdipCreateBitmapFromHBITMAP [GDIPLUS.@]
5267 GpStatus WINGDIPAPI GdipCreateBitmapFromHBITMAP(HBITMAP hbm, HPALETTE hpal, GpBitmap** bitmap)
5269 BITMAP bm;
5270 GpStatus retval;
5271 PixelFormat format;
5272 BitmapData lockeddata;
5273 char bmibuf[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
5274 BITMAPINFO *pbmi = (BITMAPINFO *)bmibuf;
5276 TRACE("%p %p %p\n", hbm, hpal, bitmap);
5278 if(!hbm || !bitmap)
5279 return InvalidParameter;
5281 if (GetObjectA(hbm, sizeof(bm), &bm) != sizeof(bm))
5282 return InvalidParameter;
5284 /* TODO: Figure out the correct format for 16, 32, 64 bpp */
5285 switch(bm.bmBitsPixel) {
5286 case 1:
5287 format = PixelFormat1bppIndexed;
5288 break;
5289 case 4:
5290 format = PixelFormat4bppIndexed;
5291 break;
5292 case 8:
5293 format = PixelFormat8bppIndexed;
5294 break;
5295 case 16:
5296 format = get_16bpp_format(hbm);
5297 if (format == PixelFormatUndefined)
5298 return InvalidParameter;
5299 break;
5300 case 24:
5301 format = PixelFormat24bppRGB;
5302 break;
5303 case 32:
5304 format = PixelFormat32bppRGB;
5305 break;
5306 case 48:
5307 format = PixelFormat48bppRGB;
5308 break;
5309 default:
5310 FIXME("don't know how to handle %d bpp\n", bm.bmBitsPixel);
5311 return InvalidParameter;
5314 retval = GdipCreateBitmapFromScan0(bm.bmWidth, bm.bmHeight, 0,
5315 format, NULL, bitmap);
5317 if (retval == Ok)
5319 retval = GdipBitmapLockBits(*bitmap, NULL, ImageLockModeWrite,
5320 format, &lockeddata);
5321 if (retval == Ok)
5323 HDC hdc;
5324 INT src_height;
5326 hdc = CreateCompatibleDC(NULL);
5328 pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
5329 pbmi->bmiHeader.biBitCount = 0;
5331 GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);
5333 src_height = abs(pbmi->bmiHeader.biHeight);
5334 pbmi->bmiHeader.biHeight = -src_height;
5336 GetDIBits(hdc, hbm, 0, src_height, lockeddata.Scan0, pbmi, DIB_RGB_COLORS);
5338 DeleteDC(hdc);
5340 GdipBitmapUnlockBits(*bitmap, &lockeddata);
5343 /* According to the tests hpal is ignored */
5344 if (retval == Ok && pbmi->bmiHeader.biBitCount <= 8)
5346 ColorPalette *palette;
5347 int i, num_palette_entries;
5349 num_palette_entries = pbmi->bmiHeader.biClrUsed;
5350 if (!num_palette_entries)
5351 num_palette_entries = 1 << pbmi->bmiHeader.biBitCount;
5353 palette = heap_alloc_zero(sizeof(ColorPalette) + sizeof(ARGB) * (num_palette_entries-1));
5354 if (!palette)
5355 retval = OutOfMemory;
5356 else
5358 palette->Flags = 0;
5359 palette->Count = num_palette_entries;
5361 for (i=0; i<num_palette_entries; i++)
5363 palette->Entries[i] = 0xff000000 | pbmi->bmiColors[i].rgbRed << 16 |
5364 pbmi->bmiColors[i].rgbGreen << 8 | pbmi->bmiColors[i].rgbBlue;
5367 retval = GdipSetImagePalette(&(*bitmap)->image, palette);
5370 heap_free(palette);
5373 if (retval != Ok)
5375 GdipDisposeImage(&(*bitmap)->image);
5376 *bitmap = NULL;
5380 return retval;
5383 /*****************************************************************************
5384 * GdipCreateEffect [GDIPLUS.@]
5386 GpStatus WINGDIPAPI GdipCreateEffect(const GUID guid, CGpEffect **effect)
5388 FIXME("(%s, %p): stub\n", debugstr_guid(&guid), effect);
5390 if(!effect)
5391 return InvalidParameter;
5393 *effect = NULL;
5395 return NotImplemented;
5398 /*****************************************************************************
5399 * GdipDeleteEffect [GDIPLUS.@]
5401 GpStatus WINGDIPAPI GdipDeleteEffect(CGpEffect *effect)
5403 FIXME("(%p): stub\n", effect);
5404 /* note: According to Jose Roca's GDI+ Docs, this is not implemented
5405 * in Windows's gdiplus */
5406 return NotImplemented;
5409 /*****************************************************************************
5410 * GdipSetEffectParameters [GDIPLUS.@]
5412 GpStatus WINGDIPAPI GdipSetEffectParameters(CGpEffect *effect,
5413 const VOID *params, const UINT size)
5415 static int calls;
5417 TRACE("(%p,%p,%u)\n", effect, params, size);
5419 if(!(calls++))
5420 FIXME("not implemented\n");
5422 return NotImplemented;
5425 /*****************************************************************************
5426 * GdipGetImageFlags [GDIPLUS.@]
5428 GpStatus WINGDIPAPI GdipGetImageFlags(GpImage *image, UINT *flags)
5430 TRACE("%p %p\n", image, flags);
5432 if(!image || !flags)
5433 return InvalidParameter;
5435 *flags = image->flags;
5437 return Ok;
5440 GpStatus WINGDIPAPI GdipTestControl(GpTestControlEnum control, void *param)
5442 TRACE("(%d, %p)\n", control, param);
5444 switch(control){
5445 case TestControlForceBilinear:
5446 if(param)
5447 FIXME("TestControlForceBilinear not handled\n");
5448 break;
5449 case TestControlNoICM:
5450 if(param)
5451 FIXME("TestControlNoICM not handled\n");
5452 break;
5453 case TestControlGetBuildNumber:
5454 *((DWORD*)param) = 3102;
5455 break;
5458 return Ok;
5461 GpStatus WINGDIPAPI GdipImageForceValidation(GpImage *image)
5463 TRACE("%p\n", image);
5465 return Ok;
5468 /*****************************************************************************
5469 * GdipGetImageThumbnail [GDIPLUS.@]
5471 GpStatus WINGDIPAPI GdipGetImageThumbnail(GpImage *image, UINT width, UINT height,
5472 GpImage **ret_image, GetThumbnailImageAbort cb,
5473 VOID * cb_data)
5475 GpStatus stat;
5476 GpGraphics *graphics;
5477 UINT srcwidth, srcheight;
5479 TRACE("(%p %u %u %p %p %p)\n",
5480 image, width, height, ret_image, cb, cb_data);
5482 if (!image || !ret_image)
5483 return InvalidParameter;
5485 if (!width) width = 120;
5486 if (!height) height = 120;
5488 GdipGetImageWidth(image, &srcwidth);
5489 GdipGetImageHeight(image, &srcheight);
5491 stat = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppPARGB,
5492 NULL, (GpBitmap**)ret_image);
5494 if (stat == Ok)
5496 stat = GdipGetImageGraphicsContext(*ret_image, &graphics);
5498 if (stat == Ok)
5500 stat = GdipDrawImageRectRectI(graphics, image,
5501 0, 0, width, height, 0, 0, srcwidth, srcheight, UnitPixel,
5502 NULL, NULL, NULL);
5504 GdipDeleteGraphics(graphics);
5507 if (stat != Ok)
5509 GdipDisposeImage(*ret_image);
5510 *ret_image = NULL;
5514 return stat;
5517 /*****************************************************************************
5518 * GdipImageRotateFlip [GDIPLUS.@]
5520 GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type)
5522 GpBitmap *new_bitmap;
5523 GpBitmap *bitmap;
5524 int bpp, bytesperpixel;
5525 BOOL rotate_90, flip_x, flip_y;
5526 int src_x_offset, src_y_offset;
5527 LPBYTE src_origin;
5528 UINT x, y, width, height;
5529 BitmapData dst_lock;
5530 GpStatus stat;
5532 TRACE("(%p, %u)\n", image, type);
5534 if (!image)
5535 return InvalidParameter;
5536 if (!image_lock(image))
5537 return ObjectBusy;
5539 rotate_90 = type&1;
5540 flip_x = (type&6) == 2 || (type&6) == 4;
5541 flip_y = (type&3) == 1 || (type&3) == 2;
5543 if (image->type != ImageTypeBitmap)
5545 FIXME("Not implemented for type %i\n", image->type);
5546 image_unlock(image);
5547 return NotImplemented;
5550 bitmap = (GpBitmap*)image;
5551 bpp = PIXELFORMATBPP(bitmap->format);
5553 if (bpp < 8)
5555 FIXME("Not implemented for %i bit images\n", bpp);
5556 image_unlock(image);
5557 return NotImplemented;
5560 if (rotate_90)
5562 width = bitmap->height;
5563 height = bitmap->width;
5565 else
5567 width = bitmap->width;
5568 height = bitmap->height;
5571 bytesperpixel = bpp/8;
5573 stat = GdipCreateBitmapFromScan0(width, height, 0, bitmap->format, NULL, &new_bitmap);
5575 if (stat == Ok)
5577 stat = GdipBitmapLockBits(new_bitmap, NULL, ImageLockModeWrite, bitmap->format, &dst_lock);
5579 if (stat == Ok)
5581 LPBYTE src_row, src_pixel;
5582 LPBYTE dst_row, dst_pixel;
5584 src_origin = bitmap->bits;
5585 if (flip_x) src_origin += bytesperpixel * (bitmap->width - 1);
5586 if (flip_y) src_origin += bitmap->stride * (bitmap->height - 1);
5588 if (rotate_90)
5590 if (flip_y) src_x_offset = -bitmap->stride;
5591 else src_x_offset = bitmap->stride;
5592 if (flip_x) src_y_offset = -bytesperpixel;
5593 else src_y_offset = bytesperpixel;
5595 else
5597 if (flip_x) src_x_offset = -bytesperpixel;
5598 else src_x_offset = bytesperpixel;
5599 if (flip_y) src_y_offset = -bitmap->stride;
5600 else src_y_offset = bitmap->stride;
5603 src_row = src_origin;
5604 dst_row = dst_lock.Scan0;
5605 for (y=0; y<height; y++)
5607 src_pixel = src_row;
5608 dst_pixel = dst_row;
5609 for (x=0; x<width; x++)
5611 /* FIXME: This could probably be faster without memcpy. */
5612 memcpy(dst_pixel, src_pixel, bytesperpixel);
5613 dst_pixel += bytesperpixel;
5614 src_pixel += src_x_offset;
5616 src_row += src_y_offset;
5617 dst_row += dst_lock.Stride;
5620 GdipBitmapUnlockBits(new_bitmap, &dst_lock);
5621 move_bitmap(bitmap, new_bitmap, FALSE);
5623 else GdipDisposeImage(&new_bitmap->image);
5626 image_unlock(image);
5627 return stat;
5630 /*****************************************************************************
5631 * GdipImageSetAbort [GDIPLUS.@]
5633 GpStatus WINGDIPAPI GdipImageSetAbort(GpImage *image, GdiplusAbort *pabort)
5635 TRACE("(%p, %p)\n", image, pabort);
5637 if (!image)
5638 return InvalidParameter;
5640 if (pabort)
5641 FIXME("Abort callback is not supported.\n");
5643 return Ok;
5646 /*****************************************************************************
5647 * GdipBitmapConvertFormat [GDIPLUS.@]
5649 GpStatus WINGDIPAPI GdipBitmapConvertFormat(GpBitmap *bitmap, PixelFormat format, DitherType dithertype,
5650 PaletteType palettetype, ColorPalette *palette, REAL alphathreshold)
5652 FIXME("(%p, 0x%08x, %d, %d, %p, %f): stub\n", bitmap, format, dithertype, palettetype, palette, alphathreshold);
5653 return NotImplemented;
5656 static void set_histogram_point_argb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5658 ch0[ color >> 24 ]++;
5659 ch1[(color >> 16) & 0xff]++;
5660 ch2[(color >> 8) & 0xff]++;
5661 ch3[ color & 0xff]++;
5664 static void set_histogram_point_pargb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5666 BYTE alpha = color >> 24;
5668 ch0[alpha]++;
5669 ch1[(((color >> 16) & 0xff) * alpha) / 0xff]++;
5670 ch2[(((color >> 8) & 0xff) * alpha) / 0xff]++;
5671 ch3[(( color & 0xff) * alpha) / 0xff]++;
5674 static void set_histogram_point_rgb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5676 ch0[(color >> 16) & 0xff]++;
5677 ch1[(color >> 8) & 0xff]++;
5678 ch2[ color & 0xff]++;
5681 static void set_histogram_point_gray(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5683 ch0[(76 * ((color >> 16) & 0xff) + 150 * ((color >> 8) & 0xff) + 29 * (color & 0xff)) / 0xff]++;
5686 static void set_histogram_point_b(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5688 ch0[color & 0xff]++;
5691 static void set_histogram_point_g(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5693 ch0[(color >> 8) & 0xff]++;
5696 static void set_histogram_point_r(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5698 ch0[(color >> 16) & 0xff]++;
5701 static void set_histogram_point_a(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5703 ch0[(color >> 24) & 0xff]++;
5706 /*****************************************************************************
5707 * GdipBitmapGetHistogram [GDIPLUS.@]
5709 GpStatus WINGDIPAPI GdipBitmapGetHistogram(GpBitmap *bitmap, HistogramFormat format, UINT num_of_entries,
5710 UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
5712 static void (* const set_histogram_point[])(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) =
5714 set_histogram_point_argb,
5715 set_histogram_point_pargb,
5716 set_histogram_point_rgb,
5717 set_histogram_point_gray,
5718 set_histogram_point_b,
5719 set_histogram_point_g,
5720 set_histogram_point_r,
5721 set_histogram_point_a,
5723 UINT width, height, x, y;
5725 TRACE("(%p, %d, %u, %p, %p, %p, %p)\n", bitmap, format, num_of_entries,
5726 ch0, ch1, ch2, ch3);
5728 if (!bitmap || num_of_entries != 256)
5729 return InvalidParameter;
5731 /* Make sure passed channel pointers match requested format */
5732 switch (format)
5734 case HistogramFormatARGB:
5735 case HistogramFormatPARGB:
5736 if (!ch0 || !ch1 || !ch2 || !ch3)
5737 return InvalidParameter;
5738 memset(ch0, 0, num_of_entries * sizeof(UINT));
5739 memset(ch1, 0, num_of_entries * sizeof(UINT));
5740 memset(ch2, 0, num_of_entries * sizeof(UINT));
5741 memset(ch3, 0, num_of_entries * sizeof(UINT));
5742 break;
5743 case HistogramFormatRGB:
5744 if (!ch0 || !ch1 || !ch2 || ch3)
5745 return InvalidParameter;
5746 memset(ch0, 0, num_of_entries * sizeof(UINT));
5747 memset(ch1, 0, num_of_entries * sizeof(UINT));
5748 memset(ch2, 0, num_of_entries * sizeof(UINT));
5749 break;
5750 case HistogramFormatGray:
5751 case HistogramFormatB:
5752 case HistogramFormatG:
5753 case HistogramFormatR:
5754 case HistogramFormatA:
5755 if (!ch0 || ch1 || ch2 || ch3)
5756 return InvalidParameter;
5757 memset(ch0, 0, num_of_entries * sizeof(UINT));
5758 break;
5759 default:
5760 WARN("Invalid histogram format requested, %d\n", format);
5761 return InvalidParameter;
5764 GdipGetImageWidth(&bitmap->image, &width);
5765 GdipGetImageHeight(&bitmap->image, &height);
5767 for (y = 0; y < height; y++)
5768 for (x = 0; x < width; x++)
5770 ARGB color;
5772 GdipBitmapGetPixel(bitmap, x, y, &color);
5773 set_histogram_point[format](color, ch0, ch1, ch2, ch3);
5776 return Ok;
5779 /*****************************************************************************
5780 * GdipBitmapGetHistogramSize [GDIPLUS.@]
5782 GpStatus WINGDIPAPI GdipBitmapGetHistogramSize(HistogramFormat format, UINT *num_of_entries)
5784 TRACE("(%d, %p)\n", format, num_of_entries);
5786 if (!num_of_entries)
5787 return InvalidParameter;
5789 *num_of_entries = 256;
5790 return Ok;
5793 static GpStatus create_optimal_palette(ColorPalette *palette, INT desired,
5794 BOOL transparent, GpBitmap *bitmap)
5796 GpStatus status;
5797 BitmapData data;
5798 HRESULT hr;
5799 IWICImagingFactory *factory;
5800 IWICPalette *wic_palette;
5802 if (!bitmap) return InvalidParameter;
5803 if (palette->Count < desired) return GenericError;
5805 status = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, PixelFormat24bppRGB, &data);
5806 if (status != Ok) return status;
5808 hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory);
5809 if (hr != S_OK)
5811 GdipBitmapUnlockBits(bitmap, &data);
5812 return hresult_to_status(hr);
5815 hr = IWICImagingFactory_CreatePalette(factory, &wic_palette);
5816 if (hr == S_OK)
5818 IWICBitmap *bitmap;
5820 /* PixelFormat24bppRGB actually stores the bitmap bits as BGR. */
5821 hr = IWICImagingFactory_CreateBitmapFromMemory(factory, data.Width, data.Height,
5822 &GUID_WICPixelFormat24bppBGR, data.Stride, data.Stride * data.Width, data.Scan0, &bitmap);
5823 if (hr == S_OK)
5825 hr = IWICPalette_InitializeFromBitmap(wic_palette, (IWICBitmapSource *)bitmap, desired, transparent);
5826 if (hr == S_OK)
5828 palette->Flags = 0;
5829 IWICPalette_GetColorCount(wic_palette, &palette->Count);
5830 IWICPalette_GetColors(wic_palette, palette->Count, (UINT *)palette->Entries, &palette->Count);
5833 IWICBitmap_Release(bitmap);
5836 IWICPalette_Release(wic_palette);
5839 IWICImagingFactory_Release(factory);
5840 GdipBitmapUnlockBits(bitmap, &data);
5842 return hresult_to_status(hr);
5845 /*****************************************************************************
5846 * GdipInitializePalette [GDIPLUS.@]
5848 GpStatus WINGDIPAPI GdipInitializePalette(ColorPalette *palette,
5849 PaletteType type, INT desired, BOOL transparent, GpBitmap *bitmap)
5851 TRACE("(%p,%d,%d,%d,%p)\n", palette, type, desired, transparent, bitmap);
5853 if (!palette) return InvalidParameter;
5855 switch (type)
5857 case PaletteTypeCustom:
5858 return Ok;
5860 case PaletteTypeOptimal:
5861 return create_optimal_palette(palette, desired, transparent, bitmap);
5863 /* WIC palette type enumeration matches these gdiplus enums */
5864 case PaletteTypeFixedBW:
5865 case PaletteTypeFixedHalftone8:
5866 case PaletteTypeFixedHalftone27:
5867 case PaletteTypeFixedHalftone64:
5868 case PaletteTypeFixedHalftone125:
5869 case PaletteTypeFixedHalftone216:
5870 case PaletteTypeFixedHalftone252:
5871 case PaletteTypeFixedHalftone256:
5873 ColorPalette *wic_palette;
5874 GpStatus status = Ok;
5876 wic_palette = get_palette(NULL, type);
5877 if (!wic_palette) return OutOfMemory;
5879 if (palette->Count >= wic_palette->Count)
5881 palette->Flags = wic_palette->Flags;
5882 palette->Count = wic_palette->Count;
5883 memcpy(palette->Entries, wic_palette->Entries, wic_palette->Count * sizeof(wic_palette->Entries[0]));
5885 else
5886 status = GenericError;
5888 heap_free(wic_palette);
5890 return status;
5893 default:
5894 FIXME("unknown palette type %d\n", type);
5895 break;
5898 return InvalidParameter;