Commit 1fdf9e2e authored by Robert Menzel's avatar Robert Menzel

better image loading/saving

parent 25d5c229
......@@ -16,6 +16,7 @@
#include <ACGL/Base/Macros.hh>
#include <ACGL/OpenGL/GL.hh>
#include <ACGL/OpenGL/Tools.hh>
#include <ACGL/Math/Math.hh>
namespace ACGL{
......@@ -33,7 +34,8 @@ public:
height(0),
depth(0),
format(GL_RGBA),
type(GL_UNSIGNED_BYTE)
type(GL_UNSIGNED_BYTE),
paddingBytesPerRow(0)
{}
virtual ~TextureData(void)
{
......@@ -50,7 +52,14 @@ public:
GLsizei getDepth (void) const { return depth; }
GLenum getFormat (void) const { return format; }
GLenum getType (void) const { return type; }
GLsizei getPadding (void) const { return paddingBytesPerRow; }
glm::uvec3 getSize (void) const { return glm::uvec3( width, height, depth ); }
//! the byte alignment of each pixel row, e.g. 1,2,4,8 (bytes)
GLsizei getPackAlignment()const;
//! 1,2,3 or 4 based on format
GLsizei getNumberOfChannels() const;
// ========================================================================================================= \/
// ================================================================================================= SETTERS \/
// ========================================================================================================= \/
......@@ -61,6 +70,7 @@ public:
void setDepth (GLsizei _depth) { depth = _depth; }
void setFormat(GLenum _format) { format = _format; }
void setType (GLenum _type) { type = _type; }
void setPadding(GLsizei _padding){ paddingBytesPerRow = _padding; }
void setSize (const glm::uvec3 &_size) { width = _size.x; height = _size.y; depth = _size.z; }
// ========================================================================================================= \/
......@@ -71,8 +81,10 @@ private:
GLsizei width;
GLsizei height;
GLsizei depth;
GLenum format;
GLenum type;
GLenum format; // channel types and count
GLenum type; // data type
GLsizei paddingBytesPerRow; // number of padding bytes added per row: glReadPixel can read with padding and
// some image writers support/need this as well (e.g. QT)
};
ACGL_SMARTPOINTER_TYPEDEFS(TextureData)
......
......@@ -32,7 +32,12 @@ SharedTextureData loadTextureData(const std::string &_filename);
bool saveTextureData(const SharedTextureData &_textureData, const std::string &_filename);
//! saves the viewport visible part of the framebuffer 0 to a file named _prefix_DATE-TIME._fileEnding
bool saveScreenshot( const std::string &_prefix = "screenshot", const std::string _fileEnding = "png" );
bool saveScreenshot( const std::string &_prefix, const std::string _fileEnding);
//! saves the viewport visible part of the framebuffer 0 to a file named screenshot_DATE-TIME._fileEnding
inline bool saveScreenshot( const std::string _fileEnding = "png" ) {
return saveScreenshot( "screenshot", _fileEnding );
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// library specific load
......@@ -65,7 +70,7 @@ bool saveTextureDataToQT( const SharedTextureData &_data, const std::string &_fi
template< typename T>
unsigned char *preProcess( const SharedTextureData &_data)
{
unsigned int channelCount = getNumberOfChannels( _data->getFormat() );
unsigned int channelCount = _data->getNumberOfChannels();
unsigned int pixelCount = _data->getWidth() * _data->getHeight();
T *processedrawdata = new T[pixelCount * channelCount];
......
/***********************************************************************
* Copyright 2011-2013 Computer Graphics Group RWTH Aachen University. *
* All rights reserved. *
* Distributed under the terms of the MIT License (see LICENSE.TXT). *
**********************************************************************/
#pragma once
#include <ACGL/ACGL.hh>
namespace ACGL{
namespace Utils{
//! returns the alignment of the pointer, e.g. 4 if the pointer is 4-byte aligned
//! maximal alignment returned is 64 byte (might get increased in the future), minimum 1
size_t pointerAlignment( const void* _pointer );
}
}
/***********************************************************************
* Copyright 2011-2013 Computer Graphics Group RWTH Aachen University. *
* All rights reserved. *
* Distributed under the terms of the MIT License (see LICENSE.TXT). *
**********************************************************************/
#include <ACGL/OpenGL/Data/TextureData.hh>
#include <ACGL/Utils/Memory.hh>
using namespace ACGL;
using namespace ACGL::OpenGL;
GLsizei TextureData::getPackAlignment()const {
size_t dataAlignment = Utils::pointerAlignment( pData );
size_t rowAlignment = Utils::pointerAlignment( pData+(width*getNumberOfChannels() + paddingBytesPerRow) );
return std::min( dataAlignment, rowAlignment ); //minimum of the data and the begining of the second row
}
GLsizei TextureData::getNumberOfChannels() const
{
return ACGL::OpenGL::getNumberOfChannels( format );
}
......@@ -7,6 +7,7 @@
#include <ACGL/OpenGL/Data/TextureDataLoadStore.hh>
#include <ACGL/OpenGL/Tools.hh>
#include <ACGL/Base/FileHelpers.hh>
#include <ACGL/Utils/Memory.hh>
// for the screenshot function:
#include <ACGL/OpenGL/Objects/FrameBufferObject.hh>
......@@ -19,6 +20,8 @@
#ifdef ACGL_COMPILE_WITH_QT
#include <QImage>
#include <QGLWidget>
#include <QByteArray>
#include <QImageWriter>
#endif
using namespace ACGL;
......@@ -89,8 +92,15 @@ bool saveScreenshot( const std::string &_prefix, const std::string _fileEnding )
char timestring[30];
strftime( timestring, 30, "_%Y-%m-%d_%H-%M-%S", localtime( &rawtime ) );
std::string filename = _prefix + timestring + "." + _fileEnding;
return ACGL::OpenGL::saveTextureData( FrameBufferObject::getImageData(), filename );
static int i = 0;
i++;
std::string filename = _prefix + timestring + "_" + ACGL::Base::StringOperations::toString(i) + "." + _fileEnding;
ACGL::OpenGL::SharedTextureData screenShot = FrameBufferObject::getImageData();
bool success = ACGL::OpenGL::saveTextureData( screenShot, filename );
return success;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
......@@ -205,12 +215,6 @@ SharedTextureData loadTextureDataFromQT(const std::string &_filename)
bool saveTextureDataToPPM(const SharedTextureData& _textureData, const std::string &_filename)
{
if(_textureData->getFormat() != GL_RGB)
{
error() << "saveTextureDataToPPM: Unsupported texture format, must be GL_RGB" << std::endl;
return false;
}
std::ofstream outFileStream(_filename.c_str(), std::ios_base::out | std::ios_base::binary);
if(!outFileStream.good())
......@@ -241,16 +245,24 @@ bool saveTextureDataToPPM(const SharedTextureData& _textureData, const std::stri
// Single whitespace as a delimiter
outFileStream << "\n";
GLsizei channelCount = _textureData->getNumberOfChannels();
// Now, write the image data in binary format
for(GLsizei y = 0; y < _textureData->getHeight(); ++y)
{
for(GLsizei x = 0; x < _textureData->getWidth(); ++x)
{
GLsizei i = ((_textureData->getHeight() - y - 1) * _textureData->getWidth()) + x;
GLsizei row = _textureData->getHeight() - y - 1; // from bottom to the top to mirror the image
GLsizei i = channelCount * (row * _textureData->getWidth() + x) + (row * _textureData->getPadding());
outFileStream.put(_textureData->getData()[(3*i + 0) * getGLTypeSize(_textureData->getType())]);
outFileStream.put(_textureData->getData()[(3*i + 1) * getGLTypeSize(_textureData->getType())]);
outFileStream.put(_textureData->getData()[(3*i + 2) * getGLTypeSize(_textureData->getType())]);
unsigned char r = _textureData->getData()[(i + 0)];
unsigned char g = (channelCount > 1)? _textureData->getData()[(i + 1)] : 0;
unsigned char b = (channelCount > 2)? _textureData->getData()[(i + 2)] : 0;
outFileStream.put( r );
outFileStream.put( g );
outFileStream.put( b );
// ignore alpha if there is an alpha
}
}
......@@ -260,8 +272,12 @@ bool saveTextureDataToPPM(const SharedTextureData& _textureData, const std::stri
bool saveTextureDataToLodepng( const SharedTextureData &_data, const std::string &_filename )
{
if (_data->getPadding() != 0) {
error() << "png saving via lodepng does not support padding per row" << std::endl;
return false;
}
GLenum channelDataType = _data->getType();
unsigned int channelCount = getNumberOfChannels( _data->getFormat() );
unsigned int channelCount = _data->getNumberOfChannels();
int channelBitCount = 0;
if (channelDataType == GL_BYTE || channelDataType == GL_UNSIGNED_BYTE) channelBitCount = 8;
......@@ -318,8 +334,28 @@ bool saveTextureDataToLodepng( const SharedTextureData &_data, const std::string
#ifdef ACGL_COMPILE_WITH_QT
bool saveTextureDataToQT( const SharedTextureData &_data, const std::string &_filename )
{
// TODO implement me
return false;
QImage::Format format = QImage::Format_ARGB32;
if ( pointerAlignment( (void*)_data->getData() ) < 4 ) {
error() << "image data is not 32bit aligned, QT will not be able to handle that" << std::endl;
return false;
}
GLenum glFormat = _data->getFormat();
if (glFormat == GL_RGB) {
format = QImage::Format_RGB888;
if (_data->getPackAlignment() < 4 ) {
error() << "scan lines of image data are not 32bit aligned, QT will not be able to handle that" << std::endl;
return false;
}
} else if (glFormat == GL_RGBA) {
format = QImage::Format_ARGB32;
} else {
error() << "unsupported OpenGL format" << std::endl;
}
QImage image = QImage( _data->getData(), _data->getWidth(), _data->getHeight(), format );
return image.save( QString( _filename.c_str() ) );
}
#endif
......
......@@ -288,7 +288,16 @@ SharedLocationMappings FrameBufferObject::getAttachmentLocations() const
SharedTextureData FrameBufferObject::getImageData(GLsizei _width, GLsizei _height, GLint _x, GLint _y, GLenum _readBuffer)
{
GLubyte* frameBufferData = new GLubyte[_width * _height * 3];
GLenum format = GL_RGB;
//
// glReadPixels aligns each pixel row for 4 bytes which can create gaps in each row
// in case of 1-3 channel data (and one byte per channel)
//
size_t packingAlignment = 4; // can be 1,2,4,8
size_t bytesOfDataPerRow = _width*getNumberOfChannels(format);
size_t paddingPerRow = ( packingAlignment - (bytesOfDataPerRow % packingAlignment) ) % packingAlignment;
GLubyte* frameBufferData = new GLubyte[_height * (bytesOfDataPerRow + paddingPerRow) ];
glBindFramebuffer(GL_FRAMEBUFFER, 0);
......@@ -297,14 +306,16 @@ SharedTextureData FrameBufferObject::getImageData(GLsizei _width, GLsizei _heigh
glReadBuffer(_readBuffer);
#endif
glReadPixels(_x, _y, _width, _height, GL_RGB, GL_UNSIGNED_BYTE, frameBufferData);
glPixelStorei( GL_PACK_ALIGNMENT, (GLint)packingAlignment ); // 4 is the default
glReadPixels(_x, _y, _width, _height, format, GL_UNSIGNED_BYTE, frameBufferData);
SharedTextureData texData = SharedTextureData(new TextureData());
texData->setWidth(_width);
texData->setHeight(_height);
texData->setFormat(GL_RGB);
texData->setFormat(format);
texData->setType(GL_UNSIGNED_BYTE);
texData->setData(frameBufferData); // frameBufferData memory will be managed by texData
texData->setPadding(paddingPerRow);
openGLRareError();
......
/***********************************************************************
* Copyright 2011-2012 Computer Graphics Group RWTH Aachen University. *
* All rights reserved. *
* Distributed under the terms of the MIT License (see LICENSE.TXT). *
**********************************************************************/
#include <ACGL/Utils/Memory.hh>
size_t ACGL::Utils::pointerAlignment( const void* _pointer ) {
size_t address = (size_t) _pointer;
for (int i = 6; i > 0; --i) {
size_t alignment = 2<<i;
if (address % alignment == 0) return alignment;
}
return 1;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment