]> Dogcows Code - chaz/yoink/blobdiff - src/texture.cc
new classes; yajl library
[chaz/yoink] / src / texture.cc
diff --git a/src/texture.cc b/src/texture.cc
new file mode 100644 (file)
index 0000000..b91d23a
--- /dev/null
@@ -0,0 +1,308 @@
+
+/*******************************************************************************
+
+ 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
+
This page took 0.030437 seconds and 4 git commands to generate.