savepng.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /*
  2. * SDL_SavePNG -- libpng-based SDL_Surface writer.
  3. *
  4. * This code is free software, available under zlib/libpng license.
  5. * http://www.libpng.org/pub/png/src/libpng-LICENSE.txt
  6. */
  7. #include <SDL.h>
  8. #include <png.h>
  9. #define SUCCESS 0
  10. #define ERROR -1
  11. #define USE_ROW_POINTERS
  12. #if SDL_BYTEORDER == SDL_BIG_ENDIAN
  13. #define rmask 0xFF000000
  14. #define gmask 0x00FF0000
  15. #define bmask 0x0000FF00
  16. #define amask 0x000000FF
  17. #else
  18. #define rmask 0x000000FF
  19. #define gmask 0x0000FF00
  20. #define bmask 0x00FF0000
  21. #define amask 0xFF000000
  22. #endif
  23. /* libpng callbacks */
  24. static void png_error_SDL(png_structp ctx, png_const_charp str)
  25. {
  26. SDL_SetError("libpng: %s\n", str);
  27. }
  28. static void png_write_SDL(png_structp png_ptr, png_bytep data, png_size_t length)
  29. {
  30. SDL_RWops *rw = (SDL_RWops*)png_get_io_ptr(png_ptr);
  31. SDL_RWwrite(rw, data, sizeof(png_byte), length);
  32. }
  33. SDL_Surface *SDL_PNGFormatAlpha(SDL_Surface *src)
  34. {
  35. SDL_Surface *surf;
  36. SDL_Rect rect = { 0 };
  37. /* NO-OP for images < 32bpp and 32bpp images that already have Alpha channel */
  38. if (src->format->BitsPerPixel <= 24 || src->format->Amask) {
  39. src->refcount++;
  40. return src;
  41. }
  42. /* Convert 32bpp alpha-less image to 24bpp alpha-less image */
  43. rect.w = src->w;
  44. rect.h = src->h;
  45. surf = SDL_CreateRGBSurface(src->flags, src->w, src->h, 24,
  46. src->format->Rmask, src->format->Gmask, src->format->Bmask, 0);
  47. SDL_LowerBlit(src, &rect, surf, &rect);
  48. return surf;
  49. }
  50. int SDL_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst)
  51. {
  52. png_structp png_ptr;
  53. png_infop info_ptr;
  54. png_colorp pal_ptr;
  55. SDL_Palette *pal;
  56. int i, colortype;
  57. #ifdef USE_ROW_POINTERS
  58. png_bytep *row_pointers;
  59. #endif
  60. /* Initialize and do basic error checking */
  61. if (!dst)
  62. {
  63. SDL_SetError("Argument 2 to SDL_SavePNG_RW can't be NULL, expecting SDL_RWops*\n");
  64. return (ERROR);
  65. }
  66. if (!surface)
  67. {
  68. SDL_SetError("Argument 1 to SDL_SavePNG_RW can't be NULL, expecting SDL_Surface*\n");
  69. if (freedst) SDL_RWclose(dst);
  70. return (ERROR);
  71. }
  72. png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_error_SDL, NULL); /* err_ptr, err_fn, warn_fn */
  73. if (!png_ptr)
  74. {
  75. SDL_SetError("Unable to png_create_write_struct on %s\n", PNG_LIBPNG_VER_STRING);
  76. if (freedst) SDL_RWclose(dst);
  77. return (ERROR);
  78. }
  79. info_ptr = png_create_info_struct(png_ptr);
  80. if (!info_ptr)
  81. {
  82. SDL_SetError("Unable to png_create_info_struct\n");
  83. png_destroy_write_struct(&png_ptr, NULL);
  84. if (freedst) SDL_RWclose(dst);
  85. return (ERROR);
  86. }
  87. if (setjmp(png_jmpbuf(png_ptr))) /* All other errors, see also "png_error_SDL" */
  88. {
  89. png_destroy_write_struct(&png_ptr, &info_ptr);
  90. if (freedst) SDL_RWclose(dst);
  91. return (ERROR);
  92. }
  93. /* Setup our RWops writer */
  94. png_set_write_fn(png_ptr, dst, png_write_SDL, NULL); /* w_ptr, write_fn, flush_fn */
  95. /* Prepare chunks */
  96. colortype = PNG_COLOR_MASK_COLOR;
  97. if (surface->format->BytesPerPixel > 0
  98. && surface->format->BytesPerPixel <= 8
  99. && (pal = surface->format->palette))
  100. {
  101. colortype |= PNG_COLOR_MASK_PALETTE;
  102. pal_ptr = (png_colorp)malloc(pal->ncolors * sizeof(png_color));
  103. for (i = 0; i < pal->ncolors; i++) {
  104. pal_ptr[i].red = pal->colors[i].r;
  105. pal_ptr[i].green = pal->colors[i].g;
  106. pal_ptr[i].blue = pal->colors[i].b;
  107. }
  108. png_set_PLTE(png_ptr, info_ptr, pal_ptr, pal->ncolors);
  109. free(pal_ptr);
  110. }
  111. else if (surface->format->BytesPerPixel > 3 || surface->format->Amask)
  112. colortype |= PNG_COLOR_MASK_ALPHA;
  113. png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h, 8, colortype,
  114. PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
  115. // png_set_packing(png_ptr);
  116. /* Allow BGR surfaces */
  117. if (surface->format->Rmask == bmask
  118. && surface->format->Gmask == gmask
  119. && surface->format->Bmask == rmask)
  120. png_set_bgr(png_ptr);
  121. /* Write everything */
  122. png_write_info(png_ptr, info_ptr);
  123. #ifdef USE_ROW_POINTERS
  124. row_pointers = (png_bytep*) malloc(sizeof(png_bytep)*surface->h);
  125. for (i = 0; i < surface->h; i++)
  126. row_pointers[i] = (png_bytep)(Uint8*)surface->pixels + i * surface->pitch;
  127. png_write_image(png_ptr, row_pointers);
  128. free(row_pointers);
  129. #else
  130. for (i = 0; i < surface->h; i++)
  131. png_write_row(png_ptr, (png_bytep)(Uint8*)surface->pixels + i * surface->pitch);
  132. #endif
  133. png_write_end(png_ptr, info_ptr);
  134. /* Done */
  135. png_destroy_write_struct(&png_ptr, &info_ptr);
  136. if (freedst) SDL_RWclose(dst);
  137. return (SUCCESS);
  138. }