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 *****************************************************************************/
10 #include <cstring> // strncmp
28 MOOF_REGISTER_RESOURCE(image
, png
, textures
);
30 //static int power_of_two(int input)
34 //while (value < input)
41 unsigned image::global_object_
= 0;
44 static void read_from_stream(png_structp context
, png_bytep data
, png_size_t length
)
46 std::istream
& stream(*(std::istream
*)png_get_io_ptr(context
));
47 stream
.read((char*)data
, length
);
51 image::image(const std::string
& path
) :
54 min_filter_(GL_NEAREST
),
55 mag_filter_(GL_NEAREST
),
61 std::ifstream
file(path
.c_str(), std::ifstream::binary
);
63 throw std::runtime_error("no valid image found at " + path
);
65 png_byte signature
[8];
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;
85 context(png_create_read_struct(PNG_LIBPNG_VER_STRING
,
87 info(png_create_info_struct(context
))
89 if (!context
|| !info
) throw 0;
93 png_destroy_read_struct(context
? &context
: 0,
98 if (setjmp(png_jmpbuf(png
.context
))) throw 0;
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
);
104 bpp
= png_get_bit_depth(png
.context
, png
.info
);
105 colors
= png_get_color_type(png
.context
, png
.info
);
108 case PNG_COLOR_TYPE_PALETTE
:
109 png_set_palette_to_rgb(png
.context
);
112 case PNG_COLOR_TYPE_GRAY
:
113 if (bpp
< 8) png_set_expand(png
.context
);
116 case PNG_COLOR_TYPE_GRAY_ALPHA
:
117 png_set_gray_to_rgb(png
.context
);
121 if (bpp
== 16) png_set_strip_16(png
.context
);
123 png_read_update_info(png
.context
, png
.info
);
125 bpp
= png_get_bit_depth(png
.context
, png
.info
);
126 channels_
= png_get_channels(png
.context
, png
.info
);
127 depth_
= bpp
* channels_
;
130 bool texture
= false;
131 png_get_text(png
.context
, png
.info
, &texts
, &nutext_s
);
132 for (int i
= 0; i
< nutext_s
; ++i
)
134 if (strncmp(texts
[i
].key
, "X-Yoink-Texture", 11) == 0)
136 set_texture_info(texts
[i
].text
);
142 width_
= png_get_image_width(png
.context
, png
.info
);
143 height_
= png_get_image_height(png
.context
, png
.info
);
145 pitch_
= png_get_rowbytes(png
.context
, png
.info
);
146 pixels_
= new char[width_
* pitch_
];
148 rows
= new png_bytep
[height_
];
151 log_debug("texture detected; loading flipped");
152 for (int i
= 0; i
< height_
; ++i
)
154 rows
[height_
-1-i
] = (png_bytep
)(pixels_
+
155 i
* channels_
* width_
);
160 log_debug("no texture attributes found");
161 for (int i
= 0; i
< height_
; ++i
)
163 rows
[i
] = (png_bytep
)(pixels_
+
164 i
* channels_
* width_
);
168 png_read_image(png
.context
, rows
);
169 png_read_end(png
.context
, 0);
182 void image::set_as_icon() const
186 SDL_Surface
* context
= SDL_CreateRGBSurfaceFrom
193 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
206 SDL_WM_SetIcon(context
, 0);
207 SDL_FreeSurface(context
);
210 bool image::tile_coordinates(int index
, scalar coords
[8]) const
212 // make sure the index represents a real tile
213 if (index
< 0 && index
>= tile_s_
* tile_t_
) return false;
215 scalar w
= 1.0 / scalar(tile_s_
);
216 scalar h
= 1.0 / scalar(tile_t_
);
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];
230 void image::bind() const
232 ASSERT(video::current() && "should have a video context set");
238 if (object_
!= global_object_
)
240 glBindTexture(GL_TEXTURE_2D
, (GLuint
)object_
);
241 global_object_
= object_
;
245 void image::reset_binding()
247 ASSERT(video::current() && "should have a video context set");
249 glBindTexture(GL_TEXTURE_2D
, 0);
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.
257 void image::upload_to_gl() const
259 if (object_
) return; // already loaded
261 glGenTextures(1, (GLuint
*)&object_
);
262 glBindTexture(GL_TEXTURE_2D
, (GLuint
)object_
);
265 if (channels_
== 3) mode
= GL_RGB
;
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
292 void image::unload_from_gl() const
296 if (object_
== global_object_
) global_object_
= 0;
297 glDeleteTextures(1, (GLuint
*)&object_
);
302 void image::context_recreated()
304 object_
= global_object_
= 0;
309 * Sets some texture properties such as the filters and external
310 * coordinate behavior.
312 void image::set_properties() const
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_
);
320 void image::set_texture_info(const std::string
& info
)
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
);
335 if (script
.do_string(info
) != script::success
)
343 log_info("loading texture information...");
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");