]> Dogcows Code - chaz/yoink/blob - src/moof/image.cc
09f797ca8f3fc3d9235fb7ccf4f1ad21f5f84eae
[chaz/yoink] / src / moof / image.cc
1
2 /*] Copyright (c) 2009-2011, Charles McGarvey [*****************************
3 **] All rights reserved.
4 *
5 * Distributable under the terms and conditions of the 2-clause BSD license;
6 * see the file COPYING for a complete text of the license.
7 *
8 *****************************************************************************/
9
10 #include <cstring> // strncmp
11 #include <fstream>
12 #include <stdexcept>
13
14 #include <png.h>
15 #include <SDL/SDL.h>
16
17 #include "backend.hh"
18 #include "image.hh"
19 #include "log.hh"
20 #include "opengl.hh"
21 #include "script.hh"
22 #include "video.hh"
23
24
25 namespace moof {
26
27
28 MOOF_REGISTER_RESOURCE(image, png, textures);
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 static void read_from_stream(png_structp context, png_bytep data, png_size_t length)
45 {
46 std::istream& stream(*(std::istream*)png_get_io_ptr(context));
47 stream.read((char*)data, length);
48 }
49
50
51 image::image(const std::string& path) :
52 pixels_(0),
53 object_(0),
54 min_filter_(GL_NEAREST),
55 mag_filter_(GL_NEAREST),
56 tile_s_(1),
57 tile_t_(1),
58 wrap_s_(GL_CLAMP),
59 wrap_t_(GL_CLAMP)
60 {
61 std::ifstream file(path.c_str(), std::ifstream::binary);
62 if (!file.good())
63 throw std::runtime_error("no valid image found at " + path);
64
65 png_byte signature[8];
66 size_t bytesRead;
67
68 int bpp;
69
70 png_byte colors;
71 png_bytepp rows = 0;
72
73 png_textp texts = 0;
74 int nutext_s;
75
76 bytesRead = file.read((char*)signature, sizeof(signature)).gcount();
77 if (bytesRead < sizeof(signature) ||
78 png_sig_cmp(signature, 0, sizeof(signature)) != 0) throw 0;
79
80 struct png
81 {
82 png_structp context;
83 png_infop info;
84 png() :
85 context(png_create_read_struct(PNG_LIBPNG_VER_STRING,
86 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 texture = 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 texture = 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 (texture)
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_ +
155 i * channels_ * width_);
156 }
157 }
158 else
159 {
160 log_debug("no texture attributes found");
161 for (int i = 0; i < height_; ++i)
162 {
163 rows[i] = (png_bytep)(pixels_ +
164 i * channels_ * width_);
165 }
166 }
167
168 png_read_image(png.context, rows);
169 png_read_end(png.context, 0);
170
171 // TODO leak
172 delete[] rows;
173 }
174
175 image::~image()
176 {
177 unload_from_gl();
178 delete[] pixels_;
179 }
180
181
182 void image::set_as_icon() const
183 {
184 backend backend;
185
186 SDL_Surface* context = SDL_CreateRGBSurfaceFrom
187 (
188 pixels_,
189 width_,
190 height_,
191 depth_,
192 pitch_,
193 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
194 0x000000FF,
195 0x0000FF00,
196 0x00FF0000,
197 0xFF000000
198 #else
199 0xFF000000,
200 0x00FF0000,
201 0x0000FF00,
202 0x000000FF
203 #endif
204 );
205
206 SDL_WM_SetIcon(context, 0);
207 SDL_FreeSurface(context);
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 void image::bind() const
231 {
232 ASSERT(video::current() && "should have a video context set");
233
234 if (object_ == 0)
235 {
236 upload_to_gl();
237 }
238 if (object_ != global_object_)
239 {
240 glBindTexture(GL_TEXTURE_2D, (GLuint)object_);
241 global_object_ = object_;
242 }
243 }
244
245 void image::reset_binding()
246 {
247 ASSERT(video::current() && "should have a video context set");
248
249 glBindTexture(GL_TEXTURE_2D, 0);
250 global_object_ = 0;
251 }
252
253 /*
254 * Upload the image to GL so that it will be accessible by a much more
255 * manageable handle and hopefully reside in video memory.
256 */
257 void image::upload_to_gl() const
258 {
259 if (object_) return; // already loaded
260
261 glGenTextures(1, (GLuint*)&object_);
262 glBindTexture(GL_TEXTURE_2D, (GLuint)object_);
263
264 GLuint mode;
265 if (channels_ == 3) mode = GL_RGB;
266 else mode = GL_RGBA;
267
268 glTexImage2D
269 //gluBuild2DMipmaps
270 (
271 GL_TEXTURE_2D,
272 0,
273 mode,
274 //3,
275 width_,
276 height_,
277 0,
278 mode,
279 GL_UNSIGNED_BYTE,
280 pixels_
281 );
282
283 set_properties();
284
285 // we want to know when the GL context is recreated
286 //dispatcher& dispatcher = dispatcher::global();
287 //new_context_ = dispatcher.add_target("video.newcontext",
288 //boost::bind(&image::context_recreated, this));
289 // FIXME this has const issues
290 }
291
292 void image::unload_from_gl() const
293 {
294 if (object_)
295 {
296 if (object_ == global_object_) global_object_ = 0;
297 glDeleteTextures(1, (GLuint*)&object_);
298 object_ = 0;
299 }
300 }
301
302 void image::context_recreated()
303 {
304 object_ = global_object_ = 0;
305 upload_to_gl();
306 }
307
308 /*
309 * Sets some texture properties such as the filters and external
310 * coordinate behavior.
311 */
312 void image::set_properties() const
313 {
314 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter_);
315 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter_);
316 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s_);
317 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t_);
318 }
319
320 void image::set_texture_info(const std::string& info)
321 {
322 script script;
323 log::import(script);
324
325 script::slot g = script.globals();
326 g.set_field("CLAMP", GL_CLAMP);
327 g.set_field("REPEAT", GL_REPEAT);
328 g.set_field("LINEAR", GL_LINEAR);
329 g.set_field("NEAREST", GL_NEAREST);
330 g.set_field("LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR);
331 g.set_field("LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST);
332 g.set_field("NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR);
333 g.set_field("NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST);
334
335 if (script.do_string(info) != script::success)
336 {
337 std::string str;
338 script[-1].get(str);
339 log_warning(str);
340 }
341 else
342 {
343 log_info("loading texture information...");
344
345 script::slot globals = script.globals();
346 globals.get(min_filter_, "min_filter");
347 globals.get(mag_filter_, "mag_filter");
348 globals.get(tile_s_, "tile_s");
349 globals.get(tile_t_, "tile_t");
350 globals.get(wrap_s_, "wrap_s");
351 globals.get(wrap_t_, "wrap_t");
352 }
353 }
354
355
356 } // namespace moof
357
This page took 0.0495 seconds and 3 git commands to generate.