X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2Fmoof%2Fimage.cc;h=c9c0638dba4adf6561eccdfc9ba941ab00f9f33c;hp=b9076f0c9ec1a46d3808851136155971574c6d4a;hb=62f3ceaff75a6b08cb1aec9a465773bb81a2d79d;hpb=6c9943707d4f33035830eba0587a61a34eaecbc2 diff --git a/src/moof/image.cc b/src/moof/image.cc index b9076f0..c9c0638 100644 --- a/src/moof/image.cc +++ b/src/moof/image.cc @@ -1,22 +1,22 @@ -/*] Copyright (c) 2009-2010, Charles McGarvey [************************** +/*] Copyright (c) 2009-2011, 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 // strncmp #include #include - #include + #include +#include + #include "backend.hh" +#include "debug.hh" #include "image.hh" #include "log.hh" #include "opengl.hh" @@ -27,52 +27,98 @@ namespace moof { +MOOF_REGISTER_RESOURCE(image, bmp, textures); MOOF_REGISTER_RESOURCE(image, png, textures); -//static int power_of_two(int input) -//{ - //int value = 1; - - //while (value < input) - //{ - //value <<= 1; - //} - //return value; -//} - -unsigned image::global_object_ = 0; +static int higher_power_of_two(int input) +{ + int value = 2; + while (value <= input) value <<= 1; + return value; +} -static void read_from_stream(png_structp context, png_bytep data, png_size_t length) +static void read_from_stream(png_structp context, + png_bytep data, png_size_t length) { std::istream& stream(*(std::istream*)png_get_io_ptr(context)); stream.read((char*)data, length); } -image::image(const std::string& path) : - pixels_(0), - object_(0), - min_filter_(GL_NEAREST), - mag_filter_(GL_NEAREST), - tile_s_(1), - tile_t_(1), - wrap_s_(GL_CLAMP), - wrap_t_(GL_CLAMP) +struct texture_attributes +{ + texture_attributes() : + min_filter(GL_NEAREST), + mag_filter(GL_NEAREST), + tile_s(1), + tile_t(1), + wrap_s(GL_CLAMP_TO_EDGE), + wrap_t(GL_CLAMP_TO_EDGE) {} + + void init(const std::string& info) + { + script script; + log::import(script); + + script::slot g = script.globals(); +#define EXPORT_CONSTANT(K) g.set_field(#K, GL_##K) + EXPORT_CONSTANT(CLAMP); + EXPORT_CONSTANT(CLAMP_TO_EDGE); + EXPORT_CONSTANT(REPEAT); + EXPORT_CONSTANT(LINEAR); + EXPORT_CONSTANT(NEAREST); + EXPORT_CONSTANT(LINEAR_MIPMAP_LINEAR); + EXPORT_CONSTANT(LINEAR_MIPMAP_NEAREST); + EXPORT_CONSTANT(NEAREST_MIPMAP_LINEAR); + EXPORT_CONSTANT(NEAREST_MIPMAP_NEAREST); +#undef export_constant + + 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(min_filter, "min_filter"); + globals.get(mag_filter, "mag_filter"); + globals.get(tile_s, "tile_s"); + globals.get(tile_t, "tile_t"); + globals.get(wrap_s, "wrap_s"); + globals.get(wrap_t, "wrap_t"); + } + } + + GLuint min_filter; + GLuint mag_filter; + int tile_s; + int tile_t; + GLuint wrap_s; + GLuint wrap_t; +}; + + +static SDL_Surface* load_png(const std::string& path, texture_attributes& attribs) { - std::ifstream file(path.c_str()); - if (!file.good()) throw std::runtime_error("no valid image found at " + path); + std::ifstream file(path.c_str(), std::ifstream::binary); + if (!file.good()) + throw std::runtime_error("no valid image found at " + path); png_byte signature[8]; size_t bytesRead; - int bpp; + int bpp; png_byte colors; - png_bytepp rows = 0; + png_bytepp rows; png_textp texts = 0; - int nutext_s; + int nutext_s; bytesRead = file.read((char*)signature, sizeof(signature)).gcount(); if (bytesRead < sizeof(signature) || @@ -80,10 +126,11 @@ image::image(const std::string& path) : struct png { - png_structp context; + png_structp context; png_infop info; png() : - context(png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0)), + context(png_create_read_struct(PNG_LIBPNG_VER_STRING, + 0, 0, 0)), info(png_create_info_struct(context)) { if (!context || !info) throw 0; @@ -91,7 +138,7 @@ image::image(const std::string& path) : ~png() { png_destroy_read_struct(context ? &context : 0, - info ? &info : 0, 0); + info ? &info : 0, 0); } } png; @@ -105,17 +152,17 @@ image::image(const std::string& path) : colors = png_get_color_type(png.context, png.info); switch (colors) { - case PNG_COLOR_TYPE_PALETTE: - png_set_palette_to_rgb(png.context); - break; + case PNG_COLOR_TYPE_PALETTE: + png_set_palette_to_rgb(png.context); + break; - case PNG_COLOR_TYPE_GRAY: - if (bpp < 8) png_set_expand(png.context); - break; + case PNG_COLOR_TYPE_GRAY: + if (bpp < 8) png_set_expand(png.context); + break; - case PNG_COLOR_TYPE_GRAY_ALPHA: - png_set_gray_to_rgb(png.context); - break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + png_set_gray_to_rgb(png.context); + break; } if (bpp == 16) png_set_strip_16(png.context); @@ -123,51 +170,139 @@ image::image(const std::string& path) : png_read_update_info(png.context, png.info); bpp = png_get_bit_depth(png.context, png.info); - channels_ = png_get_channels(png.context, png.info); - depth_ = bpp * channels_; + int channels = png_get_channels(png.context, png.info); + int depth = bpp * channels; // read comments - bool isTexture = false; + bool texture = false; png_get_text(png.context, png.info, &texts, &nutext_s); for (int i = 0; i < nutext_s; ++i) { - if (strncmp(texts[i].key, "X-Yoink-Texture", 11) == 0) + if (std::string(texts[i].key) == "X-Yoink-Texture") { - set_texture_info(texts[i].text); - isTexture = true; + attribs.init(texts[i].text); + texture = true; break; } } - width_ = png_get_image_width(png.context, png.info); - height_ = png_get_image_height(png.context, png.info); - - pitch_ = png_get_rowbytes(png.context, png.info); - pixels_ = new char[width_ * pitch_]; + int width = png_get_image_width(png.context, png.info); + int height = png_get_image_height(png.context, png.info); + int pitch = png_get_rowbytes(png.context, png.info); + char* pixels = new char[height * pitch]; - rows = new png_bytep[height_]; - if (isTexture) + rows = new png_bytep[height]; + if (texture) { log_debug("texture detected; loading flipped"); - for (int i = 0; i < height_; ++i) + for (int i = 0; i < height; ++i) { - rows[height_-1-i] = (png_bytep)(pixels_ + i * channels_ * width_); + rows[height-1-i] = (png_bytep)(pixels + + i * channels * width); } } else { log_debug("no texture attributes found"); - for (int i = 0; i < height_; ++i) + for (int i = 0; i < height; ++i) { - rows[i] = (png_bytep)(pixels_ + i * channels_ * width_); + rows[i] = (png_bytep)(pixels + + i * channels * width); } } png_read_image(png.context, rows); + delete[] rows; + png_read_end(png.context, 0); - // TODO leak - delete[] rows; + SDL_Surface* src = SDL_CreateRGBSurfaceFrom + ( + pixels, + width, + height, + depth, + pitch, +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0xFF000000 +#else + 0xFF000000, + 0x00FF0000, + 0x0000FF00, + 0x000000FF +#endif + ); + if (!src) throw std::runtime_error(SDL_GetError()); + return src; +} + +static SDL_Surface* load_bmp(const std::string& path) +{ + return SDL_LoadBMP(path.c_str()); +} + + +#if 0 +static void save_bmp(SDL_Surface* image, const std::string& path) +{ + if (SDL_SaveBMP(image, path.c_str()) != 0) + throw std::runtime_error(SDL_GetError()); +} + + +static void destroy_context(SDL_Surface* context) +{ + if (context->flags & SDL_PREALLOC) delete[] (char*)context->pixels; + SDL_FreeSurface(context); +} +#endif + + +image::image(const std::string& path) : + pixels_(0), + object_(0), + min_filter_(GL_NEAREST), + mag_filter_(GL_NEAREST), + tile_s_(1), + tile_t_(1), + wrap_s_(GL_CLAMP_TO_EDGE), + wrap_t_(GL_CLAMP_TO_EDGE) +{ + std::string ext = stlplus::extension_part(path); + + SDL_Surface* context = 0; + if (ext == "png") + { + texture_attributes attribs; + context = load_png(path, attribs); + pixels_ = (char*)context->pixels; + width_ = context->w; + height_ = context->h; + depth_ = 32; + pitch_ = context->pitch; + channels_ = 4; + min_filter_ = attribs.min_filter; + mag_filter_ = attribs.mag_filter; + tile_s_ = attribs.tile_s; + tile_t_ = attribs.tile_t; + wrap_s_ = attribs.wrap_s; + wrap_t_ = attribs.wrap_t; + postprocess(); + } + else if (ext == "bmp") + { + context = load_bmp(path); + pixels_ = (char*)context->pixels; + width_ = context->w; + height_ = context->h; + depth_ = 32; + pitch_ = context->pitch; + channels_ = 4; + } + // TODO leaking context } image::~image() @@ -177,6 +312,189 @@ image::~image() } +void image::postprocess() +{ + if (1 == tile_s_ && 1 == tile_t_) return; + + SDL_Surface* src = SDL_CreateRGBSurfaceFrom + ( + pixels_, + width_, + height_, + depth_, + pitch_, +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0xFF000000 +#else + 0xFF000000, + 0x00FF0000, + 0x0000FF00, + 0x000000FF +#endif + ); + SDL_Surface* dst = SDL_CreateRGBSurface( + SDL_SWSURFACE, + higher_power_of_two(width_), + higher_power_of_two(height_), + depth_, +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0xFF000000 +#else + 0xFF000000, + 0x00FF0000, + 0x0000FF00, + 0x000000FF +#endif + ); + ASSERT(src && dst); + + SDL_SetAlpha(src, 0, 128); + SDL_SetAlpha(dst, 0, 128); + + int src_tilew = src->w / tile_s_; + int src_tileh = src->h / tile_t_; + int dst_tilew = dst->w / tile_s_; + int dst_tileh = dst->h / tile_t_; + + for (int t = 0; t < tile_t_; ++t) for (int s = 0; s < tile_s_; ++s) + { + SDL_Rect srcrect; + SDL_Rect dstrect; + srcrect.x = s * src_tilew; + srcrect.y = t * src_tileh; + srcrect.w = src_tilew; + srcrect.h = src_tileh; + dstrect.x = s * dst_tilew + (src_tilew / 2); + dstrect.y = t * dst_tileh + (src_tileh / 2); + log_warning("SRC", srcrect.x, srcrect.y, srcrect.w, srcrect.h); + log_warning("DST", dstrect.x, dstrect.y); + SDL_BlitSurface(src, &srcrect, dst, &dstrect); + + srcrect.x = s * src_tilew; + srcrect.y = t * src_tileh; + srcrect.w = 1; + srcrect.h = src_tileh; + dstrect.y = t * dst_tileh + (src_tileh / 2); + for (int x = s * dst_tilew + (src_tilew / 2) - 1; s * dst_tilew <= x; --x) + { + dstrect.x = x; + SDL_BlitSurface(src, &srcrect, dst, &dstrect); + } + + srcrect.x = (s + 1) * src_tilew - 1; + srcrect.y = t * src_tileh; + srcrect.w = 1; + srcrect.h = src_tileh; + dstrect.y = t * dst_tileh + (src_tileh / 2); + for (int x = (s + 1) * dst_tilew - (src_tilew / 2); x < (s + 1) * dst_tilew; ++x) + { + dstrect.x = x; + SDL_BlitSurface(src, &srcrect, dst, &dstrect); + } + + srcrect.x = s * src_tilew; + srcrect.y = t * src_tileh; + srcrect.w = src_tilew; + srcrect.h = 1; + dstrect.x = s * dst_tilew + (src_tilew / 2); + for (int y = t * dst_tileh + (src_tileh / 2) - 1; t * dst_tileh <= y; --y) + { + dstrect.y = y; + SDL_BlitSurface(src, &srcrect, dst, &dstrect); + } + + srcrect.x = s * src_tilew; + srcrect.y = (t + 1) * src_tileh - 1; + srcrect.w = src_tilew; + srcrect.h = 1; + dstrect.x = s * dst_tilew + (src_tilew / 2); + for (int y = (t + 1) * dst_tileh - (src_tileh / 2); y < (t + 1) * dst_tileh; ++y) + { + dstrect.y = y; + SDL_BlitSurface(src, &srcrect, dst, &dstrect); + } + } + SDL_FreeSurface(src); + + char* pixels = new char[dst->w * dst->pitch]; + SDL_Surface* finaldst = SDL_CreateRGBSurfaceFrom + ( + pixels, + dst->w, + dst->h, + depth_, + dst->pitch, +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0xFF000000 +#else + 0xFF000000, + 0x00FF0000, + 0x0000FF00, + 0x000000FF +#endif + ); + SDL_BlitSurface(dst, 0, finaldst, 0); + + //SDL_SaveBMP(dst, (stlplus::basename_part(path) + ".bmp").c_str()); + SDL_FreeSurface(dst); + + width_ = finaldst->w; + height_ = finaldst->h; + pitch_ = finaldst->pitch; + + SDL_FreeSurface(finaldst); + + delete[] pixels_; + pixels_ = pixels; +} + + +void image::fix_uv(std::vector& p) const +{ + vector2 mid(SCALAR(0.0), SCALAR(0.0)); + for (int i = 0; i < p.size(); ++i) + { + mid[0] += p[i][0]; + mid[1] += p[i][1]; + } + mid[0] /= p.size(); + mid[1] /= p.size(); + mid[0] *= tile_s_; + mid[1] *= tile_t_; + mid[0] = std::floor(mid[0]); + mid[1] = std::floor(mid[1]); + log_warning("midpoint:", mid); + scalar s = mid[0]; + scalar t = mid[1]; + + scalar src_width = width_ >> 1; + scalar src_height = height_ >> 1; + int src_tilew = src_width / tile_s_; + int src_tileh = src_height / tile_t_; + int dst_tilew = width_ / tile_s_; + int dst_tileh = height_ / tile_t_; + + for (int i = 0; i < p.size(); ++i) + { + //scalar s = p[i][0] * src_width; + scalar x = s * dst_tilew + (src_tilew / 2) + (p[i][0] * src_width - s * src_tilew); + p[i][0] = x / width_; + //scalar t = p[i][1] * src_height; + scalar y = t * dst_tileh + (src_tileh / 2) + (p[i][1] * src_height - t * src_tileh);; + p[i][1] = y / height_; + } +} + + void image::set_as_icon() const { backend backend; @@ -202,11 +520,9 @@ void image::set_as_icon() const ); 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 @@ -227,10 +543,9 @@ bool image::tile_coordinates(int index, scalar coords[8]) const return true; } - void image::bind() const { - ASSERT(video::current() && "should have a video context set"); + ASSERT(video::ready() && "should have a video context set"); if (object_ == 0) { @@ -245,31 +560,26 @@ void image::bind() const void image::reset_binding() { - ASSERT(video::current() && "should have a video context set"); + ASSERT(video::ready() && "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; - } + if (object_) return; // already loaded glGenTextures(1, (GLuint*)&object_); glBindTexture(GL_TEXTURE_2D, (GLuint)object_); GLuint mode; if (channels_ == 3) mode = GL_RGB; - else mode = GL_RGBA; + else mode = GL_RGBA; glTexImage2D //gluBuild2DMipmaps @@ -299,11 +609,7 @@ void image::unload_from_gl() const { if (object_) { - if (object_ == global_object_) - { - global_object_ = 0; - } - + if (object_ == global_object_) global_object_ = 0; glDeleteTextures(1, (GLuint*)&object_); object_ = 0; } @@ -328,40 +634,7 @@ void image::set_properties() const } -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(min_filter_, "min_filter"); - globals.get(mag_filter_, "mag_filter"); - globals.get(tile_s_, "tile_s"); - globals.get(tile_t_, "tile_t"); - globals.get(wrap_s_, "wrap_s"); - globals.get(wrap_t_, "wrap_t"); - } -} +unsigned image::global_object_ = 0; } // namespace moof