--- /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 <cstdio> // FILE
+#include <cstring> // strncmp
+
+#include <SDL/SDL.h>
+#include <png.h>
+
+#include "Image.hh"
+#include "Library.hh"
+#include "Log.hh"
+
+
+namespace Mf {
+
+
+class Image::Impl : public Library<Impl>
+{
+public:
+
+ explicit Impl(const std::string& name, bool flipped = false) :
+ Library<Impl>(name),
+ mContext(0),
+ mPixels(0)
+ {
+ init(getName(), flipped);
+ }
+
+ ~Impl()
+ {
+ SDL_FreeSurface(mContext);
+ delete[] mPixels;
+ }
+
+
+ void flip()
+ {
+ unsigned char* pixels = (Uint8*)(mContext->pixels);
+
+ unsigned pitch = mContext->pitch;
+ unsigned char line[pitch];
+
+ int yBegin = 0;
+ int yEnd = mContext->h - 1;
+
+ if (SDL_MUSTLOCK(mContext)) SDL_LockSurface(mContext);
+ 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(mContext)) SDL_UnlockSurface(mContext);
+ }
+
+ void setAsIcon() const
+ {
+ SDL_WM_SetIcon(mContext, 0);
+ }
+
+
+ SDL_Surface* mContext;
+ char* mPixels;
+
+ unsigned mDepth;
+ GLuint mColorMode;
+
+ std::string mComment;
+
+
+private:
+
+ bool init(const std::string& filePath, bool flipped)
+ {
+ logInfo("opening image file...");
+ FILE* fp = fopen(filePath.c_str(), "rb");
+ if (!fp) return false;
+
+ png_byte signature[8];
+ size_t bytesRead;
+
+ png_infop pngInfo = 0;
+ png_infop pngInfoEnd = 0;
+ png_structp pngObj = 0;
+
+ int width;
+ int height;
+ int pitch;
+ int bpp;
+ int channels;
+
+ png_byte colors;
+ png_bytepp rows = 0;
+
+ png_textp texts = 0;
+ int numTexts;
+
+ logInfo("checking signature...");
+ bytesRead = fread(signature, 1, sizeof(signature), fp);
+ logInfo << "reading " << bytesRead << " bytes of signature" << std::endl;
+ if (bytesRead < sizeof(signature) ||
+ png_sig_cmp(signature, 0, sizeof(signature)) != 0) goto cleanup;
+
+ logInfo("creating png structures...");
+ 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;
+
+ logInfo("setting up long jump...");
+ 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);
+ logInfo << "texture bpp: " << bpp << std::endl;
+ 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_gray_1_2_4_to_8(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);
+ mDepth = bpp * channels;
+
+ logInfo << "texture channels: " << channels << std::endl;
+ if (channels == 3) mColorMode = GL_RGB;
+ else mColorMode = GL_RGBA;
+
+ // read comments
+ png_get_text(pngObj, pngInfo, &texts, &numTexts);
+ logInfo << "num texts: " << numTexts << std::endl;
+ for (int i = 0; i < numTexts; ++i)
+ {
+ if (strncmp(texts[i].key, "Comment", 7) == 0)
+ {
+ mComment = texts[i].text;
+ break;
+ }
+ }
+
+ width = png_get_image_width(pngObj, pngInfo);
+ height = png_get_image_height(pngObj, pngInfo);
+
+ pitch = png_get_rowbytes(pngObj, pngInfo);
+ mPixels = new char[width * pitch];
+
+ rows = new png_bytep[height];
+ if (flipped)
+ {
+ for (int i = 0; i < height; ++i)
+ {
+ rows[height - 1 - i] = (png_bytep)(mPixels + i * channels * width);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < height; ++i)
+ {
+ rows[i] = (png_bytep)(mPixels + i * channels * width);
+ }
+ }
+
+ png_read_image(pngObj, rows);
+ png_read_end(pngObj, 0);
+
+ mContext = SDL_CreateRGBSurfaceFrom
+ (
+ mPixels,
+ width,
+ height,
+ bpp * channels,
+ pitch,
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+ 0x000000FF,
+ 0x0000FF00,
+ 0x00FF0000,
+ 0xFF000000
+#else
+ 0xFF000000,
+ 0x00FF0000,
+ 0x0000FF00,
+ 0x000000FF
+#endif
+ );
+
+ cleanup:
+
+ logInfo("cleaning up...");
+ delete[] rows;
+ png_destroy_read_struct(pngObj ? &pngObj : 0,
+ pngInfo ? &pngInfo : 0,
+ pngInfoEnd ? &pngInfoEnd : 0);
+ fclose(fp);
+
+ return mContext;
+ }
+};
+
+
+Image::Image(const std::string& name) :
+ // pass through
+ mImpl(Image::Impl::getInstance(name)) {}
+
+
+bool Image::isValid() const
+{
+ return mImpl->mContext;
+}
+
+int Image::getWidth() const
+{
+ return mImpl->mContext->w;
+}
+
+int Image::getHeight() const
+{
+ return mImpl->mContext->h;
+}
+
+unsigned Image::getDepth() const
+{
+ return mImpl->mDepth;
+}
+
+unsigned Image::getPitch() const
+{
+ return mImpl->mContext->pitch;
+}
+
+GLuint Image::getColorMode() const
+{
+ return mImpl->mColorMode;
+}
+
+std::string Image::getComment() const
+{
+ return mImpl->mComment;
+}
+
+const char* Image::getPixels() const
+{
+ return mImpl->mPixels;
+}
+
+char* Image::getPixels()
+{
+ return mImpl->mPixels;
+}
+
+
+void Image::flip()
+{
+ // pass through
+ mImpl->flip();
+}
+
+void Image::setAsIcon() const
+{
+ // pass through
+ mImpl->setAsIcon();
+}
+
+
+
+std::string Image::getPath(const std::string& name)
+{
+ std::string path = Resource::getPath("images/" + name + ".png");
+ return path;
+}
+
+
+} // namespace Mf
+
+/** vim: set ts=4 sw=4 tw=80: *************************************************/
+