/
PNGFile.cpp
158 lines (122 loc) · 6.09 KB
/
PNGFile.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#include "PNGFile.h"
#include <iostream>
#include <fstream>
#include <assert.h>
//-------------------------------------------------------------//
//------------- PNGFile -----------------------//
//-------------------------------------------------------------//
void leerDatosfn(png_structp pPNGReadStruct, png_bytep data, png_size_t length) {
//Ahora obtenemos el std::istream de donde estamos leyendo del Struct de lectura PNG
//Este lo pasamos en el llamado a la funcion png_set_read_fn()
png_voidp a = png_get_io_ptr(pPNGReadStruct);
//Hacemos un cast ya que a es en realidad un puntero a std::istream
//Y leemos length cantidad de bytes en el buffer data
((std::istream*)a)->read((char*)data, length);
}
PNGFile::PNGFile(): m_pHeader(NULL), m_pPNGReadStruct(NULL)
{}
PNGFile::~PNGFile()//Llamara al destructor de ImageFile asi que no habra memory leaks
{
if( m_pPNGReadStruct )
png_destroy_read_struct(&m_pPNGReadStruct, &m_pHeader,(png_infopp)0);//liberamos el png y el encabezado
}
void PNGFile::_Load(std::istream &Stream)
{
//Leemos y verificamos la firma del archivo para asegurarnos que sea un png
png_bytep pFirma = new png_byte[8];
Stream.read((char*)pFirma, sizeof(png_byte)*8);
if( png_sig_cmp(pFirma, 0, 8) != 0 )
throw std::exception();//"El archivo cargado no es un png");
delete[] pFirma;
m_pPNGReadStruct = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if ( m_pPNGReadStruct == NULL )//Si fallo la creacion del struct de lectura del png
throw std::exception();//"No se pudo crear el struct de lectura de png");
//Establecemos la funcion/callback que usare LibPNG para leer los datos
png_set_read_fn(m_pPNGReadStruct,(png_voidp)&Stream, leerDatosfn);
//Intentamos crear el encabezado del archivo png
m_pHeader = png_create_info_struct(m_pPNGReadStruct);
if ( m_pHeader == NULL )
{
png_destroy_read_struct(&m_pPNGReadStruct, (png_infopp)0, (png_infopp)0);//liberamos el png
throw std::exception();//"No se pudo crear el encabezado del archivo png");
}
//Apuntara a cada fila de la imagen en m_EspacioColores... ver mas abajo una explicacion mas detallada
png_bytep* ArregloFilas = NULL;
//Informamos que si ocurre un error leyendo el archivo salte a este if
if (setjmp(png_jmpbuf(m_pPNGReadStruct))) {
png_destroy_read_struct(&m_pPNGReadStruct, &m_pHeader,(png_infopp)0);//liberamos el png y el encabezado
if (ArregloFilas != NULL)
delete [] ArregloFilas;
if (m_ArregloColores != NULL)
delete [] m_ArregloColores;
throw std::exception();//"Sucedio un error leyendo el archivo png");
}
//Avisamos a libpng que la firma ya fue leida y que lea el encabezado
png_set_sig_bytes(m_pPNGReadStruct, 8);
//Leemos el encabezado del png
png_read_info(m_pPNGReadStruct, m_pHeader);
//Leemos las dimensiones de la imagen del encabezado
m_Width = png_get_image_width (m_pPNGReadStruct, m_pHeader);
m_Height = png_get_image_height(m_pPNGReadStruct, m_pHeader);
//Bits por canal de la imagen(NO POR PIXEL, SINO POR CANAL... EN GENERAL 1 PIXEL TIENE MUCHOS CANALES)
png_uint_32 bitdepth = png_get_bit_depth(m_pPNGReadStruct, m_pHeader);
//Numero de canales de la imagen
png_uint_32 channels = png_get_channels(m_pPNGReadStruct, m_pHeader);
//Tipo de espacio de colores... luego lo convertiremos a m_EspacioColores
png_uint_32 color_type = png_get_color_type(m_pPNGReadStruct, m_pHeader);
//Si el formato de la imagen no es R8_G8_B8_ o R8_G8_B8_A8 intentamos convertirla a
//estos usando funciones de LibPNG
switch (color_type)
{
case PNG_COLOR_TYPE_RGB:
m_EspacioColores = ImageFile::R8_G8_B8;
break;
case PNG_COLOR_TYPE_RGBA:
m_EspacioColores = ImageFile::R8_G8_B8_A8;
break;
case PNG_COLOR_TYPE_PALETTE:
png_set_palette_to_rgb(m_pPNGReadStruct);
m_EspacioColores = ImageFile::R8_G8_B8;
channels = 3;//ahora tendremos 3 canales
break;
case PNG_COLOR_TYPE_GRAY:
png_set_gray_to_rgb(m_pPNGReadStruct);
m_EspacioColores = ImageFile::R8_G8_B8;
bitdepth = 8;
channels = 3;//ahora tendremos 3 canales
break;
default:
throw std::exception();//"La implementacion de PNGFile no soporta el espacio de colores de la imagen que se esta cargando");
}
//Si la imagen tiene transparencia la convertimos al canal alpha
if (png_get_valid(m_pPNGReadStruct, m_pHeader, PNG_INFO_tRNS)) {
png_set_tRNS_to_alpha(m_pPNGReadStruct);
channels+=1;//Un canal mas
}
//Si cada canal tiene 16 bits de profundidad lo convertimos a 8 bits
//ya que nuestros formatos son de 8 bits de profundidad
if( bitdepth == 16 )
png_set_strip_16(m_pPNGReadStruct);
//LibPNG lee los datos de color por filas de la imagen
//Para hacerlo requiere que le pasemos un arreglo de punteros a donde se almacenara cada
//fila de la imagen
ArregloFilas = new png_bytep[m_Height];
//Almacenamos el buffer donde se almacenara la imagen leida(ArregloFilas apuntara a direciones dentro de este buffer)
m_ArregloColores = new unsigned char[m_Width * m_Height * (bitdepth * channels / 8)];
//El tamaño que ocupa una fila de la imagen
const unsigned int rowTotSize = m_Width * bitdepth * channels / 8;
//Ahora apuntamos los punteros de ArregloFilas a las direciones de m_ArregloColores
//donde cada elemento de ArregloFilas es la dir de una fila de la imagen
for (unsigned int i = 0; i < m_Height; i++)
ArregloFilas[i] = (png_bytep)m_ArregloColores + i*rowTotSize;
//Aca leemos los datos de la imagen del archivo
//Alamcena los cada fila en las direciones señaladas por ArregloFilas
//que a su vez apuntan dentro de m_ArregloColores... lo cual significa
//que luego de esto m_ArregloColores tendra los datos de la imagen como los deseamos
png_read_image(m_pPNGReadStruct, ArregloFilas);
//Limpiamos el arreglo de punteros a filas(NO LOS DATOS SINO SOLO LOS PUNTEROS)
delete[] (png_bytep)ArregloFilas;
png_destroy_read_struct(&m_pPNGReadStruct, &m_pHeader,(png_infopp)0);//liberamos el png y el encabezado
if( Stream.bad() || Stream.fail() || Stream.eof() )
throw std::exception();//"La carga del archivo bmp tuvo errores");
}