--- /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 <stdexcept>
+#include <cstdlib>
+
+#include <boost/bind.hpp>
+
+#include <SDL/SDL.h>
+#include <SDL/SDL_image.h>
+
+#include "dispatcher.hh"
+#include "opengl.hh"
+
+#include "texture.hh"
+
+
+namespace dc {
+
+
+class texture_impl
+{
+ void unloadFromGL()
+ {
+ if (object)
+ {
+ glDeleteTextures(1, &object);
+ object = 0;
+ }
+ }
+
+ void contextRecreated(const notification& note)
+ {
+ unloadFromGL();
+ uploadToGL();
+ }
+
+public:
+ texture_impl(const std::string& filePath, bool keepInMemory)
+ : object(0), imageData(0), imagePath(filePath), keepData(keepInMemory)
+ {
+ dispatcher::instance().addHandler("video.context_recreated",
+ boost::bind(&texture_impl::contextRecreated, this, _1),
+ this);
+ }
+
+ ~texture_impl()
+ {
+ dispatcher::instance().removeHandler(this);
+
+ if (imageData)
+ {
+ SDL_FreeSurface(imageData);
+ }
+ unloadFromGL();
+ }
+
+
+ static int powerOfTwo(int input)
+ {
+ int value = 1;
+
+ while (value < input)
+ {
+ value <<= 1;
+ }
+ return value;
+ }
+
+ // Adapted from some public domain code. This code is common enough that it
+ // really should be included in SDL_image...
+ static SDL_Surface* prepareImageForGL(SDL_Surface* surface)
+ {
+ /* Use the surface width and height expanded to powers of 2 */
+ int w = powerOfTwo(surface->w);
+ int h = powerOfTwo(surface->h);
+
+ SDL_Surface* image = SDL_CreateRGBSurface
+ (
+ SDL_SWSURFACE,
+ w, h,
+ 32,
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */
+ 0x000000FF,
+ 0x0000FF00,
+ 0x00FF0000,
+ 0xFF000000
+#else
+ 0xFF000000,
+ 0x00FF0000,
+ 0x0000FF00,
+ 0x000000FF
+#endif
+ );
+
+ if (!image)
+ {
+ return 0;
+ }
+
+ // Save the alpha blending attributes.
+ 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;
+ /* Copy the surface into the GL texture image */
+ srcArea.x = 0; destArea.x = 0;
+ /* Copy it in at the bottom, because we're going to flip
+ this image upside-down in a moment
+ */
+ srcArea.y = 0; destArea.y = h - surface->h;
+ srcArea.w = surface->w;
+ srcArea.h = surface->h;
+ SDL_BlitSurface(surface, &srcArea, image, &destArea);
+
+ /* Restore the alpha blending attributes */
+ if (savedFlags & SDL_SRCALPHA)
+ {
+ SDL_SetAlpha(surface, savedFlags, savedAlpha);
+ }
+
+ /* Turn the image upside-down, because OpenGL textures
+ start at the bottom-left, instead of the top-left
+ */
+ Uint8 line[image->pitch];
+
+ /* These two make the following more readable */
+ Uint8 *pixels = static_cast<Uint8*>(image->pixels);
+ Uint16 pitch = image->pitch;
+ int ybegin = 0;
+ int yend = image->h - 1;
+
+ // TODO: consider if this lock is legal/appropriate
+ 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;
+ }
+
+
+ void loadImageData()
+ {
+ SDL_Surface* surface;
+
+ surface = IMG_Load(imagePath.c_str());
+
+ if (!surface)
+ {
+ throw std::runtime_error("could not load image data from file");
+ }
+
+ imageData = prepareImageForGL(surface);
+ SDL_FreeSurface(surface);
+
+ if (!imageData)
+ {
+ throw std::runtime_error("error in preparing image data for GL");
+ }
+
+ if (imageData->format->BytesPerPixel == 3)
+ {
+ mode = GL_RGB;
+ }
+ else if (imageData->format->BytesPerPixel == 4)
+ {
+ mode = GL_RGBA;
+ }
+ else
+ {
+ SDL_FreeSurface(imageData);
+ throw std::runtime_error("image must be 24 or 32 bpp");
+ }
+
+ width = imageData->w;
+ height = imageData->h;
+ }
+
+
+ void uploadToGL()
+ {
+ if (object)
+ {
+ // Already loaded.
+ return;
+ }
+
+ if (!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
+ );
+
+ // These default filters can be changed later...
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ if (!keepData)
+ {
+ SDL_FreeSurface(imageData);
+ imageData = 0;
+ }
+ }
+
+
+ unsigned width;
+ unsigned height;
+ int mode;
+ GLuint object;
+
+ std::string imagePath;
+ bool keepData;
+ SDL_Surface* imageData;
+};
+
+
+texture::texture(const std::string& filePath, bool keepInMemory)
+ : impl(new texture_impl(filePath, keepInMemory)) {}
+
+
+const std::string& texture::filePath()
+{
+ return impl->imagePath;
+}
+
+
+void texture::bind()
+{
+ glBindTexture(GL_TEXTURE_2D, object());
+}
+
+GLuint texture::object()
+{
+ if (!impl->object)
+ {
+ impl->uploadToGL();
+ }
+
+ return impl->object;
+}
+
+
+unsigned texture::width()
+{
+ return impl->width;
+}
+
+unsigned texture::height()
+{
+ return impl->height;
+}
+
+
+
+} // namespace dc
+