Added support for a photo archive.
This commit is contained in:
parent
5cc98a420d
commit
0683c1b5a8
@ -70,6 +70,13 @@ std::string ImageReaderWriter::insertReference(const std::string& fileName, unsi
|
||||
|
||||
osg::Image* ImageReaderWriter::readImage_Archive(DataReference& dr, float& s,float& t)
|
||||
{
|
||||
for(PhotoArchiveList::iterator itr=_photoArchiveList.begin();
|
||||
itr!=_photoArchiveList.end();
|
||||
++itr)
|
||||
{
|
||||
osg::Image* image = (*itr)->readImage(dr._fileName,dr._resolutionX,dr._resolutionY,s,t);
|
||||
if (image) return image;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -100,8 +107,6 @@ osg::Image* ImageReaderWriter::readImage_DynamicSampling(DataReference& dr, floa
|
||||
|
||||
osgDB::ReaderWriter::ReadResult ImageReaderWriter::readNode(const std::string& fileName, const Options*)
|
||||
{
|
||||
std::cout<<"Request to read paged image "<<fileName<<std::endl;
|
||||
|
||||
DataReferenceMap::iterator itr = _dataReferences.find(fileName);
|
||||
if (itr==_dataReferences.end()) return ReaderWriter::ReadResult::FILE_NOT_HANDLED;
|
||||
|
||||
@ -110,9 +115,12 @@ osgDB::ReaderWriter::ReadResult ImageReaderWriter::readNode(const std::string& f
|
||||
osg::Image* image = 0;
|
||||
float s=1.0f,t=1.0f;
|
||||
|
||||
if (_photoArchive.valid())
|
||||
// try to load photo from any loaded PhotoArchives
|
||||
if (!_photoArchiveList.empty())
|
||||
image = readImage_Archive(dr,s,t);
|
||||
else
|
||||
|
||||
// not loaded yet, so try to load it directly.
|
||||
if (!image)
|
||||
image = readImage_DynamicSampling(dr,s,t);
|
||||
|
||||
|
||||
|
@ -27,13 +27,10 @@ class ImageReaderWriter : public osgDB::ReaderWriter
|
||||
|
||||
virtual const char* className() { return "ImageReader"; }
|
||||
|
||||
void setPhotoArchive(PhotoArchive* archive) { _photoArchive = archive; }
|
||||
PhotoArchive* getPhotoArchive() { return _photoArchive.get(); }
|
||||
const PhotoArchive* getPhotoArchive() const { return _photoArchive.get(); }
|
||||
void addPhotoArchive(PhotoArchive* archive) { _photoArchiveList.push_back(archive); }
|
||||
|
||||
std::string insertReference(const std::string& fileName, unsigned int res, float width, float height, bool backPage);
|
||||
|
||||
|
||||
virtual ReadResult readNode(const std::string& fileName, const Options*);
|
||||
|
||||
protected:
|
||||
@ -60,10 +57,11 @@ class ImageReaderWriter : public osgDB::ReaderWriter
|
||||
|
||||
osg::Image* readImage_DynamicSampling(DataReference& dr, float& s,float& t);
|
||||
|
||||
typedef std::map<std::string,DataReference> DataReferenceMap;
|
||||
typedef std::map< std::string,DataReference > DataReferenceMap;
|
||||
typedef std::vector< osg::ref_ptr<PhotoArchive> > PhotoArchiveList;
|
||||
|
||||
DataReferenceMap _dataReferences;
|
||||
osg::ref_ptr<PhotoArchive> _photoArchive;
|
||||
PhotoArchiveList _photoArchiveList;
|
||||
|
||||
|
||||
};
|
||||
|
@ -12,3 +12,332 @@
|
||||
|
||||
#include "PhotoArchive.h"
|
||||
|
||||
#include <osg/GLU>
|
||||
#include <osg/Notify>
|
||||
#include <osgDB/ReadFile>
|
||||
#include <Producer/RenderSurface>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
|
||||
|
||||
const std::string FILE_IDENTIFER("osgphotoalbum photo archive");
|
||||
|
||||
class GraphicsContext {
|
||||
public:
|
||||
GraphicsContext()
|
||||
{
|
||||
rs = new Producer::RenderSurface;
|
||||
rs->setWindowRectangle(0,0,1,1);
|
||||
rs->useBorder(false);
|
||||
rs->useConfigEventThread(false);
|
||||
rs->realize();
|
||||
std::cout<<"Realized window"<<std::endl;
|
||||
}
|
||||
|
||||
virtual ~GraphicsContext()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
Producer::ref_ptr<Producer::RenderSurface> rs;
|
||||
};
|
||||
|
||||
PhotoArchive::PhotoArchive(const std::string& filename)
|
||||
{
|
||||
readPhotoIndex(filename);
|
||||
}
|
||||
|
||||
bool PhotoArchive::readPhotoIndex(const std::string& filename)
|
||||
{
|
||||
std::ifstream in(filename.c_str());
|
||||
|
||||
char* fileIndentifier = new char [FILE_IDENTIFER.size()];
|
||||
in.read(fileIndentifier,FILE_IDENTIFER.size());
|
||||
if (FILE_IDENTIFER!=fileIndentifier) return false;
|
||||
|
||||
unsigned int numPhotos;
|
||||
in.read((char*)&numPhotos,sizeof(numPhotos));
|
||||
|
||||
_photoIndex.resize(numPhotos);
|
||||
|
||||
in.read((char*)&_photoIndex.front(),sizeof(PhotoHeader)*numPhotos);
|
||||
|
||||
// success record filename.
|
||||
_archiveFileName = filename;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PhotoArchive::getImageFileNameList(FileNameList& filenameList)
|
||||
{
|
||||
for(PhotoIndexList::const_iterator itr=_photoIndex.begin();
|
||||
itr!=_photoIndex.end();
|
||||
++itr)
|
||||
{
|
||||
filenameList.push_back(std::string(itr->filename));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
osg::Image* PhotoArchive::readImage(const std::string& filename,
|
||||
unsigned int target_s, unsigned target_t,
|
||||
float& original_s, float& original_t)
|
||||
{
|
||||
for(PhotoIndexList::const_iterator itr=_photoIndex.begin();
|
||||
itr!=_photoIndex.end();
|
||||
++itr)
|
||||
{
|
||||
if (filename==itr->filename)
|
||||
{
|
||||
const PhotoHeader& photoHeader = *itr;
|
||||
|
||||
if (target_s <= photoHeader.thumbnail_s &&
|
||||
target_t <= photoHeader.thumbnail_t &&
|
||||
photoHeader.thumbnail_position != 0)
|
||||
{
|
||||
std::ifstream in(_archiveFileName.c_str(),std::ios::in | std::ios::binary);
|
||||
|
||||
// find image
|
||||
in.seekg(photoHeader.thumbnail_position);
|
||||
|
||||
// read image header
|
||||
ImageHeader imageHeader;
|
||||
in.read((char*)&imageHeader,sizeof(ImageHeader));
|
||||
unsigned char* data = new unsigned char[imageHeader.size];
|
||||
in.read((char*)data,imageHeader.size);
|
||||
|
||||
osg::Image* image = new osg::Image;
|
||||
image->setImage(photoHeader.thumbnail_s,photoHeader.thumbnail_t,1,
|
||||
imageHeader.pixelFormat,imageHeader.pixelFormat,imageHeader.type,
|
||||
data,osg::Image::USE_NEW_DELETE);
|
||||
|
||||
original_s = photoHeader.original_s;
|
||||
original_t = photoHeader.original_t;
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
if (photoHeader.fullsize_s &&
|
||||
photoHeader.fullsize_t &&
|
||||
photoHeader.fullsize_position != 0)
|
||||
{
|
||||
std::ifstream in(_archiveFileName.c_str(),std::ios::in | std::ios::binary);
|
||||
|
||||
// find image
|
||||
in.seekg(photoHeader.fullsize_position);
|
||||
|
||||
// read image header
|
||||
ImageHeader imageHeader;
|
||||
in.read((char*)&imageHeader,sizeof(ImageHeader));
|
||||
unsigned char* data = new unsigned char[imageHeader.size];
|
||||
in.read((char*)data,imageHeader.size);
|
||||
|
||||
osg::Image* image = new osg::Image;
|
||||
image->setImage(photoHeader.fullsize_s,photoHeader.fullsize_t,1,
|
||||
imageHeader.pixelFormat,imageHeader.pixelFormat,imageHeader.type,
|
||||
data,osg::Image::USE_NEW_DELETE);
|
||||
|
||||
original_s = photoHeader.original_s;
|
||||
original_t = photoHeader.original_t;
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void PhotoArchive::buildArchive(const std::string& filename, const FileNameList& imageList, unsigned int thumbnailSize, unsigned int maximumSize, bool compressed)
|
||||
{
|
||||
|
||||
PhotoIndexList photoIndex;
|
||||
photoIndex.reserve(imageList.size());
|
||||
for(FileNameList::const_iterator fitr=imageList.begin();
|
||||
fitr!=imageList.end();
|
||||
++fitr)
|
||||
{
|
||||
PhotoHeader header;
|
||||
|
||||
// set name
|
||||
strncpy(header.filename,fitr->c_str(),255);
|
||||
header.filename[255]=0;
|
||||
|
||||
header.thumbnail_s = thumbnailSize;
|
||||
header.thumbnail_t = thumbnailSize;
|
||||
header.thumbnail_position = 0;
|
||||
|
||||
header.fullsize_s = thumbnailSize;
|
||||
header.fullsize_t = thumbnailSize;
|
||||
header.fullsize_position = 0;
|
||||
|
||||
photoIndex.push_back(header);
|
||||
|
||||
}
|
||||
|
||||
std::cout<<"Building photo archive containing "<<photoIndex.size()<<" pictures"<<std::endl;
|
||||
|
||||
// create a graphics context so we can do data operations
|
||||
GraphicsContext context;
|
||||
|
||||
// open up the archive for writing to
|
||||
std::ofstream out(filename.c_str(), std::ios::out | std::ios::binary);
|
||||
|
||||
// write out file indentifier.
|
||||
out.write(FILE_IDENTIFER.c_str(),FILE_IDENTIFER.size());
|
||||
|
||||
unsigned int numPhotos = photoIndex.size();
|
||||
out.write((char*)&numPhotos,sizeof(unsigned int));
|
||||
|
||||
// write the photo index to ensure we can the correct amount of space
|
||||
// available.
|
||||
unsigned int startOfPhotoIndex = out.tellp();
|
||||
out.write((char*)&photoIndex.front(),sizeof(PhotoHeader)*photoIndex.size());
|
||||
|
||||
unsigned int photoCount=1;
|
||||
for(PhotoIndexList::iterator pitr=photoIndex.begin();
|
||||
pitr!=photoIndex.end();
|
||||
++pitr,++photoCount)
|
||||
{
|
||||
PhotoHeader& photoHeader = *pitr;
|
||||
|
||||
|
||||
std::cout<<"Processng image "<<photoCount<<" of "<< photoIndex.size()<<" filename="<< photoHeader.filename << std::endl;
|
||||
std::cout<<" reading image...";std::cout.flush();
|
||||
|
||||
osg::ref_ptr<osg::Image> image = osgDB::readImageFile(photoHeader.filename);
|
||||
|
||||
std::cout<<"done."<< std::endl;
|
||||
|
||||
photoHeader.original_s = image->s();
|
||||
photoHeader.original_t = image->t();
|
||||
|
||||
{
|
||||
|
||||
std::cout<<" creating thumbnail image...";
|
||||
// first need to rescale image to the require thumbnail size
|
||||
unsigned int newTotalSize =
|
||||
image->computeRowWidthInBytes(thumbnailSize,image->getPixelFormat(),image->getDataType(),image->getPacking())*
|
||||
thumbnailSize;
|
||||
|
||||
// need to sort out what size to really use...
|
||||
unsigned char* newData = new unsigned char [newTotalSize];
|
||||
if (!newData)
|
||||
{
|
||||
// should we throw an exception??? Just return for time being.
|
||||
osg::notify(osg::FATAL) << "Error scaleImage() did not succeed : out of memory."<<newTotalSize<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
glPixelStorei(GL_PACK_ALIGNMENT,image->getPacking());
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT,image->getPacking());
|
||||
|
||||
GLint status = gluScaleImage(image->getPixelFormat(),
|
||||
image->s(),
|
||||
image->t(),
|
||||
image->getDataType(),
|
||||
image->data(),
|
||||
thumbnailSize,
|
||||
thumbnailSize,
|
||||
image->getDataType(),
|
||||
newData);
|
||||
|
||||
if (status!=0)
|
||||
{
|
||||
delete [] newData;
|
||||
|
||||
osg::notify(osg::WARN) << "Error scaleImage() did not succeed : errorString = "<<gluErrorString((GLenum)status)<<std::endl;
|
||||
}
|
||||
|
||||
// now set up the photo header.
|
||||
photoHeader.thumbnail_s = thumbnailSize;
|
||||
photoHeader.thumbnail_t = thumbnailSize;
|
||||
photoHeader.thumbnail_position = (unsigned int)out.tellp();
|
||||
|
||||
// set up image header
|
||||
ImageHeader imageHeader;
|
||||
imageHeader.s = thumbnailSize;
|
||||
imageHeader.t = thumbnailSize;
|
||||
imageHeader.internalTextureformat = image->getInternalTextureFormat();
|
||||
imageHeader.pixelFormat = image->getPixelFormat();
|
||||
imageHeader.type = image->getDataType();
|
||||
imageHeader.size = newTotalSize;
|
||||
|
||||
// write out image header and image data.
|
||||
out.write((char*)&imageHeader,sizeof(ImageHeader));
|
||||
out.write((char*)newData,imageHeader.size);
|
||||
|
||||
delete [] newData;
|
||||
|
||||
std::cout<<"done."<< std::endl;
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
std::cout<<" creating fullsize image...";std::cout.flush();
|
||||
|
||||
|
||||
photoHeader.fullsize_s = osg::minimum((unsigned int)image->s(),maximumSize);
|
||||
photoHeader.fullsize_t = osg::minimum((unsigned int)image->t(),maximumSize);
|
||||
photoHeader.fullsize_position = (unsigned int)out.tellp();
|
||||
|
||||
// first need to rescale image to the require thumbnail size
|
||||
unsigned int newTotalSize =
|
||||
image->computeRowWidthInBytes(photoHeader.fullsize_s,image->getPixelFormat(),image->getDataType(),image->getPacking())*
|
||||
photoHeader.fullsize_t;
|
||||
|
||||
// need to sort out what size to really use...
|
||||
unsigned char* newData = new unsigned char [newTotalSize];
|
||||
if (!newData)
|
||||
{
|
||||
// should we throw an exception??? Just return for time being.
|
||||
osg::notify(osg::FATAL) << "Error scaleImage() did not succeed : out of memory."<<newTotalSize<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
glPixelStorei(GL_PACK_ALIGNMENT,image->getPacking());
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT,image->getPacking());
|
||||
|
||||
GLint status = gluScaleImage(image->getPixelFormat(),
|
||||
image->s(),
|
||||
image->t(),
|
||||
image->getDataType(),
|
||||
image->data(),
|
||||
photoHeader.fullsize_s,
|
||||
photoHeader.fullsize_t,
|
||||
image->getDataType(),
|
||||
newData);
|
||||
|
||||
if (status!=0)
|
||||
{
|
||||
delete [] newData;
|
||||
|
||||
osg::notify(osg::WARN) << "Error scaleImage() did not succeed : errorString = "<<gluErrorString((GLenum)status)<<std::endl;
|
||||
}
|
||||
|
||||
ImageHeader imageHeader;
|
||||
imageHeader.s = photoHeader.fullsize_s;
|
||||
imageHeader.t = photoHeader.fullsize_t;
|
||||
imageHeader.internalTextureformat = image->getInternalTextureFormat();
|
||||
imageHeader.pixelFormat = image->getPixelFormat();
|
||||
imageHeader.type = image->getDataType();
|
||||
imageHeader.size = newTotalSize;
|
||||
|
||||
out.write((char*)&imageHeader,sizeof(ImageHeader));
|
||||
out.write((char*)newData,imageHeader.size);
|
||||
//out.write((char*)image->data(),imageHeader.size);
|
||||
|
||||
delete [] newData;
|
||||
|
||||
std::cout<<"done."<< std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// rewrite photo index now it has the correct sizes set
|
||||
out.seekp(startOfPhotoIndex);
|
||||
out.write((char*)&photoIndex.front(),sizeof(PhotoHeader)*photoIndex.size());
|
||||
|
||||
}
|
||||
|
@ -1,3 +1,15 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield
|
||||
*
|
||||
* This application is open source and may be redistributed and/or modified under
|
||||
* the terms of the GNU Public License (GPL) version 1.0 or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* OpenSceneGraph Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef PHOTOARCHIVE_H
|
||||
#define PHOTOARCHIVE_H
|
||||
|
||||
@ -5,6 +17,90 @@
|
||||
|
||||
class PhotoArchive : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
static PhotoArchive* open(const std::string& filename)
|
||||
{
|
||||
osg::ref_ptr<PhotoArchive> archive = new PhotoArchive(filename);
|
||||
if (!archive->empty()) return archive.release();
|
||||
else return 0;
|
||||
}
|
||||
|
||||
typedef std::vector<std::string> FileNameList;
|
||||
|
||||
bool empty() { return _photoIndex.empty(); }
|
||||
|
||||
void getImageFileNameList(FileNameList& filenameList);
|
||||
|
||||
static void buildArchive(const std::string& filename, const FileNameList& imageList, unsigned int thumbnailSize=256, unsigned int maximumSize=1024, bool compressed=true);
|
||||
|
||||
osg::Image* readImage(const std::string& filename,
|
||||
unsigned int target_s, unsigned target_t,
|
||||
float& original_s, float& original_t);
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
PhotoArchive(const std::string& filename);
|
||||
|
||||
virtual ~PhotoArchive() {}
|
||||
|
||||
bool readPhotoIndex(const std::string& filename);
|
||||
|
||||
struct PhotoHeader
|
||||
{
|
||||
PhotoHeader():
|
||||
original_s(0),
|
||||
original_t(0),
|
||||
thumbnail_s(0),
|
||||
thumbnail_t(0),
|
||||
thumbnail_position(0),
|
||||
fullsize_s(0),
|
||||
fullsize_t(0),
|
||||
fullsize_position(0)
|
||||
{
|
||||
filename[0]='\0';
|
||||
}
|
||||
|
||||
char filename[256];
|
||||
unsigned int original_s;
|
||||
unsigned int original_t;
|
||||
|
||||
unsigned int thumbnail_s;
|
||||
unsigned int thumbnail_t;
|
||||
unsigned int thumbnail_position;
|
||||
|
||||
unsigned int fullsize_s;
|
||||
unsigned int fullsize_t;
|
||||
unsigned int fullsize_position;
|
||||
};
|
||||
|
||||
|
||||
struct ImageHeader
|
||||
{
|
||||
ImageHeader():
|
||||
s(0),
|
||||
t(0),
|
||||
internalTextureformat(0),
|
||||
pixelFormat(0),
|
||||
type(0),
|
||||
size(0) {}
|
||||
|
||||
unsigned int s;
|
||||
unsigned int t;
|
||||
GLint internalTextureformat;
|
||||
GLenum pixelFormat;
|
||||
GLenum type;
|
||||
unsigned int size;
|
||||
};
|
||||
|
||||
|
||||
typedef std::vector<PhotoHeader> PhotoIndexList;
|
||||
|
||||
std::string _archiveFileName;
|
||||
PhotoIndexList _photoIndex;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -18,6 +18,8 @@
|
||||
|
||||
#include <osgUtil/Optimizer>
|
||||
|
||||
#include <osgDB/FileNameUtils>
|
||||
|
||||
#include <osgText/Text>
|
||||
|
||||
#include <osgProducer/Viewer>
|
||||
@ -447,7 +449,24 @@ Album::Album(osg::ArgumentParser& arguments, float width, float height)
|
||||
|
||||
for(int pos=1;pos<arguments.argc();++pos)
|
||||
{
|
||||
if (arguments.isString(pos)) fileList.push_back(arguments[pos]);
|
||||
if (arguments.isString(pos))
|
||||
{
|
||||
std::string filename(arguments[pos]);
|
||||
if (osgDB::getLowerCaseFileExtension(filename)=="album")
|
||||
{
|
||||
PhotoArchive* photoArchive = PhotoArchive::open(filename);
|
||||
if (photoArchive)
|
||||
{
|
||||
g_ImageReaderWriter.get()->addPhotoArchive(photoArchive);
|
||||
photoArchive->getImageFileNameList(fileList);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
fileList.push_back(arguments[pos]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_radiusOfRings = 0.02;
|
||||
@ -652,6 +671,7 @@ int main( int argc, char **argv )
|
||||
arguments.getApplicationUsage()->addCommandLineOption("-d <float>","Time delay in sceonds between the display of successive image pairs when in auto advance mode.");
|
||||
arguments.getApplicationUsage()->addCommandLineOption("-a","Enter auto advance of image pairs on start up.");
|
||||
arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
|
||||
arguments.getApplicationUsage()->addCommandLineOption("--create <filename>","Create an photo archive of specified files");
|
||||
|
||||
|
||||
// construct the viewer.
|
||||
@ -683,6 +703,9 @@ int main( int argc, char **argv )
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string archiveName;
|
||||
while (arguments.read("--create",archiveName)) {}
|
||||
|
||||
// any option left unread are converted into errors to write out later.
|
||||
arguments.reportRemainingOptionsAsUnrecognized();
|
||||
|
||||
@ -700,6 +723,20 @@ int main( int argc, char **argv )
|
||||
}
|
||||
|
||||
|
||||
if (!archiveName.empty())
|
||||
{
|
||||
// archive name set to create
|
||||
PhotoArchive::FileNameList fileNameList;
|
||||
for(int i=1;i<arguments.argc();++i)
|
||||
{
|
||||
if (arguments.isString(i)) fileNameList.push_back(std::string(arguments[i]));
|
||||
}
|
||||
|
||||
PhotoArchive::buildArchive(archiveName,fileNameList);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// now the windows have been realized we switch off the cursor to prevent it
|
||||
// distracting the people seeing the stereo images.
|
||||
|
Loading…
Reference in New Issue
Block a user