File: PNGLoader.cpp

package info (click to toggle)
darkradiant 3.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 41,080 kB
  • sloc: cpp: 264,743; ansic: 10,659; python: 1,852; xml: 1,650; sh: 92; makefile: 21
file content (186 lines) | stat: -rw-r--r-- 4,861 bytes parent folder | download | duplicates (4)
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#include "PNGLoader.h"

#include <png.h>
#include <cstring>
#include "RGBAImage.h"
#include "stream/ScopedArchiveBuffer.h"

typedef unsigned char byte;

namespace image
{

#define png_infopp_NULL (png_infopp)nullptr
#define int_p_NULL (int*)nullptr

void user_warning_fn(png_structp png_ptr, png_const_charp warning_msg)
{
	rError() << "libpng warning: " << warning_msg << std::endl;
}

void user_error_fn(png_structp png_ptr, png_const_charp error_msg)
{
	rError() << "libpng error: " << error_msg << std::endl;
	longjmp(png_jmpbuf(png_ptr), 1);
}

void user_read_data(png_structp png_ptr, png_bytep data, png_uint_32 length)
{
	png_bytep* p_p_fbuffer = (png_bytep*)png_get_io_ptr(png_ptr);
	std::memcpy(data, *p_p_fbuffer, length);
	*p_p_fbuffer += length;
}

RGBAImagePtr LoadPNGBuff(unsigned char* fbuffer)
{
	png_bytep p_fbuffer;

	p_fbuffer = fbuffer;

	// the reading glue
	// http://www.libpng.org/pub/png/libpng-manual.html

	png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
		(png_voidp)NULL, user_error_fn, user_warning_fn);

	if (!png_ptr)
	{
		rError() << "libpng error: png_create_read_struct\n";
		return RGBAImagePtr();
	}

	png_infop info_ptr = png_create_info_struct(png_ptr);

	if (!info_ptr)
	{
		png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
		rError() << "libpng error: png_create_info_struct (info_ptr)" << std::endl;
		return RGBAImagePtr();
	}

	png_infop end_info = png_create_info_struct(png_ptr);

	if (!end_info)
	{
		png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);

		rError() << "libpng error: png_create_info_struct (end_info)" << std::endl;
		return RGBAImagePtr();
	}

	// configure the read function
	png_set_read_fn(png_ptr, (png_voidp)&p_fbuffer, (png_rw_ptr)&user_read_data);

	if (setjmp(png_jmpbuf(png_ptr)))
	{
		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
		return RGBAImagePtr();
	}

	png_read_info(png_ptr, info_ptr);

	int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
	int color_type = png_get_color_type(png_ptr, info_ptr);

	// we want to treat all images the same way
	//   The following code transforms grayscale images of less than 8 to 8 bits,
	//   changes paletted images to RGB, and adds a full alpha channel if there is
	//   transparency information in a tRNS chunk.
	if (color_type == PNG_COLOR_TYPE_PALETTE)
	{
		png_set_palette_to_rgb(png_ptr);
	}

	if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
	{
#if PNG_LIBPNG_VER < 10400
		png_set_gray_1_2_4_to_8(png_ptr);
#else
		png_set_expand_gray_1_2_4_to_8(png_ptr);
#endif 
	}

	if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
	{
		png_set_tRNS_to_alpha(png_ptr);
	}

	if (!(color_type & PNG_COLOR_MASK_ALPHA))
	{
		// Set the background color to draw transparent and alpha images over.
		png_color_16 my_background, * image_background;

		if (png_get_bKGD(png_ptr, info_ptr, &image_background))
		{
			png_set_background(png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
		}
		else
		{
			png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
		}

		// Add alpha byte after each RGB triplet
		png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
	}

    // Since the RGBA image supports 8-bit only, tell PNG to scale it down if the file is 16 bits
#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
    png_set_scale_16(png_ptr);
#else
    png_set_strip_16(png_ptr);
#endif

#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
    // This converts Grey and Grey+Alpha to RGBA
    png_set_gray_to_rgb(png_ptr);
#endif

	// read the sucker in one chunk
	png_read_update_info(png_ptr, info_ptr);

	color_type = png_get_color_type(png_ptr, info_ptr);
	bit_depth = png_get_bit_depth(png_ptr, info_ptr);

    auto width = png_get_image_width(png_ptr, info_ptr);
	auto height = png_get_image_height(png_ptr, info_ptr);

	// allocate the pixel buffer, and the row pointers
	RGBAImagePtr image(new RGBAImage(width, height));

    // Allocate a set of row pointers
    std::vector<png_byte*> rowPointers(height);
    auto pixelBuffer = image->getPixels();
    constexpr auto bytesPerPixel = sizeof(RGBAPixel) / sizeof(uint8_t);

    for (decltype(height) i = 0; i < height; ++i)
	{
        rowPointers[i] = static_cast<png_byte*>(pixelBuffer + i * width * bytesPerPixel);
	}

	// actual read
	png_read_image(png_ptr, rowPointers.data());

	/* read rest of file, and get additional chunks in info_ptr - REQUIRED */
	png_read_end(png_ptr, info_ptr);

	/* free up the memory structure */
	png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);

	return image;
}

ImagePtr PNGLoader::load(ArchiveFile& file) const
{
    archive::ScopedArchiveBuffer buffer(file);

    return LoadPNGBuff(buffer.buffer);
}

ImageTypeLoader::Extensions PNGLoader::getExtensions() const
{
    Extensions extensions;
    extensions.push_back("png");
    return extensions;
}

}