| /* |
| * Copyright © 2008 Ben Smith |
| * Copyright © 2010-2011 Linaro Limited |
| * |
| * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark. |
| * |
| * glmark2 is free software: you can redistribute it and/or modify it under the |
| * terms of the GNU General Public License as published by the Free Software |
| * Foundation, either version 3 of the License, or (at your option) any later |
| * version. |
| * |
| * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
| * details. |
| * |
| * You should have received a copy of the GNU General Public License along with |
| * glmark2. If not, see <http://www.gnu.org/licenses/>. |
| * |
| * Authors: |
| * Ben Smith (original glmark benchmark) |
| * Alexandros Frantzis (glmark2) |
| */ |
| #include "texture.h" |
| #include "log.h" |
| #include "util.h" |
| |
| #include <cstdarg> |
| #include <png.h> |
| #include <memory> |
| #include <vector> |
| |
| class PNGState |
| { |
| public: |
| PNGState() : |
| png_(0), |
| info_(0), |
| rows_(0) {} |
| ~PNGState() |
| { |
| if (png_) |
| { |
| png_destroy_read_struct(&png_, &info_, 0); |
| } |
| } |
| bool gotData(const std::string& filename) |
| { |
| static const int png_transforms = PNG_TRANSFORM_STRIP_16 | |
| PNG_TRANSFORM_GRAY_TO_RGB | |
| PNG_TRANSFORM_PACKING | |
| PNG_TRANSFORM_EXPAND; |
| |
| Log::debug("Reading PNG file %s\n", filename.c_str()); |
| |
| const std::auto_ptr<std::istream> is_ptr(Util::get_resource(filename)); |
| if (!(*is_ptr)) { |
| Log::error("Cannot open file %s!\n", filename.c_str()); |
| return false; |
| } |
| |
| /* Set up all the libpng structs we need */ |
| png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); |
| if (!png_) { |
| Log::error("Couldn't create libpng read struct\n"); |
| return false; |
| } |
| |
| info_ = png_create_info_struct(png_); |
| if (!info_) { |
| Log::error("Couldn't create libpng info struct\n"); |
| return false; |
| } |
| |
| /* Set up libpng error handling */ |
| if (setjmp(png_jmpbuf(png_))) { |
| Log::error("libpng error while reading file %s\n", filename.c_str()); |
| return false; |
| } |
| |
| /* Read the image information and data */ |
| png_set_read_fn(png_, reinterpret_cast<voidp>(is_ptr.get()), png_read_fn); |
| |
| png_read_png(png_, info_, png_transforms, 0); |
| |
| rows_ = png_get_rows(png_, info_); |
| |
| return true; |
| } |
| unsigned int width() const { return png_get_image_width(png_, info_); } |
| unsigned int height() const { return png_get_image_height(png_, info_); } |
| unsigned int pixelBytes() const |
| { |
| if (png_get_color_type(png_, info_) == PNG_COLOR_TYPE_RGB) |
| { |
| return 3; |
| } |
| return 4; |
| } |
| const unsigned char* row(unsigned int idx) const { return rows_[idx]; } |
| private: |
| static void png_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) |
| { |
| std::istream *is = reinterpret_cast<std::istream*>(png_get_io_ptr(png_ptr)); |
| is->read(reinterpret_cast<char *>(data), length); |
| } |
| png_structp png_; |
| png_infop info_; |
| png_bytepp rows_; |
| }; |
| |
| class ImageData { |
| void resize(unsigned int w, unsigned int h, unsigned int b) |
| { |
| width = w; |
| height = h; |
| bpp = b; |
| delete [] pixels; |
| pixels = new unsigned char[bpp * width * height]; |
| } |
| |
| public: |
| ImageData() : pixels(0), width(0), height(0), bpp(0) {} |
| ~ImageData() { delete [] pixels; } |
| bool load_png(const std::string &filename); |
| |
| unsigned char *pixels; |
| unsigned int width; |
| unsigned int height; |
| unsigned int bpp; |
| }; |
| |
| bool |
| ImageData::load_png(const std::string &filename) |
| { |
| PNGState png; |
| bool ret = png.gotData(filename); |
| if (!ret) |
| { |
| return ret; |
| } |
| |
| resize(png.width(), png.height(), png.pixelBytes()); |
| |
| Log::debug(" Height: %d Width: %d Bpp: %d\n", width, height, bpp); |
| |
| /* |
| * Copy the image data to a contiguous memory area suitable for texture |
| * upload. |
| */ |
| for (unsigned int i = 0; i < height; i++) { |
| memcpy(&pixels[bpp * width * i], |
| png.row(height - i - 1), |
| width * bpp); |
| } |
| |
| return ret; |
| } |
| |
| static void |
| setup_texture(GLuint *tex, ImageData &image, GLint min_filter, GLint mag_filter) |
| { |
| GLenum format = image.bpp == 3 ? GL_RGB : GL_RGBA; |
| |
| glGenTextures(1, tex); |
| glBindTexture(GL_TEXTURE_2D, *tex); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| glTexImage2D(GL_TEXTURE_2D, 0, format, image.width, image.height, 0, |
| format, GL_UNSIGNED_BYTE, image.pixels); |
| |
| if ((min_filter != GL_NEAREST && min_filter != GL_LINEAR) || |
| (mag_filter != GL_NEAREST && mag_filter != GL_LINEAR)) |
| { |
| glGenerateMipmap(GL_TEXTURE_2D); |
| } |
| } |
| |
| namespace TexturePrivate |
| { |
| TextureMap textureMap; |
| } |
| |
| bool |
| Texture::load(const std::string &textureName, GLuint *pTexture, ...) |
| { |
| // Make sure the named texture is in the map. |
| TextureMap::const_iterator textureIt = TexturePrivate::textureMap.find(textureName); |
| if (textureIt == TexturePrivate::textureMap.end()) |
| { |
| return false; |
| } |
| |
| // Pull the pathname out of the descriptor and use it for the PNG load. |
| TextureDescriptor* desc = textureIt->second; |
| const std::string& filename = desc->pathname(); |
| ImageData image; |
| |
| if (!image.load_png(filename)) |
| return false; |
| |
| va_list ap; |
| va_start(ap, pTexture); |
| GLint arg; |
| |
| while ((arg = va_arg(ap, GLint)) != 0) { |
| GLint arg2 = va_arg(ap, GLint); |
| setup_texture(pTexture, image, arg, arg2); |
| pTexture++; |
| } |
| |
| va_end(ap); |
| |
| return true; |
| } |
| |
| const TextureMap& |
| Texture::find_textures() |
| { |
| using std::vector; |
| using std::string; |
| if (!TexturePrivate::textureMap.empty()) |
| { |
| return TexturePrivate::textureMap; |
| } |
| vector<string> pathVec; |
| string dataDir(GLMARK_DATA_PATH"/textures"); |
| Util::list_files(dataDir, pathVec); |
| // Now that we have a list of all of the model files available to us, |
| // let's go through and pull out the names and what format they're in |
| // so the scene can decide which ones to use. |
| for(vector<string>::const_iterator pathIt = pathVec.begin(); |
| pathIt != pathVec.end(); |
| pathIt++) |
| { |
| const string& curPath = *pathIt; |
| string::size_type namePos(0); |
| string::size_type slashPos = curPath.rfind("/"); |
| if (slashPos != string::npos) |
| { |
| // Advance to the first character after the last slash |
| namePos = slashPos + 1; |
| } |
| |
| string::size_type extPos = curPath.rfind(".png"); |
| if (extPos == string::npos) |
| { |
| // We can't trivially determine it's a PNG file so skip it... |
| continue; |
| } |
| |
| string name(curPath, namePos, extPos - namePos); |
| TextureDescriptor* desc = new TextureDescriptor(name, curPath); |
| TexturePrivate::textureMap.insert(std::make_pair(name, desc)); |
| } |
| |
| return TexturePrivate::textureMap; |
| } |