1///////////////////////////////////////////////////////////////////////////// 2// Name: src/common/imagepng.cpp 3// Purpose: wxImage PNG handler 4// Author: Robert Roebling 5// RCS-ID: $Id: imagpng.cpp 67009 2011-02-24 08:42:57Z JS $ 6// Copyright: (c) Robert Roebling 7// Licence: wxWindows licence 8///////////////////////////////////////////////////////////////////////////// 9 10// ============================================================================ 11// declarations 12// ============================================================================ 13 14// ---------------------------------------------------------------------------- 15// headers 16// ---------------------------------------------------------------------------- 17 18// For compilers that support precompilation, includes "wx.h". 19#include "wx/wxprec.h" 20 21#ifdef __BORLANDC__ 22 #pragma hdrstop 23#endif 24 25#if wxUSE_IMAGE && wxUSE_LIBPNG 26 27#include "wx/imagpng.h" 28 29#ifndef WX_PRECOMP 30 #include "wx/log.h" 31 #include "wx/app.h" 32 #include "wx/bitmap.h" 33 #include "wx/module.h" 34#endif 35 36#include "png.h" 37#include "wx/filefn.h" 38#include "wx/wfstream.h" 39#include "wx/intl.h" 40#include "wx/palette.h" 41 42// For memcpy 43#include <string.h> 44 45#ifdef __SALFORDC__ 46#ifdef FAR 47#undef FAR 48#endif 49#endif 50 51// ---------------------------------------------------------------------------- 52// constants 53// ---------------------------------------------------------------------------- 54 55// image can not have any transparent pixels at all, have only 100% opaque 56// and/or 100% transparent pixels in which case a simple mask is enough to 57// store this information in wxImage or have a real alpha channel in which case 58// we need to have it in wxImage as well 59enum Transparency 60{ 61 Transparency_None, 62 Transparency_Mask, 63 Transparency_Alpha 64}; 65 66// ---------------------------------------------------------------------------- 67// local functions 68// ---------------------------------------------------------------------------- 69 70// return the kind of transparency needed for this image assuming that it does 71// have transparent pixels, i.e. either Transparency_Alpha or Transparency_Mask 72static Transparency 73CheckTransparency(unsigned char **lines, 74 png_uint_32 x, png_uint_32 y, png_uint_32 w, png_uint_32 h, 75 size_t numColBytes); 76 77// init the alpha channel for the image and fill it with 1s up to (x, y) 78static unsigned char *InitAlpha(wxImage *image, png_uint_32 x, png_uint_32 y); 79 80// find a free colour for the mask in the PNG data array 81static void 82FindMaskColour(unsigned char **lines, png_uint_32 width, png_uint_32 height, 83 unsigned char& rMask, unsigned char& gMask, unsigned char& bMask); 84 85// is the pixel with this value of alpha a fully opaque one? 86static inline 87bool IsOpaque(unsigned char a) 88{ 89 return a == 0xff; 90} 91 92// is the pixel with this value of alpha a fully transparent one? 93static inline 94bool IsTransparent(unsigned char a) 95{ 96 return !a; 97} 98 99// ============================================================================ 100// wxPNGHandler implementation 101// ============================================================================ 102 103IMPLEMENT_DYNAMIC_CLASS(wxPNGHandler,wxImageHandler) 104 105#if wxUSE_STREAMS 106 107#ifndef PNGLINKAGEMODE 108 #ifdef PNGAPI 109 #define PNGLINKAGEMODE PNGAPI 110 #elif defined(__WATCOMC__) 111 // we need an explicit cdecl for Watcom, at least according to 112 // 113 // http://sf.net/tracker/index.php?func=detail&aid=651492&group_id=9863&atid=109863 114 // 115 // more testing is needed for this however, please remove this comment 116 // if you can confirm that my fix works with Watcom 11 117 #define PNGLINKAGEMODE cdecl 118 #else 119 #define PNGLINKAGEMODE LINKAGEMODE 120 #endif 121#endif 122 123 124// VS: wxPNGInfoStruct declared below is a hack that needs some explanation. 125// First, let me describe what's the problem: libpng uses jmp_buf in 126// its png_struct structure. Unfortunately, this structure is 127// compiler-specific and may vary in size, so if you use libpng compiled 128// as DLL with another compiler than the main executable, it may not work 129// (this is for example the case with wxMGL port and SciTech MGL library 130// that provides custom runtime-loadable libpng implementation with jmpbuf 131// disabled altogether). Luckily, it is still possible to use setjmp() & 132// longjmp() as long as the structure is not part of png_struct. 133// 134// Sadly, there's no clean way to attach user-defined data to png_struct. 135// There is only one customizable place, png_struct.io_ptr, which is meant 136// only for I/O routines and is set with png_set_read_fn or 137// png_set_write_fn. The hacky part is that we use io_ptr to store 138// a pointer to wxPNGInfoStruct that holds I/O structures _and_ jmp_buf. 139 140struct wxPNGInfoStruct 141{ 142 jmp_buf jmpbuf; 143 bool verbose; 144 145 union 146 { 147 wxInputStream *in; 148 wxOutputStream *out; 149 } stream; 150}; 151 152#define WX_PNG_INFO(png_ptr) ((wxPNGInfoStruct*)png_get_io_ptr(png_ptr)) 153 154// ---------------------------------------------------------------------------- 155// helper functions 156// ---------------------------------------------------------------------------- 157 158extern "C" 159{ 160 161void PNGLINKAGEMODE wx_PNG_stream_reader( png_structp png_ptr, png_bytep data, 162 png_size_t length ) 163{ 164 WX_PNG_INFO(png_ptr)->stream.in->Read(data, length); 165} 166 167void PNGLINKAGEMODE wx_PNG_stream_writer( png_structp png_ptr, png_bytep data, 168 png_size_t length ) 169{ 170 WX_PNG_INFO(png_ptr)->stream.out->Write(data, length); 171} 172 173void 174PNGLINKAGEMODE wx_png_warning(png_structp png_ptr, png_const_charp message) 175{ 176 wxPNGInfoStruct *info = png_ptr ? WX_PNG_INFO(png_ptr) : NULL; 177 if ( !info || info->verbose ) 178 wxLogWarning( wxString::FromAscii(message) ); 179} 180 181// from pngerror.c 182// so that the libpng doesn't send anything on stderr 183void 184PNGLINKAGEMODE wx_png_error(png_structp png_ptr, png_const_charp message) 185{ 186 wx_png_warning(NULL, message); 187 188 // we're not using libpng built-in jump buffer (see comment before 189 // wxPNGInfoStruct above) so we have to return ourselves, otherwise libpng 190 // would just abort 191 longjmp(WX_PNG_INFO(png_ptr)->jmpbuf, 1); 192} 193 194} // extern "C" 195 196// ---------------------------------------------------------------------------- 197// LoadFile() helpers 198// ---------------------------------------------------------------------------- 199 200// determine the kind of transparency we need for this image: if the only alpha 201// values it has are 0 (transparent) and 0xff (opaque) then we can simply 202// create a mask for it, we should be ok with a simple mask but otherwise we 203// need a full blown alpha channel in wxImage 204// 205// parameters: 206// lines raw PNG data 207// x, y starting position 208// w, h size of the image 209// numColBytes number of colour bytes (1 for grey scale, 3 for RGB) 210// (NB: alpha always follows the colour bytes) 211Transparency 212CheckTransparency(unsigned char **lines, 213 png_uint_32 x, png_uint_32 y, png_uint_32 w, png_uint_32 h, 214 size_t numColBytes) 215{ 216 // suppose that a mask will suffice and check all the remaining alpha 217 // values to see if it does 218 for ( ; y < h; y++ ) 219 { 220 // each pixel is numColBytes+1 bytes, offset into the current line by 221 // the current x position 222 unsigned const char *ptr = lines[y] + (x * (numColBytes + 1)); 223 224 for ( png_uint_32 x2 = x; x2 < w; x2++ ) 225 { 226 // skip the grey or colour byte(s) 227 ptr += numColBytes; 228 229 unsigned char a2 = *ptr++; 230 231 if ( !IsTransparent(a2) && !IsOpaque(a2) ) 232 { 233 // not fully opaque nor fully transparent, hence need alpha 234 return Transparency_Alpha; 235 } 236 } 237 238 // during the next loop iteration check all the pixels in the row 239 x = 0; 240 } 241 242 // mask will be enough 243 return Transparency_Mask; 244} 245 246unsigned char *InitAlpha(wxImage *image, png_uint_32 x, png_uint_32 y) 247{ 248 // create alpha channel 249 image->SetAlpha(); 250 251 unsigned char *alpha = image->GetAlpha(); 252 253 // set alpha for the pixels we had so far 254 png_uint_32 end = y * image->GetWidth() + x; 255 for ( png_uint_32 i = 0; i < end; i++ ) 256 { 257 // all the previous pixels were opaque 258 *alpha++ = 0xff; 259 } 260 261 return alpha; 262} 263 264void 265FindMaskColour(unsigned char **lines, png_uint_32 width, png_uint_32 height, 266 unsigned char& rMask, unsigned char& gMask, unsigned char& bMask) 267{ 268 // choosing the colour for the mask is more 269 // difficult: we need to iterate over the entire 270 // image for this in order to choose an unused 271 // colour (this is not very efficient but what else 272 // can we do?) 273 wxImageHistogram h; 274 unsigned nentries = 0; 275 unsigned char r2, g2, b2; 276 for ( png_uint_32 y2 = 0; y2 < height; y2++ ) 277 { 278 const unsigned char *p = lines[y2]; 279 for ( png_uint_32 x2 = 0; x2 < width; x2++ ) 280 { 281 r2 = *p++; 282 g2 = *p++; 283 b2 = *p++; 284 ++p; // jump over alpha 285 286 wxImageHistogramEntry& 287 entry = h[wxImageHistogram:: MakeKey(r2, g2, b2)]; 288 289 if ( entry.value++ == 0 ) 290 entry.index = nentries++; 291 } 292 } 293 294 if ( !h.FindFirstUnusedColour(&rMask, &gMask, &bMask) ) 295 { 296 wxLogWarning(_("Too many colours in PNG, the image may be slightly blurred.")); 297 298 // use a fixed mask colour and we'll fudge 299 // the real pixels with this colour (see 300 // below) 301 rMask = 0xfe; 302 gMask = 0; 303 bMask = 0xff; 304 } 305} 306 307// ---------------------------------------------------------------------------- 308// reading PNGs 309// ---------------------------------------------------------------------------- 310 311bool wxPNGHandler::DoCanRead( wxInputStream& stream ) 312{ 313 unsigned char hdr[4]; 314 315 if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) 316 return false; 317 318 return memcmp(hdr, "\211PNG", WXSIZEOF(hdr)) == 0; 319} 320 321// convert data from RGB to wxImage format 322static 323void CopyDataFromPNG(wxImage *image, 324 unsigned char **lines, 325 png_uint_32 width, 326 png_uint_32 height, 327 int color_type) 328{ 329 Transparency transparency = Transparency_None; 330 331 // only non NULL if transparency == Transparency_Alpha 332 unsigned char *alpha = NULL; 333 334 // RGB of the mask colour if transparency == Transparency_Mask 335 // (but init them anyhow to avoid compiler warnings) 336 unsigned char rMask = 0, 337 gMask = 0, 338 bMask = 0; 339 340 unsigned char *ptrDst = image->GetData(); 341 if ( !(color_type & PNG_COLOR_MASK_COLOR) ) 342 { 343 // grey image: GAGAGA... where G == grey component and A == alpha 344 for ( png_uint_32 y = 0; y < height; y++ ) 345 { 346 const unsigned char *ptrSrc = lines[y]; 347 for ( png_uint_32 x = 0; x < width; x++ ) 348 { 349 unsigned char g = *ptrSrc++; 350 unsigned char a = *ptrSrc++; 351 352 // the first time we encounter a transparent pixel we must 353 // decide about what to do about them 354 if ( !IsOpaque(a) && transparency == Transparency_None ) 355 { 356 // we'll need at least the mask for this image and 357 // maybe even full alpha channel info: the former is 358 // only enough if we have alpha values of 0 and 0xff 359 // only, otherwisewe need the latter 360 transparency = CheckTransparency 361 ( 362 lines, 363 x, y, 364 width, height, 365 1 366 ); 367 368 if ( transparency == Transparency_Mask ) 369 { 370 // let's choose this colour for the mask: this is 371 // not a problem here as all the other pixels are 372 // grey, i.e. R == G == B which is not the case for 373 // this one so no confusion is possible 374 rMask = 0xff; 375 gMask = 0; 376 bMask = 0xff; 377 } 378 else // transparency == Transparency_Alpha 379 { 380 alpha = InitAlpha(image, x, y); 381 } 382 } 383 384 switch ( transparency ) 385 { 386 case Transparency_Mask: 387 if ( IsTransparent(a) ) 388 { 389 *ptrDst++ = rMask; 390 *ptrDst++ = gMask; 391 *ptrDst++ = bMask; 392 break; 393 } 394 // else: !transparent 395 396 // must be opaque then as otherwise we shouldn't be 397 // using the mask at all 398 wxASSERT_MSG( IsOpaque(a), _T("logic error") ); 399 400 // fall through 401 402 case Transparency_Alpha: 403 if ( alpha ) 404 *alpha++ = a; 405 // fall through 406 407 case Transparency_None: 408 *ptrDst++ = g; 409 *ptrDst++ = g; 410 *ptrDst++ = g; 411 break; 412 } 413 } 414 } 415 } 416 else // colour image: RGBRGB... 417 { 418 for ( png_uint_32 y = 0; y < height; y++ ) 419 { 420 const unsigned char *ptrSrc = lines[y]; 421 for ( png_uint_32 x = 0; x < width; x++ ) 422 { 423 unsigned char r = *ptrSrc++; 424 unsigned char g = *ptrSrc++; 425 unsigned char b = *ptrSrc++; 426 unsigned char a = *ptrSrc++; 427 428 // the logic here is the same as for the grey case except 429 // where noted 430 if ( !IsOpaque(a) && transparency == Transparency_None ) 431 { 432 transparency = CheckTransparency 433 ( 434 lines, 435 x, y, 436 width, height, 437 3 438 ); 439 440 if ( transparency == Transparency_Mask ) 441 { 442 FindMaskColour(lines, width, height, 443 rMask, gMask, bMask); 444 } 445 else // transparency == Transparency_Alpha 446 { 447 alpha = InitAlpha(image, x, y); 448 } 449 450 } 451 452 switch ( transparency ) 453 { 454 case Transparency_Mask: 455 if ( IsTransparent(a) ) 456 { 457 *ptrDst++ = rMask; 458 *ptrDst++ = gMask; 459 *ptrDst++ = bMask; 460 break; 461 } 462 else // !transparent 463 { 464 // must be opaque then as otherwise we shouldn't be 465 // using the mask at all 466 wxASSERT_MSG( IsOpaque(a), _T("logic error") ); 467 468 // if we couldn't find a unique colour for the 469 // mask, we can have real pixels with the same 470 // value as the mask and it's better to slightly 471 // change their colour than to make them 472 // transparent 473 if ( r == rMask && g == gMask && b == bMask ) 474 { 475 r++; 476 } 477 } 478 479 // fall through 480 481 case Transparency_Alpha: 482 if ( alpha ) 483 *alpha++ = a; 484 // fall through 485 486 case Transparency_None: 487 *ptrDst++ = r; 488 *ptrDst++ = g; 489 *ptrDst++ = b; 490 break; 491 } 492 } 493 } 494 } 495 496 if ( transparency == Transparency_Mask ) 497 { 498 image->SetMaskColour(rMask, gMask, bMask); 499 } 500} 501 502// temporarily disable the warning C4611 (interaction between '_setjmp' and 503// C++ object destruction is non-portable) - I don't see any dtors here 504#ifdef __VISUALC__ 505 #pragma warning(disable:4611) 506#endif /* VC++ */ 507 508bool 509wxPNGHandler::LoadFile(wxImage *image, 510 wxInputStream& stream, 511 bool verbose, 512 int WXUNUSED(index)) 513{ 514 // VZ: as this function uses setjmp() the only fool-proof error handling 515 // method is to use goto (setjmp is not really C++ dtors friendly...) 516 517 unsigned char **lines = NULL; 518 png_infop info_ptr = (png_infop) NULL; 519 wxPNGInfoStruct wxinfo; 520 521 png_uint_32 i, width, height = 0; 522 int bit_depth, color_type, interlace_type; 523 524 wxinfo.verbose = verbose; 525 wxinfo.stream.in = &stream; 526 527 image->Destroy(); 528 529 png_structp png_ptr = png_create_read_struct 530 ( 531 PNG_LIBPNG_VER_STRING, 532 NULL, 533 wx_png_error, 534 wx_png_warning 535 ); 536 if (!png_ptr) 537 goto error; 538 539 // NB: please see the comment near wxPNGInfoStruct declaration for 540 // explanation why this line is mandatory 541 png_set_read_fn( png_ptr, &wxinfo, wx_PNG_stream_reader); 542 543 info_ptr = png_create_info_struct( png_ptr ); 544 if (!info_ptr) 545 goto error; 546 547 if (setjmp(wxinfo.jmpbuf)) 548 goto error; 549 550 png_read_info( png_ptr, info_ptr ); 551 png_get_IHDR( png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, (int*) NULL, (int*) NULL ); 552 553 if (color_type == PNG_COLOR_TYPE_PALETTE) 554 png_set_expand( png_ptr ); 555 556 // Fix for Bug [ 439207 ] Monochrome PNG images come up black 557 if (bit_depth < 8) 558 png_set_expand( png_ptr ); 559 560 png_set_strip_16( png_ptr ); 561 png_set_packing( png_ptr ); 562 if (png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS)) 563 png_set_expand( png_ptr ); 564 png_set_filler( png_ptr, 0xff, PNG_FILLER_AFTER ); 565 566 image->Create((int)width, (int)height, (bool) false /* no need to init pixels */); 567 568 if (!image->Ok()) 569 goto error; 570 571 // initialize all line pointers to NULL to ensure that they can be safely 572 // free()d if an error occurs before all of them could be allocated 573 lines = (unsigned char **)calloc(height, sizeof(unsigned char *)); 574 if ( !lines ) 575 goto error; 576 577 for (i = 0; i < height; i++) 578 { 579 if ((lines[i] = (unsigned char *)malloc( (size_t)(width * (sizeof(unsigned char) * 4)))) == NULL) 580 goto error; 581 } 582 583 png_read_image( png_ptr, lines ); 584 png_read_end( png_ptr, info_ptr ); 585 586#if wxUSE_PALETTE 587 if (color_type == PNG_COLOR_TYPE_PALETTE) 588 { 589 int ncolors = 0; 590 png_colorp palette; 591 png_get_PLTE( png_ptr, info_ptr, &palette, &ncolors); 592 unsigned char* r = new unsigned char[ncolors]; 593 unsigned char* g = new unsigned char[ncolors]; 594 unsigned char* b = new unsigned char[ncolors]; 595 int j; 596 597 for (j = 0; j < ncolors; j++) 598 { 599 r[j] = palette[j].red; 600 g[j] = palette[j].green; 601 b[j] = palette[j].blue; 602 } 603 604 image->SetPalette(wxPalette(ncolors, r, g, b)); 605 delete[] r; 606 delete[] g; 607 delete[] b; 608 } 609#endif // wxUSE_PALETTE 610 611 png_destroy_read_struct( &png_ptr, &info_ptr, (png_infopp) NULL ); 612 613 // loaded successfully, now init wxImage with this data 614 CopyDataFromPNG(image, lines, width, height, color_type); 615 616 for ( i = 0; i < height; i++ ) 617 free( lines[i] ); 618 free( lines ); 619 620 return true; 621 622error: 623 if (verbose) 624 wxLogError(_("Couldn't load a PNG image - file is corrupted or not enough memory.")); 625 626 if ( image->Ok() ) 627 { 628 image->Destroy(); 629 } 630 631 if ( lines ) 632 { 633 for ( unsigned int n = 0; n < height; n++ ) 634 free( lines[n] ); 635 636 free( lines ); 637 } 638 639 if ( png_ptr ) 640 { 641 if ( info_ptr ) 642 { 643 png_destroy_read_struct( &png_ptr, &info_ptr, (png_infopp) NULL ); 644 free(info_ptr); 645 } 646 else 647 png_destroy_read_struct( &png_ptr, (png_infopp) NULL, (png_infopp) NULL ); 648 } 649 return false; 650} 651 652// ---------------------------------------------------------------------------- 653// writing PNGs 654// ---------------------------------------------------------------------------- 655 656bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose ) 657{ 658 wxPNGInfoStruct wxinfo; 659 660 wxinfo.verbose = verbose; 661 wxinfo.stream.out = &stream; 662 663 png_structp png_ptr = png_create_write_struct 664 ( 665 PNG_LIBPNG_VER_STRING, 666 NULL, 667 wx_png_error, 668 wx_png_warning 669 ); 670 if (!png_ptr) 671 { 672 if (verbose) 673 wxLogError(_("Couldn't save PNG image.")); 674 return false; 675 } 676 677 png_infop info_ptr = png_create_info_struct(png_ptr); 678 if (info_ptr == NULL) 679 { 680 png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); 681 if (verbose) 682 wxLogError(_("Couldn't save PNG image.")); 683 return false; 684 } 685 686 if (setjmp(wxinfo.jmpbuf)) 687 { 688 png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); 689 if (verbose) 690 wxLogError(_("Couldn't save PNG image.")); 691 return false; 692 } 693 694 // NB: please see the comment near wxPNGInfoStruct declaration for 695 // explanation why this line is mandatory 696 png_set_write_fn( png_ptr, &wxinfo, wx_PNG_stream_writer, NULL); 697 698 const int iColorType = image->HasOption(wxIMAGE_OPTION_PNG_FORMAT) 699 ? image->GetOptionInt(wxIMAGE_OPTION_PNG_FORMAT) 700 : wxPNG_TYPE_COLOUR; 701 const int iBitDepth = image->HasOption(wxIMAGE_OPTION_PNG_BITDEPTH) 702 ? image->GetOptionInt(wxIMAGE_OPTION_PNG_BITDEPTH) 703 : 8; 704 705 wxASSERT_MSG( iBitDepth == 8 || iBitDepth == 16, 706 _T("PNG bit depth must be 8 or 16") ); 707 708 bool bHasAlpha = image->HasAlpha(); 709 bool bHasMask = image->HasMask(); 710 bool bUseAlpha = bHasAlpha || bHasMask; 711 712 int iPngColorType; 713 if ( iColorType==wxPNG_TYPE_COLOUR ) 714 { 715 iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_RGB_ALPHA 716 : PNG_COLOR_TYPE_RGB; 717 } 718 else 719 { 720 iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_GRAY_ALPHA 721 : PNG_COLOR_TYPE_GRAY; 722 } 723 724 png_set_IHDR( png_ptr, info_ptr, image->GetWidth(), image->GetHeight(), 725 iBitDepth, iPngColorType, 726 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, 727 PNG_FILTER_TYPE_BASE); 728 729 int iElements; 730 png_color_8 sig_bit; 731 732 if ( iPngColorType & PNG_COLOR_MASK_COLOR ) 733 { 734 sig_bit.red = 735 sig_bit.green = 736 sig_bit.blue = (png_byte)iBitDepth; 737 iElements = 3; 738 } 739 else // grey 740 { 741 sig_bit.gray = (png_byte)iBitDepth; 742 iElements = 1; 743 } 744 745 if ( iPngColorType & PNG_COLOR_MASK_ALPHA ) 746 { 747 sig_bit.alpha = (png_byte)iBitDepth; 748 iElements++; 749 } 750 751 if ( iBitDepth == 16 ) 752 iElements *= 2; 753 754 png_set_sBIT( png_ptr, info_ptr, &sig_bit ); 755 png_write_info( png_ptr, info_ptr ); 756 png_set_shift( png_ptr, &sig_bit ); 757 png_set_packing( png_ptr ); 758 759 unsigned char * 760 data = (unsigned char *)malloc( image->GetWidth() * iElements ); 761 if ( !data ) 762 { 763 png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); 764 return false; 765 } 766 767 unsigned char * 768 pAlpha = (unsigned char *)(bHasAlpha ? image->GetAlpha() : NULL); 769 int iHeight = image->GetHeight(); 770 int iWidth = image->GetWidth(); 771 772 unsigned char uchMaskRed = 0, uchMaskGreen = 0, uchMaskBlue = 0; 773 774 if ( bHasMask ) 775 { 776 uchMaskRed = image->GetMaskRed(); 777 uchMaskGreen = image->GetMaskGreen(); 778 uchMaskBlue = image->GetMaskBlue(); 779 } 780 781 unsigned char *pColors = image->GetData(); 782 783 for (int y = 0; y != iHeight; ++y) 784 { 785 unsigned char *pData = data; 786 for (int x = 0; x != iWidth; x++) 787 { 788 unsigned char uchRed = *pColors++; 789 unsigned char uchGreen = *pColors++; 790 unsigned char uchBlue = *pColors++; 791 792 switch ( iColorType ) 793 { 794 default: 795 wxFAIL_MSG( _T("unknown wxPNG_TYPE_XXX") ); 796 // fall through 797 798 case wxPNG_TYPE_COLOUR: 799 *pData++ = uchRed; 800 if ( iBitDepth == 16 ) 801 *pData++ = 0; 802 *pData++ = uchGreen; 803 if ( iBitDepth == 16 ) 804 *pData++ = 0; 805 *pData++ = uchBlue; 806 if ( iBitDepth == 16 ) 807 *pData++ = 0; 808 break; 809 810 case wxPNG_TYPE_GREY: 811 { 812 // where do these coefficients come from? maybe we 813 // should have image options for them as well? 814 unsigned uiColor = 815 (unsigned) (76.544*(unsigned)uchRed + 816 150.272*(unsigned)uchGreen + 817 36.864*(unsigned)uchBlue); 818 819 *pData++ = (unsigned char)((uiColor >> 8) & 0xFF); 820 if ( iBitDepth == 16 ) 821 *pData++ = (unsigned char)(uiColor & 0xFF); 822 } 823 break; 824 825 case wxPNG_TYPE_GREY_RED: 826 *pData++ = uchRed; 827 if ( iBitDepth == 16 ) 828 *pData++ = 0; 829 break; 830 } 831 832 if ( bUseAlpha ) 833 { 834 unsigned char uchAlpha = 255; 835 if ( bHasAlpha ) 836 uchAlpha = *pAlpha++; 837 838 if ( bHasMask ) 839 { 840 if ( (uchRed == uchMaskRed) 841 && (uchGreen == uchMaskGreen) 842 && (uchBlue == uchMaskBlue) ) 843 uchAlpha = 0; 844 } 845 846 *pData++ = uchAlpha; 847 if ( iBitDepth == 16 ) 848 *pData++ = 0; 849 } 850 } 851 852 png_bytep row_ptr = data; 853 png_write_rows( png_ptr, &row_ptr, 1 ); 854 } 855 856 free(data); 857 png_write_end( png_ptr, info_ptr ); 858 png_destroy_write_struct( &png_ptr, (png_infopp)&info_ptr ); 859 860 return true; 861} 862 863#ifdef __VISUALC__ 864 #pragma warning(default:4611) 865#endif /* VC++ */ 866 867#endif // wxUSE_STREAMS 868 869#endif // wxUSE_LIBPNG 870