added png_reader.cpp
@@ -39,6 +39,8 @@ | ||
39 | 39 | ::glBindTexture(GL_TEXTURE_2D, handle_); |
40 | 40 | ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); |
41 | 41 | ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
42 | + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); | |
43 | + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); | |
42 | 44 | ::glTexImage2D( |
43 | 45 | GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, |
44 | 46 | GL_RGBA, GL_UNSIGNED_BYTE, data); |
@@ -0,0 +1,169 @@ | ||
1 | +// png_test.cpp: the test program for the class png_reader | |
2 | + | |
3 | +// Copyright Takeshi Mouri 2008. | |
4 | +// Distributed under the Boost Software License, Version 1.0. | |
5 | +// (See accompanying file LICENSE_1_0.txt or copy at | |
6 | +// http://www.boost.org/LICENSE_1_0.txt) | |
7 | + | |
8 | +// See http://hamigaki.sourceforge.jp/ for library home page. | |
9 | + | |
10 | +#include <boost/config.hpp> | |
11 | + | |
12 | +#include "png_reader.hpp" | |
13 | +#include "texture.hpp" | |
14 | +#include "render_context.hpp" | |
15 | +#include <boost/scoped_array.hpp> | |
16 | +#include <cstring> | |
17 | +#include <fstream> | |
18 | +#include <iostream> | |
19 | +#include <stdexcept> | |
20 | + | |
21 | +#if defined(BOOST_WINDOWS) | |
22 | + #include <windows.h> | |
23 | +#endif | |
24 | +#include <GL/gl.h> | |
25 | + | |
26 | +class main_window_data | |
27 | +{ | |
28 | +public: | |
29 | + explicit main_window_data(GtkWidget* widget) : rc_(widget), texture_(rc_) | |
30 | + { | |
31 | + rc_.select(); | |
32 | + | |
33 | + std::ifstream is("milk.png", std::ios_base::binary); | |
34 | + | |
35 | + hamigaki::png_reader png(is); | |
36 | + const int width = static_cast<int>(png.width()); | |
37 | + const int height = static_cast<int>(png.height()); | |
38 | + const unsigned long pitch = png.pitch(); | |
39 | + | |
40 | + typedef unsigned char ubyte; | |
41 | + boost::scoped_array<ubyte> row(new ubyte[pitch]); | |
42 | + boost::scoped_array<ubyte> data(new ubyte[4*width*height]); | |
43 | + | |
44 | + const std::size_t data_pitch = static_cast<std::size_t>(4*width); | |
45 | + for (int i = 0; i < height; ++i) | |
46 | + { | |
47 | + png.read_next_row(row.get()); | |
48 | + std::memcpy(&data[data_pitch*i], row.get(), data_pitch); | |
49 | + } | |
50 | + | |
51 | + texture_.set_image(width, height, data.get()); | |
52 | + } | |
53 | + | |
54 | + void render() | |
55 | + { | |
56 | + rc_.select(); | |
57 | + | |
58 | + ::glClearColor(0.0f, 0.0f, 1.0f, 1.0f); | |
59 | + ::glClear(GL_COLOR_BUFFER_BIT); | |
60 | + | |
61 | + ::glAlphaFunc(GL_GREATER, 0.1f); | |
62 | + ::glEnable(GL_ALPHA_TEST); | |
63 | + ::glEnable(GL_TEXTURE_2D); | |
64 | + texture_.bind(); | |
65 | + | |
66 | + ::glBegin(GL_QUADS); | |
67 | + ::glTexCoord2f(0.0f, 0.0f); | |
68 | + ::glVertex3f(-0.75f, 0.75f, 0.0f); | |
69 | + ::glTexCoord2f(1.0f, 0.0f); | |
70 | + ::glVertex3f(0.75f, 0.75f, 0.0f); | |
71 | + ::glTexCoord2f(1.0f, 1.0f); | |
72 | + ::glVertex3f(0.75f, -0.75f, 0.0f); | |
73 | + ::glTexCoord2f(0.0f, 1.0f); | |
74 | + ::glVertex3f(-0.75f, -0.75f, 0.0f); | |
75 | + ::glEnd(); | |
76 | + | |
77 | + ::glDisable(GL_TEXTURE_2D); | |
78 | + | |
79 | + rc_.swap_buffers(); | |
80 | + } | |
81 | + | |
82 | +private: | |
83 | + hamigaki::render_context rc_; | |
84 | + hamigaki::texture texture_; | |
85 | + | |
86 | + main_window_data(const main_window_data&); | |
87 | + main_window_data& operator=(const main_window_data&); | |
88 | +}; | |
89 | + | |
90 | +void destroy(GtkWidget*, gpointer) | |
91 | +{ | |
92 | + ::gtk_main_quit(); | |
93 | +} | |
94 | + | |
95 | +void realize(GtkWidget* widget, gpointer user_data) | |
96 | +{ | |
97 | + main_window_data*& pimpl = *static_cast<main_window_data**>(user_data); | |
98 | + try | |
99 | + { | |
100 | + pimpl = new main_window_data(widget); | |
101 | + } | |
102 | + catch (const std::exception& e) | |
103 | + { | |
104 | + std::cerr << "Error: " << e.what() << std::endl; | |
105 | + pimpl = 0; | |
106 | + } | |
107 | +} | |
108 | + | |
109 | +void unrealize(GtkWidget*, gpointer user_data) | |
110 | +{ | |
111 | + main_window_data*& pimpl = *static_cast<main_window_data**>(user_data); | |
112 | + delete pimpl; | |
113 | + pimpl = 0; | |
114 | +} | |
115 | + | |
116 | +gboolean render(gpointer user_data) | |
117 | +{ | |
118 | + if (main_window_data*& pimpl = *static_cast<main_window_data**>(user_data)) | |
119 | + { | |
120 | + try | |
121 | + { | |
122 | + pimpl->render(); | |
123 | + } | |
124 | + catch (const std::exception& e) | |
125 | + { | |
126 | + std::cerr << "Error: " << e.what() << std::endl; | |
127 | + } | |
128 | + } | |
129 | + | |
130 | + return TRUE; | |
131 | +} | |
132 | + | |
133 | +template<class Widget, class Arg> | |
134 | +inline gulong connect_signal( | |
135 | + Widget* w, const char* sig, GCallback func, Arg* arg) | |
136 | +{ | |
137 | + gulong id = ::g_signal_connect(G_OBJECT(w), sig, func, arg); | |
138 | + if (id == 0) | |
139 | + throw std::runtime_error("g_signal_connect() failed"); | |
140 | + return id; | |
141 | +} | |
142 | + | |
143 | +int main(int argc, char* argv[]) | |
144 | +{ | |
145 | + try | |
146 | + { | |
147 | + ::gtk_set_locale(); | |
148 | + ::gtk_init(&argc, &argv); | |
149 | + | |
150 | + GtkWidget* window = ::gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
151 | + if (window == 0) | |
152 | + throw std::runtime_error("gtk_window_new() failed"); | |
153 | + | |
154 | + main_window_data* pimpl = 0; | |
155 | + connect_signal(window, "destroy", G_CALLBACK(destroy), &pimpl); | |
156 | + connect_signal(window, "realize", G_CALLBACK(realize), &pimpl); | |
157 | + connect_signal(window, "unrealize", G_CALLBACK(unrealize), &pimpl); | |
158 | + ::g_timeout_add(16, &render, &pimpl); | |
159 | + | |
160 | + ::gtk_widget_show_all(window); | |
161 | + ::gtk_main(); | |
162 | + } | |
163 | + catch (const std::exception& e) | |
164 | + { | |
165 | + std::cerr << "Error: " << e.what() << std::endl; | |
166 | + } | |
167 | + | |
168 | + return 0; | |
169 | +} |
@@ -0,0 +1,192 @@ | ||
1 | +// png_reader.cpp: PNG reader class | |
2 | + | |
3 | +// Copyright Takeshi Mouri 2008. | |
4 | +// Distributed under the Boost Software License, Version 1.0. | |
5 | +// (See accompanying file LICENSE_1_0.txt or copy at | |
6 | +// http://www.boost.org/LICENSE_1_0.txt) | |
7 | + | |
8 | +// See http://hamigaki.sourceforge.jp/ for library home page. | |
9 | + | |
10 | +#include "png_reader.hpp" | |
11 | +#include <csetjmp> | |
12 | +#include <istream> | |
13 | +#include <stdexcept> | |
14 | +#include <png.h> | |
15 | + | |
16 | +#if !defined(NDEBUG) | |
17 | + #include <windows.h> | |
18 | +#endif | |
19 | + | |
20 | +namespace | |
21 | +{ | |
22 | + | |
23 | +void PNGAPI do_long_jump(png_struct* ctx, const char*) | |
24 | +{ | |
25 | + longjmp(png_jmpbuf(ctx), 1); | |
26 | +} | |
27 | + | |
28 | +void PNGAPI process_error(png_struct*, const char* msg) | |
29 | +{ | |
30 | + throw std::runtime_error(msg); | |
31 | +} | |
32 | + | |
33 | +#if defined(NDEBUG) | |
34 | +void PNGAPI process_warning(png_struct*, const char*) | |
35 | +{ | |
36 | +} | |
37 | +#else | |
38 | +void PNGAPI process_warning(png_struct*, const char* msg) | |
39 | +{ | |
40 | + ::OutputDebugStringA(msg); | |
41 | +} | |
42 | +#endif | |
43 | + | |
44 | +void PNGAPI read_from_stream(png_struct* ctx, unsigned char* p, std::size_t n) | |
45 | +{ | |
46 | + std::streamsize size = static_cast<std::streamsize>(n); | |
47 | + | |
48 | + std::istream& is = *static_cast<std::istream*>(png_get_io_ptr(ctx)); | |
49 | + is.read(reinterpret_cast<char*>(p), size); | |
50 | + if (is.gcount() != size) | |
51 | + throw std::runtime_error("failed read_from_stream()"); | |
52 | +} | |
53 | + | |
54 | +} // namespace | |
55 | + | |
56 | +namespace hamigaki | |
57 | +{ | |
58 | + | |
59 | +class png_reader::impl | |
60 | +{ | |
61 | +public: | |
62 | + explicit impl(std::istream& is) | |
63 | + { | |
64 | + // Note: | |
65 | + // png_create_read_struct() uses setjmp() for error handling | |
66 | + // So error_fn must call longjmp() in this function | |
67 | + pimpl_ = ::png_create_read_struct( | |
68 | + PNG_LIBPNG_VER_STRING, 0, &do_long_jump, &process_warning); | |
69 | + | |
70 | + if (!pimpl_) | |
71 | + throw std::runtime_error("failed png_create_read_struct()"); | |
72 | + | |
73 | + // Now, error_fn can throw a exception | |
74 | + ::png_set_error_fn(pimpl_, 0, &process_error, &process_warning); | |
75 | + | |
76 | + info_ptr_ = ::png_create_info_struct(pimpl_); | |
77 | + if (!info_ptr_) | |
78 | + { | |
79 | + ::png_destroy_read_struct(&pimpl_, 0, 0); | |
80 | + throw std::runtime_error("failed png_create_info_struct()"); | |
81 | + } | |
82 | + | |
83 | + ::png_set_read_fn(pimpl_, &is, read_from_stream); | |
84 | + } | |
85 | + | |
86 | + ~impl() | |
87 | + { | |
88 | + ::png_destroy_read_struct(&pimpl_, &info_ptr_, 0); | |
89 | + } | |
90 | + | |
91 | + void read_info() | |
92 | + { | |
93 | + ::png_read_info(pimpl_, info_ptr_); | |
94 | + } | |
95 | + | |
96 | + void force_rgba() | |
97 | + { | |
98 | + unsigned long w; | |
99 | + unsigned long h; | |
100 | + int bits; | |
101 | + int type; | |
102 | + | |
103 | + ::png_get_IHDR(pimpl_, info_ptr_, &w, &h, &bits, &type, 0, 0, 0); | |
104 | + | |
105 | + ::png_set_strip_16(pimpl_); | |
106 | + ::png_set_packing(pimpl_); | |
107 | + | |
108 | + if (type == PNG_COLOR_TYPE_PALETTE) | |
109 | + ::png_set_palette_to_rgb(pimpl_); | |
110 | + | |
111 | + if (((type & PNG_COLOR_MASK_COLOR) == 0) && (bits < 8)) | |
112 | + ::png_set_gray_1_2_4_to_8(pimpl_); | |
113 | + | |
114 | + if (png_get_valid(pimpl_, info_ptr_, PNG_INFO_tRNS)) | |
115 | + ::png_set_tRNS_to_alpha(pimpl_); | |
116 | + else if ((type & PNG_COLOR_MASK_ALPHA) == 0) | |
117 | + ::png_set_add_alpha(pimpl_, 0xFF, PNG_FILLER_AFTER); | |
118 | + | |
119 | + ::png_set_bgr(pimpl_); | |
120 | + | |
121 | + ::png_read_update_info(pimpl_, info_ptr_); | |
122 | + } | |
123 | + | |
124 | + unsigned long width() const | |
125 | + { | |
126 | + return ::png_get_image_width(pimpl_, info_ptr_); | |
127 | + } | |
128 | + | |
129 | + unsigned long height() const | |
130 | + { | |
131 | + return ::png_get_image_height(pimpl_, info_ptr_); | |
132 | + } | |
133 | + | |
134 | + unsigned long pitch() const | |
135 | + { | |
136 | + return ::png_get_rowbytes(pimpl_, info_ptr_); | |
137 | + } | |
138 | + | |
139 | + void read_next_row(unsigned char* buf) | |
140 | + { | |
141 | + ::png_read_row(pimpl_, buf, 0); | |
142 | + } | |
143 | + | |
144 | + void read_end() | |
145 | + { | |
146 | + ::png_read_end(pimpl_, 0); | |
147 | + } | |
148 | + | |
149 | +private: | |
150 | + png_struct* pimpl_; | |
151 | + png_info* info_ptr_; | |
152 | + | |
153 | + impl(const impl&); | |
154 | + impl& operator=(const impl&); | |
155 | +}; | |
156 | + | |
157 | +png_reader::png_reader(std::istream& is) : pimpl_(new impl(is)) | |
158 | +{ | |
159 | + pimpl_->read_info(); | |
160 | + pimpl_->force_rgba(); | |
161 | +} | |
162 | + | |
163 | +png_reader::~png_reader() | |
164 | +{ | |
165 | +} | |
166 | + | |
167 | +unsigned long png_reader::width() const | |
168 | +{ | |
169 | + return pimpl_->width(); | |
170 | +} | |
171 | + | |
172 | +unsigned long png_reader::height() const | |
173 | +{ | |
174 | + return pimpl_->height(); | |
175 | +} | |
176 | + | |
177 | +unsigned long png_reader::pitch() const | |
178 | +{ | |
179 | + return pimpl_->pitch(); | |
180 | +} | |
181 | + | |
182 | +void png_reader::read_next_row(unsigned char* buf) | |
183 | +{ | |
184 | + pimpl_->read_next_row(buf); | |
185 | +} | |
186 | + | |
187 | +void png_reader::read_end() | |
188 | +{ | |
189 | + pimpl_->read_end(); | |
190 | +} | |
191 | + | |
192 | +} // namespace hamigaki |
@@ -0,0 +1,37 @@ | ||
1 | +// png_reader.hpp: PNG reader class | |
2 | + | |
3 | +// Copyright Takeshi Mouri 2008. | |
4 | +// Distributed under the Boost Software License, Version 1.0. | |
5 | +// (See accompanying file LICENSE_1_0.txt or copy at | |
6 | +// http://www.boost.org/LICENSE_1_0.txt) | |
7 | + | |
8 | +// See http://hamigaki.sourceforge.jp/ for library home page. | |
9 | + | |
10 | +#ifndef HAMIGAKI_PNG_IMAGE_HPP | |
11 | +#define HAMIGAKI_PNG_IMAGE_HPP | |
12 | + | |
13 | +#include <boost/shared_ptr.hpp> | |
14 | +#include <iosfwd> | |
15 | + | |
16 | +namespace hamigaki | |
17 | +{ | |
18 | + | |
19 | +class png_reader | |
20 | +{ | |
21 | +public: | |
22 | + explicit png_reader(std::istream& is); | |
23 | + ~png_reader(); | |
24 | + unsigned long width() const; | |
25 | + unsigned long height() const; | |
26 | + unsigned long pitch() const; | |
27 | + void read_next_row(unsigned char* buf); | |
28 | + void read_end(); | |
29 | + | |
30 | +private: | |
31 | + class impl; | |
32 | + boost::shared_ptr<impl> pimpl_; | |
33 | +}; | |
34 | + | |
35 | +} // namespace hamigaki | |
36 | + | |
37 | +#endif // HAMIGAKI_PNG_IMAGE_HPP |