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