/*] Copyright (c) 2009-2010, Charles McGarvey [************************** **] All rights reserved. * * vi:ts=4 sw=4 tw=75 * * Distributable under the terms and conditions of the 2-clause BSD license; * see the file COPYING for a complete text of the license. * **************************************************************************/ #include // FILE #include // strncmp #include #include #include #include "backend.hh" #include "image.hh" #include "log.hh" #include "opengl.hh" #include "script.hh" #include "video.hh" namespace moof { //static int power_of_two(int input) //{ //int value = 1; //while (value < input) //{ //value <<= 1; //} //return value; //} unsigned image::global_object_ = 0; image::image(const std::string& path) : pixels_(0), object_(0), min_filter_(GL_NEAREST), mag_filter_(GL_NEAREST), wrap_s_(GL_CLAMP), wrap_t_(GL_CLAMP), tile_width_(1), tile_height_(1) { FILE* fp = resource::open_file(path); if (!fp) throw std::runtime_error("image not found at " + path); png_byte signature[8]; size_t bytesRead; png_infop pngInfo = 0; png_infop pngInfoEnd = 0; png_structp pngObj = 0; int bpp; png_byte colors; png_bytepp rows = 0; png_textp texts = 0; int nutext_s; bytesRead = fread(signature, 1, sizeof(signature), fp); if (bytesRead < sizeof(signature) || png_sig_cmp(signature, 0, sizeof(signature)) != 0) goto cleanup; pngObj = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if (!pngObj) goto cleanup; pngInfo = png_create_info_struct(pngObj); if (!pngInfo) goto cleanup; pngInfoEnd = png_create_info_struct(pngObj); if (!pngInfoEnd) goto cleanup; if (setjmp(png_jmpbuf(pngObj))) goto cleanup; png_init_io(pngObj, fp); png_set_sig_bytes(pngObj, sizeof(signature)); png_read_info(pngObj, pngInfo); bpp = png_get_bit_depth(pngObj, pngInfo); colors = png_get_color_type(pngObj, pngInfo); switch (colors) { case PNG_COLOR_TYPE_PALETTE: png_set_palette_to_rgb(pngObj); break; case PNG_COLOR_TYPE_GRAY: if (bpp < 8) png_set_expand(pngObj); break; case PNG_COLOR_TYPE_GRAY_ALPHA: png_set_gray_to_rgb(pngObj); break; } if (bpp == 16) png_set_strip_16(pngObj); png_read_update_info(pngObj, pngInfo); bpp = png_get_bit_depth(pngObj, pngInfo); channels_ = png_get_channels(pngObj, pngInfo); depth_ = bpp * channels_; // read comments png_get_text(pngObj, pngInfo, &texts, &nutext_s); for (int i = 0; i < nutext_s; ++i) { if (strncmp(texts[i].key, "TextureInfo", 11) == 0) { set_texture_info(texts[i].text); break; } } width_ = png_get_image_width(pngObj, pngInfo); height_ = png_get_image_height(pngObj, pngInfo); pitch_ = png_get_rowbytes(pngObj, pngInfo); pixels_ = new char[width_ * pitch_]; rows = new png_bytep[height_]; for (int i = 0; i < height_; ++i) { rows[i] = (png_bytep)(pixels_ + i * channels_ * width_); } png_read_image(pngObj, rows); png_read_end(pngObj, 0); cleanup: delete[] rows; png_destroy_read_struct(pngObj ? &pngObj : 0, pngInfo ? &pngInfo : 0, pngInfoEnd ? &pngInfoEnd : 0); fclose(fp); } image::~image() { unload_from_gl(); delete[] pixels_; } void image::set_as_icon() const { backend backend; SDL_Surface* context = SDL_CreateRGBSurfaceFrom ( pixels_, width_, height_, depth_, pitch_, #if SDL_BYTEORDER == SDL_LIL_ENDIAN 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 #else 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF #endif ); SDL_WM_SetIcon(context, 0); SDL_FreeSurface(context); } bool image::tile_coordinates(int index, scalar coords[8]) const { // make sure the index represents a real tile if (index < 0 && index >= tile_width_ * tile_height_) return false; scalar w = 1.0 / scalar(tile_width_); scalar h = 1.0 / scalar(tile_height_); coords[0] = scalar(index % tile_width_) * w; coords[1] = (scalar(tile_height_ - 1) - scalar(index / tile_width_)) * h; coords[2] = coords[0] + w; coords[3] = coords[1]; coords[4] = coords[2]; coords[5] = coords[1] + h; coords[6] = coords[0]; coords[7] = coords[5]; return true; } void image::bind() const { ASSERT(video::current() && "should have a video context set"); if (object_ == 0) { upload_to_gl(); } if (object_ != global_object_) { glBindTexture(GL_TEXTURE_2D, (GLuint)object_); global_object_ = object_; } } void image::reset_binding() { ASSERT(video::current() && "should have a video context set"); glBindTexture(GL_TEXTURE_2D, 0); global_object_ = 0; } /* * Upload the image to GL so that it will be accessible by a much more * manageable handle and hopefully reside in video memory. */ void image::upload_to_gl() const { if (object_) { // already loaded return; } glGenTextures(1, (GLuint*)&object_); glBindTexture(GL_TEXTURE_2D, (GLuint)object_); GLuint mode; if (channels_ == 3) mode = GL_RGB; else mode = GL_RGBA; glTexImage2D //gluBuild2DMipmaps ( GL_TEXTURE_2D, 0, mode, //3, width_, height_, 0, mode, GL_UNSIGNED_BYTE, pixels_ ); set_properties(); // we want to know when the GL context is recreated //dispatcher& dispatcher = dispatcher::global(); //new_context_ = dispatcher.add_target("video.newcontext", //boost::bind(&image::context_recreated, this)); // FIXME this has const issues } void image::unload_from_gl() const { if (object_) { if (object_ == global_object_) { global_object_ = 0; } glDeleteTextures(1, (GLuint*)&object_); object_ = 0; } } void image::context_recreated() { object_ = global_object_ = 0; upload_to_gl(); } /* * Sets some texture properties such as the filters and external * coordinate behavior. */ void image::set_properties() const { //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter_); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter_); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s_); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t_); } void image::set_texture_info(const std::string& info) { script script; log::import(script); script::slot g = script.globals(); g.set_field("CLAMP", GL_CLAMP); g.set_field("REPEAT", GL_REPEAT); g.set_field("LINEAR", GL_LINEAR); g.set_field("NEAREST", GL_NEAREST); g.set_field("LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR); g.set_field("LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST); g.set_field("NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR); g.set_field("NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST); if (script.do_string(info) != script::success) { std::string str; script[-1].get(str); log_warning(str); } else { log_info("loading texture information..."); script::slot globals = script.globals(); globals.get(tile_width_, "tiles_s"); globals.get(tile_height_, "tiles_t"); globals.get(min_filter_, "min_filter"); globals.get(mag_filter_, "mag_filter"); globals.get(wrap_s_, "wrap_s"); globals.get(wrap_t_, "wrap_t"); } } class image_resource_loader { public: image_resource_loader() { resource::register_type("png"); } ~image_resource_loader() { resource::unregister_type("png"); } }; static image_resource_loader loader; } // namespace moof