File: Png.cpp

package info (click to toggle)
storm-lang 0.7.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 52,004 kB
  • sloc: ansic: 261,462; cpp: 140,405; sh: 14,891; perl: 9,846; python: 2,525; lisp: 2,504; asm: 860; makefile: 678; pascal: 70; java: 52; xml: 37; awk: 12
file content (182 lines) | stat: -rw-r--r-- 5,447 bytes parent folder | download
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
#include "stdafx.h"
#include "Png.h"
#include "Exception.h"

#ifndef WINDOWS
#include <png.h>
#endif

namespace graphics {

	static Bool CODECALL pngApplicable(IStream *from) {
		return checkHeader(from, "\x89PNG", false);
	}

	static FormatOptions *CODECALL pngCreate(ImageFormat *f) {
		return new (f) PNGOptions();
	}

	ImageFormat *pngFormat(Engine &e) {
		const wchar *exts[] = { S("png"), null };
		return new (e) ImageFormat(S("Portable Network Graphics"), exts, &pngApplicable, &pngCreate);
	}

	PNGOptions::PNGOptions() {}

	// See ImageWin32.cpp for loading/saving on Windows.
#ifndef WINDOWS

	// IO callbacks from libpng.
	static void pngRead(png_structp png, byte *to, size_t length) {
		IStream *src = (IStream*)png_get_io_ptr(png);
		Buffer out = src->fill(length);
		if (out.count() > 0)
			memcpy(to, out.dataPtr(), out.count());
	}

	static Image *loadPng(PNGOptions *options, IStream *from, const wchar *&error) {
		// Anchor 'from' on the stack so that the GC does not move it.
		IStream *volatile anchor = null;
		atomicWrite(anchor, from);

		png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
		png_infop info = null;
		error = S("Failed to initialize libpng");

		// Custom error handling:
		if (setjmp(png_jmpbuf(png))) {
			// Error. Clean up and exit.
			png_destroy_read_struct(&png, &info, NULL);
			error = S("Internal error in libpng.");
			return null;
		}

		png_set_read_fn(png, (void *)anchor, &pngRead);

		if (png) {
			info = png_create_info_struct(png);
			error = S("Failed to create info struct.");
		}

		png_uint_32 ok = 0;
		png_uint_32 width = 0, height = 0;
		int depth = 0, type = 0;
		if (info) {
			png_read_info(png, info);
			ok = png_get_IHDR(png, info, &width, &height, &depth, &type, NULL, NULL, NULL);
			error = S("Failed to read PNG header.");
		}

		Image *output = null;
		if (ok) {
			int typeNoAlpha = type & ~PNG_COLOR_MASK_ALPHA;

			// Fix the format.
			if (typeNoAlpha == PNG_COLOR_TYPE_PALETTE)
				png_set_palette_to_rgb(png);
			if (typeNoAlpha == PNG_COLOR_TYPE_GRAY)
				png_set_gray_to_rgb(png);
			if (png_get_valid(png, info, PNG_INFO_tRNS))
				png_set_tRNS_to_alpha(png);
			if (depth == 16)
				png_set_strip_16(png);
			if (depth < 8) {
				png_color_8p bit;
				if (png_get_sBIT(png, info, &bit))
					png_set_shift(png, bit);
			}
			if (!(type & PNG_COLOR_MASK_ALPHA))
				png_set_add_alpha(png, 0xFF, PNG_FILLER_AFTER);

			png_read_update_info(png, info);

			// Read the PNG file itself!
			output = new (from) Image(Nat(width), Nat(height));

			// Make sure to pin the internal array by storing a pointer to it on the stack.
			byte *volatile buffer = null;
			atomicWrite(buffer, output->buffer());
			Nat stride = output->stride();

			// Create a GcArray where we can store pointers to inside 'buffer'. Note: it is not
			// scanned, as that would lead to pointers to the middle of objects. Since the buffer is
			// pinned on the stack, it can not move so this should be fine anyway.
			GcArray<byte *> *rows = runtime::allocArray<byte *>(output->engine(), &sizeArrayType, height);
			for (Nat i = 0; i < height; i++)
				rows->v[i] = buffer + stride*i;

			png_read_image(png, rows->v);
		}

		// Clean up any structures we allocated.
		png_destroy_read_struct(&png, &info, NULL);

		return output;
	}

	Image *PNGOptions::load(IStream *from) {
		const wchar *error;
		Image *out = loadPng(this, from, error);
		if (!out)
			throw new (this) ImageLoadError(error);
		return out;
	}

	static void pngWrite(png_structp png, byte *from, size_t length) {
		OStream *dest = (OStream *)png_get_io_ptr(png);
		dest->write(buffer(dest->engine(), from, length));
	}

	static void pngFlush(png_structp png) {
		OStream *dest = (OStream *)png_get_io_ptr(png);
		dest->flush();
	}

	void PNGOptions::save(Image *image, OStream *to) {
		OStream *volatile anchor = null;
		atomicWrite(anchor, to);

		const wchar *error = S("Failed to initialize libpng.");
		png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
		png_infop info = null;
		if (!png) {
			throw new (image) ImageSaveError(error);
		}

		if (setjmp(png_jmpbuf(png))) {
			// Error. Clean up and exit.
			png_destroy_write_struct(&png, &info);
			throw new (image) ImageSaveError(S("Internal error in libpng."));
		}

		info = png_create_info_struct(png);
		if (!info)
			throw new (image) ImageSaveError(S("Failed to create png info struct."));

		png_set_write_fn(png, (void *)anchor, &pngWrite, &pngFlush);

		png_set_IHDR(png, info, image->width(), image->height(), 8,
					PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
		png_write_info(png, info);

		// Make sure to pin the internal array by storing a pointer to it on the stack.
		byte *volatile buffer = null;
		atomicWrite(buffer, image->buffer());
		Nat stride = image->stride();

		// Create a GcArray where we can store pointers to inside 'buffer'. Note: it is not
		// scanned, as that would lead to pointers to the middle of objects. Since the buffer is
		// pinned on the stack, it can not move so this should be fine anyway.
		GcArray<byte *> *rows = runtime::allocArray<byte *>(image->engine(), &sizeArrayType, image->height());
		for (Nat i = 0; i < image->height(); i++)
			rows->v[i] = buffer + stride*i;

		png_write_image(png, rows->v);
		png_write_end(png, NULL);

		png_destroy_write_struct(&png, &info);
	}

#endif

}