b5100d5ccc3b19fddec90d6dad793bc2af3bf14c
[chaz/yoink] / src / moof / image.cc
1
2 /*] Copyright (c) 2009-2010, Charles McGarvey [**************************
3 **] All rights reserved.
4 *
5 * vi:ts=4 sw=4 tw=75
6 *
7 * Distributable under the terms and conditions of the 2-clause BSD license;
8 * see the file COPYING for a complete text of the license.
9 *
10 **************************************************************************/
11
12 #include <cstdio> // FILE
13 #include <cstring> // strncmp
14 #include <stdexcept>
15
16 #include <png.h>
17 #include <SDL/SDL.h>
18
19 #include "backend.hh"
20 #include "image.hh"
21 #include "log.hh"
22 #include "opengl.hh"
23 #include "script.hh"
24 #include "video.hh"
25
26
27 namespace moof {
28
29
30 //static int power_of_two(int input)
31 //{
32 //int value = 1;
33
34 //while (value < input)
35 //{
36 //value <<= 1;
37 //}
38 //return value;
39 //}
40
41 unsigned image::global_object_ = 0;
42
43
44 image::image(const std::string& path) :
45 pixels_(0),
46 object_(0),
47 min_filter_(GL_NEAREST),
48 mag_filter_(GL_NEAREST),
49 wrap_s_(GL_CLAMP),
50 wrap_t_(GL_CLAMP),
51 tile_width_(1),
52 tile_height_(1)
53 {
54 FILE* fp = fopen(path.c_str(), "rb");
55 if (!fp) throw std::runtime_error("image not found at " + path);
56
57 png_byte signature[8];
58 size_t bytesRead;
59
60 png_infop pngInfo = 0;
61 png_infop pngInfoEnd = 0;
62 png_structp pngObj = 0;
63
64 int bpp;
65
66 png_byte colors;
67 png_bytepp rows = 0;
68
69 png_textp texts = 0;
70 int nutext_s;
71
72 bytesRead = fread(signature, 1, sizeof(signature), fp);
73 if (bytesRead < sizeof(signature) ||
74 png_sig_cmp(signature, 0, sizeof(signature)) != 0) goto cleanup;
75
76 pngObj = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
77 if (!pngObj) goto cleanup;
78
79 pngInfo = png_create_info_struct(pngObj);
80 if (!pngInfo) goto cleanup;
81
82 pngInfoEnd = png_create_info_struct(pngObj);
83 if (!pngInfoEnd) goto cleanup;
84
85 if (setjmp(png_jmpbuf(pngObj))) goto cleanup;
86
87 png_init_io(pngObj, fp);
88 png_set_sig_bytes(pngObj, sizeof(signature));
89 png_read_info(pngObj, pngInfo);
90
91 bpp = png_get_bit_depth(pngObj, pngInfo);
92 colors = png_get_color_type(pngObj, pngInfo);
93 switch (colors)
94 {
95 case PNG_COLOR_TYPE_PALETTE:
96 png_set_palette_to_rgb(pngObj);
97 break;
98
99 case PNG_COLOR_TYPE_GRAY:
100 if (bpp < 8) png_set_expand(pngObj);
101 break;
102
103 case PNG_COLOR_TYPE_GRAY_ALPHA:
104 png_set_gray_to_rgb(pngObj);
105 break;
106 }
107
108 if (bpp == 16) png_set_strip_16(pngObj);
109
110 png_read_update_info(pngObj, pngInfo);
111
112 bpp = png_get_bit_depth(pngObj, pngInfo);
113 channels_ = png_get_channels(pngObj, pngInfo);
114 depth_ = bpp * channels_;
115
116 // read comments
117 png_get_text(pngObj, pngInfo, &texts, &nutext_s);
118 for (int i = 0; i < nutext_s; ++i)
119 {
120 if (strncmp(texts[i].key, "TextureInfo", 11) == 0)
121 {
122 set_texture_info(texts[i].text);
123 break;
124 }
125 }
126
127 width_ = png_get_image_width(pngObj, pngInfo);
128 height_ = png_get_image_height(pngObj, pngInfo);
129
130 pitch_ = png_get_rowbytes(pngObj, pngInfo);
131 pixels_ = new char[width_ * pitch_];
132
133 rows = new png_bytep[height_];
134 for (int i = 0; i < height_; ++i)
135 {
136 rows[i] = (png_bytep)(pixels_ + i * channels_ * width_);
137 }
138
139 png_read_image(pngObj, rows);
140 png_read_end(pngObj, 0);
141
142 cleanup:
143
144 delete[] rows;
145 png_destroy_read_struct(pngObj ? &pngObj : 0,
146 pngInfo ? &pngInfo : 0,
147 pngInfoEnd ? &pngInfoEnd : 0);
148 fclose(fp);
149 }
150
151 image::~image()
152 {
153 unload_from_gl();
154 delete[] pixels_;
155 }
156
157
158 void image::set_as_icon() const
159 {
160 backend backend;
161
162 SDL_Surface* context = SDL_CreateRGBSurfaceFrom
163 (
164 pixels_,
165 width_,
166 height_,
167 depth_,
168 pitch_,
169 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
170 0x000000FF,
171 0x0000FF00,
172 0x00FF0000,
173 0xFF000000
174 #else
175 0xFF000000,
176 0x00FF0000,
177 0x0000FF00,
178 0x000000FF
179 #endif
180 );
181
182 SDL_WM_SetIcon(context, 0);
183
184 SDL_FreeSurface(context);
185 }
186
187
188 bool image::tile_coordinates(int index, scalar coords[8]) const
189 {
190 // make sure the index represents a real tile
191 if (index < 0 && index >= tile_width_ * tile_height_) return false;
192
193 scalar w = 1.0 / scalar(tile_width_);
194 scalar h = 1.0 / scalar(tile_height_);
195
196 coords[0] = scalar(index % tile_width_) * w;
197 coords[1] = (scalar(tile_height_ - 1) - scalar(index / tile_width_)) * h;
198 coords[2] = coords[0] + w;
199 coords[3] = coords[1];
200 coords[4] = coords[2];
201 coords[5] = coords[1] + h;
202 coords[6] = coords[0];
203 coords[7] = coords[5];
204
205 return true;
206 }
207
208
209 void image::bind() const
210 {
211 ASSERT(video::current() && "should have a video context set");
212
213 if (object_ == 0)
214 {
215 upload_to_gl();
216 }
217 if (object_ != global_object_)
218 {
219 glBindTexture(GL_TEXTURE_2D, (GLuint)object_);
220 global_object_ = object_;
221 }
222 }
223
224 void image::reset_binding()
225 {
226 ASSERT(video::current() && "should have a video context set");
227
228 glBindTexture(GL_TEXTURE_2D, 0);
229 global_object_ = 0;
230 }
231
232
233 /*
234 * Upload the image to GL so that it will be accessible by a much more
235 * manageable handle and hopefully reside in video memory.
236 */
237 void image::upload_to_gl() const
238 {
239 if (object_)
240 {
241 // already loaded
242 return;
243 }
244
245 glGenTextures(1, (GLuint*)&object_);
246 glBindTexture(GL_TEXTURE_2D, (GLuint)object_);
247
248 GLuint mode;
249 if (channels_ == 3) mode = GL_RGB;
250 else mode = GL_RGBA;
251
252 glTexImage2D
253 //gluBuild2DMipmaps
254 (
255 GL_TEXTURE_2D,
256 0,
257 mode,
258 //3,
259 width_,
260 height_,
261 0,
262 mode,
263 GL_UNSIGNED_BYTE,
264 pixels_
265 );
266
267 set_properties();
268
269 // we want to know when the GL context is recreated
270 //dispatcher& dispatcher = dispatcher::global();
271 //new_context_ = dispatcher.add_target("video.newcontext",
272 //boost::bind(&image::context_recreated, this));
273 // FIXME this has const issues
274 }
275
276 void image::unload_from_gl() const
277 {
278 if (object_)
279 {
280 if (object_ == global_object_)
281 {
282 global_object_ = 0;
283 }
284
285 glDeleteTextures(1, (GLuint*)&object_);
286 object_ = 0;
287 }
288 }
289
290 void image::context_recreated()
291 {
292 object_ = global_object_ = 0;
293 upload_to_gl();
294 }
295
296 /*
297 * Sets some texture properties such as the filters and external
298 * coordinate behavior.
299 */
300 void image::set_properties() const
301 {
302 //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter_);
303 //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter_);
304 //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s_);
305 //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t_);
306 }
307
308
309 void image::set_texture_info(const std::string& info)
310 {
311 script script;
312 log::import(script);
313
314 script::slot g = script.globals();
315 g.set_field("CLAMP", GL_CLAMP);
316 g.set_field("REPEAT", GL_REPEAT);
317 g.set_field("LINEAR", GL_LINEAR);
318 g.set_field("NEAREST", GL_NEAREST);
319 g.set_field("LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR);
320 g.set_field("LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST);
321 g.set_field("NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR);
322 g.set_field("NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST);
323
324 if (script.do_string(info) != script::success)
325 {
326 std::string str;
327 script[-1].get(str);
328 log_warning(str);
329 }
330 else
331 {
332 log_info("loading texture information...");
333
334 script::slot globals = script.globals();
335 globals.get(tile_width_, "tiles_s");
336 globals.get(tile_height_, "tiles_t");
337 globals.get(min_filter_, "min_filter");
338 globals.get(mag_filter_, "mag_filter");
339 globals.get(wrap_s_, "wrap_s");
340 globals.get(wrap_t_, "wrap_t");
341 }
342 }
343
344
345 class image_resource_loader
346 {
347 public:
348
349 image_resource_loader()
350 {
351 resource::register_type<image>("png", "textures");
352 }
353
354 ~image_resource_loader()
355 {
356 resource::unregister_type("png");
357 }
358 };
359
360 static image_resource_loader loader;
361
362
363 } // namespace moof
364
This page took 0.044525 seconds and 3 git commands to generate.