modificaciones a mod_map para soporte de pngs de 24 bits con transparencias.

Started by DCelso, November 09, 2010, 03:48:39 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

DCelso

Hola Splinter, dejo aquí mi sugerencia para mod_map para soporte de este tipo de imágenes que hemos visto que puede hacer mtpaint y que los visores de PNGs leen correctamente.
Bueno y ya para premio, podemos hacer que guarde bennu este formato cuando esté la pantalla en modo 16 bits ya que es el más próximo y fiel a la información guardada en memoria para este modo (y así ahorramos un poco de espacio, porque hasta ahora guardaba pngs de 32 bits con canales alfa a 0 o ff :D).

Resalto en negrita los cambios.
Archivo file_png.c

/*
*  Copyright � 2006-2010 SplinterGU (Fenix/Bennugd)
*  Copyright � 2002-2006 Fenix Team (Fenix)
*  Copyright � 1999-2002 Jos� Luis Cebri�n Pag�e (Fenix)
*
*  This file is part of Bennu - Game Development
*
*  Bennu is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  Bennu is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
*
*/

/* --------------------------------------------------------------------------- */

#include <png.h>

#include "mod_map.h"

/* --------------------------------------------------------------------------- */

/*
*  GLOBAL VARIABLES
*/

/* --------------------------------------------------------------------------- */

static uint8_t colors[256 * 3] ;

static file * png ;

/* --------------------------------------------------------------------------- */

static void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length )
{
    file_read( png, data, length ) ;
}

GRAPH * gr_read_png( const char * filename )
{
GRAPH * bitmap ;
    unsigned int n, x ;
    uint16_t * ptr ;
    uint32_t * ptr32 ;
    uint32_t * orig ;
    uint32_t * row ;
    uint32_t Rshift, Gshift, Bshift ;
    uint32_t Rmask, Gmask, Bmask ;

    png_bytep * rowpointers ;

    png_structp png_ptr ;
    png_infop info_ptr, end_info ;
    png_uint_32 width, height, rowbytes;
    int depth, color ;

    /* Abre el fichero y se asegura de que screen est� inicializada */

    png = file_open( filename, "rb" ) ;
    if ( !png ) return NULL;

    /* Prepara las estructuras internas */

    png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 ) ;
    if ( !png_ptr )
    {
        file_close( png );
        return NULL;
    }

    info_ptr = png_create_info_struct( png_ptr ) ;
    end_info = png_create_info_struct( png_ptr ) ;
    if ( !info_ptr || !end_info )
    {
        png_destroy_read_struct( &png_ptr, &info_ptr, &end_info ) ;
        file_close( png ) ;
        return NULL;
    }

    /* Rutina de error */

    if ( setjmp( png_ptr->jmpbuf ) )
    {
        png_destroy_read_struct( &png_ptr, &info_ptr, &end_info ) ;
        file_close( png ) ;
        return NULL;
    }

    /* Recupera informaci�n sobre el PNG */

    png_set_read_fn( png_ptr, 0, user_read_data ) ;
    png_read_info( png_ptr, info_ptr ) ;
    png_get_IHDR( png_ptr, info_ptr, &width, &height, &depth, &color, 0, 0, 0 ) ;

    row = malloc( sizeof( uint32_t ) * width );
    if ( !row )
    {
        png_destroy_read_struct( &png_ptr, &info_ptr, &end_info ) ;
        file_close( png ) ;
        return NULL;
    }

    rowpointers = malloc( sizeof( png_bytep ) * height );
    if ( !rowpointers )
    {
        png_destroy_read_struct( &png_ptr, &info_ptr, &end_info ) ;
        free( row ) ;
        file_close( png ) ;
        return NULL;
    }

    /* Configura los distintos modos disponibles */

    if ( color == PNG_COLOR_TYPE_GRAY /*|| color == PNG_COLOR_TYPE_GRAY_ALPHA*/ )
    {
        png_set_gray_to_rgb( png_ptr );
        if ( color == PNG_COLOR_TYPE_GRAY ) png_set_filler( png_ptr, 0xFF, PNG_FILLER_AFTER ) ;
    }

    if ( depth == 16 ) png_set_strip_16( png_ptr ) ;

    if ( color == PNG_COLOR_TYPE_RGB ) png_set_filler( png_ptr, 0xFF, PNG_FILLER_AFTER ) ;

    png_set_bgr( png_ptr ) ;

    /* Recupera el fichero, convirtiendo a 16 bits si es preciso */

    rowbytes = png_get_rowbytes( png_ptr, info_ptr ) ;
    bitmap = bitmap_new( 0, width, height, ( color == PNG_COLOR_TYPE_PALETTE ) ? 8 : ( sys_pixel_format->depth == 16 ? 16 : 32 ) ) ;
    if ( !bitmap )
    {
        png_destroy_read_struct( &png_ptr, &info_ptr, &end_info ) ;
        free( rowpointers ) ;
        free( row ) ;
        file_close( png ) ;
        return NULL;
    }

    if ( color == PNG_COLOR_TYPE_PALETTE )
    {
        /* Read the color palette */

        png_colorp png_palette = ( png_colorp ) png_malloc( png_ptr, 256 * sizeof( png_color ) ) ;
        if ( !png_palette )
        {
            png_destroy_read_struct( &png_ptr, &info_ptr, &end_info ) ;
            free( rowpointers ) ;
            free( row ) ;
            file_close( png ) ;
            return NULL;
        }
        png_get_PLTE( png_ptr, info_ptr, &png_palette, ( int * ) &n ) ;

        uint8_t * p = colors;

        for ( n = 0; n < 256 ; n++ )
        {
            * p++ = png_palette[n].red;
            * p++ = png_palette[n].green;
            * p++ = png_palette[n].blue;
        }

        bitmap->format->palette = pal_new_rgb(( uint8_t * )colors );
        pal_refresh( bitmap->format->palette );

        if ( !sys_pixel_format->palette )
        {
            sys_pixel_format->palette = pal_new( bitmap->format->palette );
/*            pal_use( bitmap->format->palette ); */
            palette_changed = 1 ;
        }

        for ( n = 0 ; n < height ; n++ ) rowpointers[n] = (( uint8_t* )bitmap->data ) + n * bitmap->pitch ;
        png_read_image( png_ptr, rowpointers ) ;

        /* If the depth is less than 8, expand the pixel values */

        if ( depth == 4 )
        {
            for ( n =  0; n < height; n++ )
            {
                char * orig, * dest;
                orig = ( char *  ) ( rowpointers[n] );
                dest = orig + width - 1;
                orig += ( width - 1 ) / 2;

                for ( x = width; x--; )
                {
                    *dest-- = ( *orig >> ((( 1 - ( x & 0x01 ) ) << 2 ) ) ) & 0x0F ;
                    if ( !( x & 0x01 ) ) orig--;
                }
            }
        }
        else if ( depth == 2 )
        {
            for ( n = 0; n < height; n++ )
            {
                char * orig, * dest;
                orig = ( char * ) rowpointers[n];
                dest = orig + width - 1;
                orig += ( width - 1 ) / 4;

                for ( x = width; x--; )
                {
                    *dest-- = ( *orig >> ((( 3 - ( x & 0x03 ) ) << 1 ) ) ) & 0x03 ;
                    if ( !( x & 0x03 ) ) orig--;
                }
            }
        }
        else if ( depth == 1 )
        {
            for ( n = 0; n < height; n++ )
            {
                char * orig, * dest;
                orig = ( char * ) rowpointers[n];
                dest = orig + width - 1;
                orig += ( width - 1 ) / 8;

                for ( x = width; x--; )
                {
                    *dest-- = ( *orig >> ( 7 - ( x & 0x07 ) ) ) & 0x01 ;
                    if ( !( x & 0x07 ) ) orig--;
                }
            }
        }
    }
    else if ( depth == 8 && sys_pixel_format->depth != 16 )
    {
        for ( n = 0 ; n < height ; n++ )
        {
            rowpointers[0] = ( void * )row ;
            png_read_rows( png_ptr, rowpointers, 0, 1 ) ;

            ptr32 = ( uint32_t* )((( uint8_t * )bitmap->data ) + n * bitmap->pitch );
            orig = row ;
            for ( x = 0 ; x < width ; x++ )
            {
                ARRANGE_DWORD( orig );
                *ptr32 = *orig ;
<b>
                if ((color==PNG_COLOR_TYPE_RGB)&& (info_ptr->pixel_depth==24)&& (info_ptr->valid & PNG_INFO_tRNS)){
                    uint8_t * ptr8 = (uint8_t *)orig;
if(
(ptr8[0]==info_ptr->trans_values.red) &&
(ptr8[1]==info_ptr->trans_values.green) &&
(ptr8[2]==info_ptr->trans_values.blue)
)
*ptr32 = 0;

               }
</b>
                ptr32++, orig++ ;
            }
        }
    }
    else
    {
        Rshift = 8;
        Gshift = 5;
        Bshift = 3;

        Rmask = 0xF80000 ; // 3
        Gmask = 0x00FC00 ; // 2
        Bmask = 0x0000F8 ; // 3
        for ( n = 0 ; n < height ; n++ )
        {
            rowpointers[0] = ( void * )row ;
            png_read_rows( png_ptr, rowpointers, 0, 1 ) ;

            ptr = ( uint16_t* )((( uint8_t * )bitmap->data ) + n * bitmap->pitch );
            orig = row ;
            for ( x = 0 ; x < width ; x++ )
            {
                ARRANGE_DWORD( orig );

                if (( *orig ) & 0x80000000 )
                {
                    *ptr = (( *orig & Rmask ) >> Rshift ) | (( *orig & Gmask ) >> Gshift ) | (( *orig & Bmask ) >> Bshift )  ;
                    if ( !*ptr )( *ptr )++ ;
                }
                else
                    *ptr = 0 ;
<b>
                if ((color==PNG_COLOR_TYPE_RGB)&& (info_ptr->pixel_depth==24)&& (info_ptr->valid & PNG_INFO_tRNS)){
                uint8_t * ptr8 = (uint8_t *)orig;
        if(
        (ptr8[0]==info_ptr->trans_values.red) &&
        (ptr8[1]==info_ptr->trans_values.green) &&
        (ptr8[2]==info_ptr->trans_values.blue)
        ){
        *ptr = 0;

        }
                }
</b>
                ptr++, orig++ ;
            }
        }

    }

    /* Fin */

    if ( !setjmp( png_ptr->jmpbuf ) ) png_read_end( png_ptr, 0 ) ;

    bitmap->modified = 1 ;

    png_destroy_read_struct( &png_ptr, &info_ptr, &end_info ) ;
    free( rowpointers ) ;
    free( row ) ;
    file_close( png ) ;

    return bitmap ;
}

/*
*  FUNCTION : gr_save_png
*
*  Save a GRAPH into a PNG file
*
*  PARAMS :
*      gr              GRAPH with the image to save
*      filename        name for the file
*
*  RETURN VALUE :
*      0 - FAILURE
*      1 - SUCCESS
*
*/

int gr_save_png( GRAPH * gr, const char * filename )
{
    if ( !gr ) return( 0 ) ;

    FILE * file = fopen( filename, "wb" ) ;
    png_structp png_ptr ;
    png_infop info_ptr ;
    png_uint_32 k, i ;
    png_bytep * rowpointers ;
    png_colorp pal ;
    uint32_t * data, * ptr ;
    uint16_t * orig ;
    uint32_t * orig32 ;
    rgb_component * gpal = NULL;

    if ( !file ) return( 0 ) ;

    rowpointers = malloc( sizeof( png_bytep ) * gr->height );
    if ( !rowpointers )
    {
        fclose( file ) ;
        return 0 ;
    }

    png_ptr  = png_create_write_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 ) ;
    if ( !png_ptr )
    {
        free( rowpointers ) ;
        fclose( file ) ;
        return( 0 ) ;
    }

    info_ptr = png_create_info_struct( png_ptr ) ;
    if ( !info_ptr )
    {
        png_destroy_write_struct( &png_ptr, NULL ) ;
        free( rowpointers ) ;
        fclose( file ) ;
        return( 0 ) ;
    }

    /* Error handling... */

    if ( setjmp( png_ptr->jmpbuf ) )
    {
        png_destroy_write_struct( &png_ptr, NULL ) ;
        free( rowpointers ) ;
        fclose( file ) ;
        return( 0 ) ;
    }

    png_init_io( png_ptr, file ) ;

    if ( gr->format->depth == 8 )
    {
        /* 8 bits PNG file */
        png_set_IHDR( png_ptr, info_ptr, gr->width,
                gr->height, 8, PNG_COLOR_TYPE_PALETTE,
                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
                PNG_FILTER_TYPE_BASE ) ;

        pal = ( png_colorp ) png_malloc( png_ptr, 256 * sizeof( png_color ) ) ;
        if ( !pal )
        {
            png_destroy_write_struct( &png_ptr, NULL ) ;
            free( rowpointers ) ;
            fclose( file ) ;
            return( 0 ) ;
        }

        if ( gr->format->palette )
            gpal = gr->format->palette->rgb;
        else if ( sys_pixel_format->palette )
            gpal = sys_pixel_format->palette->rgb;
        else
            gpal = ( rgb_component * )default_palette;

        /* Generate palette info */
        for ( k = 0 ; k < 256 ; k++ )
        {
            pal[k].red   = gpal[k].r ;
            pal[k].green = gpal[k].g ;
            pal[k].blue  = gpal[k].b ;
        }
        png_set_PLTE( png_ptr, info_ptr, pal, 256 ) ;
        png_write_info( png_ptr, info_ptr ) ;

        /* no need to rearrange data */
        for ( k = 0 ; k < ( unsigned )gr->height ; k++ )
            rowpointers[k] = ( uint8_t * )gr->data + gr->pitch * k ;
        png_write_image( png_ptr, rowpointers ) ;

        /* Free allocated palette... */
        png_free( png_ptr, ( png_voidp ) pal ) ;
        info_ptr->palette = NULL ;
    }
    else
    {
        png_set_IHDR( png_ptr, info_ptr, gr->width,
                gr->height, 8, PNG_COLOR_TYPE_RGB_ALPHA,
                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
                PNG_FILTER_TYPE_BASE ) ;
<b>
        if ( gr->format->depth == 16 ){
        info_ptr->color_type=PNG_COLOR_TYPE_RGB;
        info_ptr->trans_values.red =0;
        info_ptr->trans_values.green=0;
        info_ptr->trans_values.blue=0;
        info_ptr->valid = info_ptr->valid | PNG_INFO_tRNS;
        }
</b>
        png_write_info( png_ptr, info_ptr ) ;

        data = malloc( gr->width * gr->height * 4 ) ;
        if ( !data )
        {
            png_destroy_write_struct( &png_ptr, NULL ) ;
            free( rowpointers ) ;
            fclose( file ) ;
            return( 0 ) ;
        }

        if ( gr->format->depth == 16 )
        {
<b>
           uint8_t *ptr8;
            ptr8 = (uint8_t *) data;
            for ( k = 0; k < ( unsigned )gr->height; k++ )
            {
                rowpointers[k] = ptr8 ;
                orig = ( uint16_t * )( gr->data + gr->pitch * k ) ;
                for ( i = 0 ; i < ( unsigned )gr->width ; i++ )
                {
ptr8[0] =( *orig & 0xf800 ) >> 8;
ptr8[1] =( *orig & 0x07e0 ) >> 3;
ptr8[2] =( *orig & 0x001f ) << 3;
                    orig++ ;
                    ptr8+=3  ;
                }
            }
</b>
        }
        else if ( gr->format->depth == 32 )
        {
            for ( k = 0; k < ( unsigned )gr->height; k++ )
            {
                ptr  = data + gr->width * k ; /* uses dword for each pixel! */
                orig32 = ( uint32_t * )( gr->data + gr->pitch * k ) ;
                rowpointers[k] = ( uint8_t * )ptr ;
                for ( i = 0 ; i < ( unsigned )gr->width ; i++ )
                {
/*
                    if ( !*orig32 && !( gr->info_flags & GI_NOCOLORKEY ) )
                        *ptr = 0 ;
                    else if ( !( *orig32 & 0xff000000 ) )
                        *ptr =
                            0xff000000 |
                            (( *orig32 & 0x00ff0000 ) >> 16 ) |
                            (( *orig32 & 0x0000ff00 ) ) |
                            (( *orig32 & 0x000000ff ) << 16 ) ;
                    else
*/
                        *ptr =
                            (( *orig32 & 0xff000000 ) ) |
                            (( *orig32 & 0x00ff0000 ) >> 16 ) |
                            (( *orig32 & 0x0000ff00 ) ) |
                            (( *orig32 & 0x000000ff ) << 16 ) ;

                    /* Rearrange data */
                    ARRANGE_DWORD( ptr ) ;

                    orig32++ ;
                    ptr++  ;
                }
            }
        }
        png_write_image( png_ptr, rowpointers ) ;
        free( data ) ;
    }

    png_write_end( png_ptr, info_ptr ) ;
    fclose( file ) ;
    png_destroy_write_struct( &png_ptr, NULL ) ;
    free( rowpointers ) ;
    return ( 1 ) ;
}

/* --------------------------------------------------------------------------- */

int gr_load_png( const char * mapname )
{
    GRAPH * gr = gr_read_png( mapname ) ;
    if ( !gr ) return 0 ;
    gr->code = bitmap_next_code() ;
    grlib_add_map( 0, gr ) ;
    return gr->code ;
}

/* --------------------------------------------------------------------------- */

Monstruos Diabólicos

"A PAck of classic GAMEs For BennuGD" en desarrollo
http://code.google.com/p/apagame4be/

DCelso

He probado todas las combinaciones que se me han ocurrido de imágenes cargadas en modo 16 bits y guardadas y también he probado a hacer lo mismo en modo 32 y no he encontrado aparentemente ningún problema con la integración de los cambios, no obstante puede que encuentres bastantes mejoras en el código ya que yo a veces no soy muy bueno optimizando y me voy por el camino rápido :D.
Un ejemplo base con el que he realizado pruebas:

import "mod_key";
import "mod_proc";
import "mod_map";
import "mod_screen";
import "mod_video";

process main()
private
int escape=0, was_clicked=0, mx=0;
string str1="", str2="";
int square, pic1, pic2, pic3, pic4,pic4b, pic5,pic6;
begin
  set_mode(320,240,26);
  square=new_map(300,220,16);
  map_clear(0,square,rgb(0,255,0));
  pic5=png_load("A24b.png");
repeat

  put(0,square,160,120);
  put(0,pic5,280,170);

  if(key(_ESC) ) escape=1; end
  frame;
until(escape);
save_png(0,pic5,"A24saved.png");
unload_map(0,square);
unload_map(0,pic5);
let_me_alone();
end

donde A24b.png puede ser cualquier tipo de PNG suportado por bennu. y A24saved es la imagen resultado PNG de 24 bits con transparencia usando el 0 absoluto como transparente.
Monstruos Diabólicos

"A PAck of classic GAMEs For BennuGD" en desarrollo
http://code.google.com/p/apagame4be/

DCelso

Se me ocurre que quizás sería bueno tener una save_png en el que pudieras especificar con un parámetro más si quieres guardar la información de transparencia.
Pero bueno, siempre puedes eliminarla a posteriori con un programa de diseño gráfico así que a lo mejor no es tan útil :D.
Monstruos Diabólicos

"A PAck of classic GAMEs For BennuGD" en desarrollo
http://code.google.com/p/apagame4be/

SplinterGU

ah, ya veo, se agrega en la parte del "else if ( depth == 8 && sys_pixel_format->depth != 16 )"

cuando posteaste originalmente, entendi que le agregabas un if... pero asi me parece bien el cambio como lo has puesto aca.

no importa la optimizacion ahora, y el tema del png_save, no estaba seguro, pero acabo de ver el codigo y esta bien, es correcto que asi sea, ya que siempre el valor 0 es transparente, entonces esta bien guardarlo asi, para que luego no se ingrese como un valor no transparente.

gracias DCelso por la mejora.
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

SplinterGU

estoy viendo el codigo, y esta parte en la funcion save_png, me parece que no es necesaria, tal cual esta en el codigo actual deberia funcionar.


           uint8_t *ptr8;
            ptr8 = (uint8_t *) data;
            for ( k = 0; k < ( unsigned )gr->height; k++ )
            {
                rowpointers[k] = ptr8 ;
                orig = ( uint16_t * )( gr->data + gr->pitch * k ) ;
                for ( i = 0 ; i < ( unsigned )gr->width ; i++ )
                {
ptr8[0] =( *orig & 0xf800 ) >> 8;
ptr8[1] =( *orig & 0x07e0 ) >> 3;
ptr8[2] =( *orig & 0x001f ) << 3;
                    orig++ ;
                    ptr8+=3  ;
                }
            }


tampoco entiendo porque quedo tan diferente esta parte con respecto a la original.
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

SplinterGU

bueno, no, no funciona la save.

explicame por que la diferencia en los 16bits en el save, gracias.
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

SplinterGU

sin los cambios en la save_png, cargo el png del otro hilo llamado A02.png y lo almaceno y respeta perfectamente las transparencias.

en realidad probe con los siguientes archivos:

A02.png
A03.png
A04.png
A08bis.png
A08.png
A24.png
A32.png

y al salvarlos con el actual save_png, salen los png con las mismas transparencias que el original.

esta bien eso?
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

SplinterGU

otra cosa que veo es que tu ejemplo, pusiste el set_mode a 26, pero termina seteando a 16bits, lo mismo pasa si pones 24, con eso responde a una duda que tenia de alguien que dijo que bennugd dejaba setear el video a 24bits, en realidad lo setea a 16.
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

FreeYourMind

Sugerencia:

Se podria soportar semitransparencias, o sea, imagenes con varios niveles de transparencia ?

josebita

Quote from: FreeYourMind on November 09, 2010, 05:04:48 PM
Sugerencia:

Se podria soportar semitransparencias, o sea, imagenes con varios niveles de transparencia ?
Eso son los 32 bits :)

FreeYourMind

 :D
Vale, la lo pillo, como hay mas gama de colores, estan incluidos semi transparencias para cada uno  :D

josebita

En modo de 32 bits los colores se representan con cuatro componentes: RGBA donde "A" es el valor del alpha en cada pixel.

DCelso

Ups, eso del 26 fue un fallo , quería poner a 16 bits. :D,
En cuanto a la diferencia entre tu save_png y el mio es toda, tu save_png como está cuando tenemos set_mode a 16 bits guarda un PNG de 32 bits en el que el canal alfa está a FF o 00, dependiendo de si encuentra un negro. En el caso de este save_png guarda un PNG de 24 bits (con toda la información de la imagen de 16 bits que hay cargada) donde en otro campo de la cabecera guardas que el negro negro, el 0 es el color que usaran los visores para considerarlo transparente. por tanto ahorras 1 byte por cada pixel en el guardado. (a eso me refería con ahorrar espacio), claro está tu PNG es muy válido, esto no es una correción, podríamos decirlo "una mejora" pero como tu dices innecesaria, osea que si no te gusta pues no se sube y punto :D, tu mandas.

Para el caso de modo 32 bits este save_png sigue haciendo lo mismo de antes así que es igual de válido.

En cuanto a lo que me preguntas de 16 bits es por eso mismo, porque tu tienes guardado en memoria una imagen de 16 bits y vas a guardarlo en una imagen de 24 bits, de ahí la conversión.

En cuanto al tema de semitransparencias, no se puede, en modo 16 bits no se soporta así que es impensable poder guardarlas, además el mismo formato PNG de 24 bits solo tiene un campo para guardar un único valor RGB a tratar como transparente puro, y como bien dicen, para evitar esto y soportar semitransparencias está el modo 32 bits que asigna un nuevo byte a cada pixel para especificar el nivel de transparencia de ese pixel.

En cuanto a que no funciona mi save_png, ¿como? si lo he trasteado todo lo que pude :(, voy a revisarlo a ver si es que me salté algo o es simplemente porque me confundí en poner el código y puse 26 en vez de 16 :D.

Lo que estoy viendo es que los "containers" de bennu en memoria para imágenes son mucho más potentes que el .fpg y el .map, quizás habría que replantearse unos nuevos formatos de fichero para sacarle la mayor chica a este fantástico Bennu. :D. Incluso ganar velocidad en la carga :D.

Y bueno, splinterGu, esto son simples sugerencias, no tienes por qué usarlas si no las ves útiles ya que aqui tú mandas :D, y si quieres puedes retocarlas o reimplementarlas a tu manera que seguro que es más óptima
Monstruos Diabólicos

"A PAck of classic GAMEs For BennuGD" en desarrollo
http://code.google.com/p/apagame4be/

DCelso

Resultados de las prueba a fondo.
Ahora también he añadido que guarde la información de color transparente en PNGs indexados (<=8 bits) ya que no cuesta y así nuestros visores de imágenes veran exactamente lo mismo que bennu, :D.

/*
*  Copyright � 2006-2010 SplinterGU (Fenix/Bennugd)
*  Copyright � 2002-2006 Fenix Team (Fenix)
*  Copyright � 1999-2002 Jos� Luis Cebri�n Pag�e (Fenix)
*
*  This file is part of Bennu - Game Development
*
*  Bennu is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  Bennu is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
*
*/

/* --------------------------------------------------------------------------- */

#include <png.h>

#include "mod_map.h"

/* --------------------------------------------------------------------------- */

/*
*  GLOBAL VARIABLES
*/

/* --------------------------------------------------------------------------- */

static uint8_t colors[256 * 3] ;

static file * png ;

/* --------------------------------------------------------------------------- */

static void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length )
{
    file_read( png, data, length ) ;
}

GRAPH * gr_read_png( const char * filename )
{
GRAPH * bitmap ;
    unsigned int n, x ;
    uint16_t * ptr ;
    uint32_t * ptr32 ;
    uint32_t * orig ;
    uint32_t * row ;
    uint32_t Rshift, Gshift, Bshift ;
    uint32_t Rmask, Gmask, Bmask ;

    png_bytep * rowpointers ;

    png_structp png_ptr ;
    png_infop info_ptr, end_info ;
    png_uint_32 width, height, rowbytes;
    int depth, color ;

    /* Abre el fichero y se asegura de que screen est� inicializada */

    png = file_open( filename, "rb" ) ;
    if ( !png ) return NULL;

    /* Prepara las estructuras internas */

    png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 ) ;
    if ( !png_ptr )
    {
        file_close( png );
        return NULL;
    }

    info_ptr = png_create_info_struct( png_ptr ) ;
    end_info = png_create_info_struct( png_ptr ) ;
    if ( !info_ptr || !end_info )
    {
        png_destroy_read_struct( &png_ptr, &info_ptr, &end_info ) ;
        file_close( png ) ;
        return NULL;
    }

    /* Rutina de error */

    if ( setjmp( png_ptr->jmpbuf ) )
    {
        png_destroy_read_struct( &png_ptr, &info_ptr, &end_info ) ;
        file_close( png ) ;
        return NULL;
    }

    /* Recupera informaci�n sobre el PNG */

    png_set_read_fn( png_ptr, 0, user_read_data ) ;
    png_read_info( png_ptr, info_ptr ) ;
    png_get_IHDR( png_ptr, info_ptr, &width, &height, &depth, &color, 0, 0, 0 ) ;

    row = malloc( sizeof( uint32_t ) * width );
    if ( !row )
    {
        png_destroy_read_struct( &png_ptr, &info_ptr, &end_info ) ;
        file_close( png ) ;
        return NULL;
    }

    rowpointers = malloc( sizeof( png_bytep ) * height );
    if ( !rowpointers )
    {
        png_destroy_read_struct( &png_ptr, &info_ptr, &end_info ) ;
        free( row ) ;
        file_close( png ) ;
        return NULL;
    }

    /* Configura los distintos modos disponibles */

    if ( color == PNG_COLOR_TYPE_GRAY /*|| color == PNG_COLOR_TYPE_GRAY_ALPHA*/ )
    {
        png_set_gray_to_rgb( png_ptr );
        if ( color == PNG_COLOR_TYPE_GRAY ) png_set_filler( png_ptr, 0xFF, PNG_FILLER_AFTER ) ;
    }

    if ( depth == 16 ) png_set_strip_16( png_ptr ) ;

    if ( color == PNG_COLOR_TYPE_RGB ) png_set_filler( png_ptr, 0xFF, PNG_FILLER_AFTER ) ;

    png_set_bgr( png_ptr ) ;

    /* Recupera el fichero, convirtiendo a 16 bits si es preciso */

    rowbytes = png_get_rowbytes( png_ptr, info_ptr ) ;
    bitmap = bitmap_new( 0, width, height, ( color == PNG_COLOR_TYPE_PALETTE ) ? 8 : ( sys_pixel_format->depth == 16 ? 16 : 32 ) ) ;
    if ( !bitmap )
    {
        png_destroy_read_struct( &png_ptr, &info_ptr, &end_info ) ;
        free( rowpointers ) ;
        free( row ) ;
        file_close( png ) ;
        return NULL;
    }

    if ( color == PNG_COLOR_TYPE_PALETTE )
    {
        /* Read the color palette */

        png_colorp png_palette = ( png_colorp ) png_malloc( png_ptr, 256 * sizeof( png_color ) ) ;
        if ( !png_palette )
        {
            png_destroy_read_struct( &png_ptr, &info_ptr, &end_info ) ;
            free( rowpointers ) ;
            free( row ) ;
            file_close( png ) ;
            return NULL;
        }
        png_get_PLTE( png_ptr, info_ptr, &png_palette, ( int * ) &n ) ;

        uint8_t * p = colors;

        for ( n = 0; n < 256 ; n++ )
        {
            * p++ = png_palette[n].red;
            * p++ = png_palette[n].green;
            * p++ = png_palette[n].blue;
        }

        bitmap->format->palette = pal_new_rgb(( uint8_t * )colors );
        pal_refresh( bitmap->format->palette );

        if ( !sys_pixel_format->palette )
        {
            sys_pixel_format->palette = pal_new( bitmap->format->palette );
/*            pal_use( bitmap->format->palette ); */
            palette_changed = 1 ;
        }

        for ( n = 0 ; n < height ; n++ ) rowpointers[n] = (( uint8_t* )bitmap->data ) + n * bitmap->pitch ;
        png_read_image( png_ptr, rowpointers ) ;

        /* If the depth is less than 8, expand the pixel values */

        if ( depth == 4 )
        {
            for ( n =  0; n < height; n++ )
            {
                char * orig, * dest;
                orig = ( char *  ) ( rowpointers[n] );
                dest = orig + width - 1;
                orig += ( width - 1 ) / 2;

                for ( x = width; x--; )
                {
                    *dest-- = ( *orig >> ((( 1 - ( x & 0x01 ) ) << 2 ) ) ) & 0x0F ;
                    if ( !( x & 0x01 ) ) orig--;
                }
            }
        }
        else if ( depth == 2 )
        {
            for ( n = 0; n < height; n++ )
            {
                char * orig, * dest;
                orig = ( char * ) rowpointers[n];
                dest = orig + width - 1;
                orig += ( width - 1 ) / 4;

                for ( x = width; x--; )
                {
                    *dest-- = ( *orig >> ((( 3 - ( x & 0x03 ) ) << 1 ) ) ) & 0x03 ;
                    if ( !( x & 0x03 ) ) orig--;
                }
            }
        }
        else if ( depth == 1 )
        {
            for ( n = 0; n < height; n++ )
            {
                char * orig, * dest;
                orig = ( char * ) rowpointers[n];
                dest = orig + width - 1;
                orig += ( width - 1 ) / 8;

                for ( x = width; x--; )
                {
                    *dest-- = ( *orig >> ( 7 - ( x & 0x07 ) ) ) & 0x01 ;
                    if ( !( x & 0x07 ) ) orig--;
                }
            }
        }
    }
    else if ( depth == 8 && sys_pixel_format->depth != 16 )
    {
        for ( n = 0 ; n < height ; n++ )
        {
            rowpointers[0] = ( void * )row ;
            png_read_rows( png_ptr, rowpointers, 0, 1 ) ;

            ptr32 = ( uint32_t* )((( uint8_t * )bitmap->data ) + n * bitmap->pitch );
            orig = row ;
            for ( x = 0 ; x < width ; x++ )
            {
                ARRANGE_DWORD( orig );
                <b>*ptr32 = *orig ;
                if ((color==PNG_COLOR_TYPE_RGB)&& (info_ptr->pixel_depth==24)&& (info_ptr->valid & PNG_INFO_tRNS)){
                    uint8_t * ptr8 = (uint8_t *)orig;
if(
(ptr8[0]==info_ptr->trans_values.red) &&
(ptr8[1]==info_ptr->trans_values.green) &&
(ptr8[2]==info_ptr->trans_values.blue)
)
*ptr32 = 0;

               }</b>
                ptr32++, orig++ ;
            }
        }
    }
    else
    {
        Rshift = 8;
        Gshift = 5;
        Bshift = 3;

        Rmask = 0xF80000 ; // 3
        Gmask = 0x00FC00 ; // 2
        Bmask = 0x0000F8 ; // 3
        for ( n = 0 ; n < height ; n++ )
        {
            rowpointers[0] = ( void * )row ;
            png_read_rows( png_ptr, rowpointers, 0, 1 ) ;

            ptr = ( uint16_t* )((( uint8_t * )bitmap->data ) + n * bitmap->pitch );
            orig = row ;
            for ( x = 0 ; x < width ; x++ )
            {
                ARRANGE_DWORD( orig );

                if (( *orig ) & 0x80000000 )
                {
                    *ptr = (( *orig & Rmask ) >> Rshift ) | (( *orig & Gmask ) >> Gshift ) | (( *orig & Bmask ) >> Bshift )  ;
                    if ( !*ptr )( *ptr )++ ;
                }
                else
                    *ptr = 0 ;<b>
                if ((color==PNG_COLOR_TYPE_RGB)&& (info_ptr->pixel_depth==24)&& (info_ptr->valid & PNG_INFO_tRNS)){
                uint8_t * ptr8 = (uint8_t *)orig;
        if(
        (ptr8[0]==info_ptr->trans_values.red) &&
        (ptr8[1]==info_ptr->trans_values.green) &&
        (ptr8[2]==info_ptr->trans_values.blue)
        ){
        *ptr = 0;

        }
                }</b>
                ptr++, orig++ ;
            }
        }

    }

    /* Fin */

    if ( !setjmp( png_ptr->jmpbuf ) ) png_read_end( png_ptr, 0 ) ;

    bitmap->modified = 1 ;

    png_destroy_read_struct( &png_ptr, &info_ptr, &end_info ) ;
    free( rowpointers ) ;
    free( row ) ;
    file_close( png ) ;

    return bitmap ;
}

/*
*  FUNCTION : gr_save_png
*
*  Save a GRAPH into a PNG file
*
*  PARAMS :
*      gr              GRAPH with the image to save
*      filename        name for the file
*
*  RETURN VALUE :
*      0 - FAILURE
*      1 - SUCCESS
*
*/

int gr_save_png( GRAPH * gr, const char * filename )
{
    if ( !gr ) return( 0 ) ;

    FILE * file = fopen( filename, "wb" ) ;
    png_structp png_ptr ;
    png_infop info_ptr ;
    png_uint_32 k, i ;
    png_bytep * rowpointers ;
    png_colorp pal ;
    uint32_t * data, * ptr ;
    uint16_t * orig ;
    uint32_t * orig32 ;
    rgb_component * gpal = NULL;

    if ( !file ) return( 0 ) ;

    rowpointers = malloc( sizeof( png_bytep ) * gr->height );
    if ( !rowpointers )
    {
        fclose( file ) ;
        return 0 ;
    }

    png_ptr  = png_create_write_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 ) ;
    if ( !png_ptr )
    {
        free( rowpointers ) ;
        fclose( file ) ;
        return( 0 ) ;
    }

    info_ptr = png_create_info_struct( png_ptr ) ;
    if ( !info_ptr )
    {
        png_destroy_write_struct( &png_ptr, NULL ) ;
        free( rowpointers ) ;
        fclose( file ) ;
        return( 0 ) ;
    }

    /* Error handling... */

    if ( setjmp( png_ptr->jmpbuf ) )
    {
        png_destroy_write_struct( &png_ptr, NULL ) ;
        free( rowpointers ) ;
        fclose( file ) ;
        return( 0 ) ;
    }

    png_init_io( png_ptr, file ) ;

    if ( gr->format->depth == 8 )
    {<b>
        uint8_t trans = 1;</b>
    /* 8 bits PNG file */
        png_set_IHDR( png_ptr, info_ptr, gr->width,
                gr->height, 8, PNG_COLOR_TYPE_PALETTE,
                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
                PNG_FILTER_TYPE_BASE ) ;<b>
        if (!( gr->info_flags & GI_NOCOLORKEY )){
info_ptr->num_trans =1;
info_ptr->trans =&trans;
info_ptr->valid = info_ptr->valid | PNG_INFO_tRNS;
        }</b>

        pal = ( png_colorp ) png_malloc( png_ptr, 256 * sizeof( png_color ) ) ;
        if ( !pal )
        {
            png_destroy_write_struct( &png_ptr, NULL ) ;
            free( rowpointers ) ;
            fclose( file ) ;
            return( 0 ) ;
        }

        if ( gr->format->palette )
            gpal = gr->format->palette->rgb;
        else if ( sys_pixel_format->palette )
            gpal = sys_pixel_format->palette->rgb;
        else
            gpal = ( rgb_component * )default_palette;

        /* Generate palette info */
        for ( k = 0 ; k < 256 ; k++ )
        {
            pal[k].red   = gpal[k].r ;
            pal[k].green = gpal[k].g ;
            pal[k].blue  = gpal[k].b ;
        }
        png_set_PLTE( png_ptr, info_ptr, pal, 256 ) ;
        png_write_info( png_ptr, info_ptr ) ;

        /* no need to rearrange data */
        for ( k = 0 ; k < ( unsigned )gr->height ; k++ )
            rowpointers[k] = ( uint8_t * )gr->data + gr->pitch * k ;
        png_write_image( png_ptr, rowpointers ) ;

        /* Free allocated palette... */
        png_free( png_ptr, ( png_voidp ) pal ) ;
        info_ptr->palette = NULL ;
    }
    else
    {
        png_set_IHDR( png_ptr, info_ptr, gr->width,
                gr->height, 8, PNG_COLOR_TYPE_RGB_ALPHA,
                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
                PNG_FILTER_TYPE_BASE ) ;
        <b>
        if ( gr->format->depth == 16 ){
        info_ptr->color_type=PNG_COLOR_TYPE_RGB;
        if (!( gr->info_flags & GI_NOCOLORKEY )){
    info_ptr->trans_values.index =0;
    info_ptr->trans_values.red =0;
    info_ptr->trans_values.green=0;
    info_ptr->trans_values.blue=0;
    info_ptr->trans_values.gray=0;
        }
        info_ptr->valid = info_ptr->valid | PNG_INFO_tRNS;
        }</b>

        png_write_info( png_ptr, info_ptr ) ;

        data = malloc( gr->width * gr->height * 4 ) ;
        if ( !data )
        {
            png_destroy_write_struct( &png_ptr, NULL ) ;
            free( rowpointers ) ;
            fclose( file ) ;
            return( 0 ) ;
        }

        if ( gr->format->depth == 16 )<b>
        {
        uint8_t *ptr8;
            ptr8 = (uint8_t *) data;
            for ( k = 0; k < ( unsigned )gr->height; k++ )
            {
                rowpointers[k] = ptr8 ;
                orig = ( uint16_t * )( gr->data + gr->pitch * k ) ;
                for ( i = 0 ; i < ( unsigned )gr->width ; i++ )
                {
ptr8[0] =( *orig & 0xf800 ) >> 8;
ptr8[1] =( *orig & 0x07e0 ) >> 3;
ptr8[2] =( *orig & 0x001f ) << 3;
                    orig++ ;
                    ptr8+=3  ;
                }
            }
        }</b>
        else if ( gr->format->depth == 32 )
        {
            for ( k = 0; k < ( unsigned )gr->height; k++ )
            {
                ptr  = data + gr->width * k ; /* uses dword for each pixel! */
                orig32 = ( uint32_t * )( gr->data + gr->pitch * k ) ;
                rowpointers[k] = ( uint8_t * )ptr ;
                for ( i = 0 ; i < ( unsigned )gr->width ; i++ )
                {
/*
                    if ( !*orig32 && !( gr->info_flags & GI_NOCOLORKEY ) )
                        *ptr = 0 ;
                    else if ( !( *orig32 & 0xff000000 ) )
                        *ptr =
                            0xff000000 |
                            (( *orig32 & 0x00ff0000 ) >> 16 ) |
                            (( *orig32 & 0x0000ff00 ) ) |
                            (( *orig32 & 0x000000ff ) << 16 ) ;
                    else
*/
                        *ptr =
                            (( *orig32 & 0xff000000 ) ) |
                            (( *orig32 & 0x00ff0000 ) >> 16 ) |
                            (( *orig32 & 0x0000ff00 ) ) |
                            (( *orig32 & 0x000000ff ) << 16 ) ;

                    /* Rearrange data */
                    ARRANGE_DWORD( ptr ) ;

                    orig32++ ;
                    ptr++  ;
                }
            }
        }
        png_write_image( png_ptr, rowpointers ) ;
        free( data ) ;
    }

    png_write_end( png_ptr, info_ptr ) ;
    fclose( file ) ;
    png_destroy_write_struct( &png_ptr, NULL ) ;
    free( rowpointers ) ;
    return ( 1 ) ;
}

/* --------------------------------------------------------------------------- */

int gr_load_png( const char * mapname )
{
    GRAPH * gr = gr_read_png( mapname ) ;
    if ( !gr ) return 0 ;
    gr->code = bitmap_next_code() ;
    grlib_add_map( 0, gr ) ;
    return gr->code ;
}

/* --------------------------------------------------------------------------- */

En un set_mode a 16 bits (recordemos esto de aquí en adelante, modo 16 bits), he probado a cargar imagenes indexadas de 1,2,4,8 bits, imágenes de 24 bits e imágenes de 32 bits y luego guardarlas con los siguientes resultados:
En las imágenes indexadas:
Las imágenes cargadas no contienen información de transparente ya que bennu usa siempre el color del índice 0 como transparente y odvia la información de transparencia de las imágenes indexadas.
Las imágenes guardadas ponen en la información de color el índice 0 así los visores verán lo mismo que bennu.

En las imágenes de 24 bits:
Si la imagen no tiene información de transparencia, bennu no pone ningún color a transparente, pero cambia todos los negros 0,0,0 a 0,0,8.
Si la imagen tiene información de un color a tratar como transparente (), bennu cambia los pixeles con ese color al 0 (tranpsarente para bennu)-
Al guardar estas imágenes de 24bits bennu guarda estas imágenes informando que el 0,0,0 se tratará como transparente, por tanto nuestros visores verán lo mismo que bennu veía.

En las imágenes de 32 bits:
Bennu elimina los semitransparentes y pone a transparente los valores alfa del 0 al 127.
Al guardar estas imágenes se guardan en 24 bits con información de que el 0,0,0 se tratará como transparente, así se ve la imagen como la veía bennu en los visores, sin semitransparentes :D.

Adjunto test realizado con los diferentes formatos de imagen.
Monstruos Diabólicos

"A PAck of classic GAMEs For BennuGD" en desarrollo
http://code.google.com/p/apagame4be/

SplinterGU

free... muy bueno tu comentario,  me rei un buen rato... gracias... ;)

DCelso, claro, eso era lo que me parecia raro, porque veia que en 16bits guardabas 24bits y no 32bits como se espera que lo haga bennugd, es cierto que no tiene mucho sentido guardar un mapa de 16bits en un png de 32bits, porque el canal alpha es 0 o 255 (0% o 100%), pero bueno, no importa mucho, mas si tenemos en cuenta que hasta tu mejora cargabamos pngs de 32bits y bennugd lo convierte a 16bits. No esta mal tu idea, pero prefiero dejarlo como esta, y en algun momento hacer una funcion sobrecargada para tal fin, porque si lo cambio ahora incompatibilizare el sistema con la version anterior que guardaba png de 32bits.

lo de que no funciona, me referia al cambio parcial que habia hecho yo, tu codigo funciona bien.

gracias nuevamente, ya esta subido al SVN.
Download Lastest BennuGD Release: http://www.bennugd.org/node/2