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