2009-04-05 6 views
4

J'ai écrit un patch simple pour ajouter des capacités d'économie de PNG au SDL_Image library. Cela fonctionne presque aussi. Le problème est, les couleurs sortent toutes brouillées, et je ne connais pas assez C pour comprendre ce qui ne va pas. Quelqu'un peut-il jeter un coup d'oeil à ceci et m'aider à le réparer?Comment enregistrer une surface SDL sur un PNG et obtenir les couleurs correctes?

utilisation:

  1. Charger une image PNG 256 couleurs avec IMG_LoadPNG_RW.
  2. Enregistrer avec IMG_SavePNG_RW.
  3. Voir si elles sont identiques ou non.

Patch:

Index: IMG_png.c 
=================================================================== 
--- IMG_png.c (revision 4475) 
+++ IMG_png.c (working copy) 
@@ -76,17 +76,25 @@ 
    png_infop (*png_create_info_struct) (png_structp png_ptr); 
    png_structp (*png_create_read_struct) (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn); 
    void (*png_destroy_read_struct) (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr); 
+ png_structp (*png_create_write_struct) (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn); 
+ void (*png_destroy_write_struct) (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr); 
    png_uint_32 (*png_get_IHDR) (png_structp png_ptr, png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, int *bit_depth, int *color_type, int *interlace_method, int *compression_method, int *filter_method); 
+ void (*png_set_IHDR) (png_structp png_ptr, png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, int color_type, int interlace_method, int compression_method, int filter_method); 
    png_voidp (*png_get_io_ptr) (png_structp png_ptr); 
    png_uint_32 (*png_get_tRNS) (png_structp png_ptr, png_infop info_ptr, png_bytep *trans, int *num_trans, png_color_16p *trans_values); 
    png_uint_32 (*png_get_valid) (png_structp png_ptr, png_infop info_ptr, png_uint_32 flag); 
    void (*png_read_image) (png_structp png_ptr, png_bytepp image); 
+ void (*png_write_image) (png_structp png_ptr, png_bytepp image); 
    void (*png_read_info) (png_structp png_ptr, png_infop info_ptr); 
+ void (*png_write_info) (png_structp png_ptr, png_infop info_ptr); 
+ void (*png_set_PLTE) (png_structp png_ptr, png_infop info_ptr, png_colorp palette, int num_palette); 
+ void (*png_write_end) (png_structp png_ptr, png_infop info_ptr); 
    void (*png_read_update_info) (png_structp png_ptr, png_infop info_ptr); 
    void (*png_set_expand) (png_structp png_ptr); 
    void (*png_set_gray_to_rgb) (png_structp png_ptr); 
    void (*png_set_packing) (png_structp png_ptr); 
    void (*png_set_read_fn) (png_structp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn); 
+ void (*png_set_write_fn) (png_structp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn); 
    void (*png_set_strip_16) (png_structp png_ptr); 
    int (*png_sig_cmp) (png_bytep sig, png_size_t start, png_size_t num_to_check); 
} lib; 
@@ -120,6 +128,20 @@ 
      SDL_UnloadObject(lib.handle); 
      return -1; 
     } 
+  lib.png_create_write_struct = 
+   (png_structp (*) (png_const_charp, png_voidp, png_error_ptr, png_error_ptr)) 
+   SDL_LoadFunction(lib.handle, "png_create_write_struct"); 
+  if (lib.png_create_write_struct == NULL) { 
+   SDL_UnloadObject(lib.handle); 
+   return -1; 
+  } 
+  lib.png_destroy_write_struct = 
+   (void (*) (png_structpp, png_infopp)) 
+   SDL_LoadFunction(lib.handle, "png_destroy_write_struct"); 
+  if (lib.png_destroy_write_struct == NULL) { 
+   SDL_UnloadObject(lib.handle); 
+   return -1; 
+  } 
     lib.png_get_IHDR = 
      (png_uint_32 (*) (png_structp, png_infop, png_uint_32 *, png_uint_32 *, int *, int *, int *, int *, int *)) 
      SDL_LoadFunction(lib.handle, "png_get_IHDR"); 
@@ -127,6 +149,13 @@ 
      SDL_UnloadObject(lib.handle); 
      return -1; 
     } 
+  lib.png_set_IHDR = 
+   (void (*) (png_structp, png_infop, png_uint_32, png_uint_32, int, int, int, int, int)) 
+   SDL_LoadFunction(lib.handle, "png_set_IHDR"); 
+  if (lib.png_set_IHDR == NULL) { 
+   SDL_UnloadObject(lib.handle); 
+   return -1; 
+  } 
     lib.png_get_io_ptr = 
      (png_voidp (*) (png_structp)) 
      SDL_LoadFunction(lib.handle, "png_get_io_ptr"); 
@@ -155,6 +184,13 @@ 
      SDL_UnloadObject(lib.handle); 
      return -1; 
     } 
+  lib.png_write_image = 
+   (void (*) (png_structp, png_bytepp)) 
+   SDL_LoadFunction(lib.handle, "png_write_image"); 
+  if (lib.png_write_image == NULL) { 
+   SDL_UnloadObject(lib.handle); 
+   return -1; 
+  } 
     lib.png_read_info = 
      (void (*) (png_structp, png_infop)) 
      SDL_LoadFunction(lib.handle, "png_read_info"); 
@@ -162,6 +198,27 @@ 
      SDL_UnloadObject(lib.handle); 
      return -1; 
     } 
+  lib.png_write_info = 
+   (void (*) (png_structp, png_infop)) 
+   SDL_LoadFunction(lib.handle, "png_write_info"); 
+  if (lib.png_write_info == NULL) { 
+   SDL_UnloadObject(lib.handle); 
+   return -1; 
+  } 
+  lib.png_set_PLTE = 
+   (void (*) (png_structp, png_infop, png_colorp, int)) 
+   SDL_LoadFunction(lib.handle, "png_set_PLTE"); 
+  if (lib.png_set_PLTE == NULL) { 
+   SDL_UnloadObject(lib.handle); 
+   return -1; 
+  } 
+  lib.png_write_end = 
+   (void (*) (png_structp, png_infop)) 
+   SDL_LoadFunction(lib.handle, "png_write_end"); 
+  if (lib.png_write_end == NULL) { 
+   SDL_UnloadObject(lib.handle); 
+   return -1; 
+  } 
     lib.png_read_update_info = 
      (void (*) (png_structp, png_infop)) 
      SDL_LoadFunction(lib.handle, "png_read_update_info"); 
@@ -197,6 +254,13 @@ 
      SDL_UnloadObject(lib.handle); 
      return -1; 
     } 
+  lib.png_set_write_fn = 
+   (void (*) (png_structp, png_voidp, png_rw_ptr)) 
+   SDL_LoadFunction(lib.handle, "png_set_write_fn"); 
+  if (lib.png_set_write_fn == NULL) { 
+   SDL_UnloadObject(lib.handle); 
+   return -1; 
+  } 
     lib.png_set_strip_16 = 
      (void (*) (png_structp)) 
      SDL_LoadFunction(lib.handle, "png_set_strip_16"); 
@@ -472,7 +536,7 @@ 
      palette->colors[i].g = i; 
      palette->colors[i].b = i; 
     } 
-  } else if (info_ptr->num_palette > 0) { 
+  } else if (info_ptr->num_palette > 0) { 
     palette->ncolors = info_ptr->num_palette; 
     for(i=0; i<info_ptr->num_palette; ++i) { 
      palette->colors[i].b = info_ptr->palette[i].blue; 
@@ -505,18 +569,167 @@ 
    return(surface); 
} 

-#else 
+static void png_write_data(png_structp ctx, png_bytep area, png_size_t size) 
+{ 
+ SDL_RWops *src; 

-/* See if an image is contained in a data source */ 
-int IMG_isPNG(SDL_RWops *src) 
+ src = (SDL_RWops *)lib.png_get_io_ptr(ctx); 
+ SDL_RWwrite(src, area, size, 1); 
+} 
+/* write a png file */ 
+int IMG_SavePNG_RW(SDL_Surface *surface, SDL_RWops *src) 
{ 
- return(0); 
-} 
+ png_structp png_ptr; 
+ png_infop info_ptr; 
+ png_colorp palette = NULL; 
+ int start; 
+ int colorType; 
+ int i; 
+ const char *error; 
+ SDL_Palette *sdlPalette; 
+ png_uint_32 height = surface->h; 
+ png_uint_32 width = surface->w; 
+ png_bytep *volatile row_pointers; 
+ int row; 

-/* Load a PNG type image from an SDL datasource */ 
-SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src) 
-{ 
- return(NULL); 
+ if (!src) { 
+  /* The error message has been set in SDL_RWFromFile */ 
+  return -1; 
+ } 
+ start = SDL_RWtell(src); 
+ 
+ if (IMG_InitPNG() < 0) { 
+  return -1; 
+ } 
+ 
+ /* Create and initialize the png_struct with the desired error handler 
+ * functions. If you want to use the default stderr and longjump method, 
+ * you can supply NULL for the last three parameters. We also check that 
+ * the library version is compatible with the one used at compile time, 
+ * in case we are using dynamically linked libraries. REQUIRED. 
+ */ 
+ png_ptr = NULL; info_ptr = NULL; 
+ 
+ /* Create the PNG loading context structure */ 
+ png_ptr = lib.png_create_read_struct(PNG_LIBPNG_VER_STRING, 
+      NULL,NULL,NULL); 
+ if (png_ptr == NULL){ 
+  error = "Couldn't allocate memory for PNG file or incompatible PNG dll"; 
+  goto done; 
+ } 
+ 
+ /* Allocate/initialize the memory for image information. REQUIRED. */ 
+ info_ptr = lib.png_create_info_struct(png_ptr); 
+ if (info_ptr == NULL) { 
+  error = "Couldn't create image information for PNG file"; 
+  goto done; 
+ } 
+ 
+ /* Set error handling if you are using setjmp/longjmp method (this is 
+ * the normal method of doing things with libpng). REQUIRED unless you 
+ * set up your own error handlers in png_create_read_struct() earlier. 
+ */ 
+ if (setjmp(png_ptr->jmpbuf)) { 
+  error = "Error reading the PNG file."; 
+  goto done; 
+ } 
+ 
+ /* Set up the output control */ 
+ lib.png_set_write_fn(png_ptr, src, png_write_data); 
+ 
+ /* Set the image information here. Width and height are up to 2^31, 
+  * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on 
+  * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, 
+  * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, 
+  * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or 
+  * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST 
+  * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED 
+  */ 
+ sdlPalette = surface->format->palette; 
+ if (sdlPalette) 
+ {  
+  colorType = PNG_COLOR_TYPE_PALETTE; 
+ } else if (surface->format->Amask) 
+ { 
+  colorType = PNG_COLOR_TYPE_RGB_ALPHA; 
+ } else 
+ { 
+  colorType = PNG_COLOR_TYPE_RGB; 
+ } 
+ lib.png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h, surface->format->BitsPerPixel, colorType, 
+  PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); 
+ 
+ /* set the palette if there is one. REQUIRED for indexed-color images */ 
+ if (colorType == PNG_COLOR_TYPE_PALETTE) 
+ { 
+  palette = (png_colorp) malloc(sdlPalette->ncolors * sizeof(png_color)); 
+ 
+  for(i=0; i < sdlPalette->ncolors; ++i) { 
+   palette[i].blue = sdlPalette->colors[i].b; 
+   palette[i].green = sdlPalette->colors[i].g; 
+   palette[i].red = sdlPalette->colors[i].r; 
+  } 
+  lib.png_set_PLTE(png_ptr, info_ptr, palette, sdlPalette->ncolors); 
+ } 
+ else 
+ { //not sure how to handle this 
+//  sig_bit.red = true_red_bit_depth; 
+//  sig_bit.green = true_green_bit_depth; 
+//  sig_bit.blue = true_blue_bit_depth; 
+  /* if the image has an alpha channel then */ 
+//  sig_bit.alpha = true_alpha_bit_depth; 
+//  png_set_sBIT(png_ptr, info_ptr, sig_bit); 
+ } 
+ 
+ /* Write the file header information. REQUIRED */ 
+ lib.png_write_info(png_ptr, info_ptr); 
+ 
+ /* The easiest way to write the image (you may have a different memory 
+  * layout, however, so choose what fits your needs best). You need to 
+  * use the first method if you aren't handling interlacing yourself. 
+  */ 
+ 
+ row_pointers = (png_bytep*) malloc(sizeof(png_bytep)*height); 
+ if ((row_pointers == NULL)) { 
+  error = "Out of memory"; 
+  goto done; 
+ } 
+ for (row = 0; row < (int)height; row++) { 
+  row_pointers[row] = (png_bytep) 
+    (Uint8 *)surface->pixels + row*surface->pitch; 
+ } 
+ 
+ if (height > PNG_UINT_32_MAX/sizeof(png_bytep)) 
+ { 
+  error = "Image is too tall to process in memory"; 
+  goto done; 
+ } 
+ 
+ /* Read the entire image in one go */ 
+ lib.png_write_image(png_ptr, row_pointers); 
+ 
+ /* It is REQUIRED to call this to finish writing the rest of the file */ 
+ lib.png_write_end(png_ptr, info_ptr); 
+ 
+done: /* Clean up and return */ 
+ if (png_ptr) { 
+  lib.png_destroy_write_struct(&png_ptr, &info_ptr); 
+ } 
+ if (row_pointers) { 
+  free(row_pointers); 
+ } 
+ if (palette) 
+ { 
+  free(palette); 
+ } 
+ if (error) { 
+  IMG_QuitPNG(); 
+  IMG_SetError(error); 
+  return(-1); 
+ } else { 
+  IMG_QuitPNG(); 
+ } 
+ return(0); 
} 

#endif /* LOAD_PNG */ 
Index: SDL_image.h 
=================================================================== 
--- SDL_image.h (revision 4475) 
+++ SDL_image.h (working copy) 
@@ -107,6 +107,8 @@ 

extern DECLSPEC SDL_Surface * SDLCALL IMG_ReadXPMFromArray(char **xpm); 

+extern DECLSPEC int SDLCALL IMG_SavePNG_RW(SDL_Surface *surface, SDL_RWops *src); 
+ 
/* We'll use SDL for reporting errors */ 
#define IMG_SetError SDL_SetError 
#define IMG_GetError SDL_GetError 

Répondre

2

J'ai réussi à le faire fonctionner. J'avais besoin d'appeler png_set_bgr() pour corriger la palette.

2

Sauvegarde surfaces SDL comme des images PNG est un problème qui a été résolu sur la bande de manière répétée:
http://www.bishoujo.us/svn/renpy/trunk/module/
http://lists.libsdl.org/pipermail/sdl-libsdl.org/2006-May/055936.html
http://www.os4depot.net/index.php?function=showfile&file=development/example/sdlpngsavesurf.lha
http://encelo.netsons.org/programming/sdl

Plutôt que demander de l'aide deb L'utilisation d'une quantité importante de code, la comparaison de votre code avec ces autres implémentations et le fait de savoir où ils diffèrent, ou de demander laquelle de ces autres implémentations est la meilleure à utiliser, serait probablement plus productive. En outre, patcher la bibliothèque SDL_image pourrait être une solution moins robuste que d'écrire des bibliothèques dépendantes séparées pour implémenter l'enregistrement PNG. De cette façon, la prochaine mise à niveau SDL ne cassera pas votre code. Si vous envisagiez de fournir le correctif à SDL, toutes les autres implémentations mentionnées remontent à quelques années. Je ne pense pas que SDL_image n'inclue pas l'enregistrement PNG par manque de personne voulant ou être capable de le coder.

+0

Merci beaucoup. C'est très utile. –

Questions connexes