aboutsummaryrefslogtreecommitdiff
path: root/Src/png/PNGLoader.cpp
blob: 5f5c6fbcd20e9588a76edceb84030725a7d672f2 (plain) (blame)
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#include "PNGLoader.h"
#include "api__png.h"
#include <png.h>
#include <wchar.h>
#include <malloc.h>
#include <bfc/platform/strcmp.h>
#include <intsafe.h>
void premultiplyARGB32(ARGB32 *words, int nwords)
{
  for (; nwords > 0; nwords--, words++) {
    unsigned char *pixel = (unsigned char *)words;
    unsigned int alpha = pixel[3];
    if (alpha == 255) continue;
    pixel[0] = (pixel[0] * alpha) >> 8;	// blue
    pixel[1] = (pixel[1] * alpha) >> 8;	// green
    pixel[2] = (pixel[2] * alpha) >> 8;	// red
  }
}

static bool StringEnds(const wchar_t *a, const wchar_t *b)
{
	size_t aLen = wcslen(a);
	size_t bLen	= wcslen(b);
	if (aLen < bLen) return false;  // too short
	if (!_wcsicmp(a + aLen- bLen, b))
		return true;
	return false;
}

int PNGLoader::isMine(const wchar_t *filename) 
{
	if (filename && StringEnds(filename, L".PNG"))
		return 1;
	else
		return 0;
}

const wchar_t *PNGLoader::mimeType() 
{
	return L"image/png";
}

int PNGLoader::getHeaderSize() 
{
	return 8;
}

int PNGLoader::testData(const void *data, int datalen) {
	unsigned char *ptr = (unsigned char *)data;
	return !png_sig_cmp(ptr, 0, datalen);
}

typedef struct {
	const unsigned char *data;
	size_t pos;
	size_t datalen;
} my_read_info;

static void my_png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
	my_read_info *mri = (my_read_info *)png_get_io_ptr(png_ptr);
	if (mri->datalen - mri->pos < length)
		length = mri->datalen - mri->pos;
	memmove(data, mri->data + mri->pos, length);
	mri->pos += length;
}

ARGB32 *PNGLoader::loadImage(const void *data, int datalen, int *w, int *h, ifc_xmlreaderparams *params) {
	int w0=0, h0=0;

	ARGB32 *pixels = read_png(data, datalen, &w0, &h0, FALSE);

	if (pixels == NULL) return NULL;

	premultiplyARGB32(pixels, w0 * h0);

	if(w) *w = w0;
	if(h) *h = h0;

	return pixels;
}

ARGB32 *PNGLoader::loadImageData(const void *data, int datalen, int *w, int *h, ifc_xmlreaderparams *params) 
{
	return read_png(data, datalen, w, h, FALSE);
}

int PNGLoader::getDimensions(const void *data, int datalen, int *w, int *h) {
	return (read_png(data, datalen, w, h, TRUE) == reinterpret_cast<ARGB32*>(-1));
}

// From libpng example.c
ARGB32 *PNGLoader::read_png(const void *data, int datalen, int *w, int *h, int dimensions_only) {
	png_structp png_ptr;
	png_infop info_ptr;
	png_uint_32 width, height;
	int bit_depth, color_type, interlace_type;
	my_read_info mri;
	mri.data = static_cast<const unsigned char *>(data);
	mri.pos = 0;
	mri.datalen = datalen;

	/* 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 supply the
	* the compiler header file version, so that we know if the application
	* was compiled with a compatible version of the library.  REQUIRED
	*/
	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
		NULL, NULL, NULL);

	if (png_ptr == NULL)
	{
		return NULL;
	}

	/* Allocate/initialize the memory for image information.  REQUIRED. */
	info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL)
	{
		png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
		return NULL;
	}

	/* Set error handling if you are using the setjmp/longjmp method (this is
	* the normal method of doing things with libpng).  REQUIRED unless you
	* set up your own error handlers in the png_create_read_struct() earlier.
	*/
	if (setjmp(png_jmpbuf(png_ptr)))
	{
		/* Free all of the memory associated with the png_ptr and info_ptr */
		png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
		/* If we get here, we had a problem reading the file */
		return NULL;
	}

	png_set_read_fn(png_ptr, &mri, my_png_read_data);

	/* The call to png_read_info() gives us all of the information from the
	* PNG file before the first IDAT (image data chunk).  REQUIRED
	*/
	png_read_info(png_ptr, info_ptr);

	png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
		&interlace_type, NULL, NULL);

	if (w) *w = (int)width;
	if (h) *h = (int)height;

	ARGB32 *retval = 0;

	if (!dimensions_only) {

		/* tell libpng to strip 16 bit/color files down to 8 bits/color */
		if (bit_depth == 16) png_set_strip_16(png_ptr);
		if (bit_depth < 8) png_set_packing(png_ptr);

		/* flip the RGB pixels to BGR (or RGBA to BGRA) */
		png_set_bgr(png_ptr);

		/* Expand paletted colors into true RGB triplets */
		if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr);

		/* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
		if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
			png_set_expand(png_ptr);

		png_set_gray_to_rgb(png_ptr);

		/* Expand paletted or RGB images with transparency to full alpha channels
		* so the data will be available as RGBA quartets.
		*/
		if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
			png_set_expand(png_ptr);
		}

		/* Add filler (or alpha) byte (before/after each RGB triplet) */
		png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);

		/* Optional call to gamma correct and add the background to the palette
		* and update info structure.  REQUIRED if you are expecting libpng to
		* update the palette for you (ie you selected such a transform above).
		*/
		png_read_update_info(png_ptr, info_ptr);

		/* Allocate the memory to hold the image using the fields of info_ptr. */

		/* The easiest way to read the image: */
		//row_pointers = (png_bytep*)malloc(sizeof(png_bytep*)*height);
		size_t row_ptr_size = 0;
		if (SizeTMult(sizeof(png_bytep*), height, &row_ptr_size) == S_OK)
		{
			png_bytep *row_pointers = (png_bytep*)alloca(row_ptr_size);
			size_t image_size=0;
			if (SizeTMult(width, height, &image_size) == S_OK && SizeTMult(image_size, 4, &image_size)== S_OK)
			{
				ARGB32 *bytes = (ARGB32 *)WASABI_API_MEMMGR->sysMalloc(image_size);

				for (unsigned int row = 0; row < height; row++) {
					row_pointers[row] = ((unsigned char *)bytes) + width * 4 * (row);
				}

				/* Now it's time to read the image.  One of these methods is REQUIRED */
				png_read_image(png_ptr, row_pointers);

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

				retval = bytes;
			}
		}

		//free(row_pointers);
	}

	/* clean up after the read, and free any memory allocated - REQUIRED */
	png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);

	/* that's it */
	return retval;
}

#define CBCLASS PNGLoader
START_DISPATCH;
  CB(ISMINE, isMine);
  CB(MIMETYPE, mimeType);
  CB(TESTDATA, testData);
  CB(GETHEADERSIZE, getHeaderSize);
  CB(GETDIMENSIONS, getDimensions);
  CB(LOADIMAGE, loadImage);
  CB(LOADIMAGEDATA, loadImageData);
END_DISPATCH;
#undef CBCLASS