2 /*] Copyright (c) 2009-2010, Charles McGarvey [**************************
3 **] All rights reserved.
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.
10 **************************************************************************/
12 #include <cstring> // strncmp
30 MOOF_REGISTER_RESOURCE(image
, png
, textures
);
32 //static int power_of_two(int input)
36 //while (value < input)
43 unsigned image::global_object_
= 0;
46 static void read_from_stream(png_structp context
, png_bytep data
, png_size_t length
)
48 std::istream
& stream(*(std::istream
*)png_get_io_ptr(context
));
49 stream
.read((char*)data
, length
);
53 image::image(const std::string
& path
) :
56 min_filter_(GL_NEAREST
),
57 mag_filter_(GL_NEAREST
),
63 std::ifstream
file(path
.c_str());
64 if (!file
.good()) throw std::runtime_error("no valid image found at " + path
);
66 png_byte signature
[8];
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;
86 context(png_create_read_struct(PNG_LIBPNG_VER_STRING
, 0, 0, 0)),
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 isTexture
= 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_
+ i
* channels_
* width_
);
159 log_debug("no texture attributes found");
160 for (int i
= 0; i
< height_
; ++i
)
162 rows
[i
] = (png_bytep
)(pixels_
+ i
* channels_
* width_
);
166 png_read_image(png
.context
, rows
);
167 png_read_end(png
.context
, 0);
180 void image::set_as_icon() const
184 SDL_Surface
* context
= SDL_CreateRGBSurfaceFrom
191 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
204 SDL_WM_SetIcon(context
, 0);
206 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];
231 void image::bind() const
233 ASSERT(video::current() && "should have a video context set");
239 if (object_
!= global_object_
)
241 glBindTexture(GL_TEXTURE_2D
, (GLuint
)object_
);
242 global_object_
= object_
;
246 void image::reset_binding()
248 ASSERT(video::current() && "should have a video context set");
250 glBindTexture(GL_TEXTURE_2D
, 0);
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.
259 void image::upload_to_gl() const
267 glGenTextures(1, (GLuint
*)&object_
);
268 glBindTexture(GL_TEXTURE_2D
, (GLuint
)object_
);
271 if (channels_
== 3) mode
= GL_RGB
;
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
298 void image::unload_from_gl() const
302 if (object_
== global_object_
)
307 glDeleteTextures(1, (GLuint
*)&object_
);
312 void image::context_recreated()
314 object_
= global_object_
= 0;
319 * Sets some texture properties such as the filters and external
320 * coordinate behavior.
322 void image::set_properties() const
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_
);
331 void image::set_texture_info(const std::string
& info
)
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
);
346 if (script
.do_string(info
) != script::success
)
354 log_info("loading texture information...");
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");