+++ /dev/null
-
-/*******************************************************************************
-
- Copyright (c) 2009, Charles McGarvey
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-*******************************************************************************/
-
-#include <cstdlib>
-
-#include <boost/bind.hpp>
-
-#include <SDL/SDL.h>
-#include <SDL/SDL_image.h>
-
-#include "mippleton.hh"
-
-#include "dispatcher.hh"
-#include "opengl.hh"
-
-#include "texture.hh"
-
-
-namespace dc {
-
-
-/**
- * The texture implementation just contains all the information about the image
- * which is worth having in memory. The image data itself is not worth keeping
- * in memory if the texture has been loaded to GL, but the name of the resource
- * is retained so that it can be reloaded if necessary. The implementation is a
- * mippleton so that multiple texture objects can share the same internal
- * objects and avoid having duplicate textures loaded to GL.
- */
-
-class texture::texture_impl : public mippleton<texture_impl>
-{
-
- /**
- * Delete the texture (if it is loaded) from GL.
- */
-
- void unloadFromGL()
- {
- if (object_)
- {
- glDeleteTextures(1, &object_);
- object_ = 0;
- }
- }
-
- /**
- * If the GL context was recreated, we probably need to reload the texture.
- * This may involve reading it from disk again, but hopefully the OS was
- * smart enough to cache it if the client has plenty of RAM.
- */
-
- void contextRecreated(const notification& note)
- {
- unloadFromGL();
- uploadToGL();
- }
-
- /**
- * This is a helper method used by some of the texture loading code. It
- * returns the first power of two which is greater than the input value.
- */
-
- static int powerOfTwo(int input)
- {
- int value = 1;
-
- while (value < input)
- {
- value <<= 1;
- }
- return value;
- }
-
-public:
-
- /**
- * Construction is initialization.
- */
-
- explicit texture_impl(const std::string& name) :
- mippleton<texture_impl>(name),
- width_(0),
- height_(0),
- mode_(0),
- minFilter_(GL_NEAREST),
- maxFilter_(GL_NEAREST),
- wrapU_(GL_CLAMP),
- wrapV_(GL_CLAMP),
- object_(0)
- {
- uploadToGL();
-
- // we want to know when the GL context is recreated
- dispatcher::instance().addHandler("video.context_recreated",
- boost::bind(&texture_impl::contextRecreated, this, _1), this);
- }
-
- ~texture_impl()
- {
- unloadFromGL();
-
- dispatcher::instance().removeHandler(this);
- }
-
-
- /**
- * Adapted from some public domain code. This stuff is common enough that
- * it really should be included in SDL_image... We need this because images
- * loaded with SDL_image aren't exactly GL-ready right out of the box. This
- * method makes them ready.
- */
-
- static SDL_Surface* prepareImageForGL(SDL_Surface* surface)
- {
- int w = powerOfTwo(surface->w);
- int h = powerOfTwo(surface->h);
-
- // 1. OpenGL images must (generally) have dimensions of a power-of-two.
- // If this one doesn't, we can at least be more friendly by expanding
- // the dimensions so that they are, though there will be some empty
- // space within the range of normal texture coordinates. It's better of
- // textures are the right size to begin with.
-
- SDL_Surface* image = SDL_CreateRGBSurface
- (
- SDL_SWSURFACE,
- w, h,
- 32,
-#if SDL_BYTEORDER == SDL_LIL_ENDIAN
- 0x000000FF,
- 0x0000FF00,
- 0x00FF0000,
- 0xFF000000
-#else
- 0xFF000000,
- 0x00FF0000,
- 0x0000FF00,
- 0x000000FF
-#endif
- );
-
- if (!image)
- {
- return 0;
- }
-
- Uint32 savedFlags = surface->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
- Uint8 savedAlpha = surface->format->alpha;
- if (savedFlags & SDL_SRCALPHA)
- {
- SDL_SetAlpha(surface, 0, 0);
- }
-
- SDL_Rect srcArea, destArea;
- srcArea.x = 0; destArea.x = 0;
- srcArea.y = 0; destArea.y = h - surface->h;
- srcArea.w = surface->w;
- srcArea.h = surface->h;
- SDL_BlitSurface(surface, &srcArea, image, &destArea);
-
- if (savedFlags & SDL_SRCALPHA)
- {
- SDL_SetAlpha(surface, savedFlags, savedAlpha);
- }
-
- // 2. OpenGL textures make more sense within the coordinate system when
- // they are "upside down," so let's flip it.
-
- Uint8 line[image->pitch];
-
- Uint8 *pixels = static_cast<Uint8*>(image->pixels);
- Uint16 pitch = image->pitch;
- int ybegin = 0;
- int yend = image->h - 1;
-
- if (SDL_MUSTLOCK(image)) SDL_LockSurface(image);
- while (ybegin < yend)
- {
- memcpy(line, pixels + pitch * ybegin, pitch);
- memcpy(pixels + pitch * ybegin, pixels + pitch * yend, pitch);
- memcpy(pixels + pitch * yend, line, pitch);
- ybegin++;
- yend--;
- }
- if (SDL_MUSTLOCK(image)) SDL_UnlockSurface(image);
-
- return image;
- }
-
- /**
- * Use SDL_image to load images from file. A surface with the image data is
- * returned.
- * @return Image data.
- */
-
- SDL_Surface* loadImageData()
- {
- SDL_Surface* surface;
-
- surface = IMG_Load(texture::getPathToResource(getName()).c_str());
-
- if (!surface)
- {
- throw texture::exception("loading failed");
- }
-
- SDL_Surface* temp = prepareImageForGL(surface);
- SDL_FreeSurface(surface);
-
- if (!temp)
- {
- throw texture::exception("image couldn't be prepared for GL");
- }
-
- if (temp->format->BytesPerPixel == 3)
- {
- mode_ = GL_RGB;
- }
- else if (temp->format->BytesPerPixel == 4)
- {
- mode_ = GL_RGBA;
- }
- else
- {
- SDL_FreeSurface(temp);
- throw texture::exception("image is not the required 24 or 32 bpp");
- }
-
- width_ = temp->w;
- height_ = temp->h;
-
- return temp;
- }
-
-
- /**
- * Upload the image to GL so that it will be accessible by a much more
- * manageable handle and hopefully reside in video memory.
- */
-
- void uploadToGL()
- {
- if (object_)
- {
- // Already loaded.
- return;
- }
-
- SDL_Surface* imageData = loadImageData();
-
- glGenTextures(1, &object_);
- glBindTexture(GL_TEXTURE_2D, object_);
-
- glTexImage2D
- (
- GL_TEXTURE_2D,
- 0,
- mode_,
- imageData->w,
- imageData->h,
- 0,
- mode_,
- GL_UNSIGNED_BYTE,
- imageData->pixels
- );
-
- setProperties();
-
- SDL_FreeSurface(imageData);
- }
-
-
- /**
- * Sets some texture properties such as the filters and external coordinate
- * behavior.
- */
-
- void setProperties()
- {
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter_);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, maxFilter_);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapU_);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapV_);
- }
-
-
- unsigned width_; ///< Horizontal dimension of the image.
- unsigned height_; ///< Vertical dimension.
-
- GLuint mode_; ///< Depth of the image, GL_RGB or GL_RGBA.
- GLuint minFilter_; ///< Filter.
- GLuint maxFilter_; ///< Filter.
- GLuint wrapU_; ///< Wrapping behavior horizontally.
- GLuint wrapV_; ///< Wrapping behavior vertically.
- GLuint object_; ///< GL texture handle.
-};
-
-
-texture::texture(const std::string& name) :
- // pass through
- impl(texture::texture_impl::retain(name), &texture::texture_impl::release)
-{}
-
-
-/**
- * Bind the GL texture for mapping, etc.
- */
-
-void texture::bind()
-{
- glBindTexture(GL_TEXTURE_2D, getObject());
-}
-
-
-/**
- * Get the texture object, for the curious.
- */
-
-GLuint texture::getObject()
-{
- // pass through
- return impl->object_;
-}
-
-
-unsigned texture::getWidth()
-{
- // pass through
- return impl->width_;
-}
-
-unsigned texture::getHeight()
-{
- // pass through
- return impl->height_;
-}
-
-
-void texture::setMinFilter(GLuint filter)
-{
- impl->minFilter_ = filter;
-}
-
-void texture::setMaxFilter(GLuint filter)
-{
- impl->maxFilter_ = filter;
-}
-
-void texture::setWrapU(GLuint wrap)
-{
- impl->wrapU_ = wrap;
-}
-
-void texture::setWrapV(GLuint wrap)
-{
- impl->wrapV_ = wrap;
-}
-
-
-void texture::applyChanges()
-{
- bind();
- impl->setProperties();
-}
-
-
-std::string texture::getPathToResource(const std::string& name)
-{
- // TODO since this is a generic library class, more than PNG should be
- // supported
- return resource::getPathToResource("textures/" + name + ".png");
-}
-
-
-} // namespace dc
-
-/** vim: set ts=4 sw=4 tw=80: *************************************************/
-