2 /*] Copyright (c) 2009-2011, Charles McGarvey [*****************************
3 **] All rights reserved.
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.
8 *****************************************************************************/
16 #include <stlplus/portability/file_system.hpp>
30 MOOF_REGISTER_RESOURCE(image
, bmp
, textures
);
31 MOOF_REGISTER_RESOURCE(image
, png
, textures
);
34 static int higher_power_of_two(int input
)
37 while (value
<= input
) value
<<= 1;
41 static void read_from_stream(png_structp context
,
42 png_bytep data
, png_size_t length
)
44 std::istream
& stream(*(std::istream
*)png_get_io_ptr(context
));
45 stream
.read((char*)data
, length
);
49 struct texture_attributes
51 texture_attributes() :
52 min_filter(GL_NEAREST
),
53 mag_filter(GL_NEAREST
),
56 wrap_s(GL_CLAMP_TO_EDGE
),
57 wrap_t(GL_CLAMP_TO_EDGE
) {}
59 void init(const std::string
& info
)
64 script::slot g
= script
.globals();
65 #define EXPORT_CONSTANT(K) g.set_field(#K, GL_##K)
66 EXPORT_CONSTANT(CLAMP
);
67 EXPORT_CONSTANT(CLAMP_TO_EDGE
);
68 EXPORT_CONSTANT(REPEAT
);
69 EXPORT_CONSTANT(LINEAR
);
70 EXPORT_CONSTANT(NEAREST
);
71 EXPORT_CONSTANT(LINEAR_MIPMAP_LINEAR
);
72 EXPORT_CONSTANT(LINEAR_MIPMAP_NEAREST
);
73 EXPORT_CONSTANT(NEAREST_MIPMAP_LINEAR
);
74 EXPORT_CONSTANT(NEAREST_MIPMAP_NEAREST
);
75 #undef export_constant
77 if (script
.do_string(info
) != script::success
)
85 log_info("loading texture information...");
87 script::slot globals
= script
.globals();
88 globals
.get(min_filter
, "min_filter");
89 globals
.get(mag_filter
, "mag_filter");
90 globals
.get(tile_s
, "tile_s");
91 globals
.get(tile_t
, "tile_t");
92 globals
.get(wrap_s
, "wrap_s");
93 globals
.get(wrap_t
, "wrap_t");
106 static SDL_Surface
* load_png(const std::string
& path
, texture_attributes
& attribs
)
108 std::ifstream
file(path
.c_str(), std::ifstream::binary
);
110 throw std::runtime_error("no valid image found at " + path
);
112 png_byte signature
[8];
123 bytesRead
= file
.read((char*)signature
, sizeof(signature
)).gcount();
124 if (bytesRead
< sizeof(signature
) ||
125 png_sig_cmp(signature
, 0, sizeof(signature
)) != 0) throw 0;
132 context(png_create_read_struct(PNG_LIBPNG_VER_STRING
,
134 info(png_create_info_struct(context
))
136 if (!context
|| !info
) throw 0;
140 png_destroy_read_struct(context
? &context
: 0,
141 info
? &info
: 0, 0);
145 if (setjmp(png_jmpbuf(png
.context
))) throw 0;
147 png_set_read_fn(png
.context
, (void*)&file
, read_from_stream
);
148 png_set_sig_bytes(png
.context
, sizeof(signature
));
149 png_read_info(png
.context
, png
.info
);
151 bpp
= png_get_bit_depth(png
.context
, png
.info
);
152 colors
= png_get_color_type(png
.context
, png
.info
);
155 case PNG_COLOR_TYPE_PALETTE
:
156 png_set_palette_to_rgb(png
.context
);
159 case PNG_COLOR_TYPE_GRAY
:
160 if (bpp
< 8) png_set_expand(png
.context
);
163 case PNG_COLOR_TYPE_GRAY_ALPHA
:
164 png_set_gray_to_rgb(png
.context
);
168 if (bpp
== 16) png_set_strip_16(png
.context
);
170 png_read_update_info(png
.context
, png
.info
);
172 bpp
= png_get_bit_depth(png
.context
, png
.info
);
173 channels_
= png_get_channels(png
.context
, png
.info
);
174 depth_
= bpp
* channels_
;
177 bool texture
= false;
178 png_get_text(png
.context
, png
.info
, &texts
, &nutext_s
);
179 for (int i
= 0; i
< nutext_s
; ++i
)
181 if (strncmp(texts
[i
].key
, "X-Yoink-Texture", 11) == 0)
183 set_texture_info(texts
[i
].text
);
189 width_
= png_get_image_width(png
.context
, png
.info
);
190 height_
= png_get_image_height(png
.context
, png
.info
);
192 pitch_
= png_get_rowbytes(png
.context
, png
.info
);
193 pixels_
= new char[width_
* pitch_
];
195 rows
= new png_bytep
[height_
];
198 log_debug("texture detected; loading flipped");
199 for (int i
= 0; i
< height_
; ++i
)
201 rows
[height_
-1-i
] = (png_bytep
)(pixels_
+
202 i
* channels_
* width_
);
207 log_debug("no texture attributes found");
208 for (int i
= 0; i
< height_
; ++i
)
210 rows
[i
] = (png_bytep
)(pixels_
+
211 i
* channels_
* width_
);
215 png_read_image(png
.context
, rows
);
216 png_read_end(png
.context
, 0);
229 void image::set_as_icon() const
233 SDL_Surface
* context
= SDL_CreateRGBSurfaceFrom
240 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
253 SDL_WM_SetIcon(context
, 0);
254 SDL_FreeSurface(context
);
257 bool image::tile_coordinates(int index
, scalar coords
[8]) const
259 // make sure the index represents a real tile
260 if (index
< 0 && index
>= tile_s_
* tile_t_
) return false;
262 scalar w
= 1.0 / scalar(tile_s_
);
263 scalar h
= 1.0 / scalar(tile_t_
);
265 coords
[0] = scalar(index
% tile_s_
) * w
;
266 coords
[1] = (scalar(tile_t_
- 1) - scalar(index
/ tile_s_
)) * h
;
267 coords
[2] = coords
[0] + w
;
268 coords
[3] = coords
[1];
269 coords
[4] = coords
[2];
270 coords
[5] = coords
[1] + h
;
271 coords
[6] = coords
[0];
272 coords
[7] = coords
[5];
277 void image::bind() const
279 ASSERT(video::current() && "should have a video context set");
285 if (object_
!= global_object_
)
287 glBindTexture(GL_TEXTURE_2D
, (GLuint
)object_
);
288 global_object_
= object_
;
292 void image::reset_binding()
294 ASSERT(video::current() && "should have a video context set");
296 glBindTexture(GL_TEXTURE_2D
, 0);
301 * Upload the image to GL so that it will be accessible by a much more
302 * manageable handle and hopefully reside in video memory.
304 void image::upload_to_gl() const
306 if (object_
) return; // already loaded
308 glGenTextures(1, (GLuint
*)&object_
);
309 glBindTexture(GL_TEXTURE_2D
, (GLuint
)object_
);
312 if (channels_
== 3) mode
= GL_RGB
;
332 // we want to know when the GL context is recreated
333 //dispatcher& dispatcher = dispatcher::global();
334 //new_context_ = dispatcher.add_target("video.newcontext",
335 //boost::bind(&image::context_recreated, this));
336 // FIXME this has const issues
339 void image::unload_from_gl() const
343 if (object_
== global_object_
) global_object_
= 0;
344 glDeleteTextures(1, (GLuint
*)&object_
);
349 void image::context_recreated()
351 object_
= global_object_
= 0;
356 * Sets some texture properties such as the filters and external
357 * coordinate behavior.
359 void image::set_properties() const
361 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, min_filter_
);
362 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, mag_filter_
);
363 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, wrap_s_
);
364 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, wrap_t_
);
367 void image::set_texture_info(const std::string
& info
)
372 script::slot g
= script
.globals();
373 g
.set_field("CLAMP", GL_CLAMP
);
374 g
.set_field("REPEAT", GL_REPEAT
);
375 g
.set_field("LINEAR", GL_LINEAR
);
376 g
.set_field("NEAREST", GL_NEAREST
);
377 g
.set_field("LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR
);
378 g
.set_field("LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST
);
379 g
.set_field("NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR
);
380 g
.set_field("NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST
);
382 if (script
.do_string(info
) != script::success
)
390 log_info("loading texture information...");
392 script::slot globals
= script
.globals();
393 globals
.get(min_filter_
, "min_filter");
394 globals
.get(mag_filter_
, "mag_filter");
395 globals
.get(tile_s_
, "tile_s");
396 globals
.get(tile_t_
, "tile_t");
397 globals
.get(wrap_s_
, "wrap_s");
398 globals
.get(wrap_t_
, "wrap_t");