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 BOOL has_png_transparency_chunk(IStream *pIStream) 3959 { 3960 LARGE_INTEGER seek; 3961 BOOL has_tRNS = FALSE; 3962 HRESULT hr; 3963 BYTE header[8]; 3964 3965 seek.QuadPart = 8; 3966 do 3967 { 3968 ULARGE_INTEGER chunk_start; 3969 ULONG bytesread, chunk_size; 3970 3971 hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, &chunk_start); 3972 if (FAILED(hr)) break; 3973 3974 hr = IStream_Read(pIStream, header, 8, &bytesread); 3975 if (FAILED(hr) || bytesread < 8) break; 3976 3977 chunk_size = (header[0] << 24) | (header[1] << 16) | (header[2] << 8) | header[3]; 3978 if (!memcmp(&header[4], "tRNS", 4)) 3979 { 3980 has_tRNS = TRUE; 3981 break; 3982 } 3983 3984 seek.QuadPart = chunk_start.QuadPart + chunk_size + 12; /* skip data and CRC */ 3985 } while (memcmp(&header[4], "IDAT", 4) && memcmp(&header[4], "IEND", 4)); 3986 3987 TRACE("has_tRNS = %d\n", has_tRNS); 3988 return has_tRNS; 3989 } 3990 3991 static GpStatus decode_image_png(IStream* stream, GpImage **image) 3992 { 3993 IWICBitmapDecoder *decoder; 3994 IWICBitmapFrameDecode *frame; 3995 GpStatus status; 3996 HRESULT hr; 3997 GUID format; 3998 BOOL force_conversion = FALSE; 3999 4000 status = initialize_decoder_wic(stream, &GUID_ContainerFormatPng, &decoder); 4001 if (status != Ok) 4002 return status; 4003 4004 hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame); 4005 if (hr == S_OK) 4006 { 4007 hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &format); 4008 if (hr == S_OK) 4009 { 4010 if (IsEqualGUID(&format, &GUID_WICPixelFormat8bppGray)) 4011 force_conversion = TRUE; 4012 else if ((IsEqualGUID(&format, &GUID_WICPixelFormat8bppIndexed) || 4013 IsEqualGUID(&format, &GUID_WICPixelFormat4bppIndexed) || 4014 IsEqualGUID(&format, &GUID_WICPixelFormat2bppIndexed) || 4015 IsEqualGUID(&format, &GUID_WICPixelFormat1bppIndexed) || 4016 IsEqualGUID(&format, &GUID_WICPixelFormat24bppBGR)) && 4017 has_png_transparency_chunk(stream)) 4018 force_conversion = TRUE; 4019 4020 status = decode_frame_wic(decoder, force_conversion, 0, png_metadata_reader, image); 4021 } 4022 else 4023 status = hresult_to_status(hr); 4024 4025 IWICBitmapFrameDecode_Release(frame); 4026 } 4027 else 4028 status = hresult_to_status(hr); 4029 4030 IWICBitmapDecoder_Release(decoder); 4031 return status; 4032 } 4033 4034 static GpStatus decode_image_gif(IStream* stream, GpImage **image) 4035 { 4036 IWICBitmapDecoder *decoder; 4037 UINT frame_count; 4038 GpStatus status; 4039 HRESULT hr; 4040 4041 status = initialize_decoder_wic(stream, &GUID_ContainerFormatGif, &decoder); 4042 if(status != Ok) 4043 return status; 4044 4045 hr = IWICBitmapDecoder_GetFrameCount(decoder, &frame_count); 4046 if(FAILED(hr)) 4047 return hresult_to_status(hr); 4048 4049 status = decode_frame_wic(decoder, frame_count > 1, 0, gif_metadata_reader, image); 4050 IWICBitmapDecoder_Release(decoder); 4051 if(status != Ok) 4052 return status; 4053 4054 if(frame_count > 1) { 4055 heap_free((*image)->palette); 4056 (*image)->palette = NULL; 4057 } 4058 return Ok; 4059 } 4060 4061 static GpStatus decode_image_tiff(IStream* stream, GpImage **image) 4062 { 4063 return decode_image_wic(stream, &GUID_ContainerFormatTiff, NULL, image); 4064 } 4065 4066 static GpStatus load_wmf(IStream *stream, GpMetafile **metafile) 4067 { 4068 WmfPlaceableFileHeader pfh; 4069 BOOL is_placeable = FALSE; 4070 LARGE_INTEGER seek; 4071 GpStatus status; 4072 METAHEADER mh; 4073 HMETAFILE hmf; 4074 HRESULT hr; 4075 UINT size; 4076 void *buf; 4077 4078 hr = IStream_Read(stream, &mh, sizeof(mh), &size); 4079 if (hr != S_OK || size != sizeof(mh)) 4080 return GenericError; 4081 4082 if (((WmfPlaceableFileHeader *)&mh)->Key == WMF_PLACEABLE_KEY) 4083 { 4084 seek.QuadPart = 0; 4085 hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL); 4086 if (FAILED(hr)) return hresult_to_status(hr); 4087 4088 hr = IStream_Read(stream, &pfh, sizeof(pfh), &size); 4089 if (hr != S_OK || size != sizeof(pfh)) 4090 return GenericError; 4091 4092 hr = IStream_Read(stream, &mh, sizeof(mh), &size); 4093 if (hr != S_OK || size != sizeof(mh)) 4094 return GenericError; 4095 4096 is_placeable = TRUE; 4097 } 4098 4099 seek.QuadPart = is_placeable ? sizeof(pfh) : 0; 4100 hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL); 4101 if (FAILED(hr)) return hresult_to_status(hr); 4102 4103 buf = heap_alloc(mh.mtSize * 2); 4104 if (!buf) return OutOfMemory; 4105 4106 hr = IStream_Read(stream, buf, mh.mtSize * 2, &size); 4107 if (hr != S_OK || size != mh.mtSize * 2) 4108 { 4109 heap_free(buf); 4110 return GenericError; 4111 } 4112 4113 hmf = SetMetaFileBitsEx(mh.mtSize * 2, buf); 4114 heap_free(buf); 4115 if (!hmf) 4116 return GenericError; 4117 4118 status = GdipCreateMetafileFromWmf(hmf, TRUE, is_placeable ? &pfh : NULL, metafile); 4119 if (status != Ok) 4120 DeleteMetaFile(hmf); 4121 return status; 4122 } 4123 4124 static GpStatus decode_image_wmf(IStream *stream, GpImage **image) 4125 { 4126 GpMetafile *metafile; 4127 GpStatus status; 4128 4129 TRACE("%p %p\n", stream, image); 4130 4131 if (!stream || !image) 4132 return InvalidParameter; 4133 4134 status = load_wmf(stream, &metafile); 4135 if (status != Ok) 4136 { 4137 TRACE("Could not load metafile\n"); 4138 return status; 4139 } 4140 4141 *image = (GpImage *)metafile; 4142 TRACE("<-- %p\n", *image); 4143 4144 return Ok; 4145 } 4146 4147 static GpStatus load_emf(IStream *stream, GpMetafile **metafile) 4148 { 4149 LARGE_INTEGER seek; 4150 ENHMETAHEADER emh; 4151 HENHMETAFILE hemf; 4152 GpStatus status; 4153 HRESULT hr; 4154 UINT size; 4155 void *buf; 4156 4157 hr = IStream_Read(stream, &emh, sizeof(emh), &size); 4158 if (hr != S_OK || size != sizeof(emh) || emh.dSignature != ENHMETA_SIGNATURE) 4159 return GenericError; 4160 4161 seek.QuadPart = 0; 4162 hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL); 4163 if (FAILED(hr)) return hresult_to_status(hr); 4164 4165 buf = heap_alloc(emh.nBytes); 4166 if (!buf) return OutOfMemory; 4167 4168 hr = IStream_Read(stream, buf, emh.nBytes, &size); 4169 if (hr != S_OK || size != emh.nBytes) 4170 { 4171 heap_free(buf); 4172 return GenericError; 4173 } 4174 4175 hemf = SetEnhMetaFileBits(emh.nBytes, buf); 4176 heap_free(buf); 4177 if (!hemf) 4178 return GenericError; 4179 4180 status = GdipCreateMetafileFromEmf(hemf, TRUE, metafile); 4181 if (status != Ok) 4182 DeleteEnhMetaFile(hemf); 4183 return status; 4184 } 4185 4186 static GpStatus decode_image_emf(IStream *stream, GpImage **image) 4187 { 4188 GpMetafile *metafile; 4189 GpStatus status; 4190 4191 TRACE("%p %p\n", stream, image); 4192 4193 if (!stream || !image) 4194 return InvalidParameter; 4195 4196 status = load_emf(stream, &metafile); 4197 if (status != Ok) 4198 { 4199 TRACE("Could not load metafile\n"); 4200 return status; 4201 } 4202 4203 *image = (GpImage *)metafile; 4204 TRACE("<-- %p\n", *image); 4205 4206 return Ok; 4207 } 4208 4209 typedef GpStatus (*encode_image_func)(GpImage *image, IStream* stream, 4210 GDIPCONST EncoderParameters* params); 4211 4212 typedef GpStatus (*decode_image_func)(IStream *stream, GpImage **image); 4213 4214 typedef GpStatus (*select_image_func)(GpImage *image, UINT active_frame); 4215 4216 typedef struct image_codec { 4217 ImageCodecInfo info; 4218 encode_image_func encode_func; 4219 decode_image_func decode_func; 4220 select_image_func select_func; 4221 } image_codec; 4222 4223 typedef enum { 4224 BMP, 4225 JPEG, 4226 GIF, 4227 TIFF, 4228 EMF, 4229 WMF, 4230 PNG, 4231 ICO, 4232 NUM_CODECS 4233 } ImageFormat; 4234 4235 static const struct image_codec codecs[NUM_CODECS]; 4236 4237 static GpStatus get_decoder_info(IStream* stream, const struct image_codec **result) 4238 { 4239 BYTE signature[8]; 4240 const BYTE *pattern, *mask; 4241 LARGE_INTEGER seek; 4242 HRESULT hr; 4243 UINT bytesread; 4244 int i; 4245 DWORD j, sig; 4246 4247 /* seek to the start of the stream */ 4248 seek.QuadPart = 0; 4249 hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL); 4250 if (FAILED(hr)) return hresult_to_status(hr); 4251 4252 /* read the first 8 bytes */ 4253 /* FIXME: This assumes all codecs have signatures <= 8 bytes in length */ 4254 hr = IStream_Read(stream, signature, 8, &bytesread); 4255 if (FAILED(hr)) return hresult_to_status(hr); 4256 if (hr == S_FALSE || bytesread == 0) return GenericError; 4257 4258 for (i = 0; i < NUM_CODECS; i++) { 4259 if ((codecs[i].info.Flags & ImageCodecFlagsDecoder) && 4260 bytesread >= codecs[i].info.SigSize) 4261 { 4262 for (sig=0; sig<codecs[i].info.SigCount; sig++) 4263 { 4264 pattern = &codecs[i].info.SigPattern[codecs[i].info.SigSize*sig]; 4265 mask = &codecs[i].info.SigMask[codecs[i].info.SigSize*sig]; 4266 for (j=0; j<codecs[i].info.SigSize; j++) 4267 if ((signature[j] & mask[j]) != pattern[j]) 4268 break; 4269 if (j == codecs[i].info.SigSize) 4270 { 4271 *result = &codecs[i]; 4272 return Ok; 4273 } 4274 } 4275 } 4276 } 4277 4278 TRACE("no match for %i byte signature %x %x %x %x %x %x %x %x\n", bytesread, 4279 signature[0],signature[1],signature[2],signature[3], 4280 signature[4],signature[5],signature[6],signature[7]); 4281 4282 return GenericError; 4283 } 4284 4285 static GpStatus get_decoder_info_from_image(GpImage *image, const struct image_codec **result) 4286 { 4287 int i; 4288 4289 for (i = 0; i < NUM_CODECS; i++) { 4290 if ((codecs[i].info.Flags & ImageCodecFlagsDecoder) && 4291 IsEqualIID(&codecs[i].info.FormatID, &image->format)) 4292 { 4293 *result = &codecs[i]; 4294 return Ok; 4295 } 4296 } 4297 4298 TRACE("no match for format: %s\n", wine_dbgstr_guid(&image->format)); 4299 return GenericError; 4300 } 4301 4302 GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image, GDIPCONST GUID *dimensionID, 4303 UINT frame) 4304 { 4305 GpStatus stat; 4306 const struct image_codec *codec = NULL; 4307 BOOL unlock; 4308 4309 TRACE("(%p,%s,%u)\n", image, debugstr_guid(dimensionID), frame); 4310 4311 if (!image || !dimensionID) 4312 return InvalidParameter; 4313 if(!image_lock(image, &unlock)) 4314 return ObjectBusy; 4315 4316 if (frame >= image->frame_count) 4317 { 4318 WARN("requested frame %u, but image has only %u\n", frame, image->frame_count); 4319 image_unlock(image, unlock); 4320 return InvalidParameter; 4321 } 4322 4323 if (image->type != ImageTypeBitmap && image->type != ImageTypeMetafile) 4324 { 4325 WARN("invalid image type %d\n", image->type); 4326 image_unlock(image, unlock); 4327 return InvalidParameter; 4328 } 4329 4330 if (image->current_frame == frame) 4331 { 4332 image_unlock(image, unlock); 4333 return Ok; 4334 } 4335 4336 if (!image->decoder) 4337 { 4338 TRACE("image doesn't have an associated decoder\n"); 4339 image_unlock(image, unlock); 4340 return Ok; 4341 } 4342 4343 /* choose an appropriate image decoder */ 4344 stat = get_decoder_info_from_image(image, &codec); 4345 if (stat != Ok) 4346 { 4347 WARN("can't find decoder info\n"); 4348 image_unlock(image, unlock); 4349 return stat; 4350 } 4351 4352 stat = codec->select_func(image, frame); 4353 image_unlock(image, unlock); 4354 return stat; 4355 } 4356 4357 GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream *stream, GpImage **image) 4358 { 4359 GpStatus stat; 4360 LARGE_INTEGER seek; 4361 HRESULT hr; 4362 const struct image_codec *codec=NULL; 4363 4364 TRACE("%p %p\n", stream, image); 4365 4366 if (!stream || !image) 4367 return InvalidParameter; 4368 4369 /* choose an appropriate image decoder */ 4370 stat = get_decoder_info(stream, &codec); 4371 if (stat != Ok) return stat; 4372 4373 /* seek to the start of the stream */ 4374 seek.QuadPart = 0; 4375 hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL); 4376 if (FAILED(hr)) return hresult_to_status(hr); 4377 4378 /* call on the image decoder to do the real work */ 4379 stat = codec->decode_func(stream, image); 4380 4381 /* take note of the original data format */ 4382 if (stat == Ok) 4383 { 4384 memcpy(&(*image)->format, &codec->info.FormatID, sizeof(GUID)); 4385 return Ok; 4386 } 4387 4388 return stat; 4389 } 4390 4391 /* FIXME: no ICM */ 4392 GpStatus WINGDIPAPI GdipLoadImageFromStreamICM(IStream* stream, GpImage **image) 4393 { 4394 TRACE("%p %p\n", stream, image); 4395 4396 return GdipLoadImageFromStream(stream, image); 4397 } 4398 4399 GpStatus WINGDIPAPI GdipRemovePropertyItem(GpImage *image, PROPID propId) 4400 { 4401 static int calls; 4402 4403 TRACE("(%p,%u)\n", image, propId); 4404 4405 if(!image) 4406 return InvalidParameter; 4407 4408 if(!(calls++)) 4409 FIXME("not implemented\n"); 4410 4411 return NotImplemented; 4412 } 4413 4414 GpStatus WINGDIPAPI GdipSetPropertyItem(GpImage *image, GDIPCONST PropertyItem* item) 4415 { 4416 static int calls; 4417 4418 if (!image || !item) return InvalidParameter; 4419 4420 TRACE("(%p,%p:%#x,%u,%u,%p)\n", image, item, item->id, item->type, item->length, item->value); 4421 4422 if(!(calls++)) 4423 FIXME("not implemented\n"); 4424 4425 return Ok; 4426 } 4427 4428 GpStatus WINGDIPAPI GdipSaveImageToFile(GpImage *image, GDIPCONST WCHAR* filename, 4429 GDIPCONST CLSID *clsidEncoder, 4430 GDIPCONST EncoderParameters *encoderParams) 4431 { 4432 GpStatus stat; 4433 IStream *stream; 4434 4435 TRACE("%p (%s) %p %p\n", image, debugstr_w(filename), clsidEncoder, encoderParams); 4436 4437 if (!image || !filename|| !clsidEncoder) 4438 return InvalidParameter; 4439 4440 stat = GdipCreateStreamOnFile(filename, GENERIC_WRITE, &stream); 4441 if (stat != Ok) 4442 return GenericError; 4443 4444 stat = GdipSaveImageToStream(image, stream, clsidEncoder, encoderParams); 4445 4446 IStream_Release(stream); 4447 return stat; 4448 } 4449 4450 /************************************************************************* 4451 * Encoding functions - 4452 * These functions encode an image in different image file formats. 4453 */ 4454 4455 static GpStatus encode_image_wic(GpImage *image, IStream* stream, 4456 REFGUID container, GDIPCONST EncoderParameters* params) 4457 { 4458 GpStatus stat; 4459 GpBitmap *bitmap; 4460 IWICImagingFactory *factory; 4461 IWICBitmapEncoder *encoder; 4462 IWICBitmapFrameEncode *frameencode; 4463 IPropertyBag2 *encoderoptions; 4464 HRESULT hr; 4465 UINT width, height; 4466 PixelFormat gdipformat=0; 4467 const WICPixelFormatGUID *desired_wicformat; 4468 WICPixelFormatGUID wicformat; 4469 GpRect rc; 4470 BitmapData lockeddata; 4471 UINT i; 4472 4473 if (image->type != ImageTypeBitmap) 4474 return GenericError; 4475 4476 bitmap = (GpBitmap*)image; 4477 4478 GdipGetImageWidth(image, &width); 4479 GdipGetImageHeight(image, &height); 4480 4481 rc.X = 0; 4482 rc.Y = 0; 4483 rc.Width = width; 4484 rc.Height = height; 4485 4486 hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory); 4487 if (FAILED(hr)) 4488 return hresult_to_status(hr); 4489 hr = IWICImagingFactory_CreateEncoder(factory, container, NULL, &encoder); 4490 IWICImagingFactory_Release(factory); 4491 if (FAILED(hr)) 4492 return hresult_to_status(hr); 4493 4494 hr = IWICBitmapEncoder_Initialize(encoder, stream, WICBitmapEncoderNoCache); 4495 4496 if (SUCCEEDED(hr)) 4497 { 4498 hr = IWICBitmapEncoder_CreateNewFrame(encoder, &frameencode, &encoderoptions); 4499 } 4500 4501 if (SUCCEEDED(hr)) /* created frame */ 4502 { 4503 hr = IWICBitmapFrameEncode_Initialize(frameencode, encoderoptions); 4504 4505 if (SUCCEEDED(hr)) 4506 hr = IWICBitmapFrameEncode_SetSize(frameencode, width, height); 4507 4508 if (SUCCEEDED(hr)) 4509 hr = IWICBitmapFrameEncode_SetResolution(frameencode, image->xres, image->yres); 4510 4511 if (SUCCEEDED(hr)) 4512 { 4513 for (i=0; pixel_formats[i].wic_format; i++) 4514 { 4515 if (pixel_formats[i].gdip_format == bitmap->format) 4516 { 4517 desired_wicformat = pixel_formats[i].wic_format; 4518 gdipformat = bitmap->format; 4519 break; 4520 } 4521 } 4522 if (!gdipformat) 4523 { 4524 desired_wicformat = &GUID_WICPixelFormat32bppBGRA; 4525 gdipformat = PixelFormat32bppARGB; 4526 } 4527 4528 memcpy(&wicformat, desired_wicformat, sizeof(GUID)); 4529 hr = IWICBitmapFrameEncode_SetPixelFormat(frameencode, &wicformat); 4530 } 4531 4532 if (SUCCEEDED(hr) && !IsEqualGUID(desired_wicformat, &wicformat)) 4533 { 4534 /* Encoder doesn't support this bitmap's format. */ 4535 gdipformat = 0; 4536 for (i=0; pixel_formats[i].wic_format; i++) 4537 { 4538 if (IsEqualGUID(&wicformat, pixel_formats[i].wic_format)) 4539 { 4540 gdipformat = pixel_formats[i].gdip_format; 4541 break; 4542 } 4543 } 4544 if (!gdipformat) 4545 { 4546 ERR("Cannot support encoder format %s\n", debugstr_guid(&wicformat)); 4547 hr = E_FAIL; 4548 } 4549 } 4550 4551 if (SUCCEEDED(hr)) 4552 { 4553 stat = GdipBitmapLockBits(bitmap, &rc, ImageLockModeRead, gdipformat, 4554 &lockeddata); 4555 4556 if (stat == Ok) 4557 { 4558 UINT row_size = (lockeddata.Width * PIXELFORMATBPP(gdipformat) + 7)/8; 4559 BYTE *row; 4560 4561 /* write one row at a time in case stride is negative */ 4562 row = lockeddata.Scan0; 4563 for (i=0; i<lockeddata.Height; i++) 4564 { 4565 hr = IWICBitmapFrameEncode_WritePixels(frameencode, 1, row_size, row_size, row); 4566 if (FAILED(hr)) break; 4567 row += lockeddata.Stride; 4568 } 4569 4570 GdipBitmapUnlockBits(bitmap, &lockeddata); 4571 } 4572 else 4573 hr = E_FAIL; 4574 } 4575 4576 if (SUCCEEDED(hr)) 4577 hr = IWICBitmapFrameEncode_Commit(frameencode); 4578 4579 IWICBitmapFrameEncode_Release(frameencode); 4580 IPropertyBag2_Release(encoderoptions); 4581 } 4582 4583 if (SUCCEEDED(hr)) 4584 hr = IWICBitmapEncoder_Commit(encoder); 4585 4586 IWICBitmapEncoder_Release(encoder); 4587 return hresult_to_status(hr); 4588 } 4589 4590 static GpStatus encode_image_BMP(GpImage *image, IStream* stream, 4591 GDIPCONST EncoderParameters* params) 4592 { 4593 return encode_image_wic(image, stream, &GUID_ContainerFormatBmp, params); 4594 } 4595 4596 static GpStatus encode_image_tiff(GpImage *image, IStream* stream, 4597 GDIPCONST EncoderParameters* params) 4598 { 4599 return encode_image_wic(image, stream, &GUID_ContainerFormatTiff, params); 4600 } 4601 4602 GpStatus encode_image_png(GpImage *image, IStream* stream, 4603 GDIPCONST EncoderParameters* params) 4604 { 4605 return encode_image_wic(image, stream, &GUID_ContainerFormatPng, params); 4606 } 4607 4608 static GpStatus encode_image_jpeg(GpImage *image, IStream* stream, 4609 GDIPCONST EncoderParameters* params) 4610 { 4611 return encode_image_wic(image, stream, &GUID_ContainerFormatJpeg, params); 4612 } 4613 4614 static GpStatus encode_image_gif(GpImage *image, IStream* stream, 4615 GDIPCONST EncoderParameters* params) 4616 { 4617 return encode_image_wic(image, stream, &GUID_ContainerFormatGif, params); 4618 } 4619 4620 /***************************************************************************** 4621 * GdipSaveImageToStream [GDIPLUS.@] 4622 */ 4623 GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream, 4624 GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params) 4625 { 4626 GpStatus stat; 4627 encode_image_func encode_image; 4628 int i; 4629 4630 TRACE("%p, %p, %s, %p\n", image, stream, wine_dbgstr_guid(clsid), params); 4631 4632 if(!image || !stream) 4633 return InvalidParameter; 4634 4635 /* select correct encoder */ 4636 encode_image = NULL; 4637 for (i = 0; i < NUM_CODECS; i++) { 4638 if ((codecs[i].info.Flags & ImageCodecFlagsEncoder) && 4639 IsEqualCLSID(clsid, &codecs[i].info.Clsid)) 4640 encode_image = codecs[i].encode_func; 4641 } 4642 if (encode_image == NULL) 4643 return UnknownImageFormat; 4644 4645 stat = encode_image(image, stream, params); 4646 4647 return stat; 4648 } 4649 4650 /***************************************************************************** 4651 * GdipSaveAdd [GDIPLUS.@] 4652 */ 4653 GpStatus WINGDIPAPI GdipSaveAdd(GpImage *image, GDIPCONST EncoderParameters *params) 4654 { 4655 FIXME("(%p,%p): stub\n", image, params); 4656 return Ok; 4657 } 4658 4659 /***************************************************************************** 4660 * GdipGetImagePalette [GDIPLUS.@] 4661 */ 4662 GpStatus WINGDIPAPI GdipGetImagePalette(GpImage *image, ColorPalette *palette, INT size) 4663 { 4664 INT count; 4665 4666 TRACE("(%p,%p,%i)\n", image, palette, size); 4667 4668 if (!image || !palette) 4669 return InvalidParameter; 4670 4671 count = image->palette ? image->palette->Count : 0; 4672 4673 if (size < (sizeof(UINT)*2+sizeof(ARGB)*count)) 4674 { 4675 TRACE("<-- InsufficientBuffer\n"); 4676 return InsufficientBuffer; 4677 } 4678 4679 if (image->palette) 4680 { 4681 palette->Flags = image->palette->Flags; 4682 palette->Count = image->palette->Count; 4683 memcpy(palette->Entries, image->palette->Entries, sizeof(ARGB)*image->palette->Count); 4684 } 4685 else 4686 { 4687 palette->Flags = 0; 4688 palette->Count = 0; 4689 } 4690 return Ok; 4691 } 4692 4693 /***************************************************************************** 4694 * GdipSetImagePalette [GDIPLUS.@] 4695 */ 4696 GpStatus WINGDIPAPI GdipSetImagePalette(GpImage *image, 4697 GDIPCONST ColorPalette *palette) 4698 { 4699 ColorPalette *new_palette; 4700 4701 TRACE("(%p,%p)\n", image, palette); 4702 4703 if(!image || !palette || palette->Count > 256) 4704 return InvalidParameter; 4705 4706 new_palette = heap_alloc_zero(2 * sizeof(UINT) + palette->Count * sizeof(ARGB)); 4707 if (!new_palette) return OutOfMemory; 4708 4709 heap_free(image->palette); 4710 image->palette = new_palette; 4711 image->palette->Flags = palette->Flags; 4712 image->palette->Count = palette->Count; 4713 memcpy(image->palette->Entries, palette->Entries, sizeof(ARGB)*palette->Count); 4714 4715 return Ok; 4716 } 4717 4718 /************************************************************************* 4719 * Encoders - 4720 * Structures that represent which formats we support for encoding. 4721 */ 4722 4723 /* ImageCodecInfo creation routines taken from libgdiplus */ 4724 static const WCHAR bmp_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'B', 'M', 'P', 0}; /* Built-in BMP */ 4725 static const WCHAR bmp_extension[] = {'*','.','B', 'M', 'P',';', '*','.', 'D','I', 'B',';', '*','.', 'R', 'L', 'E',0}; /* *.BMP;*.DIB;*.RLE */ 4726 static const WCHAR bmp_mimetype[] = {'i', 'm', 'a','g', 'e', '/', 'b', 'm', 'p', 0}; /* image/bmp */ 4727 static const WCHAR bmp_format[] = {'B', 'M', 'P', 0}; /* BMP */ 4728 static const BYTE bmp_sig_pattern[] = { 0x42, 0x4D }; 4729 static const BYTE bmp_sig_mask[] = { 0xFF, 0xFF }; 4730 4731 static const WCHAR jpeg_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'J','P','E','G', 0}; 4732 static const WCHAR jpeg_extension[] = {'*','.','J','P','G',';', '*','.','J','P','E','G',';', '*','.','J','P','E',';', '*','.','J','F','I','F',0}; 4733 static const WCHAR jpeg_mimetype[] = {'i','m','a','g','e','/','j','p','e','g', 0}; 4734 static const WCHAR jpeg_format[] = {'J','P','E','G',0}; 4735 static const BYTE jpeg_sig_pattern[] = { 0xFF, 0xD8 }; 4736 static const BYTE jpeg_sig_mask[] = { 0xFF, 0xFF }; 4737 4738 static const WCHAR gif_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'G','I','F', 0}; 4739 static const WCHAR gif_extension[] = {'*','.','G','I','F',0}; 4740 static const WCHAR gif_mimetype[] = {'i','m','a','g','e','/','g','i','f', 0}; 4741 static const WCHAR gif_format[] = {'G','I','F',0}; 4742 static const BYTE gif_sig_pattern[12] = "GIF87aGIF89a"; 4743 static const BYTE gif_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 4744 4745 static const WCHAR tiff_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'T','I','F','F', 0}; 4746 static const WCHAR tiff_extension[] = {'*','.','T','I','F','F',';','*','.','T','I','F',0}; 4747 static const WCHAR tiff_mimetype[] = {'i','m','a','g','e','/','t','i','f','f', 0}; 4748 static const WCHAR tiff_format[] = {'T','I','F','F',0}; 4749 static const BYTE tiff_sig_pattern[] = {0x49,0x49,42,0,0x4d,0x4d,0,42}; 4750 static const BYTE tiff_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 4751 4752 static const WCHAR emf_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'E','M','F', 0}; 4753 static const WCHAR emf_extension[] = {'*','.','E','M','F',0}; 4754 static const WCHAR emf_mimetype[] = {'i','m','a','g','e','/','x','-','e','m','f', 0}; 4755 static const WCHAR emf_format[] = {'E','M','F',0}; 4756 static const BYTE emf_sig_pattern[] = { 0x01, 0x00, 0x00, 0x00 }; 4757 static const BYTE emf_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF }; 4758 4759 static const WCHAR wmf_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'W','M','F', 0}; 4760 static const WCHAR wmf_extension[] = {'*','.','W','M','F',0}; 4761 static const WCHAR wmf_mimetype[] = {'i','m','a','g','e','/','x','-','w','m','f', 0}; 4762 static const WCHAR wmf_format[] = {'W','M','F',0}; 4763 static const BYTE wmf_sig_pattern[] = { 0xd7, 0xcd }; 4764 static const BYTE wmf_sig_mask[] = { 0xFF, 0xFF }; 4765 4766 static const WCHAR png_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'P','N','G', 0}; 4767 static const WCHAR png_extension[] = {'*','.','P','N','G',0}; 4768 static const WCHAR png_mimetype[] = {'i','m','a','g','e','/','p','n','g', 0}; 4769 static const WCHAR png_format[] = {'P','N','G',0}; 4770 static const BYTE png_sig_pattern[] = { 137, 80, 78, 71, 13, 10, 26, 10, }; 4771 static const BYTE png_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 4772 4773 static const WCHAR ico_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'I','C','O', 0}; 4774 static const WCHAR ico_extension[] = {'*','.','I','C','O',0}; 4775 static const WCHAR ico_mimetype[] = {'i','m','a','g','e','/','x','-','i','c','o','n', 0}; 4776 static const WCHAR ico_format[] = {'I','C','O',0}; 4777 static const BYTE ico_sig_pattern[] = { 0x00, 0x00, 0x01, 0x00 }; 4778 static const BYTE ico_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF }; 4779 4780 static const struct image_codec codecs[NUM_CODECS] = { 4781 { 4782 { /* BMP */ 4783 /* Clsid */ { 0x557cf400, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } }, 4784 /* FormatID */ { 0xb96b3cabU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} }, 4785 /* CodecName */ bmp_codecname, 4786 /* DllName */ NULL, 4787 /* FormatDescription */ bmp_format, 4788 /* FilenameExtension */ bmp_extension, 4789 /* MimeType */ bmp_mimetype, 4790 /* Flags */ ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin, 4791 /* Version */ 1, 4792 /* SigCount */ 1, 4793 /* SigSize */ 2, 4794 /* SigPattern */ bmp_sig_pattern, 4795 /* SigMask */ bmp_sig_mask, 4796 }, 4797 encode_image_BMP, 4798 decode_image_bmp, 4799 select_frame_wic 4800 }, 4801 { 4802 { /* JPEG */ 4803 /* Clsid */ { 0x557cf401, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } }, 4804 /* FormatID */ { 0xb96b3caeU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} }, 4805 /* CodecName */ jpeg_codecname, 4806 /* DllName */ NULL, 4807 /* FormatDescription */ jpeg_format, 4808 /* FilenameExtension */ jpeg_extension, 4809 /* MimeType */ jpeg_mimetype, 4810 /* Flags */ ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin, 4811 /* Version */ 1, 4812 /* SigCount */ 1, 4813 /* SigSize */ 2, 4814 /* SigPattern */ jpeg_sig_pattern, 4815 /* SigMask */ jpeg_sig_mask, 4816 }, 4817 encode_image_jpeg, 4818 decode_image_jpeg, 4819 select_frame_wic 4820 }, 4821 { 4822 { /* GIF */ 4823 /* Clsid */ { 0x557cf402, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } }, 4824 /* FormatID */ { 0xb96b3cb0U, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} }, 4825 /* CodecName */ gif_codecname, 4826 /* DllName */ NULL, 4827 /* FormatDescription */ gif_format, 4828 /* FilenameExtension */ gif_extension, 4829 /* MimeType */ gif_mimetype, 4830 /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsEncoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin, 4831 /* Version */ 1, 4832 /* SigCount */ 2, 4833 /* SigSize */ 6, 4834 /* SigPattern */ gif_sig_pattern, 4835 /* SigMask */ gif_sig_mask, 4836 }, 4837 encode_image_gif, 4838 decode_image_gif, 4839 select_frame_gif 4840 }, 4841 { 4842 { /* TIFF */ 4843 /* Clsid */ { 0x557cf405, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } }, 4844 /* FormatID */ { 0xb96b3cb1U, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} }, 4845 /* CodecName */ tiff_codecname, 4846 /* DllName */ NULL, 4847 /* FormatDescription */ tiff_format, 4848 /* FilenameExtension */ tiff_extension, 4849 /* MimeType */ tiff_mimetype, 4850 /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsEncoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin, 4851 /* Version */ 1, 4852 /* SigCount */ 2, 4853 /* SigSize */ 4, 4854 /* SigPattern */ tiff_sig_pattern, 4855 /* SigMask */ tiff_sig_mask, 4856 }, 4857 encode_image_tiff, 4858 decode_image_tiff, 4859 select_frame_wic 4860 }, 4861 { 4862 { /* EMF */ 4863 /* Clsid */ { 0x557cf403, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } }, 4864 /* FormatID */ { 0xb96b3cacU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} }, 4865 /* CodecName */ emf_codecname, 4866 /* DllName */ NULL, 4867 /* FormatDescription */ emf_format, 4868 /* FilenameExtension */ emf_extension, 4869 /* MimeType */ emf_mimetype, 4870 /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsSupportVector | ImageCodecFlagsBuiltin, 4871 /* Version */ 1, 4872 /* SigCount */ 1, 4873 /* SigSize */ 4, 4874 /* SigPattern */ emf_sig_pattern, 4875 /* SigMask */ emf_sig_mask, 4876 }, 4877 NULL, 4878 decode_image_emf, 4879 NULL 4880 }, 4881 { 4882 { /* WMF */ 4883 /* Clsid */ { 0x557cf404, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } }, 4884 /* FormatID */ { 0xb96b3cadU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} }, 4885 /* CodecName */ wmf_codecname, 4886 /* DllName */ NULL, 4887 /* FormatDescription */ wmf_format, 4888 /* FilenameExtension */ wmf_extension, 4889 /* MimeType */ wmf_mimetype, 4890 /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsSupportVector | ImageCodecFlagsBuiltin, 4891 /* Version */ 1, 4892 /* SigCount */ 1, 4893 /* SigSize */ 2, 4894 /* SigPattern */ wmf_sig_pattern, 4895 /* SigMask */ wmf_sig_mask, 4896 }, 4897 NULL, 4898 decode_image_wmf, 4899 NULL 4900 }, 4901 { 4902 { /* PNG */ 4903 /* Clsid */ { 0x557cf406, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } }, 4904 /* FormatID */ { 0xb96b3cafU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} }, 4905 /* CodecName */ png_codecname, 4906 /* DllName */ NULL, 4907 /* FormatDescription */ png_format, 4908 /* FilenameExtension */ png_extension, 4909 /* MimeType */ png_mimetype, 4910 /* Flags */ ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin, 4911 /* Version */ 1, 4912 /* SigCount */ 1, 4913 /* SigSize */ 8, 4914 /* SigPattern */ png_sig_pattern, 4915 /* SigMask */ png_sig_mask, 4916 }, 4917 encode_image_png, 4918 decode_image_png, 4919 select_frame_wic 4920 }, 4921 { 4922 { /* ICO */ 4923 /* Clsid */ { 0x557cf407, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } }, 4924 /* FormatID */ { 0xb96b3cabU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} }, 4925 /* CodecName */ ico_codecname, 4926 /* DllName */ NULL, 4927 /* FormatDescription */ ico_format, 4928 /* FilenameExtension */ ico_extension, 4929 /* MimeType */ ico_mimetype, 4930 /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin, 4931 /* Version */ 1, 4932 /* SigCount */ 1, 4933 /* SigSize */ 4, 4934 /* SigPattern */ ico_sig_pattern, 4935 /* SigMask */ ico_sig_mask, 4936 }, 4937 NULL, 4938 decode_image_icon, 4939 select_frame_wic 4940 }, 4941 }; 4942 4943 /***************************************************************************** 4944 * GdipGetImageDecodersSize [GDIPLUS.@] 4945 */ 4946 GpStatus WINGDIPAPI GdipGetImageDecodersSize(UINT *numDecoders, UINT *size) 4947 { 4948 int decoder_count=0; 4949 int i; 4950 TRACE("%p %p\n", numDecoders, size); 4951 4952 if (!numDecoders || !size) 4953 return InvalidParameter; 4954 4955 for (i=0; i<NUM_CODECS; i++) 4956 { 4957 if (codecs[i].info.Flags & ImageCodecFlagsDecoder) 4958 decoder_count++; 4959 } 4960 4961 *numDecoders = decoder_count; 4962 *size = decoder_count * sizeof(ImageCodecInfo); 4963 4964 return Ok; 4965 } 4966 4967 /***************************************************************************** 4968 * GdipGetImageDecoders [GDIPLUS.@] 4969 */ 4970 GpStatus WINGDIPAPI GdipGetImageDecoders(UINT numDecoders, UINT size, ImageCodecInfo *decoders) 4971 { 4972 int i, decoder_count=0; 4973 TRACE("%u %u %p\n", numDecoders, size, decoders); 4974 4975 if (!decoders || 4976 size != numDecoders * sizeof(ImageCodecInfo)) 4977 return GenericError; 4978 4979 for (i=0; i<NUM_CODECS; i++) 4980 { 4981 if (codecs[i].info.Flags & ImageCodecFlagsDecoder) 4982 { 4983 if (decoder_count == numDecoders) return GenericError; 4984 memcpy(&decoders[decoder_count], &codecs[i].info, sizeof(ImageCodecInfo)); 4985 decoder_count++; 4986 } 4987 } 4988 4989 if (decoder_count < numDecoders) return GenericError; 4990 4991 return Ok; 4992 } 4993 4994 /***************************************************************************** 4995 * GdipGetImageEncodersSize [GDIPLUS.@] 4996 */ 4997 GpStatus WINGDIPAPI GdipGetImageEncodersSize(UINT *numEncoders, UINT *size) 4998 { 4999 int encoder_count=0; 5000 int i; 5001 TRACE("%p %p\n", numEncoders, size); 5002 5003 if (!numEncoders || !size) 5004 return InvalidParameter; 5005 5006 for (i=0; i<NUM_CODECS; i++) 5007 { 5008 if (codecs[i].info.Flags & ImageCodecFlagsEncoder) 5009 encoder_count++; 5010 } 5011 5012 *numEncoders = encoder_count; 5013 *size = encoder_count * sizeof(ImageCodecInfo); 5014 5015 return Ok; 5016 } 5017 5018 /***************************************************************************** 5019 * GdipGetImageEncoders [GDIPLUS.@] 5020 */ 5021 GpStatus WINGDIPAPI GdipGetImageEncoders(UINT numEncoders, UINT size, ImageCodecInfo *encoders) 5022 { 5023 int i, encoder_count=0; 5024 TRACE("%u %u %p\n", numEncoders, size, encoders); 5025 5026 if (!encoders || 5027 size != numEncoders * sizeof(ImageCodecInfo)) 5028 return GenericError; 5029 5030 for (i=0; i<NUM_CODECS; i++) 5031 { 5032 if (codecs[i].info.Flags & ImageCodecFlagsEncoder) 5033 { 5034 if (encoder_count == numEncoders) return GenericError; 5035 memcpy(&encoders[encoder_count], &codecs[i].info, sizeof(ImageCodecInfo)); 5036 encoder_count++; 5037 } 5038 } 5039 5040 if (encoder_count < numEncoders) return GenericError; 5041 5042 return Ok; 5043 } 5044 5045 GpStatus WINGDIPAPI GdipGetEncoderParameterListSize(GpImage *image, 5046 GDIPCONST CLSID* clsidEncoder, UINT *size) 5047 { 5048 static int calls; 5049 5050 TRACE("(%p,%s,%p)\n", image, debugstr_guid(clsidEncoder), size); 5051 5052 if(!(calls++)) 5053 FIXME("not implemented\n"); 5054 5055 *size = 0; 5056 5057 return NotImplemented; 5058 } 5059 5060 static PixelFormat get_16bpp_format(HBITMAP hbm) 5061 { 5062 BITMAPV4HEADER bmh; 5063 HDC hdc; 5064 PixelFormat result; 5065 5066 hdc = CreateCompatibleDC(NULL); 5067 5068 memset(&bmh, 0, sizeof(bmh)); 5069 bmh.bV4Size = sizeof(bmh); 5070 bmh.bV4Width = 1; 5071 bmh.bV4Height = 1; 5072 bmh.bV4V4Compression = BI_BITFIELDS; 5073 bmh.bV4BitCount = 16; 5074 5075 GetDIBits(hdc, hbm, 0, 0, NULL, (BITMAPINFO*)&bmh, DIB_RGB_COLORS); 5076 5077 if (bmh.bV4RedMask == 0x7c00 && 5078 bmh.bV4GreenMask == 0x3e0 && 5079 bmh.bV4BlueMask == 0x1f) 5080 { 5081 result = PixelFormat16bppRGB555; 5082 } 5083 else if (bmh.bV4RedMask == 0xf800 && 5084 bmh.bV4GreenMask == 0x7e0 && 5085 bmh.bV4BlueMask == 0x1f) 5086 { 5087 result = PixelFormat16bppRGB565; 5088 } 5089 else 5090 { 5091 FIXME("unrecognized bitfields %x,%x,%x\n", bmh.bV4RedMask, 5092 bmh.bV4GreenMask, bmh.bV4BlueMask); 5093 result = PixelFormatUndefined; 5094 } 5095 5096 DeleteDC(hdc); 5097 5098 return result; 5099 } 5100 5101 /***************************************************************************** 5102 * GdipCreateBitmapFromHBITMAP [GDIPLUS.@] 5103 */ 5104 GpStatus WINGDIPAPI GdipCreateBitmapFromHBITMAP(HBITMAP hbm, HPALETTE hpal, GpBitmap** bitmap) 5105 { 5106 BITMAP bm; 5107 GpStatus retval; 5108 PixelFormat format; 5109 BitmapData lockeddata; 5110 5111 TRACE("%p %p %p\n", hbm, hpal, bitmap); 5112 5113 if(!hbm || !bitmap) 5114 return InvalidParameter; 5115 5116 if (GetObjectA(hbm, sizeof(bm), &bm) != sizeof(bm)) 5117 return InvalidParameter; 5118 5119 /* TODO: Figure out the correct format for 16, 32, 64 bpp */ 5120 switch(bm.bmBitsPixel) { 5121 case 1: 5122 format = PixelFormat1bppIndexed; 5123 break; 5124 case 4: 5125 format = PixelFormat4bppIndexed; 5126 break; 5127 case 8: 5128 format = PixelFormat8bppIndexed; 5129 break; 5130 case 16: 5131 format = get_16bpp_format(hbm); 5132 if (format == PixelFormatUndefined) 5133 return InvalidParameter; 5134 break; 5135 case 24: 5136 format = PixelFormat24bppRGB; 5137 break; 5138 case 32: 5139 format = PixelFormat32bppRGB; 5140 break; 5141 case 48: 5142 format = PixelFormat48bppRGB; 5143 break; 5144 default: 5145 FIXME("don't know how to handle %d bpp\n", bm.bmBitsPixel); 5146 return InvalidParameter; 5147 } 5148 5149 retval = GdipCreateBitmapFromScan0(bm.bmWidth, bm.bmHeight, 0, 5150 format, NULL, bitmap); 5151 5152 if (retval == Ok) 5153 { 5154 retval = GdipBitmapLockBits(*bitmap, NULL, ImageLockModeWrite, 5155 format, &lockeddata); 5156 if (retval == Ok) 5157 { 5158 HDC hdc; 5159 char bmibuf[FIELD_OFFSET(BITMAPINFO, bmiColors) + 256 * sizeof(RGBQUAD)]; 5160 BITMAPINFO *pbmi = (BITMAPINFO *)bmibuf; 5161 INT src_height; 5162 5163 hdc = CreateCompatibleDC(NULL); 5164 5165 pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 5166 pbmi->bmiHeader.biBitCount = 0; 5167 5168 GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS); 5169 5170 src_height = abs(pbmi->bmiHeader.biHeight); 5171 pbmi->bmiHeader.biHeight = -src_height; 5172 5173 GetDIBits(hdc, hbm, 0, src_height, lockeddata.Scan0, pbmi, DIB_RGB_COLORS); 5174 5175 DeleteDC(hdc); 5176 5177 GdipBitmapUnlockBits(*bitmap, &lockeddata); 5178 } 5179 5180 if (retval == Ok && hpal) 5181 { 5182 PALETTEENTRY entry[256]; 5183 ColorPalette *palette=NULL; 5184 int i, num_palette_entries; 5185 5186 num_palette_entries = GetPaletteEntries(hpal, 0, 256, entry); 5187 if (!num_palette_entries) 5188 retval = GenericError; 5189 5190 palette = heap_alloc_zero(sizeof(ColorPalette) + sizeof(ARGB) * (num_palette_entries-1)); 5191 if (!palette) 5192 retval = OutOfMemory; 5193 5194 if (retval == Ok) 5195 { 5196 palette->Flags = 0; 5197 palette->Count = num_palette_entries; 5198 5199 for (i=0; i<num_palette_entries; i++) 5200 { 5201 palette->Entries[i] = 0xff000000 | entry[i].peRed << 16 | 5202 entry[i].peGreen << 8 | entry[i].peBlue; 5203 } 5204 5205 retval = GdipSetImagePalette(&(*bitmap)->image, palette); 5206 } 5207 5208 heap_free(palette); 5209 } 5210 5211 if (retval != Ok) 5212 { 5213 GdipDisposeImage(&(*bitmap)->image); 5214 *bitmap = NULL; 5215 } 5216 } 5217 5218 return retval; 5219 } 5220 5221 /***************************************************************************** 5222 * GdipCreateEffect [GDIPLUS.@] 5223 */ 5224 GpStatus WINGDIPAPI GdipCreateEffect(const GUID guid, CGpEffect **effect) 5225 { 5226 FIXME("(%s, %p): stub\n", debugstr_guid(&guid), effect); 5227 5228 if(!effect) 5229 return InvalidParameter; 5230 5231 *effect = NULL; 5232 5233 return NotImplemented; 5234 } 5235 5236 /***************************************************************************** 5237 * GdipDeleteEffect [GDIPLUS.@] 5238 */ 5239 GpStatus WINGDIPAPI GdipDeleteEffect(CGpEffect *effect) 5240 { 5241 FIXME("(%p): stub\n", effect); 5242 /* note: According to Jose Roca's GDI+ Docs, this is not implemented 5243 * in Windows's gdiplus */ 5244 return NotImplemented; 5245 } 5246 5247 /***************************************************************************** 5248 * GdipSetEffectParameters [GDIPLUS.@] 5249 */ 5250 GpStatus WINGDIPAPI GdipSetEffectParameters(CGpEffect *effect, 5251 const VOID *params, const UINT size) 5252 { 5253 static int calls; 5254 5255 TRACE("(%p,%p,%u)\n", effect, params, size); 5256 5257 if(!(calls++)) 5258 FIXME("not implemented\n"); 5259 5260 return NotImplemented; 5261 } 5262 5263 /***************************************************************************** 5264 * GdipGetImageFlags [GDIPLUS.@] 5265 */ 5266 GpStatus WINGDIPAPI GdipGetImageFlags(GpImage *image, UINT *flags) 5267 { 5268 TRACE("%p %p\n", image, flags); 5269 5270 if(!image || !flags) 5271 return InvalidParameter; 5272 5273 *flags = image->flags; 5274 5275 return Ok; 5276 } 5277 5278 GpStatus WINGDIPAPI GdipTestControl(GpTestControlEnum control, void *param) 5279 { 5280 TRACE("(%d, %p)\n", control, param); 5281 5282 switch(control){ 5283 case TestControlForceBilinear: 5284 if(param) 5285 FIXME("TestControlForceBilinear not handled\n"); 5286 break; 5287 case TestControlNoICM: 5288 if(param) 5289 FIXME("TestControlNoICM not handled\n"); 5290 break; 5291 case TestControlGetBuildNumber: 5292 *((DWORD*)param) = 3102; 5293 break; 5294 } 5295 5296 return Ok; 5297 } 5298 5299 GpStatus WINGDIPAPI GdipImageForceValidation(GpImage *image) 5300 { 5301 TRACE("%p\n", image); 5302 5303 return Ok; 5304 } 5305 5306 /***************************************************************************** 5307 * GdipGetImageThumbnail [GDIPLUS.@] 5308 */ 5309 GpStatus WINGDIPAPI GdipGetImageThumbnail(GpImage *image, UINT width, UINT height, 5310 GpImage **ret_image, GetThumbnailImageAbort cb, 5311 VOID * cb_data) 5312 { 5313 GpStatus stat; 5314 GpGraphics *graphics; 5315 UINT srcwidth, srcheight; 5316 5317 TRACE("(%p %u %u %p %p %p)\n", 5318 image, width, height, ret_image, cb, cb_data); 5319 5320 if (!image || !ret_image) 5321 return InvalidParameter; 5322 5323 if (!width) width = 120; 5324 if (!height) height = 120; 5325 5326 GdipGetImageWidth(image, &srcwidth); 5327 GdipGetImageHeight(image, &srcheight); 5328 5329 stat = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppPARGB, 5330 NULL, (GpBitmap**)ret_image); 5331 5332 if (stat == Ok) 5333 { 5334 stat = GdipGetImageGraphicsContext(*ret_image, &graphics); 5335 5336 if (stat == Ok) 5337 { 5338 stat = GdipDrawImageRectRectI(graphics, image, 5339 0, 0, width, height, 0, 0, srcwidth, srcheight, UnitPixel, 5340 NULL, NULL, NULL); 5341 5342 GdipDeleteGraphics(graphics); 5343 } 5344 5345 if (stat != Ok) 5346 { 5347 GdipDisposeImage(*ret_image); 5348 *ret_image = NULL; 5349 } 5350 } 5351 5352 return stat; 5353 } 5354 5355 /***************************************************************************** 5356 * GdipImageRotateFlip [GDIPLUS.@] 5357 */ 5358 GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type) 5359 { 5360 GpBitmap *new_bitmap; 5361 GpBitmap *bitmap; 5362 int bpp, bytesperpixel; 5363 BOOL rotate_90, flip_x, flip_y; 5364 int src_x_offset, src_y_offset; 5365 LPBYTE src_origin; 5366 UINT x, y, width, height; 5367 BitmapData src_lock, dst_lock; 5368 GpStatus stat; 5369 BOOL unlock; 5370 5371 TRACE("(%p, %u)\n", image, type); 5372 5373 if (!image) 5374 return InvalidParameter; 5375 if (!image_lock(image, &unlock)) 5376 return ObjectBusy; 5377 5378 rotate_90 = type&1; 5379 flip_x = (type&6) == 2 || (type&6) == 4; 5380 flip_y = (type&3) == 1 || (type&3) == 2; 5381 5382 if (image->type != ImageTypeBitmap) 5383 { 5384 FIXME("Not implemented for type %i\n", image->type); 5385 image_unlock(image, unlock); 5386 return NotImplemented; 5387 } 5388 5389 bitmap = (GpBitmap*)image; 5390 bpp = PIXELFORMATBPP(bitmap->format); 5391 5392 if (bpp < 8) 5393 { 5394 FIXME("Not implemented for %i bit images\n", bpp); 5395 image_unlock(image, unlock); 5396 return NotImplemented; 5397 } 5398 5399 if (rotate_90) 5400 { 5401 width = bitmap->height; 5402 height = bitmap->width; 5403 } 5404 else 5405 { 5406 width = bitmap->width; 5407 height = bitmap->height; 5408 } 5409 5410 bytesperpixel = bpp/8; 5411 5412 stat = GdipCreateBitmapFromScan0(width, height, 0, bitmap->format, NULL, &new_bitmap); 5413 5414 if (stat != Ok) 5415 { 5416 image_unlock(image, unlock); 5417 return stat; 5418 } 5419 5420 stat = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, bitmap->format, &src_lock); 5421 5422 if (stat == Ok) 5423 { 5424 stat = GdipBitmapLockBits(new_bitmap, NULL, ImageLockModeWrite, bitmap->format, &dst_lock); 5425 5426 if (stat == Ok) 5427 { 5428 LPBYTE src_row, src_pixel; 5429 LPBYTE dst_row, dst_pixel; 5430 5431 src_origin = src_lock.Scan0; 5432 if (flip_x) src_origin += bytesperpixel * (bitmap->width - 1); 5433 if (flip_y) src_origin += src_lock.Stride * (bitmap->height - 1); 5434 5435 if (rotate_90) 5436 { 5437 if (flip_y) src_x_offset = -src_lock.Stride; 5438 else src_x_offset = src_lock.Stride; 5439 if (flip_x) src_y_offset = -bytesperpixel; 5440 else src_y_offset = bytesperpixel; 5441 } 5442 else 5443 { 5444 if (flip_x) src_x_offset = -bytesperpixel; 5445 else src_x_offset = bytesperpixel; 5446 if (flip_y) src_y_offset = -src_lock.Stride; 5447 else src_y_offset = src_lock.Stride; 5448 } 5449 5450 src_row = src_origin; 5451 dst_row = dst_lock.Scan0; 5452 for (y=0; y<height; y++) 5453 { 5454 src_pixel = src_row; 5455 dst_pixel = dst_row; 5456 for (x=0; x<width; x++) 5457 { 5458 /* FIXME: This could probably be faster without memcpy. */ 5459 memcpy(dst_pixel, src_pixel, bytesperpixel); 5460 dst_pixel += bytesperpixel; 5461 src_pixel += src_x_offset; 5462 } 5463 src_row += src_y_offset; 5464 dst_row += dst_lock.Stride; 5465 } 5466 5467 GdipBitmapUnlockBits(new_bitmap, &dst_lock); 5468 } 5469 5470 GdipBitmapUnlockBits(bitmap, &src_lock); 5471 } 5472 5473 if (stat == Ok) 5474 move_bitmap(bitmap, new_bitmap, FALSE); 5475 else 5476 GdipDisposeImage(&new_bitmap->image); 5477 5478 image_unlock(image, unlock); 5479 return stat; 5480 } 5481 5482 /***************************************************************************** 5483 * GdipImageSetAbort [GDIPLUS.@] 5484 */ 5485 GpStatus WINGDIPAPI GdipImageSetAbort(GpImage *image, GdiplusAbort *pabort) 5486 { 5487 TRACE("(%p, %p)\n", image, pabort); 5488 5489 if (!image) 5490 return InvalidParameter; 5491 5492 if (pabort) 5493 FIXME("Abort callback is not supported.\n"); 5494 5495 return Ok; 5496 } 5497 5498 /***************************************************************************** 5499 * GdipBitmapConvertFormat [GDIPLUS.@] 5500 */ 5501 GpStatus WINGDIPAPI GdipBitmapConvertFormat(GpBitmap *bitmap, PixelFormat format, DitherType dithertype, 5502 PaletteType palettetype, ColorPalette *palette, REAL alphathreshold) 5503 { 5504 FIXME("(%p, 0x%08x, %d, %d, %p, %f): stub\n", bitmap, format, dithertype, palettetype, palette, alphathreshold); 5505 return NotImplemented; 5506 } 5507 5508 static void set_histogram_point_argb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) 5509 { 5510 ch0[ color >> 24 ]++; 5511 ch1[(color >> 16) & 0xff]++; 5512 ch2[(color >> 8) & 0xff]++; 5513 ch3[ color & 0xff]++; 5514 } 5515 5516 static void set_histogram_point_pargb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) 5517 { 5518 BYTE alpha = color >> 24; 5519 5520 ch0[alpha]++; 5521 ch1[(((color >> 16) & 0xff) * alpha) / 0xff]++; 5522 ch2[(((color >> 8) & 0xff) * alpha) / 0xff]++; 5523 ch3[(( color & 0xff) * alpha) / 0xff]++; 5524 } 5525 5526 static void set_histogram_point_rgb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) 5527 { 5528 ch0[(color >> 16) & 0xff]++; 5529 ch1[(color >> 8) & 0xff]++; 5530 ch2[ color & 0xff]++; 5531 } 5532 5533 static void set_histogram_point_gray(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) 5534 { 5535 ch0[(76 * ((color >> 16) & 0xff) + 150 * ((color >> 8) & 0xff) + 29 * (color & 0xff)) / 0xff]++; 5536 } 5537 5538 static void set_histogram_point_b(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) 5539 { 5540 ch0[color & 0xff]++; 5541 } 5542 5543 static void set_histogram_point_g(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) 5544 { 5545 ch0[(color >> 8) & 0xff]++; 5546 } 5547 5548 static void set_histogram_point_r(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) 5549 { 5550 ch0[(color >> 16) & 0xff]++; 5551 } 5552 5553 static void set_histogram_point_a(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) 5554 { 5555 ch0[(color >> 24) & 0xff]++; 5556 } 5557 5558 /***************************************************************************** 5559 * GdipBitmapGetHistogram [GDIPLUS.@] 5560 */ 5561 GpStatus WINGDIPAPI GdipBitmapGetHistogram(GpBitmap *bitmap, HistogramFormat format, UINT num_of_entries, 5562 UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) 5563 { 5564 static void (* const set_histogram_point[])(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) = 5565 { 5566 set_histogram_point_argb, 5567 set_histogram_point_pargb, 5568 set_histogram_point_rgb, 5569 set_histogram_point_gray, 5570 set_histogram_point_b, 5571 set_histogram_point_g, 5572 set_histogram_point_r, 5573 set_histogram_point_a, 5574 }; 5575 UINT width, height, x, y; 5576 5577 TRACE("(%p, %d, %u, %p, %p, %p, %p)\n", bitmap, format, num_of_entries, 5578 ch0, ch1, ch2, ch3); 5579 5580 if (!bitmap || num_of_entries != 256) 5581 return InvalidParameter; 5582 5583 /* Make sure passed channel pointers match requested format */ 5584 switch (format) 5585 { 5586 case HistogramFormatARGB: 5587 case HistogramFormatPARGB: 5588 if (!ch0 || !ch1 || !ch2 || !ch3) 5589 return InvalidParameter; 5590 memset(ch0, 0, num_of_entries * sizeof(UINT)); 5591 memset(ch1, 0, num_of_entries * sizeof(UINT)); 5592 memset(ch2, 0, num_of_entries * sizeof(UINT)); 5593 memset(ch3, 0, num_of_entries * sizeof(UINT)); 5594 break; 5595 case HistogramFormatRGB: 5596 if (!ch0 || !ch1 || !ch2 || ch3) 5597 return InvalidParameter; 5598 memset(ch0, 0, num_of_entries * sizeof(UINT)); 5599 memset(ch1, 0, num_of_entries * sizeof(UINT)); 5600 memset(ch2, 0, num_of_entries * sizeof(UINT)); 5601 break; 5602 case HistogramFormatGray: 5603 case HistogramFormatB: 5604 case HistogramFormatG: 5605 case HistogramFormatR: 5606 case HistogramFormatA: 5607 if (!ch0 || ch1 || ch2 || ch3) 5608 return InvalidParameter; 5609 memset(ch0, 0, num_of_entries * sizeof(UINT)); 5610 break; 5611 default: 5612 WARN("Invalid histogram format requested, %d\n", format); 5613 return InvalidParameter; 5614 } 5615 5616 GdipGetImageWidth(&bitmap->image, &width); 5617 GdipGetImageHeight(&bitmap->image, &height); 5618 5619 for (y = 0; y < height; y++) 5620 for (x = 0; x < width; x++) 5621 { 5622 ARGB color; 5623 5624 GdipBitmapGetPixel(bitmap, x, y, &color); 5625 set_histogram_point[format](color, ch0, ch1, ch2, ch3); 5626 } 5627 5628 return Ok; 5629 } 5630 5631 /***************************************************************************** 5632 * GdipBitmapGetHistogramSize [GDIPLUS.@] 5633 */ 5634 GpStatus WINGDIPAPI GdipBitmapGetHistogramSize(HistogramFormat format, UINT *num_of_entries) 5635 { 5636 TRACE("(%d, %p)\n", format, num_of_entries); 5637 5638 if (!num_of_entries) 5639 return InvalidParameter; 5640 5641 *num_of_entries = 256; 5642 return Ok; 5643 } 5644 5645 static GpStatus create_optimal_palette(ColorPalette *palette, INT desired, 5646 BOOL transparent, GpBitmap *bitmap) 5647 { 5648 GpStatus status; 5649 BitmapData data; 5650 HRESULT hr; 5651 IWICImagingFactory *factory; 5652 IWICPalette *wic_palette; 5653 5654 if (!bitmap) return InvalidParameter; 5655 if (palette->Count < desired) return GenericError; 5656 5657 status = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, PixelFormat24bppRGB, &data); 5658 if (status != Ok) return status; 5659 5660 hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory); 5661 if (hr != S_OK) 5662 { 5663 GdipBitmapUnlockBits(bitmap, &data); 5664 return hresult_to_status(hr); 5665 } 5666 5667 hr = IWICImagingFactory_CreatePalette(factory, &wic_palette); 5668 if (hr == S_OK) 5669 { 5670 IWICBitmap *bitmap; 5671 5672 /* PixelFormat24bppRGB actually stores the bitmap bits as BGR. */ 5673 hr = IWICImagingFactory_CreateBitmapFromMemory(factory, data.Width, data.Height, 5674 &GUID_WICPixelFormat24bppBGR, data.Stride, data.Stride * data.Width, data.Scan0, &bitmap); 5675 if (hr == S_OK) 5676 { 5677 hr = IWICPalette_InitializeFromBitmap(wic_palette, (IWICBitmapSource *)bitmap, desired, transparent); 5678 if (hr == S_OK) 5679 { 5680 palette->Flags = 0; 5681 IWICPalette_GetColorCount(wic_palette, &palette->Count); 5682 IWICPalette_GetColors(wic_palette, palette->Count, palette->Entries, &palette->Count); 5683 } 5684 5685 IWICBitmap_Release(bitmap); 5686 } 5687 5688 IWICPalette_Release(wic_palette); 5689 } 5690 5691 IWICImagingFactory_Release(factory); 5692 GdipBitmapUnlockBits(bitmap, &data); 5693 5694 return hresult_to_status(hr); 5695 } 5696 5697 /***************************************************************************** 5698 * GdipInitializePalette [GDIPLUS.@] 5699 */ 5700 GpStatus WINGDIPAPI GdipInitializePalette(ColorPalette *palette, 5701 PaletteType type, INT desired, BOOL transparent, GpBitmap *bitmap) 5702 { 5703 TRACE("(%p,%d,%d,%d,%p)\n", palette, type, desired, transparent, bitmap); 5704 5705 if (!palette) return InvalidParameter; 5706 5707 switch (type) 5708 { 5709 case PaletteTypeCustom: 5710 return Ok; 5711 5712 case PaletteTypeOptimal: 5713 return create_optimal_palette(palette, desired, transparent, bitmap); 5714 5715 /* WIC palette type enumeration matches these gdiplus enums */ 5716 case PaletteTypeFixedBW: 5717 case PaletteTypeFixedHalftone8: 5718 case PaletteTypeFixedHalftone27: 5719 case PaletteTypeFixedHalftone64: 5720 case PaletteTypeFixedHalftone125: 5721 case PaletteTypeFixedHalftone216: 5722 case PaletteTypeFixedHalftone252: 5723 case PaletteTypeFixedHalftone256: 5724 { 5725 ColorPalette *wic_palette; 5726 GpStatus status = Ok; 5727 5728 wic_palette = get_palette(NULL, type); 5729 if (!wic_palette) return OutOfMemory; 5730 5731 if (palette->Count >= wic_palette->Count) 5732 { 5733 palette->Flags = wic_palette->Flags; 5734 palette->Count = wic_palette->Count; 5735 memcpy(palette->Entries, wic_palette->Entries, wic_palette->Count * sizeof(wic_palette->Entries[0])); 5736 } 5737 else 5738 status = GenericError; 5739 5740 heap_free(wic_palette); 5741 5742 return status; 5743 } 5744 5745 default: 5746 FIXME("unknown palette type %d\n", type); 5747 break; 5748 } 5749 5750 return InvalidParameter; 5751 } 5752