aboutsummaryrefslogtreecommitdiff
path: root/src/image-reader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/image-reader.cpp')
-rw-r--r--src/image-reader.cpp386
1 files changed, 386 insertions, 0 deletions
diff --git a/src/image-reader.cpp b/src/image-reader.cpp
new file mode 100644
index 0000000..58407bc
--- /dev/null
+++ b/src/image-reader.cpp
@@ -0,0 +1,386 @@
+/*
+ * Copyright © 2012 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:
+ * Alexandros Frantzis
+ */
+#include <cstdio>
+#include <png.h>
+#include <jpeglib.h>
+#include <memory>
+
+#include "image-reader.h"
+#include "log.h"
+#include "util.h"
+
+/*******
+ * PNG *
+ *******/
+
+struct PNGReaderPrivate
+{
+ PNGReaderPrivate() :
+ png(0), info(0), rows(0), png_error(0),
+ current_row(0), row_stride(0) {}
+
+ 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;
+ bool png_error;
+ unsigned int current_row;
+ unsigned int row_stride;
+};
+
+PNGReader::PNGReader(const std::string& filename):
+ priv_(new PNGReaderPrivate())
+{
+ priv_->png_error = !init(filename);
+}
+
+PNGReader::~PNGReader()
+{
+ finish();
+ delete priv_;
+}
+
+bool
+PNGReader::error()
+{
+ return priv_->png_error;
+}
+
+bool
+PNGReader::nextRow(unsigned char *dst)
+{
+ bool ret;
+
+ if (priv_->current_row < height()) {
+ memcpy(dst, priv_->rows[priv_->current_row], priv_->row_stride);
+ priv_->current_row++;
+ ret = true;
+ }
+ else {
+ ret = false;
+ }
+
+ return ret;
+}
+
+unsigned int
+PNGReader::width() const
+{
+ return png_get_image_width(priv_->png, priv_->info);
+}
+
+unsigned int
+PNGReader::height() const
+{
+ return png_get_image_height(priv_->png, priv_->info);
+}
+
+unsigned int
+PNGReader::pixelBytes() const
+{
+ if (png_get_color_type(priv_->png, priv_->info) == PNG_COLOR_TYPE_RGB)
+ {
+ return 3;
+ }
+ return 4;
+}
+
+
+bool
+PNGReader::init(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 */
+ priv_->png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+ if (!priv_->png) {
+ Log::error("Couldn't create libpng read struct\n");
+ return false;
+ }
+
+ priv_->info = png_create_info_struct(priv_->png);
+ if (!priv_->info) {
+ Log::error("Couldn't create libpng info struct\n");
+ return false;
+ }
+
+ /* Set up libpng error handling */
+ if (setjmp(png_jmpbuf(priv_->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(priv_->png, reinterpret_cast<voidp>(is_ptr.get()),
+ PNGReaderPrivate::png_read_fn);
+
+ png_read_png(priv_->png, priv_->info, png_transforms, 0);
+
+ priv_->rows = png_get_rows(priv_->png, priv_->info);
+
+ priv_->current_row = 0;
+ priv_->row_stride = width() * pixelBytes();
+
+ return true;
+}
+
+void
+PNGReader::finish()
+{
+ if (priv_->png)
+ {
+ png_destroy_read_struct(&priv_->png, &priv_->info, 0);
+ }
+}
+
+
+/********
+ * JPEG *
+ ********/
+
+struct JPEGErrorMgr
+{
+ struct jpeg_error_mgr pub;
+ jmp_buf jmp_buffer;
+
+ JPEGErrorMgr()
+ {
+ jpeg_std_error(&pub);
+ pub.error_exit = error_exit;
+ }
+
+ static void error_exit(j_common_ptr cinfo)
+ {
+ JPEGErrorMgr *err =
+ reinterpret_cast<JPEGErrorMgr *>(cinfo->err);
+
+ char buffer[JMSG_LENGTH_MAX];
+
+ /* Create the message */
+ (*cinfo->err->format_message)(cinfo, buffer);
+ std::string msg(std::string(buffer) + "\n");
+ Log::error(msg.c_str());
+
+ longjmp(err->jmp_buffer, 1);
+ }
+};
+
+struct JPEGIStreamSourceMgr
+{
+ static const int BUFFER_SIZE = 4096;
+ struct jpeg_source_mgr pub;
+ std::istream *is;
+ JOCTET buffer[BUFFER_SIZE];
+
+ JPEGIStreamSourceMgr(const std::string& filename) : is(0)
+ {
+ is = Util::get_resource(filename);
+
+ /* Fill in jpeg_source_mgr pub struct */
+ pub.init_source = init_source;
+ pub.fill_input_buffer = fill_input_buffer;
+ pub.skip_input_data = skip_input_data;
+ pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
+ pub.term_source = term_source;
+ pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
+ pub.next_input_byte = NULL; /* until buffer loaded */
+ }
+
+ ~JPEGIStreamSourceMgr()
+ {
+ delete is;
+ }
+
+ bool error()
+ {
+ return !is || (is->fail() && !is->eof());
+ }
+
+ static void init_source(j_decompress_ptr cinfo)
+ {
+ static_cast<void>(cinfo);
+ }
+
+ static boolean fill_input_buffer(j_decompress_ptr cinfo)
+ {
+ JPEGIStreamSourceMgr *src =
+ reinterpret_cast<JPEGIStreamSourceMgr *>(cinfo->src);
+
+ src->is->read(reinterpret_cast<char *>(src->buffer), BUFFER_SIZE);
+
+ src->pub.next_input_byte = src->buffer;
+ src->pub.bytes_in_buffer = src->is->gcount();
+
+ /*
+ * If the decoder needs more data, but we have no more bytes left to
+ * read mark the end of input.
+ */
+ if (src->pub.bytes_in_buffer == 0) {
+ src->pub.bytes_in_buffer = 2;
+ src->buffer[0] = 0xFF;
+ src->buffer[0] = JPEG_EOI;
+ }
+
+ return TRUE;
+ }
+
+ static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
+ {
+ JPEGIStreamSourceMgr *src =
+ reinterpret_cast<JPEGIStreamSourceMgr *>(cinfo->src);
+
+ if (num_bytes > 0) {
+ size_t n = static_cast<size_t>(num_bytes);
+ while (n > src->pub.bytes_in_buffer) {
+ n -= src->pub.bytes_in_buffer;
+ (*src->fill_input_buffer)(cinfo);
+ }
+ src->pub.next_input_byte += n;
+ src->pub.bytes_in_buffer -= n;
+ }
+ }
+
+ static void term_source(j_decompress_ptr cinfo)
+ {
+ static_cast<void>(cinfo);
+ }
+};
+
+struct JPEGReaderPrivate
+{
+ JPEGReaderPrivate(const std::string& filename) :
+ source_mgr(filename), jpeg_error(false) {}
+
+ struct jpeg_decompress_struct cinfo;
+ JPEGErrorMgr error_mgr;
+ JPEGIStreamSourceMgr source_mgr;
+ bool jpeg_error;
+};
+
+
+JPEGReader::JPEGReader(const std::string& filename) :
+ priv_(new JPEGReaderPrivate(filename))
+{
+ priv_->jpeg_error = !init(filename);
+}
+
+JPEGReader::~JPEGReader()
+{
+ finish();
+ delete priv_;
+}
+
+bool
+JPEGReader::error()
+{
+ return priv_->jpeg_error || priv_->source_mgr.error();
+}
+
+bool
+JPEGReader::nextRow(unsigned char *dst)
+{
+ bool ret = true;
+ unsigned char *buffer[1];
+ buffer[0] = dst;
+
+ /* Set up error handling */
+ if (setjmp(priv_->error_mgr.jmp_buffer)) {
+ return false;
+ }
+
+ /* While there are lines left, read next line */
+ if (priv_->cinfo.output_scanline < priv_->cinfo.output_height) {
+ jpeg_read_scanlines(&priv_->cinfo, buffer, 1);
+ }
+ else {
+ jpeg_finish_decompress(&priv_->cinfo);
+ ret = false;
+ }
+
+ return ret;
+}
+
+unsigned int
+JPEGReader::width() const
+{
+ return priv_->cinfo.output_width;
+}
+
+unsigned int
+JPEGReader::height() const
+{
+ return priv_->cinfo.output_height;
+}
+
+unsigned int
+JPEGReader::pixelBytes() const
+{
+ return priv_->cinfo.output_components;
+}
+
+bool
+JPEGReader::init(const std::string& filename)
+{
+ Log::debug("Reading JPEG file %s\n", filename.c_str());
+
+ /* Initialize error manager */
+ priv_->cinfo.err = reinterpret_cast<jpeg_error_mgr*>(&priv_->error_mgr);
+
+ if (setjmp(priv_->error_mgr.jmp_buffer)) {
+ return false;
+ }
+
+ jpeg_create_decompress(&priv_->cinfo);
+ priv_->cinfo.src = reinterpret_cast<jpeg_source_mgr*>(&priv_->source_mgr);
+
+ /* Read header */
+ jpeg_read_header(&priv_->cinfo, TRUE);
+
+ jpeg_start_decompress(&priv_->cinfo);
+
+ return true;
+}
+
+void
+JPEGReader::finish()
+{
+ jpeg_destroy_decompress(&priv_->cinfo);
+}
+
+
+