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