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