[GDIPLUS]
[reactos.git] / reactos / dll / win32 / gdiplus / image.c
1 /*
2 * Copyright (C) 2007 Google (Evan Stade)
3 * Copyright (C) 2012 Dmitry Timoshkov
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19
20 #include "gdiplus_private.h"
21
22 #include <assert.h>
23 #include <olectl.h>
24
25 #define PIXELFORMATBPP(x) ((x) ? ((x) >> 8) & 255 : 24)
26
27 static const struct
28 {
29 const WICPixelFormatGUID *wic_format;
30 PixelFormat gdip_format;
31 /* predefined palette type to use for pixel format conversions */
32 WICBitmapPaletteType palette_type;
33 } pixel_formats[] =
34 {
35 { &GUID_WICPixelFormatBlackWhite, PixelFormat1bppIndexed, WICBitmapPaletteTypeFixedBW },
36 { &GUID_WICPixelFormat1bppIndexed, PixelFormat1bppIndexed, WICBitmapPaletteTypeFixedBW },
37 { &GUID_WICPixelFormat8bppGray, PixelFormat8bppIndexed, WICBitmapPaletteTypeFixedGray256 },
38 { &GUID_WICPixelFormat8bppIndexed, PixelFormat8bppIndexed, WICBitmapPaletteTypeFixedHalftone256 },
39 { &GUID_WICPixelFormat16bppBGR555, PixelFormat16bppRGB555, WICBitmapPaletteTypeFixedHalftone256 },
40 { &GUID_WICPixelFormat24bppBGR, PixelFormat24bppRGB, WICBitmapPaletteTypeFixedHalftone256 },
41 { &GUID_WICPixelFormat32bppBGR, PixelFormat32bppRGB, WICBitmapPaletteTypeFixedHalftone256 },
42 { &GUID_WICPixelFormat32bppBGRA, PixelFormat32bppARGB, WICBitmapPaletteTypeFixedHalftone256 },
43 { &GUID_WICPixelFormat32bppPBGRA, PixelFormat32bppPARGB, WICBitmapPaletteTypeFixedHalftone256 },
44 { NULL }
45 };
46
47 static ColorPalette *get_palette(IWICBitmapFrameDecode *frame, WICBitmapPaletteType palette_type)
48 {
49 HRESULT hr;
50 IWICImagingFactory *factory;
51 IWICPalette *wic_palette;
52 ColorPalette *palette = NULL;
53
54 hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
55 &IID_IWICImagingFactory, (void **)&factory);
56 if (hr != S_OK) return NULL;
57
58 hr = IWICImagingFactory_CreatePalette(factory, &wic_palette);
59 if (hr == S_OK)
60 {
61 hr = WINCODEC_ERR_PALETTEUNAVAILABLE;
62 if (frame)
63 hr = IWICBitmapFrameDecode_CopyPalette(frame, wic_palette);
64 if (hr != S_OK)
65 {
66 TRACE("using predefined palette %#x\n", palette_type);
67 hr = IWICPalette_InitializePredefined(wic_palette, palette_type, FALSE);
68 }
69 if (hr == S_OK)
70 {
71 UINT count;
72 BOOL mono, gray;
73
74 IWICPalette_IsBlackWhite(wic_palette, &mono);
75 IWICPalette_IsGrayscale(wic_palette, &gray);
76
77 IWICPalette_GetColorCount(wic_palette, &count);
78 palette = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(UINT) + count * sizeof(ARGB));
79 IWICPalette_GetColors(wic_palette, count, palette->Entries, &palette->Count);
80
81 if (mono)
82 palette->Flags = 0;
83 else if (gray)
84 palette->Flags = PaletteFlagsGrayScale;
85 else
86 palette->Flags = PaletteFlagsHalftone;
87 }
88 IWICPalette_Release(wic_palette);
89 }
90 IWICImagingFactory_Release(factory);
91 return palette;
92 }
93
94 static INT ipicture_pixel_height(IPicture *pic)
95 {
96 HDC hdcref;
97 OLE_YSIZE_HIMETRIC y;
98
99 IPicture_get_Height(pic, &y);
100
101 hdcref = CreateCompatibleDC(0);
102 y = MulDiv(y, GetDeviceCaps(hdcref, LOGPIXELSY), INCH_HIMETRIC);
103 DeleteDC(hdcref);
104
105 return y;
106 }
107
108 static INT ipicture_pixel_width(IPicture *pic)
109 {
110 HDC hdcref;
111 OLE_XSIZE_HIMETRIC x;
112
113 IPicture_get_Width(pic, &x);
114
115 hdcref = CreateCompatibleDC(0);
116 x = MulDiv(x, GetDeviceCaps(hdcref, LOGPIXELSX), INCH_HIMETRIC);
117 DeleteDC(hdcref);
118
119 return x;
120 }
121
122 GpStatus WINGDIPAPI GdipBitmapApplyEffect(GpBitmap* bitmap, CGpEffect* effect,
123 RECT* roi, BOOL useAuxData, VOID** auxData, INT* auxDataSize)
124 {
125 FIXME("(%p %p %p %d %p %p): stub\n", bitmap, effect, roi, useAuxData, auxData, auxDataSize);
126 /*
127 * Note: According to Jose Roca's GDI+ docs, this function is not
128 * implemented in Windows's GDI+.
129 */
130 return NotImplemented;
131 }
132
133 GpStatus WINGDIPAPI GdipBitmapCreateApplyEffect(GpBitmap** inputBitmaps,
134 INT numInputs, CGpEffect* effect, RECT* roi, RECT* outputRect,
135 GpBitmap** outputBitmap, BOOL useAuxData, VOID** auxData, INT* auxDataSize)
136 {
137 FIXME("(%p %d %p %p %p %p %d %p %p): stub\n", inputBitmaps, numInputs, effect, roi, outputRect, outputBitmap, useAuxData, auxData, auxDataSize);
138 /*
139 * Note: According to Jose Roca's GDI+ docs, this function is not
140 * implemented in Windows's GDI+.
141 */
142 return NotImplemented;
143 }
144
145 static inline void getpixel_1bppIndexed(BYTE *index, const BYTE *row, UINT x)
146 {
147 *index = (row[x/8]>>(7-x%8)) & 1;
148 }
149
150 static inline void getpixel_4bppIndexed(BYTE *index, const BYTE *row, UINT x)
151 {
152 if (x & 1)
153 *index = row[x/2]&0xf;
154 else
155 *index = row[x/2]>>4;
156 }
157
158 static inline void getpixel_8bppIndexed(BYTE *index, const BYTE *row, UINT x)
159 {
160 *index = row[x];
161 }
162
163 static inline void getpixel_16bppGrayScale(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
164 const BYTE *row, UINT x)
165 {
166 *r = *g = *b = row[x*2+1];
167 *a = 255;
168 }
169
170 static inline void getpixel_16bppRGB555(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
171 const BYTE *row, UINT x)
172 {
173 WORD pixel = *((const WORD*)(row)+x);
174 *r = (pixel>>7&0xf8)|(pixel>>12&0x7);
175 *g = (pixel>>2&0xf8)|(pixel>>6&0x7);
176 *b = (pixel<<3&0xf8)|(pixel>>2&0x7);
177 *a = 255;
178 }
179
180 static inline void getpixel_16bppRGB565(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
181 const BYTE *row, UINT x)
182 {
183 WORD pixel = *((const WORD*)(row)+x);
184 *r = (pixel>>8&0xf8)|(pixel>>13&0x7);
185 *g = (pixel>>3&0xfc)|(pixel>>9&0x3);
186 *b = (pixel<<3&0xf8)|(pixel>>2&0x7);
187 *a = 255;
188 }
189
190 static inline void getpixel_16bppARGB1555(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
191 const BYTE *row, UINT x)
192 {
193 WORD pixel = *((const WORD*)(row)+x);
194 *r = (pixel>>7&0xf8)|(pixel>>12&0x7);
195 *g = (pixel>>2&0xf8)|(pixel>>6&0x7);
196 *b = (pixel<<3&0xf8)|(pixel>>2&0x7);
197 if ((pixel&0x8000) == 0x8000)
198 *a = 255;
199 else
200 *a = 0;
201 }
202
203 static inline void getpixel_24bppRGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
204 const BYTE *row, UINT x)
205 {
206 *r = row[x*3+2];
207 *g = row[x*3+1];
208 *b = row[x*3];
209 *a = 255;
210 }
211
212 static inline void getpixel_32bppRGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
213 const BYTE *row, UINT x)
214 {
215 *r = row[x*4+2];
216 *g = row[x*4+1];
217 *b = row[x*4];
218 *a = 255;
219 }
220
221 static inline void getpixel_32bppARGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
222 const BYTE *row, UINT x)
223 {
224 *r = row[x*4+2];
225 *g = row[x*4+1];
226 *b = row[x*4];
227 *a = row[x*4+3];
228 }
229
230 static inline void getpixel_32bppPARGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
231 const BYTE *row, UINT x)
232 {
233 *a = row[x*4+3];
234 if (*a == 0)
235 *r = *g = *b = 0;
236 else
237 {
238 *r = row[x*4+2] * 255 / *a;
239 *g = row[x*4+1] * 255 / *a;
240 *b = row[x*4] * 255 / *a;
241 }
242 }
243
244 static inline void getpixel_48bppRGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
245 const BYTE *row, UINT x)
246 {
247 *r = row[x*6+5];
248 *g = row[x*6+3];
249 *b = row[x*6+1];
250 *a = 255;
251 }
252
253 static inline void getpixel_64bppARGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
254 const BYTE *row, UINT x)
255 {
256 *r = row[x*8+5];
257 *g = row[x*8+3];
258 *b = row[x*8+1];
259 *a = row[x*8+7];
260 }
261
262 static inline void getpixel_64bppPARGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
263 const BYTE *row, UINT x)
264 {
265 *a = row[x*8+7];
266 if (*a == 0)
267 *r = *g = *b = 0;
268 else
269 {
270 *r = row[x*8+5] * 255 / *a;
271 *g = row[x*8+3] * 255 / *a;
272 *b = row[x*8+1] * 255 / *a;
273 }
274 }
275
276 GpStatus WINGDIPAPI GdipBitmapGetPixel(GpBitmap* bitmap, INT x, INT y,
277 ARGB *color)
278 {
279 BYTE r, g, b, a;
280 BYTE index;
281 BYTE *row;
282
283 if(!bitmap || !color ||
284 x < 0 || y < 0 || x >= bitmap->width || y >= bitmap->height)
285 return InvalidParameter;
286
287 row = bitmap->bits+bitmap->stride*y;
288
289 switch (bitmap->format)
290 {
291 case PixelFormat1bppIndexed:
292 getpixel_1bppIndexed(&index,row,x);
293 break;
294 case PixelFormat4bppIndexed:
295 getpixel_4bppIndexed(&index,row,x);
296 break;
297 case PixelFormat8bppIndexed:
298 getpixel_8bppIndexed(&index,row,x);
299 break;
300 case PixelFormat16bppGrayScale:
301 getpixel_16bppGrayScale(&r,&g,&b,&a,row,x);
302 break;
303 case PixelFormat16bppRGB555:
304 getpixel_16bppRGB555(&r,&g,&b,&a,row,x);
305 break;
306 case PixelFormat16bppRGB565:
307 getpixel_16bppRGB565(&r,&g,&b,&a,row,x);
308 break;
309 case PixelFormat16bppARGB1555:
310 getpixel_16bppARGB1555(&r,&g,&b,&a,row,x);
311 break;
312 case PixelFormat24bppRGB:
313 getpixel_24bppRGB(&r,&g,&b,&a,row,x);
314 break;
315 case PixelFormat32bppRGB:
316 getpixel_32bppRGB(&r,&g,&b,&a,row,x);
317 break;
318 case PixelFormat32bppARGB:
319 getpixel_32bppARGB(&r,&g,&b,&a,row,x);
320 break;
321 case PixelFormat32bppPARGB:
322 getpixel_32bppPARGB(&r,&g,&b,&a,row,x);
323 break;
324 case PixelFormat48bppRGB:
325 getpixel_48bppRGB(&r,&g,&b,&a,row,x);
326 break;
327 case PixelFormat64bppARGB:
328 getpixel_64bppARGB(&r,&g,&b,&a,row,x);
329 break;
330 case PixelFormat64bppPARGB:
331 getpixel_64bppPARGB(&r,&g,&b,&a,row,x);
332 break;
333 default:
334 FIXME("not implemented for format 0x%x\n", bitmap->format);
335 return NotImplemented;
336 }
337
338 if (bitmap->format & PixelFormatIndexed)
339 *color = bitmap->image.palette->Entries[index];
340 else
341 *color = a<<24|r<<16|g<<8|b;
342
343 return Ok;
344 }
345
346 static inline UINT get_palette_index(BYTE r, BYTE g, BYTE b, BYTE a, ColorPalette *palette)
347 {
348 BYTE index = 0;
349 int best_distance = 0x7fff;
350 int distance;
351 UINT i;
352
353 if (!palette) return 0;
354 /* This algorithm scans entire palette,
355 computes difference from desired color (all color components have equal weight)
356 and returns the index of color with least difference.
357
358 Note: Maybe it could be replaced with a better algorithm for better image quality
359 and performance, though better algorithm would probably need some pre-built lookup
360 tables and thus may actually be slower if this method is called only few times per
361 every image.
362 */
363 for(i=0;i<palette->Count;i++) {
364 ARGB color=palette->Entries[i];
365 distance=abs(b-(color & 0xff)) + abs(g-(color>>8 & 0xff)) + abs(r-(color>>16 & 0xff)) + abs(a-(color>>24 & 0xff));
366 if (distance<best_distance) {
367 best_distance=distance;
368 index=i;
369 }
370 }
371 return index;
372 }
373
374 static inline void setpixel_8bppIndexed(BYTE r, BYTE g, BYTE b, BYTE a,
375 BYTE *row, UINT x, ColorPalette *palette)
376 {
377 BYTE index = get_palette_index(r,g,b,a,palette);
378 row[x]=index;
379 }
380
381 static inline void setpixel_1bppIndexed(BYTE r, BYTE g, BYTE b, BYTE a,
382 BYTE *row, UINT x, ColorPalette *palette)
383 {
384 row[x/8] = (row[x/8] & ~(1<<(7-x%8))) | (get_palette_index(r,g,b,a,palette)<<(7-x%8));
385 }
386
387 static inline void setpixel_4bppIndexed(BYTE r, BYTE g, BYTE b, BYTE a,
388 BYTE *row, UINT x, ColorPalette *palette)
389 {
390 if (x & 1)
391 row[x/2] = (row[x/2] & 0xf0) | get_palette_index(r,g,b,a,palette);
392 else
393 row[x/2] = (row[x/2] & 0x0f) | get_palette_index(r,g,b,a,palette)<<4;
394 }
395
396 static inline void setpixel_16bppGrayScale(BYTE r, BYTE g, BYTE b, BYTE a,
397 BYTE *row, UINT x)
398 {
399 *((WORD*)(row)+x) = (r+g+b)*85;
400 }
401
402 static inline void setpixel_16bppRGB555(BYTE r, BYTE g, BYTE b, BYTE a,
403 BYTE *row, UINT x)
404 {
405 *((WORD*)(row)+x) = (r<<7&0x7c00)|
406 (g<<2&0x03e0)|
407 (b>>3&0x001f);
408 }
409
410 static inline void setpixel_16bppRGB565(BYTE r, BYTE g, BYTE b, BYTE a,
411 BYTE *row, UINT x)
412 {
413 *((WORD*)(row)+x) = (r<<8&0xf800)|
414 (g<<3&0x07e0)|
415 (b>>3&0x001f);
416 }
417
418 static inline void setpixel_16bppARGB1555(BYTE r, BYTE g, BYTE b, BYTE a,
419 BYTE *row, UINT x)
420 {
421 *((WORD*)(row)+x) = (a<<8&0x8000)|
422 (r<<7&0x7c00)|
423 (g<<2&0x03e0)|
424 (b>>3&0x001f);
425 }
426
427 static inline void setpixel_24bppRGB(BYTE r, BYTE g, BYTE b, BYTE a,
428 BYTE *row, UINT x)
429 {
430 row[x*3+2] = r;
431 row[x*3+1] = g;
432 row[x*3] = b;
433 }
434
435 static inline void setpixel_32bppRGB(BYTE r, BYTE g, BYTE b, BYTE a,
436 BYTE *row, UINT x)
437 {
438 *((DWORD*)(row)+x) = (r<<16)|(g<<8)|b;
439 }
440
441 static inline void setpixel_32bppARGB(BYTE r, BYTE g, BYTE b, BYTE a,
442 BYTE *row, UINT x)
443 {
444 *((DWORD*)(row)+x) = (a<<24)|(r<<16)|(g<<8)|b;
445 }
446
447 static inline void setpixel_32bppPARGB(BYTE r, BYTE g, BYTE b, BYTE a,
448 BYTE *row, UINT x)
449 {
450 r = r * a / 255;
451 g = g * a / 255;
452 b = b * a / 255;
453 *((DWORD*)(row)+x) = (a<<24)|(r<<16)|(g<<8)|b;
454 }
455
456 static inline void setpixel_48bppRGB(BYTE r, BYTE g, BYTE b, BYTE a,
457 BYTE *row, UINT x)
458 {
459 row[x*6+5] = row[x*6+4] = r;
460 row[x*6+3] = row[x*6+2] = g;
461 row[x*6+1] = row[x*6] = b;
462 }
463
464 static inline void setpixel_64bppARGB(BYTE r, BYTE g, BYTE b, BYTE a,
465 BYTE *row, UINT x)
466 {
467 UINT64 a64=a, r64=r, g64=g, b64=b;
468 *((UINT64*)(row)+x) = (a64<<56)|(a64<<48)|(r64<<40)|(r64<<32)|(g64<<24)|(g64<<16)|(b64<<8)|b64;
469 }
470
471 static inline void setpixel_64bppPARGB(BYTE r, BYTE g, BYTE b, BYTE a,
472 BYTE *row, UINT x)
473 {
474 UINT64 a64, r64, g64, b64;
475 a64 = a * 257;
476 r64 = r * a / 255;
477 g64 = g * a / 255;
478 b64 = b * a / 255;
479 *((UINT64*)(row)+x) = (a64<<48)|(r64<<32)|(g64<<16)|b64;
480 }
481
482 GpStatus WINGDIPAPI GdipBitmapSetPixel(GpBitmap* bitmap, INT x, INT y,
483 ARGB color)
484 {
485 BYTE a, r, g, b;
486 BYTE *row;
487
488 if(!bitmap || x < 0 || y < 0 || x >= bitmap->width || y >= bitmap->height)
489 return InvalidParameter;
490
491 a = color>>24;
492 r = color>>16;
493 g = color>>8;
494 b = color;
495
496 row = bitmap->bits + bitmap->stride * y;
497
498 switch (bitmap->format)
499 {
500 case PixelFormat16bppGrayScale:
501 setpixel_16bppGrayScale(r,g,b,a,row,x);
502 break;
503 case PixelFormat16bppRGB555:
504 setpixel_16bppRGB555(r,g,b,a,row,x);
505 break;
506 case PixelFormat16bppRGB565:
507 setpixel_16bppRGB565(r,g,b,a,row,x);
508 break;
509 case PixelFormat16bppARGB1555:
510 setpixel_16bppARGB1555(r,g,b,a,row,x);
511 break;
512 case PixelFormat24bppRGB:
513 setpixel_24bppRGB(r,g,b,a,row,x);
514 break;
515 case PixelFormat32bppRGB:
516 setpixel_32bppRGB(r,g,b,a,row,x);
517 break;
518 case PixelFormat32bppARGB:
519 setpixel_32bppARGB(r,g,b,a,row,x);
520 break;
521 case PixelFormat32bppPARGB:
522 setpixel_32bppPARGB(r,g,b,a,row,x);
523 break;
524 case PixelFormat48bppRGB:
525 setpixel_48bppRGB(r,g,b,a,row,x);
526 break;
527 case PixelFormat64bppARGB:
528 setpixel_64bppARGB(r,g,b,a,row,x);
529 break;
530 case PixelFormat64bppPARGB:
531 setpixel_64bppPARGB(r,g,b,a,row,x);
532 break;
533 case PixelFormat8bppIndexed:
534 setpixel_8bppIndexed(r,g,b,a,row,x,bitmap->image.palette);
535 break;
536 case PixelFormat4bppIndexed:
537 setpixel_4bppIndexed(r,g,b,a,row,x,bitmap->image.palette);
538 break;
539 case PixelFormat1bppIndexed:
540 setpixel_1bppIndexed(r,g,b,a,row,x,bitmap->image.palette);
541 break;
542 default:
543 FIXME("not implemented for format 0x%x\n", bitmap->format);
544 return NotImplemented;
545 }
546
547 return Ok;
548 }
549
550 GpStatus convert_pixels(INT width, INT height,
551 INT dst_stride, BYTE *dst_bits, PixelFormat dst_format,
552 INT src_stride, const BYTE *src_bits, PixelFormat src_format,
553 ColorPalette *palette)
554 {
555 INT x, y;
556
557 if (src_format == dst_format ||
558 (dst_format == PixelFormat32bppRGB && PIXELFORMATBPP(src_format) == 32))
559 {
560 UINT widthbytes = PIXELFORMATBPP(src_format) * width / 8;
561 for (y=0; y<height; y++)
562 memcpy(dst_bits+dst_stride*y, src_bits+src_stride*y, widthbytes);
563 return Ok;
564 }
565
566 #define convert_indexed_to_rgb(getpixel_function, setpixel_function) do { \
567 for (x=0; x<width; x++) \
568 for (y=0; y<height; y++) { \
569 BYTE index; \
570 ARGB argb; \
571 BYTE *color = (BYTE *)&argb; \
572 getpixel_function(&index, src_bits+src_stride*y, x); \
573 argb = (palette && index < palette->Count) ? palette->Entries[index] : 0; \
574 setpixel_function(color[2], color[1], color[0], color[3], dst_bits+dst_stride*y, x); \
575 } \
576 return Ok; \
577 } while (0);
578
579 #define convert_rgb_to_rgb(getpixel_function, setpixel_function) do { \
580 for (x=0; x<width; x++) \
581 for (y=0; y<height; y++) { \
582 BYTE r, g, b, a; \
583 getpixel_function(&r, &g, &b, &a, src_bits+src_stride*y, x); \
584 setpixel_function(r, g, b, a, dst_bits+dst_stride*y, x); \
585 } \
586 return Ok; \
587 } while (0);
588
589 #define convert_rgb_to_indexed(getpixel_function, setpixel_function) do { \
590 for (x=0; x<width; x++) \
591 for (y=0; y<height; y++) { \
592 BYTE r, g, b, a; \
593 getpixel_function(&r, &g, &b, &a, src_bits+src_stride*y, x); \
594 setpixel_function(r, g, b, a, dst_bits+dst_stride*y, x, palette); \
595 } \
596 return Ok; \
597 } while (0);
598
599 switch (src_format)
600 {
601 case PixelFormat1bppIndexed:
602 switch (dst_format)
603 {
604 case PixelFormat16bppGrayScale:
605 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_16bppGrayScale);
606 case PixelFormat16bppRGB555:
607 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_16bppRGB555);
608 case PixelFormat16bppRGB565:
609 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_16bppRGB565);
610 case PixelFormat16bppARGB1555:
611 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_16bppARGB1555);
612 case PixelFormat24bppRGB:
613 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_24bppRGB);
614 case PixelFormat32bppRGB:
615 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_32bppRGB);
616 case PixelFormat32bppARGB:
617 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_32bppARGB);
618 case PixelFormat32bppPARGB:
619 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_32bppPARGB);
620 case PixelFormat48bppRGB:
621 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_48bppRGB);
622 case PixelFormat64bppARGB:
623 convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_64bppARGB);
624 default:
625 break;
626 }
627 break;
628 case PixelFormat4bppIndexed:
629 switch (dst_format)
630 {
631 case PixelFormat16bppGrayScale:
632 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_16bppGrayScale);
633 case PixelFormat16bppRGB555:
634 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_16bppRGB555);
635 case PixelFormat16bppRGB565:
636 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_16bppRGB565);
637 case PixelFormat16bppARGB1555:
638 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_16bppARGB1555);
639 case PixelFormat24bppRGB:
640 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_24bppRGB);
641 case PixelFormat32bppRGB:
642 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_32bppRGB);
643 case PixelFormat32bppARGB:
644 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_32bppARGB);
645 case PixelFormat32bppPARGB:
646 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_32bppPARGB);
647 case PixelFormat48bppRGB:
648 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_48bppRGB);
649 case PixelFormat64bppARGB:
650 convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_64bppARGB);
651 default:
652 break;
653 }
654 break;
655 case PixelFormat8bppIndexed:
656 switch (dst_format)
657 {
658 case PixelFormat16bppGrayScale:
659 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_16bppGrayScale);
660 case PixelFormat16bppRGB555:
661 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_16bppRGB555);
662 case PixelFormat16bppRGB565:
663 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_16bppRGB565);
664 case PixelFormat16bppARGB1555:
665 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_16bppARGB1555);
666 case PixelFormat24bppRGB:
667 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_24bppRGB);
668 case PixelFormat32bppRGB:
669 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_32bppRGB);
670 case PixelFormat32bppARGB:
671 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_32bppARGB);
672 case PixelFormat32bppPARGB:
673 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_32bppPARGB);
674 case PixelFormat48bppRGB:
675 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_48bppRGB);
676 case PixelFormat64bppARGB:
677 convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_64bppARGB);
678 default:
679 break;
680 }
681 break;
682 case PixelFormat16bppGrayScale:
683 switch (dst_format)
684 {
685 case PixelFormat1bppIndexed:
686 convert_rgb_to_indexed(getpixel_16bppGrayScale, setpixel_1bppIndexed);
687 case PixelFormat8bppIndexed:
688 convert_rgb_to_indexed(getpixel_16bppGrayScale, setpixel_8bppIndexed);
689 case PixelFormat16bppRGB555:
690 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_16bppRGB555);
691 case PixelFormat16bppRGB565:
692 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_16bppRGB565);
693 case PixelFormat16bppARGB1555:
694 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_16bppARGB1555);
695 case PixelFormat24bppRGB:
696 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_24bppRGB);
697 case PixelFormat32bppRGB:
698 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_32bppRGB);
699 case PixelFormat32bppARGB:
700 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_32bppARGB);
701 case PixelFormat32bppPARGB:
702 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_32bppPARGB);
703 case PixelFormat48bppRGB:
704 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_48bppRGB);
705 case PixelFormat64bppARGB:
706 convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_64bppARGB);
707 default:
708 break;
709 }
710 break;
711 case PixelFormat16bppRGB555:
712 switch (dst_format)
713 {
714 case PixelFormat1bppIndexed:
715 convert_rgb_to_indexed(getpixel_16bppRGB555, setpixel_1bppIndexed);
716 case PixelFormat8bppIndexed:
717 convert_rgb_to_indexed(getpixel_16bppRGB555, setpixel_8bppIndexed);
718 case PixelFormat16bppGrayScale:
719 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_16bppGrayScale);
720 case PixelFormat16bppRGB565:
721 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_16bppRGB565);
722 case PixelFormat16bppARGB1555:
723 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_16bppARGB1555);
724 case PixelFormat24bppRGB:
725 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_24bppRGB);
726 case PixelFormat32bppRGB:
727 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_32bppRGB);
728 case PixelFormat32bppARGB:
729 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_32bppARGB);
730 case PixelFormat32bppPARGB:
731 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_32bppPARGB);
732 case PixelFormat48bppRGB:
733 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_48bppRGB);
734 case PixelFormat64bppARGB:
735 convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_64bppARGB);
736 default:
737 break;
738 }
739 break;
740 case PixelFormat16bppRGB565:
741 switch (dst_format)
742 {
743 case PixelFormat1bppIndexed:
744 convert_rgb_to_indexed(getpixel_16bppRGB565, setpixel_1bppIndexed);
745 case PixelFormat8bppIndexed:
746 convert_rgb_to_indexed(getpixel_16bppRGB565, setpixel_8bppIndexed);
747 case PixelFormat16bppGrayScale:
748 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_16bppGrayScale);
749 case PixelFormat16bppRGB555:
750 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_16bppRGB555);
751 case PixelFormat16bppARGB1555:
752 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_16bppARGB1555);
753 case PixelFormat24bppRGB:
754 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_24bppRGB);
755 case PixelFormat32bppRGB:
756 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_32bppRGB);
757 case PixelFormat32bppARGB:
758 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_32bppARGB);
759 case PixelFormat32bppPARGB:
760 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_32bppPARGB);
761 case PixelFormat48bppRGB:
762 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_48bppRGB);
763 case PixelFormat64bppARGB:
764 convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_64bppARGB);
765 default:
766 break;
767 }
768 break;
769 case PixelFormat16bppARGB1555:
770 switch (dst_format)
771 {
772 case PixelFormat1bppIndexed:
773 convert_rgb_to_indexed(getpixel_16bppARGB1555, setpixel_1bppIndexed);
774 case PixelFormat8bppIndexed:
775 convert_rgb_to_indexed(getpixel_16bppARGB1555, setpixel_8bppIndexed);
776 case PixelFormat16bppGrayScale:
777 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_16bppGrayScale);
778 case PixelFormat16bppRGB555:
779 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_16bppRGB555);
780 case PixelFormat16bppRGB565:
781 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_16bppRGB565);
782 case PixelFormat24bppRGB:
783 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_24bppRGB);
784 case PixelFormat32bppRGB:
785 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_32bppRGB);
786 case PixelFormat32bppARGB:
787 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_32bppARGB);
788 case PixelFormat32bppPARGB:
789 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_32bppPARGB);
790 case PixelFormat48bppRGB:
791 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_48bppRGB);
792 case PixelFormat64bppARGB:
793 convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_64bppARGB);
794 default:
795 break;
796 }
797 break;
798 case PixelFormat24bppRGB:
799 switch (dst_format)
800 {
801 case PixelFormat1bppIndexed:
802 convert_rgb_to_indexed(getpixel_24bppRGB, setpixel_1bppIndexed);
803 case PixelFormat8bppIndexed:
804 convert_rgb_to_indexed(getpixel_24bppRGB, setpixel_8bppIndexed);
805 case PixelFormat16bppGrayScale:
806 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_16bppGrayScale);
807 case PixelFormat16bppRGB555:
808 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_16bppRGB555);
809 case PixelFormat16bppRGB565:
810 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_16bppRGB565);
811 case PixelFormat16bppARGB1555:
812 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_16bppARGB1555);
813 case PixelFormat32bppRGB:
814 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_32bppRGB);
815 case PixelFormat32bppARGB:
816 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_32bppARGB);
817 case PixelFormat32bppPARGB:
818 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_32bppPARGB);
819 case PixelFormat48bppRGB:
820 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_48bppRGB);
821 case PixelFormat64bppARGB:
822 convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_64bppARGB);
823 default:
824 break;
825 }
826 break;
827 case PixelFormat32bppRGB:
828 switch (dst_format)
829 {
830 case PixelFormat1bppIndexed:
831 convert_rgb_to_indexed(getpixel_32bppRGB, setpixel_1bppIndexed);
832 case PixelFormat8bppIndexed:
833 convert_rgb_to_indexed(getpixel_32bppRGB, setpixel_8bppIndexed);
834 case PixelFormat16bppGrayScale:
835 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_16bppGrayScale);
836 case PixelFormat16bppRGB555:
837 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_16bppRGB555);
838 case PixelFormat16bppRGB565:
839 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_16bppRGB565);
840 case PixelFormat16bppARGB1555:
841 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_16bppARGB1555);
842 case PixelFormat24bppRGB:
843 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_24bppRGB);
844 case PixelFormat32bppARGB:
845 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_32bppARGB);
846 case PixelFormat32bppPARGB:
847 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_32bppPARGB);
848 case PixelFormat48bppRGB:
849 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_48bppRGB);
850 case PixelFormat64bppARGB:
851 convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_64bppARGB);
852 default:
853 break;
854 }
855 break;
856 case PixelFormat32bppARGB:
857 switch (dst_format)
858 {
859 case PixelFormat1bppIndexed:
860 convert_rgb_to_indexed(getpixel_32bppARGB, setpixel_1bppIndexed);
861 case PixelFormat8bppIndexed:
862 convert_rgb_to_indexed(getpixel_32bppARGB, setpixel_8bppIndexed);
863 case PixelFormat16bppGrayScale:
864 convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_16bppGrayScale);
865 case PixelFormat16bppRGB555:
866 convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_16bppRGB555);
867 case PixelFormat16bppRGB565:
868 convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_16bppRGB565);
869 case PixelFormat16bppARGB1555:
870 convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_16bppARGB1555);
871 case PixelFormat24bppRGB:
872 convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_24bppRGB);
873 case PixelFormat32bppPARGB:
874 convert_32bppARGB_to_32bppPARGB(width, height, dst_bits, dst_stride, src_bits, src_stride);
875 return Ok;
876 case PixelFormat48bppRGB:
877 convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_48bppRGB);
878 case PixelFormat64bppARGB:
879 convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_64bppARGB);
880 default:
881 break;
882 }
883 break;
884 case PixelFormat32bppPARGB:
885 switch (dst_format)
886 {
887 case PixelFormat1bppIndexed:
888 convert_rgb_to_indexed(getpixel_32bppPARGB, setpixel_1bppIndexed);
889 case PixelFormat8bppIndexed:
890 convert_rgb_to_indexed(getpixel_32bppPARGB, setpixel_8bppIndexed);
891 case PixelFormat16bppGrayScale:
892 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_16bppGrayScale);
893 case PixelFormat16bppRGB555:
894 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_16bppRGB555);
895 case PixelFormat16bppRGB565:
896 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_16bppRGB565);
897 case PixelFormat16bppARGB1555:
898 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_16bppARGB1555);
899 case PixelFormat24bppRGB:
900 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_24bppRGB);
901 case PixelFormat32bppRGB:
902 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_32bppRGB);
903 case PixelFormat32bppARGB:
904 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_32bppARGB);
905 case PixelFormat48bppRGB:
906 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_48bppRGB);
907 case PixelFormat64bppARGB:
908 convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_64bppARGB);
909 default:
910 break;
911 }
912 break;
913 case PixelFormat48bppRGB:
914 switch (dst_format)
915 {
916 case PixelFormat1bppIndexed:
917 convert_rgb_to_indexed(getpixel_48bppRGB, setpixel_1bppIndexed);
918 case PixelFormat8bppIndexed:
919 convert_rgb_to_indexed(getpixel_48bppRGB, setpixel_8bppIndexed);
920 case PixelFormat16bppGrayScale:
921 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_16bppGrayScale);
922 case PixelFormat16bppRGB555:
923 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_16bppRGB555);
924 case PixelFormat16bppRGB565:
925 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_16bppRGB565);
926 case PixelFormat16bppARGB1555:
927 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_16bppARGB1555);
928 case PixelFormat24bppRGB:
929 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_24bppRGB);
930 case PixelFormat32bppRGB:
931 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_32bppRGB);
932 case PixelFormat32bppARGB:
933 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_32bppARGB);
934 case PixelFormat32bppPARGB:
935 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_32bppPARGB);
936 case PixelFormat64bppARGB:
937 convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_64bppARGB);
938 default:
939 break;
940 }
941 break;
942 case PixelFormat64bppARGB:
943 switch (dst_format)
944 {
945 case PixelFormat1bppIndexed:
946 convert_rgb_to_indexed(getpixel_64bppARGB, setpixel_1bppIndexed);
947 case PixelFormat8bppIndexed:
948 convert_rgb_to_indexed(getpixel_64bppARGB, setpixel_8bppIndexed);
949 case PixelFormat16bppGrayScale:
950 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_16bppGrayScale);
951 case PixelFormat16bppRGB555:
952 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_16bppRGB555);
953 case PixelFormat16bppRGB565:
954 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_16bppRGB565);
955 case PixelFormat16bppARGB1555:
956 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_16bppARGB1555);
957 case PixelFormat24bppRGB:
958 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_24bppRGB);
959 case PixelFormat32bppRGB:
960 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_32bppRGB);
961 case PixelFormat32bppARGB:
962 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_32bppARGB);
963 case PixelFormat32bppPARGB:
964 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_32bppPARGB);
965 case PixelFormat48bppRGB:
966 convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_48bppRGB);
967 default:
968 break;
969 }
970 break;
971 case PixelFormat64bppPARGB:
972 switch (dst_format)
973 {
974 case PixelFormat1bppIndexed:
975 convert_rgb_to_indexed(getpixel_64bppPARGB, setpixel_1bppIndexed);
976 case PixelFormat8bppIndexed:
977 convert_rgb_to_indexed(getpixel_64bppPARGB, setpixel_8bppIndexed);
978 case PixelFormat16bppGrayScale:
979 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_16bppGrayScale);
980 case PixelFormat16bppRGB555:
981 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_16bppRGB555);
982 case PixelFormat16bppRGB565:
983 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_16bppRGB565);
984 case PixelFormat16bppARGB1555:
985 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_16bppARGB1555);
986 case PixelFormat24bppRGB:
987 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_24bppRGB);
988 case PixelFormat32bppRGB:
989 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_32bppRGB);
990 case PixelFormat32bppARGB:
991 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_32bppARGB);
992 case PixelFormat32bppPARGB:
993 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_32bppPARGB);
994 case PixelFormat48bppRGB:
995 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_48bppRGB);
996 case PixelFormat64bppARGB:
997 convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_64bppARGB);
998 default:
999 break;
1000 }
1001 break;
1002 default:
1003 break;
1004 }
1005
1006 #undef convert_indexed_to_rgb
1007 #undef convert_rgb_to_rgb
1008
1009 return NotImplemented;
1010 }
1011
1012 /* This function returns a pointer to an array of pixels that represents the
1013 * bitmap. The *entire* bitmap is locked according to the lock mode specified by
1014 * flags. It is correct behavior that a user who calls this function with write
1015 * privileges can write to the whole bitmap (not just the area in rect).
1016 *
1017 * FIXME: only used portion of format is bits per pixel. */
1018 GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
1019 UINT flags, PixelFormat format, BitmapData* lockeddata)
1020 {
1021 INT bitspp = PIXELFORMATBPP(format);
1022 GpRect act_rect; /* actual rect to be used */
1023 GpStatus stat;
1024
1025 TRACE("%p %p %d 0x%x %p\n", bitmap, rect, flags, format, lockeddata);
1026
1027 if(!lockeddata || !bitmap)
1028 return InvalidParameter;
1029
1030 if(rect){
1031 if(rect->X < 0 || rect->Y < 0 || (rect->X + rect->Width > bitmap->width) ||
1032 (rect->Y + rect->Height > bitmap->height) || !flags)
1033 return InvalidParameter;
1034
1035 act_rect = *rect;
1036 }
1037 else{
1038 act_rect.X = act_rect.Y = 0;
1039 act_rect.Width = bitmap->width;
1040 act_rect.Height = bitmap->height;
1041 }
1042
1043 if(bitmap->lockmode)
1044 {
1045 WARN("bitmap is already locked and cannot be locked again\n");
1046 return WrongState;
1047 }
1048
1049 if (bitmap->bits && bitmap->format == format && !(flags & ImageLockModeUserInputBuf))
1050 {
1051 /* no conversion is necessary; just use the bits directly */
1052 lockeddata->Width = act_rect.Width;
1053 lockeddata->Height = act_rect.Height;
1054 lockeddata->PixelFormat = format;
1055 lockeddata->Reserved = flags;
1056 lockeddata->Stride = bitmap->stride;
1057 lockeddata->Scan0 = bitmap->bits + (bitspp / 8) * act_rect.X +
1058 bitmap->stride * act_rect.Y;
1059
1060 bitmap->lockmode = flags | ImageLockModeRead;
1061 bitmap->numlocks++;
1062
1063 return Ok;
1064 }
1065
1066 /* Make sure we can convert to the requested format. */
1067 if (flags & ImageLockModeRead)
1068 {
1069 stat = convert_pixels(0, 0, 0, NULL, format, 0, NULL, bitmap->format, NULL);
1070 if (stat == NotImplemented)
1071 {
1072 FIXME("cannot read bitmap from %x to %x\n", bitmap->format, format);
1073 return NotImplemented;
1074 }
1075 }
1076
1077 /* If we're opening for writing, make sure we'll be able to write back in
1078 * the original format. */
1079 if (flags & ImageLockModeWrite)
1080 {
1081 stat = convert_pixels(0, 0, 0, NULL, bitmap->format, 0, NULL, format, NULL);
1082 if (stat == NotImplemented)
1083 {
1084 FIXME("cannot write bitmap from %x to %x\n", format, bitmap->format);
1085 return NotImplemented;
1086 }
1087 }
1088
1089 lockeddata->Width = act_rect.Width;
1090 lockeddata->Height = act_rect.Height;
1091 lockeddata->PixelFormat = format;
1092 lockeddata->Reserved = flags;
1093
1094 if(!(flags & ImageLockModeUserInputBuf))
1095 {
1096 lockeddata->Stride = (((act_rect.Width * bitspp + 7) / 8) + 3) & ~3;
1097
1098 bitmap->bitmapbits = GdipAlloc(lockeddata->Stride * act_rect.Height);
1099
1100 if (!bitmap->bitmapbits) return OutOfMemory;
1101
1102 lockeddata->Scan0 = bitmap->bitmapbits;
1103 }
1104
1105 if (flags & ImageLockModeRead)
1106 {
1107 static int fixme=0;
1108
1109 if (!fixme && (PIXELFORMATBPP(bitmap->format) * act_rect.X) % 8 != 0)
1110 {
1111 FIXME("Cannot copy rows that don't start at a whole byte.\n");
1112 fixme = 1;
1113 }
1114
1115 stat = convert_pixels(act_rect.Width, act_rect.Height,
1116 lockeddata->Stride, lockeddata->Scan0, format,
1117 bitmap->stride,
1118 bitmap->bits + bitmap->stride * act_rect.Y + PIXELFORMATBPP(bitmap->format) * act_rect.X / 8,
1119 bitmap->format, bitmap->image.palette);
1120
1121 if (stat != Ok)
1122 {
1123 GdipFree(bitmap->bitmapbits);
1124 bitmap->bitmapbits = NULL;
1125 return stat;
1126 }
1127 }
1128
1129 bitmap->lockmode = flags | ImageLockModeRead;
1130 bitmap->numlocks++;
1131 bitmap->lockx = act_rect.X;
1132 bitmap->locky = act_rect.Y;
1133
1134 return Ok;
1135 }
1136
1137 GpStatus WINGDIPAPI GdipBitmapSetResolution(GpBitmap* bitmap, REAL xdpi, REAL ydpi)
1138 {
1139 TRACE("(%p, %.2f, %.2f)\n", bitmap, xdpi, ydpi);
1140
1141 if (!bitmap || xdpi == 0.0 || ydpi == 0.0)
1142 return InvalidParameter;
1143
1144 bitmap->image.xres = xdpi;
1145 bitmap->image.yres = ydpi;
1146
1147 return Ok;
1148 }
1149
1150 GpStatus WINGDIPAPI GdipBitmapUnlockBits(GpBitmap* bitmap,
1151 BitmapData* lockeddata)
1152 {
1153 GpStatus stat;
1154 static int fixme=0;
1155
1156 TRACE("(%p,%p)\n", bitmap, lockeddata);
1157
1158 if(!bitmap || !lockeddata)
1159 return InvalidParameter;
1160
1161 if(!bitmap->lockmode)
1162 return WrongState;
1163
1164 if(!(lockeddata->Reserved & ImageLockModeWrite)){
1165 if(!(--bitmap->numlocks))
1166 bitmap->lockmode = 0;
1167
1168 GdipFree(bitmap->bitmapbits);
1169 bitmap->bitmapbits = NULL;
1170 return Ok;
1171 }
1172
1173 if (!bitmap->bitmapbits && !(lockeddata->Reserved & ImageLockModeUserInputBuf))
1174 {
1175 /* we passed a direct reference; no need to do anything */
1176 bitmap->lockmode = 0;
1177 bitmap->numlocks = 0;
1178 return Ok;
1179 }
1180
1181 if (!fixme && (PIXELFORMATBPP(bitmap->format) * bitmap->lockx) % 8 != 0)
1182 {
1183 FIXME("Cannot copy rows that don't start at a whole byte.\n");
1184 fixme = 1;
1185 }
1186
1187 stat = convert_pixels(lockeddata->Width, lockeddata->Height,
1188 bitmap->stride,
1189 bitmap->bits + bitmap->stride * bitmap->locky + PIXELFORMATBPP(bitmap->format) * bitmap->lockx / 8,
1190 bitmap->format,
1191 lockeddata->Stride, lockeddata->Scan0, lockeddata->PixelFormat, NULL);
1192
1193 if (stat != Ok)
1194 {
1195 ERR("failed to convert pixels; this should never happen\n");
1196 }
1197
1198 GdipFree(bitmap->bitmapbits);
1199 bitmap->bitmapbits = NULL;
1200 bitmap->lockmode = 0;
1201 bitmap->numlocks = 0;
1202
1203 return stat;
1204 }
1205
1206 GpStatus WINGDIPAPI GdipCloneBitmapArea(REAL x, REAL y, REAL width, REAL height,
1207 PixelFormat format, GpBitmap* srcBitmap, GpBitmap** dstBitmap)
1208 {
1209 BitmapData lockeddata_src, lockeddata_dst;
1210 int i;
1211 UINT row_size;
1212 Rect area;
1213 GpStatus stat;
1214
1215 TRACE("(%f,%f,%f,%f,0x%x,%p,%p)\n", x, y, width, height, format, srcBitmap, dstBitmap);
1216
1217 if (!srcBitmap || !dstBitmap || srcBitmap->image.type != ImageTypeBitmap ||
1218 x < 0 || y < 0 ||
1219 x + width > srcBitmap->width || y + height > srcBitmap->height)
1220 {
1221 TRACE("<-- InvalidParameter\n");
1222 return InvalidParameter;
1223 }
1224
1225 if (format == PixelFormatDontCare)
1226 format = srcBitmap->format;
1227
1228 area.X = gdip_round(x);
1229 area.Y = gdip_round(y);
1230 area.Width = gdip_round(width);
1231 area.Height = gdip_round(height);
1232
1233 stat = GdipBitmapLockBits(srcBitmap, &area, ImageLockModeRead, format,
1234 &lockeddata_src);
1235 if (stat != Ok) return stat;
1236
1237 stat = GdipCreateBitmapFromScan0(lockeddata_src.Width, lockeddata_src.Height,
1238 0, lockeddata_src.PixelFormat, NULL, dstBitmap);
1239 if (stat == Ok)
1240 {
1241 stat = GdipBitmapLockBits(*dstBitmap, NULL, ImageLockModeWrite,
1242 lockeddata_src.PixelFormat, &lockeddata_dst);
1243
1244 if (stat == Ok)
1245 {
1246 /* copy the image data */
1247 row_size = (lockeddata_src.Width * PIXELFORMATBPP(lockeddata_src.PixelFormat) +7)/8;
1248 for (i=0; i<lockeddata_src.Height; i++)
1249 memcpy((BYTE*)lockeddata_dst.Scan0+lockeddata_dst.Stride*i,
1250 (BYTE*)lockeddata_src.Scan0+lockeddata_src.Stride*i,
1251 row_size);
1252
1253 GdipBitmapUnlockBits(*dstBitmap, &lockeddata_dst);
1254 }
1255
1256 if (stat != Ok)
1257 GdipDisposeImage((GpImage*)*dstBitmap);
1258 }
1259
1260 GdipBitmapUnlockBits(srcBitmap, &lockeddata_src);
1261
1262 if (stat != Ok)
1263 {
1264 *dstBitmap = NULL;
1265 }
1266
1267 return stat;
1268 }
1269
1270 GpStatus WINGDIPAPI GdipCloneBitmapAreaI(INT x, INT y, INT width, INT height,
1271 PixelFormat format, GpBitmap* srcBitmap, GpBitmap** dstBitmap)
1272 {
1273 TRACE("(%i,%i,%i,%i,0x%x,%p,%p)\n", x, y, width, height, format, srcBitmap, dstBitmap);
1274
1275 return GdipCloneBitmapArea(x, y, width, height, format, srcBitmap, dstBitmap);
1276 }
1277
1278 GpStatus WINGDIPAPI GdipCloneImage(GpImage *image, GpImage **cloneImage)
1279 {
1280 GpStatus stat = GenericError;
1281
1282 TRACE("%p, %p\n", image, cloneImage);
1283
1284 if (!image || !cloneImage)
1285 return InvalidParameter;
1286
1287 if (image->picture)
1288 {
1289 IStream* stream;
1290 HRESULT hr;
1291 INT size;
1292 LARGE_INTEGER move;
1293
1294 hr = CreateStreamOnHGlobal(0, TRUE, &stream);
1295 if (FAILED(hr))
1296 return GenericError;
1297
1298 hr = IPicture_SaveAsFile(image->picture, stream, FALSE, &size);
1299 if(FAILED(hr))
1300 {
1301 WARN("Failed to save image on stream\n");
1302 goto out;
1303 }
1304
1305 /* Set seek pointer back to the beginning of the picture */
1306 move.QuadPart = 0;
1307 hr = IStream_Seek(stream, move, STREAM_SEEK_SET, NULL);
1308 if (FAILED(hr))
1309 goto out;
1310
1311 stat = GdipLoadImageFromStream(stream, cloneImage);
1312 if (stat != Ok) WARN("Failed to load image from stream\n");
1313
1314 out:
1315 IStream_Release(stream);
1316 return stat;
1317 }
1318 else if (image->type == ImageTypeBitmap)
1319 {
1320 GpBitmap *bitmap = (GpBitmap*)image;
1321 BitmapData lockeddata_src, lockeddata_dst;
1322 int i;
1323 UINT row_size;
1324
1325 stat = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, bitmap->format,
1326 &lockeddata_src);
1327 if (stat != Ok) return stat;
1328
1329 stat = GdipCreateBitmapFromScan0(lockeddata_src.Width, lockeddata_src.Height,
1330 0, lockeddata_src.PixelFormat, NULL, (GpBitmap**)cloneImage);
1331 if (stat == Ok)
1332 {
1333 stat = GdipBitmapLockBits((GpBitmap*)*cloneImage, NULL, ImageLockModeWrite,
1334 lockeddata_src.PixelFormat, &lockeddata_dst);
1335
1336 if (stat == Ok)
1337 {
1338 /* copy the image data */
1339 row_size = (lockeddata_src.Width * PIXELFORMATBPP(lockeddata_src.PixelFormat) +7)/8;
1340 for (i=0; i<lockeddata_src.Height; i++)
1341 memcpy((BYTE*)lockeddata_dst.Scan0+lockeddata_dst.Stride*i,
1342 (BYTE*)lockeddata_src.Scan0+lockeddata_src.Stride*i,
1343 row_size);
1344
1345 GdipBitmapUnlockBits((GpBitmap*)*cloneImage, &lockeddata_dst);
1346 }
1347
1348 if (stat != Ok)
1349 GdipDisposeImage(*cloneImage);
1350 }
1351
1352 GdipBitmapUnlockBits(bitmap, &lockeddata_src);
1353
1354 if (stat != Ok)
1355 {
1356 *cloneImage = NULL;
1357 }
1358 else memcpy(&(*cloneImage)->format, &image->format, sizeof(GUID));
1359
1360 return stat;
1361 }
1362 else if (image->type == ImageTypeMetafile && ((GpMetafile*)image)->hemf)
1363 {
1364 GpMetafile *result, *metafile;
1365
1366 metafile = (GpMetafile*)image;
1367
1368 result = GdipAlloc(sizeof(*result));
1369 if (!result)
1370 return OutOfMemory;
1371
1372 result->image.type = ImageTypeMetafile;
1373 result->image.format = image->format;
1374 result->image.flags = image->flags;
1375 result->image.frame_count = 1;
1376 result->image.xres = image->xres;
1377 result->image.yres = image->yres;
1378 result->bounds = metafile->bounds;
1379 result->unit = metafile->unit;
1380 result->metafile_type = metafile->metafile_type;
1381 result->hemf = CopyEnhMetaFileW(metafile->hemf, NULL);
1382
1383 if (!result->hemf)
1384 {
1385 GdipFree(result);
1386 return OutOfMemory;
1387 }
1388
1389 *cloneImage = &result->image;
1390 return Ok;
1391 }
1392 else
1393 {
1394 WARN("GpImage with no image data (metafile in wrong state?)\n");
1395 return InvalidParameter;
1396 }
1397 }
1398
1399 GpStatus WINGDIPAPI GdipCreateBitmapFromFile(GDIPCONST WCHAR* filename,
1400 GpBitmap **bitmap)
1401 {
1402 GpStatus stat;
1403 IStream *stream;
1404
1405 TRACE("(%s) %p\n", debugstr_w(filename), bitmap);
1406
1407 if(!filename || !bitmap)
1408 return InvalidParameter;
1409
1410 stat = GdipCreateStreamOnFile(filename, GENERIC_READ, &stream);
1411
1412 if(stat != Ok)
1413 return stat;
1414
1415 stat = GdipCreateBitmapFromStream(stream, bitmap);
1416
1417 IStream_Release(stream);
1418
1419 return stat;
1420 }
1421
1422 GpStatus WINGDIPAPI GdipCreateBitmapFromGdiDib(GDIPCONST BITMAPINFO* info,
1423 VOID *bits, GpBitmap **bitmap)
1424 {
1425 DWORD height, stride;
1426 PixelFormat format;
1427
1428 FIXME("(%p, %p, %p) - partially implemented\n", info, bits, bitmap);
1429
1430 if (!info || !bits || !bitmap)
1431 return InvalidParameter;
1432
1433 height = abs(info->bmiHeader.biHeight);
1434 stride = ((info->bmiHeader.biWidth * info->bmiHeader.biBitCount + 31) >> 3) & ~3;
1435
1436 if(info->bmiHeader.biHeight > 0) /* bottom-up */
1437 {
1438 bits = (BYTE*)bits + (height - 1) * stride;
1439 stride = -stride;
1440 }
1441
1442 switch(info->bmiHeader.biBitCount) {
1443 case 1:
1444 format = PixelFormat1bppIndexed;
1445 break;
1446 case 4:
1447 format = PixelFormat4bppIndexed;
1448 break;
1449 case 8:
1450 format = PixelFormat8bppIndexed;
1451 break;
1452 case 16:
1453 format = PixelFormat16bppRGB555;
1454 break;
1455 case 24:
1456 format = PixelFormat24bppRGB;
1457 break;
1458 case 32:
1459 format = PixelFormat32bppRGB;
1460 break;
1461 default:
1462 FIXME("don't know how to handle %d bpp\n", info->bmiHeader.biBitCount);
1463 *bitmap = NULL;
1464 return InvalidParameter;
1465 }
1466
1467 return GdipCreateBitmapFromScan0(info->bmiHeader.biWidth, height, stride, format,
1468 bits, bitmap);
1469
1470 }
1471
1472 /* FIXME: no icm */
1473 GpStatus WINGDIPAPI GdipCreateBitmapFromFileICM(GDIPCONST WCHAR* filename,
1474 GpBitmap **bitmap)
1475 {
1476 TRACE("(%s) %p\n", debugstr_w(filename), bitmap);
1477
1478 return GdipCreateBitmapFromFile(filename, bitmap);
1479 }
1480
1481 GpStatus WINGDIPAPI GdipCreateBitmapFromResource(HINSTANCE hInstance,
1482 GDIPCONST WCHAR* lpBitmapName, GpBitmap** bitmap)
1483 {
1484 HBITMAP hbm;
1485 GpStatus stat = InvalidParameter;
1486
1487 TRACE("%p (%s) %p\n", hInstance, debugstr_w(lpBitmapName), bitmap);
1488
1489 if(!lpBitmapName || !bitmap)
1490 return InvalidParameter;
1491
1492 /* load DIB */
1493 hbm = LoadImageW(hInstance, lpBitmapName, IMAGE_BITMAP, 0, 0,
1494 LR_CREATEDIBSECTION);
1495
1496 if(hbm){
1497 stat = GdipCreateBitmapFromHBITMAP(hbm, NULL, bitmap);
1498 DeleteObject(hbm);
1499 }
1500
1501 return stat;
1502 }
1503
1504 GpStatus WINGDIPAPI GdipCreateHBITMAPFromBitmap(GpBitmap* bitmap,
1505 HBITMAP* hbmReturn, ARGB background)
1506 {
1507 GpStatus stat;
1508 HBITMAP result;
1509 UINT width, height;
1510 BITMAPINFOHEADER bih;
1511 LPBYTE bits;
1512 BitmapData lockeddata;
1513 TRACE("(%p,%p,%x)\n", bitmap, hbmReturn, background);
1514
1515 if (!bitmap || !hbmReturn) return InvalidParameter;
1516
1517 GdipGetImageWidth((GpImage*)bitmap, &width);
1518 GdipGetImageHeight((GpImage*)bitmap, &height);
1519
1520 bih.biSize = sizeof(bih);
1521 bih.biWidth = width;
1522 bih.biHeight = height;
1523 bih.biPlanes = 1;
1524 bih.biBitCount = 32;
1525 bih.biCompression = BI_RGB;
1526 bih.biSizeImage = 0;
1527 bih.biXPelsPerMeter = 0;
1528 bih.biYPelsPerMeter = 0;
1529 bih.biClrUsed = 0;
1530 bih.biClrImportant = 0;
1531
1532 result = CreateDIBSection(0, (BITMAPINFO*)&bih, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
1533
1534 if (result)
1535 {
1536 lockeddata.Stride = -width * 4;
1537 lockeddata.Scan0 = bits + (width * 4 * (height - 1));
1538
1539 stat = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead|ImageLockModeUserInputBuf,
1540 PixelFormat32bppPARGB, &lockeddata);
1541
1542 if (stat == Ok)
1543 stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
1544 }
1545 else
1546 stat = GenericError;
1547
1548 if (stat != Ok && result)
1549 {
1550 DeleteObject(result);
1551 result = NULL;
1552 }
1553
1554 *hbmReturn = result;
1555
1556 return stat;
1557 }
1558
1559 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
1560 GpMetafile* metafile, BOOL* succ, EmfType emfType,
1561 const WCHAR* description, GpMetafile** out_metafile)
1562 {
1563 static int calls;
1564
1565 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
1566 debugstr_w(description), out_metafile);
1567
1568 if(!ref || !metafile || !out_metafile)
1569 return InvalidParameter;
1570
1571 *succ = FALSE;
1572 *out_metafile = NULL;
1573
1574 if(!(calls++))
1575 FIXME("not implemented\n");
1576
1577 return NotImplemented;
1578 }
1579
1580 GpStatus WINGDIPAPI GdipCreateBitmapFromGraphics(INT width, INT height,
1581 GpGraphics* target, GpBitmap** bitmap)
1582 {
1583 GpStatus ret;
1584
1585 TRACE("(%d, %d, %p, %p)\n", width, height, target, bitmap);
1586
1587 if(!target || !bitmap)
1588 return InvalidParameter;
1589
1590 ret = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppPARGB,
1591 NULL, bitmap);
1592
1593 if (ret == Ok)
1594 {
1595 GdipGetDpiX(target, &(*bitmap)->image.xres);
1596 GdipGetDpiY(target, &(*bitmap)->image.yres);
1597 }
1598
1599 return ret;
1600 }
1601
1602 GpStatus WINGDIPAPI GdipCreateBitmapFromHICON(HICON hicon, GpBitmap** bitmap)
1603 {
1604 GpStatus stat;
1605 ICONINFO iinfo;
1606 BITMAP bm;
1607 int ret;
1608 UINT width, height, stride;
1609 GpRect rect;
1610 BitmapData lockeddata;
1611 HDC screendc;
1612 BOOL has_alpha;
1613 int x, y;
1614 BITMAPINFOHEADER bih;
1615 DWORD *src;
1616 BYTE *dst_row;
1617 DWORD *dst;
1618
1619 TRACE("%p, %p\n", hicon, bitmap);
1620
1621 if(!bitmap || !GetIconInfo(hicon, &iinfo))
1622 return InvalidParameter;
1623
1624 /* get the size of the icon */
1625 ret = GetObjectA(iinfo.hbmColor ? iinfo.hbmColor : iinfo.hbmMask, sizeof(bm), &bm);
1626 if (ret == 0) {
1627 DeleteObject(iinfo.hbmColor);
1628 DeleteObject(iinfo.hbmMask);
1629 return GenericError;
1630 }
1631
1632 width = bm.bmWidth;
1633 height = iinfo.hbmColor ? abs(bm.bmHeight) : abs(bm.bmHeight) / 2;
1634 stride = width * 4;
1635
1636 stat = GdipCreateBitmapFromScan0(width, height, stride, PixelFormat32bppARGB, NULL, bitmap);
1637 if (stat != Ok) {
1638 DeleteObject(iinfo.hbmColor);
1639 DeleteObject(iinfo.hbmMask);
1640 return stat;
1641 }
1642
1643 rect.X = 0;
1644 rect.Y = 0;
1645 rect.Width = width;
1646 rect.Height = height;
1647
1648 stat = GdipBitmapLockBits(*bitmap, &rect, ImageLockModeWrite, PixelFormat32bppARGB, &lockeddata);
1649 if (stat != Ok) {
1650 DeleteObject(iinfo.hbmColor);
1651 DeleteObject(iinfo.hbmMask);
1652 GdipDisposeImage((GpImage*)*bitmap);
1653 return stat;
1654 }
1655
1656 bih.biSize = sizeof(bih);
1657 bih.biWidth = width;
1658 bih.biHeight = iinfo.hbmColor ? -height: -height * 2;
1659 bih.biPlanes = 1;
1660 bih.biBitCount = 32;
1661 bih.biCompression = BI_RGB;
1662 bih.biSizeImage = 0;
1663 bih.biXPelsPerMeter = 0;
1664 bih.biYPelsPerMeter = 0;
1665 bih.biClrUsed = 0;
1666 bih.biClrImportant = 0;
1667
1668 screendc = CreateCompatibleDC(0);
1669 if (iinfo.hbmColor)
1670 {
1671 GetDIBits(screendc, iinfo.hbmColor, 0, height, lockeddata.Scan0, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
1672
1673 if (bm.bmBitsPixel == 32)
1674 {
1675 has_alpha = FALSE;
1676
1677 /* If any pixel has a non-zero alpha, ignore hbmMask */
1678 src = (DWORD*)lockeddata.Scan0;
1679 for (x=0; x<width && !has_alpha; x++)
1680 for (y=0; y<height && !has_alpha; y++)
1681 if ((*src++ & 0xff000000) != 0)
1682 has_alpha = TRUE;
1683 }
1684 else has_alpha = FALSE;
1685 }
1686 else
1687 {
1688 GetDIBits(screendc, iinfo.hbmMask, 0, height, lockeddata.Scan0, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
1689 has_alpha = FALSE;
1690 }
1691
1692 if (!has_alpha)
1693 {
1694 if (iinfo.hbmMask)
1695 {
1696 BYTE *bits = HeapAlloc(GetProcessHeap(), 0, height * stride);
1697
1698 /* read alpha data from the mask */
1699 if (iinfo.hbmColor)
1700 GetDIBits(screendc, iinfo.hbmMask, 0, height, bits, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
1701 else
1702 GetDIBits(screendc, iinfo.hbmMask, height, height, bits, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
1703
1704 src = (DWORD*)bits;
1705 dst_row = lockeddata.Scan0;
1706 for (y=0; y<height; y++)
1707 {
1708 dst = (DWORD*)dst_row;
1709 for (x=0; x<height; x++)
1710 {
1711 DWORD src_value = *src++;
1712 if (src_value)
1713 *dst++ = 0;
1714 else
1715 *dst++ |= 0xff000000;
1716 }
1717 dst_row += lockeddata.Stride;
1718 }
1719
1720 HeapFree(GetProcessHeap(), 0, bits);
1721 }
1722 else
1723 {
1724 /* set constant alpha of 255 */
1725 dst_row = lockeddata.Scan0;
1726 for (y=0; y<height; y++)
1727 {
1728 dst = (DWORD*)dst_row;
1729 for (x=0; x<height; x++)
1730 *dst++ |= 0xff000000;
1731 dst_row += lockeddata.Stride;
1732 }
1733 }
1734 }
1735
1736 DeleteDC(screendc);
1737
1738 DeleteObject(iinfo.hbmColor);
1739 DeleteObject(iinfo.hbmMask);
1740
1741 GdipBitmapUnlockBits(*bitmap, &lockeddata);
1742
1743 return Ok;
1744 }
1745
1746 static void generate_halftone_palette(ARGB *entries, UINT count)
1747 {
1748 static const BYTE halftone_values[6]={0x00,0x33,0x66,0x99,0xcc,0xff};
1749 UINT i;
1750
1751 for (i=0; i<8 && i<count; i++)
1752 {
1753 entries[i] = 0xff000000;
1754 if (i&1) entries[i] |= 0x800000;
1755 if (i&2) entries[i] |= 0x8000;
1756 if (i&4) entries[i] |= 0x80;
1757 }
1758
1759 if (8 < count)
1760 entries[i] = 0xffc0c0c0;
1761
1762 for (i=9; i<16 && i<count; i++)
1763 {
1764 entries[i] = 0xff000000;
1765 if (i&1) entries[i] |= 0xff0000;
1766 if (i&2) entries[i] |= 0xff00;
1767 if (i&4) entries[i] |= 0xff;
1768 }
1769
1770 for (i=16; i<40 && i<count; i++)
1771 {
1772 entries[i] = 0;
1773 }
1774
1775 for (i=40; i<256 && i<count; i++)
1776 {
1777 entries[i] = 0xff000000;
1778 entries[i] |= halftone_values[(i-40)%6];
1779 entries[i] |= halftone_values[((i-40)/6)%6] << 8;
1780 entries[i] |= halftone_values[((i-40)/36)%6] << 16;
1781 }
1782 }
1783
1784 static GpStatus get_screen_resolution(REAL *xres, REAL *yres)
1785 {
1786 HDC screendc = CreateCompatibleDC(0);
1787
1788 if (!screendc) return GenericError;
1789
1790 *xres = (REAL)GetDeviceCaps(screendc, LOGPIXELSX);
1791 *yres = (REAL)GetDeviceCaps(screendc, LOGPIXELSY);
1792
1793 DeleteDC(screendc);
1794
1795 return Ok;
1796 }
1797
1798 GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width, INT height, INT stride,
1799 PixelFormat format, BYTE* scan0, GpBitmap** bitmap)
1800 {
1801 BITMAPINFO* pbmi;
1802 HBITMAP hbitmap=NULL;
1803 INT row_size, dib_stride;
1804 BYTE *bits=NULL, *own_bits=NULL;
1805 REAL xres, yres;
1806 GpStatus stat;
1807
1808 TRACE("%d %d %d 0x%x %p %p\n", width, height, stride, format, scan0, bitmap);
1809
1810 if (!bitmap) return InvalidParameter;
1811
1812 if(width <= 0 || height <= 0 || (scan0 && (stride % 4))){
1813 *bitmap = NULL;
1814 return InvalidParameter;
1815 }
1816
1817 if(scan0 && !stride)
1818 return InvalidParameter;
1819
1820 stat = get_screen_resolution(&xres, &yres);
1821 if (stat != Ok) return stat;
1822
1823 row_size = (width * PIXELFORMATBPP(format)+7) / 8;
1824 dib_stride = (row_size + 3) & ~3;
1825
1826 if(stride == 0)
1827 stride = dib_stride;
1828
1829 if (format & PixelFormatGDI && !(format & (PixelFormatAlpha|PixelFormatIndexed)) && !scan0)
1830 {
1831 pbmi = GdipAlloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
1832 if (!pbmi)
1833 return OutOfMemory;
1834
1835 pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1836 pbmi->bmiHeader.biWidth = width;
1837 pbmi->bmiHeader.biHeight = -height;
1838 pbmi->bmiHeader.biPlanes = 1;
1839 /* FIXME: use the rest of the data from format */
1840 pbmi->bmiHeader.biBitCount = PIXELFORMATBPP(format);
1841 pbmi->bmiHeader.biCompression = BI_RGB;
1842 pbmi->bmiHeader.biSizeImage = 0;
1843 pbmi->bmiHeader.biXPelsPerMeter = 0;
1844 pbmi->bmiHeader.biYPelsPerMeter = 0;
1845 pbmi->bmiHeader.biClrUsed = 0;
1846 pbmi->bmiHeader.biClrImportant = 0;
1847
1848 hbitmap = CreateDIBSection(0, pbmi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
1849
1850 GdipFree(pbmi);
1851
1852 if (!hbitmap) return GenericError;
1853
1854 stride = dib_stride;
1855 }
1856 else
1857 {
1858 /* Not a GDI format; don't try to make an HBITMAP. */
1859 if (scan0)
1860 bits = scan0;
1861 else
1862 {
1863 INT size = abs(stride) * height;
1864
1865 own_bits = bits = GdipAlloc(size);
1866 if (!own_bits) return OutOfMemory;
1867
1868 if (stride < 0)
1869 bits += stride * (1 - height);
1870 }
1871 }
1872
1873 *bitmap = GdipAlloc(sizeof(GpBitmap));
1874 if(!*bitmap)
1875 {
1876 DeleteObject(hbitmap);
1877 GdipFree(own_bits);
1878 return OutOfMemory;
1879 }
1880
1881 (*bitmap)->image.type = ImageTypeBitmap;
1882 memcpy(&(*bitmap)->image.format, &ImageFormatMemoryBMP, sizeof(GUID));
1883 (*bitmap)->image.flags = ImageFlagsNone;
1884 (*bitmap)->image.frame_count = 1;
1885 (*bitmap)->image.current_frame = 0;
1886 (*bitmap)->image.palette = NULL;
1887 (*bitmap)->image.xres = xres;
1888 (*bitmap)->image.yres = yres;
1889 (*bitmap)->width = width;
1890 (*bitmap)->height = height;
1891 (*bitmap)->format = format;
1892 (*bitmap)->image.picture = NULL;
1893 (*bitmap)->image.stream = NULL;
1894 (*bitmap)->hbitmap = hbitmap;
1895 (*bitmap)->hdc = NULL;
1896 (*bitmap)->bits = bits;
1897 (*bitmap)->stride = stride;
1898 (*bitmap)->own_bits = own_bits;
1899 (*bitmap)->metadata_reader = NULL;
1900 (*bitmap)->prop_count = 0;
1901 (*bitmap)->prop_item = NULL;
1902
1903 /* set format-related flags */
1904 if (format & (PixelFormatAlpha|PixelFormatPAlpha|PixelFormatIndexed))
1905 (*bitmap)->image.flags |= ImageFlagsHasAlpha;
1906
1907 if (format == PixelFormat1bppIndexed ||
1908 format == PixelFormat4bppIndexed ||
1909 format == PixelFormat8bppIndexed)
1910 {
1911 (*bitmap)->image.palette = GdipAlloc(sizeof(UINT) * 2 + sizeof(ARGB) * (1 << PIXELFORMATBPP(format)));
1912
1913 if (!(*bitmap)->image.palette)
1914 {
1915 GdipDisposeImage(&(*bitmap)->image);
1916 *bitmap = NULL;
1917 return OutOfMemory;
1918 }
1919
1920 (*bitmap)->image.palette->Count = 1 << PIXELFORMATBPP(format);
1921
1922 if (format == PixelFormat1bppIndexed)
1923 {
1924 (*bitmap)->image.palette->Flags = PaletteFlagsGrayScale;
1925 (*bitmap)->image.palette->Entries[0] = 0xff000000;
1926 (*bitmap)->image.palette->Entries[1] = 0xffffffff;
1927 }
1928 else
1929 {
1930 if (format == PixelFormat8bppIndexed)
1931 (*bitmap)->image.palette->Flags = PaletteFlagsHalftone;
1932
1933 generate_halftone_palette((*bitmap)->image.palette->Entries,
1934 (*bitmap)->image.palette->Count);
1935 }
1936 }
1937
1938 TRACE("<-- %p\n", *bitmap);
1939
1940 return Ok;
1941 }
1942
1943 GpStatus WINGDIPAPI GdipCreateBitmapFromStream(IStream* stream,
1944 GpBitmap **bitmap)
1945 {
1946 GpStatus stat;
1947
1948 TRACE("%p %p\n", stream, bitmap);
1949
1950 stat = GdipLoadImageFromStream(stream, (GpImage**) bitmap);
1951
1952 if(stat != Ok)
1953 return stat;
1954
1955 if((*bitmap)->image.type != ImageTypeBitmap){
1956 GdipDisposeImage(&(*bitmap)->image);
1957 *bitmap = NULL;
1958 return GenericError; /* FIXME: what error to return? */
1959 }
1960
1961 return Ok;
1962 }
1963
1964 /* FIXME: no icm */
1965 GpStatus WINGDIPAPI GdipCreateBitmapFromStreamICM(IStream* stream,
1966 GpBitmap **bitmap)
1967 {
1968 TRACE("%p %p\n", stream, bitmap);
1969
1970 return GdipCreateBitmapFromStream(stream, bitmap);
1971 }
1972
1973 GpStatus WINGDIPAPI GdipCreateCachedBitmap(GpBitmap *bitmap, GpGraphics *graphics,
1974 GpCachedBitmap **cachedbmp)
1975 {
1976 GpStatus stat;
1977
1978 TRACE("%p %p %p\n", bitmap, graphics, cachedbmp);
1979
1980 if(!bitmap || !graphics || !cachedbmp)
1981 return InvalidParameter;
1982
1983 *cachedbmp = GdipAlloc(sizeof(GpCachedBitmap));
1984 if(!*cachedbmp)
1985 return OutOfMemory;
1986
1987 stat = GdipCloneImage(&(bitmap->image), &(*cachedbmp)->image);
1988 if(stat != Ok){
1989 GdipFree(*cachedbmp);
1990 return stat;
1991 }
1992
1993 return Ok;
1994 }
1995
1996 GpStatus WINGDIPAPI GdipCreateHICONFromBitmap(GpBitmap *bitmap, HICON *hicon)
1997 {
1998 GpStatus stat;
1999 BitmapData lockeddata;
2000 ULONG andstride, xorstride, bitssize;
2001 LPBYTE andbits, xorbits, androw, xorrow, srcrow;
2002 UINT x, y;
2003
2004 TRACE("(%p, %p)\n", bitmap, hicon);
2005
2006 if (!bitmap || !hicon)
2007 return InvalidParameter;
2008
2009 stat = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead,
2010 PixelFormat32bppPARGB, &lockeddata);
2011 if (stat == Ok)
2012 {
2013 andstride = ((lockeddata.Width+31)/32)*4;
2014 xorstride = lockeddata.Width*4;
2015 bitssize = (andstride + xorstride) * lockeddata.Height;
2016
2017 andbits = GdipAlloc(bitssize);
2018
2019 if (andbits)
2020 {
2021 xorbits = andbits + andstride * lockeddata.Height;
2022
2023 for (y=0; y<lockeddata.Height; y++)
2024 {
2025 srcrow = ((LPBYTE)lockeddata.Scan0) + lockeddata.Stride * y;
2026
2027 androw = andbits + andstride * y;
2028 for (x=0; x<lockeddata.Width; x++)
2029 if (srcrow[3+4*x] >= 128)
2030 androw[x/8] |= 1 << (7-x%8);
2031
2032 xorrow = xorbits + xorstride * y;
2033 memcpy(xorrow, srcrow, xorstride);
2034 }
2035
2036 *hicon = CreateIcon(NULL, lockeddata.Width, lockeddata.Height, 1, 32,
2037 andbits, xorbits);
2038
2039 GdipFree(andbits);
2040 }
2041 else
2042 stat = OutOfMemory;
2043
2044 GdipBitmapUnlockBits(bitmap, &lockeddata);
2045 }
2046
2047 return stat;
2048 }
2049
2050 GpStatus WINGDIPAPI GdipDeleteCachedBitmap(GpCachedBitmap *cachedbmp)
2051 {
2052 TRACE("%p\n", cachedbmp);
2053
2054 if(!cachedbmp)
2055 return InvalidParameter;
2056
2057 GdipDisposeImage(cachedbmp->image);
2058 GdipFree(cachedbmp);
2059
2060 return Ok;
2061 }
2062
2063 GpStatus WINGDIPAPI GdipDrawCachedBitmap(GpGraphics *graphics,
2064 GpCachedBitmap *cachedbmp, INT x, INT y)
2065 {
2066 TRACE("%p %p %d %d\n", graphics, cachedbmp, x, y);
2067
2068 if(!graphics || !cachedbmp)
2069 return InvalidParameter;
2070
2071 return GdipDrawImage(graphics, cachedbmp->image, (REAL)x, (REAL)y);
2072 }
2073
2074 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
2075 LPBYTE pData16, INT iMapMode, INT eFlags)
2076 {
2077 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
2078 return NotImplemented;
2079 }
2080
2081 /* Internal utility function: Replace the image data of dst with that of src,
2082 * and free src. */
2083 static void move_bitmap(GpBitmap *dst, GpBitmap *src, BOOL clobber_palette)
2084 {
2085 assert(src->image.type == ImageTypeBitmap);
2086 assert(dst->image.type == ImageTypeBitmap);
2087
2088 GdipFree(dst->bitmapbits);
2089 GdipFree(dst->own_bits);
2090 DeleteDC(dst->hdc);
2091 DeleteObject(dst->hbitmap);
2092
2093 if (clobber_palette)
2094 {
2095 GdipFree(dst->image.palette);
2096 dst->image.palette = src->image.palette;
2097 }
2098 else
2099 GdipFree(src->image.palette);
2100
2101 dst->image.xres = src->image.xres;
2102 dst->image.yres = src->image.yres;
2103 dst->width = src->width;
2104 dst->height = src->height;
2105 dst->format = src->format;
2106 dst->hbitmap = src->hbitmap;
2107 dst->hdc = src->hdc;
2108 dst->bits = src->bits;
2109 dst->stride = src->stride;
2110 dst->own_bits = src->own_bits;
2111 if (dst->metadata_reader)
2112 IWICMetadataReader_Release(dst->metadata_reader);
2113 dst->metadata_reader = src->metadata_reader;
2114 GdipFree(dst->prop_item);
2115 dst->prop_item = src->prop_item;
2116 dst->prop_count = src->prop_count;
2117 if (dst->image.stream)
2118 IStream_Release(dst->image.stream);
2119 dst->image.stream = src->image.stream;
2120 dst->image.frame_count = src->image.frame_count;
2121 dst->image.current_frame = src->image.current_frame;
2122 dst->image.format = src->image.format;
2123
2124 src->image.type = ~0;
2125 GdipFree(src);
2126 }
2127
2128 static GpStatus free_image_data(GpImage *image)
2129 {
2130 if(!image)
2131 return InvalidParameter;
2132
2133 if (image->type == ImageTypeBitmap)
2134 {
2135 GdipFree(((GpBitmap*)image)->bitmapbits);
2136 GdipFree(((GpBitmap*)image)->own_bits);
2137 DeleteDC(((GpBitmap*)image)->hdc);
2138 DeleteObject(((GpBitmap*)image)->hbitmap);
2139 if (((GpBitmap*)image)->metadata_reader)
2140 IWICMetadataReader_Release(((GpBitmap*)image)->metadata_reader);
2141 GdipFree(((GpBitmap*)image)->prop_item);
2142 }
2143 else if (image->type == ImageTypeMetafile)
2144 {
2145 GpMetafile *metafile = (GpMetafile*)image;
2146 GdipFree(metafile->comment_data);
2147 DeleteEnhMetaFile(CloseEnhMetaFile(metafile->record_dc));
2148 if (!metafile->preserve_hemf)
2149 DeleteEnhMetaFile(metafile->hemf);
2150 if (metafile->record_graphics)
2151 {
2152 WARN("metafile closed while recording\n");
2153 /* not sure what to do here; for now just prevent the graphics from functioning or using this object */
2154 metafile->record_graphics->image = NULL;
2155 metafile->record_graphics->busy = TRUE;
2156 }
2157 }
2158 else
2159 {
2160 WARN("invalid image: %p\n", image);
2161 return ObjectBusy;
2162 }
2163 if (image->picture)
2164 IPicture_Release(image->picture);
2165 if (image->stream)
2166 IStream_Release(image->stream);
2167 GdipFree(image->palette);
2168
2169 return Ok;
2170 }
2171
2172 GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image)
2173 {
2174 GpStatus status;
2175
2176 TRACE("%p\n", image);
2177
2178 status = free_image_data(image);
2179 if (status != Ok) return status;
2180 image->type = ~0;
2181 GdipFree(image);
2182
2183 return Ok;
2184 }
2185
2186 GpStatus WINGDIPAPI GdipFindFirstImageItem(GpImage *image, ImageItemData* item)
2187 {
2188 static int calls;
2189
2190 TRACE("(%p,%p)\n", image, item);
2191
2192 if(!image || !item)
2193 return InvalidParameter;
2194
2195 if (!(calls++))
2196 FIXME("not implemented\n");
2197
2198 return NotImplemented;
2199 }
2200
2201 GpStatus WINGDIPAPI GdipGetImageItemData(GpImage *image, ImageItemData *item)
2202 {
2203 static int calls;
2204
2205 TRACE("(%p,%p)\n", image, item);
2206
2207 if (!(calls++))
2208 FIXME("not implemented\n");
2209
2210 return NotImplemented;
2211 }
2212
2213 GpStatus WINGDIPAPI GdipGetImageBounds(GpImage *image, GpRectF *srcRect,
2214 GpUnit *srcUnit)
2215 {
2216 TRACE("%p %p %p\n", image, srcRect, srcUnit);
2217
2218 if(!image || !srcRect || !srcUnit)
2219 return InvalidParameter;
2220 if(image->type == ImageTypeMetafile){
2221 *srcRect = ((GpMetafile*)image)->bounds;
2222 *srcUnit = ((GpMetafile*)image)->unit;
2223 }
2224 else if(image->type == ImageTypeBitmap){
2225 srcRect->X = srcRect->Y = 0.0;
2226 srcRect->Width = (REAL) ((GpBitmap*)image)->width;
2227 srcRect->Height = (REAL) ((GpBitmap*)image)->height;
2228 *srcUnit = UnitPixel;
2229 }
2230 else{
2231 srcRect->X = srcRect->Y = 0.0;
2232 srcRect->Width = ipicture_pixel_width(image->picture);
2233 srcRect->Height = ipicture_pixel_height(image->picture);
2234 *srcUnit = UnitPixel;
2235 }
2236
2237 TRACE("returning (%f, %f) (%f, %f) unit type %d\n", srcRect->X, srcRect->Y,
2238 srcRect->Width, srcRect->Height, *srcUnit);
2239
2240 return Ok;
2241 }
2242
2243 GpStatus WINGDIPAPI GdipGetImageDimension(GpImage *image, REAL *width,
2244 REAL *height)
2245 {
2246 TRACE("%p %p %p\n", image, width, height);
2247
2248 if(!image || !height || !width)
2249 return InvalidParameter;
2250
2251 if(image->type == ImageTypeMetafile){
2252 *height = units_to_pixels(((GpMetafile*)image)->bounds.Height, ((GpMetafile*)image)->unit, image->yres);
2253 *width = units_to_pixels(((GpMetafile*)image)->bounds.Width, ((GpMetafile*)image)->unit, image->xres);
2254 }
2255 else if(image->type == ImageTypeBitmap){
2256 *height = ((GpBitmap*)image)->height;
2257 *width = ((GpBitmap*)image)->width;
2258 }
2259 else{
2260 *height = ipicture_pixel_height(image->picture);
2261 *width = ipicture_pixel_width(image->picture);
2262 }
2263
2264 TRACE("returning (%f, %f)\n", *height, *width);
2265 return Ok;
2266 }
2267
2268 GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image,
2269 GpGraphics **graphics)
2270 {
2271 HDC hdc;
2272 GpStatus stat;
2273
2274 TRACE("%p %p\n", image, graphics);
2275
2276 if(!image || !graphics)
2277 return InvalidParameter;
2278
2279 if (image->type == ImageTypeBitmap && ((GpBitmap*)image)->hbitmap)
2280 {
2281 hdc = ((GpBitmap*)image)->hdc;
2282
2283 if(!hdc){
2284 hdc = CreateCompatibleDC(0);
2285 SelectObject(hdc, ((GpBitmap*)image)->hbitmap);
2286 ((GpBitmap*)image)->hdc = hdc;
2287 }
2288
2289 stat = GdipCreateFromHDC(hdc, graphics);
2290
2291 if (stat == Ok)
2292 {
2293 (*graphics)->image = image;
2294 (*graphics)->xres = image->xres;
2295 (*graphics)->yres = image->yres;
2296 }
2297 }
2298 else if (image->type == ImageTypeMetafile)
2299 stat = METAFILE_GetGraphicsContext((GpMetafile*)image, graphics);
2300 else
2301 stat = graphics_from_image(image, graphics);
2302
2303 return stat;
2304 }
2305
2306 GpStatus WINGDIPAPI GdipGetImageHeight(GpImage *image, UINT *height)
2307 {
2308 TRACE("%p %p\n", image, height);
2309
2310 if(!image || !height)
2311 return InvalidParameter;
2312
2313 if(image->type == ImageTypeMetafile)
2314 *height = units_to_pixels(((GpMetafile*)image)->bounds.Height, ((GpMetafile*)image)->unit, image->yres);
2315 else if(image->type == ImageTypeBitmap)
2316 *height = ((GpBitmap*)image)->height;
2317 else
2318 *height = ipicture_pixel_height(image->picture);
2319
2320 TRACE("returning %d\n", *height);
2321
2322 return Ok;
2323 }
2324
2325 GpStatus WINGDIPAPI GdipGetImageHorizontalResolution(GpImage *image, REAL *res)
2326 {
2327 if(!image || !res)
2328 return InvalidParameter;
2329
2330 *res = image->xres;
2331
2332 TRACE("(%p) <-- %0.2f\n", image, *res);
2333
2334 return Ok;
2335 }
2336
2337 GpStatus WINGDIPAPI GdipGetImagePaletteSize(GpImage *image, INT *size)
2338 {
2339 TRACE("%p %p\n", image, size);
2340
2341 if(!image || !size)
2342 return InvalidParameter;
2343
2344 if (!image->palette || image->palette->Count == 0)
2345 *size = sizeof(ColorPalette);
2346 else
2347 *size = sizeof(UINT)*2 + sizeof(ARGB)*image->palette->Count;
2348
2349 TRACE("<-- %u\n", *size);
2350
2351 return Ok;
2352 }
2353
2354 /* FIXME: test this function for non-bitmap types */
2355 GpStatus WINGDIPAPI GdipGetImagePixelFormat(GpImage *image, PixelFormat *format)
2356 {
2357 TRACE("%p %p\n", image, format);
2358
2359 if(!image || !format)
2360 return InvalidParameter;
2361
2362 if(image->type != ImageTypeBitmap)
2363 *format = PixelFormat24bppRGB;
2364 else
2365 *format = ((GpBitmap*) image)->format;
2366
2367 return Ok;
2368 }
2369
2370 GpStatus WINGDIPAPI GdipGetImageRawFormat(GpImage *image, GUID *format)
2371 {
2372 TRACE("(%p, %p)\n", image, format);
2373
2374 if(!image || !format)
2375 return InvalidParameter;
2376
2377 memcpy(format, &image->format, sizeof(GUID));
2378
2379 return Ok;
2380 }
2381
2382 GpStatus WINGDIPAPI GdipGetImageType(GpImage *image, ImageType *type)
2383 {
2384 TRACE("%p %p\n", image, type);
2385
2386 if(!image || !type)
2387 return InvalidParameter;
2388
2389 *type = image->type;
2390
2391 return Ok;
2392 }
2393
2394 GpStatus WINGDIPAPI GdipGetImageVerticalResolution(GpImage *image, REAL *res)
2395 {
2396 if(!image || !res)
2397 return InvalidParameter;
2398
2399 *res = image->yres;
2400
2401 TRACE("(%p) <-- %0.2f\n", image, *res);
2402
2403 return Ok;
2404 }
2405
2406 GpStatus WINGDIPAPI GdipGetImageWidth(GpImage *image, UINT *width)
2407 {
2408 TRACE("%p %p\n", image, width);
2409
2410 if(!image || !width)
2411 return InvalidParameter;
2412
2413 if(image->type == ImageTypeMetafile)
2414 *width = units_to_pixels(((GpMetafile*)image)->bounds.Width, ((GpMetafile*)image)->unit, image->xres);
2415 else if(image->type == ImageTypeBitmap)
2416 *width = ((GpBitmap*)image)->width;
2417 else
2418 *width = ipicture_pixel_width(image->picture);
2419
2420 TRACE("returning %d\n", *width);
2421
2422 return Ok;
2423 }
2424
2425 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
2426 MetafileHeader * header)
2427 {
2428 static int calls;
2429
2430 TRACE("(%p, %p)\n", metafile, header);
2431
2432 if(!metafile || !header)
2433 return InvalidParameter;
2434
2435 if(!(calls++))
2436 FIXME("not implemented\n");
2437
2438 memset(header, 0, sizeof(MetafileHeader));
2439
2440 return Ok;
2441 }
2442
2443 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hEmf,
2444 MetafileHeader *header)
2445 {
2446 static int calls;
2447
2448 if(!hEmf || !header)
2449 return InvalidParameter;
2450
2451 if(!(calls++))
2452 FIXME("not implemented\n");
2453
2454 memset(header, 0, sizeof(MetafileHeader));
2455
2456 return Ok;
2457 }
2458
2459 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
2460 MetafileHeader *header)
2461 {
2462 static int calls;
2463
2464 TRACE("(%s,%p)\n", debugstr_w(filename), header);
2465
2466 if(!filename || !header)
2467 return InvalidParameter;
2468
2469 if(!(calls++))
2470 FIXME("not implemented\n");
2471
2472 memset(header, 0, sizeof(MetafileHeader));
2473
2474 return Ok;
2475 }
2476
2477 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
2478 MetafileHeader *header)
2479 {
2480 static int calls;
2481
2482 TRACE("(%p,%p)\n", stream, header);
2483
2484 if(!stream || !header)
2485 return InvalidParameter;
2486
2487 if(!(calls++))
2488 FIXME("not implemented\n");
2489
2490 memset(header, 0, sizeof(MetafileHeader));
2491
2492 return Ok;
2493 }
2494
2495 GpStatus WINGDIPAPI GdipGetPropertyCount(GpImage *image, UINT *num)
2496 {
2497 TRACE("(%p, %p)\n", image, num);
2498
2499 if (!image || !num) return InvalidParameter;
2500
2501 *num = 0;
2502
2503 if (image->type == ImageTypeBitmap)
2504 {
2505 if (((GpBitmap *)image)->prop_item)
2506 {
2507 *num = ((GpBitmap *)image)->prop_count;
2508 return Ok;
2509 }
2510
2511 if (((GpBitmap *)image)->metadata_reader)
2512 IWICMetadataReader_GetCount(((GpBitmap *)image)->metadata_reader, num);
2513 }
2514
2515 return Ok;
2516 }
2517
2518 GpStatus WINGDIPAPI GdipGetPropertyIdList(GpImage *image, UINT num, PROPID *list)
2519 {
2520 HRESULT hr;
2521 IWICMetadataReader *reader;
2522 IWICEnumMetadataItem *enumerator;
2523 UINT prop_count, i, items_returned;
2524
2525 TRACE("(%p, %u, %p)\n", image, num, list);
2526
2527 if (!image || !list) return InvalidParameter;
2528
2529 if (image->type != ImageTypeBitmap)
2530 {
2531 FIXME("Not implemented for type %d\n", image->type);
2532 return NotImplemented;
2533 }
2534
2535 if (((GpBitmap *)image)->prop_item)
2536 {
2537 if (num != ((GpBitmap *)image)->prop_count) return InvalidParameter;
2538
2539 for (i = 0; i < num; i++)
2540 {
2541 list[i] = ((GpBitmap *)image)->prop_item[i].id;
2542 }
2543
2544 return Ok;
2545 }
2546
2547 reader = ((GpBitmap *)image)->metadata_reader;
2548 if (!reader)
2549 {
2550 if (num != 0) return InvalidParameter;
2551 return Ok;
2552 }
2553
2554 hr = IWICMetadataReader_GetCount(reader, &prop_count);
2555 if (FAILED(hr)) return hresult_to_status(hr);
2556
2557 if (num != prop_count) return InvalidParameter;
2558
2559 hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
2560 if (FAILED(hr)) return hresult_to_status(hr);
2561
2562 IWICEnumMetadataItem_Reset(enumerator);
2563
2564 for (i = 0; i < num; i++)
2565 {
2566 PROPVARIANT id;
2567
2568 hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, NULL, &items_returned);
2569 if (hr != S_OK) break;
2570
2571 if (id.vt != VT_UI2)
2572 {
2573 FIXME("not supported propvariant type for id: %u\n", id.vt);
2574 list[i] = 0;
2575 continue;
2576 }
2577 list[i] = id.u.uiVal;
2578 }
2579
2580 IWICEnumMetadataItem_Release(enumerator);
2581
2582 return hr == S_OK ? Ok : hresult_to_status(hr);
2583 }
2584
2585 static UINT propvariant_size(PROPVARIANT *value)
2586 {
2587 switch (value->vt & ~VT_VECTOR)
2588 {
2589 case VT_EMPTY:
2590 return 0;
2591 case VT_I1:
2592 case VT_UI1:
2593 if (!(value->vt & VT_VECTOR)) return 1;
2594 return value->u.caub.cElems;
2595 case VT_I2:
2596 case VT_UI2:
2597 if (!(value->vt & VT_VECTOR)) return 2;
2598 return value->u.caui.cElems * 2;
2599 case VT_I4:
2600 case VT_UI4:
2601 case VT_R4:
2602 if (!(value->vt & VT_VECTOR)) return 4;
2603 return value->u.caul.cElems * 4;
2604 case VT_I8:
2605 case VT_UI8:
2606 case VT_R8:
2607 if (!(value->vt & VT_VECTOR)) return 8;
2608 return value->u.cauh.cElems * 8;
2609 case VT_LPSTR:
2610 return value->u.pszVal ? strlen(value->u.pszVal) + 1 : 0;
2611 case VT_BLOB:
2612 return value->u.blob.cbSize;
2613 default:
2614 FIXME("not supported variant type %d\n", value->vt);
2615 return 0;
2616 }
2617 }
2618
2619 GpStatus WINGDIPAPI GdipGetPropertyItemSize(GpImage *image, PROPID propid, UINT *size)
2620 {
2621 HRESULT hr;
2622 IWICMetadataReader *reader;
2623 PROPVARIANT id, value;
2624
2625 TRACE("(%p,%#x,%p)\n", image, propid, size);
2626
2627 if (!size || !image) return InvalidParameter;
2628
2629 if (image->type != ImageTypeBitmap)
2630 {
2631 FIXME("Not implemented for type %d\n", image->type);
2632 return NotImplemented;
2633 }
2634
2635 if (((GpBitmap *)image)->prop_item)
2636 {
2637 UINT i;
2638
2639 for (i = 0; i < ((GpBitmap *)image)->prop_count; i++)
2640 {
2641 if (propid == ((GpBitmap *)image)->prop_item[i].id)
2642 {
2643 *size = sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length;
2644 return Ok;
2645 }
2646 }
2647
2648 return PropertyNotFound;
2649 }
2650
2651 reader = ((GpBitmap *)image)->metadata_reader;
2652 if (!reader) return PropertyNotFound;
2653
2654 id.vt = VT_UI2;
2655 id.u.uiVal = propid;
2656 hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
2657 if (FAILED(hr)) return PropertyNotFound;
2658
2659 *size = propvariant_size(&value);
2660 if (*size) *size += sizeof(PropertyItem);
2661 PropVariantClear(&value);
2662
2663 return Ok;
2664 }
2665
2666 #ifndef PropertyTagTypeSByte
2667 #define PropertyTagTypeSByte 6
2668 #define PropertyTagTypeSShort 8
2669 #define PropertyTagTypeFloat 11
2670 #define PropertyTagTypeDouble 12
2671 #endif
2672
2673 static UINT vt_to_itemtype(UINT vt)
2674 {
2675 static const struct
2676 {
2677 UINT vt, type;
2678 } vt2type[] =
2679 {
2680 { VT_I1, PropertyTagTypeSByte },
2681 { VT_UI1, PropertyTagTypeByte },
2682 { VT_I2, PropertyTagTypeSShort },
2683 { VT_UI2, PropertyTagTypeShort },
2684 { VT_I4, PropertyTagTypeSLONG },
2685 { VT_UI4, PropertyTagTypeLong },
2686 { VT_I8, PropertyTagTypeSRational },
2687 { VT_UI8, PropertyTagTypeRational },
2688 { VT_R4, PropertyTagTypeFloat },
2689 { VT_R8, PropertyTagTypeDouble },
2690 { VT_LPSTR, PropertyTagTypeASCII },
2691 { VT_BLOB, PropertyTagTypeUndefined }
2692 };
2693 UINT i;
2694 for (i = 0; i < sizeof(vt2type)/sizeof(vt2type[0]); i++)
2695 {
2696 if (vt2type[i].vt == vt) return vt2type[i].type;
2697 }
2698 FIXME("not supported variant type %u\n", vt);
2699 return 0;
2700 }
2701
2702 static GpStatus propvariant_to_item(PROPVARIANT *value, PropertyItem *item,
2703 UINT size, PROPID id)
2704 {
2705 UINT item_size, item_type;
2706
2707 item_size = propvariant_size(value);
2708 if (size != item_size + sizeof(PropertyItem)) return InvalidParameter;
2709
2710 item_type = vt_to_itemtype(value->vt & ~VT_VECTOR);
2711 if (!item_type) return InvalidParameter;
2712
2713 item->value = item + 1;
2714
2715 switch (value->vt & ~VT_VECTOR)
2716 {
2717 case VT_I1:
2718 case VT_UI1:
2719 if (!(value->vt & VT_VECTOR))
2720 *(BYTE *)item->value = value->u.bVal;
2721 else
2722 memcpy(item->value, value->u.caub.pElems, item_size);
2723 break;
2724 case VT_I2:
2725 case VT_UI2:
2726 if (!(value->vt & VT_VECTOR))
2727 *(USHORT *)item->value = value->u.uiVal;
2728 else
2729 memcpy(item->value, value->u.caui.pElems, item_size);
2730 break;
2731 case VT_I4:
2732 case VT_UI4:
2733 case VT_R4:
2734 if (!(value->vt & VT_VECTOR))
2735 *(ULONG *)item->value = value->u.ulVal;
2736 else
2737 memcpy(item->value, value->u.caul.pElems, item_size);
2738 break;
2739 case VT_I8:
2740 case VT_UI8:
2741 case VT_R8:
2742 if (!(value->vt & VT_VECTOR))
2743 *(ULONGLONG *)item->value = value->u.uhVal.QuadPart;
2744 else
2745 memcpy(item->value, value->u.cauh.pElems, item_size);
2746 break;
2747 case VT_LPSTR:
2748 memcpy(item->value, value->u.pszVal, item_size);
2749 break;
2750 case VT_BLOB:
2751 memcpy(item->value, value->u.blob.pBlobData, item_size);
2752 break;
2753 default:
2754 FIXME("not supported variant type %d\n", value->vt);
2755 return InvalidParameter;
2756 }
2757
2758 item->length = item_size;
2759 item->type = item_type;
2760 item->id = id;
2761
2762 return Ok;
2763 }
2764
2765 GpStatus WINGDIPAPI GdipGetPropertyItem(GpImage *image, PROPID propid, UINT size,
2766 PropertyItem *buffer)
2767 {
2768 GpStatus stat;
2769 HRESULT hr;
2770 IWICMetadataReader *reader;
2771 PROPVARIANT id, value;
2772
2773 TRACE("(%p,%#x,%u,%p)\n", image, propid, size, buffer);
2774
2775 if (!image || !buffer) return InvalidParameter;
2776
2777 if (image->type != ImageTypeBitmap)
2778 {
2779 FIXME("Not implemented for type %d\n", image->type);
2780 return NotImplemented;
2781 }
2782
2783 if (((GpBitmap *)image)->prop_item)
2784 {
2785 UINT i;
2786
2787 for (i = 0; i < ((GpBitmap *)image)->prop_count; i++)
2788 {
2789 if (propid == ((GpBitmap *)image)->prop_item[i].id)
2790 {
2791 if (size != sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length)
2792 return InvalidParameter;
2793
2794 *buffer = ((GpBitmap *)image)->prop_item[i];
2795 buffer->value = buffer + 1;
2796 memcpy(buffer->value, ((GpBitmap *)image)->prop_item[i].value, buffer->length);
2797 return Ok;
2798 }
2799 }
2800
2801 return PropertyNotFound;
2802 }
2803
2804 reader = ((GpBitmap *)image)->metadata_reader;
2805 if (!reader) return PropertyNotFound;
2806
2807 id.vt = VT_UI2;
2808 id.u.uiVal = propid;
2809 hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
2810 if (FAILED(hr)) return PropertyNotFound;
2811
2812 stat = propvariant_to_item(&value, buffer, size, propid);
2813 PropVariantClear(&value);
2814
2815 return stat;
2816 }
2817
2818 GpStatus WINGDIPAPI GdipGetPropertySize(GpImage *image, UINT *size, UINT *count)
2819 {
2820 HRESULT hr;
2821 IWICMetadataReader *reader;
2822 IWICEnumMetadataItem *enumerator;
2823 UINT prop_count, prop_size, i;
2824 PROPVARIANT id, value;
2825
2826 TRACE("(%p,%p,%p)\n", image, size, count);
2827
2828 if (!image || !size || !count) return InvalidParameter;
2829
2830 if (image->type != ImageTypeBitmap)
2831 {
2832 FIXME("Not implemented for type %d\n", image->type);
2833 return NotImplemented;
2834 }
2835
2836 if (((GpBitmap *)image)->prop_item)
2837 {
2838 *count = ((GpBitmap *)image)->prop_count;
2839 *size = 0;
2840
2841 for (i = 0; i < ((GpBitmap *)image)->prop_count; i++)
2842 {
2843 *size += sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length;
2844 }
2845
2846 return Ok;
2847 }
2848
2849 reader = ((GpBitmap *)image)->metadata_reader;
2850 if (!reader) return PropertyNotFound;
2851
2852 hr = IWICMetadataReader_GetCount(reader, &prop_count);
2853 if (FAILED(hr)) return hresult_to_status(hr);
2854
2855 hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
2856 if (FAILED(hr)) return hresult_to_status(hr);
2857
2858 IWICEnumMetadataItem_Reset(enumerator);
2859
2860 prop_size = 0;
2861
2862 PropVariantInit(&id);
2863 PropVariantInit(&value);
2864
2865 for (i = 0; i < prop_count; i++)
2866 {
2867 UINT items_returned, item_size;
2868
2869 hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, &value, &items_returned);
2870 if (hr != S_OK) break;
2871
2872 item_size = propvariant_size(&value);
2873 if (item_size) prop_size += sizeof(PropertyItem) + item_size;
2874
2875 PropVariantClear(&id);
2876 PropVariantClear(&value);
2877 }
2878
2879 IWICEnumMetadataItem_Release(enumerator);
2880
2881 if (hr != S_OK) return PropertyNotFound;
2882
2883 *count = prop_count;
2884 *size = prop_size;
2885 return Ok;
2886 }
2887
2888 GpStatus WINGDIPAPI GdipGetAllPropertyItems(GpImage *image, UINT size,
2889 UINT count, PropertyItem *buf)
2890 {
2891 GpStatus status;
2892 HRESULT hr;
2893 IWICMetadataReader *reader;
2894 IWICEnumMetadataItem *enumerator;
2895 UINT prop_count, prop_size, i;
2896 PROPVARIANT id, value;
2897 char *item_value;
2898
2899 TRACE("(%p,%u,%u,%p)\n", image, size, count, buf);
2900
2901 if (!image || !buf) return InvalidParameter;
2902
2903 if (image->type != ImageTypeBitmap)
2904 {
2905 FIXME("Not implemented for type %d\n", image->type);
2906 return NotImplemented;
2907 }
2908
2909 status = GdipGetPropertySize(image, &prop_size, &prop_count);
2910 if (status != Ok) return status;
2911
2912 if (prop_count != count || prop_size != size) return InvalidParameter;
2913
2914 if (((GpBitmap *)image)->prop_item)
2915 {
2916 memcpy(buf, ((GpBitmap *)image)->prop_item, prop_size);
2917
2918 item_value = (char *)(buf + prop_count);
2919
2920 for (i = 0; i < prop_count; i++)
2921 {
2922 buf[i].value = item_value;
2923 item_value += buf[i].length;
2924 }
2925
2926 return Ok;
2927 }
2928
2929 reader = ((GpBitmap *)image)->metadata_reader;
2930 if (!reader) return PropertyNotFound;
2931
2932 hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
2933 if (FAILED(hr)) return hresult_to_status(hr);
2934
2935 IWICEnumMetadataItem_Reset(enumerator);
2936
2937 item_value = (char *)(buf + prop_count);
2938
2939 PropVariantInit(&id);
2940 PropVariantInit(&value);
2941
2942 for (i = 0; i < prop_count; i++)
2943 {
2944 PropertyItem *item;
2945 UINT items_returned, item_size;
2946
2947 hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, &value, &items_returned);
2948 if (hr != S_OK) break;
2949
2950 if (id.vt != VT_UI2)
2951 {
2952 FIXME("not supported propvariant type for id: %u\n", id.vt);
2953 continue;
2954 }
2955
2956 item_size = propvariant_size(&value);
2957 if (item_size)
2958 {
2959 item = HeapAlloc(GetProcessHeap(), 0, item_size + sizeof(*item));
2960
2961 propvariant_to_item(&value, item, item_size + sizeof(*item), id.u.uiVal);
2962 buf[i].id = item->id;
2963 buf[i].type = item->type;
2964 buf[i].length = item_size;
2965 buf[i].value = item_value;
2966 memcpy(item_value, item->value, item_size);
2967 item_value += item_size;
2968
2969 HeapFree(GetProcessHeap(), 0, item);
2970 }
2971
2972 PropVariantClear(&id);
2973 PropVariantClear(&value);
2974 }
2975
2976 IWICEnumMetadataItem_Release(enumerator);
2977
2978 if (hr != S_OK) return PropertyNotFound;
2979
2980 return Ok;
2981 }
2982
2983 struct image_format_dimension
2984 {
2985 const GUID *format;
2986 const GUID *dimension;
2987 };
2988
2989 static const struct image_format_dimension image_format_dimensions[] =
2990 {
2991 {&ImageFormatGIF, &FrameDimensionTime},
2992 {&ImageFormatIcon, &FrameDimensionResolution},
2993 {NULL}
2994 };
2995
2996 GpStatus WINGDIPAPI GdipImageGetFrameCount(GpImage *image,
2997 GDIPCONST GUID* dimensionID, UINT* count)
2998 {
2999 TRACE("(%p,%s,%p)\n", image, debugstr_guid(dimensionID), count);
3000
3001 if(!image || !count)
3002 return InvalidParameter;
3003
3004 if (!dimensionID ||
3005 IsEqualGUID(dimensionID, &image->format) ||
3006 IsEqualGUID(dimensionID, &FrameDimensionPage) ||
3007 IsEqualGUID(dimensionID, &FrameDimensionTime))
3008 {
3009 *count = image->frame_count;
3010 return Ok;
3011 }
3012
3013 return InvalidParameter;
3014 }
3015
3016 GpStatus WINGDIPAPI GdipImageGetFrameDimensionsCount(GpImage *image,
3017 UINT* count)
3018 {
3019 TRACE("(%p, %p)\n", image, count);
3020
3021 /* Native gdiplus 1.1 does not yet support multiple frame dimensions. */
3022
3023 if(!image || !count)
3024 return InvalidParameter;
3025
3026 *count = 1;
3027
3028 return Ok;
3029 }
3030
3031 GpStatus WINGDIPAPI GdipImageGetFrameDimensionsList(GpImage* image,
3032 GUID* dimensionIDs, UINT count)
3033 {
3034 int i;
3035 const GUID *result=NULL;
3036
3037 TRACE("(%p,%p,%u)\n", image, dimensionIDs, count);
3038
3039 if(!image || !dimensionIDs || count != 1)
3040 return InvalidParameter;
3041
3042 for (i=0; image_format_dimensions[i].format; i++)
3043 {
3044 if (IsEqualGUID(&image->format, image_format_dimensions[i].format))
3045 {
3046 result = image_format_dimensions[i].dimension;
3047 break;
3048 }
3049 }
3050
3051 if (!result)
3052 result = &FrameDimensionPage;
3053
3054 memcpy(dimensionIDs, result, sizeof(GUID));
3055
3056 return Ok;
3057 }
3058
3059 GpStatus WINGDIPAPI GdipLoadImageFromFile(GDIPCONST WCHAR* filename,
3060 GpImage **image)
3061 {
3062 GpStatus stat;
3063 IStream *stream;
3064
3065 TRACE("(%s) %p\n", debugstr_w(filename), image);
3066
3067 if (!filename || !image)
3068 return InvalidParameter;
3069
3070 stat = GdipCreateStreamOnFile(filename, GENERIC_READ, &stream);
3071
3072 if (stat != Ok)
3073 return stat;
3074
3075 stat = GdipLoadImageFromStream(stream, image);
3076
3077 IStream_Release(stream);
3078
3079 return stat;
3080 }
3081
3082 /* FIXME: no icm handling */
3083 GpStatus WINGDIPAPI GdipLoadImageFromFileICM(GDIPCONST WCHAR* filename,GpImage **image)
3084 {
3085 TRACE("(%s) %p\n", debugstr_w(filename), image);
3086
3087 return GdipLoadImageFromFile(filename, image);
3088 }
3089
3090 static void add_property(GpBitmap *bitmap, PropertyItem *item)
3091 {
3092 UINT prop_size, prop_count;
3093 PropertyItem *prop_item;
3094
3095 if (bitmap->prop_item == NULL)
3096 {
3097 prop_size = prop_count = 0;
3098 prop_item = GdipAlloc(item->length + sizeof(PropertyItem));
3099 if (!prop_item) return;
3100 }
3101 else
3102 {
3103 UINT i;
3104 char *item_value;
3105
3106 GdipGetPropertySize((GpImage *)bitmap, &prop_size, &prop_count);
3107
3108 prop_item = GdipAlloc(prop_size + item->length + sizeof(PropertyItem));
3109 if (!prop_item) return;
3110 memcpy(prop_item, bitmap->prop_item, sizeof(PropertyItem) * bitmap->prop_count);
3111 prop_size -= sizeof(PropertyItem) * bitmap->prop_count;
3112 memcpy(prop_item + prop_count + 1, bitmap->prop_item + prop_count, prop_size);
3113
3114 item_value = (char *)(prop_item + prop_count + 1);
3115
3116 for (i = 0; i < prop_count; i++)
3117 {
3118 prop_item[i].value = item_value;
3119 item_value += prop_item[i].length;
3120 }
3121 }
3122
3123 prop_item[prop_count].id = item->id;
3124 prop_item[prop_count].type = item->type;
3125 prop_item[prop_count].length = item->length;
3126 prop_item[prop_count].value = (char *)(prop_item + prop_count + 1) + prop_size;
3127 memcpy(prop_item[prop_count].value, item->value, item->length);
3128
3129 GdipFree(bitmap->prop_item);
3130 bitmap->prop_item = prop_item;
3131 bitmap->prop_count++;
3132 }
3133
3134 static BOOL get_bool_property(IWICMetadataReader *reader, const GUID *guid, const WCHAR *prop_name)
3135 {
3136 HRESULT hr;
3137 GUID format;
3138 PROPVARIANT id, value;
3139 BOOL ret = FALSE;
3140
3141 IWICMetadataReader_GetMetadataFormat(reader, &format);
3142 if (!IsEqualGUID(&format, guid)) return FALSE;
3143
3144 PropVariantInit(&id);
3145 PropVariantInit(&value);
3146
3147 id.vt = VT_LPWSTR;
3148 id.u.pwszVal = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(prop_name) + 1) * sizeof(WCHAR));
3149 if (!id.u.pwszVal) return FALSE;
3150 lstrcpyW(id.u.pwszVal, prop_name);
3151 hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
3152 if (hr == S_OK && value.vt == VT_BOOL)
3153 ret = value.u.boolVal;
3154
3155 PropVariantClear(&id);
3156 PropVariantClear(&value);
3157
3158 return ret;
3159 }
3160
3161 static PropertyItem *get_property(IWICMetadataReader *reader, const GUID *guid, const WCHAR *prop_name)
3162 {
3163 HRESULT hr;
3164 GUID format;
3165 PROPVARIANT id, value;
3166 PropertyItem *item = NULL;
3167
3168 IWICMetadataReader_GetMetadataFormat(reader, &format);
3169 if (!IsEqualGUID(&format, guid)) return NULL;
3170
3171 PropVariantInit(&id);
3172 PropVariantInit(&value);
3173
3174 id.vt = VT_LPWSTR;
3175 id.u.pwszVal = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(prop_name) + 1) * sizeof(WCHAR));
3176 if (!id.u.pwszVal) return NULL;
3177 lstrcpyW(id.u.pwszVal, prop_name);
3178 hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
3179 if (hr == S_OK)
3180 {
3181 UINT item_size = propvariant_size(&value);
3182 if (item_size)
3183 {
3184 item_size += sizeof(*item);
3185 item = GdipAlloc(item_size);
3186 if (propvariant_to_item(&value, item, item_size, 0) != Ok)
3187 {
3188 GdipFree(item);
3189 item = NULL;
3190 }
3191 }
3192 }
3193
3194 PropVariantClear(&id);
3195 PropVariantClear(&value);
3196
3197 return item;
3198 }
3199
3200 static PropertyItem *get_gif_comment(IWICMetadataReader *reader)
3201 {
3202 static const WCHAR textentryW[] = { 'T','e','x','t','E','n','t','r','y',0 };
3203 PropertyItem *comment;
3204
3205 comment = get_property(reader, &GUID_MetadataFormatGifComment, textentryW);
3206 if (comment)
3207 comment->id = PropertyTagExifUserComment;
3208
3209 return comment;
3210 }
3211
3212 static PropertyItem *get_gif_loopcount(IWICMetadataReader *reader)
3213 {
3214 static const WCHAR applicationW[] = { 'A','p','p','l','i','c','a','t','i','o','n',0 };
3215 static const WCHAR dataW[] = { 'D','a','t','a',0 };
3216 PropertyItem *appext = NULL, *appdata = NULL, *loop = NULL;
3217
3218 appext = get_property(reader, &GUID_MetadataFormatAPE, applicationW);
3219 if (appext)
3220 {
3221 if (appext->type == PropertyTagTypeByte && appext->length == 11 &&
3222 (!memcmp(appext->value, "NETSCAPE2.0", 11) || !memcmp(appext->value, "ANIMEXTS1.0", 11)))
3223 {
3224 appdata = get_property(reader, &GUID_MetadataFormatAPE, dataW);
3225 if (appdata)
3226 {
3227 if (appdata->type == PropertyTagTypeByte && appdata->length == 4)
3228 {
3229 BYTE *data = appdata->value;
3230 if (data[0] == 3 && data[1] == 1)
3231 {
3232 loop = GdipAlloc(sizeof(*loop) + sizeof(SHORT));
3233 if (loop)
3234 {
3235 loop->type = PropertyTagTypeShort;
3236 loop->id = PropertyTagLoopCount;
3237 loop->length = sizeof(SHORT);
3238 loop->value = loop + 1;
3239 *(SHORT *)loop->value = data[2] | (data[3] << 8);
3240 }
3241 }
3242 }
3243 }
3244 }
3245 }
3246
3247 GdipFree(appext);
3248 GdipFree(appdata);
3249
3250 return loop;
3251 }
3252
3253 static PropertyItem *get_gif_background(IWICMetadataReader *reader)
3254 {
3255 static const WCHAR backgroundW[] = { 'B','a','c','k','g','r','o','u','n','d','C','o','l','o','r','I','n','d','e','x',0 };
3256 PropertyItem *background;
3257
3258 background = get_property(reader, &GUID_MetadataFormatLSD, backgroundW);
3259 if (background)
3260 background->id = PropertyTagIndexBackground;
3261
3262 return background;
3263 }
3264
3265 static PropertyItem *get_gif_palette(IWICBitmapDecoder *decoder, IWICMetadataReader *reader)
3266 {
3267 static const WCHAR global_flagW[] = { 'G','l','o','b','a','l','C','o','l','o','r','T','a','b','l','e','F','l','a','g',0 };
3268 HRESULT hr;
3269 IWICImagingFactory *factory;
3270 IWICPalette *palette;
3271 UINT count = 0;
3272 WICColor colors[256];
3273
3274 if (!get_bool_property(reader, &GUID_MetadataFormatLSD, global_flagW))
3275 return NULL;
3276
3277 hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
3278 &IID_IWICImagingFactory, (void **)&factory);
3279 if (hr != S_OK) return NULL;
3280
3281 hr = IWICImagingFactory_CreatePalette(factory, &palette);
3282 if (hr == S_OK)
3283 {
3284 hr = IWICBitmapDecoder_CopyPalette(decoder, palette);
3285 if (hr == S_OK)
3286 IWICPalette_GetColors(palette, 256, colors, &count);
3287
3288 IWICPalette_Release(palette);
3289 }
3290
3291 IWICImagingFactory_Release(factory);
3292
3293 if (count)
3294 {
3295 PropertyItem *pal;
3296 UINT i;
3297 BYTE *rgb;
3298
3299 pal = GdipAlloc(sizeof(*pal) + count * 3);
3300 if (!pal) return NULL;
3301 pal->type = PropertyTagTypeByte;
3302 pal->id = PropertyTagGlobalPalette;
3303 pal->value = pal + 1;
3304 pal->length = count * 3;
3305
3306 rgb = pal->value;
3307
3308 for (i = 0; i < count; i++)
3309 {
3310 rgb[i*3] = (colors[i] >> 16) & 0xff;
3311 rgb[i*3 + 1] = (colors[i] >> 8) & 0xff;
3312 rgb[i*3 + 2] = colors[i] & 0xff;
3313 }
3314
3315 return pal;
3316 }
3317
3318 return NULL;
3319 }
3320
3321 static PropertyItem *get_gif_transparent_idx(IWICMetadataReader *reader)
3322 {
3323 static const WCHAR transparency_flagW[] = { 'T','r','a','n','s','p','a','r','e','n','c','y','F','l','a','g',0 };
3324 static const WCHAR colorW[] = { 'T','r','a','n','s','p','a','r','e','n','t','C','o','l','o','r','I','n','d','e','x',0 };
3325 PropertyItem *index = NULL;
3326
3327 if (get_bool_property(reader, &GUID_MetadataFormatGCE, transparency_flagW))
3328 {
3329 index = get_property(reader, &GUID_MetadataFormatGCE, colorW);
3330 if (index)
3331 index->id = PropertyTagIndexTransparent;
3332 }
3333 return index;
3334 }
3335
3336 static LONG get_gif_frame_delay(IWICBitmapFrameDecode *frame)
3337 {
3338 static const WCHAR delayW[] = { 'D','e','l','a','y',0 };
3339 HRESULT hr;
3340 IWICMetadataBlockReader *block_reader;
3341 IWICMetadataReader *reader;
3342 UINT block_count, i;
3343 PropertyItem *delay;
3344 LONG value = 0;
3345
3346 hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader);
3347 if (hr == S_OK)
3348 {
3349 hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
3350 if (hr == S_OK)
3351 {
3352 for (i = 0; i < block_count; i++)
3353 {
3354 hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
3355 if (hr == S_OK)
3356 {
3357 delay = get_property(reader, &GUID_MetadataFormatGCE, delayW);
3358 if (delay)
3359 {
3360 if (delay->type == PropertyTagTypeShort && delay->length == 2)
3361 value = *(SHORT *)delay->value;
3362
3363 GdipFree(delay);
3364 }
3365 IWICMetadataReader_Release(reader);
3366 }
3367 }
3368 }
3369 IWICMetadataBlockReader_Release(block_reader);
3370 }
3371
3372 return value;
3373 }
3374
3375 static void gif_metadata_reader(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT active_frame)
3376 {
3377 HRESULT hr;
3378 IWICBitmapFrameDecode *frame;
3379 IWICMetadataBlockReader *block_reader;
3380 IWICMetadataReader *reader;
3381 UINT frame_count, block_count, i;
3382 PropertyItem *delay = NULL, *comment = NULL, *background = NULL;
3383 PropertyItem *transparent_idx = NULL, *loop = NULL, *palette = NULL;
3384
3385 IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
3386 if (frame_count > 1)
3387 {
3388 delay = GdipAlloc(sizeof(*delay) + frame_count * sizeof(LONG));
3389 if (delay)
3390 {
3391 LONG *value;
3392
3393 delay->type = PropertyTagTypeLong;
3394 delay->id = PropertyTagFrameDelay;
3395 delay->length = frame_count * sizeof(LONG);
3396 delay->value = delay + 1;
3397
3398 value = delay->value;
3399
3400 for (i = 0; i < frame_count; i++)
3401 {
3402 hr = IWICBitmapDecoder_GetFrame(decoder, i, &frame);
3403 if (hr == S_OK)
3404 {
3405 value[i] = get_gif_frame_delay(frame);
3406 IWICBitmapFrameDecode_Release(frame);
3407 }
3408 else value[i] = 0;
3409 }
3410 }
3411 }
3412
3413 hr = IWICBitmapDecoder_QueryInterface(decoder, &IID_IWICMetadataBlockReader, (void **)&block_reader);
3414 if (hr == S_OK)
3415 {
3416 hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
3417 if (hr == S_OK)
3418 {
3419 for (i = 0; i < block_count; i++)
3420 {
3421 hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
3422 if (hr == S_OK)
3423 {
3424 if (!comment)
3425 comment = get_gif_comment(reader);
3426
3427 if (frame_count > 1 && !loop)
3428 loop = get_gif_loopcount(reader);
3429
3430 if (!background)
3431 background = get_gif_background(reader);
3432
3433 if (!palette)
3434 palette = get_gif_palette(decoder, reader);
3435
3436 IWICMetadataReader_Release(reader);
3437 }
3438 }
3439 }
3440 IWICMetadataBlockReader_Release(block_reader);
3441 }
3442
3443 if (frame_count > 1 && !loop)
3444 {
3445 loop = GdipAlloc(sizeof(*loop) + sizeof(SHORT));
3446 if (loop)
3447 {
3448 loop->type = PropertyTagTypeShort;
3449 loop->id = PropertyTagLoopCount;
3450 loop->length = sizeof(SHORT);
3451 loop->value = loop + 1;
3452 *(SHORT *)loop->value = 1;
3453 }
3454 }
3455
3456 if (delay) add_property(bitmap, delay);
3457 if (comment) add_property(bitmap, comment);
3458 if (loop) add_property(bitmap, loop);
3459 if (palette) add_property(bitmap, palette);
3460 if (background) add_property(bitmap, background);
3461
3462 GdipFree(delay);
3463 GdipFree(comment);
3464 GdipFree(loop);
3465 GdipFree(palette);
3466 GdipFree(background);
3467
3468 /* Win7 gdiplus always returns transparent color index from frame 0 */
3469 hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
3470 if (hr != S_OK) return;
3471
3472 hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader);
3473 if (hr == S_OK)
3474 {
3475 hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
3476 if (hr == S_OK)
3477 {
3478 for (i = 0; i < block_count; i++)
3479 {
3480 hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
3481 if (hr == S_OK)
3482 {
3483 if (!transparent_idx)
3484 transparent_idx = get_gif_transparent_idx(reader);
3485
3486 IWICMetadataReader_Release(reader);
3487 }
3488 }
3489 }
3490 IWICMetadataBlockReader_Release(block_reader);
3491 }
3492
3493 if (transparent_idx) add_property(bitmap, transparent_idx);
3494 GdipFree(transparent_idx);
3495
3496 IWICBitmapFrameDecode_Release(frame);
3497 }
3498
3499 typedef void (*metadata_reader_func)(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT frame);
3500
3501 static GpStatus decode_image_wic(IStream *stream, GDIPCONST CLSID *clsid,
3502 UINT active_frame, metadata_reader_func metadata_reader, GpImage **image)
3503 {
3504 GpStatus status=Ok;
3505 GpBitmap *bitmap;
3506 HRESULT hr;
3507 IWICBitmapDecoder *decoder;
3508 IWICBitmapFrameDecode *frame;
3509 IWICBitmapSource *source=NULL;
3510 IWICMetadataBlockReader *block_reader;
3511 WICPixelFormatGUID wic_format;
3512 PixelFormat gdip_format=0;
3513 ColorPalette *palette = NULL;
3514 WICBitmapPaletteType palette_type = WICBitmapPaletteTypeFixedHalftone256;
3515 int i;
3516 UINT width, height, frame_count;
3517 BitmapData lockeddata;
3518 WICRect wrc;
3519 HRESULT initresult;
3520
3521 TRACE("%p,%s,%u,%p\n", stream, wine_dbgstr_guid(clsid), active_frame, image);
3522
3523 initresult = CoInitialize(NULL);
3524
3525 hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER,
3526 &IID_IWICBitmapDecoder, (void**)&decoder);
3527 if (FAILED(hr)) goto end;
3528
3529 hr = IWICBitmapDecoder_Initialize(decoder, stream, WICDecodeMetadataCacheOnLoad);
3530 if (SUCCEEDED(hr))
3531 {
3532 IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
3533 hr = IWICBitmapDecoder_GetFrame(decoder, active_frame, &frame);
3534 }
3535
3536 if (SUCCEEDED(hr)) /* got frame */
3537 {
3538 hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &wic_format);
3539
3540 if (SUCCEEDED(hr))
3541 {
3542 IWICBitmapSource *bmp_source;
3543 IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICBitmapSource, (void **)&bmp_source);
3544
3545 for (i=0; pixel_formats[i].wic_format; i++)
3546 {
3547 if (IsEqualGUID(&wic_format, pixel_formats[i].wic_format))
3548 {
3549 source = bmp_source;
3550 gdip_format = pixel_formats[i].gdip_format;
3551 palette_type = pixel_formats[i].palette_type;
3552 break;
3553 }
3554 }
3555 if (!source)
3556 {
3557 /* unknown format; fall back on 32bppARGB */
3558 hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, bmp_source, &source);
3559 gdip_format = PixelFormat32bppARGB;
3560 IWICBitmapSource_Release(bmp_source);
3561 }
3562 TRACE("%s => %#x\n", wine_dbgstr_guid(&wic_format), gdip_format);
3563 }
3564
3565 if (SUCCEEDED(hr)) /* got source */
3566 {
3567 hr = IWICBitmapSource_GetSize(source, &width, &height);
3568
3569 if (SUCCEEDED(hr))
3570 status = GdipCreateBitmapFromScan0(width, height, 0, gdip_format,
3571 NULL, &bitmap);
3572
3573 if (SUCCEEDED(hr) && status == Ok) /* created bitmap */
3574 {
3575 status = GdipBitmapLockBits(bitmap, NULL, ImageLockModeWrite,
3576 gdip_format, &lockeddata);
3577 if (status == Ok) /* locked bitmap */
3578 {
3579 wrc.X = 0;
3580 wrc.Width = width;
3581 wrc.Height = 1;
3582 for (i=0; i<height; i++)
3583 {
3584 wrc.Y = i;
3585 hr = IWICBitmapSource_CopyPixels(source, &wrc, abs(lockeddata.Stride),
3586 abs(lockeddata.Stride), (BYTE*)lockeddata.Scan0+lockeddata.Stride*i);
3587 if (FAILED(hr)) break;
3588 }
3589
3590 GdipBitmapUnlockBits(bitmap, &lockeddata);
3591 }
3592
3593 if (SUCCEEDED(hr) && status == Ok)
3594 *image = (GpImage*)bitmap;
3595 else
3596 {
3597 *image = NULL;
3598 GdipDisposeImage((GpImage*)bitmap);
3599 }
3600
3601 if (SUCCEEDED(hr) && status == Ok)
3602 {
3603 double dpix, dpiy;
3604 hr = IWICBitmapSource_GetResolution(source, &dpix, &dpiy);
3605 if (SUCCEEDED(hr))
3606 {
3607 bitmap->image.xres = dpix;
3608 bitmap->image.yres = dpiy;
3609 }
3610 hr = S_OK;
3611 }
3612 }
3613
3614 IWICBitmapSource_Release(source);
3615 }
3616
3617 if (SUCCEEDED(hr)) {
3618 bitmap->metadata_reader = NULL;
3619
3620 if (metadata_reader)
3621 metadata_reader(bitmap, decoder, active_frame);
3622 else if (IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader) == S_OK)
3623 {
3624 UINT block_count = 0;
3625 if (IWICMetadataBlockReader_GetCount(block_reader, &block_count) == S_OK && block_count)
3626 IWICMetadataBlockReader_GetReaderByIndex(block_reader, 0, &bitmap->metadata_reader);
3627 IWICMetadataBlockReader_Release(block_reader);
3628 }
3629
3630 palette = get_palette(frame, palette_type);
3631 IWICBitmapFrameDecode_Release(frame);
3632 }
3633 }
3634
3635 IWICBitmapDecoder_Release(decoder);
3636
3637 end:
3638 if (SUCCEEDED(initresult)) CoUninitialize();
3639
3640 if (FAILED(hr) && status == Ok) status = hresult_to_status(hr);
3641
3642 if (status == Ok)
3643 {
3644 /* Native GDI+ used to be smarter, but since Win7 it just sets these flags. */
3645 bitmap->image.flags |= ImageFlagsReadOnly|ImageFlagsHasRealPixelSize|ImageFlagsHasRealDPI|ImageFlagsColorSpaceRGB;
3646 bitmap->image.frame_count = frame_count;
3647 bitmap->image.current_frame = active_frame;
3648 bitmap->image.stream = stream;
3649 if (palette)
3650 {
3651 GdipFree(bitmap->image.palette);
3652 bitmap->image.palette = palette;
3653 }
3654 else
3655 {
3656 if (IsEqualGUID(&wic_format, &GUID_WICPixelFormatBlackWhite))
3657 bitmap->image.palette->Flags = 0;
3658 }
3659 /* Pin the source stream */
3660 IStream_AddRef(stream);
3661 TRACE("=> %p\n", *image);
3662 }
3663
3664 return status;
3665 }
3666
3667 static GpStatus decode_image_icon(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image)
3668 {
3669 return decode_image_wic(stream, &CLSID_WICIcoDecoder, active_frame, NULL, image);
3670 }
3671
3672 static GpStatus decode_image_bmp(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image)
3673 {
3674 GpStatus status;
3675 GpBitmap* bitmap;
3676
3677 status = decode_image_wic(stream, &CLSID_WICBmpDecoder, active_frame, NULL, image);
3678
3679 bitmap = (GpBitmap*)*image;
3680
3681 if (status == Ok && bitmap->format == PixelFormat32bppARGB)
3682 {
3683 /* WIC supports bmp files with alpha, but gdiplus does not */
3684 bitmap->format = PixelFormat32bppRGB;
3685 }
3686
3687 return status;
3688 }
3689
3690 static GpStatus decode_image_jpeg(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image)
3691 {
3692 return decode_image_wic(stream, &CLSID_WICJpegDecoder, active_frame, NULL, image);
3693 }
3694
3695 static GpStatus decode_image_png(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image)
3696 {
3697 return decode_image_wic(stream, &CLSID_WICPngDecoder, active_frame, NULL, image);
3698 }
3699
3700 static GpStatus decode_image_gif(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image)
3701 {
3702 return decode_image_wic(stream, &CLSID_WICGifDecoder, active_frame, gif_metadata_reader, image);
3703 }
3704
3705 static GpStatus decode_image_tiff(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image)
3706 {
3707 return decode_image_wic(stream, &CLSID_WICTiffDecoder, active_frame, NULL, image);
3708 }
3709
3710 static GpStatus decode_image_olepicture_metafile(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image)
3711 {
3712 IPicture *pic;
3713
3714 TRACE("%p %p\n", stream, image);
3715
3716 if(!stream || !image)
3717 return InvalidParameter;
3718
3719 if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
3720 (LPVOID*) &pic) != S_OK){
3721 TRACE("Could not load picture\n");
3722 return GenericError;
3723 }
3724
3725 /* FIXME: missing initialization code */
3726 *image = GdipAlloc(sizeof(GpMetafile));
3727 if(!*image) return OutOfMemory;
3728 (*image)->type = ImageTypeMetafile;
3729 (*image)->stream = NULL;
3730 (*image)->picture = pic;
3731 (*image)->flags = ImageFlagsNone;
3732 (*image)->frame_count = 1;
3733 (*image)->current_frame = 0;
3734 (*image)->palette = NULL;
3735
3736 TRACE("<-- %p\n", *image);
3737
3738 return Ok;
3739 }
3740
3741 typedef GpStatus (*encode_image_func)(GpImage *image, IStream* stream,
3742 GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params);
3743
3744 typedef GpStatus (*decode_image_func)(IStream *stream, REFCLSID clsid, UINT active_frame, GpImage **image);
3745
3746 typedef struct image_codec {
3747 ImageCodecInfo info;
3748 encode_image_func encode_func;
3749 decode_image_func decode_func;
3750 } image_codec;
3751
3752 typedef enum {
3753 BMP,
3754 JPEG,
3755 GIF,
3756 TIFF,
3757 EMF,
3758 WMF,
3759 PNG,
3760 ICO,
3761 NUM_CODECS
3762 } ImageFormat;
3763
3764 static const struct image_codec codecs[NUM_CODECS];
3765
3766 static GpStatus get_decoder_info(IStream* stream, const struct image_codec **result)
3767 {
3768 BYTE signature[8];
3769 const BYTE *pattern, *mask;
3770 LARGE_INTEGER seek;
3771 HRESULT hr;
3772 UINT bytesread;
3773 int i;
3774 DWORD j, sig;
3775
3776 /* seek to the start of the stream */
3777 seek.QuadPart = 0;
3778 hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
3779 if (FAILED(hr)) return hresult_to_status(hr);
3780
3781 /* read the first 8 bytes */
3782 /* FIXME: This assumes all codecs have signatures <= 8 bytes in length */
3783 hr = IStream_Read(stream, signature, 8, &bytesread);
3784 if (FAILED(hr)) return hresult_to_status(hr);
3785 if (hr == S_FALSE || bytesread == 0) return GenericError;
3786
3787 for (i = 0; i < NUM_CODECS; i++) {
3788 if ((codecs[i].info.Flags & ImageCodecFlagsDecoder) &&
3789 bytesread >= codecs[i].info.SigSize)
3790 {
3791 for (sig=0; sig<codecs[i].info.SigCount; sig++)
3792 {
3793 pattern = &codecs[i].info.SigPattern[codecs[i].info.SigSize*sig];
3794 mask = &codecs[i].info.SigMask[codecs[i].info.SigSize*sig];
3795 for (j=0; j<codecs[i].info.SigSize; j++)
3796 if ((signature[j] & mask[j]) != pattern[j])
3797 break;
3798 if (j == codecs[i].info.SigSize)
3799 {
3800 *result = &codecs[i];
3801 return Ok;
3802 }
3803 }
3804 }
3805 }
3806
3807 TRACE("no match for %i byte signature %x %x %x %x %x %x %x %x\n", bytesread,
3808 signature[0],signature[1],signature[2],signature[3],
3809 signature[4],signature[5],signature[6],signature[7]);
3810
3811 return GenericError;
3812 }
3813
3814 GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image, GDIPCONST GUID *dimensionID,
3815 UINT frame)
3816 {
3817 GpStatus stat;
3818 LARGE_INTEGER seek;
3819 HRESULT hr;
3820 const struct image_codec *codec = NULL;
3821 GpImage *new_image;
3822
3823 TRACE("(%p,%s,%u)\n", image, debugstr_guid(dimensionID), frame);
3824
3825 if (!image || !dimensionID)
3826 return InvalidParameter;
3827
3828 if (frame >= image->frame_count)
3829 {
3830 WARN("requested frame %u, but image has only %u\n", frame, image->frame_count);
3831 return InvalidParameter;
3832 }
3833
3834 if (image->type != ImageTypeBitmap && image->type != ImageTypeMetafile)
3835 {
3836 WARN("invalid image type %d\n", image->type);
3837 return InvalidParameter;
3838 }
3839
3840 if (image->current_frame == frame)
3841 return Ok;
3842
3843 if (!image->stream)
3844 {
3845 TRACE("image doesn't have an associated stream\n");
3846 return Ok;
3847 }
3848
3849 /* choose an appropriate image decoder */
3850 stat = get_decoder_info(image->stream, &codec);
3851 if (stat != Ok)
3852 {
3853 WARN("can't find decoder info\n");
3854 return stat;
3855 }
3856
3857 /* seek to the start of the stream */
3858 seek.QuadPart = 0;
3859 hr = IStream_Seek(image->stream, seek, STREAM_SEEK_SET, NULL);
3860 if (FAILED(hr))
3861 return hresult_to_status(hr);
3862
3863 /* call on the image decoder to do the real work */
3864 stat = codec->decode_func(image->stream, &codec->info.Clsid, frame, &new_image);
3865
3866 if (stat == Ok)
3867 {
3868 memcpy(&new_image->format, &codec->info.FormatID, sizeof(GUID));
3869 free_image_data(image);
3870 if (image->type == ImageTypeBitmap)
3871 *(GpBitmap *)image = *(GpBitmap *)new_image;
3872 else if (image->type == ImageTypeMetafile)
3873 *(GpMetafile *)image = *(GpMetafile *)new_image;
3874 new_image->type = ~0;
3875 GdipFree(new_image);
3876 return Ok;
3877 }
3878
3879 return stat;
3880 }
3881
3882 GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream *stream, GpImage **image)
3883 {
3884 GpStatus stat;
3885 LARGE_INTEGER seek;
3886 HRESULT hr;
3887 const struct image_codec *codec=NULL;
3888
3889 /* choose an appropriate image decoder */
3890 stat = get_decoder_info(stream, &codec);
3891 if (stat != Ok) return stat;
3892
3893 /* seek to the start of the stream */
3894 seek.QuadPart = 0;
3895 hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
3896 if (FAILED(hr)) return hresult_to_status(hr);
3897
3898 /* call on the image decoder to do the real work */
3899 stat = codec->decode_func(stream, &codec->info.Clsid, 0, image);
3900
3901 /* take note of the original data format */
3902 if (stat == Ok)
3903 {
3904 memcpy(&(*image)->format, &codec->info.FormatID, sizeof(GUID));
3905 return Ok;
3906 }
3907
3908 return stat;
3909 }
3910
3911 /* FIXME: no ICM */
3912 GpStatus WINGDIPAPI GdipLoadImageFromStreamICM(IStream* stream, GpImage **image)
3913 {
3914 TRACE("%p %p\n", stream, image);
3915
3916 return GdipLoadImageFromStream(stream, image);
3917 }
3918
3919 GpStatus WINGDIPAPI GdipRemovePropertyItem(GpImage *image, PROPID propId)
3920 {
3921 static int calls;
3922
3923 TRACE("(%p,%u)\n", image, propId);
3924
3925 if(!image)
3926 return InvalidParameter;
3927
3928 if(!(calls++))
3929 FIXME("not implemented\n");
3930
3931 return NotImplemented;
3932 }
3933
3934 GpStatus WINGDIPAPI GdipSetPropertyItem(GpImage *image, GDIPCONST PropertyItem* item)
3935 {
3936 static int calls;
3937
3938 if (!image || !item) return InvalidParameter;
3939
3940 TRACE("(%p,%p:%#x,%u,%u,%p)\n", image, item, item->id, item->type, item->length, item->value);
3941
3942 if(!(calls++))
3943 FIXME("not implemented\n");
3944
3945 return Ok;
3946 }
3947
3948 GpStatus WINGDIPAPI GdipSaveImageToFile(GpImage *image, GDIPCONST WCHAR* filename,
3949 GDIPCONST CLSID *clsidEncoder,
3950 GDIPCONST EncoderParameters *encoderParams)
3951 {
3952 GpStatus stat;
3953 IStream *stream;
3954
3955 TRACE("%p (%s) %p %p\n", image, debugstr_w(filename), clsidEncoder, encoderParams);
3956
3957 if (!image || !filename|| !clsidEncoder)
3958 return InvalidParameter;
3959
3960 stat = GdipCreateStreamOnFile(filename, GENERIC_WRITE, &stream);
3961 if (stat != Ok)
3962 return GenericError;
3963
3964 stat = GdipSaveImageToStream(image, stream, clsidEncoder, encoderParams);
3965
3966 IStream_Release(stream);
3967 return stat;
3968 }
3969
3970 /*************************************************************************
3971 * Encoding functions -
3972 * These functions encode an image in different image file formats.
3973 */
3974 #define BITMAP_FORMAT_BMP 0x4d42 /* "BM" */
3975 #define BITMAP_FORMAT_JPEG 0xd8ff
3976 #define BITMAP_FORMAT_GIF 0x4947
3977 #define BITMAP_FORMAT_PNG 0x5089
3978 #define BITMAP_FORMAT_APM 0xcdd7
3979
3980 static GpStatus encode_image_WIC(GpImage *image, IStream* stream,
3981 GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
3982 {
3983 GpStatus stat;
3984 GpBitmap *bitmap;
3985 IWICBitmapEncoder *encoder;
3986 IWICBitmapFrameEncode *frameencode;
3987 IPropertyBag2 *encoderoptions;
3988 HRESULT hr;
3989 UINT width, height;
3990 PixelFormat gdipformat=0;
3991 WICPixelFormatGUID wicformat;
3992 GpRect rc;
3993 BitmapData lockeddata;
3994 HRESULT initresult;
3995 UINT i;
3996
3997 if (image->type != ImageTypeBitmap)
3998 return GenericError;
3999
4000 bitmap = (GpBitmap*)image;
4001
4002 GdipGetImageWidth(image, &width);
4003 GdipGetImageHeight(image, &height);
4004
4005 rc.X = 0;
4006 rc.Y = 0;
4007 rc.Width = width;
4008 rc.Height = height;
4009
4010 initresult = CoInitialize(NULL);
4011
4012 hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER,
4013 &IID_IWICBitmapEncoder, (void**)&encoder);
4014 if (FAILED(hr))
4015 {
4016 if (SUCCEEDED(initresult)) CoUninitialize();
4017 return hresult_to_status(hr);
4018 }
4019
4020 hr = IWICBitmapEncoder_Initialize(encoder, stream, WICBitmapEncoderNoCache);
4021
4022 if (SUCCEEDED(hr))
4023 {
4024 hr = IWICBitmapEncoder_CreateNewFrame(encoder, &frameencode, &encoderoptions);
4025 }
4026
4027 if (SUCCEEDED(hr)) /* created frame */
4028 {
4029 hr = IWICBitmapFrameEncode_Initialize(frameencode, encoderoptions);
4030
4031 if (SUCCEEDED(hr))
4032 hr = IWICBitmapFrameEncode_SetSize(frameencode, width, height);
4033
4034 if (SUCCEEDED(hr))
4035 hr = IWICBitmapFrameEncode_SetResolution(frameencode, image->xres, image->yres);
4036
4037 if (SUCCEEDED(hr))
4038 {
4039 for (i=0; pixel_formats[i].wic_format; i++)
4040 {
4041 if (pixel_formats[i].gdip_format == bitmap->format)
4042 {
4043 memcpy(&wicformat, pixel_formats[i].wic_format, sizeof(GUID));
4044 gdipformat = bitmap->format;
4045 break;
4046 }
4047 }
4048 if (!gdipformat)
4049 {
4050 memcpy(&wicformat, &GUID_WICPixelFormat32bppBGRA, sizeof(GUID));
4051 gdipformat = PixelFormat32bppARGB;
4052 }
4053
4054 hr = IWICBitmapFrameEncode_SetPixelFormat(frameencode, &wicformat);
4055 }
4056
4057 if (SUCCEEDED(hr))
4058 {
4059 stat = GdipBitmapLockBits(bitmap, &rc, ImageLockModeRead, gdipformat,
4060 &lockeddata);
4061
4062 if (stat == Ok)
4063 {
4064 UINT row_size = (lockeddata.Width * PIXELFORMATBPP(gdipformat) + 7)/8;
4065 BYTE *row;
4066
4067 /* write one row at a time in case stride is negative */
4068 row = lockeddata.Scan0;
4069 for (i=0; i<lockeddata.Height; i++)
4070 {
4071 hr = IWICBitmapFrameEncode_WritePixels(frameencode, 1, row_size, row_size, row);
4072 if (FAILED(hr)) break;
4073 row += lockeddata.Stride;
4074 }
4075
4076 GdipBitmapUnlockBits(bitmap, &lockeddata);
4077 }
4078 else
4079 hr = E_FAIL;
4080 }
4081
4082 if (SUCCEEDED(hr))
4083 hr = IWICBitmapFrameEncode_Commit(frameencode);
4084
4085 IWICBitmapFrameEncode_Release(frameencode);
4086 IPropertyBag2_Release(encoderoptions);
4087 }
4088
4089 if (SUCCEEDED(hr))
4090 hr = IWICBitmapEncoder_Commit(encoder);
4091
4092 IWICBitmapEncoder_Release(encoder);
4093
4094 if (SUCCEEDED(initresult)) CoUninitialize();
4095
4096 return hresult_to_status(hr);
4097 }
4098
4099 static GpStatus encode_image_BMP(GpImage *image, IStream* stream,
4100 GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
4101 {
4102 return encode_image_WIC(image, stream, &CLSID_WICBmpEncoder, params);
4103 }
4104
4105 static GpStatus encode_image_tiff(GpImage *image, IStream* stream,
4106 GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
4107 {
4108 return encode_image_WIC(image, stream, &CLSID_WICTiffEncoder, params);
4109 }
4110
4111 static GpStatus encode_image_png(GpImage *image, IStream* stream,
4112 GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
4113 {
4114 return encode_image_WIC(image, stream, &CLSID_WICPngEncoder, params);
4115 }
4116
4117 static GpStatus encode_image_jpeg(GpImage *image, IStream* stream,
4118 GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
4119 {
4120 return encode_image_WIC(image, stream, &CLSID_WICJpegEncoder, params);
4121 }
4122
4123 /*****************************************************************************
4124 * GdipSaveImageToStream [GDIPLUS.@]
4125 */
4126 GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream,
4127 GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
4128 {
4129 GpStatus stat;
4130 encode_image_func encode_image;
4131 int i;
4132
4133 TRACE("%p %p %p %p\n", image, stream, clsid, params);
4134
4135 if(!image || !stream)
4136 return InvalidParameter;
4137
4138 /* select correct encoder */
4139 encode_image = NULL;
4140 for (i = 0; i < NUM_CODECS; i++) {
4141 if ((codecs[i].info.Flags & ImageCodecFlagsEncoder) &&
4142 IsEqualCLSID(clsid, &codecs[i].info.Clsid))
4143 encode_image = codecs[i].encode_func;
4144 }
4145 if (encode_image == NULL)
4146 return UnknownImageFormat;
4147
4148 stat = encode_image(image, stream, clsid, params);
4149
4150 return stat;
4151 }
4152
4153 /*****************************************************************************
4154 * GdipSaveAdd [GDIPLUS.@]
4155 */
4156 GpStatus WINGDIPAPI GdipSaveAdd(GpImage *image, GDIPCONST EncoderParameters *params)
4157 {
4158 FIXME("(%p,%p): stub\n", image, params);
4159 return Ok;
4160 }
4161
4162 /*****************************************************************************
4163 * GdipGetImagePalette [GDIPLUS.@]
4164 */
4165 GpStatus WINGDIPAPI GdipGetImagePalette(GpImage *image, ColorPalette *palette, INT size)
4166 {
4167 INT count;
4168
4169 TRACE("(%p,%p,%i)\n", image, palette, size);
4170
4171 if (!image || !palette)
4172 return InvalidParameter;
4173
4174 count = image->palette ? image->palette->Count : 0;
4175
4176 if (size < (sizeof(UINT)*2+sizeof(ARGB)*count))
4177 {
4178 TRACE("<-- InsufficientBuffer\n");
4179 return InsufficientBuffer;
4180 }
4181
4182 if (image->palette)
4183 {
4184 palette->Flags = image->palette->Flags;
4185 palette->Count = image->palette->Count;
4186 memcpy(palette->Entries, image->palette->Entries, sizeof(ARGB)*image->palette->Count);
4187 }
4188 else
4189 {
4190 palette->Flags = 0;
4191 palette->Count = 0;
4192 }
4193 return Ok;
4194 }
4195
4196 /*****************************************************************************
4197 * GdipSetImagePalette [GDIPLUS.@]
4198 */
4199 GpStatus WINGDIPAPI GdipSetImagePalette(GpImage *image,
4200 GDIPCONST ColorPalette *palette)
4201 {
4202 ColorPalette *new_palette;
4203
4204 TRACE("(%p,%p)\n", image, palette);
4205
4206 if(!image || !palette || palette->Count > 256)
4207 return InvalidParameter;
4208
4209 new_palette = GdipAlloc(2 * sizeof(UINT) + palette->Count * sizeof(ARGB));
4210 if (!new_palette) return OutOfMemory;
4211
4212 GdipFree(image->palette);
4213 image->palette = new_palette;
4214 image->palette->Flags = palette->Flags;
4215 image->palette->Count = palette->Count;
4216 memcpy(image->palette->Entries, palette->Entries, sizeof(ARGB)*palette->Count);
4217
4218 return Ok;
4219 }
4220
4221 /*************************************************************************
4222 * Encoders -
4223 * Structures that represent which formats we support for encoding.
4224 */
4225
4226 /* ImageCodecInfo creation routines taken from libgdiplus */
4227 static const WCHAR bmp_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'B', 'M', 'P', 0}; /* Built-in BMP */
4228 static const WCHAR bmp_extension[] = {'*','.','B', 'M', 'P',';', '*','.', 'D','I', 'B',';', '*','.', 'R', 'L', 'E',0}; /* *.BMP;*.DIB;*.RLE */
4229 static const WCHAR bmp_mimetype[] = {'i', 'm', 'a','g', 'e', '/', 'b', 'm', 'p', 0}; /* image/bmp */
4230 static const WCHAR bmp_format[] = {'B', 'M', 'P', 0}; /* BMP */
4231 static const BYTE bmp_sig_pattern[] = { 0x42, 0x4D };
4232 static const BYTE bmp_sig_mask[] = { 0xFF, 0xFF };
4233
4234 static const WCHAR jpeg_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'J','P','E','G', 0};
4235 static const WCHAR jpeg_extension[] = {'*','.','J','P','G',';', '*','.','J','P','E','G',';', '*','.','J','P','E',';', '*','.','J','F','I','F',0};
4236 static const WCHAR jpeg_mimetype[] = {'i','m','a','g','e','/','j','p','e','g', 0};
4237 static const WCHAR jpeg_format[] = {'J','P','E','G',0};
4238 static const BYTE jpeg_sig_pattern[] = { 0xFF, 0xD8 };
4239 static const BYTE jpeg_sig_mask[] = { 0xFF, 0xFF };
4240
4241 static const WCHAR gif_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'G','I','F', 0};
4242 static const WCHAR gif_extension[] = {'*','.','G','I','F',0};
4243 static const WCHAR gif_mimetype[] = {'i','m','a','g','e','/','g','i','f', 0};
4244 static const WCHAR gif_format[] = {'G','I','F',0};
4245 static const BYTE gif_sig_pattern[12] = "GIF87aGIF89a";
4246 static const BYTE gif_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
4247
4248 static const WCHAR tiff_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'T','I','F','F', 0};
4249 static const WCHAR tiff_extension[] = {'*','.','T','I','F','F',';','*','.','T','I','F',0};
4250 static const WCHAR tiff_mimetype[] = {'i','m','a','g','e','/','t','i','f','f', 0};
4251 static const WCHAR tiff_format[] = {'T','I','F','F',0};
4252 static const BYTE tiff_sig_pattern[] = {0x49,0x49,42,0,0x4d,0x4d,0,42};
4253 static const BYTE tiff_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
4254
4255 static const WCHAR emf_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'E','M','F', 0};
4256 static const WCHAR emf_extension[] = {'*','.','E','M','F',0};
4257 static const WCHAR emf_mimetype[] = {'i','m','a','g','e','/','x','-','e','m','f', 0};
4258 static const WCHAR emf_format[] = {'E','M','F',0};
4259 static const BYTE emf_sig_pattern[] = { 0x01, 0x00, 0x00, 0x00 };
4260 static const BYTE emf_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF };
4261
4262 static const WCHAR wmf_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'W','M','F', 0};
4263 static const WCHAR wmf_extension[] = {'*','.','W','M','F',0};
4264 static const WCHAR wmf_mimetype[] = {'i','m','a','g','e','/','x','-','w','m','f', 0};
4265 static const WCHAR wmf_format[] = {'W','M','F',0};
4266 static const BYTE wmf_sig_pattern[] = { 0xd7, 0xcd };
4267 static const BYTE wmf_sig_mask[] = { 0xFF, 0xFF };
4268
4269 static const WCHAR png_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'P','N','G', 0};
4270 static const WCHAR png_extension[] = {'*','.','P','N','G',0};
4271 static const WCHAR png_mimetype[] = {'i','m','a','g','e','/','p','n','g', 0};
4272 static const WCHAR png_format[] = {'P','N','G',0};
4273 static const BYTE png_sig_pattern[] = { 137, 80, 78, 71, 13, 10, 26, 10, };
4274 static const BYTE png_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
4275
4276 static const WCHAR ico_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'I','C','O', 0};
4277 static const WCHAR ico_extension[] = {'*','.','I','C','O',0};
4278 static const WCHAR ico_mimetype[] = {'i','m','a','g','e','/','x','-','i','c','o','n', 0};
4279 static const WCHAR ico_format[] = {'I','C','O',0};
4280 static const BYTE ico_sig_pattern[] = { 0x00, 0x00, 0x01, 0x00 };
4281 static const BYTE ico_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF };
4282
4283 static const struct image_codec codecs[NUM_CODECS] = {
4284 {
4285 { /* BMP */
4286 /* Clsid */ { 0x557cf400, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4287 /* FormatID */ { 0xb96b3cabU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4288 /* CodecName */ bmp_codecname,
4289 /* DllName */ NULL,
4290 /* FormatDescription */ bmp_format,
4291 /* FilenameExtension */ bmp_extension,
4292 /* MimeType */ bmp_mimetype,
4293 /* Flags */ ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4294 /* Version */ 1,
4295 /* SigCount */ 1,
4296 /* SigSize */ 2,
4297 /* SigPattern */ bmp_sig_pattern,
4298 /* SigMask */ bmp_sig_mask,
4299 },
4300 encode_image_BMP,
4301 decode_image_bmp
4302 },
4303 {
4304 { /* JPEG */
4305 /* Clsid */ { 0x557cf401, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4306 /* FormatID */ { 0xb96b3caeU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4307 /* CodecName */ jpeg_codecname,
4308 /* DllName */ NULL,
4309 /* FormatDescription */ jpeg_format,
4310 /* FilenameExtension */ jpeg_extension,
4311 /* MimeType */ jpeg_mimetype,
4312 /* Flags */ ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4313 /* Version */ 1,
4314 /* SigCount */ 1,
4315 /* SigSize */ 2,
4316 /* SigPattern */ jpeg_sig_pattern,
4317 /* SigMask */ jpeg_sig_mask,
4318 },
4319 encode_image_jpeg,
4320 decode_image_jpeg
4321 },
4322 {
4323 { /* GIF */
4324 /* Clsid */ { 0x557cf402, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4325 /* FormatID */ { 0xb96b3cb0U, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4326 /* CodecName */ gif_codecname,
4327 /* DllName */ NULL,
4328 /* FormatDescription */ gif_format,
4329 /* FilenameExtension */ gif_extension,
4330 /* MimeType */ gif_mimetype,
4331 /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4332 /* Version */ 1,
4333 /* SigCount */ 2,
4334 /* SigSize */ 6,
4335 /* SigPattern */ gif_sig_pattern,
4336 /* SigMask */ gif_sig_mask,
4337 },
4338 NULL,
4339 decode_image_gif
4340 },
4341 {
4342 { /* TIFF */
4343 /* Clsid */ { 0x557cf405, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4344 /* FormatID */ { 0xb96b3cb1U, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4345 /* CodecName */ tiff_codecname,
4346 /* DllName */ NULL,
4347 /* FormatDescription */ tiff_format,
4348 /* FilenameExtension */ tiff_extension,
4349 /* MimeType */ tiff_mimetype,
4350 /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsEncoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4351 /* Version */ 1,
4352 /* SigCount */ 2,
4353 /* SigSize */ 4,
4354 /* SigPattern */ tiff_sig_pattern,
4355 /* SigMask */ tiff_sig_mask,
4356 },
4357 encode_image_tiff,
4358 decode_image_tiff
4359 },
4360 {
4361 { /* EMF */
4362 /* Clsid */ { 0x557cf403, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4363 /* FormatID */ { 0xb96b3cacU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4364 /* CodecName */ emf_codecname,
4365 /* DllName */ NULL,
4366 /* FormatDescription */ emf_format,
4367 /* FilenameExtension */ emf_extension,
4368 /* MimeType */ emf_mimetype,
4369 /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsSupportVector | ImageCodecFlagsBuiltin,
4370 /* Version */ 1,
4371 /* SigCount */ 1,
4372 /* SigSize */ 4,
4373 /* SigPattern */ emf_sig_pattern,
4374 /* SigMask */ emf_sig_mask,
4375 },
4376 NULL,
4377 decode_image_olepicture_metafile
4378 },
4379 {
4380 { /* WMF */
4381 /* Clsid */ { 0x557cf404, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4382 /* FormatID */ { 0xb96b3cadU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4383 /* CodecName */ wmf_codecname,
4384 /* DllName */ NULL,
4385 /* FormatDescription */ wmf_format,
4386 /* FilenameExtension */ wmf_extension,
4387 /* MimeType */ wmf_mimetype,
4388 /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsSupportVector | ImageCodecFlagsBuiltin,
4389 /* Version */ 1,
4390 /* SigCount */ 1,
4391 /* SigSize */ 2,
4392 /* SigPattern */ wmf_sig_pattern,
4393 /* SigMask */ wmf_sig_mask,
4394 },
4395 NULL,
4396 decode_image_olepicture_metafile
4397 },
4398 {
4399 { /* PNG */
4400 /* Clsid */ { 0x557cf406, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4401 /* FormatID */ { 0xb96b3cafU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4402 /* CodecName */ png_codecname,
4403 /* DllName */ NULL,
4404 /* FormatDescription */ png_format,
4405 /* FilenameExtension */ png_extension,
4406 /* MimeType */ png_mimetype,
4407 /* Flags */ ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4408 /* Version */ 1,
4409 /* SigCount */ 1,
4410 /* SigSize */ 8,
4411 /* SigPattern */ png_sig_pattern,
4412 /* SigMask */ png_sig_mask,
4413 },
4414 encode_image_png,
4415 decode_image_png
4416 },
4417 {
4418 { /* ICO */
4419 /* Clsid */ { 0x557cf407, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
4420 /* FormatID */ { 0xb96b3cabU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
4421 /* CodecName */ ico_codecname,
4422 /* DllName */ NULL,
4423 /* FormatDescription */ ico_format,
4424 /* FilenameExtension */ ico_extension,
4425 /* MimeType */ ico_mimetype,
4426 /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
4427 /* Version */ 1,
4428 /* SigCount */ 1,
4429 /* SigSize */ 4,
4430 /* SigPattern */ ico_sig_pattern,
4431 /* SigMask */ ico_sig_mask,
4432 },
4433 NULL,
4434 decode_image_icon
4435 },
4436 };
4437
4438 /*****************************************************************************
4439 * GdipGetImageDecodersSize [GDIPLUS.@]
4440 */
4441 GpStatus WINGDIPAPI GdipGetImageDecodersSize(UINT *numDecoders, UINT *size)
4442 {
4443 int decoder_count=0;
4444 int i;
4445 TRACE("%p %p\n", numDecoders, size);
4446
4447 if (!numDecoders || !size)
4448 return InvalidParameter;
4449
4450 for (i=0; i<NUM_CODECS; i++)
4451 {
4452 if (codecs[i].info.Flags & ImageCodecFlagsDecoder)
4453 decoder_count++;
4454 }
4455
4456 *numDecoders = decoder_count;
4457 *size = decoder_count * sizeof(ImageCodecInfo);
4458
4459 return Ok;
4460 }
4461
4462 /*****************************************************************************
4463 * GdipGetImageDecoders [GDIPLUS.@]
4464 */
4465 GpStatus WINGDIPAPI GdipGetImageDecoders(UINT numDecoders, UINT size, ImageCodecInfo *decoders)
4466 {
4467 int i, decoder_count=0;
4468 TRACE("%u %u %p\n", numDecoders, size, decoders);
4469
4470 if (!decoders ||
4471 size != numDecoders * sizeof(ImageCodecInfo))
4472 return GenericError;
4473
4474 for (i=0; i<NUM_CODECS; i++)
4475 {
4476 if (codecs[i].info.Flags & ImageCodecFlagsDecoder)
4477 {
4478 if (decoder_count == numDecoders) return GenericError;
4479 memcpy(&decoders[decoder_count], &codecs[i].info, sizeof(ImageCodecInfo));
4480 decoder_count++;
4481 }
4482 }
4483
4484 if (decoder_count < numDecoders) return GenericError;
4485
4486 return Ok;
4487 }
4488
4489 /*****************************************************************************
4490 * GdipGetImageEncodersSize [GDIPLUS.@]
4491 */
4492 GpStatus WINGDIPAPI GdipGetImageEncodersSize(UINT *numEncoders, UINT *size)
4493 {
4494 int encoder_count=0;
4495 int i;
4496 TRACE("%p %p\n", numEncoders, size);
4497
4498 if (!numEncoders || !size)
4499 return InvalidParameter;
4500
4501 for (i=0; i<NUM_CODECS; i++)
4502 {
4503 if (codecs[i].info.Flags & ImageCodecFlagsEncoder)
4504 encoder_count++;
4505 }
4506
4507 *numEncoders = encoder_count;
4508 *size = encoder_count * sizeof(ImageCodecInfo);
4509
4510 return Ok;
4511 }
4512
4513 /*****************************************************************************
4514 * GdipGetImageEncoders [GDIPLUS.@]
4515 */
4516 GpStatus WINGDIPAPI GdipGetImageEncoders(UINT numEncoders, UINT size, ImageCodecInfo *encoders)
4517 {
4518 int i, encoder_count=0;
4519 TRACE("%u %u %p\n", numEncoders, size, encoders);
4520
4521 if (!encoders ||
4522 size != numEncoders * sizeof(ImageCodecInfo))
4523 return GenericError;
4524
4525 for (i=0; i<NUM_CODECS; i++)
4526 {
4527 if (codecs[i].info.Flags & ImageCodecFlagsEncoder)
4528 {
4529 if (encoder_count == numEncoders) return GenericError;
4530 memcpy(&encoders[encoder_count], &codecs[i].info, sizeof(ImageCodecInfo));
4531 encoder_count++;
4532 }
4533 }
4534
4535 if (encoder_count < numEncoders) return GenericError;
4536
4537 return Ok;
4538 }
4539
4540 GpStatus WINGDIPAPI GdipGetEncoderParameterListSize(GpImage *image,
4541 GDIPCONST CLSID* clsidEncoder, UINT *size)
4542 {
4543 static int calls;
4544
4545 TRACE("(%p,%s,%p)\n", image, debugstr_guid(clsidEncoder), size);
4546
4547 if(!(calls++))
4548 FIXME("not implemented\n");
4549
4550 *size = 0;
4551
4552 return NotImplemented;
4553 }
4554
4555 static PixelFormat get_16bpp_format(HBITMAP hbm)
4556 {
4557 BITMAPV4HEADER bmh;
4558 HDC hdc;
4559 PixelFormat result;
4560
4561 hdc = CreateCompatibleDC(NULL);
4562
4563 memset(&bmh, 0, sizeof(bmh));
4564 bmh.bV4Size = sizeof(bmh);
4565 bmh.bV4Width = 1;
4566 bmh.bV4Height = 1;
4567 bmh.bV4V4Compression = BI_BITFIELDS;
4568 bmh.bV4BitCount = 16;
4569
4570 GetDIBits(hdc, hbm, 0, 0, NULL, (BITMAPINFO*)&bmh, DIB_RGB_COLORS);
4571
4572 if (bmh.bV4RedMask == 0x7c00 &&
4573 bmh.bV4GreenMask == 0x3e0 &&
4574 bmh.bV4BlueMask == 0x1f)
4575 {
4576 result = PixelFormat16bppRGB555;
4577 }
4578 else if (bmh.bV4RedMask == 0xf800 &&
4579 bmh.bV4GreenMask == 0x7e0 &&
4580 bmh.bV4BlueMask == 0x1f)
4581 {
4582 result = PixelFormat16bppRGB565;
4583 }
4584 else
4585 {
4586 FIXME("unrecognized bitfields %x,%x,%x\n", bmh.bV4RedMask,
4587 bmh.bV4GreenMask, bmh.bV4BlueMask);
4588 result = PixelFormatUndefined;
4589 }
4590
4591 DeleteDC(hdc);
4592
4593 return result;
4594 }
4595
4596 /*****************************************************************************
4597 * GdipCreateBitmapFromHBITMAP [GDIPLUS.@]
4598 */
4599 GpStatus WINGDIPAPI GdipCreateBitmapFromHBITMAP(HBITMAP hbm, HPALETTE hpal, GpBitmap** bitmap)
4600 {
4601 BITMAP bm;
4602 GpStatus retval;
4603 PixelFormat format;
4604 BitmapData lockeddata;
4605
4606 TRACE("%p %p %p\n", hbm, hpal, bitmap);
4607
4608 if(!hbm || !bitmap)
4609 return InvalidParameter;
4610
4611 if (GetObjectA(hbm, sizeof(bm), &bm) != sizeof(bm))
4612 return InvalidParameter;
4613
4614 /* TODO: Figure out the correct format for 16, 32, 64 bpp */
4615 switch(bm.bmBitsPixel) {
4616 case 1:
4617 format = PixelFormat1bppIndexed;
4618 break;
4619 case 4:
4620 format = PixelFormat4bppIndexed;
4621 break;
4622 case 8:
4623 format = PixelFormat8bppIndexed;
4624 break;
4625 case 16:
4626 format = get_16bpp_format(hbm);
4627 if (format == PixelFormatUndefined)
4628 return InvalidParameter;
4629 break;
4630 case 24:
4631 format = PixelFormat24bppRGB;
4632 break;
4633 case 32:
4634 format = PixelFormat32bppRGB;
4635 break;
4636 case 48:
4637 format = PixelFormat48bppRGB;
4638 break;
4639 default:
4640 FIXME("don't know how to handle %d bpp\n", bm.bmBitsPixel);
4641 return InvalidParameter;
4642 }
4643
4644 retval = GdipCreateBitmapFromScan0(bm.bmWidth, bm.bmHeight, 0,
4645 format, NULL, bitmap);
4646
4647 if (retval == Ok)
4648 {
4649 retval = GdipBitmapLockBits(*bitmap, NULL, ImageLockModeWrite,
4650 format, &lockeddata);
4651 if (retval == Ok)
4652 {
4653 HDC hdc;
4654 BITMAPINFO *pbmi;
4655 INT src_height;
4656
4657 hdc = CreateCompatibleDC(NULL);
4658 pbmi = GdipAlloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
4659
4660 if (pbmi)
4661 {
4662 pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
4663 pbmi->bmiHeader.biBitCount = 0;
4664
4665 GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);
4666
4667 src_height = abs(pbmi->bmiHeader.biHeight);
4668 pbmi->bmiHeader.biHeight = -src_height;
4669
4670 GetDIBits(hdc, hbm, 0, src_height, lockeddata.Scan0, pbmi, DIB_RGB_COLORS);
4671
4672 GdipFree(pbmi);
4673 }
4674 else
4675 retval = OutOfMemory;
4676
4677 DeleteDC(hdc);
4678
4679 GdipBitmapUnlockBits(*bitmap, &lockeddata);
4680 }
4681
4682 if (retval == Ok && hpal)
4683 {
4684 PALETTEENTRY entry[256];
4685 ColorPalette *palette=NULL;
4686 int i, num_palette_entries;
4687
4688 num_palette_entries = GetPaletteEntries(hpal, 0, 256, entry);
4689 if (!num_palette_entries)
4690 retval = GenericError;
4691
4692 palette = GdipAlloc(sizeof(ColorPalette) + sizeof(ARGB) * (num_palette_entries-1));
4693 if (!palette)
4694 retval = OutOfMemory;
4695
4696 if (retval == Ok)
4697 {
4698 palette->Flags = 0;
4699 palette->Count = num_palette_entries;
4700
4701 for (i=0; i<num_palette_entries; i++)
4702 {
4703 palette->Entries[i] = 0xff000000 | entry[i].peRed << 16 |
4704 entry[i].peGreen << 8 | entry[i].peBlue;
4705 }
4706
4707 retval = GdipSetImagePalette((GpImage*)*bitmap, palette);
4708 }
4709
4710 GdipFree(palette);
4711 }
4712
4713 if (retval != Ok)
4714 {
4715 GdipDisposeImage((GpImage*)*bitmap);
4716 *bitmap = NULL;
4717 }
4718 }
4719
4720 return retval;
4721 }
4722
4723 GpStatus WINGDIPAPI GdipDeleteEffect(CGpEffect *effect)
4724 {
4725 FIXME("(%p): stub\n", effect);
4726 /* note: According to Jose Roca's GDI+ Docs, this is not implemented
4727 * in Windows's gdiplus */
4728 return NotImplemented;
4729 }
4730
4731 /*****************************************************************************
4732 * GdipSetEffectParameters [GDIPLUS.@]
4733 */
4734 GpStatus WINGDIPAPI GdipSetEffectParameters(CGpEffect *effect,
4735 const VOID *params, const UINT size)
4736 {
4737 static int calls;
4738
4739 TRACE("(%p,%p,%u)\n", effect, params, size);
4740
4741 if(!(calls++))
4742 FIXME("not implemented\n");
4743
4744 return NotImplemented;
4745 }
4746
4747 /*****************************************************************************
4748 * GdipGetImageFlags [GDIPLUS.@]
4749 */
4750 GpStatus WINGDIPAPI GdipGetImageFlags(GpImage *image, UINT *flags)
4751 {
4752 TRACE("%p %p\n", image, flags);
4753
4754 if(!image || !flags)
4755 return InvalidParameter;
4756
4757 *flags = image->flags;
4758
4759 return Ok;
4760 }
4761
4762 GpStatus WINGDIPAPI GdipTestControl(GpTestControlEnum control, void *param)
4763 {
4764 TRACE("(%d, %p)\n", control, param);
4765
4766 switch(control){
4767 case TestControlForceBilinear:
4768 if(param)
4769 FIXME("TestControlForceBilinear not handled\n");
4770 break;
4771 case TestControlNoICM:
4772 if(param)
4773 FIXME("TestControlNoICM not handled\n");
4774 break;
4775 case TestControlGetBuildNumber:
4776 *((DWORD*)param) = 3102;
4777 break;
4778 }
4779
4780 return Ok;
4781 }
4782
4783 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
4784 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
4785 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
4786 GpMetafile **metafile)
4787 {
4788 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
4789 frameUnit, debugstr_w(desc), metafile);
4790
4791 return NotImplemented;
4792 }
4793
4794 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
4795 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
4796 GDIPCONST WCHAR *desc, GpMetafile **metafile)
4797 {
4798 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
4799 frameUnit, debugstr_w(desc), metafile);
4800
4801 return NotImplemented;
4802 }
4803
4804 GpStatus WINGDIPAPI GdipImageForceValidation(GpImage *image)
4805 {
4806 TRACE("%p\n", image);
4807
4808 return Ok;
4809 }
4810
4811 /*****************************************************************************
4812 * GdipGetImageThumbnail [GDIPLUS.@]
4813 */
4814 GpStatus WINGDIPAPI GdipGetImageThumbnail(GpImage *image, UINT width, UINT height,
4815 GpImage **ret_image, GetThumbnailImageAbort cb,
4816 VOID * cb_data)
4817 {
4818 GpStatus stat;
4819 GpGraphics *graphics;
4820 UINT srcwidth, srcheight;
4821
4822 TRACE("(%p %u %u %p %p %p)\n",
4823 image, width, height, ret_image, cb, cb_data);
4824
4825 if (!image || !ret_image)
4826 return InvalidParameter;
4827
4828 if (!width) width = 120;
4829 if (!height) height = 120;
4830
4831 GdipGetImageWidth(image, &srcwidth);
4832 GdipGetImageHeight(image, &srcheight);
4833
4834 stat = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppPARGB,
4835 NULL, (GpBitmap**)ret_image);
4836
4837 if (stat == Ok)
4838 {
4839 stat = GdipGetImageGraphicsContext(*ret_image, &graphics);
4840
4841 if (stat == Ok)
4842 {
4843 stat = GdipDrawImageRectRectI(graphics, image,
4844 0, 0, width, height, 0, 0, srcwidth, srcheight, UnitPixel,
4845 NULL, NULL, NULL);
4846
4847 GdipDeleteGraphics(graphics);
4848 }
4849
4850 if (stat != Ok)
4851 {
4852 GdipDisposeImage(*ret_image);
4853 *ret_image = NULL;
4854 }
4855 }
4856
4857 return stat;
4858 }
4859
4860 /*****************************************************************************
4861 * GdipImageRotateFlip [GDIPLUS.@]
4862 */
4863 GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type)
4864 {
4865 GpBitmap *new_bitmap;
4866 GpBitmap *bitmap;
4867 int bpp, bytesperpixel;
4868 int rotate_90, flip_x, flip_y;
4869 int src_x_offset, src_y_offset;
4870 LPBYTE src_origin;
4871 UINT x, y, width, height;
4872 BitmapData src_lock, dst_lock;
4873 GpStatus stat;
4874
4875 TRACE("(%p, %u)\n", image, type);
4876
4877 if (!image)
4878 return InvalidParameter;
4879
4880 rotate_90 = type&1;
4881 flip_x = (type&6) == 2 || (type&6) == 4;
4882 flip_y = (type&3) == 1 || (type&3) == 2;
4883
4884 if (image->type != ImageTypeBitmap)
4885 {
4886 FIXME("Not implemented for type %i\n", image->type);
4887 return NotImplemented;
4888 }
4889
4890 bitmap = (GpBitmap*)image;
4891 bpp = PIXELFORMATBPP(bitmap->format);
4892
4893 if (bpp < 8)
4894 {
4895 FIXME("Not implemented for %i bit images\n", bpp);
4896 return NotImplemented;
4897 }
4898
4899 if (rotate_90)
4900 {
4901 width = bitmap->height;
4902 height = bitmap->width;
4903 }
4904 else
4905 {
4906 width = bitmap->width;
4907 height = bitmap->height;
4908 }
4909
4910 bytesperpixel = bpp/8;
4911
4912 stat = GdipCreateBitmapFromScan0(width, height, 0, bitmap->format, NULL, &new_bitmap);
4913
4914 if (stat != Ok)
4915 return stat;
4916
4917 stat = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, bitmap->format, &src_lock);
4918
4919 if (stat == Ok)
4920 {
4921 stat = GdipBitmapLockBits(new_bitmap, NULL, ImageLockModeWrite, bitmap->format, &dst_lock);
4922
4923 if (stat == Ok)
4924 {
4925 LPBYTE src_row, src_pixel;
4926 LPBYTE dst_row, dst_pixel;
4927
4928 src_origin = src_lock.Scan0;
4929 if (flip_x) src_origin += bytesperpixel * (bitmap->width - 1);
4930 if (flip_y) src_origin += src_lock.Stride * (bitmap->height - 1);
4931
4932 if (rotate_90)
4933 {
4934 if (flip_y) src_x_offset = -src_lock.Stride;
4935 else src_x_offset = src_lock.Stride;
4936 if (flip_x) src_y_offset = -bytesperpixel;
4937 else src_y_offset = bytesperpixel;
4938 }
4939 else
4940 {
4941 if (flip_x) src_x_offset = -bytesperpixel;
4942 else src_x_offset = bytesperpixel;
4943 if (flip_y) src_y_offset = -src_lock.Stride;
4944 else src_y_offset = src_lock.Stride;
4945 }
4946
4947 src_row = src_origin;
4948 dst_row = dst_lock.Scan0;
4949 for (y=0; y<height; y++)
4950 {
4951 src_pixel = src_row;
4952 dst_pixel = dst_row;
4953 for (x=0; x<width; x++)
4954 {
4955 /* FIXME: This could probably be faster without memcpy. */
4956 memcpy(dst_pixel, src_pixel, bytesperpixel);
4957 dst_pixel += bytesperpixel;
4958 src_pixel += src_x_offset;
4959 }
4960 src_row += src_y_offset;
4961 dst_row += dst_lock.Stride;
4962 }
4963
4964 GdipBitmapUnlockBits(new_bitmap, &dst_lock);
4965 }
4966
4967 GdipBitmapUnlockBits(bitmap, &src_lock);
4968 }
4969
4970 if (stat == Ok)
4971 move_bitmap(bitmap, new_bitmap, FALSE);
4972 else
4973 GdipDisposeImage((GpImage*)new_bitmap);
4974
4975 return stat;
4976 }
4977
4978 /*****************************************************************************
4979 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
4980 */
4981
4982 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
4983 GpMetafile* metafile, BOOL* conversionSuccess,
4984 const WCHAR* filename, EmfType emfType,
4985 const WCHAR* description, GpMetafile** out_metafile)
4986 {
4987 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
4988 return NotImplemented;
4989 }