blob: 84d775721d4c1e6df6ce1551670e4ee2187e9d18 [file] [log] [blame]
/*
* 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;
}