initial commit
This commit is contained in:
+62
-41
@@ -1,52 +1,73 @@
|
||||
# C++ objects and libs
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
# This file is used to ignore files which are generated
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
*~
|
||||
*.autosave
|
||||
*.a
|
||||
*.la
|
||||
*.lai
|
||||
*.core
|
||||
*.moc
|
||||
*.o
|
||||
*.obj
|
||||
*.orig
|
||||
*.rej
|
||||
*.so
|
||||
*.so.*
|
||||
*.dll
|
||||
*.dylib
|
||||
|
||||
# Qt-es
|
||||
object_script.*.Release
|
||||
object_script.*.Debug
|
||||
*_plugin_import.cpp
|
||||
*_pch.h.cpp
|
||||
*_resource.rc
|
||||
*.qm
|
||||
.#*
|
||||
*.*#
|
||||
core
|
||||
!core/
|
||||
tags
|
||||
.DS_Store
|
||||
.directory
|
||||
*.debug
|
||||
Makefile*
|
||||
*.prl
|
||||
*.app
|
||||
moc_*.cpp
|
||||
ui_*.h
|
||||
qrc_*.cpp
|
||||
Thumbs.db
|
||||
*.res
|
||||
*.rc
|
||||
/.qmake.cache
|
||||
/.qmake.stash
|
||||
*.pro.user
|
||||
*.pro.user.*
|
||||
*.qbs.user
|
||||
*.qbs.user.*
|
||||
*.moc
|
||||
moc_*.cpp
|
||||
moc_*.h
|
||||
qrc_*.cpp
|
||||
ui_*.h
|
||||
*.qmlc
|
||||
*.jsc
|
||||
Makefile*
|
||||
*build-*
|
||||
*.qm
|
||||
*.prl
|
||||
|
||||
# Qt unit tests
|
||||
target_wrapper.*
|
||||
# qtcreator generated files
|
||||
*.pro.user*
|
||||
|
||||
# QtCreator
|
||||
*.autosave
|
||||
# xemacs temporary files
|
||||
*.flc
|
||||
|
||||
# QtCreator Qml
|
||||
*.qmlproject.user
|
||||
*.qmlproject.user.*
|
||||
# Vim temporary files
|
||||
.*.swp
|
||||
|
||||
# QtCreator CMake
|
||||
CMakeLists.txt.user*
|
||||
# Visual Studio generated files
|
||||
*.ib_pdb_index
|
||||
*.idb
|
||||
*.ilk
|
||||
*.pdb
|
||||
*.sln
|
||||
*.suo
|
||||
*.vcproj
|
||||
*vcproj.*.*.user
|
||||
*.ncb
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.vcxproj
|
||||
*vcxproj.*
|
||||
|
||||
# QtCreator 4.8< compilation database
|
||||
compile_commands.json
|
||||
# MinGW generated files
|
||||
*.Debug
|
||||
*.Release
|
||||
|
||||
# Python byte code
|
||||
*.pyc
|
||||
|
||||
# Binaries
|
||||
# --------
|
||||
*.dll
|
||||
*.exe
|
||||
|
||||
# QtCreator local machine specific files for imported projects
|
||||
*creator.user*
|
||||
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
#include "calbumroots.h"
|
||||
|
||||
#include <QSqlQuery>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
cAlbumRoots::cAlbumRoots(const qint32& id, QObject* parent) :
|
||||
QObject(parent),
|
||||
m_id(id),
|
||||
m_label(""),
|
||||
m_status(0),
|
||||
m_type(0),
|
||||
m_identifier(""),
|
||||
m_specificPath(""),
|
||||
m_item(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
cAlbumRoots::cAlbumRoots(const qint32& id, const QString& label, const quint32 status, const qint32 type, const QString& identifier, const QString& specificPath, QObject* parent) :
|
||||
QObject(parent),
|
||||
m_id(id),
|
||||
m_label(label),
|
||||
m_status(status),
|
||||
m_type(type),
|
||||
m_identifier(identifier),
|
||||
m_specificPath(specificPath)
|
||||
{
|
||||
}
|
||||
|
||||
qint32 cAlbumRoots::id()
|
||||
{
|
||||
return(m_id);
|
||||
}
|
||||
|
||||
void cAlbumRoots::setLabel(const QString& label)
|
||||
{
|
||||
m_label = label;
|
||||
}
|
||||
|
||||
QString cAlbumRoots::label()
|
||||
{
|
||||
return(m_label);
|
||||
}
|
||||
|
||||
void cAlbumRoots::setStatus(const qint32& status)
|
||||
{
|
||||
m_status = status;
|
||||
}
|
||||
|
||||
qint32 cAlbumRoots::status()
|
||||
{
|
||||
return(m_status);
|
||||
}
|
||||
|
||||
void cAlbumRoots::setType(const qint32& type)
|
||||
{
|
||||
m_type = type;
|
||||
}
|
||||
|
||||
qint32 cAlbumRoots::type()
|
||||
{
|
||||
return(m_type);
|
||||
}
|
||||
|
||||
void cAlbumRoots::setIdentifier(const QString& identifier)
|
||||
{
|
||||
m_identifier = identifier;
|
||||
}
|
||||
|
||||
QString cAlbumRoots::identifier()
|
||||
{
|
||||
return(m_identifier);
|
||||
}
|
||||
|
||||
void cAlbumRoots::setSpecificPath(const QString& specificPath)
|
||||
{
|
||||
m_specificPath = specificPath;
|
||||
}
|
||||
|
||||
QString cAlbumRoots::specificPath()
|
||||
{
|
||||
return(m_specificPath);
|
||||
}
|
||||
|
||||
void cAlbumRoots::setItem(QStandardItem* item)
|
||||
{
|
||||
m_item = item;
|
||||
}
|
||||
|
||||
QStandardItem* cAlbumRoots::item()
|
||||
{
|
||||
return(m_item);
|
||||
}
|
||||
|
||||
cAlbumRootsList::cAlbumRootsList(QSqlDatabase* dbDigikam, QSqlDatabase *dbThumbnail, QObject* parent) :
|
||||
QObject(parent),
|
||||
m_dbDigikam(dbDigikam),
|
||||
m_dbThumbnail(dbThumbnail)
|
||||
{
|
||||
}
|
||||
|
||||
bool cAlbumRootsList::load()
|
||||
{
|
||||
if(!m_dbDigikam)
|
||||
return(false);
|
||||
|
||||
QSqlQuery query(*m_dbDigikam);
|
||||
|
||||
if(!query.exec("SELECT id, "
|
||||
" label, "
|
||||
" status, "
|
||||
" type, "
|
||||
" identifier, "
|
||||
" specificPath "
|
||||
"FROM AlbumRoots "
|
||||
"ORDER BY label;"))
|
||||
{
|
||||
qDebug() << "cAlbumRootList: loading failed.";
|
||||
return(false);
|
||||
}
|
||||
|
||||
while(query.next())
|
||||
add(query.value("id").toInt(), query.value("label").toString(), query.value("status").toInt(), query.value("type").toInt(), query.value("identifier").toString(), query.value("specificPath").toString(), this);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
cAlbumRoots* cAlbumRootsList::add(const qint32& id, QObject* parent)
|
||||
{
|
||||
cAlbumRoots* lpNew = find(id);
|
||||
if(lpNew)
|
||||
return(lpNew);
|
||||
|
||||
lpNew = new cAlbumRoots(id, parent);
|
||||
append(lpNew);
|
||||
return(lpNew);
|
||||
}
|
||||
|
||||
cAlbumRoots* cAlbumRootsList::add(const qint32& id, const QString& label, const quint32 status, const qint32 type, const QString& identifier, const QString& specificPath, QObject* parent)
|
||||
{
|
||||
cAlbumRoots* lpNew = find(id);
|
||||
if(lpNew)
|
||||
return(lpNew);
|
||||
|
||||
lpNew = new cAlbumRoots(id, label, status, type, identifier, specificPath, parent);
|
||||
append(lpNew);
|
||||
return(lpNew);
|
||||
}
|
||||
|
||||
cAlbumRoots* cAlbumRootsList::find(const qint32& id)
|
||||
{
|
||||
for(int i = 0;i < count();i++)
|
||||
{
|
||||
if(at(i)->id() == id)
|
||||
return(at(i));
|
||||
}
|
||||
|
||||
return(nullptr);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef CALBUMROOTS_H
|
||||
#define CALBUMROOTS_H
|
||||
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QStandardItem>
|
||||
|
||||
#include <QSqlDatabase>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <QMetaType>
|
||||
|
||||
|
||||
class cAlbumRoots : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
cAlbumRoots(const qint32& id, QObject* parent = nullptr);
|
||||
cAlbumRoots(const qint32& id, const QString& label, const quint32 status, const qint32 type, const QString& identifier, const QString& specificPath, QObject* parent = nullptr);
|
||||
|
||||
qint32 id();
|
||||
|
||||
void setLabel(const QString& label);
|
||||
QString label();
|
||||
|
||||
void setStatus(const qint32& status);
|
||||
qint32 status();
|
||||
|
||||
void setType(const qint32& type);
|
||||
qint32 type();
|
||||
|
||||
void setIdentifier(const QString& identifier);
|
||||
QString identifier();
|
||||
|
||||
void setSpecificPath(const QString &specificPath);
|
||||
QString specificPath();
|
||||
|
||||
void setItem(QStandardItem* item);
|
||||
QStandardItem* item();
|
||||
|
||||
private:
|
||||
qint32 m_id;
|
||||
QString m_label;
|
||||
quint32 m_status;
|
||||
qint32 m_type;
|
||||
QString m_identifier;
|
||||
QString m_specificPath;
|
||||
QStandardItem* m_item;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(cAlbumRoots*)
|
||||
|
||||
class cAlbumRootsList : public QObject, public QList<cAlbumRoots*>
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
cAlbumRootsList(QSqlDatabase* dbDigikam, QSqlDatabase* dbThumbnail, QObject* parent = nullptr);
|
||||
|
||||
bool load();
|
||||
cAlbumRoots* add(const qint32 &id, QObject* parent = nullptr);
|
||||
cAlbumRoots* add(const qint32& id, const QString& label, const quint32 status, const qint32 type, const QString& identifier, const QString& specificPath, QObject* parent = nullptr);
|
||||
cAlbumRoots* find(const qint32& id);
|
||||
|
||||
private:
|
||||
QSqlDatabase* m_dbDigikam;
|
||||
QSqlDatabase* m_dbThumbnail;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(cAlbumRootsList*)
|
||||
|
||||
#endif // CALBUMROOTS_H
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
#include "calbums.h"
|
||||
|
||||
#include <QSqlQuery>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
cAlbums::cAlbums(const qint32& id, cAlbumsList* albumsList, QSqlDatabase* dbDigikam, QSqlDatabase* dbThumbnail, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_id(id),
|
||||
m_albumRoots(nullptr),
|
||||
m_relativePath(""),
|
||||
m_date(QDate()),
|
||||
m_caption(""),
|
||||
m_collection(""),
|
||||
m_modificationDate(QDateTime()),
|
||||
m_parentAlbums(nullptr),
|
||||
m_imagesList(nullptr),
|
||||
m_albumsList(albumsList),
|
||||
m_dbDigikam(dbDigikam),
|
||||
m_dbThumbnail(dbThumbnail),
|
||||
m_item(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
cAlbums::cAlbums(const qint32& id, cAlbumRoots* albumRoots, const QString& relativePath, const QDate& date, const QString& caption, const QString& collection, const QDateTime& modificationDate, cAlbumsList* albumsList, QSqlDatabase* dbDigikam, QSqlDatabase* dbThumbnail, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_id(id),
|
||||
m_albumRoots(albumRoots),
|
||||
m_relativePath(relativePath),
|
||||
m_date(date),
|
||||
m_caption(caption),
|
||||
m_collection(collection),
|
||||
m_modificationDate(modificationDate),
|
||||
m_parentAlbums(nullptr),
|
||||
m_imagesList(nullptr),
|
||||
m_albumsList(albumsList),
|
||||
m_dbDigikam(dbDigikam),
|
||||
m_dbThumbnail(dbThumbnail),
|
||||
m_item(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
qint32 cAlbums::id()
|
||||
{
|
||||
return(m_id);
|
||||
}
|
||||
|
||||
void cAlbums::setAlbumRoots(cAlbumRoots* albumRoots)
|
||||
{
|
||||
m_albumRoots = albumRoots;
|
||||
}
|
||||
|
||||
cAlbumRoots* cAlbums::albumRoots()
|
||||
{
|
||||
return(m_albumRoots);
|
||||
}
|
||||
|
||||
void cAlbums::setRelativePath(const QString& relativePath)
|
||||
{
|
||||
m_relativePath = relativePath;
|
||||
}
|
||||
|
||||
QString cAlbums::relativePath()
|
||||
{
|
||||
return(m_relativePath);
|
||||
}
|
||||
|
||||
QString cAlbums::path()
|
||||
{
|
||||
QString path = m_relativePath.mid(m_relativePath.lastIndexOf("/")+1);
|
||||
return(path);
|
||||
}
|
||||
|
||||
void cAlbums::setDate(const QDate& date)
|
||||
{
|
||||
m_date = date;
|
||||
}
|
||||
|
||||
QDate cAlbums::date()
|
||||
{
|
||||
return(m_date);
|
||||
}
|
||||
|
||||
void cAlbums::setCaption(const QString& caption)
|
||||
{
|
||||
m_caption = caption;
|
||||
}
|
||||
|
||||
QString cAlbums::caption()
|
||||
{
|
||||
return(m_caption);
|
||||
}
|
||||
|
||||
void cAlbums::setCollection(const QString& collection)
|
||||
{
|
||||
m_collection = collection;
|
||||
}
|
||||
|
||||
QString cAlbums::collection()
|
||||
{
|
||||
return(m_collection);
|
||||
}
|
||||
|
||||
void cAlbums::setModificationDate(const QDateTime modificationDate)
|
||||
{
|
||||
m_modificationDate = modificationDate;
|
||||
}
|
||||
|
||||
QDateTime cAlbums::modificationDate()
|
||||
{
|
||||
return(m_modificationDate);
|
||||
}
|
||||
|
||||
void cAlbums::setParentAlbums(cAlbums *parentAlbums)
|
||||
{
|
||||
m_parentAlbums = parentAlbums;
|
||||
}
|
||||
|
||||
cAlbums* cAlbums::parentAlbums()
|
||||
{
|
||||
return(m_parentAlbums);
|
||||
}
|
||||
|
||||
void cAlbums::loadImages()
|
||||
{
|
||||
if(!m_imagesList)
|
||||
{
|
||||
m_imagesList = new cImagesList(m_dbDigikam, m_dbThumbnail, m_albumsList, this);
|
||||
m_imagesList->load(this);
|
||||
}
|
||||
}
|
||||
|
||||
cImagesList* cAlbums::imagesList()
|
||||
{
|
||||
return(m_imagesList);
|
||||
}
|
||||
|
||||
void cAlbums::setItem(QStandardItem* item)
|
||||
{
|
||||
m_item = item;
|
||||
}
|
||||
|
||||
QStandardItem* cAlbums::item()
|
||||
{
|
||||
return(m_item);
|
||||
}
|
||||
|
||||
cAlbumsList::cAlbumsList(QSqlDatabase* dbDigikam, QSqlDatabase *dbThumbnail, cAlbumRootsList* albumRootsList, QObject* parent) :
|
||||
QObject(parent),
|
||||
m_dbDigikam(dbDigikam),
|
||||
m_dbThumbnail(dbThumbnail),
|
||||
m_albumRootsList(albumRootsList)
|
||||
{
|
||||
}
|
||||
|
||||
bool cAlbumsList::load()
|
||||
{
|
||||
if(!m_dbDigikam)
|
||||
return(false);
|
||||
|
||||
QSqlQuery query(*m_dbDigikam);
|
||||
|
||||
if(!query.exec("SELECT id, "
|
||||
" albumRoot, "
|
||||
" relativePath, "
|
||||
" date, "
|
||||
" caption, "
|
||||
" collection, "
|
||||
" modificationDate "
|
||||
"FROM Albums "
|
||||
"ORDER BY relativePath;"))
|
||||
{
|
||||
qDebug() << "cAlbumsList: loading failed.";
|
||||
return(false);
|
||||
}
|
||||
|
||||
while(query.next())
|
||||
add(query.value("id").toInt(), query.value("albumRoot").toInt(), query.value("relativePath").toString(), query.value("date").toDate(), query.value("caption").toString(), query.value("collection").toString(), query.value("modificationDate").toDateTime(), this);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
cAlbums* cAlbumsList::add(const qint32& id, QObject* parent)
|
||||
{
|
||||
cAlbums* lpNew = find(id);
|
||||
if(lpNew)
|
||||
return(lpNew);
|
||||
|
||||
lpNew = new cAlbums(id, this, m_dbDigikam, m_dbThumbnail, parent);
|
||||
|
||||
append(lpNew);
|
||||
return(lpNew);
|
||||
}
|
||||
|
||||
cAlbums* cAlbumsList::add(const qint32& id, cAlbumRoots* albumRoots, const QString& relativePath, const QDate& date, const QString& caption, const QString& collection, const QDateTime& modificationDate, QObject *parent)
|
||||
{
|
||||
cAlbums* lpNew = find(id);
|
||||
if(lpNew)
|
||||
return(lpNew);
|
||||
|
||||
lpNew = new cAlbums(id, albumRoots, relativePath, date, caption, collection, modificationDate, this, m_dbDigikam, m_dbThumbnail, parent);
|
||||
|
||||
QString parentAlbum = relativePath.left(relativePath.lastIndexOf("/"));
|
||||
if(!parentAlbum.isEmpty())
|
||||
lpNew->setParentAlbums(find(parentAlbum));
|
||||
|
||||
append(lpNew);
|
||||
return(lpNew);
|
||||
}
|
||||
|
||||
cAlbums* cAlbumsList::add(const qint32& id, const qint32& albumRootsID, const QString& relativePath, const QDate& date, const QString& caption, const QString& collection, const QDateTime& modificationDate, QObject *parent)
|
||||
{
|
||||
cAlbums* lpNew = find(id);
|
||||
if(lpNew)
|
||||
return(lpNew);
|
||||
|
||||
cAlbumRoots* albumRoots = m_albumRootsList->find(albumRootsID);
|
||||
lpNew = new cAlbums(id, albumRoots, relativePath, date, caption, collection, modificationDate, this, m_dbDigikam, m_dbThumbnail, parent);
|
||||
|
||||
QString parentAlbum = relativePath.left(relativePath.lastIndexOf("/"));
|
||||
|
||||
if(!parentAlbum.isEmpty())
|
||||
lpNew->setParentAlbums(find(parentAlbum));
|
||||
else if(relativePath.length() > 1)
|
||||
lpNew->setParentAlbums(at(0));
|
||||
|
||||
append(lpNew);
|
||||
return(lpNew);
|
||||
}
|
||||
|
||||
cAlbums* cAlbumsList::find(const qint32& id)
|
||||
{
|
||||
for(int i = 0;i < count();i++)
|
||||
{
|
||||
if(at(i)->id() == id)
|
||||
return(at(i));
|
||||
}
|
||||
return(nullptr);
|
||||
}
|
||||
|
||||
cAlbums* cAlbumsList::find(const QString& relativePath)
|
||||
{
|
||||
for(int i = 0;i < count();i++)
|
||||
{
|
||||
if(at(i)->relativePath() == relativePath)
|
||||
return(at(i));
|
||||
}
|
||||
return(nullptr);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
#ifndef CALBUMS_H
|
||||
#define CALBUMS_H
|
||||
|
||||
|
||||
#include "calbumroots.h"
|
||||
#include "cimages.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QDate>
|
||||
#include <QDateTime>
|
||||
#include <QList>
|
||||
#include <QStandardItem>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <QMetaType>
|
||||
|
||||
|
||||
class cAlbums : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
cAlbums(const qint32& id, cAlbumsList* albumsList, QSqlDatabase* dbDigikam, QSqlDatabase* m_dbThumbnail, QObject *parent = nullptr);
|
||||
cAlbums(const qint32& id, cAlbumRoots* albumRoots, const QString& relativePath, const QDate& date, const QString& caption, const QString& collection, const QDateTime& modificationDate, cAlbumsList* albumsList, QSqlDatabase* dbDigikam, QSqlDatabase* dbThumbnail, QObject *parent = nullptr);
|
||||
|
||||
qint32 id();
|
||||
|
||||
void setAlbumRoots(cAlbumRoots* albumRoots);
|
||||
cAlbumRoots* albumRoots();
|
||||
|
||||
void setRelativePath(const QString& relativePath);
|
||||
QString relativePath();
|
||||
|
||||
QString path();
|
||||
|
||||
void setDate(const QDate& date);
|
||||
QDate date();
|
||||
|
||||
void setCaption(const QString& caption);
|
||||
QString caption();
|
||||
|
||||
void setCollection(const QString& collection);
|
||||
QString collection();
|
||||
|
||||
void setModificationDate(const QDateTime modificationDate);
|
||||
QDateTime modificationDate();
|
||||
|
||||
void setParentAlbums(cAlbums* root);
|
||||
cAlbums* parentAlbums();
|
||||
|
||||
void loadImages();
|
||||
cImagesList* imagesList();
|
||||
|
||||
void setItem(QStandardItem* item);
|
||||
QStandardItem* item();
|
||||
|
||||
private:
|
||||
qint32 m_id;
|
||||
cAlbumRoots* m_albumRoots;
|
||||
QString m_relativePath;
|
||||
QDate m_date;
|
||||
QString m_caption;
|
||||
QString m_collection;
|
||||
QDateTime m_modificationDate;
|
||||
cAlbums* m_parentAlbums;
|
||||
cImagesList* m_imagesList;
|
||||
cAlbumsList* m_albumsList;
|
||||
QSqlDatabase* m_dbDigikam;
|
||||
QSqlDatabase* m_dbThumbnail;
|
||||
QStandardItem* m_item;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(cAlbums*)
|
||||
|
||||
class cAlbumsList : public QObject, public QList<cAlbums*>
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
cAlbumsList(QSqlDatabase* dbDigikam, QSqlDatabase* dbThumbnail, cAlbumRootsList* albumRootsList, QObject* parent = nullptr);
|
||||
|
||||
bool load();
|
||||
cAlbums* add(const qint32& id, QObject* parent = nullptr);
|
||||
cAlbums* add(const qint32& id, cAlbumRoots* albumRoots, const QString& relativePath, const QDate& date, const QString& caption, const QString& collection, const QDateTime& modificationDate, QObject *parent = nullptr);
|
||||
cAlbums* add(const qint32& id, const qint32& albumRootsID, const QString& relativePath, const QDate& date, const QString& caption, const QString& collection, const QDateTime& modificationDate, QObject *parent = nullptr);
|
||||
cAlbums* find(const qint32& id);
|
||||
cAlbums* find(const QString& relativePath);
|
||||
|
||||
private:
|
||||
QSqlDatabase* m_dbDigikam;
|
||||
QSqlDatabase* m_dbThumbnail;
|
||||
cAlbumRootsList* m_albumRootsList;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(cAlbumsList*)
|
||||
|
||||
#endif // CALBUMS_H
|
||||
@@ -0,0 +1,20 @@
|
||||
/*!
|
||||
\file cfoldersortfilterproxymodel.cpp
|
||||
|
||||
*/
|
||||
|
||||
#include "cfoldersortfilterproxymodel.h"
|
||||
|
||||
|
||||
cFolderSortFilterProxyModel::cFolderSortFilterProxyModel(QObject* parent) :
|
||||
QSortFilterProxyModel (parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool cFolderSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||
{
|
||||
QVariant leftData = sourceModel()->data(left);
|
||||
QVariant rightData = sourceModel()->data(right);
|
||||
|
||||
return(leftData.toString() < rightData.toString());
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*!
|
||||
\file cfoldersortfilterproxymodel.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef CFOLDERSORTFILTERPROXYMODEL_H
|
||||
#define CFOLDERSORTFILTERPROXYMODEL_H
|
||||
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\class cFolderSortFilterProxyModel cfoldersortfilterproxymodel.h "cfoldersortfilterproxymodel.h"
|
||||
*/
|
||||
class cFolderSortFilterProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
cFolderSortFilterProxyModel(QObject* parent = nullptr);
|
||||
|
||||
protected:
|
||||
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
||||
};
|
||||
|
||||
#endif // CFOLDERSORTFILTERPROXYMODEL_H
|
||||
+454
@@ -0,0 +1,454 @@
|
||||
/*!
|
||||
\file cimage.cpp
|
||||
|
||||
*/
|
||||
|
||||
#include "cimage.h"
|
||||
|
||||
#include "opencv2/imgproc/types_c.h"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
#include <QTransform>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
|
||||
cImage::cImage() :
|
||||
QImage(),
|
||||
m_isChromatic(true),
|
||||
m_camType(camera_unknown),
|
||||
m_raw(false)
|
||||
{
|
||||
}
|
||||
|
||||
cImage::cImage(const QSize &size, QImage::Format format) :
|
||||
QImage(size, format),
|
||||
m_isChromatic(true),
|
||||
m_camType(camera_unknown),
|
||||
m_raw(false)
|
||||
{
|
||||
}
|
||||
|
||||
cImage::cImage(int width, int height, QImage::Format format) :
|
||||
QImage(width, height, format),
|
||||
m_isChromatic(true),
|
||||
m_camType(camera_unknown),
|
||||
m_raw(false)
|
||||
{
|
||||
}
|
||||
|
||||
cImage::cImage(uchar *data, int width, int height, QImage::Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo) :
|
||||
QImage(data, width, height, format, cleanupFunction, cleanupInfo),
|
||||
m_isChromatic(true),
|
||||
m_camType(camera_unknown),
|
||||
m_raw(false)
|
||||
{
|
||||
}
|
||||
|
||||
cImage::cImage(const uchar *data, int width, int height, QImage::Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo) :
|
||||
QImage(data, width, height, format, cleanupFunction, cleanupInfo),
|
||||
m_isChromatic(true),
|
||||
m_camType(camera_unknown),
|
||||
m_raw(false)
|
||||
{
|
||||
}
|
||||
|
||||
cImage::cImage(uchar *data, int width, int height, int bytesPerLine, QImage::Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo) :
|
||||
QImage(data, width, height, bytesPerLine, format, cleanupFunction, cleanupInfo),
|
||||
m_isChromatic(true),
|
||||
m_camType(camera_unknown),
|
||||
m_raw(false)
|
||||
{
|
||||
}
|
||||
|
||||
cImage::cImage(const uchar *data, int width, int height, int bytesPerLine, QImage::Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo) :
|
||||
QImage(data, width, height, bytesPerLine, format, cleanupFunction, cleanupInfo),
|
||||
m_isChromatic(true),
|
||||
m_camType(camera_unknown),
|
||||
m_raw(false)
|
||||
{
|
||||
}
|
||||
|
||||
cImage::cImage(const QString &fileName, const char *format) :
|
||||
QImage(),
|
||||
m_isChromatic(true),
|
||||
m_camType(camera_unknown),
|
||||
m_raw(false)
|
||||
{
|
||||
load(fileName, format);
|
||||
}
|
||||
|
||||
cImage::cImage(const QImage &image) :
|
||||
QImage(image),
|
||||
m_isChromatic(true),
|
||||
m_camType(camera_unknown),
|
||||
m_raw(false)
|
||||
{
|
||||
}
|
||||
|
||||
cImage::cImage(QImage &&other) :
|
||||
QImage(other),
|
||||
m_isChromatic(true),
|
||||
m_camType(camera_unknown),
|
||||
m_raw(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool cImage::load(const QString &fileName, const char *format)
|
||||
{
|
||||
QFileInfo fileInfo(fileName);
|
||||
|
||||
if(!fileInfo.suffix().compare("NEF", Qt::CaseInsensitive))
|
||||
return(loadRAW(fileName));
|
||||
|
||||
if(fileName.contains(".pgf", Qt::CaseInsensitive))
|
||||
return(loadPGF(fileName));
|
||||
if(QImage::load(fileName, format))
|
||||
return(true);
|
||||
|
||||
return(loadRAW(fileName));
|
||||
}
|
||||
|
||||
bool cImage::openBuffer(const QString &fileName, const QScopedPointer<QByteArray>& ba, LibRaw& iProcessor)
|
||||
{
|
||||
int error = LIBRAW_DATA_ERROR;
|
||||
QFileInfo fi(fileName);
|
||||
|
||||
if(fi.suffix().contains("iiq", Qt::CaseInsensitive) || !ba || ba->isEmpty())
|
||||
error = iProcessor.open_file(fileName.toStdString().c_str());
|
||||
else
|
||||
{
|
||||
if(ba->isEmpty() || ba->size() < 100)
|
||||
return(false);
|
||||
|
||||
error = iProcessor.open_buffer((void*)ba->constData(), static_cast<size_t>(ba->size()));
|
||||
}
|
||||
|
||||
return (error == LIBRAW_SUCCESS);
|
||||
}
|
||||
|
||||
void cImage::detectSpecialCamera(const LibRaw & iProcessor)
|
||||
{
|
||||
if(QString(iProcessor.imgdata.idata.model) == "IQ260 Achromatic")
|
||||
m_isChromatic = false;
|
||||
|
||||
if (QString(iProcessor.imgdata.idata.model).contains("IQ260"))
|
||||
m_camType = camera_iiq;
|
||||
else if (!QString(iProcessor.imgdata.idata.make).compare("Canon", Qt::CaseInsensitive))
|
||||
m_camType = camera_canon;
|
||||
}
|
||||
|
||||
cv::Mat cImage::demosaic(LibRaw & iProcessor)
|
||||
{
|
||||
cv::Mat rawMat = cv::Mat(iProcessor.imgdata.sizes.height, iProcessor.imgdata.sizes.width, CV_16UC1);
|
||||
double dynamicRange = static_cast<double>(iProcessor.imgdata.color.maximum - iProcessor.imgdata.color.black);
|
||||
|
||||
// normalize all image values
|
||||
for(int rIdx = 0; rIdx < rawMat.rows; rIdx++)
|
||||
{
|
||||
unsigned short *ptrRaw = rawMat.ptr<unsigned short>(rIdx);
|
||||
|
||||
for (int cIdx = 0; cIdx < rawMat.cols; cIdx++)
|
||||
{
|
||||
int colIdx = iProcessor.COLOR(rIdx, cIdx);
|
||||
double val = static_cast<double>(iProcessor.imgdata.image[(rawMat.cols*rIdx) + cIdx][colIdx]);
|
||||
|
||||
// normalize the value w.r.t the black point defined
|
||||
val = (val - iProcessor.imgdata.color.black) / dynamicRange;
|
||||
ptrRaw[cIdx] = clip<unsigned short>(val * USHRT_MAX); // for conversion to 16U
|
||||
}
|
||||
}
|
||||
|
||||
// no demosaicing
|
||||
if(m_isChromatic)
|
||||
{
|
||||
unsigned long type = static_cast<unsigned long>(iProcessor.imgdata.idata.filters);
|
||||
type = type & 255;
|
||||
|
||||
cv::Mat rgbImg;
|
||||
|
||||
//define bayer pattern
|
||||
if(type == 180)
|
||||
cvtColor(rawMat, rgbImg, CV_BayerBG2RGB); //bitmask 10 11 01 00 -> 3(G) 2(B) 1(G) 0(R) -> RG RG RG
|
||||
else if(type == 30)
|
||||
cvtColor(rawMat, rgbImg, CV_BayerRG2RGB); //bitmask 00 01 11 10 -> 0 1 3 2
|
||||
else if(type == 225)
|
||||
cvtColor(rawMat, rgbImg, CV_BayerGB2RGB); //bitmask 11 10 00 01
|
||||
else if(type == 75)
|
||||
cvtColor(rawMat, rgbImg, CV_BayerGR2RGB); //bitmask 01 00 10 11
|
||||
else
|
||||
{
|
||||
qWarning() << "Wrong Bayer Pattern (not BG, RG, GB, GR)\n";
|
||||
return cv::Mat();
|
||||
}
|
||||
|
||||
rawMat = rgbImg;
|
||||
}
|
||||
|
||||
// 16U (1 or 3 channeled) Mat
|
||||
return(rawMat);
|
||||
}
|
||||
|
||||
cv::Mat cImage::prepareImg(const LibRaw & iProcessor)
|
||||
{
|
||||
cv::Mat rawMat = cv::Mat(iProcessor.imgdata.sizes.height, iProcessor.imgdata.sizes.width, CV_16UC3, cv::Scalar(0));
|
||||
double dynamicRange = static_cast<double>(iProcessor.imgdata.color.maximum - iProcessor.imgdata.color.black);
|
||||
|
||||
// normalization function
|
||||
auto normalize = [&](double val)
|
||||
{
|
||||
val = (val - iProcessor.imgdata.color.black) / dynamicRange;
|
||||
return clip<unsigned short>(val * USHRT_MAX);
|
||||
};
|
||||
|
||||
for (int rIdx = 0; rIdx < rawMat.rows; rIdx++)
|
||||
{
|
||||
unsigned short *ptrI = rawMat.ptr<unsigned short>(rIdx);
|
||||
|
||||
for (int cIdx = 0; cIdx < rawMat.cols; cIdx++)
|
||||
{
|
||||
|
||||
*ptrI = normalize(iProcessor.imgdata.image[rawMat.cols*rIdx + cIdx][0]);
|
||||
ptrI++;
|
||||
*ptrI = normalize(iProcessor.imgdata.image[rawMat.cols*rIdx + cIdx][1]);
|
||||
ptrI++;
|
||||
*ptrI = normalize(iProcessor.imgdata.image[rawMat.cols*rIdx + cIdx][2]);
|
||||
ptrI++;
|
||||
}
|
||||
}
|
||||
|
||||
return rawMat;
|
||||
}
|
||||
|
||||
void cImage::whiteBalance(const LibRaw & iProcessor, cv::Mat & img)
|
||||
{
|
||||
// white balance must not be empty at this point
|
||||
cv::Mat wb = whiteMultipliers(iProcessor);
|
||||
const float* wbp = wb.ptr<float>();
|
||||
assert(wb.cols == 4);
|
||||
|
||||
for(int rIdx = 0; rIdx < img.rows; rIdx++)
|
||||
{
|
||||
unsigned short *ptr = img.ptr<unsigned short>(rIdx);
|
||||
|
||||
for (int cIdx = 0; cIdx < img.cols; cIdx++)
|
||||
{
|
||||
//apply white balance correction
|
||||
unsigned short r = clip<unsigned short>(*ptr * wbp[0]);
|
||||
unsigned short g = clip<unsigned short>(*(ptr+1) * wbp[1]);
|
||||
unsigned short b = clip<unsigned short>(*(ptr+2) * wbp[2]);
|
||||
|
||||
//apply color correction
|
||||
int cr = qRound(iProcessor.imgdata.color.rgb_cam[0][0] * r +
|
||||
iProcessor.imgdata.color.rgb_cam[0][1] * g +
|
||||
iProcessor.imgdata.color.rgb_cam[0][2] * b);
|
||||
int cg = qRound(iProcessor.imgdata.color.rgb_cam[1][0] * r +
|
||||
iProcessor.imgdata.color.rgb_cam[1][1] * g +
|
||||
iProcessor.imgdata.color.rgb_cam[1][2] * b);
|
||||
int cb = qRound(iProcessor.imgdata.color.rgb_cam[2][0] * r +
|
||||
iProcessor.imgdata.color.rgb_cam[2][1] * g +
|
||||
iProcessor.imgdata.color.rgb_cam[2][2] * b);
|
||||
|
||||
// clip & save color corrected values
|
||||
*ptr = clip<unsigned short>(cr);
|
||||
ptr++;
|
||||
*ptr = clip<unsigned short>(cg);
|
||||
ptr++;
|
||||
*ptr = clip<unsigned short>(cb);
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cv::Mat cImage::whiteMultipliers(const LibRaw & iProcessor)
|
||||
{
|
||||
// get camera white balance multipliers
|
||||
cv::Mat wm(1, 4, CV_32FC1);
|
||||
|
||||
float* wmp = wm.ptr<float>();
|
||||
|
||||
for(int idx = 0; idx < wm.cols; idx++)
|
||||
wmp[idx] = iProcessor.imgdata.color.cam_mul[idx];
|
||||
|
||||
if(wmp[3] == 0.0f)
|
||||
wmp[3] = wmp[1]; // take green (usually its RGBG)
|
||||
|
||||
// normalize white balance multipliers
|
||||
float w = static_cast<float>(cv::sum(wm)[0] / 4.0f);
|
||||
float maxW = 1.0f;
|
||||
|
||||
//clipping according the camera model
|
||||
//if w > 2.0 maxW is 256, otherwise 512
|
||||
//tested empirically
|
||||
//check if it can be defined by some metadata settings?
|
||||
if(w > 2.0f)
|
||||
maxW = 255.0f;
|
||||
if(w > 2.0f && m_camType == camera_canon)
|
||||
maxW = 511.0f; // some cameras would even need ~800 - why?
|
||||
|
||||
//normalize white point
|
||||
wm /= maxW;
|
||||
|
||||
// 1 x 4 32FC1 white balance vector
|
||||
return wm;
|
||||
}
|
||||
|
||||
void cImage::gammaCorrection(const LibRaw & iProcessor, cv::Mat& img)
|
||||
{
|
||||
// white balance must not be empty at this point
|
||||
cv::Mat gt = gammaTable(iProcessor);
|
||||
const unsigned short* gammaLookup = gt.ptr<unsigned short>();
|
||||
assert(gt.cols == USHRT_MAX);
|
||||
|
||||
for(int rIdx = 0; rIdx < img.rows; rIdx++)
|
||||
{
|
||||
unsigned short *ptr = img.ptr<unsigned short>(rIdx);
|
||||
|
||||
for(int cIdx = 0; cIdx < img.cols * img.channels(); cIdx++)
|
||||
{
|
||||
// values close to 0 are treated linear
|
||||
if (ptr[cIdx] <= 5) // 0.018 * 255
|
||||
ptr[cIdx] = static_cast<unsigned short>(qRound(ptr[cIdx] * static_cast<double>(iProcessor.imgdata.params.gamm[1]) / 255.0));
|
||||
else
|
||||
ptr[cIdx] = gammaLookup[ptr[cIdx]];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cv::Mat cImage::gammaTable(const LibRaw & iProcessor)
|
||||
{
|
||||
// OK this is an instance of reverse engineering:
|
||||
// we found out that the values of (at least) the PhaseOne's achromatic back have to be doubled
|
||||
// our images are no close to what their software (Capture One does) - only the gamma correction
|
||||
// seems to be slightly different... -> now we can load compressed IIQs that are not supported by PS : )
|
||||
double cameraHackMlp = (QString(iProcessor.imgdata.idata.model) == "IQ260 Achromatic") ? 2.0 : 1.0;
|
||||
|
||||
//read gamma value and create gamma table
|
||||
double gamma = static_cast<double>(iProcessor.imgdata.params.gamm[0]);
|
||||
|
||||
cv::Mat gmt(1, USHRT_MAX, CV_16UC1);
|
||||
unsigned short* gmtp = gmt.ptr<unsigned short>();
|
||||
|
||||
for (int idx = 0; idx < gmt.cols; idx++) {
|
||||
gmtp[idx] = clip<unsigned short>(qRound((1.099*std::pow(static_cast<double>(idx) / USHRT_MAX, gamma) - 0.099) * 255 * cameraHackMlp));
|
||||
}
|
||||
|
||||
// a 1 x 65535 U16 gamma table
|
||||
return gmt;
|
||||
}
|
||||
|
||||
QImage cImage::raw2Img(const LibRaw & iProcessor, cv::Mat & img)
|
||||
{
|
||||
//check the pixel aspect ratio of the raw image
|
||||
if(iProcessor.imgdata.sizes.pixel_aspect != 1.0f)
|
||||
cv::resize(img, img, cv::Size(), static_cast<double>(iProcessor.imgdata.sizes.pixel_aspect), 1.0f);
|
||||
|
||||
// revert back to 8-bit image
|
||||
img.convertTo(img, CV_8U);
|
||||
|
||||
// TODO: for now - fix this!
|
||||
if(img.channels() == 1)
|
||||
cv::cvtColor(img, img, CV_GRAY2RGB);
|
||||
|
||||
return(mat2QImage(img));
|
||||
}
|
||||
|
||||
QImage cImage::mat2QImage(cv::Mat img)
|
||||
{
|
||||
QImage qImg;
|
||||
|
||||
// since Mat header is copied, a new buffer should be allocated (check this!)
|
||||
if(img.depth() == CV_32F)
|
||||
img.convertTo(img, CV_8U, 255);
|
||||
|
||||
if(img.type() == CV_8UC1)
|
||||
qImg = QImage(img.data, static_cast<int>(img.cols), static_cast<int>(img.rows), static_cast<int>(img.step), QImage::Format_Indexed8); // opencv uses size_t for scaling in x64 applications
|
||||
if(img.type() == CV_8UC3)
|
||||
qImg = QImage(img.data, static_cast<int>(img.cols), static_cast<int>(img.rows), static_cast<int>(img.step), QImage::Format_RGB888);
|
||||
if(img.type() == CV_8UC4)
|
||||
qImg = QImage(img.data, static_cast<int>(img.cols), static_cast<int>(img.rows), static_cast<int>(img.step), QImage::Format_ARGB32);
|
||||
|
||||
qImg = qImg.copy();
|
||||
|
||||
return qImg;
|
||||
}
|
||||
|
||||
bool cImage::loadRAW(const QString &fileName)
|
||||
{
|
||||
QScopedPointer<QByteArray> ba;
|
||||
LibRaw iProcessor;
|
||||
|
||||
if(!openBuffer(fileName, ba, iProcessor))
|
||||
{
|
||||
qDebug() << "could not open buffer";
|
||||
return(false);
|
||||
}
|
||||
|
||||
detectSpecialCamera(iProcessor);
|
||||
|
||||
int error = iProcessor.unpack();
|
||||
|
||||
if(strcmp(iProcessor.version(), "0.13.5") != 0) // fixes a bug specific to libraw 13 - version call is UNTESTED
|
||||
iProcessor.raw2image();
|
||||
|
||||
if(error != LIBRAW_SUCCESS)
|
||||
return(false);
|
||||
|
||||
cv::Mat rawMat;
|
||||
|
||||
if(iProcessor.imgdata.idata.filters)
|
||||
rawMat = demosaic(iProcessor);
|
||||
else
|
||||
rawMat = prepareImg(iProcessor);
|
||||
|
||||
// color correction + white balance
|
||||
if(m_isChromatic)
|
||||
whiteBalance(iProcessor, rawMat);
|
||||
|
||||
gammaCorrection(iProcessor, rawMat);
|
||||
|
||||
// reduce color noise
|
||||
// if (DkSettingsManager::param().resources().filterRawImages && mIsChromatic)
|
||||
// reduceColorNoise(iProcessor, rawMat);
|
||||
|
||||
*this = raw2Img(iProcessor, rawMat);
|
||||
|
||||
iProcessor.recycle();
|
||||
rawMat.release();
|
||||
|
||||
m_raw = true;
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool cImage::loadPGF(const QString &fileName)
|
||||
{
|
||||
QFile file(fileName);
|
||||
|
||||
if(!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
qDebug() << "Cannot open PGF file to read...";
|
||||
return(false);
|
||||
}
|
||||
|
||||
QImage img;
|
||||
QByteArray data(file.size(), '\x00');
|
||||
QDataStream stream(&file);
|
||||
stream.readRawData(data.data(), data.size());
|
||||
|
||||
if (!PGFUtils::readPGFImageData(data, img))
|
||||
{
|
||||
qDebug() << "loadPGFScaled failed...";
|
||||
return(false);
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool cImage::isRaw()
|
||||
{
|
||||
return(m_raw);
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
/*!
|
||||
\file cimage.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef CIMAGE_H
|
||||
#define CIMAGE_H
|
||||
|
||||
|
||||
#include "libraw/libraw.h"
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
#include <QImage>
|
||||
|
||||
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\class cImage cimage.h "cimage.h"
|
||||
*/
|
||||
class cImage : public QImage
|
||||
{
|
||||
public:
|
||||
cImage();
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn cImage
|
||||
\param size
|
||||
\param format
|
||||
*/
|
||||
cImage(const QSize &size, QImage::Format format);
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn cImage
|
||||
\param width
|
||||
\param height
|
||||
\param format
|
||||
*/
|
||||
cImage(int width, int height, QImage::Format format);
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn cImage
|
||||
\param data
|
||||
\param width
|
||||
\param height
|
||||
\param format
|
||||
\param cleanupFunction
|
||||
\param cleanupInfo
|
||||
*/
|
||||
cImage(uchar *data, int width, int height, QImage::Format format, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr);
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn cImage
|
||||
\param data
|
||||
\param width
|
||||
\param height
|
||||
\param format
|
||||
\param cleanupFunction
|
||||
\param cleanupInfo
|
||||
*/
|
||||
cImage(const uchar *data, int width, int height, QImage::Format format, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr);
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn cImage
|
||||
\param data
|
||||
\param width
|
||||
\param height
|
||||
\param bytesPerLine
|
||||
\param format
|
||||
\param cleanupFunction
|
||||
\param cleanupInfo
|
||||
*/
|
||||
cImage(uchar *data, int width, int height, int bytesPerLine, QImage::Format format, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr);
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn cImage
|
||||
\param data
|
||||
\param width
|
||||
\param height
|
||||
\param bytesPerLine
|
||||
\param format
|
||||
\param cleanupFunction
|
||||
\param cleanupInfo
|
||||
*/
|
||||
cImage(const uchar *data, int width, int height, int bytesPerLine, QImage::Format format, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr);
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn cImage
|
||||
\param fileName
|
||||
\param format
|
||||
*/
|
||||
cImage(const QString &fileName, const char *format = nullptr);
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn cImage
|
||||
\param image
|
||||
*/
|
||||
cImage(const QImage &image);
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn cImage
|
||||
\param other
|
||||
*/
|
||||
cImage(QImage &&other);
|
||||
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn load
|
||||
\param fileName
|
||||
\param format
|
||||
\return bool
|
||||
*/
|
||||
bool load(const QString &fileName, const char *format = nullptr);
|
||||
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn isRaw
|
||||
\return bool
|
||||
*/
|
||||
bool isRaw();
|
||||
|
||||
protected:
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\enum Cam
|
||||
*/
|
||||
enum Cam
|
||||
{
|
||||
camera_unknown = 0,
|
||||
camera_iiq,
|
||||
camera_canon,
|
||||
camera_end
|
||||
};
|
||||
|
||||
template <typename num>
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn clip
|
||||
\param val
|
||||
\return num
|
||||
*/
|
||||
num clip(float val) const
|
||||
{
|
||||
int vr = qRound(val);
|
||||
|
||||
// trust me I'm an engineer @ -2
|
||||
// with -2 we do not get pink in oversaturated areas
|
||||
if (vr > std::numeric_limits<num>::max())
|
||||
vr = std::numeric_limits<num>::max()-2;
|
||||
if (vr < 0)
|
||||
vr = 0;
|
||||
|
||||
return static_cast<num>(vr);
|
||||
}
|
||||
|
||||
template <typename num>
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn clip
|
||||
\param val
|
||||
\return num
|
||||
*/
|
||||
num clip(double val) const
|
||||
{
|
||||
int vr = qRound(val);
|
||||
|
||||
// trust me I'm an engineer @ -2
|
||||
// with -2 we do not get pink in oversaturated areas
|
||||
if (vr > std::numeric_limits<num>::max())
|
||||
vr = std::numeric_limits<num>::max()-2;
|
||||
if (vr < 0)
|
||||
vr = 0;
|
||||
|
||||
return static_cast<num>(vr);
|
||||
}
|
||||
|
||||
template <typename num>
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn clip
|
||||
\param val
|
||||
\return num
|
||||
*/
|
||||
num clip(int val) const
|
||||
{
|
||||
int vr = qRound(static_cast<double>(val));
|
||||
|
||||
// trust me I'm an engineer @ -2
|
||||
// with -2 we do not get pink in oversaturated areas
|
||||
if (vr > std::numeric_limits<num>::max())
|
||||
vr = std::numeric_limits<num>::max()-2;
|
||||
if (vr < 0)
|
||||
vr = 0;
|
||||
|
||||
return static_cast<num>(vr);
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_isChromatic; /*!< TODO: describe */
|
||||
Cam m_camType; /*!< TODO: describe */
|
||||
bool m_raw; /*!< TODO: describe */
|
||||
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn loadRAW
|
||||
\param fileName
|
||||
\return bool
|
||||
*/
|
||||
bool loadRAW(const QString &fileName);
|
||||
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn loadPGF
|
||||
\param fileName
|
||||
\return bool
|
||||
*/
|
||||
bool loadPGF(const QString &fileName);
|
||||
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn openBuffer
|
||||
\param fileName
|
||||
\param ba
|
||||
\param iProcessor
|
||||
\return bool
|
||||
*/
|
||||
bool openBuffer(const QString &fileName, const QScopedPointer<QByteArray>& ba, LibRaw& iProcessor);
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn detectSpecialCamera
|
||||
\param iProcessor
|
||||
*/
|
||||
void detectSpecialCamera(const LibRaw & iProcessor);
|
||||
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn demosaic
|
||||
\param iProcessor
|
||||
\return cv::Mat
|
||||
*/
|
||||
cv::Mat demosaic(LibRaw & iProcessor);
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn prepareImg
|
||||
\param iProcessor
|
||||
\return cv::Mat
|
||||
*/
|
||||
cv::Mat prepareImg(const LibRaw & iProcessor);
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn whiteBalance
|
||||
\param iProcessor
|
||||
\param img
|
||||
*/
|
||||
void whiteBalance(const LibRaw & iProcessor, cv::Mat & img);
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn whiteMultipliers
|
||||
\param iProcessor
|
||||
\return cv::Mat
|
||||
*/
|
||||
cv::Mat whiteMultipliers(const LibRaw & iProcessor);
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn gammaCorrection
|
||||
\param iProcessor
|
||||
\param img
|
||||
*/
|
||||
void gammaCorrection(const LibRaw & iProcessor, cv::Mat& img);
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn gammaTable
|
||||
\param iProcessor
|
||||
\return cv::Mat
|
||||
*/
|
||||
cv::Mat gammaTable(const LibRaw & iProcessor);
|
||||
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn raw2Img
|
||||
\param iProcessor
|
||||
\param img
|
||||
\return QImage
|
||||
*/
|
||||
QImage raw2Img(const LibRaw & iProcessor, cv::Mat & img);
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn mat2QImage
|
||||
\param img
|
||||
\return QImage
|
||||
*/
|
||||
QImage mat2QImage(cv::Mat img);
|
||||
};
|
||||
|
||||
#endif // CIMAGE_H
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
#include "cimages.h"
|
||||
#include "calbums.h"
|
||||
|
||||
#include <QSqlQuery>
|
||||
|
||||
|
||||
cImages::cImages(const qint32& id, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_id(id),
|
||||
m_albums(nullptr),
|
||||
m_name(""),
|
||||
m_status(0),
|
||||
m_category(0),
|
||||
m_modificationDate(QDateTime()),
|
||||
m_fileSize(0),
|
||||
m_uniqueHash(""),
|
||||
m_manualOrder(0),
|
||||
m_item(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
cImages::cImages(const qint32& id, cAlbums* albums, const QString& name, const qint8& status, const qint8& category, const QDateTime& modificationDate, const qint32& fileSize, const QString& uniqueHash, const qint32& manualOrder, QSqlDatabase *dbThumbnail, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_id(id),
|
||||
m_albums(albums),
|
||||
m_name(name),
|
||||
m_status(status),
|
||||
m_category(category),
|
||||
m_modificationDate(modificationDate),
|
||||
m_fileSize(fileSize),
|
||||
m_uniqueHash(uniqueHash),
|
||||
m_manualOrder(manualOrder),
|
||||
m_dbThumbnail(dbThumbnail),
|
||||
m_item(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
qint32 cImages::id()
|
||||
{
|
||||
return(m_id);
|
||||
}
|
||||
|
||||
void cImages::setAlbums(cAlbums* albums)
|
||||
{
|
||||
m_albums = albums;
|
||||
}
|
||||
|
||||
cAlbums* cImages::albums()
|
||||
{
|
||||
return(m_albums);
|
||||
}
|
||||
|
||||
void cImages::setName(const QString& name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
QString cImages::name()
|
||||
{
|
||||
return(m_name);
|
||||
}
|
||||
|
||||
void cImages::setStatus(qint8& status)
|
||||
{
|
||||
m_status = status;
|
||||
}
|
||||
|
||||
qint8 cImages::status()
|
||||
{
|
||||
return(m_status);
|
||||
}
|
||||
|
||||
void cImages::setCategory(qint8& category)
|
||||
{
|
||||
m_category = category;
|
||||
}
|
||||
|
||||
qint8 cImages::category()
|
||||
{
|
||||
return(m_category);
|
||||
}
|
||||
|
||||
void cImages::setModificationDate(const QDateTime modificationDate)
|
||||
{
|
||||
m_modificationDate = modificationDate;
|
||||
}
|
||||
|
||||
QDateTime cImages::modificationDate()
|
||||
{
|
||||
return(m_modificationDate);
|
||||
}
|
||||
|
||||
void cImages::setFileSize(qint32& fileSize)
|
||||
{
|
||||
m_fileSize = fileSize;
|
||||
}
|
||||
|
||||
qint32 cImages::fileSize()
|
||||
{
|
||||
return(m_fileSize);
|
||||
}
|
||||
|
||||
void cImages::setUniqueHash(const QString& uniqueHash)
|
||||
{
|
||||
m_uniqueHash = uniqueHash;
|
||||
}
|
||||
|
||||
QString cImages::uniqueHash()
|
||||
{
|
||||
return(m_uniqueHash);
|
||||
}
|
||||
|
||||
void cImages::setManualOrder(qint32& manualOrder)
|
||||
{
|
||||
m_manualOrder = manualOrder;
|
||||
}
|
||||
|
||||
qint32 cImages::manualOrder()
|
||||
{
|
||||
return(m_manualOrder);
|
||||
}
|
||||
|
||||
void cImages::setItem(QStandardItem* item)
|
||||
{
|
||||
m_item = item;
|
||||
}
|
||||
|
||||
QStandardItem* cImages::item()
|
||||
{
|
||||
return(m_item);
|
||||
}
|
||||
|
||||
void cImages::loadThumbnail()
|
||||
{
|
||||
if(!m_dbThumbnail)
|
||||
return;
|
||||
|
||||
QSqlQuery query(*m_dbThumbnail);
|
||||
QString sql = QString("SELECT t.id id, "
|
||||
" t.type type, "
|
||||
" t.modificationDate modificationDate, "
|
||||
" t.orientationHint orientationHint, "
|
||||
" t.data data "
|
||||
"FROM Thumbnails t "
|
||||
"JOIN UniqueHashes h ON (t.id = h.thumbId) "
|
||||
"WHERE h.uniqueHash = '%1';").arg(m_uniqueHash);
|
||||
|
||||
if(!query.exec(sql))
|
||||
{
|
||||
qDebug() << "cImages: Thumbnail Loading failed.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cImagesList::cImagesList(QSqlDatabase* dbDigikam, QSqlDatabase *dbThumbnail, cAlbumsList* albumsList, QObject* parent) :
|
||||
QObject(parent),
|
||||
m_dbDigikam(dbDigikam),
|
||||
m_dbThumbnail(dbThumbnail),
|
||||
m_albumsList(albumsList)
|
||||
{
|
||||
}
|
||||
|
||||
bool cImagesList::load(cAlbums* albums)
|
||||
{
|
||||
if(!m_dbDigikam)
|
||||
return(false);
|
||||
|
||||
QSqlQuery query(*m_dbDigikam);
|
||||
QString sql = QString("SELECT id, "
|
||||
" album, "
|
||||
" name, "
|
||||
" status, "
|
||||
" category, "
|
||||
" modificationDate, "
|
||||
" fileSize, "
|
||||
" uniqueHash, "
|
||||
" manualOrder "
|
||||
"FROM Images "
|
||||
"WHERE album = %1 "
|
||||
"ORDER BY name;").arg(albums->id());
|
||||
if(!query.exec(sql))
|
||||
{
|
||||
qDebug() << "Images: loading failed.";
|
||||
return(false);
|
||||
}
|
||||
|
||||
while(query.next())
|
||||
add(query.value("id").toInt(), albums, query.value("name").toString(), query.value("status").toInt(), query.value("category").toInt(), query.value("modificationDate").toDateTime(), query.value("fileSize").toInt(), query.value("uniqueHash").toString(), query.value("manualOrder").toInt(), this);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool cImagesList::load(const qint32 &albumsID)
|
||||
{
|
||||
if(!m_dbDigikam)
|
||||
return(false);
|
||||
|
||||
QSqlQuery query(*m_dbDigikam);
|
||||
QString sql = QString("SELECT id, "
|
||||
" album, "
|
||||
" name, "
|
||||
" status, "
|
||||
" category, "
|
||||
" modificationDate, "
|
||||
" fileSize, "
|
||||
" uniqueHash, "
|
||||
" manualOrder "
|
||||
"FROM Images "
|
||||
"WHERE album = %1 "
|
||||
"ORDER BY name;").arg(albumsID);
|
||||
if(!query.exec(sql))
|
||||
{
|
||||
qDebug() << "Images: loading failed.";
|
||||
return(false);
|
||||
}
|
||||
|
||||
while(query.next())
|
||||
add(query.value("id").toInt(), query.value("album").toInt(), query.value("name").toString(), query.value("status").toInt(), query.value("category").toInt(), query.value("modificationDate").toDateTime(), query.value("fileSize").toInt(), query.value("uniqueHash").toString(), query.value("manualOrder").toInt(), this);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
cImages* cImagesList::add(const qint32& id, QObject* parent)
|
||||
{
|
||||
cImages* lpNew = find(id);
|
||||
if(lpNew)
|
||||
return(lpNew);
|
||||
|
||||
lpNew = new cImages(id, parent);
|
||||
|
||||
append(lpNew);
|
||||
return(lpNew);
|
||||
}
|
||||
|
||||
cImages* cImagesList::add(const qint32& id, cAlbums* albums, const QString& name, const qint8& status, const qint8& category, const QDateTime& modificationDate, const qint32& fileSize, const QString& uniqueHash, const qint32& manualOrder, QObject *parent)
|
||||
{
|
||||
cImages* lpNew = find(id);
|
||||
if(lpNew)
|
||||
return(lpNew);
|
||||
|
||||
lpNew = new cImages(id, albums, name, status, category, modificationDate, fileSize, uniqueHash, manualOrder, m_dbThumbnail, parent);
|
||||
lpNew->loadThumbnail();
|
||||
|
||||
append(lpNew);
|
||||
return(lpNew);
|
||||
}
|
||||
|
||||
cImages* cImagesList::add(const qint32& id, const qint32& albumsID, const QString& name, const qint8& status, const qint8& category, const QDateTime& modificationDate, const qint32& fileSize, const QString& uniqueHash, const qint32& manualOrder, QObject *parent)
|
||||
{
|
||||
cImages* lpNew = find(id);
|
||||
if(lpNew)
|
||||
return(lpNew);
|
||||
|
||||
cAlbums* albums = m_albumsList->find(albumsID);
|
||||
lpNew = new cImages(id, albums, name, status, category, modificationDate, fileSize, uniqueHash, manualOrder, m_dbThumbnail, parent);
|
||||
lpNew->loadThumbnail();
|
||||
|
||||
append(lpNew);
|
||||
return(lpNew);
|
||||
}
|
||||
|
||||
cImages* cImagesList::find(const qint32& id)
|
||||
{
|
||||
for(int i = 0;i < count();i++)
|
||||
{
|
||||
if(at(i)->id() == id)
|
||||
return(at(i));
|
||||
}
|
||||
return(nullptr);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
#ifndef CIMAGES_H
|
||||
#define CIMAGES_H
|
||||
|
||||
|
||||
#include <QString>
|
||||
#include <QDateTime>
|
||||
#include <QList>
|
||||
#include <QStandardItem>
|
||||
|
||||
#include <QSqlDatabase>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <QMetaType>
|
||||
|
||||
|
||||
class cAlbums;
|
||||
class cAlbumsList;
|
||||
|
||||
class cImages : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
cImages(const qint32& id, QObject *parent = nullptr);
|
||||
cImages(const qint32& id, cAlbums* albums, const QString& name, const qint8& status, const qint8& category, const QDateTime& modificationDate, const qint32& fileSize, const QString& uniqueHash, const qint32& manualOrder, QSqlDatabase* dbThumbnail, QObject *parent = nullptr);
|
||||
|
||||
qint32 id();
|
||||
|
||||
void setAlbums(cAlbums* albums);
|
||||
cAlbums* albums();
|
||||
|
||||
void setName(const QString& name);
|
||||
QString name();
|
||||
|
||||
void setStatus(qint8& status);
|
||||
qint8 status();
|
||||
|
||||
void setCategory(qint8& category);
|
||||
qint8 category();
|
||||
|
||||
void setModificationDate(const QDateTime modificationDate);
|
||||
QDateTime modificationDate();
|
||||
|
||||
void setFileSize(qint32& fileSize);
|
||||
qint32 fileSize();
|
||||
|
||||
void setUniqueHash(const QString& uniqueHash);
|
||||
QString uniqueHash();
|
||||
|
||||
void setManualOrder(qint32& manualOrder);
|
||||
qint32 manualOrder();
|
||||
|
||||
void setItem(QStandardItem* item);
|
||||
QStandardItem* item();
|
||||
|
||||
void loadThumbnail();
|
||||
|
||||
private:
|
||||
qint32 m_id;
|
||||
cAlbums* m_albums;
|
||||
QString m_name;
|
||||
qint8 m_status;
|
||||
qint8 m_category;
|
||||
QDateTime m_modificationDate;
|
||||
qint32 m_fileSize;
|
||||
QString m_uniqueHash;
|
||||
qint32 m_manualOrder;
|
||||
QSqlDatabase* m_dbThumbnail;
|
||||
QStandardItem* m_item;
|
||||
|
||||
signals:
|
||||
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(cImages*)
|
||||
|
||||
class cImagesList : public QObject, public QList<cImages*>
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
cImagesList(QSqlDatabase* dbDigikam, QSqlDatabase* dbThumbnail, cAlbumsList* albumsList, QObject* parent = nullptr);
|
||||
|
||||
bool load(cAlbums* albums);
|
||||
bool load(const qint32& albumsID);
|
||||
cImages* add(const qint32& id, QObject* parent = nullptr);
|
||||
cImages* add(const qint32& id, cAlbums* albums, const QString& name, const qint8& status, const qint8& category, const QDateTime& modificationDate, const qint32& fileSize, const QString& uniqueHash, const qint32& manualOrder, QObject *parent = nullptr);
|
||||
cImages* add(const qint32& id, const qint32& albumsID, const QString& name, const qint8& status, const qint8& category, const QDateTime &modificationDate, const qint32 &fileSize, const QString& uniqueHash, const qint32 &manualOrder, QObject *parent = nullptr);
|
||||
cImages* find(const qint32& id);
|
||||
|
||||
private:
|
||||
QSqlDatabase* m_dbDigikam;
|
||||
QSqlDatabase* m_dbThumbnail;
|
||||
cAlbumsList* m_albumsList;
|
||||
};
|
||||
|
||||
#endif // CIMAGES_H
|
||||
+277
@@ -0,0 +1,277 @@
|
||||
#include "cmainwindow.h"
|
||||
#include "ui_cmainwindow.h"
|
||||
|
||||
#include <QSqlError>
|
||||
|
||||
#include <QSettings>
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
cMainWindow::cMainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::cMainWindow),
|
||||
m_albumRootsList(nullptr),
|
||||
m_folderViewModel(nullptr),
|
||||
m_folderSortFilterProxyModel(nullptr),
|
||||
m_thumbnailViewModel(nullptr),
|
||||
m_thumbnailSortFilterProxyModel(nullptr),
|
||||
m_loading(false)
|
||||
{
|
||||
initUI();
|
||||
loadData();
|
||||
initSignals();
|
||||
}
|
||||
|
||||
cMainWindow::~cMainWindow()
|
||||
{
|
||||
if(m_albumRootsList)
|
||||
delete m_albumRootsList;
|
||||
|
||||
if(m_dbThumbnail.isOpen())
|
||||
m_dbThumbnail.close();
|
||||
|
||||
if(m_dbDigikam.isOpen())
|
||||
m_dbDigikam.close();
|
||||
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void cMainWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
QSettings settings;
|
||||
settings.setValue("main/width", QVariant::fromValue(size().width()));
|
||||
settings.setValue("main/height", QVariant::fromValue(size().height()));
|
||||
settings.setValue("main/x", QVariant::fromValue(x()));
|
||||
settings.setValue("main/y", QVariant::fromValue(y()));
|
||||
if(this->isMaximized())
|
||||
settings.setValue("main/maximized", QVariant::fromValue(true));
|
||||
else
|
||||
settings.setValue("main/maximized", QVariant::fromValue(false));
|
||||
|
||||
QList<qint32> sizes = ui->m_splitter->sizes();
|
||||
|
||||
for(int x = 0;x < sizes.count();x++)
|
||||
settings.setValue(QString("main/splitter%1").arg(x+1), QVariant::fromValue(sizes[x]));
|
||||
|
||||
// m_filterPanel->saveSettings();
|
||||
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void cMainWindow::initUI()
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
m_folderViewModel = new QStandardItemModel;
|
||||
// ui->m_folderView->setModel(m_folderViewModel);
|
||||
m_folderSortFilterProxyModel = new cFolderSortFilterProxyModel(this);
|
||||
ui->m_folderView->setModel(m_folderSortFilterProxyModel);
|
||||
m_folderSortFilterProxyModel->setSourceModel(m_folderViewModel);
|
||||
|
||||
m_thumbnailViewModel = new QStandardItemModel;
|
||||
m_thumbnailSortFilterProxyModel = new cThumbnailSortFilterProxyModel(this);
|
||||
ui->m_thumbnailView->setModel(m_thumbnailSortFilterProxyModel);
|
||||
m_thumbnailSortFilterProxyModel->setSourceModel(m_thumbnailViewModel);
|
||||
|
||||
QSettings settings;
|
||||
|
||||
if(!settings.value("main/maximized").toBool())
|
||||
{
|
||||
qint32 iX = settings.value("main/x", QVariant::fromValue(-1)).toInt();
|
||||
qint32 iY = settings.value("main/y", QVariant::fromValue(-1)).toInt();
|
||||
qint32 iWidth = settings.value("main/width", QVariant::fromValue(-1)).toInt();
|
||||
qint32 iHeight = settings.value("main/height", QVariant::fromValue(-1)).toInt();
|
||||
|
||||
if(iWidth != -1 && iHeight != -1)
|
||||
resize(iWidth, iHeight);
|
||||
if(iX != -1 && iY != -1)
|
||||
move(iX, iY);
|
||||
}
|
||||
|
||||
qint32 iWidth1 = settings.value("main/splitter1", QVariant::fromValue(-1)).toInt();
|
||||
qint32 iWidth2 = settings.value("main/splitter2", QVariant::fromValue(-1)).toInt();
|
||||
qint32 iWidth3 = settings.value("main/splitter3", QVariant::fromValue(-1)).toInt();
|
||||
|
||||
ui->m_splitter->setSizes(QList<int>() << iWidth1 << iWidth2 << iWidth3);
|
||||
}
|
||||
|
||||
void cMainWindow::loadData()
|
||||
{
|
||||
m_dbDigikam = QSqlDatabase::addDatabase("QSQLITE", "digikam4");
|
||||
m_dbDigikam.setDatabaseName("C:\\Temp\\__DIGIKAM__\\digikam4.db");
|
||||
if(!m_dbDigikam.open())
|
||||
{
|
||||
qDebug() << "Digikam: DB Open failed. " << m_dbDigikam.lastError().text();
|
||||
return;
|
||||
}
|
||||
|
||||
m_dbThumbnail = QSqlDatabase::addDatabase("QSQLITE", "thumbnails-digikam");
|
||||
m_dbThumbnail.setDatabaseName("C:\\Temp\\__DIGIKAM__\\thumbnails-digikam.db");
|
||||
if(!m_dbThumbnail.open())
|
||||
{
|
||||
qDebug() << "Thumbnail: DB Open failed. " << m_dbDigikam.lastError().text();
|
||||
return;
|
||||
}
|
||||
|
||||
m_albumRootsList = new cAlbumRootsList(&m_dbDigikam, &m_dbThumbnail, this);
|
||||
if(!m_albumRootsList)
|
||||
{
|
||||
qDebug() << "Album Root List: out of memory.";
|
||||
return;
|
||||
}
|
||||
|
||||
if(!m_albumRootsList->load())
|
||||
{
|
||||
qDebug() << "Album Root List: load failed.";
|
||||
return;
|
||||
}
|
||||
|
||||
m_albumsList = new cAlbumsList(&m_dbDigikam, &m_dbThumbnail, m_albumRootsList, this);
|
||||
if(!m_albumsList)
|
||||
{
|
||||
qDebug() << "Album List: out of memory.";
|
||||
return;
|
||||
}
|
||||
|
||||
if(!m_albumsList->load())
|
||||
{
|
||||
qDebug() << "Album List: load failed.";
|
||||
return;
|
||||
}
|
||||
|
||||
displayData();
|
||||
ui->m_folderView->expandAll();
|
||||
}
|
||||
|
||||
void cMainWindow::displayData()
|
||||
{
|
||||
m_loading = true;
|
||||
|
||||
m_thumbnailViewModel->clear();
|
||||
m_folderViewModel->clear();
|
||||
|
||||
m_rootItem = new QStandardItem("library");
|
||||
m_folderViewModel->appendRow(m_rootItem);
|
||||
|
||||
for(int x = 0;x < m_albumRootsList->count();x++)
|
||||
{
|
||||
QStandardItem* lpItem = new QStandardItem(m_albumRootsList->at(x)->label());
|
||||
lpItem->setData(QVariant::fromValue(m_albumRootsList->at(x)), Qt::UserRole+1);
|
||||
lpItem->setCheckable(true);
|
||||
m_albumRootsList->at(x)->setItem(lpItem);
|
||||
m_rootItem->appendRow(lpItem);
|
||||
}
|
||||
|
||||
for(int x = 0;x < m_albumsList->count();x++)
|
||||
{
|
||||
cAlbums* lpAlbums = m_albumsList->at(x);
|
||||
|
||||
QStandardItem* lpItem = new QStandardItem(lpAlbums->path());
|
||||
lpItem->setData(QVariant::fromValue(lpAlbums), Qt::UserRole+1);
|
||||
lpItem->setCheckable(true);
|
||||
|
||||
QStandardItem* lpRootItem = findDBRootItem(m_folderViewModel, m_rootItem, lpAlbums->albumRoots());
|
||||
|
||||
if(lpAlbums->parentAlbums())
|
||||
lpRootItem = lpAlbums->parentAlbums()->item();
|
||||
|
||||
lpAlbums->setItem(lpItem);
|
||||
lpRootItem->appendRow(lpItem);
|
||||
}
|
||||
|
||||
m_loading = false;
|
||||
}
|
||||
|
||||
QStandardItem* cMainWindow::findDBRootItem(QStandardItemModel* model, QStandardItem *rootItem, cAlbumRoots* albumRoots)
|
||||
{
|
||||
QModelIndex parent;
|
||||
|
||||
if(rootItem)
|
||||
parent = rootItem->index();
|
||||
|
||||
for(int x = 0;x < model->rowCount(parent);x++)
|
||||
{
|
||||
QStandardItem* item = model->itemFromIndex(model->index(x, 0, parent));
|
||||
if(item->data(Qt::UserRole+1).value<cAlbumRoots*>() == albumRoots)
|
||||
return(item);
|
||||
}
|
||||
|
||||
return(nullptr);
|
||||
}
|
||||
|
||||
QStandardItem* cMainWindow::findParentItem(QStandardItemModel* model, QStandardItem *rootItem, cAlbums* albumRoots)
|
||||
{
|
||||
QModelIndex parent;
|
||||
|
||||
if(rootItem)
|
||||
parent = rootItem->index();
|
||||
|
||||
for(int x = 0;x < model->rowCount(parent);x++)
|
||||
{
|
||||
QStandardItem* item = model->itemFromIndex(model->index(x, 0, parent));
|
||||
|
||||
if(item->data(Qt::UserRole+1).value<cAlbums*>() == albumRoots)
|
||||
return(item);
|
||||
|
||||
if(item->hasChildren())
|
||||
{
|
||||
item = findParentItem(model, item, albumRoots);
|
||||
if(item)
|
||||
return(item);
|
||||
}
|
||||
}
|
||||
|
||||
return(nullptr);
|
||||
}
|
||||
|
||||
void cMainWindow::initSignals()
|
||||
{
|
||||
connect(m_folderViewModel, &QStandardItemModel::itemChanged, this, &cMainWindow::onFolderViewItemChanged);
|
||||
connect(ui->m_folderView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &cMainWindow::onFolderSelected);
|
||||
}
|
||||
|
||||
void cMainWindow::onFolderViewItemChanged(QStandardItem* item)
|
||||
{
|
||||
Qt::CheckState state = item->checkState();
|
||||
QModelIndex parent = item->index();
|
||||
|
||||
for(int x = 0;x < m_folderViewModel->rowCount(parent);x++)
|
||||
{
|
||||
QStandardItem* curItem = m_folderViewModel->itemFromIndex(m_folderViewModel->index(x, 0, parent));
|
||||
if(curItem)
|
||||
curItem->setCheckState(state);
|
||||
}
|
||||
}
|
||||
|
||||
void cMainWindow::onFolderSelected(const QItemSelection& /*selection*/, const QItemSelection& /*previous*/)
|
||||
{
|
||||
if(m_loading)
|
||||
return;
|
||||
|
||||
m_thumbnailViewModel->clear();
|
||||
|
||||
if(!ui->m_folderView->selectionModel()->selectedIndexes().count())
|
||||
return;
|
||||
|
||||
QModelIndex index = ui->m_folderView->selectionModel()->selectedIndexes()[0];
|
||||
if(!index.isValid())
|
||||
return;
|
||||
|
||||
cAlbums* albums = m_folderSortFilterProxyModel->data(index, Qt::UserRole+1).value<cAlbums*>();
|
||||
|
||||
albums->loadImages();
|
||||
cImagesList* imagesList = albums->imagesList();
|
||||
if(!imagesList)
|
||||
return;
|
||||
|
||||
for(int x = 0;x < imagesList->count();x++)
|
||||
{
|
||||
cImages* images = imagesList->at(x);
|
||||
if(!images)
|
||||
continue;
|
||||
|
||||
QStandardItem* item = new QStandardItem(images->name());
|
||||
item->setData(QVariant::fromValue(images));
|
||||
m_thumbnailViewModel->appendRow(item);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef CMAINWINDOW_H
|
||||
#define CMAINWINDOW_H
|
||||
|
||||
|
||||
#include "calbumroots.h"
|
||||
#include "calbums.h"
|
||||
#include "cfoldersortfilterproxymodel.h"
|
||||
#include "cthumbnailsortfilterproxymodel.h"
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QCloseEvent>
|
||||
|
||||
#include <QStandardItem>
|
||||
#include <QSqlDatabase>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui { class cMainWindow; }
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class cMainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
cMainWindow(QWidget *parent = nullptr);
|
||||
~cMainWindow();
|
||||
|
||||
private:
|
||||
Ui::cMainWindow* ui;
|
||||
QSqlDatabase m_dbDigikam;
|
||||
QSqlDatabase m_dbThumbnail;
|
||||
cAlbumRootsList* m_albumRootsList;
|
||||
cAlbumsList* m_albumsList;
|
||||
|
||||
QStandardItemModel* m_folderViewModel;
|
||||
cFolderSortFilterProxyModel* m_folderSortFilterProxyModel;
|
||||
QStandardItemModel* m_thumbnailViewModel;
|
||||
cThumbnailSortFilterProxyModel* m_thumbnailSortFilterProxyModel;
|
||||
QStandardItem* m_rootItem;
|
||||
|
||||
bool m_loading;
|
||||
|
||||
void initUI();
|
||||
void loadData();
|
||||
void displayData();
|
||||
void initSignals();
|
||||
|
||||
QStandardItem* findDBRootItem(QStandardItemModel* model, QStandardItem* rootItem, cAlbumRoots* albumRoots);
|
||||
QStandardItem* findParentItem(QStandardItemModel* model, QStandardItem* rootItem, cAlbums *albumRoots);
|
||||
protected:
|
||||
void closeEvent(QCloseEvent* event);
|
||||
|
||||
private slots:
|
||||
void onFolderViewItemChanged(QStandardItem* item);
|
||||
void onFolderSelected(const QItemSelection& selection, const QItemSelection& previous);
|
||||
};
|
||||
#endif // CMAINWINDOW_H
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>cMainWindow</class>
|
||||
<widget class="QMainWindow" name="cMainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1280</width>
|
||||
<height>894</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>cMainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QSplitter" name="m_splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QTreeView" name="m_folderView">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="headerHidden">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QListView" name="m_thumbnailView"/>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1280</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,130 @@
|
||||
/*!
|
||||
\file cthumbnailfilterproxymodel.cpp
|
||||
|
||||
*/
|
||||
|
||||
#include "cthumbnailsortfilterproxymodel.h"
|
||||
//#include "cpicture.h"
|
||||
|
||||
|
||||
cThumbnailSortFilterProxyModel::cThumbnailSortFilterProxyModel(QObject* parent) :
|
||||
QSortFilterProxyModel (parent)
|
||||
{
|
||||
}
|
||||
|
||||
void cThumbnailSortFilterProxyModel::setFilterPath(const QString &szPath)
|
||||
{
|
||||
m_szPath = szPath;
|
||||
m_szPath.replace("\\", "/");
|
||||
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
QString cThumbnailSortFilterProxyModel::filterPath()
|
||||
{
|
||||
return(m_szPath);
|
||||
}
|
||||
|
||||
QStringList cThumbnailSortFilterProxyModel::titleList()
|
||||
{
|
||||
return(m_titleList);
|
||||
}
|
||||
|
||||
void cThumbnailSortFilterProxyModel::setTitleList(const QStringList& titleList)
|
||||
{
|
||||
m_titleList = titleList;
|
||||
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
QList<qint32> cThumbnailSortFilterProxyModel::personList()
|
||||
{
|
||||
return(m_personList);
|
||||
}
|
||||
|
||||
void cThumbnailSortFilterProxyModel::setPersonList(const QList<qint32>& personList, bool bAnd)
|
||||
{
|
||||
m_personList = personList;
|
||||
m_bPersonAnd = bAnd;
|
||||
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
QList<qint32> cThumbnailSortFilterProxyModel::locationList()
|
||||
{
|
||||
return(m_locationList);
|
||||
}
|
||||
|
||||
void cThumbnailSortFilterProxyModel::setLocationList(const QList<qint32>& locationList, bool bAnd)
|
||||
{
|
||||
m_locationList = locationList;
|
||||
m_bLocationAnd = bAnd;
|
||||
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
QList<qint32> cThumbnailSortFilterProxyModel::tagList()
|
||||
{
|
||||
return(m_tagList);
|
||||
}
|
||||
|
||||
void cThumbnailSortFilterProxyModel::setTagList(const QList<qint32>& tagList, bool bAnd)
|
||||
{
|
||||
m_tagList = tagList;
|
||||
m_bTagAnd = bAnd;
|
||||
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
bool cThumbnailSortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
// bool bValid = false;
|
||||
|
||||
// QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||
// cPicture* lpPicture = sourceModel()->data(index0, Qt::UserRole+1).value<cPicture*>();
|
||||
|
||||
// if(!lpPicture)
|
||||
// return(false);
|
||||
|
||||
// if(m_szPath.isEmpty())
|
||||
// bValid = true;
|
||||
// else
|
||||
// {
|
||||
// QString szPath = lpPicture->filePath();
|
||||
|
||||
// if(!szPath.startsWith(m_szPath))
|
||||
// return(false);
|
||||
|
||||
// if(szPath.length() == m_szPath.length())
|
||||
// bValid = true;
|
||||
|
||||
// if(szPath.mid(m_szPath.length(), 1) == "/")
|
||||
// bValid = true;
|
||||
// }
|
||||
|
||||
// if(!bValid)
|
||||
// return(false);
|
||||
|
||||
// if(!m_titleList.contains(lpPicture->title()) && !m_titleList.isEmpty())
|
||||
// return(false);
|
||||
|
||||
// if(!m_personList.isEmpty())
|
||||
// {
|
||||
// if(!lpPicture->personList().contains(m_personList, m_bPersonAnd))
|
||||
// return(false);
|
||||
// }
|
||||
|
||||
// if(!m_locationList.isEmpty())
|
||||
// {
|
||||
// if(!lpPicture->locationList().contains(m_locationList, m_bLocationAnd))
|
||||
// return(false);
|
||||
// }
|
||||
|
||||
// if(!m_tagList.isEmpty())
|
||||
// {
|
||||
// if(!lpPicture->tagList().contains(m_tagList, m_bTagAnd))
|
||||
// return(false);
|
||||
// }
|
||||
|
||||
return(true);
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*!
|
||||
\file cthumbnailfilterproxymodel.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef CTHUMBNAILFILTERPROXYMODEL_H
|
||||
#define CTHUMBNAILFILTERPROXYMODEL_H
|
||||
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\class cThumbnailFilterProxyModel cthumbnailfilterproxymodel.h "cthumbnailfilterproxymodel.h"
|
||||
*/
|
||||
class cThumbnailSortFilterProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
cThumbnailSortFilterProxyModel(QObject* parent = nullptr);
|
||||
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn filterPath
|
||||
*/
|
||||
QString filterPath();
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn setFilterPath
|
||||
\param szPath
|
||||
*/
|
||||
void setFilterPath(const QString& szPath);
|
||||
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn titleList
|
||||
*/
|
||||
QStringList titleList();
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn setTitleList
|
||||
\param titleList
|
||||
*/
|
||||
void setTitleList(const QStringList& titleList);
|
||||
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn personList
|
||||
*/
|
||||
QList<qint32> personList();
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn setPersonList
|
||||
\param personList
|
||||
*/
|
||||
void setPersonList(const QList<qint32>& personList, bool bAnd);
|
||||
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn locationList
|
||||
*/
|
||||
QList<qint32> locationList();
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn setLocationList
|
||||
\param locationList
|
||||
*/
|
||||
void setLocationList(const QList<qint32>& locationList, bool bAnd);
|
||||
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn tagList
|
||||
*/
|
||||
QList<qint32> tagList();
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn setTagList
|
||||
\param tagList
|
||||
*/
|
||||
void setTagList(const QList<qint32>& tagList, bool bAnd);
|
||||
|
||||
protected:
|
||||
/*!
|
||||
\brief
|
||||
|
||||
\fn filterAcceptsRow
|
||||
\param sourceRow
|
||||
\param sourceParent
|
||||
\return bool
|
||||
*/
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
|
||||
private:
|
||||
QString m_szPath; /*!< TODO: describe */
|
||||
QStringList m_titleList; /*!< TODO: describe */
|
||||
QList<qint32> m_personList; /*!< TODO: describe */
|
||||
QList<qint32> m_locationList; /*!< TODO: describe */
|
||||
QList<qint32> m_tagList; /*!< TODO: describe */
|
||||
bool m_bPersonAnd; /*!< TODO: describe */
|
||||
bool m_bLocationAnd; /*!< TODO: describe */
|
||||
bool m_bTagAnd; /*!< TODO: describe */
|
||||
};
|
||||
|
||||
#endif // CTHUMBNAILFILTERPROXYMODEL_H
|
||||
@@ -0,0 +1,84 @@
|
||||
VERSION = 0.0.1.0
|
||||
QMAKE_TARGET_COMPANY = WIN-DESIGN
|
||||
QMAKE_TARGET_PRODUCT = exportDigikam
|
||||
QMAKE_TARGET_DESCRIPTION = exportDigikam
|
||||
QMAKE_TARGET_COPYRIGHT = (c) 2022 WIN-DESIGN
|
||||
|
||||
QT += core gui sql
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
CONFIG += c++11
|
||||
|
||||
win32-msvc* {
|
||||
contains(QT_ARCH, i386) {
|
||||
message("msvc 32-bit")
|
||||
} else {
|
||||
message("msvc 64-bit")
|
||||
}
|
||||
}
|
||||
|
||||
win32-g++ {
|
||||
message("mingw")
|
||||
INCLUDEPATH += C:\dev\3rdParty\exiv2-0.27.5\include C:\dev\3rdParty\libraw C:\dev\3rdParty\libjpeg\include C:\dev\3rdParty\opencv\include dng
|
||||
LIBS += -LC:\dev\3rdParty\exiv2-0.27.5\lib -lexiv2.dll -LC:\dev\3rdParty\libraw\lib -LC:\dev\3rdParty\opencv\x64\mingw\lib -lraw -lws2_32 -lz -lopencv_core412.dll -lopencv_imgproc412.dll
|
||||
}
|
||||
|
||||
unix {
|
||||
message("*nix")
|
||||
LIBS += -lraw -lexiv2
|
||||
}
|
||||
|
||||
QMAKE_CXXFLAGS += -DLIBRAW_NODLL -DLIBRAW_NOTHREADS
|
||||
|
||||
# You can make your code fail to compile if it uses deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
DEFINES += APP_VERSION=\\\"$$VERSION\\\"
|
||||
|
||||
SOURCES += \
|
||||
calbumroots.cpp \
|
||||
calbums.cpp \
|
||||
cfoldersortfilterproxymodel.cpp \
|
||||
cimage.cpp \
|
||||
cimages.cpp \
|
||||
cthumbnailsortfilterproxymodel.cpp \
|
||||
main.cpp \
|
||||
cmainwindow.cpp \
|
||||
pgfutils/libpgf/Decoder.cpp \
|
||||
pgfutils/libpgf/Encoder.cpp \
|
||||
pgfutils/libpgf/PGFimage.cpp \
|
||||
pgfutils/libpgf/PGFstream.cpp \
|
||||
pgfutils/libpgf/Subband.cpp \
|
||||
pgfutils/libpgf/WaveletTransform.cpp \
|
||||
pgfutils/pgfutils.cpp
|
||||
|
||||
HEADERS += \
|
||||
calbumroots.h \
|
||||
calbums.h \
|
||||
cfoldersortfilterproxymodel.h \
|
||||
cimage.h \
|
||||
cimages.h \
|
||||
cmainwindow.h \
|
||||
cthumbnailsortfilterproxymodel.h \
|
||||
pgfutils/libpgf/BitStream.h \
|
||||
pgfutils/libpgf/Decoder.h \
|
||||
pgfutils/libpgf/Encoder.h \
|
||||
pgfutils/libpgf/PGFimage.h \
|
||||
pgfutils/libpgf/PGFplatform.h \
|
||||
pgfutils/libpgf/PGFstream.h \
|
||||
pgfutils/libpgf/PGFtypes.h \
|
||||
pgfutils/libpgf/Subband.h \
|
||||
pgfutils/libpgf/WaveletTransform.h \
|
||||
pgfutils/pgfutils.h
|
||||
|
||||
FORMS += \
|
||||
cmainwindow.ui
|
||||
|
||||
# Default rules for deployment.
|
||||
qnx: target.path = /tmp/$${TARGET}/bin
|
||||
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||
!isEmpty(target.path): INSTALLS += target
|
||||
|
||||
DISTFILES += \
|
||||
pgfutils/libpgf/README
|
||||
@@ -0,0 +1,28 @@
|
||||
#include "cmainwindow.h"
|
||||
|
||||
|
||||
#include <QApplication>
|
||||
#include <QSettings>
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
|
||||
a.setApplicationVersion(APP_VERSION);
|
||||
a.setApplicationDisplayName("exportDigikam");
|
||||
a.setOrganizationName("WIN-DESIGN");
|
||||
a.setOrganizationDomain("windesign.at");
|
||||
a.setApplicationName("exportDigikam");
|
||||
|
||||
QSettings settings;
|
||||
|
||||
cMainWindow w;
|
||||
|
||||
if(settings.value("main/maximized").toBool())
|
||||
w.showMaximized();
|
||||
else
|
||||
w.show();
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* The Progressive Graphics File; http://www.libpgf.org
|
||||
*
|
||||
* $Date: 2006-06-04 22:05:59 +0200 (So, 04 Jun 2006) $
|
||||
* $Revision: 229 $
|
||||
*
|
||||
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
* as published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// @file Bitstream.h
|
||||
/// @brief PGF bit-stream operations
|
||||
/// @author C. Stamm
|
||||
|
||||
#ifndef PGF_BITSTREAM_H
|
||||
#define PGF_BITSTREAM_H
|
||||
|
||||
#include "PGFtypes.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// constants
|
||||
//static const WordWidth = 32;
|
||||
//static const WordWidthLog = 5;
|
||||
static const UINT32 Filled = 0xFFFFFFFF;
|
||||
|
||||
/// @brief Make 64 bit unsigned integer from two 32 bit unsigned integers
|
||||
#define MAKEU64(a, b) ((UINT64) (((UINT32) (a)) | ((UINT64) ((UINT32) (b))) << 32))
|
||||
|
||||
/*
|
||||
static UINT8 lMask[] = {
|
||||
0x00, // 00000000
|
||||
0x80, // 10000000
|
||||
0xc0, // 11000000
|
||||
0xe0, // 11100000
|
||||
0xf0, // 11110000
|
||||
0xf8, // 11111000
|
||||
0xfc, // 11111100
|
||||
0xfe, // 11111110
|
||||
0xff, // 11111111
|
||||
};
|
||||
*/
|
||||
// these procedures have to be inlined because of performance reasons
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Set one bit of a bit stream to 1
|
||||
/// @param stream A bit stream stored in array of unsigned integers
|
||||
/// @param pos A valid zero-based position in the bit stream
|
||||
inline void SetBit(UINT32* stream, UINT32 pos) {
|
||||
stream[pos >> WordWidthLog] |= (1 << (pos%WordWidth));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Set one bit of a bit stream to 0
|
||||
/// @param stream A bit stream stored in array of unsigned integers
|
||||
/// @param pos A valid zero-based position in the bit stream
|
||||
inline void ClearBit(UINT32* stream, UINT32 pos) {
|
||||
stream[pos >> WordWidthLog] &= ~(1 << (pos%WordWidth));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return one bit of a bit stream
|
||||
/// @param stream A bit stream stored in array of unsigned integers
|
||||
/// @param pos A valid zero-based position in the bit stream
|
||||
/// @return bit at position pos of bit stream stream
|
||||
inline bool GetBit(UINT32* stream, UINT32 pos) {
|
||||
return (stream[pos >> WordWidthLog] & (1 << (pos%WordWidth))) > 0;
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Compare k-bit binary representation of stream at position pos with val
|
||||
/// @param stream A bit stream stored in array of unsigned integers
|
||||
/// @param pos A valid zero-based position in the bit stream
|
||||
/// @param k Number of bits to compare
|
||||
/// @param val Value to compare with
|
||||
/// @return true if equal
|
||||
inline bool CompareBitBlock(UINT32* stream, UINT32 pos, UINT32 k, UINT32 val) {
|
||||
const UINT32 iLoInt = pos >> WordWidthLog;
|
||||
const UINT32 iHiInt = (pos + k - 1) >> WordWidthLog;
|
||||
ASSERT(iLoInt <= iHiInt);
|
||||
const UINT32 mask = (Filled >> (WordWidth - k));
|
||||
|
||||
if (iLoInt == iHiInt) {
|
||||
// fits into one integer
|
||||
val &= mask;
|
||||
val <<= (pos%WordWidth);
|
||||
return (stream[iLoInt] & val) == val;
|
||||
} else {
|
||||
// must be splitted over integer boundary
|
||||
UINT64 v1 = MAKEU64(stream[iLoInt], stream[iHiInt]);
|
||||
UINT64 v2 = UINT64(val & mask) << (pos%WordWidth);
|
||||
return (v1 & v2) == v2;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Store k-bit binary representation of val in stream at position pos
|
||||
/// @param stream A bit stream stored in array of unsigned integers
|
||||
/// @param pos A valid zero-based position in the bit stream
|
||||
/// @param val Value to store in stream at position pos
|
||||
/// @param k Number of bits of integer representation of val
|
||||
inline void SetValueBlock(UINT32* stream, UINT32 pos, UINT32 val, UINT32 k) {
|
||||
const UINT32 offset = pos%WordWidth;
|
||||
const UINT32 iLoInt = pos >> WordWidthLog;
|
||||
const UINT32 iHiInt = (pos + k - 1) >> WordWidthLog;
|
||||
ASSERT(iLoInt <= iHiInt);
|
||||
const UINT32 loMask = Filled << offset;
|
||||
const UINT32 hiMask = Filled >> (WordWidth - 1 - ((pos + k - 1)%WordWidth));
|
||||
|
||||
if (iLoInt == iHiInt) {
|
||||
// fits into one integer
|
||||
stream[iLoInt] &= ~(loMask & hiMask); // clear bits
|
||||
stream[iLoInt] |= val << offset; // write value
|
||||
} else {
|
||||
// must be splitted over integer boundary
|
||||
stream[iLoInt] &= ~loMask; // clear bits
|
||||
stream[iLoInt] |= val << offset; // write lower part of value
|
||||
stream[iHiInt] &= ~hiMask; // clear bits
|
||||
stream[iHiInt] |= val >> (WordWidth - offset); // write higher part of value
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Read k-bit number from stream at position pos
|
||||
/// @param stream A bit stream stored in array of unsigned integers
|
||||
/// @param pos A valid zero-based position in the bit stream
|
||||
/// @param k Number of bits to read: 1 <= k <= 32
|
||||
inline UINT32 GetValueBlock(UINT32* stream, UINT32 pos, UINT32 k) {
|
||||
UINT32 count, hiCount;
|
||||
const UINT32 iLoInt = pos >> WordWidthLog; // integer of first bit
|
||||
const UINT32 iHiInt = (pos + k - 1) >> WordWidthLog; // integer of last bit
|
||||
const UINT32 loMask = Filled << (pos%WordWidth);
|
||||
const UINT32 hiMask = Filled >> (WordWidth - 1 - ((pos + k - 1)%WordWidth));
|
||||
|
||||
if (iLoInt == iHiInt) {
|
||||
// inside integer boundary
|
||||
count = stream[iLoInt] & (loMask & hiMask);
|
||||
count >>= pos%WordWidth;
|
||||
} else {
|
||||
// overlapping integer boundary
|
||||
count = stream[iLoInt] & loMask;
|
||||
count >>= pos%WordWidth;
|
||||
hiCount = stream[iHiInt] & hiMask;
|
||||
hiCount <<= WordWidth - (pos%WordWidth);
|
||||
count |= hiCount;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Clear block of size at least len at position pos in stream
|
||||
/// @param stream A bit stream stored in array of unsigned integers
|
||||
/// @param pos A valid zero-based position in the bit stream
|
||||
/// @param len Number of bits set to 0
|
||||
inline void ClearBitBlock(UINT32* stream, UINT32 pos, UINT32 len) {
|
||||
ASSERT(len > 0);
|
||||
const UINT32 iFirstInt = pos >> WordWidthLog;
|
||||
const UINT32 iLastInt = (pos + len - 1) >> WordWidthLog;
|
||||
|
||||
const UINT32 startMask = Filled << (pos%WordWidth);
|
||||
// const UINT32 endMask=Filled>>(WordWidth-1-((pos+len-1)%WordWidth));
|
||||
|
||||
if (iFirstInt == iLastInt) {
|
||||
stream[iFirstInt] &= ~(startMask /*& endMask*/);
|
||||
} else {
|
||||
stream[iFirstInt] &= ~startMask;
|
||||
for (UINT32 i = iFirstInt + 1; i <= iLastInt; i++) { // changed <=
|
||||
stream[i] = 0;
|
||||
}
|
||||
//stream[iLastInt] &= ~endMask;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Set block of size at least len at position pos in stream
|
||||
/// @param stream A bit stream stored in array of unsigned integers
|
||||
/// @param pos A valid zero-based position in the bit stream
|
||||
/// @param len Number of bits set to 1
|
||||
inline void SetBitBlock(UINT32* stream, UINT32 pos, UINT32 len) {
|
||||
ASSERT(len > 0);
|
||||
|
||||
const UINT32 iFirstInt = pos >> WordWidthLog;
|
||||
const UINT32 iLastInt = (pos + len - 1) >> WordWidthLog;
|
||||
|
||||
const UINT32 startMask = Filled << (pos%WordWidth);
|
||||
// const UINT32 endMask=Filled>>(WordWidth-1-((pos+len-1)%WordWidth));
|
||||
|
||||
if (iFirstInt == iLastInt) {
|
||||
stream[iFirstInt] |= (startMask /*& endMask*/);
|
||||
} else {
|
||||
stream[iFirstInt] |= startMask;
|
||||
for (UINT32 i = iFirstInt + 1; i <= iLastInt; i++) { // changed <=
|
||||
stream[i] = Filled;
|
||||
}
|
||||
//stream[iLastInt] &= ~endMask;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Returns the distance to the next 1 in stream at position pos.
|
||||
/// If no 1 is found within len bits, then len is returned.
|
||||
/// @param stream A bit stream stored in array of unsigned integers
|
||||
/// @param pos A valid zero-based position in the bit stream
|
||||
/// @param len size of search area (in bits)
|
||||
/// return The distance to the next 1 in stream at position pos
|
||||
inline UINT32 SeekBitRange(UINT32* stream, UINT32 pos, UINT32 len) {
|
||||
UINT32 count = 0;
|
||||
UINT32 testMask = 1 << (pos%WordWidth);
|
||||
UINT32* word = stream + (pos >> WordWidthLog);
|
||||
|
||||
while (((*word & testMask) == 0) && (count < len)) {
|
||||
count++;
|
||||
testMask <<= 1;
|
||||
if (!testMask) {
|
||||
word++; testMask = 1;
|
||||
|
||||
// fast steps if all bits in a word are zero
|
||||
while ((count + WordWidth <= len) && (*word == 0)) {
|
||||
word++;
|
||||
count += WordWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Returns the distance to the next 0 in stream at position pos.
|
||||
/// If no 0 is found within len bits, then len is returned.
|
||||
/// @param stream A bit stream stored in array of unsigned integers
|
||||
/// @param pos A valid zero-based position in the bit stream
|
||||
/// @param len size of search area (in bits)
|
||||
/// return The distance to the next 0 in stream at position pos
|
||||
inline UINT32 SeekBit1Range(UINT32* stream, UINT32 pos, UINT32 len) {
|
||||
UINT32 count = 0;
|
||||
UINT32 testMask = 1 << (pos%WordWidth);
|
||||
UINT32* word = stream + (pos >> WordWidthLog);
|
||||
|
||||
while (((*word & testMask) != 0) && (count < len)) {
|
||||
count++;
|
||||
testMask <<= 1;
|
||||
if (!testMask) {
|
||||
word++; testMask = 1;
|
||||
|
||||
// fast steps if all bits in a word are one
|
||||
while ((count + WordWidth <= len) && (*word == Filled)) {
|
||||
word++;
|
||||
count += WordWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
/*
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// BitCopy: copies k bits from source to destination
|
||||
/// Note: only 8 bits are copied at a time, if speed is an issue, a more
|
||||
/// complicated but faster 64 bit algorithm should be used.
|
||||
inline void BitCopy(const UINT8 *sStream, UINT32 sPos, UINT8 *dStream, UINT32 dPos, UINT32 k) {
|
||||
ASSERT(k > 0);
|
||||
|
||||
div_t divS = div(sPos, 8);
|
||||
div_t divD = div(dPos, 8);
|
||||
UINT32 sOff = divS.rem;
|
||||
UINT32 dOff = divD.rem;
|
||||
INT32 tmp = div(dPos + k - 1, 8).quot;
|
||||
|
||||
const UINT8 *sAddr = sStream + divS.quot;
|
||||
UINT8 *dAddrS = dStream + divD.quot;
|
||||
UINT8 *dAddrE = dStream + tmp;
|
||||
UINT8 eMask;
|
||||
|
||||
UINT8 destSB = *dAddrS;
|
||||
UINT8 destEB = *dAddrE;
|
||||
UINT8 *dAddr;
|
||||
UINT8 prec;
|
||||
INT32 shiftl, shiftr;
|
||||
|
||||
if (dOff > sOff) {
|
||||
prec = 0;
|
||||
shiftr = dOff - sOff;
|
||||
shiftl = 8 - dOff + sOff;
|
||||
} else {
|
||||
prec = *sAddr << (sOff - dOff);
|
||||
shiftr = 8 - sOff + dOff;
|
||||
shiftl = sOff - dOff;
|
||||
sAddr++;
|
||||
}
|
||||
|
||||
for (dAddr = dAddrS; dAddr < dAddrE; dAddr++, sAddr++) {
|
||||
*dAddr = prec | (*sAddr >> shiftr);
|
||||
prec = *sAddr << shiftl;
|
||||
}
|
||||
|
||||
if ((sPos + k)%8 == 0) {
|
||||
*dAddr = prec;
|
||||
} else {
|
||||
*dAddr = prec | (*sAddr >> shiftr);
|
||||
}
|
||||
|
||||
eMask = lMask[dOff];
|
||||
*dAddrS = (destSB & eMask) | (*dAddrS & (~eMask));
|
||||
|
||||
INT32 mind = (dPos + k) % 8;
|
||||
eMask = (mind) ? lMask[mind] : lMask[8];
|
||||
*dAddrE = (destEB & (~eMask)) | (*dAddrE & eMask);
|
||||
}
|
||||
*/
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Compute bit position of the next 32-bit word
|
||||
/// @param pos current bit stream position
|
||||
/// @return bit position of next 32-bit word
|
||||
inline UINT32 AlignWordPos(UINT32 pos) {
|
||||
// return ((pos + WordWidth - 1) >> WordWidthLog) << WordWidthLog;
|
||||
return DWWIDTHBITS(pos);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Compute number of the 32-bit words
|
||||
/// @param pos Current bit stream position
|
||||
/// @return Number of 32-bit words
|
||||
inline UINT32 NumberOfWords(UINT32 pos) {
|
||||
return (pos + WordWidth - 1) >> WordWidthLog;
|
||||
}
|
||||
|
||||
#endif //PGF_BITSTREAM_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* The Progressive Graphics File; http://www.libpgf.org
|
||||
*
|
||||
* $Date: 2006-06-04 22:05:59 +0200 (So, 04 Jun 2006) $
|
||||
* $Revision: 229 $
|
||||
*
|
||||
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
* as published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// @file Decoder.h
|
||||
/// @brief PGF decoder class
|
||||
/// @author C. Stamm, R. Spuler
|
||||
|
||||
#ifndef PGF_DECODER_H
|
||||
#define PGF_DECODER_H
|
||||
|
||||
#include "PGFstream.h"
|
||||
#include "BitStream.h"
|
||||
#include "Subband.h"
|
||||
#include "WaveletTransform.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Constants
|
||||
#define BufferLen (BufferSize/WordWidth) ///< number of words per buffer
|
||||
#define CodeBufferLen BufferSize ///< number of words in code buffer (CodeBufferLen > BufferLen)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// PGF decoder class.
|
||||
/// @author C. Stamm, R. Spuler
|
||||
/// @brief PGF decoder
|
||||
class CDecoder {
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// PGF decoder macro block class.
|
||||
/// @author C. Stamm, I. Bauersachs
|
||||
/// @brief A macro block is a decoding unit of fixed size (uncoded)
|
||||
class CMacroBlock {
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Constructor: Initializes new macro block.
|
||||
CMacroBlock()
|
||||
: m_header(0) // makes sure that IsCompletelyRead() returns true for an empty macro block
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( suppress : 4351 )
|
||||
#endif
|
||||
, m_value()
|
||||
, m_codeBuffer()
|
||||
, m_valuePos(0)
|
||||
, m_sigFlagVector()
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Returns true if this macro block has been completely read.
|
||||
/// @return true if current value position is at block end
|
||||
bool IsCompletelyRead() const { return m_valuePos >= m_header.rbh.bufferSize; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Decodes already read input data into this macro block.
|
||||
/// Several macro blocks can be decoded in parallel.
|
||||
/// Call CDecoder::ReadMacroBlock before this method.
|
||||
void BitplaneDecode();
|
||||
|
||||
ROIBlockHeader m_header; ///< block header
|
||||
DataT m_value[BufferSize]; ///< output buffer of values with index m_valuePos
|
||||
UINT32 m_codeBuffer[CodeBufferLen]; ///< input buffer for encoded bitstream
|
||||
UINT32 m_valuePos; ///< current position in m_value
|
||||
|
||||
private:
|
||||
UINT32 ComposeBitplane(UINT32 bufferSize, DataT planeMask, UINT32* sigBits, UINT32* refBits, UINT32* signBits);
|
||||
UINT32 ComposeBitplaneRLD(UINT32 bufferSize, DataT planeMask, UINT32 sigPos, UINT32* refBits);
|
||||
UINT32 ComposeBitplaneRLD(UINT32 bufferSize, DataT planeMask, UINT32* sigBits, UINT32* refBits, UINT32 signPos);
|
||||
void SetBitAtPos(UINT32 pos, DataT planeMask) { (m_value[pos] >= 0) ? m_value[pos] |= planeMask : m_value[pos] -= planeMask; }
|
||||
void SetSign(UINT32 pos, bool sign) { m_value[pos] = -m_value[pos]*sign + m_value[pos]*(!sign); }
|
||||
|
||||
bool m_sigFlagVector[BufferSize+1]; // see paper from Malvar, Fast Progressive Wavelet Coder
|
||||
};
|
||||
|
||||
public:
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Constructor: Read pre-header, header, and levelLength at current stream position.
|
||||
/// It might throw an IOException.
|
||||
/// @param stream A PGF stream
|
||||
/// @param preHeader [out] A PGF pre-header
|
||||
/// @param header [out] A PGF header
|
||||
/// @param postHeader [out] A PGF post-header
|
||||
/// @param levelLength The location of the levelLength array. The array is allocated in this method. The caller has to delete this array.
|
||||
/// @param userDataPos The stream position of the user data (metadata)
|
||||
/// @param useOMP If true, then the decoder will use multi-threading based on openMP
|
||||
/// @param userDataPolicy Policy of user data (meta-data) handling while reading PGF headers.
|
||||
CDecoder(CPGFStream* stream, PGFPreHeader& preHeader, PGFHeader& header,
|
||||
PGFPostHeader& postHeader, UINT32*& levelLength, UINT64& userDataPos,
|
||||
bool useOMP, UINT32 userDataPolicy); // throws IOException
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Destructor
|
||||
~CDecoder();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Unpartitions a rectangular region of a given subband.
|
||||
/// Partitioning scheme: The plane is partitioned in squares of side length LinBlockSize.
|
||||
/// Read wavelet coefficients from the output buffer of a macro block.
|
||||
/// It might throw an IOException.
|
||||
/// @param band A subband
|
||||
/// @param quantParam Dequantization value
|
||||
/// @param width The width of the rectangle
|
||||
/// @param height The height of the rectangle
|
||||
/// @param startPos The relative subband position of the top left corner of the rectangular region
|
||||
/// @param pitch The number of bytes in row of the subband
|
||||
void Partition(CSubband* band, int quantParam, int width, int height, int startPos, int pitch);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Deccoding and dequantization of HL and LH subband (interleaved) using partitioning scheme.
|
||||
/// Partitioning scheme: The plane is partitioned in squares of side length InterBlockSize.
|
||||
/// It might throw an IOException.
|
||||
/// @param wtChannel A wavelet transform channel containing the HL and HL band
|
||||
/// @param level Wavelet transform level
|
||||
/// @param quantParam Dequantization value
|
||||
void DecodeInterleaved(CWaveletTransform* wtChannel, int level, int quantParam);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Returns the length of all encoded headers in bytes.
|
||||
/// @return The length of all encoded headers in bytes
|
||||
UINT32 GetEncodedHeaderLength() const { return m_encodedHeaderLength; }
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
/// Resets stream position to beginning of PGF pre-header
|
||||
void SetStreamPosToStart() { ASSERT(m_stream); m_stream->SetPos(FSFromStart, m_startPos); }
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
/// Resets stream position to beginning of data block
|
||||
void SetStreamPosToData() { ASSERT(m_stream); m_stream->SetPos(FSFromStart, m_startPos + m_encodedHeaderLength); }
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
/// Skips a given number of bytes in the open stream.
|
||||
/// It might throw an IOException.
|
||||
void Skip(UINT64 offset);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Dequantization of a single value at given position in subband.
|
||||
/// It might throw an IOException.
|
||||
/// @param band A subband
|
||||
/// @param bandPos A valid position in subband band
|
||||
/// @param quantParam The quantization parameter
|
||||
void DequantizeValue(CSubband* band, UINT32 bandPos, int quantParam);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Copies data from the open stream to a target buffer.
|
||||
/// It might throw an IOException.
|
||||
/// @param target The target buffer
|
||||
/// @param len The number of bytes to read
|
||||
/// @return The number of bytes copied to the target buffer
|
||||
UINT32 ReadEncodedData(UINT8* target, UINT32 len) const;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Reads next block(s) from stream and decodes them
|
||||
/// It might throw an IOException.
|
||||
void DecodeBuffer();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// @return Stream
|
||||
CPGFStream* GetStream() { return m_stream; }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Gets next macro block
|
||||
/// It might throw an IOException.
|
||||
void GetNextMacroBlock();
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Resets stream position to next tile.
|
||||
/// Used with ROI encoding scheme only.
|
||||
/// It might throw an IOException.
|
||||
void SkipTileBuffer();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Enables region of interest (ROI) status.
|
||||
void SetROI() { m_roi = true; }
|
||||
#endif
|
||||
|
||||
#ifdef TRACE
|
||||
void DumpBuffer();
|
||||
#endif
|
||||
|
||||
private:
|
||||
void ReadMacroBlock(CMacroBlock* block); ///< throws IOException
|
||||
|
||||
CPGFStream *m_stream; ///< input PGF stream
|
||||
UINT64 m_startPos; ///< stream position at the beginning of the PGF pre-header
|
||||
UINT64 m_streamSizeEstimation; ///< estimation of stream size
|
||||
UINT32 m_encodedHeaderLength; ///< stream offset from startPos to the beginning of the data part (highest level)
|
||||
|
||||
CMacroBlock **m_macroBlocks; ///< array of macroblocks
|
||||
int m_currentBlockIndex; ///< index of current macro block
|
||||
int m_macroBlockLen; ///< array length
|
||||
int m_macroBlocksAvailable; ///< number of decoded macro blocks (including currently used macro block)
|
||||
CMacroBlock *m_currentBlock; ///< current macro block (used by main thread)
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
bool m_roi; ///< true: ensures region of interest (ROI) decoding
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif //PGF_DECODER_H
|
||||
@@ -0,0 +1,830 @@
|
||||
/*
|
||||
* The Progressive Graphics File; http://www.libpgf.org
|
||||
*
|
||||
* $Date: 2007-02-03 13:04:21 +0100 (Sa, 03 Feb 2007) $
|
||||
* $Revision: 280 $
|
||||
*
|
||||
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
* as published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// @file Encoder.cpp
|
||||
/// @brief PGF encoder class implementation
|
||||
/// @author C. Stamm, R. Spuler
|
||||
|
||||
#include "Encoder.h"
|
||||
#ifdef TRACE
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// PGF: file structure
|
||||
//
|
||||
// PGFPreHeader PGFHeader [PGFPostHeader] LevelLengths Level_n-1 Level_n-2 ... Level_0
|
||||
// PGFPostHeader ::= [ColorTable] [UserData]
|
||||
// LevelLengths ::= UINT32[nLevels]
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Encoding scheme
|
||||
// input: wavelet coefficients stored in subbands
|
||||
// output: binary file
|
||||
//
|
||||
// subband
|
||||
// |
|
||||
// m_value [BufferSize]
|
||||
// | | |
|
||||
// m_sign sigBits refBits [BufferSize, BufferLen, BufferLen]
|
||||
// | | |
|
||||
// m_codeBuffer (for each plane: RLcodeLength (16 bit), RLcoded sigBits + m_sign, refBits)
|
||||
// |
|
||||
// file (for each buffer: packedLength (16 bit), packed bits)
|
||||
//
|
||||
|
||||
// Constants
|
||||
#define CodeBufferBitLen (CodeBufferLen*WordWidth) ///< max number of bits in m_codeBuffer
|
||||
#define MaxCodeLen ((1 << RLblockSizeLen) - 1) ///< max length of RL encoded block
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
/// Write pre-header, header, postHeader, and levelLength.
|
||||
/// It might throw an IOException.
|
||||
/// @param stream A PGF stream
|
||||
/// @param preHeader A already filled in PGF pre-header
|
||||
/// @param header An already filled in PGF header
|
||||
/// @param postHeader [in] An already filled in PGF post-header (containing color table, user data, ...)
|
||||
/// @param userDataPos [out] File position of user data
|
||||
/// @param useOMP If true, then the encoder will use multi-threading based on openMP
|
||||
CEncoder::CEncoder(CPGFStream* stream, PGFPreHeader preHeader, PGFHeader header, const PGFPostHeader& postHeader, UINT64& userDataPos, bool useOMP)
|
||||
: m_stream(stream)
|
||||
, m_bufferStartPos(0)
|
||||
, m_currLevelIndex(0)
|
||||
, m_nLevels(header.nLevels)
|
||||
, m_favorSpeed(false)
|
||||
, m_forceWriting(false)
|
||||
#ifdef __PGFROISUPPORT__
|
||||
, m_roi(false)
|
||||
#endif
|
||||
{
|
||||
ASSERT(m_stream);
|
||||
|
||||
int count;
|
||||
m_lastMacroBlock = 0;
|
||||
m_levelLength = nullptr;
|
||||
|
||||
// set number of threads
|
||||
#ifdef LIBPGF_USE_OPENMP
|
||||
m_macroBlockLen = omp_get_num_procs();
|
||||
#else
|
||||
m_macroBlockLen = 1;
|
||||
#endif
|
||||
|
||||
if (useOMP && m_macroBlockLen > 1) {
|
||||
#ifdef LIBPGF_USE_OPENMP
|
||||
omp_set_num_threads(m_macroBlockLen);
|
||||
#endif
|
||||
// create macro block array
|
||||
m_macroBlocks = new(std::nothrow) CMacroBlock*[m_macroBlockLen];
|
||||
if (!m_macroBlocks) ReturnWithError(InsufficientMemory);
|
||||
for (int i=0; i < m_macroBlockLen; i++) m_macroBlocks[i] = new CMacroBlock(this);
|
||||
m_currentBlock = m_macroBlocks[m_lastMacroBlock++];
|
||||
} else {
|
||||
m_macroBlocks = 0;
|
||||
m_macroBlockLen = 1;
|
||||
m_currentBlock = new CMacroBlock(this);
|
||||
}
|
||||
|
||||
// save file position
|
||||
m_startPosition = m_stream->GetPos();
|
||||
|
||||
// write preHeader
|
||||
preHeader.hSize = __VAL(preHeader.hSize);
|
||||
count = PreHeaderSize;
|
||||
m_stream->Write(&count, &preHeader);
|
||||
|
||||
// write file header
|
||||
header.height = __VAL(header.height);
|
||||
header.width = __VAL(header.width);
|
||||
count = HeaderSize;
|
||||
m_stream->Write(&count, &header);
|
||||
|
||||
// write postHeader
|
||||
if (header.mode == ImageModeIndexedColor) {
|
||||
// write color table
|
||||
count = ColorTableSize;
|
||||
m_stream->Write(&count, (void *)postHeader.clut);
|
||||
}
|
||||
// save user data file position
|
||||
userDataPos = m_stream->GetPos();
|
||||
if (postHeader.userDataLen) {
|
||||
if (postHeader.userData) {
|
||||
// write user data
|
||||
count = postHeader.userDataLen;
|
||||
m_stream->Write(&count, postHeader.userData);
|
||||
} else {
|
||||
m_stream->SetPos(FSFromCurrent, count);
|
||||
}
|
||||
}
|
||||
|
||||
// save level length file position
|
||||
m_levelLengthPos = m_stream->GetPos();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Destructor
|
||||
CEncoder::~CEncoder() {
|
||||
if (m_macroBlocks) {
|
||||
for (int i=0; i < m_macroBlockLen; i++) delete m_macroBlocks[i];
|
||||
delete[] m_macroBlocks;
|
||||
} else {
|
||||
delete m_currentBlock;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Increase post-header size and write new size into stream.
|
||||
/// @param preHeader An already filled in PGF pre-header
|
||||
/// It might throw an IOException.
|
||||
void CEncoder::UpdatePostHeaderSize(PGFPreHeader preHeader) {
|
||||
UINT64 curPos = m_stream->GetPos(); // end of user data
|
||||
int count = PreHeaderSize;
|
||||
|
||||
// write preHeader
|
||||
SetStreamPosToStart();
|
||||
preHeader.hSize = __VAL(preHeader.hSize);
|
||||
m_stream->Write(&count, &preHeader);
|
||||
|
||||
m_stream->SetPos(FSFromStart, curPos);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Create level length data structure and write a place holder into stream.
|
||||
/// It might throw an IOException.
|
||||
/// @param levelLength A reference to an integer array, large enough to save the relative file positions of all PGF levels
|
||||
/// @return number of bytes written into stream
|
||||
UINT32 CEncoder::WriteLevelLength(UINT32*& levelLength) {
|
||||
// renew levelLength
|
||||
delete[] levelLength;
|
||||
levelLength = new(std::nothrow) UINT32[m_nLevels];
|
||||
if (!levelLength) ReturnWithError(InsufficientMemory);
|
||||
for (UINT8 l = 0; l < m_nLevels; l++) levelLength[l] = 0;
|
||||
m_levelLength = levelLength;
|
||||
|
||||
// save level length file position
|
||||
m_levelLengthPos = m_stream->GetPos();
|
||||
|
||||
// write dummy levelLength
|
||||
int count = m_nLevels*WordBytes;
|
||||
m_stream->Write(&count, m_levelLength);
|
||||
|
||||
// save current file position
|
||||
SetBufferStartPos();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
/// Write new levelLength into stream.
|
||||
/// It might throw an IOException.
|
||||
/// @return Written image bytes.
|
||||
UINT32 CEncoder::UpdateLevelLength() {
|
||||
UINT64 curPos = m_stream->GetPos(); // end of image
|
||||
|
||||
// set file pos to levelLength
|
||||
m_stream->SetPos(FSFromStart, m_levelLengthPos);
|
||||
|
||||
if (m_levelLength) {
|
||||
#ifdef PGF_USE_BIG_ENDIAN
|
||||
UINT32 levelLength;
|
||||
int count = WordBytes;
|
||||
|
||||
for (int i=0; i < m_currLevelIndex; i++) {
|
||||
levelLength = __VAL(UINT32(m_levelLength[i]));
|
||||
m_stream->Write(&count, &levelLength);
|
||||
}
|
||||
#else
|
||||
int count = m_currLevelIndex*WordBytes;
|
||||
|
||||
m_stream->Write(&count, m_levelLength);
|
||||
#endif //PGF_USE_BIG_ENDIAN
|
||||
} else {
|
||||
int count = m_currLevelIndex*WordBytes;
|
||||
m_stream->SetPos(FSFromCurrent, count);
|
||||
}
|
||||
|
||||
// begin of image
|
||||
UINT32 retValue = UINT32(curPos - m_stream->GetPos());
|
||||
|
||||
// restore file position
|
||||
m_stream->SetPos(FSFromStart, curPos);
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Partitions a rectangular region of a given subband.
|
||||
/// Partitioning scheme: The plane is partitioned in squares of side length LinBlockSize.
|
||||
/// Write wavelet coefficients from subband into the input buffer of a macro block.
|
||||
/// It might throw an IOException.
|
||||
/// @param band A subband
|
||||
/// @param width The width of the rectangle
|
||||
/// @param height The height of the rectangle
|
||||
/// @param startPos The absolute subband position of the top left corner of the rectangular region
|
||||
/// @param pitch The number of bytes in row of the subband
|
||||
void CEncoder::Partition(CSubband* band, int width, int height, int startPos, int pitch) {
|
||||
ASSERT(band);
|
||||
|
||||
const div_t hh = div(height, LinBlockSize);
|
||||
const div_t ww = div(width, LinBlockSize);
|
||||
const int ws = pitch - LinBlockSize;
|
||||
const int wr = pitch - ww.rem;
|
||||
int pos, base = startPos, base2;
|
||||
|
||||
// main height
|
||||
for (int i=0; i < hh.quot; i++) {
|
||||
// main width
|
||||
base2 = base;
|
||||
for (int j=0; j < ww.quot; j++) {
|
||||
pos = base2;
|
||||
for (int y=0; y < LinBlockSize; y++) {
|
||||
for (int x=0; x < LinBlockSize; x++) {
|
||||
WriteValue(band, pos);
|
||||
pos++;
|
||||
}
|
||||
pos += ws;
|
||||
}
|
||||
base2 += LinBlockSize;
|
||||
}
|
||||
// rest of width
|
||||
pos = base2;
|
||||
for (int y=0; y < LinBlockSize; y++) {
|
||||
for (int x=0; x < ww.rem; x++) {
|
||||
WriteValue(band, pos);
|
||||
pos++;
|
||||
}
|
||||
pos += wr;
|
||||
base += pitch;
|
||||
}
|
||||
}
|
||||
// main width
|
||||
base2 = base;
|
||||
for (int j=0; j < ww.quot; j++) {
|
||||
// rest of height
|
||||
pos = base2;
|
||||
for (int y=0; y < hh.rem; y++) {
|
||||
for (int x=0; x < LinBlockSize; x++) {
|
||||
WriteValue(band, pos);
|
||||
pos++;
|
||||
}
|
||||
pos += ws;
|
||||
}
|
||||
base2 += LinBlockSize;
|
||||
}
|
||||
// rest of height
|
||||
pos = base2;
|
||||
for (int y=0; y < hh.rem; y++) {
|
||||
// rest of width
|
||||
for (int x=0; x < ww.rem; x++) {
|
||||
WriteValue(band, pos);
|
||||
pos++;
|
||||
}
|
||||
pos += wr;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
/// Pad buffer with zeros and encode buffer.
|
||||
/// It might throw an IOException.
|
||||
void CEncoder::Flush() {
|
||||
if (m_currentBlock->m_valuePos > 0) {
|
||||
// pad buffer with zeros
|
||||
memset(&(m_currentBlock->m_value[m_currentBlock->m_valuePos]), 0, (BufferSize - m_currentBlock->m_valuePos)*DataTSize);
|
||||
m_currentBlock->m_valuePos = BufferSize;
|
||||
|
||||
// encode buffer
|
||||
m_forceWriting = true; // makes sure that the following EncodeBuffer is really written into the stream
|
||||
EncodeBuffer(ROIBlockHeader(m_currentBlock->m_valuePos, true));
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Stores band value from given position bandPos into buffer m_value at position m_valuePos
|
||||
// If buffer is full encode it to file
|
||||
// It might throw an IOException.
|
||||
void CEncoder::WriteValue(CSubband* band, int bandPos) {
|
||||
if (m_currentBlock->m_valuePos == BufferSize) {
|
||||
EncodeBuffer(ROIBlockHeader(BufferSize, false));
|
||||
}
|
||||
DataT val = m_currentBlock->m_value[m_currentBlock->m_valuePos++] = band->GetData(bandPos);
|
||||
UINT32 v = abs(val);
|
||||
if (v > m_currentBlock->m_maxAbsValue) m_currentBlock->m_maxAbsValue = v;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Encode buffer and write data into stream.
|
||||
// h contains buffer size and flag indicating end of tile.
|
||||
// Encoding scheme: <wordLen>(16 bits) [ ROI ] data
|
||||
// ROI ::= <bufferSize>(15 bits) <eofTile>(1 bit)
|
||||
// It might throw an IOException.
|
||||
void CEncoder::EncodeBuffer(ROIBlockHeader h) {
|
||||
ASSERT(m_currentBlock);
|
||||
#ifdef __PGFROISUPPORT__
|
||||
ASSERT(m_roi && h.rbh.bufferSize <= BufferSize || h.rbh.bufferSize == BufferSize);
|
||||
#else
|
||||
ASSERT(h.rbh.bufferSize == BufferSize);
|
||||
#endif
|
||||
m_currentBlock->m_header = h;
|
||||
|
||||
// macro block management
|
||||
if (m_macroBlockLen == 1) {
|
||||
m_currentBlock->BitplaneEncode();
|
||||
WriteMacroBlock(m_currentBlock);
|
||||
} else {
|
||||
// save last level index
|
||||
int lastLevelIndex = m_currentBlock->m_lastLevelIndex;
|
||||
|
||||
if (m_forceWriting || m_lastMacroBlock == m_macroBlockLen) {
|
||||
// encode macro blocks
|
||||
/*
|
||||
volatile OSError error = NoError;
|
||||
#ifdef LIBPGF_USE_OPENMP
|
||||
#pragma omp parallel for ordered default(shared)
|
||||
#endif
|
||||
for (int i=0; i < m_lastMacroBlock; i++) {
|
||||
if (error == NoError) {
|
||||
m_macroBlocks[i]->BitplaneEncode();
|
||||
#ifdef LIBPGF_USE_OPENMP
|
||||
#pragma omp ordered
|
||||
#endif
|
||||
{
|
||||
try {
|
||||
WriteMacroBlock(m_macroBlocks[i]);
|
||||
} catch (IOException& e) {
|
||||
error = e.error;
|
||||
}
|
||||
delete m_macroBlocks[i]; m_macroBlocks[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error != NoError) ReturnWithError(error);
|
||||
*/
|
||||
#ifdef LIBPGF_USE_OPENMP
|
||||
#pragma omp parallel for default(shared) //no declared exceptions in next block
|
||||
#endif
|
||||
for (int i=0; i < m_lastMacroBlock; i++) {
|
||||
m_macroBlocks[i]->BitplaneEncode();
|
||||
}
|
||||
for (int i=0; i < m_lastMacroBlock; i++) {
|
||||
WriteMacroBlock(m_macroBlocks[i]);
|
||||
}
|
||||
|
||||
// prepare for next round
|
||||
m_forceWriting = false;
|
||||
m_lastMacroBlock = 0;
|
||||
}
|
||||
// re-initialize macro block
|
||||
m_currentBlock = m_macroBlocks[m_lastMacroBlock++];
|
||||
m_currentBlock->Init(lastLevelIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Write encoded macro block into stream.
|
||||
// It might throw an IOException.
|
||||
void CEncoder::WriteMacroBlock(CMacroBlock* block) {
|
||||
ASSERT(block);
|
||||
#ifdef __PGFROISUPPORT__
|
||||
ROIBlockHeader h = block->m_header;
|
||||
#endif
|
||||
UINT16 wordLen = UINT16(NumberOfWords(block->m_codePos)); ASSERT(wordLen <= CodeBufferLen);
|
||||
int count = sizeof(UINT16);
|
||||
|
||||
#ifdef TRACE
|
||||
//UINT32 filePos = (UINT32)m_stream->GetPos();
|
||||
//printf("EncodeBuffer: %d\n", filePos);
|
||||
#endif
|
||||
|
||||
#ifdef PGF_USE_BIG_ENDIAN
|
||||
// write wordLen
|
||||
UINT16 wl = __VAL(wordLen);
|
||||
m_stream->Write(&count, &wl); ASSERT(count == sizeof(UINT16));
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
// write ROIBlockHeader
|
||||
if (m_roi) {
|
||||
count = sizeof(ROIBlockHeader);
|
||||
h.val = __VAL(h.val);
|
||||
m_stream->Write(&count, &h.val); ASSERT(count == sizeof(ROIBlockHeader));
|
||||
}
|
||||
#endif // __PGFROISUPPORT__
|
||||
|
||||
// convert data
|
||||
for (int i=0; i < wordLen; i++) {
|
||||
block->m_codeBuffer[i] = __VAL(block->m_codeBuffer[i]);
|
||||
}
|
||||
#else
|
||||
// write wordLen
|
||||
m_stream->Write(&count, &wordLen); ASSERT(count == sizeof(UINT16));
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
// write ROIBlockHeader
|
||||
if (m_roi) {
|
||||
count = sizeof(ROIBlockHeader);
|
||||
m_stream->Write(&count, &h.val); ASSERT(count == sizeof(ROIBlockHeader));
|
||||
}
|
||||
#endif // __PGFROISUPPORT__
|
||||
#endif // PGF_USE_BIG_ENDIAN
|
||||
|
||||
// write encoded data into stream
|
||||
count = wordLen*WordBytes;
|
||||
m_stream->Write(&count, block->m_codeBuffer);
|
||||
|
||||
// store levelLength
|
||||
if (m_levelLength) {
|
||||
// store level length
|
||||
// EncodeBuffer has been called after m_lastLevelIndex has been updated
|
||||
ASSERT(m_currLevelIndex < m_nLevels);
|
||||
m_levelLength[m_currLevelIndex] += (UINT32)ComputeBufferLength();
|
||||
m_currLevelIndex = block->m_lastLevelIndex + 1;
|
||||
|
||||
}
|
||||
|
||||
// prepare for next buffer
|
||||
SetBufferStartPos();
|
||||
|
||||
// reset values
|
||||
block->m_valuePos = 0;
|
||||
block->m_maxAbsValue = 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// Encode buffer of given size using bit plane coding.
|
||||
// A buffer contains bufferLen UINT32 values, thus, bufferSize bits per bit plane.
|
||||
// Following coding scheme is used:
|
||||
// Buffer ::= <nPlanes>(5 bits) foreach (plane i): Plane[i]
|
||||
// Plane[i] ::= [ Sig1 | Sig2 ] [DWORD alignment] refBits
|
||||
// Sig1 ::= 1 <codeLen>(15 bits) codedSigAndSignBits
|
||||
// Sig2 ::= 0 <sigLen>(15 bits) [Sign1 | Sign2 ] [DWORD alignment] sigBits
|
||||
// Sign1 ::= 1 <codeLen>(15 bits) codedSignBits
|
||||
// Sign2 ::= 0 <signLen>(15 bits) [DWORD alignment] signBits
|
||||
void CEncoder::CMacroBlock::BitplaneEncode() {
|
||||
UINT8 nPlanes;
|
||||
UINT32 sigLen, codeLen = 0, wordPos, refLen, signLen;
|
||||
UINT32 sigBits[BufferLen] = { 0 };
|
||||
UINT32 refBits[BufferLen] = { 0 };
|
||||
UINT32 signBits[BufferLen] = { 0 };
|
||||
UINT32 planeMask;
|
||||
UINT32 bufferSize = m_header.rbh.bufferSize; ASSERT(bufferSize <= BufferSize);
|
||||
bool useRL;
|
||||
|
||||
#ifdef TRACE
|
||||
//printf("which thread: %d\n", omp_get_thread_num());
|
||||
#endif
|
||||
|
||||
// clear significance vector
|
||||
for (UINT32 k=0; k < bufferSize; k++) {
|
||||
m_sigFlagVector[k] = false;
|
||||
}
|
||||
m_sigFlagVector[bufferSize] = true; // sentinel
|
||||
|
||||
// clear output buffer
|
||||
for (UINT32 k=0; k < bufferSize; k++) {
|
||||
m_codeBuffer[k] = 0;
|
||||
}
|
||||
m_codePos = 0;
|
||||
|
||||
// compute number of bit planes and split buffer into separate bit planes
|
||||
nPlanes = NumberOfBitplanes();
|
||||
|
||||
// write number of bit planes to m_codeBuffer
|
||||
// <nPlanes>
|
||||
SetValueBlock(m_codeBuffer, 0, nPlanes, MaxBitPlanesLog);
|
||||
m_codePos += MaxBitPlanesLog;
|
||||
|
||||
// loop through all bit planes
|
||||
if (nPlanes == 0) nPlanes = MaxBitPlanes + 1;
|
||||
planeMask = 1 << (nPlanes - 1);
|
||||
|
||||
for (int plane = nPlanes - 1; plane >= 0; plane--) {
|
||||
// clear significant bitset
|
||||
for (UINT32 k=0; k < BufferLen; k++) {
|
||||
sigBits[k] = 0;
|
||||
}
|
||||
|
||||
// split bitplane in significant bitset and refinement bitset
|
||||
sigLen = DecomposeBitplane(bufferSize, planeMask, m_codePos + RLblockSizeLen + 1, sigBits, refBits, signBits, signLen, codeLen);
|
||||
|
||||
if (sigLen > 0 && codeLen <= MaxCodeLen && codeLen < AlignWordPos(sigLen) + AlignWordPos(signLen) + 2*RLblockSizeLen) {
|
||||
// set RL code bit
|
||||
// <1><codeLen>
|
||||
SetBit(m_codeBuffer, m_codePos++);
|
||||
|
||||
// write length codeLen to m_codeBuffer
|
||||
SetValueBlock(m_codeBuffer, m_codePos, codeLen, RLblockSizeLen);
|
||||
m_codePos += RLblockSizeLen + codeLen;
|
||||
} else {
|
||||
#ifdef TRACE
|
||||
//printf("new\n");
|
||||
//for (UINT32 i=0; i < bufferSize; i++) {
|
||||
// printf("%s", (GetBit(sigBits, i)) ? "1" : "_");
|
||||
// if (i%120 == 119) printf("\n");
|
||||
//}
|
||||
//printf("\n");
|
||||
#endif // TRACE
|
||||
|
||||
// run-length coding wasn't efficient enough
|
||||
// we don't use RL coding for sigBits
|
||||
// <0><sigLen>
|
||||
ClearBit(m_codeBuffer, m_codePos++);
|
||||
|
||||
// write length sigLen to m_codeBuffer
|
||||
ASSERT(sigLen <= MaxCodeLen);
|
||||
SetValueBlock(m_codeBuffer, m_codePos, sigLen, RLblockSizeLen);
|
||||
m_codePos += RLblockSizeLen;
|
||||
|
||||
if (m_encoder->m_favorSpeed || signLen == 0) {
|
||||
useRL = false;
|
||||
} else {
|
||||
// overwrite m_codeBuffer
|
||||
useRL = true;
|
||||
// run-length encode m_sign and append them to the m_codeBuffer
|
||||
codeLen = RLESigns(m_codePos + RLblockSizeLen + 1, signBits, signLen);
|
||||
}
|
||||
|
||||
if (useRL && codeLen <= MaxCodeLen && codeLen < signLen) {
|
||||
// RL encoding of m_sign was efficient
|
||||
// <1><codeLen><codedSignBits>_
|
||||
// write RL code bit
|
||||
SetBit(m_codeBuffer, m_codePos++);
|
||||
|
||||
// write codeLen to m_codeBuffer
|
||||
SetValueBlock(m_codeBuffer, m_codePos, codeLen, RLblockSizeLen);
|
||||
|
||||
// compute position of sigBits
|
||||
wordPos = NumberOfWords(m_codePos + RLblockSizeLen + codeLen);
|
||||
ASSERT(0 <= wordPos && wordPos < CodeBufferLen);
|
||||
} else {
|
||||
// RL encoding of signBits wasn't efficient
|
||||
// <0><signLen>_<signBits>_
|
||||
// clear RL code bit
|
||||
ClearBit(m_codeBuffer, m_codePos++);
|
||||
|
||||
// write signLen to m_codeBuffer
|
||||
ASSERT(signLen <= MaxCodeLen);
|
||||
SetValueBlock(m_codeBuffer, m_codePos, signLen, RLblockSizeLen);
|
||||
|
||||
// write signBits to m_codeBuffer
|
||||
wordPos = NumberOfWords(m_codePos + RLblockSizeLen);
|
||||
ASSERT(0 <= wordPos && wordPos < CodeBufferLen);
|
||||
codeLen = NumberOfWords(signLen);
|
||||
|
||||
for (UINT32 k=0; k < codeLen; k++) {
|
||||
m_codeBuffer[wordPos++] = signBits[k];
|
||||
}
|
||||
}
|
||||
|
||||
// write sigBits
|
||||
// <sigBits>_
|
||||
ASSERT(0 <= wordPos && wordPos < CodeBufferLen);
|
||||
refLen = NumberOfWords(sigLen);
|
||||
|
||||
for (UINT32 k=0; k < refLen; k++) {
|
||||
m_codeBuffer[wordPos++] = sigBits[k];
|
||||
}
|
||||
m_codePos = wordPos << WordWidthLog;
|
||||
}
|
||||
|
||||
// append refinement bitset (aligned to word boundary)
|
||||
// _<refBits>
|
||||
wordPos = NumberOfWords(m_codePos);
|
||||
ASSERT(0 <= wordPos && wordPos < CodeBufferLen);
|
||||
refLen = NumberOfWords(bufferSize - sigLen);
|
||||
|
||||
for (UINT32 k=0; k < refLen; k++) {
|
||||
m_codeBuffer[wordPos++] = refBits[k];
|
||||
}
|
||||
m_codePos = wordPos << WordWidthLog;
|
||||
planeMask >>= 1;
|
||||
}
|
||||
ASSERT(0 <= m_codePos && m_codePos <= CodeBufferBitLen);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
// Split bitplane of length bufferSize into significant and refinement bitset
|
||||
// returns length [bits] of significant bits
|
||||
// input: bufferSize, planeMask, codePos
|
||||
// output: sigBits, refBits, signBits, signLen [bits], codeLen [bits]
|
||||
// RLE
|
||||
// - Encode run of 2^k zeros by a single 0.
|
||||
// - Encode run of count 0's followed by a 1 with codeword: 1<count>x
|
||||
// - x is 0: if a positive sign is stored, otherwise 1
|
||||
// - Store each bit in m_codeBuffer[codePos] and increment codePos.
|
||||
UINT32 CEncoder::CMacroBlock::DecomposeBitplane(UINT32 bufferSize, UINT32 planeMask, UINT32 codePos, UINT32* sigBits, UINT32* refBits, UINT32* signBits, UINT32& signLen, UINT32& codeLen) {
|
||||
ASSERT(sigBits);
|
||||
ASSERT(refBits);
|
||||
ASSERT(signBits);
|
||||
ASSERT(codePos < CodeBufferBitLen);
|
||||
|
||||
UINT32 sigPos = 0;
|
||||
UINT32 valuePos = 0, valueEnd;
|
||||
UINT32 refPos = 0;
|
||||
|
||||
// set output value
|
||||
signLen = 0;
|
||||
|
||||
// prepare RLE of Sigs and Signs
|
||||
const UINT32 outStartPos = codePos;
|
||||
UINT32 k = 3;
|
||||
UINT32 runlen = 1 << k; // = 2^k
|
||||
UINT32 count = 0;
|
||||
|
||||
while (valuePos < bufferSize) {
|
||||
// search next 1 in m_sigFlagVector using searching with sentinel
|
||||
valueEnd = valuePos;
|
||||
while(!m_sigFlagVector[valueEnd]) { valueEnd++; }
|
||||
|
||||
// search 1's in m_value[plane][valuePos..valueEnd)
|
||||
// these 1's are significant bits
|
||||
while (valuePos < valueEnd) {
|
||||
if (GetBitAtPos(valuePos, planeMask)) {
|
||||
// RLE encoding
|
||||
// encode run of count 0's followed by a 1
|
||||
// with codeword: 1<count>(signBits[signPos])
|
||||
SetBit(m_codeBuffer, codePos++);
|
||||
if (k > 0) {
|
||||
SetValueBlock(m_codeBuffer, codePos, count, k);
|
||||
codePos += k;
|
||||
|
||||
// adapt k (half the zero run-length)
|
||||
k--;
|
||||
runlen >>= 1;
|
||||
}
|
||||
|
||||
// copy and write sign bit
|
||||
if (m_value[valuePos] < 0) {
|
||||
SetBit(signBits, signLen++);
|
||||
SetBit(m_codeBuffer, codePos++);
|
||||
} else {
|
||||
ClearBit(signBits, signLen++);
|
||||
ClearBit(m_codeBuffer, codePos++);
|
||||
}
|
||||
|
||||
// write a 1 to sigBits
|
||||
SetBit(sigBits, sigPos++);
|
||||
|
||||
// update m_sigFlagVector
|
||||
m_sigFlagVector[valuePos] = true;
|
||||
|
||||
// prepare for next run
|
||||
count = 0;
|
||||
} else {
|
||||
// RLE encoding
|
||||
count++;
|
||||
if (count == runlen) {
|
||||
// encode run of 2^k zeros by a single 0
|
||||
ClearBit(m_codeBuffer, codePos++);
|
||||
// adapt k (double the zero run-length)
|
||||
if (k < WordWidth) {
|
||||
k++;
|
||||
runlen <<= 1;
|
||||
}
|
||||
|
||||
// prepare for next run
|
||||
count = 0;
|
||||
}
|
||||
|
||||
// write 0 to sigBits
|
||||
sigPos++;
|
||||
}
|
||||
valuePos++;
|
||||
}
|
||||
// refinement bit
|
||||
if (valuePos < bufferSize) {
|
||||
// write one refinement bit
|
||||
if (GetBitAtPos(valuePos++, planeMask)) {
|
||||
SetBit(refBits, refPos);
|
||||
} else {
|
||||
ClearBit(refBits, refPos);
|
||||
}
|
||||
refPos++;
|
||||
}
|
||||
}
|
||||
// RLE encoding of the rest of the plane
|
||||
// encode run of count 0's followed by a 1
|
||||
// with codeword: 1<count>(signBits[signPos])
|
||||
SetBit(m_codeBuffer, codePos++);
|
||||
if (k > 0) {
|
||||
SetValueBlock(m_codeBuffer, codePos, count, k);
|
||||
codePos += k;
|
||||
}
|
||||
// write dmmy sign bit
|
||||
SetBit(m_codeBuffer, codePos++);
|
||||
|
||||
// write word filler zeros
|
||||
|
||||
ASSERT(sigPos <= bufferSize);
|
||||
ASSERT(refPos <= bufferSize);
|
||||
ASSERT(signLen <= bufferSize);
|
||||
ASSERT(valuePos == bufferSize);
|
||||
ASSERT(codePos >= outStartPos && codePos < CodeBufferBitLen);
|
||||
codeLen = codePos - outStartPos;
|
||||
|
||||
return sigPos;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// Compute number of bit planes needed
|
||||
UINT8 CEncoder::CMacroBlock::NumberOfBitplanes() {
|
||||
UINT8 cnt = 0;
|
||||
|
||||
// determine number of bitplanes for max value
|
||||
if (m_maxAbsValue > 0) {
|
||||
while (m_maxAbsValue > 0) {
|
||||
m_maxAbsValue >>= 1; cnt++;
|
||||
}
|
||||
if (cnt == MaxBitPlanes + 1) cnt = 0;
|
||||
// end cs
|
||||
ASSERT(cnt <= MaxBitPlanes);
|
||||
ASSERT((cnt >> MaxBitPlanesLog) == 0);
|
||||
return cnt;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Adaptive Run-Length encoder for long sequences of ones.
|
||||
// Returns length of output in bits.
|
||||
// - Encode run of 2^k ones by a single 1.
|
||||
// - Encode run of count 1's followed by a 0 with codeword: 0<count>.
|
||||
// - Store each bit in m_codeBuffer[codePos] and increment codePos.
|
||||
UINT32 CEncoder::CMacroBlock::RLESigns(UINT32 codePos, UINT32* signBits, UINT32 signLen) {
|
||||
ASSERT(signBits);
|
||||
ASSERT(0 <= codePos && codePos < CodeBufferBitLen);
|
||||
ASSERT(0 < signLen && signLen <= BufferSize);
|
||||
|
||||
const UINT32 outStartPos = codePos;
|
||||
UINT32 k = 0;
|
||||
UINT32 runlen = 1 << k; // = 2^k
|
||||
UINT32 count = 0;
|
||||
UINT32 signPos = 0;
|
||||
|
||||
while (signPos < signLen) {
|
||||
// search next 0 in signBits starting at position signPos
|
||||
count = SeekBit1Range(signBits, signPos, __min(runlen, signLen - signPos));
|
||||
// count 1's found
|
||||
if (count == runlen) {
|
||||
// encode run of 2^k ones by a single 1
|
||||
signPos += count;
|
||||
SetBit(m_codeBuffer, codePos++);
|
||||
// adapt k (double the 1's run-length)
|
||||
if (k < WordWidth) {
|
||||
k++;
|
||||
runlen <<= 1;
|
||||
}
|
||||
} else {
|
||||
// encode run of count 1's followed by a 0
|
||||
// with codeword: 0(count)
|
||||
signPos += count + 1;
|
||||
ClearBit(m_codeBuffer, codePos++);
|
||||
if (k > 0) {
|
||||
SetValueBlock(m_codeBuffer, codePos, count, k);
|
||||
codePos += k;
|
||||
}
|
||||
// adapt k (half the 1's run-length)
|
||||
if (k > 0) {
|
||||
k--;
|
||||
runlen >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
ASSERT(signPos == signLen || signPos == signLen + 1);
|
||||
ASSERT(codePos >= outStartPos && codePos < CodeBufferBitLen);
|
||||
return codePos - outStartPos;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
#ifdef TRACE
|
||||
void CEncoder::DumpBuffer() const {
|
||||
//printf("\nDump\n");
|
||||
//for (UINT32 i=0; i < BufferSize; i++) {
|
||||
// printf("%d", m_value[i]);
|
||||
//}
|
||||
//printf("\n");
|
||||
}
|
||||
#endif //TRACE
|
||||
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* The Progressive Graphics File; http://www.libpgf.org
|
||||
*
|
||||
* $Date: 2006-06-04 22:05:59 +0200 (So, 04 Jun 2006) $
|
||||
* $Revision: 229 $
|
||||
*
|
||||
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
* as published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// @file Encoder.h
|
||||
/// @brief PGF encoder class
|
||||
/// @author C. Stamm, R. Spuler
|
||||
|
||||
#ifndef PGF_ENCODER_H
|
||||
#define PGF_ENCODER_H
|
||||
|
||||
#include "PGFstream.h"
|
||||
#include "BitStream.h"
|
||||
#include "Subband.h"
|
||||
#include "WaveletTransform.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Constants
|
||||
#define BufferLen (BufferSize/WordWidth) ///< number of words per buffer
|
||||
#define CodeBufferLen BufferSize ///< number of words in code buffer (CodeBufferLen > BufferLen)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// PGF encoder class.
|
||||
/// @author C. Stamm
|
||||
/// @brief PGF encoder
|
||||
class CEncoder {
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// PGF encoder macro block class.
|
||||
/// @author C. Stamm, I. Bauersachs
|
||||
/// @brief A macro block is an encoding unit of fixed size (uncoded)
|
||||
class CMacroBlock {
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Constructor: Initializes new macro block.
|
||||
/// @param encoder Pointer to outer class.
|
||||
CMacroBlock(CEncoder *encoder)
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( suppress : 4351 )
|
||||
#endif
|
||||
: m_value()
|
||||
, m_codeBuffer()
|
||||
, m_header(0)
|
||||
, m_encoder(encoder)
|
||||
, m_sigFlagVector()
|
||||
{
|
||||
ASSERT(m_encoder);
|
||||
Init(-1);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Reinitialzes this macro block (allows reusage).
|
||||
/// @param lastLevelIndex Level length directory index of last encoded level: [0, nLevels)
|
||||
void Init(int lastLevelIndex) { // initialize for reusage
|
||||
m_valuePos = 0;
|
||||
m_maxAbsValue = 0;
|
||||
m_codePos = 0;
|
||||
m_lastLevelIndex = lastLevelIndex;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Encodes this macro block into internal code buffer.
|
||||
/// Several macro blocks can be encoded in parallel.
|
||||
/// Call CEncoder::WriteMacroBlock after this method.
|
||||
void BitplaneEncode();
|
||||
|
||||
DataT m_value[BufferSize]; ///< input buffer of values with index m_valuePos
|
||||
UINT32 m_codeBuffer[CodeBufferLen]; ///< output buffer for encoded bitstream
|
||||
ROIBlockHeader m_header; ///< block header
|
||||
UINT32 m_valuePos; ///< current buffer position
|
||||
UINT32 m_maxAbsValue; ///< maximum absolute coefficient in each buffer
|
||||
UINT32 m_codePos; ///< current position in encoded bitstream
|
||||
int m_lastLevelIndex; ///< index of last encoded level: [0, nLevels); used because a level-end can occur before a buffer is full
|
||||
|
||||
private:
|
||||
UINT32 RLESigns(UINT32 codePos, UINT32* signBits, UINT32 signLen);
|
||||
UINT32 DecomposeBitplane(UINT32 bufferSize, UINT32 planeMask, UINT32 codePos, UINT32* sigBits, UINT32* refBits, UINT32* signBits, UINT32& signLen, UINT32& codeLen);
|
||||
UINT8 NumberOfBitplanes();
|
||||
bool GetBitAtPos(UINT32 pos, UINT32 planeMask) const { return (abs(m_value[pos]) & planeMask) > 0; }
|
||||
|
||||
CEncoder *m_encoder; // encoder instance
|
||||
bool m_sigFlagVector[BufferSize+1]; // see paper from Malvar, Fast Progressive Wavelet Coder
|
||||
};
|
||||
|
||||
public:
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Write pre-header, header, post-Header, and levelLength.
|
||||
/// It might throw an IOException.
|
||||
/// @param stream A PGF stream
|
||||
/// @param preHeader A already filled in PGF pre-header
|
||||
/// @param header An already filled in PGF header
|
||||
/// @param postHeader [in] An already filled in PGF post-header (containing color table, user data, ...)
|
||||
/// @param userDataPos [out] File position of user data
|
||||
/// @param useOMP If true, then the encoder will use multi-threading based on openMP
|
||||
CEncoder(CPGFStream* stream, PGFPreHeader preHeader, PGFHeader header, const PGFPostHeader& postHeader,
|
||||
UINT64& userDataPos, bool useOMP); // throws IOException
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Destructor
|
||||
~CEncoder();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Encoder favors speed over compression size
|
||||
void FavorSpeedOverSize() { m_favorSpeed = true; }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Pad buffer with zeros and encode buffer.
|
||||
/// It might throw an IOException.
|
||||
void Flush();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Increase post-header size and write new size into stream.
|
||||
/// @param preHeader An already filled in PGF pre-header
|
||||
/// It might throw an IOException.
|
||||
void UpdatePostHeaderSize(PGFPreHeader preHeader);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Create level length data structure and write a place holder into stream.
|
||||
/// It might throw an IOException.
|
||||
/// @param levelLength A reference to an integer array, large enough to save the relative file positions of all PGF levels
|
||||
/// @return number of bytes written into stream
|
||||
UINT32 WriteLevelLength(UINT32*& levelLength);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Write new levelLength into stream.
|
||||
/// It might throw an IOException.
|
||||
/// @return Written image bytes.
|
||||
UINT32 UpdateLevelLength();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Partitions a rectangular region of a given subband.
|
||||
/// Partitioning scheme: The plane is partitioned in squares of side length LinBlockSize.
|
||||
/// Write wavelet coefficients from subband into the input buffer of a macro block.
|
||||
/// It might throw an IOException.
|
||||
/// @param band A subband
|
||||
/// @param width The width of the rectangle
|
||||
/// @param height The height of the rectangle
|
||||
/// @param startPos The absolute subband position of the top left corner of the rectangular region
|
||||
/// @param pitch The number of bytes in row of the subband
|
||||
void Partition(CSubband* band, int width, int height, int startPos, int pitch);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Informs the encoder about the encoded level.
|
||||
/// @param currentLevel encoded level [0, nLevels)
|
||||
void SetEncodedLevel(int currentLevel) { ASSERT(currentLevel >= 0); m_currentBlock->m_lastLevelIndex = m_nLevels - currentLevel - 1; m_forceWriting = true; }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Write a single value into subband at given position.
|
||||
/// It might throw an IOException.
|
||||
/// @param band A subband
|
||||
/// @param bandPos A valid position in subband band
|
||||
void WriteValue(CSubband* band, int bandPos);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Compute stream length of header.
|
||||
/// @return header length
|
||||
INT64 ComputeHeaderLength() const { return m_levelLengthPos - m_startPosition; }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Compute stream length of encoded buffer.
|
||||
/// @return encoded buffer length
|
||||
INT64 ComputeBufferLength() const { return m_stream->GetPos() - m_bufferStartPos; }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Compute file offset between real and expected levelLength position.
|
||||
/// @return file offset
|
||||
INT64 ComputeOffset() const { return m_stream->GetPos() - m_levelLengthPos; }
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
/// Resets stream position to beginning of PGF pre-header
|
||||
void SetStreamPosToStart() { ASSERT(m_stream); m_stream->SetPos(FSFromStart, m_startPosition); }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Save current stream position as beginning of current level.
|
||||
void SetBufferStartPos() { m_bufferStartPos = m_stream->GetPos(); }
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Encodes tile buffer and writes it into stream
|
||||
/// It might throw an IOException.
|
||||
void EncodeTileBuffer() { ASSERT(m_currentBlock && m_currentBlock->m_valuePos >= 0 && m_currentBlock->m_valuePos <= BufferSize); EncodeBuffer(ROIBlockHeader(m_currentBlock->m_valuePos, true)); }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Enables region of interest (ROI) status.
|
||||
void SetROI() { m_roi = true; }
|
||||
#endif
|
||||
|
||||
#ifdef TRACE
|
||||
void DumpBuffer() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
void EncodeBuffer(ROIBlockHeader h); // throws IOException
|
||||
void WriteMacroBlock(CMacroBlock* block); // throws IOException
|
||||
|
||||
CPGFStream *m_stream; ///< output PMF stream
|
||||
UINT64 m_startPosition; ///< stream position of PGF start (PreHeader)
|
||||
UINT64 m_levelLengthPos; ///< stream position of Metadata
|
||||
UINT64 m_bufferStartPos; ///< stream position of encoded buffer
|
||||
|
||||
CMacroBlock **m_macroBlocks; ///< array of macroblocks
|
||||
int m_macroBlockLen; ///< array length
|
||||
int m_lastMacroBlock; ///< array index of the last created macro block
|
||||
CMacroBlock *m_currentBlock; ///< current macro block (used by main thread)
|
||||
|
||||
UINT32* m_levelLength; ///< temporary saves the level index
|
||||
int m_currLevelIndex; ///< counts where (=index) to save next value
|
||||
UINT8 m_nLevels; ///< number of levels
|
||||
bool m_favorSpeed; ///< favor speed over size
|
||||
bool m_forceWriting; ///< all macro blocks have to be written into the stream
|
||||
#ifdef __PGFROISUPPORT__
|
||||
bool m_roi; ///< true: ensures region of interest (ROI) encoding
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif //PGF_ENCODER
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,581 @@
|
||||
/*
|
||||
* The Progressive Graphics File; http://www.libpgf.org
|
||||
*
|
||||
* $Date: 2007-02-03 13:04:21 +0100 (Sa, 03 Feb 2007) $
|
||||
* $Revision: 280 $
|
||||
*
|
||||
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
* as published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// @file PGFimage.h
|
||||
/// @brief PGF image class
|
||||
/// @author C. Stamm
|
||||
|
||||
#ifndef PGF_PGFIMAGE_H
|
||||
#define PGF_PGFIMAGE_H
|
||||
|
||||
#include "PGFstream.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// prototypes
|
||||
class CDecoder;
|
||||
class CEncoder;
|
||||
class CWaveletTransform;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// PGF image class is the main class. You always need a PGF object
|
||||
/// for encoding or decoding image data.
|
||||
/// Decoding:
|
||||
/// Open()
|
||||
/// Read()
|
||||
/// GetBitmap()
|
||||
/// Encoding:
|
||||
/// SetHeader()
|
||||
/// ImportBitmap()
|
||||
/// Write()
|
||||
/// @author C. Stamm, R. Spuler
|
||||
/// @brief PGF main class
|
||||
class CPGFImage {
|
||||
public:
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Standard constructor
|
||||
CPGFImage();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Destructor
|
||||
virtual ~CPGFImage();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Destroy internal data structures. Object state after this is the same as after CPGFImage().
|
||||
void Destroy();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Open a PGF image at current stream position: read pre-header, header, and ckeck image type.
|
||||
/// Precondition: The stream has been opened for reading.
|
||||
/// It might throw an IOException.
|
||||
/// @param stream A PGF stream
|
||||
void Open(CPGFStream* stream);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Returns true if the PGF has been opened for reading.
|
||||
bool IsOpen() const { return m_decoder != nullptr; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Read and decode some levels of a PGF image at current stream position.
|
||||
/// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
|
||||
/// Each level can be seen as a single image, containing the same content
|
||||
/// as all other levels, but in a different size (width, height).
|
||||
/// The image size at level i is double the size (width, height) of the image at level i+1.
|
||||
/// The image at level 0 contains the original size.
|
||||
/// Precondition: The PGF image has been opened with a call of Open(...).
|
||||
/// It might throw an IOException.
|
||||
/// @param level [0, nLevels) The image level of the resulting image in the internal image buffer.
|
||||
/// @param cb A pointer to a callback procedure. The procedure is called after reading a single level. If cb returns true, then it stops proceeding.
|
||||
/// @param data Data Pointer to C++ class container to host callback procedure.
|
||||
void Read(int level = 0, CallbackPtr cb = nullptr, void *data = nullptr);
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Read a rectangular region of interest of a PGF image at current stream position.
|
||||
/// The origin of the coordinate axis is the top-left corner of the image.
|
||||
/// All coordinates are measured in pixels.
|
||||
/// It might throw an IOException.
|
||||
/// @param rect [inout] Rectangular region of interest (ROI) at level 0. The rect might be cropped.
|
||||
/// @param level [0, nLevels) The image level of the resulting image in the internal image buffer.
|
||||
/// @param cb A pointer to a callback procedure. The procedure is called after reading a single level. If cb returns true, then it stops proceeding.
|
||||
/// @param data Data Pointer to C++ class container to host callback procedure.
|
||||
void Read(PGFRect& rect, int level = 0, CallbackPtr cb = nullptr, void *data = nullptr);
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Read and decode smallest level of a PGF image at current stream position.
|
||||
/// For details, please refert to Read(...)
|
||||
/// Precondition: The PGF image has been opened with a call of Open(...).
|
||||
/// It might throw an IOException.
|
||||
void ReadPreview() { Read(Levels() - 1); }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// After you've written a PGF image, you can call this method followed by GetBitmap/GetYUV
|
||||
/// to get a quick reconstruction (coded -> decoded image).
|
||||
/// It might throw an IOException.
|
||||
/// @param level The image level of the resulting image in the internal image buffer.
|
||||
void Reconstruct(int level = 0);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Get image data in interleaved format: (ordering of RGB data is BGR[A])
|
||||
/// Upsampling, YUV to RGB transform and interleaving are done here to reduce the number
|
||||
/// of passes over the data.
|
||||
/// The absolute value of pitch is the number of bytes of an image row of the given image buffer.
|
||||
/// If pitch is negative, then the image buffer must point to the last row of a bottom-up image (first byte on last row).
|
||||
/// if pitch is positive, then the image buffer must point to the first row of a top-down image (first byte).
|
||||
/// The sequence of output channels in the output image buffer does not need to be the same as provided by PGF. In case of different sequences you have to
|
||||
/// provide a channelMap of size of expected channels (depending on image mode). For example, PGF provides a channel sequence BGR in RGB color mode.
|
||||
/// If your provided image buffer expects a channel sequence ARGB, then the channelMap looks like { 3, 2, 1, 0 }.
|
||||
/// It might throw an IOException.
|
||||
/// @param pitch The number of bytes of a row of the image buffer.
|
||||
/// @param buff An image buffer.
|
||||
/// @param bpp The number of bits per pixel used in image buffer.
|
||||
/// @param channelMap A integer array containing the mapping of PGF channel ordering to expected channel ordering.
|
||||
/// @param cb A pointer to a callback procedure. The procedure is called after each copied buffer row. If cb returns true, then it stops proceeding.
|
||||
/// @param data Data Pointer to C++ class container to host callback procedure.
|
||||
void GetBitmap(int pitch, UINT8* buff, BYTE bpp, int channelMap[] = nullptr, CallbackPtr cb = nullptr, void *data = nullptr) const; // throws IOException
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Get YUV image data in interleaved format: (ordering is YUV[A])
|
||||
/// The absolute value of pitch is the number of bytes of an image row of the given image buffer.
|
||||
/// If pitch is negative, then the image buffer must point to the last row of a bottom-up image (first byte on last row).
|
||||
/// if pitch is positive, then the image buffer must point to the first row of a top-down image (first byte).
|
||||
/// The sequence of output channels in the output image buffer does not need to be the same as provided by PGF. In case of different sequences you have to
|
||||
/// provide a channelMap of size of expected channels (depending on image mode). For example, PGF provides a channel sequence BGR in RGB color mode.
|
||||
/// If your provided image buffer expects a channel sequence VUY, then the channelMap looks like { 2, 1, 0 }.
|
||||
/// It might throw an IOException.
|
||||
/// @param pitch The number of bytes of a row of the image buffer.
|
||||
/// @param buff An image buffer.
|
||||
/// @param bpp The number of bits per pixel used in image buffer.
|
||||
/// @param channelMap A integer array containing the mapping of PGF channel ordering to expected channel ordering.
|
||||
/// @param cb A pointer to a callback procedure. The procedure is called after each copied buffer row. If cb returns true, then it stops proceeding.
|
||||
/// @param data Data Pointer to C++ class container to host callback procedure.
|
||||
void GetYUV(int pitch, DataT* buff, BYTE bpp, int channelMap[] = nullptr, CallbackPtr cb = nullptr, void *data = nullptr) const; // throws IOException
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Import an image from a specified image buffer.
|
||||
/// This method is usually called before Write(...) and after SetHeader(...).
|
||||
/// The absolute value of pitch is the number of bytes of an image row.
|
||||
/// If pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row).
|
||||
/// If pitch is positive, then buff points to the first row of a top-down image (first byte).
|
||||
/// The sequence of input channels in the input image buffer does not need to be the same as expected from PGF. In case of different sequences you have to
|
||||
/// provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR.
|
||||
/// If your provided image buffer contains a channel sequence ARGB, then the channelMap looks like { 3, 2, 1, 0 }.
|
||||
/// It might throw an IOException.
|
||||
/// @param pitch The number of bytes of a row of the image buffer.
|
||||
/// @param buff An image buffer.
|
||||
/// @param bpp The number of bits per pixel used in image buffer.
|
||||
/// @param channelMap A integer array containing the mapping of input channel ordering to expected channel ordering.
|
||||
/// @param cb A pointer to a callback procedure. The procedure is called after each imported buffer row. If cb returns true, then it stops proceeding.
|
||||
/// @param data Data Pointer to C++ class container to host callback procedure.
|
||||
void ImportBitmap(int pitch, UINT8 *buff, BYTE bpp, int channelMap[] = nullptr, CallbackPtr cb = nullptr, void *data = nullptr);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Import a YUV image from a specified image buffer.
|
||||
/// The absolute value of pitch is the number of bytes of an image row.
|
||||
/// If pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row).
|
||||
/// If pitch is positive, then buff points to the first row of a top-down image (first byte).
|
||||
/// The sequence of input channels in the input image buffer does not need to be the same as expected from PGF. In case of different sequences you have to
|
||||
/// provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR.
|
||||
/// If your provided image buffer contains a channel sequence VUY, then the channelMap looks like { 2, 1, 0 }.
|
||||
/// It might throw an IOException.
|
||||
/// @param pitch The number of bytes of a row of the image buffer.
|
||||
/// @param buff An image buffer.
|
||||
/// @param bpp The number of bits per pixel used in image buffer.
|
||||
/// @param channelMap A integer array containing the mapping of input channel ordering to expected channel ordering.
|
||||
/// @param cb A pointer to a callback procedure. The procedure is called after each imported buffer row. If cb returns true, then it stops proceeding.
|
||||
/// @param data Data Pointer to C++ class container to host callback procedure.
|
||||
void ImportYUV(int pitch, DataT *buff, BYTE bpp, int channelMap[] = nullptr, CallbackPtr cb = nullptr, void *data = nullptr);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Encode and write an entire PGF image (header and image) at current stream position.
|
||||
/// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
|
||||
/// Each level can be seen as a single image, containing the same content
|
||||
/// as all other levels, but in a different size (width, height).
|
||||
/// The image size at level i is double the size (width, height) of the image at level i+1.
|
||||
/// The image at level 0 contains the original size.
|
||||
/// Precondition: the PGF image contains a valid header (see also SetHeader(...)).
|
||||
/// It might throw an IOException.
|
||||
/// @param stream A PGF stream
|
||||
/// @param nWrittenBytes [in-out] The number of bytes written into stream are added to the input value.
|
||||
/// @param cb A pointer to a callback procedure. The procedure is called after writing a single level. If cb returns true, then it stops proceeding.
|
||||
/// @param data Data Pointer to C++ class container to host callback procedure.
|
||||
void Write(CPGFStream* stream, UINT32* nWrittenBytes = nullptr, CallbackPtr cb = nullptr, void *data = nullptr);
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
/// Create wavelet transform channels and encoder. Write header at current stream position.
|
||||
/// Call this method before your first call of Write(int level) or WriteImage(), but after SetHeader().
|
||||
/// This method is called inside of Write(stream, ...).
|
||||
/// It might throw an IOException.
|
||||
/// @param stream A PGF stream
|
||||
/// @return The number of bytes written into stream.
|
||||
UINT32 WriteHeader(CPGFStream* stream);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Encode and write an image at current stream position.
|
||||
/// Call this method after WriteHeader(). In case you want to write uncached metadata,
|
||||
/// then do that after WriteHeader() and before WriteImage().
|
||||
/// This method is called inside of Write(stream, ...).
|
||||
/// It might throw an IOException.
|
||||
/// @param stream A PGF stream
|
||||
/// @param cb A pointer to a callback procedure. The procedure is called after writing a single level. If cb returns true, then it stops proceeding.
|
||||
/// @param data Data Pointer to C++ class container to host callback procedure.
|
||||
/// @return The number of bytes written into stream.
|
||||
UINT32 WriteImage(CPGFStream* stream, CallbackPtr cb = nullptr, void *data = nullptr);
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
//////////////////////////////////////////////////////////////////
|
||||
/// Encode and write down to given level at current stream position.
|
||||
/// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
|
||||
/// Each level can be seen as a single image, containing the same content
|
||||
/// as all other levels, but in a different size (width, height).
|
||||
/// The image size at level i is double the size (width, height) of the image at level i+1.
|
||||
/// The image at level 0 contains the original size.
|
||||
/// Preconditions: the PGF image contains a valid header (see also SetHeader(...)) and
|
||||
/// WriteHeader() has been called before. Levels() > 0.
|
||||
/// The ROI encoding scheme must be used (see also SetHeader(...)).
|
||||
/// It might throw an IOException.
|
||||
/// @param level [0, nLevels) The image level of the resulting image in the internal image buffer.
|
||||
/// @param cb A pointer to a callback procedure. The procedure is called after writing a single level. If cb returns true, then it stops proceeding.
|
||||
/// @param data Data Pointer to C++ class container to host callback procedure.
|
||||
/// @return The number of bytes written into stream.
|
||||
UINT32 Write(int level, CallbackPtr cb = nullptr, void *data = nullptr);
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Configures the encoder.
|
||||
/// @param useOMP Use parallel threading with Open MP during encoding. Default value: true. Influences the encoding only if the codec has been compiled with OpenMP support.
|
||||
/// @param favorSpeedOverSize Favors encoding speed over compression ratio. Default value: false
|
||||
void ConfigureEncoder(bool useOMP = true, bool favorSpeedOverSize = false) { m_useOMPinEncoder = useOMP; m_favorSpeedOverSize = favorSpeedOverSize; }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Configures the decoder.
|
||||
/// @param useOMP Use parallel threading with Open MP during decoding. Default value: true. Influences the decoding only if the codec has been compiled with OpenMP support.
|
||||
/// @param policy The file might contain user data (e.g. metadata). The policy defines the behaviour during Open().
|
||||
/// UP_CacheAll: User data is read and stored completely in a new allocated memory block. It can be accessed by GetUserData().
|
||||
/// UP_CachePrefix: Only prefixSize bytes at the beginning of the user data are stored in a new allocated memory block. It can be accessed by GetUserData().
|
||||
/// UP_Skip: User data is skipped and nothing is cached.
|
||||
/// @param prefixSize Is only used in combination with UP_CachePrefix. It defines the number of bytes cached.
|
||||
void ConfigureDecoder(bool useOMP = true, UserdataPolicy policy = UP_CacheAll, UINT32 prefixSize = 0) { ASSERT(prefixSize <= MaxUserDataSize); m_useOMPinDecoder = useOMP; m_userDataPolicy = (UP_CachePrefix) ? prefixSize : 0xFFFFFFFF - policy; }
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
/// Reset stream position to start of PGF pre-header or start of data. Must not be called before Open() or before Write().
|
||||
/// Use this method after Read() if you want to read the same image several times, e.g. reading different ROIs.
|
||||
/// @param startOfData true: you want to read the same image several times. false: resets stream position to the initial position
|
||||
void ResetStreamPos(bool startOfData);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Set internal PGF image buffer channel.
|
||||
/// @param channel A YUV data channel
|
||||
/// @param c A channel index
|
||||
void SetChannel(DataT* channel, int c = 0) { ASSERT(c >= 0 && c < MaxChannels); m_channel[c] = channel; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Set PGF header and user data.
|
||||
/// Precondition: The PGF image has been never opened with Open(...).
|
||||
/// It might throw an IOException.
|
||||
/// @param header A valid and already filled in PGF header structure
|
||||
/// @param flags A combination of additional version flags. In case you use level-wise encoding then set flag = PGFROI.
|
||||
/// @param userData A user-defined memory block containing any kind of cached metadata.
|
||||
/// @param userDataLength The size of user-defined memory block in bytes
|
||||
void SetHeader(const PGFHeader& header, BYTE flags = 0, const UINT8* userData = 0, UINT32 userDataLength = 0); // throws IOException
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Set maximum intensity value for image modes with more than eight bits per channel.
|
||||
/// Call this method after SetHeader, but before ImportBitmap.
|
||||
/// @param maxValue The maximum intensity value.
|
||||
void SetMaxValue(UINT32 maxValue);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Set progress mode used in Read and Write.
|
||||
/// Default mode is PM_Relative.
|
||||
/// This method must be called before Open() or SetHeader().
|
||||
/// PM_Relative: 100% = level difference between current level and target level of Read/Write
|
||||
/// PM_Absolute: 100% = number of levels
|
||||
void SetProgressMode(ProgressMode pm) { m_progressMode = pm; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Set refresh callback procedure and its parameter.
|
||||
/// The refresh callback is called during Read(...) after each level read.
|
||||
/// @param callback A refresh callback procedure
|
||||
/// @param arg A parameter of the refresh callback procedure
|
||||
void SetRefreshCallback(RefreshCB callback, void* arg) { m_cb = callback; m_cbArg = arg; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Sets the red, green, blue (RGB) color values for a range of entries in the palette (clut).
|
||||
/// It might throw an IOException.
|
||||
/// @param iFirstColor The color table index of the first entry to set.
|
||||
/// @param nColors The number of color table entries to set.
|
||||
/// @param prgbColors A pointer to the array of RGBQUAD structures to set the color table entries.
|
||||
void SetColorTable(UINT32 iFirstColor, UINT32 nColors, const RGBQUAD* prgbColors);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return an internal YUV image channel.
|
||||
/// @param c A channel index
|
||||
/// @return An internal YUV image channel
|
||||
DataT* GetChannel(int c = 0) { ASSERT(c >= 0 && c < MaxChannels); return m_channel[c]; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Retrieves red, green, blue (RGB) color values from a range of entries in the palette of the DIB section.
|
||||
/// It might throw an IOException.
|
||||
/// @param iFirstColor The color table index of the first entry to retrieve.
|
||||
/// @param nColors The number of color table entries to retrieve.
|
||||
/// @param prgbColors A pointer to the array of RGBQUAD structures to retrieve the color table entries.
|
||||
void GetColorTable(UINT32 iFirstColor, UINT32 nColors, RGBQUAD* prgbColors) const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Returns address of internal color table
|
||||
/// @return Address of color table
|
||||
const RGBQUAD* GetColorTable() const { return m_postHeader.clut; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return the PGF header structure.
|
||||
/// @return A PGF header structure
|
||||
const PGFHeader* GetHeader() const { return &m_header; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Get maximum intensity value for image modes with more than eight bits per channel.
|
||||
/// Don't call this method before the PGF header has been read.
|
||||
/// @return The maximum intensity value.
|
||||
UINT32 GetMaxValue() const { return (1 << m_header.usedBitsPerChannel) - 1; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return the stream position of the user data or 0.
|
||||
/// Precondition: The PGF image has been opened with a call of Open(...).
|
||||
UINT64 GetUserDataPos() const { return m_userDataPos; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return user data and size of user data.
|
||||
/// Precondition: The PGF image has been opened with a call of Open(...).
|
||||
/// @param cachedSize [out] Size of returned user data in bytes.
|
||||
/// @param pTotalSize [optional out] Pointer to return the size of user data stored in image header in bytes.
|
||||
/// @return A pointer to user data or nullptr if there is no user data available.
|
||||
const UINT8* GetUserData(UINT32& cachedSize, UINT32* pTotalSize = nullptr) const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return the length of all encoded headers in bytes.
|
||||
/// Precondition: The PGF image has been opened with a call of Open(...).
|
||||
/// @return The length of all encoded headers in bytes
|
||||
UINT32 GetEncodedHeaderLength() const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return the length of an encoded PGF level in bytes.
|
||||
/// Precondition: The PGF image has been opened with a call of Open(...).
|
||||
/// @param level The image level
|
||||
/// @return The length of a PGF level in bytes
|
||||
UINT32 GetEncodedLevelLength(int level) const { ASSERT(level >= 0 && level < m_header.nLevels); return m_levelLength[m_header.nLevels - level - 1]; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Reads the encoded PGF header and copies it to a target buffer.
|
||||
/// Precondition: The PGF image has been opened with a call of Open(...).
|
||||
/// It might throw an IOException.
|
||||
/// @param target The target buffer
|
||||
/// @param targetLen The length of the target buffer in bytes
|
||||
/// @return The number of bytes copied to the target buffer
|
||||
UINT32 ReadEncodedHeader(UINT8* target, UINT32 targetLen) const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Reads the data of an encoded PGF level and copies it to a target buffer
|
||||
/// without decoding.
|
||||
/// Precondition: The PGF image has been opened with a call of Open(...).
|
||||
/// It might throw an IOException.
|
||||
/// @param level The image level
|
||||
/// @param target The target buffer
|
||||
/// @param targetLen The length of the target buffer in bytes
|
||||
/// @return The number of bytes copied to the target buffer
|
||||
UINT32 ReadEncodedData(int level, UINT8* target, UINT32 targetLen) const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return current image width of given channel in pixels.
|
||||
/// The returned width depends on the levels read so far and on ROI.
|
||||
/// @param c A channel index
|
||||
/// @return Channel width in pixels
|
||||
UINT32 ChannelWidth(int c = 0) const { ASSERT(c >= 0 && c < MaxChannels); return m_width[c]; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return current image height of given channel in pixels.
|
||||
/// The returned height depends on the levels read so far and on ROI.
|
||||
/// @param c A channel index
|
||||
/// @return Channel height in pixels
|
||||
UINT32 ChannelHeight(int c = 0) const { ASSERT(c >= 0 && c < MaxChannels); return m_height[c]; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return bits per channel of the image's encoder.
|
||||
/// @return Bits per channel
|
||||
BYTE ChannelDepth() const { return MaxChannelDepth(m_preHeader.version); }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return image width of channel 0 at given level in pixels.
|
||||
/// The returned width is independent of any Read-operations and ROI.
|
||||
/// @param level A level
|
||||
/// @return Image level width in pixels
|
||||
UINT32 Width(int level = 0) const { ASSERT(level >= 0); return LevelSizeL(m_header.width, level); }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return image height of channel 0 at given level in pixels.
|
||||
/// The returned height is independent of any Read-operations and ROI.
|
||||
/// @param level A level
|
||||
/// @return Image level height in pixels
|
||||
UINT32 Height(int level = 0) const { ASSERT(level >= 0); return LevelSizeL(m_header.height, level); }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return current image level.
|
||||
/// Since Read(...) can be used to read each image level separately, it is
|
||||
/// helpful to know the current level. The current level immediately after Open(...) is Levels().
|
||||
/// @return Current image level
|
||||
BYTE Level() const { return (BYTE)m_currentLevel; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return the number of image levels.
|
||||
/// @return Number of image levels
|
||||
BYTE Levels() const { return m_header.nLevels; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return true if all levels have been read
|
||||
bool IsFullyRead() const { return m_currentLevel == 0; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return the PGF quality. The quality is inbetween 0 and MaxQuality.
|
||||
/// PGF quality 0 means lossless quality.
|
||||
/// @return PGF quality
|
||||
BYTE Quality() const { return m_header.quality; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return the number of image channels.
|
||||
/// An image of type RGB contains 3 image channels (B, G, R).
|
||||
/// @return Number of image channels
|
||||
BYTE Channels() const { return m_header.channels; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return the image mode.
|
||||
/// An image mode is a predefined constant value (see also PGFtypes.h) compatible with Adobe Photoshop.
|
||||
/// It represents an image type and format.
|
||||
/// @return Image mode
|
||||
BYTE Mode() const { return m_header.mode; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return the number of bits per pixel.
|
||||
/// Valid values can be 1, 8, 12, 16, 24, 32, 48, 64.
|
||||
/// @return Number of bits per pixel.
|
||||
BYTE BPP() const { return m_header.bpp; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return true if the pgf image supports Region Of Interest (ROI).
|
||||
/// @return true if the pgf image supports ROI.
|
||||
bool ROIisSupported() const { return (m_preHeader.version & PGFROI) == PGFROI; }
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
/// Return ROI of channel 0 at current level in pixels.
|
||||
/// The returned rect is only valid after reading a ROI.
|
||||
/// @return ROI in pixels
|
||||
PGFRect ComputeLevelROI() const;
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Returns number of used bits per input/output image channel.
|
||||
/// Precondition: header must be initialized.
|
||||
/// @return number of used bits per input/output image channel.
|
||||
BYTE UsedBitsPerChannel() const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Returns the used codec major version of a pgf image
|
||||
/// @return PGF codec major version of this image
|
||||
BYTE Version() const { BYTE ver = CodecMajorVersion(m_preHeader.version); return (ver <= 7) ? ver : (BYTE)m_header.version.major; }
|
||||
|
||||
//class methods
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Check for valid import image mode.
|
||||
/// @param mode Image mode
|
||||
/// @return True if an image of given mode can be imported with ImportBitmap(...)
|
||||
static bool ImportIsSupported(BYTE mode);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Compute and return image width/height of LL subband at given level.
|
||||
/// @param size Original image size (e.g. width or height at level 0)
|
||||
/// @param level An image level
|
||||
/// @return Image width/height at given level in pixels
|
||||
static UINT32 LevelSizeL(UINT32 size, int level) { ASSERT(level >= 0); UINT32 d = 1 << level; return (size + d - 1) >> level; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Compute and return image width/height of HH subband at given level.
|
||||
/// @param size Original image size (e.g. width or height at level 0)
|
||||
/// @param level An image level
|
||||
/// @return high pass size at given level in pixels
|
||||
static UINT32 LevelSizeH(UINT32 size, int level) { ASSERT(level >= 0); UINT32 d = 1 << (level - 1); return (size + d - 1) >> level; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return codec major version.
|
||||
/// @param version pgf pre-header version number
|
||||
/// @return PGF major of given version
|
||||
static BYTE CodecMajorVersion(BYTE version = PGFVersion);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return maximum channel depth.
|
||||
/// @param version pgf pre-header version number
|
||||
/// @return maximum channel depth in bit of given version (16 or 32 bit)
|
||||
static BYTE MaxChannelDepth(BYTE version = PGFVersion) { return (version & PGF32) ? 32 : 16; }
|
||||
|
||||
protected:
|
||||
CWaveletTransform* m_wtChannel[MaxChannels]; ///< wavelet transformed color channels
|
||||
DataT* m_channel[MaxChannels]; ///< untransformed channels in YUV format
|
||||
CDecoder* m_decoder; ///< PGF decoder
|
||||
CEncoder* m_encoder; ///< PGF encoder
|
||||
UINT32* m_levelLength; ///< length of each level in bytes; first level starts immediately after this array
|
||||
UINT32 m_width[MaxChannels]; ///< width of each channel at current level
|
||||
UINT32 m_height[MaxChannels]; ///< height of each channel at current level
|
||||
PGFPreHeader m_preHeader; ///< PGF pre-header
|
||||
PGFHeader m_header; ///< PGF file header
|
||||
PGFPostHeader m_postHeader; ///< PGF post-header
|
||||
UINT64 m_userDataPos; ///< stream position of user data
|
||||
int m_currentLevel; ///< transform level of current image
|
||||
UINT32 m_userDataPolicy; ///< user data (metadata) policy during open
|
||||
BYTE m_quant; ///< quantization parameter
|
||||
bool m_downsample; ///< chrominance channels are downsampled
|
||||
bool m_favorSpeedOverSize; ///< favor encoding speed over compression ratio
|
||||
bool m_useOMPinEncoder; ///< use Open MP in encoder
|
||||
bool m_useOMPinDecoder; ///< use Open MP in decoder
|
||||
#ifdef __PGFROISUPPORT__
|
||||
bool m_streamReinitialized; ///< stream has been reinitialized
|
||||
PGFRect m_roi; ///< region of interest
|
||||
#endif
|
||||
|
||||
private:
|
||||
RefreshCB m_cb; ///< pointer to refresh callback procedure
|
||||
void *m_cbArg; ///< refresh callback argument
|
||||
double m_percent; ///< progress [0..1]
|
||||
ProgressMode m_progressMode; ///< progress mode used in Read and Write; PM_Relative is default mode
|
||||
|
||||
void Init();
|
||||
void ComputeLevels();
|
||||
bool CompleteHeader();
|
||||
void RgbToYuv(int pitch, UINT8* rgbBuff, BYTE bpp, int channelMap[], CallbackPtr cb, void *data);
|
||||
void Downsample(int nChannel);
|
||||
UINT32 UpdatePostHeaderSize();
|
||||
void WriteLevel();
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
PGFRect GetAlignedROI(int c = 0) const;
|
||||
void SetROI(PGFRect rect);
|
||||
#endif
|
||||
|
||||
UINT8 Clamp4(DataT v) const {
|
||||
if (v & 0xFFFFFFF0) return (v < 0) ? (UINT8)0: (UINT8)15; else return (UINT8)v;
|
||||
}
|
||||
UINT16 Clamp6(DataT v) const {
|
||||
if (v & 0xFFFFFFC0) return (v < 0) ? (UINT16)0: (UINT16)63; else return (UINT16)v;
|
||||
}
|
||||
UINT8 Clamp8(DataT v) const {
|
||||
// needs only one test in the normal case
|
||||
if (v & 0xFFFFFF00) return (v < 0) ? (UINT8)0 : (UINT8)255; else return (UINT8)v;
|
||||
}
|
||||
UINT16 Clamp16(DataT v) const {
|
||||
if (v & 0xFFFF0000) return (v < 0) ? (UINT16)0: (UINT16)65535; else return (UINT16)v;
|
||||
}
|
||||
UINT32 Clamp31(DataT v) const {
|
||||
return (v < 0) ? 0 : (UINT32)v;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //PGF_PGFIMAGE_H
|
||||
@@ -0,0 +1,633 @@
|
||||
/*
|
||||
* The Progressive Graphics File; http://www.libpgf.org
|
||||
*
|
||||
* $Date: 2007-06-12 19:27:47 +0200 (Di, 12 Jun 2007) $
|
||||
* $Revision: 307 $
|
||||
*
|
||||
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
* as published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// @file PGFplatform.h
|
||||
/// @brief PGF platform specific definitions
|
||||
/// @author C. Stamm
|
||||
|
||||
#ifndef PGF_PGFPLATFORM_H
|
||||
#define PGF_PGFPLATFORM_H
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Endianess detection taken from lcms2 header.
|
||||
// This list can be endless, so only some checks are performed over here.
|
||||
//-------------------------------------------------------------------------------
|
||||
#if defined(_HOST_BIG_ENDIAN) || defined(__BIG_ENDIAN__) || defined(WORDS_BIGENDIAN)
|
||||
#define PGF_USE_BIG_ENDIAN 1
|
||||
#endif
|
||||
|
||||
#if defined(__sgi__) || defined(__sgi) || defined(__powerpc__) || defined(__sparc) || defined(__sparc__)
|
||||
#define PGF_USE_BIG_ENDIAN 1
|
||||
#endif
|
||||
|
||||
#if defined(__ppc__) || defined(__s390__) || defined(__s390x__)
|
||||
#define PGF_USE_BIG_ENDIAN 1
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_CPU_PPC
|
||||
#define PGF_USE_BIG_ENDIAN 1
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// ROI support
|
||||
//-------------------------------------------------------------------------------
|
||||
#ifndef NPGFROI
|
||||
#define __PGFROISUPPORT__ // without ROI support the program code gets simpler and smaller
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// 32 bit per channel support
|
||||
//-------------------------------------------------------------------------------
|
||||
#ifndef NPGF32
|
||||
#define __PGF32SUPPORT__ // without 32 bit the memory consumption during encoding and decoding is much lesser
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// 32 Bit platform constants
|
||||
//-------------------------------------------------------------------------------
|
||||
#define WordWidth 32 ///< WordBytes*8
|
||||
#define WordWidthLog 5 ///< ld of WordWidth
|
||||
#define WordMask 0xFFFFFFE0 ///< least WordWidthLog bits are zero
|
||||
#define WordBytes 4 ///< sizeof(UINT32)
|
||||
#define WordBytesMask 0xFFFFFFFC ///< least WordBytesLog bits are zero
|
||||
#define WordBytesLog 2 ///< ld of WordBytes
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Alignment macros (used in PGF based libraries)
|
||||
//-------------------------------------------------------------------------------
|
||||
#define DWWIDTHBITS(bits) (((bits) + WordWidth - 1) & WordMask) ///< aligns scanline width in bits to DWORD value
|
||||
#define DWWIDTH(bytes) (((bytes) + WordBytes - 1) & WordBytesMask) ///< aligns scanline width in bytes to DWORD value
|
||||
#define DWWIDTHREST(bytes) ((WordBytes - (bytes)%WordBytes)%WordBytes) ///< DWWIDTH(bytes) - bytes
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Min-Max macros
|
||||
//-------------------------------------------------------------------------------
|
||||
#ifndef __min
|
||||
#define __min(x, y) ((x) <= (y) ? (x) : (y))
|
||||
#define __max(x, y) ((x) >= (y) ? (x) : (y))
|
||||
#endif // __min
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Defines -- Adobe image modes.
|
||||
//-------------------------------------------------------------------------------
|
||||
#define ImageModeBitmap 0
|
||||
#define ImageModeGrayScale 1
|
||||
#define ImageModeIndexedColor 2
|
||||
#define ImageModeRGBColor 3
|
||||
#define ImageModeCMYKColor 4
|
||||
#define ImageModeHSLColor 5
|
||||
#define ImageModeHSBColor 6
|
||||
#define ImageModeMultichannel 7
|
||||
#define ImageModeDuotone 8
|
||||
#define ImageModeLabColor 9
|
||||
#define ImageModeGray16 10 // 565
|
||||
#define ImageModeRGB48 11
|
||||
#define ImageModeLab48 12
|
||||
#define ImageModeCMYK64 13
|
||||
#define ImageModeDeepMultichannel 14
|
||||
#define ImageModeDuotone16 15
|
||||
// pgf extension
|
||||
#define ImageModeRGBA 17
|
||||
#define ImageModeGray32 18 // MSB is 0 (can be interpreted as signed 15.16 fixed point format)
|
||||
#define ImageModeRGB12 19
|
||||
#define ImageModeRGB16 20
|
||||
#define ImageModeUnknown 255
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// WINDOWS
|
||||
//-------------------------------------------------------------------------------
|
||||
#if defined(WIN32) || defined(WINCE) || defined(WIN64) || defined(__MINGW32__)
|
||||
#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// MFC
|
||||
//-------------------------------------------------------------------------------
|
||||
#ifdef _MFC_VER
|
||||
#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista.
|
||||
#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows.
|
||||
#endif
|
||||
#include <afx.h>
|
||||
#include <afxwin.h> // MFC core and standard components
|
||||
#include <afxext.h> // MFC extensions
|
||||
#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
|
||||
#ifndef _AFX_NO_AFXCMN_SUPPORT
|
||||
#include <afxcmn.h> // MFC support for Windows Common Controls
|
||||
#endif // _AFX_NO_AFXCMN_SUPPORT
|
||||
|
||||
#else
|
||||
|
||||
#include <windows.h>
|
||||
#include <ole2.h>
|
||||
|
||||
#endif // _MFC_VER
|
||||
//-------------------------------------------------------------------------------
|
||||
|
||||
#define DllExport __declspec( dllexport )
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// unsigned number type definitions
|
||||
//-------------------------------------------------------------------------------
|
||||
typedef unsigned char UINT8;
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short UINT16;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned int UINT32;
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned long ULONG;
|
||||
typedef unsigned __int64 UINT64;
|
||||
typedef unsigned __int64 ULONGLONG;
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// signed number type definitions
|
||||
//-------------------------------------------------------------------------------
|
||||
typedef signed char INT8;
|
||||
typedef signed short INT16;
|
||||
typedef signed int INT32;
|
||||
typedef signed int BOOL;
|
||||
typedef signed long LONG;
|
||||
typedef signed __int64 INT64;
|
||||
typedef signed __int64 LONGLONG;
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// other types
|
||||
//-------------------------------------------------------------------------------
|
||||
typedef int OSError;
|
||||
typedef bool (__cdecl *CallbackPtr)(double percent, bool escapeAllowed, void *data);
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// struct type definitions
|
||||
//-------------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// DEBUG macros
|
||||
//-------------------------------------------------------------------------------
|
||||
#ifndef ASSERT
|
||||
#ifdef _DEBUG
|
||||
#define ASSERT(x) assert(x)
|
||||
#else
|
||||
#if defined(__GNUC__)
|
||||
#define ASSERT(ignore)((void) 0)
|
||||
#elif _MSC_VER >= 1300
|
||||
#define ASSERT __noop
|
||||
#else
|
||||
#define ASSERT ((void)0)
|
||||
#endif
|
||||
#endif //_DEBUG
|
||||
#endif //ASSERT
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Exception handling macros
|
||||
//-------------------------------------------------------------------------------
|
||||
#ifdef NEXCEPTIONS
|
||||
extern OSError _PGF_Error_;
|
||||
extern OSError GetLastPGFError();
|
||||
|
||||
#define ReturnWithError(err) { _PGF_Error_ = err; return; }
|
||||
#define ReturnWithError2(err, ret) { _PGF_Error_ = err; return ret; }
|
||||
#else
|
||||
#define ReturnWithError(err) throw IOException(err)
|
||||
#define ReturnWithError2(err, ret) throw IOException(err)
|
||||
#endif //NEXCEPTIONS
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// constants
|
||||
//-------------------------------------------------------------------------------
|
||||
#define FSFromStart FILE_BEGIN // 0
|
||||
#define FSFromCurrent FILE_CURRENT // 1
|
||||
#define FSFromEnd FILE_END // 2
|
||||
|
||||
#define INVALID_SET_FILE_POINTER ((DWORD)-1)
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// IO Error constants
|
||||
//-------------------------------------------------------------------------------
|
||||
#define NoError ERROR_SUCCESS ///< no error
|
||||
#define AppError 0x20000000 ///< all application error messages must be larger than this value
|
||||
#define InsufficientMemory 0x20000001 ///< memory allocation was not successfull
|
||||
#define InvalidStreamPos 0x20000002 ///< invalid memory stream position
|
||||
#define EscapePressed 0x20000003 ///< user break by ESC
|
||||
#define WrongVersion 0x20000004 ///< wrong PGF version
|
||||
#define FormatCannotRead 0x20000005 ///< wrong data file format
|
||||
#define ImageTooSmall 0x20000006 ///< image is too small
|
||||
#define ZlibError 0x20000007 ///< error in zlib functions
|
||||
#define ColorTableError 0x20000008 ///< errors related to color table size
|
||||
#define PNGError 0x20000009 ///< errors in png functions
|
||||
#define MissingData 0x2000000A ///< expected data cannot be read
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// methods
|
||||
//-------------------------------------------------------------------------------
|
||||
inline OSError FileRead(HANDLE hFile, int *count, void *buffPtr) {
|
||||
if (ReadFile(hFile, buffPtr, *count, (ULONG *)count, nullptr)) {
|
||||
return NoError;
|
||||
} else {
|
||||
return GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
inline OSError FileWrite(HANDLE hFile, int *count, void *buffPtr) {
|
||||
if (WriteFile(hFile, buffPtr, *count, (ULONG *)count, nullptr)) {
|
||||
return NoError;
|
||||
} else {
|
||||
return GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
inline OSError GetFPos(HANDLE hFile, UINT64 *pos) {
|
||||
#ifdef WINCE
|
||||
LARGE_INTEGER li;
|
||||
li.QuadPart = 0;
|
||||
|
||||
li.LowPart = SetFilePointer (hFile, li.LowPart, &li.HighPart, FILE_CURRENT);
|
||||
if (li.LowPart == INVALID_SET_FILE_POINTER) {
|
||||
OSError err = GetLastError();
|
||||
if (err != NoError) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
*pos = li.QuadPart;
|
||||
return NoError;
|
||||
#else
|
||||
LARGE_INTEGER li;
|
||||
li.QuadPart = 0;
|
||||
if (SetFilePointerEx(hFile, li, (PLARGE_INTEGER)pos, FILE_CURRENT)) {
|
||||
return NoError;
|
||||
} else {
|
||||
return GetLastError();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
inline OSError SetFPos(HANDLE hFile, int posMode, INT64 posOff) {
|
||||
#ifdef WINCE
|
||||
LARGE_INTEGER li;
|
||||
li.QuadPart = posOff;
|
||||
|
||||
if (SetFilePointer (hFile, li.LowPart, &li.HighPart, posMode) == INVALID_SET_FILE_POINTER) {
|
||||
OSError err = GetLastError();
|
||||
if (err != NoError) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return NoError;
|
||||
#else
|
||||
LARGE_INTEGER li;
|
||||
li.QuadPart = posOff;
|
||||
if (SetFilePointerEx(hFile, li, nullptr, posMode)) {
|
||||
return NoError;
|
||||
} else {
|
||||
return GetLastError();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif //WIN32
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Apple OSX
|
||||
//-------------------------------------------------------------------------------
|
||||
#ifdef __APPLE__
|
||||
#define __POSIX__
|
||||
#endif // __APPLE__
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// LINUX
|
||||
//-------------------------------------------------------------------------------
|
||||
#if defined(__linux__) || defined(__GLIBC__)
|
||||
#define __POSIX__
|
||||
#endif // __linux__ or __GLIBC__
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// SOLARIS
|
||||
//-------------------------------------------------------------------------------
|
||||
#ifdef __sun
|
||||
#define __POSIX__
|
||||
#endif // __sun
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// *BSD
|
||||
//-------------------------------------------------------------------------------
|
||||
#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
|
||||
#ifndef __POSIX__
|
||||
#define __POSIX__
|
||||
#endif
|
||||
|
||||
#ifndef off64_t
|
||||
#define off64_t off_t
|
||||
#endif
|
||||
|
||||
#ifndef lseek64
|
||||
#define lseek64 lseek
|
||||
#endif
|
||||
|
||||
#endif // __NetBSD__ or __OpenBSD__ or __FreeBSD__
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// POSIX *NIXes
|
||||
//-------------------------------------------------------------------------------
|
||||
|
||||
#ifdef __POSIX__
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h> // for int64_t and uint64_t
|
||||
#include <string.h> // memcpy()
|
||||
|
||||
#undef major
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// unsigned number type definitions
|
||||
//-------------------------------------------------------------------------------
|
||||
|
||||
typedef unsigned char UINT8;
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short UINT16;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned int UINT32;
|
||||
typedef unsigned int DWORD;
|
||||
typedef unsigned long ULONG;
|
||||
typedef unsigned long long __Uint64;
|
||||
typedef __Uint64 UINT64;
|
||||
typedef __Uint64 ULONGLONG;
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// signed number type definitions
|
||||
//-------------------------------------------------------------------------------
|
||||
typedef signed char INT8;
|
||||
typedef signed short INT16;
|
||||
typedef signed int INT32;
|
||||
typedef signed int BOOL;
|
||||
typedef signed long LONG;
|
||||
typedef int64_t INT64;
|
||||
typedef int64_t LONGLONG;
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// other types
|
||||
//-------------------------------------------------------------------------------
|
||||
typedef int OSError;
|
||||
typedef int HANDLE;
|
||||
typedef unsigned long ULONG_PTR;
|
||||
typedef void* PVOID;
|
||||
typedef char* LPTSTR;
|
||||
typedef bool (*CallbackPtr)(double percent, bool escapeAllowed, void *data);
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// struct type definitions
|
||||
//-------------------------------------------------------------------------------
|
||||
typedef struct tagRGBTRIPLE {
|
||||
BYTE rgbtBlue;
|
||||
BYTE rgbtGreen;
|
||||
BYTE rgbtRed;
|
||||
} RGBTRIPLE;
|
||||
|
||||
typedef struct tagRGBQUAD {
|
||||
BYTE rgbBlue;
|
||||
BYTE rgbGreen;
|
||||
BYTE rgbRed;
|
||||
BYTE rgbReserved;
|
||||
} RGBQUAD;
|
||||
|
||||
typedef union _LARGE_INTEGER {
|
||||
struct {
|
||||
DWORD LowPart;
|
||||
LONG HighPart;
|
||||
} u;
|
||||
LONGLONG QuadPart;
|
||||
} LARGE_INTEGER, *PLARGE_INTEGER;
|
||||
#endif // __POSIX__
|
||||
|
||||
|
||||
#if defined(__POSIX__) || defined(WINCE)
|
||||
// CMYK macros
|
||||
#define GetKValue(cmyk) ((BYTE)(cmyk))
|
||||
#define GetYValue(cmyk) ((BYTE)((cmyk)>> 8))
|
||||
#define GetMValue(cmyk) ((BYTE)((cmyk)>>16))
|
||||
#define GetCValue(cmyk) ((BYTE)((cmyk)>>24))
|
||||
#define CMYK(c,m,y,k) ((COLORREF)((((BYTE)(k)|((WORD)((BYTE)(y))<<8))|(((DWORD)(BYTE)(m))<<16))|(((DWORD)(BYTE)(c))<<24)))
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// methods
|
||||
//-------------------------------------------------------------------------------
|
||||
/* The MulDiv function multiplies two 32-bit values and then divides the 64-bit
|
||||
* result by a third 32-bit value. The return value is rounded up or down to
|
||||
* the nearest integer.
|
||||
* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winprog/winprog/muldiv.asp
|
||||
* */
|
||||
__inline int MulDiv(int nNumber, int nNumerator, int nDenominator) {
|
||||
INT64 multRes = nNumber*nNumerator;
|
||||
INT32 divRes = INT32(multRes/nDenominator);
|
||||
return divRes;
|
||||
}
|
||||
#endif // __POSIX__ or WINCE
|
||||
|
||||
|
||||
#ifdef __POSIX__
|
||||
//-------------------------------------------------------------------------------
|
||||
// DEBUG macros
|
||||
//-------------------------------------------------------------------------------
|
||||
#ifndef ASSERT
|
||||
#ifdef _DEBUG
|
||||
#define ASSERT(x) assert(x)
|
||||
#else
|
||||
#define ASSERT(x)
|
||||
#endif //_DEBUG
|
||||
#endif //ASSERT
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Exception handling macros
|
||||
//-------------------------------------------------------------------------------
|
||||
#ifdef NEXCEPTIONS
|
||||
extern OSError _PGF_Error_;
|
||||
extern OSError GetLastPGFError();
|
||||
|
||||
#define ReturnWithError(err) { _PGF_Error_ = err; return; }
|
||||
#define ReturnWithError2(err, ret) { _PGF_Error_ = err; return ret; }
|
||||
#else
|
||||
#define ReturnWithError(err) throw IOException(err)
|
||||
#define ReturnWithError2(err, ret) throw IOException(err)
|
||||
#endif //NEXCEPTIONS
|
||||
|
||||
#define THROW_ throw(IOException)
|
||||
#define CONST const
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// constants
|
||||
//-------------------------------------------------------------------------------
|
||||
#define FSFromStart SEEK_SET
|
||||
#define FSFromCurrent SEEK_CUR
|
||||
#define FSFromEnd SEEK_END
|
||||
#if defined(__cplusplus) && __cplusplus < 201103L
|
||||
#define nullptr NULL
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// IO Error constants
|
||||
//-------------------------------------------------------------------------------
|
||||
#define NoError 0x0000 ///< no error
|
||||
#define AppError 0x2000 ///< all application error messages must be larger than this value
|
||||
#define InsufficientMemory 0x2001 ///< memory allocation wasn't successfull
|
||||
#define InvalidStreamPos 0x2002 ///< invalid memory stream position
|
||||
#define EscapePressed 0x2003 ///< user break by ESC
|
||||
#define WrongVersion 0x2004 ///< wrong pgf version
|
||||
#define FormatCannotRead 0x2005 ///< wrong data file format
|
||||
#define ImageTooSmall 0x2006 ///< image is too small
|
||||
#define ZlibError 0x2007 ///< error in zlib functions
|
||||
#define ColorTableError 0x2008 ///< errors related to color table size
|
||||
#define PNGError 0x2009 ///< errors in png functions
|
||||
#define MissingData 0x200A ///< expected data cannot be read
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// methods
|
||||
//-------------------------------------------------------------------------------
|
||||
__inline OSError FileRead(HANDLE hFile, int *count, void *buffPtr) {
|
||||
*count = (int)read(hFile, buffPtr, *count);
|
||||
if (*count != -1) {
|
||||
return NoError;
|
||||
} else {
|
||||
return errno;
|
||||
}
|
||||
}
|
||||
|
||||
__inline OSError FileWrite(HANDLE hFile, int *count, void *buffPtr) {
|
||||
*count = (int)write(hFile, buffPtr, (size_t)*count);
|
||||
if (*count != -1) {
|
||||
return NoError;
|
||||
} else {
|
||||
return errno;
|
||||
}
|
||||
}
|
||||
|
||||
__inline OSError GetFPos(HANDLE hFile, UINT64 *pos) {
|
||||
#ifdef __APPLE__
|
||||
off_t ret;
|
||||
if ((ret = lseek(hFile, 0, SEEK_CUR)) == -1) {
|
||||
return errno;
|
||||
} else {
|
||||
*pos = (UINT64)ret;
|
||||
return NoError;
|
||||
}
|
||||
#else
|
||||
off64_t ret;
|
||||
if ((ret = lseek64(hFile, 0, SEEK_CUR)) == -1) {
|
||||
return errno;
|
||||
} else {
|
||||
*pos = (UINT64)ret;
|
||||
return NoError;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
__inline OSError SetFPos(HANDLE hFile, int posMode, INT64 posOff) {
|
||||
#ifdef __APPLE__
|
||||
if ((lseek(hFile, (off_t)posOff, posMode)) == -1) {
|
||||
return errno;
|
||||
} else {
|
||||
return NoError;
|
||||
}
|
||||
#else
|
||||
if ((lseek64(hFile, (off64_t)posOff, posMode)) == -1) {
|
||||
return errno;
|
||||
} else {
|
||||
return NoError;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* __POSIX__ */
|
||||
//-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Big Endian
|
||||
//-------------------------------------------------------------------------------
|
||||
#ifdef PGF_USE_BIG_ENDIAN
|
||||
|
||||
#ifndef _lrotl
|
||||
#define _lrotl(x,n) (((x) << ((UINT32)(n))) | ((x) >> (32 - (UINT32)(n))))
|
||||
#endif
|
||||
|
||||
__inline UINT16 ByteSwap(UINT16 wX) {
|
||||
return ((wX & 0xFF00) >> 8) | ((wX & 0x00FF) << 8);
|
||||
}
|
||||
|
||||
__inline UINT32 ByteSwap(UINT32 dwX) {
|
||||
#ifdef _X86_
|
||||
_asm mov eax, dwX
|
||||
_asm bswap eax
|
||||
_asm mov dwX, eax
|
||||
return dwX;
|
||||
#else
|
||||
return _lrotl(((dwX & 0xFF00FF00) >> 8) | ((dwX & 0x00FF00FF) << 8), 16);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
__inline UINT64 ByteSwap(UINT64 ui64) {
|
||||
return _byteswap_uint64(ui64);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define __VAL(x) ByteSwap(x)
|
||||
|
||||
#else //PGF_USE_BIG_ENDIAN
|
||||
|
||||
#define __VAL(x) (x)
|
||||
|
||||
#endif //PGF_USE_BIG_ENDIAN
|
||||
|
||||
// OpenMP rules (inspired from libraw project)
|
||||
// NOTE: Use LIBPGF_DISABLE_OPENMP to disable OpenMP support in whole libpgf
|
||||
#ifndef LIBPGF_DISABLE_OPENMP
|
||||
# if defined (_OPENMP)
|
||||
# if defined (WIN32) || defined(WIN64)
|
||||
# if defined (_MSC_VER) && (_MSC_VER >= 1500)
|
||||
// VS2008 SP1 and VS2010+ : OpenMP works OK
|
||||
# define LIBPGF_USE_OPENMP
|
||||
# elif defined (__INTEL_COMPILER) && (__INTEL_COMPILER >=910)
|
||||
// untested on 9.x and 10.x, Intel documentation claims OpenMP 2.5 support in 9.1
|
||||
# define LIBPGF_USE_OPENMP
|
||||
# else
|
||||
# undef LIBPGF_USE_OPENMP
|
||||
# endif
|
||||
// Not Win32
|
||||
# elif (defined(__APPLE__) || defined(__MACOSX__)) && defined(_REENTRANT)
|
||||
# undef LIBPGF_USE_OPENMP
|
||||
# else
|
||||
# define LIBPGF_USE_OPENMP
|
||||
# endif
|
||||
# endif // defined (_OPENMP)
|
||||
#endif // ifndef LIBPGF_DISABLE_OPENMP
|
||||
#ifdef LIBPGF_USE_OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
#endif //PGF_PGFPLATFORM_H
|
||||
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* The Progressive Graphics File; http://www.libpgf.org
|
||||
*
|
||||
* $Date: 2007-01-19 11:51:24 +0100 (Fr, 19 Jan 2007) $
|
||||
* $Revision: 268 $
|
||||
*
|
||||
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
* as published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// @file PGFstream.cpp
|
||||
/// @brief PGF stream class implementation
|
||||
/// @author C. Stamm
|
||||
|
||||
#include "PGFstream.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// CPGFFileStream
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void CPGFFileStream::Write(int *count, void *buffPtr) {
|
||||
ASSERT(count);
|
||||
ASSERT(buffPtr);
|
||||
ASSERT(IsValid());
|
||||
OSError err;
|
||||
if ((err = FileWrite(m_hFile, count, buffPtr)) != NoError) ReturnWithError(err);
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void CPGFFileStream::Read(int *count, void *buffPtr) {
|
||||
ASSERT(count);
|
||||
ASSERT(buffPtr);
|
||||
ASSERT(IsValid());
|
||||
OSError err;
|
||||
if ((err = FileRead(m_hFile, count, buffPtr)) != NoError) ReturnWithError(err);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void CPGFFileStream::SetPos(short posMode, INT64 posOff) {
|
||||
ASSERT(IsValid());
|
||||
OSError err;
|
||||
if ((err = SetFPos(m_hFile, posMode, posOff)) != NoError) ReturnWithError(err);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
UINT64 CPGFFileStream::GetPos() const {
|
||||
ASSERT(IsValid());
|
||||
OSError err;
|
||||
UINT64 pos = 0;
|
||||
if ((err = GetFPos(m_hFile, &pos)) != NoError) ReturnWithError2(err, pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// CPGFMemoryStream
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Allocate memory block of given size
|
||||
/// @param size Memory size
|
||||
CPGFMemoryStream::CPGFMemoryStream(size_t size)
|
||||
: m_size(size)
|
||||
, m_allocated(true) {
|
||||
m_buffer = m_pos = m_eos = new(std::nothrow) UINT8[m_size];
|
||||
if (!m_buffer) ReturnWithError(InsufficientMemory);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Use already allocated memory of given size
|
||||
/// @param pBuffer Memory location
|
||||
/// @param size Memory size
|
||||
CPGFMemoryStream::CPGFMemoryStream(UINT8 *pBuffer, size_t size)
|
||||
: m_buffer(pBuffer)
|
||||
, m_pos(pBuffer)
|
||||
, m_eos(pBuffer + size)
|
||||
, m_size(size)
|
||||
, m_allocated(false) {
|
||||
ASSERT(IsValid());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Use already allocated memory of given size
|
||||
/// @param pBuffer Memory location
|
||||
/// @param size Memory size
|
||||
void CPGFMemoryStream::Reinitialize(UINT8 *pBuffer, size_t size) {
|
||||
if (!m_allocated) {
|
||||
m_buffer = m_pos = pBuffer;
|
||||
m_size = size;
|
||||
m_eos = m_buffer + size;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void CPGFMemoryStream::Write(int *count, void *buffPtr) {
|
||||
ASSERT(count);
|
||||
ASSERT(buffPtr);
|
||||
ASSERT(IsValid());
|
||||
const size_t deltaSize = 0x4000 + *count;
|
||||
|
||||
if (m_pos + *count <= m_buffer + m_size) {
|
||||
memcpy(m_pos, buffPtr, *count);
|
||||
m_pos += *count;
|
||||
if (m_pos > m_eos) m_eos = m_pos;
|
||||
} else if (m_allocated) {
|
||||
// memory block is too small -> reallocate a deltaSize larger block
|
||||
size_t offset = m_pos - m_buffer;
|
||||
UINT8 *buf_tmp = (UINT8 *)realloc(m_buffer, m_size + deltaSize);
|
||||
if (!buf_tmp) {
|
||||
delete[] m_buffer;
|
||||
m_buffer = 0;
|
||||
ReturnWithError(InsufficientMemory);
|
||||
} else {
|
||||
m_buffer = buf_tmp;
|
||||
}
|
||||
m_size += deltaSize;
|
||||
|
||||
// reposition m_pos
|
||||
m_pos = m_buffer + offset;
|
||||
|
||||
// write block
|
||||
memcpy(m_pos, buffPtr, *count);
|
||||
m_pos += *count;
|
||||
if (m_pos > m_eos) m_eos = m_pos;
|
||||
} else {
|
||||
ReturnWithError(InsufficientMemory);
|
||||
}
|
||||
ASSERT(m_pos <= m_eos);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void CPGFMemoryStream::Read(int *count, void *buffPtr) {
|
||||
ASSERT(IsValid());
|
||||
ASSERT(count);
|
||||
ASSERT(buffPtr);
|
||||
ASSERT(m_buffer + m_size >= m_eos);
|
||||
ASSERT(m_pos <= m_eos);
|
||||
|
||||
if (m_pos + *count <= m_eos) {
|
||||
memcpy(buffPtr, m_pos, *count);
|
||||
m_pos += *count;
|
||||
} else {
|
||||
// end of memory block reached -> read only until end
|
||||
*count = (int)__max(0, m_eos - m_pos);
|
||||
memcpy(buffPtr, m_pos, *count);
|
||||
m_pos += *count;
|
||||
}
|
||||
ASSERT(m_pos <= m_eos);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void CPGFMemoryStream::SetPos(short posMode, INT64 posOff) {
|
||||
ASSERT(IsValid());
|
||||
switch(posMode) {
|
||||
case FSFromStart:
|
||||
m_pos = m_buffer + posOff;
|
||||
break;
|
||||
case FSFromCurrent:
|
||||
m_pos += posOff;
|
||||
break;
|
||||
case FSFromEnd:
|
||||
m_pos = m_eos + posOff;
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
}
|
||||
if (m_pos > m_eos)
|
||||
ReturnWithError(InvalidStreamPos);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// CPGFMemFileStream
|
||||
#ifdef _MFC_VER
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void CPGFMemFileStream::Write(int *count, void *buffPtr) {
|
||||
ASSERT(count);
|
||||
ASSERT(buffPtr);
|
||||
ASSERT(IsValid());
|
||||
m_memFile->Write(buffPtr, *count);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void CPGFMemFileStream::Read(int *count, void *buffPtr) {
|
||||
ASSERT(count);
|
||||
ASSERT(buffPtr);
|
||||
ASSERT(IsValid());
|
||||
m_memFile->Read(buffPtr, *count);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void CPGFMemFileStream::SetPos(short posMode, INT64 posOff) {
|
||||
ASSERT(IsValid());
|
||||
m_memFile->Seek(posOff, posMode);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
UINT64 CPGFMemFileStream::GetPos() const {
|
||||
return (UINT64)m_memFile->GetPosition();
|
||||
}
|
||||
#endif // _MFC_VER
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// CPGFIStream
|
||||
#if defined(WIN32) || defined(WINCE)
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void CPGFIStream::Write(int *count, void *buffPtr) {
|
||||
ASSERT(count);
|
||||
ASSERT(buffPtr);
|
||||
ASSERT(IsValid());
|
||||
|
||||
HRESULT hr = m_stream->Write(buffPtr, *count, (ULONG *)count);
|
||||
if (FAILED(hr)) {
|
||||
ReturnWithError(hr);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void CPGFIStream::Read(int *count, void *buffPtr) {
|
||||
ASSERT(count);
|
||||
ASSERT(buffPtr);
|
||||
ASSERT(IsValid());
|
||||
|
||||
HRESULT hr = m_stream->Read(buffPtr, *count, (ULONG *)count);
|
||||
if (FAILED(hr)) {
|
||||
ReturnWithError(hr);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void CPGFIStream::SetPos(short posMode, INT64 posOff) {
|
||||
ASSERT(IsValid());
|
||||
|
||||
LARGE_INTEGER li;
|
||||
li.QuadPart = posOff;
|
||||
|
||||
HRESULT hr = m_stream->Seek(li, posMode, nullptr);
|
||||
if (FAILED(hr)) {
|
||||
ReturnWithError(hr);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
UINT64 CPGFIStream::GetPos() const {
|
||||
ASSERT(IsValid());
|
||||
|
||||
LARGE_INTEGER n;
|
||||
ULARGE_INTEGER pos;
|
||||
n.QuadPart = 0;
|
||||
|
||||
HRESULT hr = m_stream->Seek(n, FSFromCurrent, &pos);
|
||||
if (SUCCEEDED(hr)) {
|
||||
return pos.QuadPart;
|
||||
} else {
|
||||
ReturnWithError2(hr, pos.QuadPart);
|
||||
}
|
||||
}
|
||||
#endif // WIN32 || WINCE
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* The Progressive Graphics File; http://www.libpgf.org
|
||||
*
|
||||
* $Date: 2007-06-11 10:56:17 +0200 (Mo, 11 Jun 2007) $
|
||||
* $Revision: 299 $
|
||||
*
|
||||
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
* as published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// @file PGFstream.h
|
||||
/// @brief PGF stream class
|
||||
/// @author C. Stamm
|
||||
|
||||
#ifndef PGF_STREAM_H
|
||||
#define PGF_STREAM_H
|
||||
|
||||
#include "PGFtypes.h"
|
||||
#include <new>
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Abstract stream base class.
|
||||
/// @author C. Stamm
|
||||
/// @brief Abstract stream base class
|
||||
class CPGFStream {
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Standard constructor.
|
||||
CPGFStream() {}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Standard destructor.
|
||||
virtual ~CPGFStream() {}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Write some bytes out of a buffer into this stream.
|
||||
/// @param count A pointer to a value containing the number of bytes should be written. After this call it contains the number of written bytes.
|
||||
/// @param buffer A memory buffer
|
||||
virtual void Write(int *count, void *buffer)=0;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Read some bytes from this stream and stores them into a buffer.
|
||||
/// @param count A pointer to a value containing the number of bytes should be read. After this call it contains the number of read bytes.
|
||||
/// @param buffer A memory buffer
|
||||
virtual void Read(int *count, void *buffer)=0;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Set stream position either absolute or relative.
|
||||
/// @param posMode A position mode (FSFromStart, FSFromCurrent, FSFromEnd)
|
||||
/// @param posOff A new stream position (absolute positioning) or a position offset (relative positioning)
|
||||
virtual void SetPos(short posMode, INT64 posOff)=0;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Get current stream position.
|
||||
/// @return Current stream position
|
||||
virtual UINT64 GetPos() const=0;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Check stream validity.
|
||||
/// @return True if stream and current position is valid
|
||||
virtual bool IsValid() const=0;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// A PGF stream subclass for external storage files.
|
||||
/// @author C. Stamm
|
||||
/// @brief File stream class
|
||||
class CPGFFileStream : public CPGFStream {
|
||||
protected:
|
||||
HANDLE m_hFile; ///< file handle
|
||||
|
||||
public:
|
||||
CPGFFileStream() : m_hFile(0) {}
|
||||
/// Constructor
|
||||
/// @param hFile File handle
|
||||
CPGFFileStream(HANDLE hFile) : m_hFile(hFile) {}
|
||||
/// @return File handle
|
||||
HANDLE GetHandle() { return m_hFile; }
|
||||
|
||||
virtual ~CPGFFileStream() { m_hFile = 0; }
|
||||
virtual void Write(int *count, void *buffer); // throws IOException
|
||||
virtual void Read(int *count, void *buffer); // throws IOException
|
||||
virtual void SetPos(short posMode, INT64 posOff); // throws IOException
|
||||
virtual UINT64 GetPos() const; // throws IOException
|
||||
virtual bool IsValid() const { return m_hFile != 0; }
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// A PGF stream subclass for internal memory.
|
||||
/// @author C. Stamm
|
||||
/// @brief Memory stream class
|
||||
class CPGFMemoryStream : public CPGFStream {
|
||||
protected:
|
||||
UINT8 *m_buffer, *m_pos;///< buffer start address and current buffer address
|
||||
UINT8 *m_eos; ///< end of stream (first address beyond written area)
|
||||
size_t m_size; ///< buffer size
|
||||
bool m_allocated; ///< indicates a new allocated buffer
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param size Size of new allocated memory buffer
|
||||
CPGFMemoryStream(size_t size);
|
||||
|
||||
/// Constructor. Use already allocated memory of given size
|
||||
/// @param pBuffer Memory location
|
||||
/// @param size Memory size
|
||||
CPGFMemoryStream(UINT8 *pBuffer, size_t size);
|
||||
|
||||
/// Use already allocated memory of given size
|
||||
/// @param pBuffer Memory location
|
||||
/// @param size Memory size
|
||||
void Reinitialize(UINT8 *pBuffer, size_t size);
|
||||
|
||||
virtual ~CPGFMemoryStream() {
|
||||
m_pos = 0;
|
||||
if (m_allocated) {
|
||||
// the memory buffer has been allocated inside of CPMFmemoryStream constructor
|
||||
delete[] m_buffer; m_buffer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Write(int *count, void *buffer); // throws IOException
|
||||
virtual void Read(int *count, void *buffer);
|
||||
virtual void SetPos(short posMode, INT64 posOff); // throws IOException
|
||||
virtual UINT64 GetPos() const { ASSERT(IsValid()); return m_pos - m_buffer; }
|
||||
virtual bool IsValid() const { return m_buffer != 0; }
|
||||
|
||||
/// @return Memory size
|
||||
size_t GetSize() const { return m_size; }
|
||||
/// @return Memory buffer
|
||||
const UINT8* GetBuffer() const { return m_buffer; }
|
||||
/// @return Memory buffer
|
||||
UINT8* GetBuffer() { return m_buffer; }
|
||||
/// @return relative position of end of stream (= stream length)
|
||||
UINT64 GetEOS() const { ASSERT(IsValid()); return m_eos - m_buffer; }
|
||||
/// @param length Stream length (= relative position of end of stream)
|
||||
void SetEOS(UINT64 length) { ASSERT(IsValid()); m_eos = m_buffer + length; }
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// A PGF stream subclass for internal memory files. Usable only with MFC.
|
||||
/// @author C. Stamm
|
||||
/// @brief Cached memory file stream class
|
||||
#ifdef _MFC_VER
|
||||
class CPGFMemFileStream : public CPGFStream {
|
||||
protected:
|
||||
CMemFile *m_memFile; ///< MFC memory file
|
||||
public:
|
||||
CPGFMemFileStream(CMemFile *memFile) : m_memFile(memFile) {}
|
||||
virtual bool IsValid() const { return m_memFile != nullptr; }
|
||||
virtual ~CPGFMemFileStream() {}
|
||||
virtual void Write(int *count, void *buffer); // throws IOException
|
||||
virtual void Read(int *count, void *buffer); // throws IOException
|
||||
virtual void SetPos(short posMode, INT64 posOff); // throws IOException
|
||||
virtual UINT64 GetPos() const; // throws IOException
|
||||
};
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// A PGF stream subclass for IStream. Usable only with COM.
|
||||
/// @author C. Stamm
|
||||
/// @brief COM IStream class
|
||||
#if defined(WIN32) || defined(WINCE)
|
||||
class CPGFIStream : public CPGFStream {
|
||||
protected:
|
||||
IStream *m_stream; ///< COM+ IStream
|
||||
public:
|
||||
CPGFIStream(IStream *stream) : m_stream(stream) { m_stream->AddRef(); }
|
||||
virtual bool IsValid() const { return m_stream != 0; }
|
||||
virtual ~CPGFIStream() { m_stream->Release(); }
|
||||
virtual void Write(int *count, void *buffer); // throws IOException
|
||||
virtual void Read(int *count, void *buffer); // throws IOException
|
||||
virtual void SetPos(short posMode, INT64 posOff); // throws IOException
|
||||
virtual UINT64 GetPos() const; // throws IOException
|
||||
IStream* GetIStream() const { return m_stream; }
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // PGF_STREAM_H
|
||||
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* The Progressive Graphics File; http://www.libpgf.org
|
||||
*
|
||||
* $Date: 2007-06-11 10:56:17 +0200 (Mo, 11 Jun 2007) $
|
||||
* $Revision: 299 $
|
||||
*
|
||||
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
* as published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// @file PGFtypes.h
|
||||
/// @brief PGF definitions
|
||||
/// @author C. Stamm
|
||||
|
||||
#ifndef PGF_PGFTYPES_H
|
||||
#define PGF_PGFTYPES_H
|
||||
|
||||
#include "PGFplatform.h"
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Codec versions
|
||||
//
|
||||
// Version 2: modified data structure PGFHeader (backward compatibility assured)
|
||||
// Version 4: DataT: INT32 instead of INT16, allows 31 bit per pixel and channel (backward compatibility assured)
|
||||
// Version 5: ROI, new block-reordering scheme (backward compatibility assured)
|
||||
// Version 6: modified data structure PGFPreHeader: hSize (header size) is now a UINT32 instead of a UINT16 (backward compatibility assured)
|
||||
// Version 7: last two bytes in header are now used for extended version numbers; new data representation for bitmaps (backward compatibility assured)
|
||||
//
|
||||
//-------------------------------------------------------------------------------
|
||||
#define PGFMajorNumber 7
|
||||
#define PGFYear 19
|
||||
#define PGFWeek 03
|
||||
|
||||
#define PPCAT_NX(A, B) A ## B
|
||||
#define PPCAT(A, B) PPCAT_NX(A, B)
|
||||
#define STRINGIZE_NX(A) #A
|
||||
#define STRINGIZE(A) STRINGIZE_NX(A)
|
||||
|
||||
//#define PGFCodecVersionID 0x071822
|
||||
#define PGFCodecVersionID PPCAT(PPCAT(PPCAT(0x0, PGFMajorNumber), PGFYear), PGFWeek)
|
||||
//#define PGFCodecVersion "7.19.3" ///< Major number, Minor number: Year (2) Week (2)
|
||||
#define PGFCodecVersion STRINGIZE(PPCAT(PPCAT(PPCAT(PPCAT(PGFMajorNumber, .), PGFYear), .), PGFWeek))
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Image constants
|
||||
//-------------------------------------------------------------------------------
|
||||
#define PGFMagic "PGF" ///< PGF identification
|
||||
#define MaxLevel 30 ///< maximum number of transform levels
|
||||
#define NSubbands 4 ///< number of subbands per level
|
||||
#define MaxChannels 8 ///< maximum number of (color) channels
|
||||
#define DownsampleThreshold 3 ///< if quality is larger than this threshold than downsampling is used
|
||||
#define ColorTableLen 256 ///< size of color lookup table (clut)
|
||||
// version flags
|
||||
#define Version2 2 ///< data structure PGFHeader of major version 2
|
||||
#define PGF32 4 ///< 32 bit values are used -> allows at maximum 31 bits, otherwise 16 bit values are used -> allows at maximum 15 bits
|
||||
#define PGFROI 8 ///< supports Regions Of Interest
|
||||
#define Version5 16 ///< new coding scheme since major version 5
|
||||
#define Version6 32 ///< hSize in PGFPreHeader uses 32 bits instead of 16 bits
|
||||
#define Version7 64 ///< Codec major and minor version number stored in PGFHeader
|
||||
// version numbers
|
||||
#ifdef __PGF32SUPPORT__
|
||||
#define PGFVersion (Version2 | PGF32 | Version5 | Version6 | Version7) ///< current standard version
|
||||
#else
|
||||
#define PGFVersion (Version2 | Version5 | Version6 | Version7) ///< current standard version
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Coder constants
|
||||
//-------------------------------------------------------------------------------
|
||||
#define BufferSize 16384 ///< must be a multiple of WordWidth, BufferSize <= UINT16_MAX
|
||||
#define RLblockSizeLen 15 ///< block size length (< 16): ld(BufferSize) < RLblockSizeLen <= 2*ld(BufferSize)
|
||||
#define LinBlockSize 8 ///< side length of a coefficient block in a HH or LL subband
|
||||
#define InterBlockSize 4 ///< side length of a coefficient block in a HL or LH subband
|
||||
#ifdef __PGF32SUPPORT__
|
||||
#define MaxBitPlanes 31 ///< maximum number of bit planes of m_value: 32 minus sign bit
|
||||
#else
|
||||
#define MaxBitPlanes 15 ///< maximum number of bit planes of m_value: 16 minus sign bit
|
||||
#endif
|
||||
#define MaxBitPlanesLog 5 ///< number of bits to code the maximum number of bit planes (in 32 or 16 bit mode)
|
||||
#define MaxQuality MaxBitPlanes ///< maximum quality
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Types
|
||||
//-------------------------------------------------------------------------------
|
||||
enum Orientation { LL = 0, HL = 1, LH = 2, HH = 3 };
|
||||
enum ProgressMode { PM_Relative, PM_Absolute };
|
||||
enum UserdataPolicy { UP_Skip = 0, UP_CachePrefix = 1, UP_CacheAll = 2 };
|
||||
|
||||
/// general PGF file structure
|
||||
/// PGFPreHeader PGFHeader [PGFPostHeader] LevelLengths Level_n-1 Level_n-2 ... Level_0
|
||||
/// PGFPostHeader ::= [ColorTable] [UserData]
|
||||
/// LevelLengths ::= UINT32[nLevels]
|
||||
|
||||
#pragma pack(1)
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// PGF magic and version (part of PGF pre-header)
|
||||
/// @author C. Stamm
|
||||
/// @brief PGF identification and version
|
||||
struct PGFMagicVersion {
|
||||
char magic[3]; ///< PGF identification = "PGF"
|
||||
UINT8 version; ///< PGF version
|
||||
// total: 4 Bytes
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// PGF pre-header defined header length and PGF identification and version
|
||||
/// @author C. Stamm
|
||||
/// @brief PGF pre-header
|
||||
struct PGFPreHeader : PGFMagicVersion {
|
||||
UINT32 hSize; ///< total size of PGFHeader, [ColorTable], and [UserData] in bytes (since Version 6: 4 Bytes)
|
||||
// total: 8 Bytes
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Version number since major version 7
|
||||
/// @author C. Stamm
|
||||
/// @brief version number stored in header since major version 7
|
||||
struct PGFVersionNumber {
|
||||
PGFVersionNumber(UINT8 _major, UINT8 _year, UINT8 _week)
|
||||
#ifdef PGF_USE_BIG_ENDIAN
|
||||
: week(_week), year(_year), major(_major) {}
|
||||
#else
|
||||
: major(_major), year(_year), week(_week) {}
|
||||
#endif // PGF_USE_BIG_ENDIAN
|
||||
|
||||
#ifdef PGF_USE_BIG_ENDIAN
|
||||
UINT16 week : 6; ///< week number in a year
|
||||
UINT16 year : 6; ///< year since 2000 (year 2001 = 1)
|
||||
UINT16 major : 4; ///< major version number
|
||||
#else
|
||||
UINT16 major : 4; ///< major version number
|
||||
UINT16 year : 6; ///< year since 2000 (year 2001 = 1)
|
||||
UINT16 week : 6; ///< week number in a year
|
||||
#endif // PGF_USE_BIG_ENDIAN
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// PGF header contains image information
|
||||
/// @author C. Stamm
|
||||
/// @brief PGF header
|
||||
struct PGFHeader {
|
||||
PGFHeader() : width(0), height(0), nLevels(0), quality(0), bpp(0), channels(0), mode(ImageModeUnknown), usedBitsPerChannel(0), version(0, 0, 0) {}
|
||||
UINT32 width; ///< image width in pixels
|
||||
UINT32 height; ///< image height in pixels
|
||||
UINT8 nLevels; ///< number of FWT transforms
|
||||
UINT8 quality; ///< quantization parameter: 0=lossless, 4=standard, 6=poor quality
|
||||
UINT8 bpp; ///< bits per pixel
|
||||
UINT8 channels; ///< number of channels
|
||||
UINT8 mode; ///< image mode according to Adobe's image modes
|
||||
UINT8 usedBitsPerChannel; ///< number of used bits per channel in 16- and 32-bit per channel modes
|
||||
PGFVersionNumber version; ///< codec version number: (since Version 7)
|
||||
// total: 16 Bytes
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// PGF post-header is optional. It contains color table and user data
|
||||
/// @author C. Stamm
|
||||
/// @brief Optional PGF post-header
|
||||
struct PGFPostHeader {
|
||||
RGBQUAD clut[ColorTableLen];///< color table for indexed color images (optional part of file header)
|
||||
UINT8 *userData; ///< user data of size userDataLen (optional part of file header)
|
||||
UINT32 userDataLen; ///< user data size in bytes (not part of file header)
|
||||
UINT32 cachedUserDataLen; ///< cached user data size in bytes (not part of file header)
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// ROI block header is used with ROI coding scheme. It contains block size and tile end flag
|
||||
/// @author C. Stamm
|
||||
/// @brief Block header used with ROI coding scheme
|
||||
union ROIBlockHeader {
|
||||
UINT16 val; ///< unstructured union value
|
||||
/// @brief Named ROI block header (part of the union)
|
||||
struct RBH {
|
||||
#ifdef PGF_USE_BIG_ENDIAN
|
||||
UINT16 tileEnd : 1; ///< 1: last part of a tile
|
||||
UINT16 bufferSize: RLblockSizeLen; ///< number of uncoded UINT32 values in a block
|
||||
#else
|
||||
UINT16 bufferSize: RLblockSizeLen; ///< number of uncoded UINT32 values in a block
|
||||
UINT16 tileEnd : 1; ///< 1: last part of a tile
|
||||
#endif // PGF_USE_BIG_ENDIAN
|
||||
} rbh; ///< ROI block header
|
||||
// total: 2 Bytes
|
||||
|
||||
/// Constructor
|
||||
/// @param v Buffer size
|
||||
ROIBlockHeader(UINT16 v) { val = v; }
|
||||
|
||||
/// Constructor
|
||||
/// @param size Buffer size
|
||||
/// @param end 0/1 Flag; 1: last part of a tile
|
||||
ROIBlockHeader(UINT32 size, bool end) { ASSERT(size < (1 << RLblockSizeLen)); rbh.bufferSize = size; rbh.tileEnd = end; }
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// PGF I/O exception
|
||||
/// @author C. Stamm
|
||||
/// @brief PGF exception
|
||||
struct IOException {
|
||||
OSError error; ///< operating system error code
|
||||
|
||||
/// Standard constructor
|
||||
IOException() : error(NoError) {}
|
||||
|
||||
/// Constructor
|
||||
/// @param err Run-time error
|
||||
IOException(OSError err) : error(err) {}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Rectangle
|
||||
/// @author C. Stamm
|
||||
/// @brief Rectangle
|
||||
struct PGFRect {
|
||||
UINT32 left, top, right, bottom;
|
||||
|
||||
/// Standard constructor
|
||||
PGFRect() : left(0), top(0), right(0), bottom(0) {}
|
||||
|
||||
/// Constructor
|
||||
/// @param x Left offset
|
||||
/// @param y Top offset
|
||||
/// @param width Rectangle width
|
||||
/// @param height Rectangle height
|
||||
PGFRect(UINT32 x, UINT32 y, UINT32 width, UINT32 height) : left(x), top(y), right(x + width), bottom(y + height) {}
|
||||
|
||||
#ifdef WIN32
|
||||
PGFRect(const RECT& rect) : left(rect.left), top(rect.top), right(rect.right), bottom(rect.bottom) {
|
||||
ASSERT(rect.left >= 0 && rect.right >= 0 && rect.left <= rect.right);
|
||||
ASSERT(rect.top >= 0 && rect.bottom >= 0 && rect.top <= rect.bottom);
|
||||
}
|
||||
|
||||
PGFRect& operator=(const RECT& rect) {
|
||||
left = rect.left; top = rect.top; right = rect.right; bottom = rect.bottom;
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator RECT() {
|
||||
RECT rect = { (LONG)left, (LONG)top, (LONG)right, (LONG)bottom };
|
||||
return rect;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @return Rectangle width
|
||||
UINT32 Width() const { return right - left; }
|
||||
|
||||
/// @return Rectangle height
|
||||
UINT32 Height() const { return bottom - top; }
|
||||
|
||||
/// Test if point (x,y) is inside this rectangle (inclusive top-left edges, exclusive bottom-right edges)
|
||||
/// @param x Point coordinate x
|
||||
/// @param y Point coordinate y
|
||||
/// @return True if point (x,y) is inside this rectangle (inclusive top-left edges, exclusive bottom-right edges)
|
||||
bool IsInside(UINT32 x, UINT32 y) const { return (x >= left && x < right && y >= top && y < bottom); }
|
||||
};
|
||||
|
||||
#ifdef __PGF32SUPPORT__
|
||||
typedef INT32 DataT;
|
||||
#else
|
||||
typedef INT16 DataT;
|
||||
#endif
|
||||
|
||||
typedef void (*RefreshCB)(void *p);
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Image constants
|
||||
//-------------------------------------------------------------------------------
|
||||
#define MagicVersionSize sizeof(PGFMagicVersion)
|
||||
#define PreHeaderSize sizeof(PGFPreHeader)
|
||||
#define HeaderSize sizeof(PGFHeader)
|
||||
#define ColorTableSize (ColorTableLen*sizeof(RGBQUAD))
|
||||
#define DataTSize sizeof(DataT)
|
||||
#define MaxUserDataSize 0x7FFFFFFF
|
||||
|
||||
#endif //PGF_PGFTYPES_H
|
||||
@@ -0,0 +1,114 @@
|
||||
The Progressive Graphics File
|
||||
=============================
|
||||
|
||||
For more information see http://www.libpgf.org. There you can find some
|
||||
documents concerning this progressive graphic file codec.
|
||||
|
||||
This project is hosted on the Sourceforge.net platform. For support and
|
||||
questions, please use the installed mailing list and forums there.
|
||||
The Sourceforge URL of our project is: http://sourceforge.net/projects/libpgf
|
||||
|
||||
=============================
|
||||
Release Notes
|
||||
|
||||
Version 7.19.3, (Tue, 15 Jan 2019)
|
||||
------------------------------------
|
||||
|
||||
1. The new version is a minor update of version 7.15.25.
|
||||
|
||||
2. This version fixes a compilation bug seen when ROI support is disabled.
|
||||
|
||||
|
||||
Version 7.15.32, (Thu, 6 Aug 2015)
|
||||
------------------------------------
|
||||
|
||||
1. The new version is a minor update of version 7.15.25.
|
||||
|
||||
2. This version improves the reuse of CPGFImage objects for several decoding operations.
|
||||
It clarifies the usage of CPGFImage::Close() and CPGFImage::Destroy() by deletion of
|
||||
Close(). Several reading operations can be performed in the following way:
|
||||
Open(), Read(), GetBitmap(), ResetStreamPos(), Read(), GetBitmap(), ResetStreamPos(), ...
|
||||
Calling Destroy() frees all allocated ressources and reinitializes the object to the
|
||||
same state as the constructor does. This allows the reuse of the CPGFImage object for
|
||||
encoding and decoding:
|
||||
SetHeader(), ImportBitmap(), Write(), ResetStreamPos(), Destroy(), Open(), Read(), GetBitmap()
|
||||
|
||||
3. Caching or skipping of user data (meta data) while opening a PGF image can be controlled
|
||||
by a new UserdataPolicy in ConfigureDecoder().
|
||||
|
||||
|
||||
Version 7.15.25, (Sat, 20 June 2015)
|
||||
------------------------------------
|
||||
|
||||
1. This new version is a replacement of version 6.14.12.
|
||||
In case you use the ROI decoding, we strongly encourage using version 6.15.25 instead of an older version.
|
||||
|
||||
2. This version fixes some decoder bugs only seen in ROI decoding.
|
||||
ROI decoding is now also supported for Bitmap and RGB12 image modes.
|
||||
|
||||
3. This version introduces a new and more efficient data format for binary images (bitmaps).
|
||||
The new format allows ROI decoding.
|
||||
The decoder supports both the old and the new format, but ROI decoding works only with the new format.
|
||||
|
||||
4. The two reserverd bytes in PGFHeader are now used for a more detailled PGF version number.
|
||||
|
||||
5. The Visual Studio project files are in the VS12 format.
|
||||
|
||||
|
||||
Version 6.14.12, (Wed, 9 April 2014)
|
||||
------------------------------------
|
||||
|
||||
1. The new version is a minor update of version 6.12.24.
|
||||
|
||||
2. It mainly contains some fixes of memory leaks in the OpenMP part and some improvements suggested by cppcheck and Coverity.
|
||||
|
||||
3. The Visual Studio project files are in the VS11 format.
|
||||
|
||||
|
||||
Version 6.12.24, (Thu, 14 June 2012)
|
||||
------------------------------------
|
||||
|
||||
1. The new version is a replacement of version 6.11.42.
|
||||
In case you use the ROI encoding scheme, we strongly encourage using version 6.12.24 instead of version 6.11.42.
|
||||
|
||||
2. This version fixes some decoder bugs, sometimes seen in ROI decoding.
|
||||
|
||||
3. This version allows a simpler user-data handling, especially for uncached metadata. The following two methods
|
||||
in the class PGFimage are designed for this purpose:
|
||||
|
||||
GetUserDataPos() returns in an opened PGF image the stream position of the user data area.
|
||||
|
||||
WriteImage() encodes and writes the image at the current stream position. This method is called after
|
||||
WriteHeader(). In case you want to write uncached metadata into the stream, then do that after WriteHeader()
|
||||
and just before WriteImage(). If you are not interested in writing uncached metadata, then you usually use
|
||||
Write() instead of WriteImage(). WriteHeader() and WriteImage() are called inside of Write().
|
||||
|
||||
|
||||
Version 6.11.42, (Sun, 23 Oct 2011)
|
||||
-----------------------------------
|
||||
|
||||
1. The new version is a replacement of version 6.11.24.
|
||||
We strongly encourage using version 6.11.42 instead of version 6.11.24.
|
||||
|
||||
2. This version fixes some decoder bugs, only seen in lossless compression of
|
||||
large images.
|
||||
|
||||
3. The rarely used, but sometimes misused, background information (3 Bytes)
|
||||
in the PGFHeader has been replaced by
|
||||
|
||||
UINT8 usedBitsPerChannel; // number of used bits per channel
|
||||
// in 16- and 32-bit per channel modes
|
||||
UINT8 reserved1, reserved2; // not used
|
||||
|
||||
The value usedBitsPerChannel is helpful in case you have more than 8 (16) but
|
||||
less than 16 (32) significant bits per channel, stored in the most
|
||||
significant bits of a pixel. For example, you have a grayscale image with 14
|
||||
bit significant data per pixel stored in the ImageModeGray16 pixel format. In
|
||||
case you have left shifted the 14 bits to be the most significant 14 bits,
|
||||
then you should set usedBitsPerChannel=14. This will increase the compression
|
||||
ratio without any drawbacks, because the 14 bits are internally right shifted.
|
||||
On the other side, if the 14 bits are the least significant bits in
|
||||
your 16 bit pixel format, then you shoulden’t set usedBitsPerChannel. It will
|
||||
be automatically set to 16, but this is no problem, since the not used most
|
||||
significant bits per pixel are never coded at all. So, in both cases the same
|
||||
compression ratio will result.
|
||||
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
* The Progressive Graphics File; http://www.libpgf.org
|
||||
*
|
||||
* $Date: 2006-06-04 22:05:59 +0200 (So, 04 Jun 2006) $
|
||||
* $Revision: 229 $
|
||||
*
|
||||
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
* as published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// @file Subband.cpp
|
||||
/// @brief PGF wavelet subband class implementation
|
||||
/// @author C. Stamm
|
||||
|
||||
#include "Subband.h"
|
||||
#include "Encoder.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Default constructor
|
||||
CSubband::CSubband()
|
||||
: m_width(0)
|
||||
, m_height(0)
|
||||
, m_size(0)
|
||||
, m_level(0)
|
||||
, m_orientation(LL)
|
||||
, m_dataPos(0)
|
||||
, m_data(0)
|
||||
#ifdef __PGFROISUPPORT__
|
||||
, m_nTiles(0)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Destructor
|
||||
CSubband::~CSubband() {
|
||||
FreeMemory();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Initialize subband parameters
|
||||
void CSubband::Initialize(UINT32 width, UINT32 height, int level, Orientation orient) {
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_size = m_width*m_height;
|
||||
m_level = level;
|
||||
m_orientation = orient;
|
||||
m_data = 0;
|
||||
m_dataPos = 0;
|
||||
#ifdef __PGFROISUPPORT__
|
||||
m_ROI.left = 0;
|
||||
m_ROI.top = 0;
|
||||
m_ROI.right = m_width;
|
||||
m_ROI.bottom = m_height;
|
||||
m_nTiles = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Allocate a memory buffer to store all wavelet coefficients of this subband.
|
||||
// @return True if the allocation works without any problems
|
||||
bool CSubband::AllocMemory() {
|
||||
UINT32 oldSize = m_size;
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
m_size = BufferWidth()*m_ROI.Height();
|
||||
#endif
|
||||
ASSERT(m_size > 0);
|
||||
|
||||
if (m_data) {
|
||||
if (oldSize >= m_size) {
|
||||
return true;
|
||||
} else {
|
||||
delete[] m_data;
|
||||
m_data = new(std::nothrow) DataT[m_size];
|
||||
return (m_data != 0);
|
||||
}
|
||||
} else {
|
||||
m_data = new(std::nothrow) DataT[m_size];
|
||||
return (m_data != 0);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Delete the memory buffer of this subband.
|
||||
void CSubband::FreeMemory() {
|
||||
if (m_data) {
|
||||
delete[] m_data; m_data = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Perform subband quantization with given quantization parameter.
|
||||
// A scalar quantization (with dead-zone) is used. A large quantization value
|
||||
// results in strong quantization and therefore in big quality loss.
|
||||
// @param quantParam A quantization parameter (larger or equal to 0)
|
||||
void CSubband::Quantize(int quantParam) {
|
||||
if (m_orientation == LL) {
|
||||
quantParam -= (m_level + 1);
|
||||
// uniform rounding quantization
|
||||
if (quantParam > 0) {
|
||||
quantParam--;
|
||||
for (UINT32 i=0; i < m_size; i++) {
|
||||
if (m_data[i] < 0) {
|
||||
m_data[i] = -(((-m_data[i] >> quantParam) + 1) >> 1);
|
||||
} else {
|
||||
m_data[i] = ((m_data[i] >> quantParam) + 1) >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (m_orientation == HH) {
|
||||
quantParam -= (m_level - 1);
|
||||
} else {
|
||||
quantParam -= m_level;
|
||||
}
|
||||
// uniform deadzone quantization
|
||||
if (quantParam > 0) {
|
||||
int threshold = ((1 << quantParam) * 7)/5; // good value
|
||||
quantParam--;
|
||||
for (UINT32 i=0; i < m_size; i++) {
|
||||
if (m_data[i] < -threshold) {
|
||||
m_data[i] = -(((-m_data[i] >> quantParam) + 1) >> 1);
|
||||
} else if (m_data[i] > threshold) {
|
||||
m_data[i] = ((m_data[i] >> quantParam) + 1) >> 1;
|
||||
} else {
|
||||
m_data[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Perform subband dequantization with given quantization parameter.
|
||||
/// A scalar quantization (with dead-zone) is used. A large quantization value
|
||||
/// results in strong quantization and therefore in big quality loss.
|
||||
/// @param quantParam A quantization parameter (larger or equal to 0)
|
||||
void CSubband::Dequantize(int quantParam) {
|
||||
if (m_orientation == LL) {
|
||||
quantParam -= m_level + 1;
|
||||
} else if (m_orientation == HH) {
|
||||
quantParam -= m_level - 1;
|
||||
} else {
|
||||
quantParam -= m_level;
|
||||
}
|
||||
if (quantParam > 0) {
|
||||
for (UINT32 i=0; i < m_size; i++) {
|
||||
m_data[i] <<= quantParam;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Extracts a rectangular subregion of this subband.
|
||||
/// Write wavelet coefficients into buffer.
|
||||
/// It might throw an IOException.
|
||||
/// @param encoder An encoder instance
|
||||
/// @param tile True if just a rectangular region is extracted, false if the entire subband is extracted.
|
||||
/// @param tileX Tile index in x-direction
|
||||
/// @param tileY Tile index in y-direction
|
||||
void CSubband::ExtractTile(CEncoder& encoder, bool tile /*= false*/, UINT32 tileX /*= 0*/, UINT32 tileY /*= 0*/) {
|
||||
#ifdef __PGFROISUPPORT__
|
||||
if (tile) {
|
||||
// compute tile position and size
|
||||
UINT32 xPos, yPos, w, h;
|
||||
TilePosition(tileX, tileY, xPos, yPos, w, h);
|
||||
|
||||
// write values into buffer using partitiong scheme
|
||||
encoder.Partition(this, w, h, xPos + yPos*m_width, m_width);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
(void)tileX; (void)tileY; (void)tile; // prevents from unreferenced formal parameter warning
|
||||
// write values into buffer using partitiong scheme
|
||||
encoder.Partition(this, m_width, m_height, 0, m_width);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Decoding and dequantization of this subband.
|
||||
/// It might throw an IOException.
|
||||
/// @param decoder A decoder instance
|
||||
/// @param quantParam Dequantization value
|
||||
/// @param tile True if just a rectangular region is placed, false if the entire subband is placed.
|
||||
/// @param tileX Tile index in x-direction
|
||||
/// @param tileY Tile index in y-direction
|
||||
void CSubband::PlaceTile(CDecoder& decoder, int quantParam, bool tile /*= false*/, UINT32 tileX /*= 0*/, UINT32 tileY /*= 0*/) {
|
||||
// allocate memory
|
||||
if (!AllocMemory()) ReturnWithError(InsufficientMemory);
|
||||
|
||||
// correct quantParam with normalization factor
|
||||
if (m_orientation == LL) {
|
||||
quantParam -= m_level + 1;
|
||||
} else if (m_orientation == HH) {
|
||||
quantParam -= m_level - 1;
|
||||
} else {
|
||||
quantParam -= m_level;
|
||||
}
|
||||
if (quantParam < 0) quantParam = 0;
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
if (tile) {
|
||||
UINT32 xPos, yPos, w, h;
|
||||
|
||||
// compute tile position and size
|
||||
TilePosition(tileX, tileY, xPos, yPos, w, h);
|
||||
|
||||
ASSERT(xPos >= m_ROI.left && yPos >= m_ROI.top);
|
||||
decoder.Partition(this, quantParam, w, h, (xPos - m_ROI.left) + (yPos - m_ROI.top)*BufferWidth(), BufferWidth());
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
(void)tileX; (void)tileY; (void)tile; // prevents from unreferenced formal parameter warning
|
||||
// read values into buffer using partitiong scheme
|
||||
decoder.Partition(this, quantParam, m_width, m_height, 0, m_width);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Set ROI
|
||||
void CSubband::SetAlignedROI(const PGFRect& roi) {
|
||||
ASSERT(roi.left <= m_width);
|
||||
ASSERT(roi.top <= m_height);
|
||||
|
||||
m_ROI = roi;
|
||||
if (m_ROI.right > m_width) m_ROI.right = m_width;
|
||||
if (m_ROI.bottom > m_height) m_ROI.bottom = m_height;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Compute tile position and size.
|
||||
/// @param tileX Tile index in x-direction
|
||||
/// @param tileY Tile index in y-direction
|
||||
/// @param xPos [out] Offset to left
|
||||
/// @param yPos [out] Offset to top
|
||||
/// @param w [out] Tile width
|
||||
/// @param h [out] Tile height
|
||||
void CSubband::TilePosition(UINT32 tileX, UINT32 tileY, UINT32& xPos, UINT32& yPos, UINT32& w, UINT32& h) const {
|
||||
ASSERT(tileX < m_nTiles); ASSERT(tileY < m_nTiles);
|
||||
// example
|
||||
// band = HH, w = 30, ldTiles = 2 -> 4 tiles in a row/column
|
||||
// --> tile widths
|
||||
// 8 7 8 7
|
||||
//
|
||||
// tile partitioning scheme
|
||||
// 0 1 2 3
|
||||
// 4 5 6 7
|
||||
// 8 9 A B
|
||||
// C D E F
|
||||
|
||||
UINT32 nTiles = m_nTiles;
|
||||
UINT32 m;
|
||||
UINT32 left = 0, right = nTiles;
|
||||
UINT32 top = 0, bottom = nTiles;
|
||||
|
||||
xPos = 0;
|
||||
yPos = 0;
|
||||
w = m_width;
|
||||
h = m_height;
|
||||
|
||||
while (nTiles > 1) {
|
||||
// compute xPos and w with binary search
|
||||
m = left + ((right - left) >> 1);
|
||||
if (tileX >= m) {
|
||||
xPos += (w + 1) >> 1;
|
||||
w >>= 1;
|
||||
left = m;
|
||||
} else {
|
||||
w = (w + 1) >> 1;
|
||||
right = m;
|
||||
}
|
||||
// compute yPos and h with binary search
|
||||
m = top + ((bottom - top) >> 1);
|
||||
if (tileY >= m) {
|
||||
yPos += (h + 1) >> 1;
|
||||
h >>= 1;
|
||||
top = m;
|
||||
} else {
|
||||
h = (h + 1) >> 1;
|
||||
bottom = m;
|
||||
}
|
||||
nTiles >>= 1;
|
||||
}
|
||||
ASSERT(xPos < m_width && (xPos + w <= m_width));
|
||||
ASSERT(yPos < m_height && (yPos + h <= m_height));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Compute tile index and extrem position (x,y) of given position (xPos, yPos).
|
||||
void CSubband::TileIndex(bool topLeft, UINT32 xPos, UINT32 yPos, UINT32& tileX, UINT32& tileY, UINT32& x, UINT32& y) const {
|
||||
UINT32 m;
|
||||
UINT32 left = 0, right = m_width;
|
||||
UINT32 top = 0, bottom = m_height;
|
||||
UINT32 nTiles = m_nTiles;
|
||||
|
||||
if (xPos > m_width) xPos = m_width;
|
||||
if (yPos > m_height) yPos = m_height;
|
||||
|
||||
if (topLeft) {
|
||||
// compute tileX with binary search
|
||||
tileX = 0;
|
||||
while (nTiles > 1) {
|
||||
nTiles >>= 1;
|
||||
m = left + ((right - left + 1) >> 1);
|
||||
if (xPos < m) {
|
||||
// exclusive m
|
||||
right = m;
|
||||
} else {
|
||||
tileX += nTiles;
|
||||
left = m;
|
||||
}
|
||||
}
|
||||
x = left;
|
||||
ASSERT(tileX >= 0 && tileX < m_nTiles);
|
||||
|
||||
// compute tileY with binary search
|
||||
nTiles = m_nTiles;
|
||||
tileY = 0;
|
||||
while (nTiles > 1) {
|
||||
nTiles >>= 1;
|
||||
m = top + ((bottom - top + 1) >> 1);
|
||||
if (yPos < m) {
|
||||
// exclusive m
|
||||
bottom = m;
|
||||
} else {
|
||||
tileY += nTiles;
|
||||
top = m;
|
||||
}
|
||||
}
|
||||
y = top;
|
||||
ASSERT(tileY >= 0 && tileY < m_nTiles);
|
||||
|
||||
} else {
|
||||
// compute tileX with binary search
|
||||
tileX = 1;
|
||||
while (nTiles > 1) {
|
||||
nTiles >>= 1;
|
||||
m = left + ((right - left + 1) >> 1);
|
||||
if (xPos <= m) {
|
||||
// inclusive m
|
||||
right = m;
|
||||
} else {
|
||||
tileX += nTiles;
|
||||
left = m;
|
||||
}
|
||||
}
|
||||
x = right;
|
||||
ASSERT(tileX > 0 && tileX <= m_nTiles);
|
||||
|
||||
// compute tileY with binary search
|
||||
nTiles = m_nTiles;
|
||||
tileY = 1;
|
||||
while (nTiles > 1) {
|
||||
nTiles >>= 1;
|
||||
m = top + ((bottom - top + 1) >> 1);
|
||||
if (yPos <= m) {
|
||||
// inclusive m
|
||||
bottom = m;
|
||||
} else {
|
||||
tileY += nTiles;
|
||||
top = m;
|
||||
}
|
||||
}
|
||||
y = bottom;
|
||||
ASSERT(tileY > 0 && tileY <= m_nTiles);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* The Progressive Graphics File; http://www.libpgf.org
|
||||
*
|
||||
* $Date: 2006-06-04 22:05:59 +0200 (So, 04 Jun 2006) $
|
||||
* $Revision: 229 $
|
||||
*
|
||||
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
* as published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// @file Subband.h
|
||||
/// @brief PGF wavelet subband class
|
||||
/// @author C. Stamm
|
||||
|
||||
#ifndef PGF_SUBBAND_H
|
||||
#define PGF_SUBBAND_H
|
||||
|
||||
#include "PGFtypes.h"
|
||||
|
||||
class CEncoder;
|
||||
class CDecoder;
|
||||
class CRoiIndices;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// PGF wavelet channel subband class.
|
||||
/// @author C. Stamm, R. Spuler
|
||||
/// @brief Wavelet channel class
|
||||
class CSubband {
|
||||
friend class CWaveletTransform;
|
||||
friend class CRoiIndices;
|
||||
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Standard constructor.
|
||||
CSubband();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Destructor.
|
||||
~CSubband();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Allocate a memory buffer to store all wavelet coefficients of this subband.
|
||||
/// @return True if the allocation did work without any problems
|
||||
bool AllocMemory();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Delete the memory buffer of this subband.
|
||||
void FreeMemory();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Extracts a rectangular subregion of this subband.
|
||||
/// Write wavelet coefficients into buffer.
|
||||
/// It might throw an IOException.
|
||||
/// @param encoder An encoder instance
|
||||
/// @param tile True if just a rectangular region is extracted, false if the entire subband is extracted.
|
||||
/// @param tileX Tile index in x-direction
|
||||
/// @param tileY Tile index in y-direction
|
||||
void ExtractTile(CEncoder& encoder, bool tile = false, UINT32 tileX = 0, UINT32 tileY = 0);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Decoding and dequantization of this subband.
|
||||
/// It might throw an IOException.
|
||||
/// @param decoder A decoder instance
|
||||
/// @param quantParam Dequantization value
|
||||
/// @param tile True if just a rectangular region is placed, false if the entire subband is placed.
|
||||
/// @param tileX Tile index in x-direction
|
||||
/// @param tileY Tile index in y-direction
|
||||
void PlaceTile(CDecoder& decoder, int quantParam, bool tile = false, UINT32 tileX = 0, UINT32 tileY = 0);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Perform subband quantization with given quantization parameter.
|
||||
/// A scalar quantization (with dead-zone) is used. A large quantization value
|
||||
/// results in strong quantization and therefore in big quality loss.
|
||||
/// @param quantParam A quantization parameter (larger or equal to 0)
|
||||
void Quantize(int quantParam);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Perform subband dequantization with given quantization parameter.
|
||||
/// A scalar quantization (with dead-zone) is used. A large quantization value
|
||||
/// results in strong quantization and therefore in big quality loss.
|
||||
/// @param quantParam A quantization parameter (larger or equal to 0)
|
||||
void Dequantize(int quantParam);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Store wavelet coefficient in subband at given position.
|
||||
/// @param pos A subband position (>= 0)
|
||||
/// @param v A wavelet coefficient
|
||||
void SetData(UINT32 pos, DataT v) { ASSERT(pos < m_size); m_data[pos] = v; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Get a pointer to an array of all wavelet coefficients of this subband.
|
||||
/// @return Pointer to array of wavelet coefficients
|
||||
DataT* GetBuffer() { return m_data; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return wavelet coefficient at given position.
|
||||
/// @param pos A subband position (>= 0)
|
||||
/// @return Wavelet coefficient
|
||||
DataT GetData(UINT32 pos) const { ASSERT(pos < m_size); return m_data[pos]; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return level of this subband.
|
||||
/// @return Level of this subband
|
||||
int GetLevel() const { return m_level; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return height of this subband.
|
||||
/// @return Height of this subband (in pixels)
|
||||
int GetHeight() const { return m_height; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return width of this subband.
|
||||
/// @return Width of this subband (in pixels)
|
||||
int GetWidth() const { return m_width; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return orientation of this subband.
|
||||
/// LL LH
|
||||
/// HL HH
|
||||
/// @return Orientation of this subband (LL, HL, LH, HH)
|
||||
Orientation GetOrientation() const { return m_orientation; }
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// Set data buffer position to given position + one row.
|
||||
/// @param pos Given position
|
||||
void IncBuffRow(UINT32 pos) { m_dataPos = pos + BufferWidth(); }
|
||||
|
||||
#endif
|
||||
|
||||
private:
|
||||
void Initialize(UINT32 width, UINT32 height, int level, Orientation orient);
|
||||
void WriteBuffer(DataT val) { ASSERT(m_dataPos < m_size); m_data[m_dataPos++] = val; }
|
||||
void SetBuffer(DataT* b) { ASSERT(b); m_data = b; }
|
||||
DataT ReadBuffer() { ASSERT(m_dataPos < m_size); return m_data[m_dataPos++]; }
|
||||
|
||||
UINT32 GetBuffPos() const { return m_dataPos; }
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
UINT32 BufferWidth() const { return m_ROI.Width(); }
|
||||
void TilePosition(UINT32 tileX, UINT32 tileY, UINT32& left, UINT32& top, UINT32& w, UINT32& h) const;
|
||||
void TileIndex(bool topLeft, UINT32 xPos, UINT32 yPos, UINT32& tileX, UINT32& tileY, UINT32& x, UINT32& y) const;
|
||||
const PGFRect& GetAlignedROI() const { return m_ROI; }
|
||||
void SetNTiles(UINT32 nTiles) { m_nTiles = nTiles; }
|
||||
void SetAlignedROI(const PGFRect& roi);
|
||||
void InitBuffPos(UINT32 left = 0, UINT32 top = 0) { m_dataPos = top*BufferWidth() + left; ASSERT(m_dataPos < m_size); }
|
||||
#else
|
||||
void InitBuffPos() { m_dataPos = 0; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
UINT32 m_width; ///< width in pixels
|
||||
UINT32 m_height; ///< height in pixels
|
||||
UINT32 m_size; ///< size of data buffer m_data
|
||||
int m_level; ///< recursion level
|
||||
Orientation m_orientation; ///< 0=LL, 1=HL, 2=LH, 3=HH L=lowpass filtered, H=highpass filterd
|
||||
UINT32 m_dataPos; ///< current position in m_data
|
||||
DataT* m_data; ///< buffer
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
PGFRect m_ROI; ///< region of interest (block aligned)
|
||||
UINT32 m_nTiles; ///< number of tiles in one dimension in this subband
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif //PGF_SUBBAND_H
|
||||
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
* The Progressive Graphics File; http://www.libpgf.org
|
||||
*
|
||||
* $Date: 2006-05-18 16:03:32 +0200 (Do, 18 Mai 2006) $
|
||||
* $Revision: 194 $
|
||||
*
|
||||
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
* as published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// @file WaveletTransform.cpp
|
||||
/// @brief PGF wavelet transform class implementation
|
||||
/// @author C. Stamm
|
||||
|
||||
#include "WaveletTransform.h"
|
||||
|
||||
#define c1 1 // best value 1
|
||||
#define c2 2 // best value 2
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Constructor: Constructs a wavelet transform pyramid of given size and levels.
|
||||
// @param width The width of the original image (at level 0) in pixels
|
||||
// @param height The height of the original image (at level 0) in pixels
|
||||
// @param levels The number of levels (>= 0)
|
||||
// @param data Input data of subband LL at level 0
|
||||
CWaveletTransform::CWaveletTransform(UINT32 width, UINT32 height, int levels, DataT* data)
|
||||
:
|
||||
#ifdef __PGFROISUPPORT__
|
||||
m_indices(nullptr),
|
||||
#endif
|
||||
m_nLevels(levels + 1) // m_nLevels in CPGFImage determines the number of FWT steps; this.m_nLevels determines the number subband-planes
|
||||
, m_subband(nullptr)
|
||||
{
|
||||
ASSERT(m_nLevels > 0 && m_nLevels <= MaxLevel + 1);
|
||||
InitSubbands(width, height, data);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Initialize size subbands on all levels
|
||||
void CWaveletTransform::InitSubbands(UINT32 width, UINT32 height, DataT* data) {
|
||||
if (m_subband) Destroy();
|
||||
|
||||
// create subbands
|
||||
m_subband = new CSubband[m_nLevels][NSubbands];
|
||||
|
||||
// init subbands
|
||||
UINT32 loWidth = width;
|
||||
UINT32 hiWidth = width;
|
||||
UINT32 loHeight = height;
|
||||
UINT32 hiHeight = height;
|
||||
|
||||
for (int level = 0; level < m_nLevels; level++) {
|
||||
m_subband[level][LL].Initialize(loWidth, loHeight, level, LL); // LL
|
||||
m_subband[level][HL].Initialize(hiWidth, loHeight, level, HL); // HL
|
||||
m_subband[level][LH].Initialize(loWidth, hiHeight, level, LH); // LH
|
||||
m_subband[level][HH].Initialize(hiWidth, hiHeight, level, HH); // HH
|
||||
hiWidth = loWidth >> 1; hiHeight = loHeight >> 1;
|
||||
loWidth = (loWidth + 1) >> 1; loHeight = (loHeight + 1) >> 1;
|
||||
}
|
||||
if (data) {
|
||||
m_subband[0][LL].SetBuffer(data);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Compute fast forward wavelet transform of LL subband at given level and
|
||||
// stores result in all 4 subbands of level + 1.
|
||||
// Wavelet transform used in writing a PGF file
|
||||
// Forward Transform of srcBand and split and store it into subbands on destLevel
|
||||
// low pass filter at even positions: 1/8[-1, 2, (6), 2, -1]
|
||||
// high pass filter at odd positions: 1/4[-2, (4), -2]
|
||||
// @param level A wavelet transform pyramid level (>= 0 && < Levels())
|
||||
// @param quant A quantization value (linear scalar quantization)
|
||||
// @return error in case of a memory allocation problem
|
||||
OSError CWaveletTransform::ForwardTransform(int level, int quant) {
|
||||
ASSERT(level >= 0 && level < m_nLevels - 1);
|
||||
const int destLevel = level + 1;
|
||||
ASSERT(m_subband[destLevel]);
|
||||
CSubband* srcBand = &m_subband[level][LL]; ASSERT(srcBand);
|
||||
const UINT32 width = srcBand->GetWidth();
|
||||
const UINT32 height = srcBand->GetHeight();
|
||||
DataT* src = srcBand->GetBuffer(); ASSERT(src);
|
||||
DataT *row0, *row1, *row2, *row3;
|
||||
|
||||
// Allocate memory for next transform level
|
||||
for (int i=0; i < NSubbands; i++) {
|
||||
if (!m_subband[destLevel][i].AllocMemory()) return InsufficientMemory;
|
||||
}
|
||||
|
||||
if (height >= FilterSize) { // changed from FilterSizeH to FilterSize
|
||||
// top border handling
|
||||
row0 = src; row1 = row0 + width; row2 = row1 + width;
|
||||
ForwardRow(row0, width);
|
||||
ForwardRow(row1, width);
|
||||
ForwardRow(row2, width);
|
||||
for (UINT32 k=0; k < width; k++) {
|
||||
row1[k] -= ((row0[k] + row2[k] + c1) >> 1); // high pass
|
||||
row0[k] += ((row1[k] + c1) >> 1); // low pass
|
||||
}
|
||||
InterleavedToSubbands(destLevel, row0, row1, width);
|
||||
row0 = row1; row1 = row2; row2 += width; row3 = row2 + width;
|
||||
|
||||
// middle part
|
||||
for (UINT32 i=3; i < height-1; i += 2) {
|
||||
ForwardRow(row2, width);
|
||||
ForwardRow(row3, width);
|
||||
for (UINT32 k=0; k < width; k++) {
|
||||
row2[k] -= ((row1[k] + row3[k] + c1) >> 1); // high pass filter
|
||||
row1[k] += ((row0[k] + row2[k] + c2) >> 2); // low pass filter
|
||||
}
|
||||
InterleavedToSubbands(destLevel, row1, row2, width);
|
||||
row0 = row2; row1 = row3; row2 = row3 + width; row3 = row2 + width;
|
||||
}
|
||||
|
||||
// bottom border handling
|
||||
if (height & 1) {
|
||||
for (UINT32 k=0; k < width; k++) {
|
||||
row1[k] += ((row0[k] + c1) >> 1); // low pass
|
||||
}
|
||||
InterleavedToSubbands(destLevel, row1, nullptr, width);
|
||||
row0 = row1; row1 += width;
|
||||
} else {
|
||||
ForwardRow(row2, width);
|
||||
for (UINT32 k=0; k < width; k++) {
|
||||
row2[k] -= row1[k]; // high pass
|
||||
row1[k] += ((row0[k] + row2[k] + c2) >> 2); // low pass
|
||||
}
|
||||
InterleavedToSubbands(destLevel, row1, row2, width);
|
||||
row0 = row1; row1 = row2; row2 += width;
|
||||
}
|
||||
} else {
|
||||
// if height is too small
|
||||
row0 = src; row1 = row0 + width;
|
||||
// first part
|
||||
for (UINT32 k=0; k < height; k += 2) {
|
||||
ForwardRow(row0, width);
|
||||
ForwardRow(row1, width);
|
||||
InterleavedToSubbands(destLevel, row0, row1, width);
|
||||
row0 += width << 1; row1 += width << 1;
|
||||
}
|
||||
// bottom
|
||||
if (height & 1) {
|
||||
InterleavedToSubbands(destLevel, row0, nullptr, width);
|
||||
}
|
||||
}
|
||||
|
||||
if (quant > 0) {
|
||||
// subband quantization (without LL)
|
||||
for (int i=1; i < NSubbands; i++) {
|
||||
m_subband[destLevel][i].Quantize(quant);
|
||||
}
|
||||
// LL subband quantization
|
||||
if (destLevel == m_nLevels - 1) {
|
||||
m_subband[destLevel][LL].Quantize(quant);
|
||||
}
|
||||
}
|
||||
|
||||
// free source band
|
||||
srcBand->FreeMemory();
|
||||
return NoError;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Forward transform one row
|
||||
// low pass filter at even positions: 1/8[-1, 2, (6), 2, -1]
|
||||
// high pass filter at odd positions: 1/4[-2, (4), -2]
|
||||
void CWaveletTransform::ForwardRow(DataT* src, UINT32 width) {
|
||||
if (width >= FilterSize) {
|
||||
UINT32 i = 3;
|
||||
|
||||
// left border handling
|
||||
src[1] -= ((src[0] + src[2] + c1) >> 1); // high pass
|
||||
src[0] += ((src[1] + c1) >> 1); // low pass
|
||||
|
||||
// middle part
|
||||
for (; i < width-1; i += 2) {
|
||||
src[i] -= ((src[i-1] + src[i+1] + c1) >> 1); // high pass
|
||||
src[i-1] += ((src[i-2] + src[i] + c2) >> 2); // low pass
|
||||
}
|
||||
|
||||
// right border handling
|
||||
if (width & 1) {
|
||||
src[i-1] += ((src[i-2] + c1) >> 1); // low pass
|
||||
} else {
|
||||
src[i] -= src[i-1]; // high pass
|
||||
src[i-1] += ((src[i-2] + src[i] + c2) >> 2); // low pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Copy transformed and interleaved (L,H,L,H,...) rows loRow and hiRow to subbands LL,HL,LH,HH
|
||||
void CWaveletTransform::InterleavedToSubbands(int destLevel, DataT* loRow, DataT* hiRow, UINT32 width) {
|
||||
const UINT32 wquot = width >> 1;
|
||||
const bool wrem = (width & 1);
|
||||
CSubband &ll = m_subband[destLevel][LL], &hl = m_subband[destLevel][HL];
|
||||
CSubband &lh = m_subband[destLevel][LH], &hh = m_subband[destLevel][HH];
|
||||
|
||||
if (hiRow) {
|
||||
for (UINT32 i=0; i < wquot; i++) {
|
||||
ll.WriteBuffer(*loRow++); // first access, than increment
|
||||
hl.WriteBuffer(*loRow++);
|
||||
lh.WriteBuffer(*hiRow++); // first access, than increment
|
||||
hh.WriteBuffer(*hiRow++);
|
||||
}
|
||||
if (wrem) {
|
||||
ll.WriteBuffer(*loRow);
|
||||
lh.WriteBuffer(*hiRow);
|
||||
}
|
||||
} else {
|
||||
for (UINT32 i=0; i < wquot; i++) {
|
||||
ll.WriteBuffer(*loRow++); // first access, than increment
|
||||
hl.WriteBuffer(*loRow++);
|
||||
}
|
||||
if (wrem) ll.WriteBuffer(*loRow);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Compute fast inverse wavelet transform of all 4 subbands of given level and
|
||||
// stores result in LL subband of level - 1.
|
||||
// Inverse wavelet transform used in reading a PGF file
|
||||
// Inverse Transform srcLevel and combine to destBand
|
||||
// low-pass coefficients at even positions, high-pass coefficients at odd positions
|
||||
// inverse filter for even positions: 1/4[-1, (4), -1]
|
||||
// inverse filter for odd positions: 1/8[-1, 4, (6), 4, -1]
|
||||
// @param srcLevel A wavelet transform pyramid level (> 0 && <= Levels())
|
||||
// @param w [out] A pointer to the returned width of subband LL (in pixels)
|
||||
// @param h [out] A pointer to the returned height of subband LL (in pixels)
|
||||
// @param data [out] A pointer to the returned array of image data
|
||||
// @return error in case of a memory allocation problem
|
||||
OSError CWaveletTransform::InverseTransform(int srcLevel, UINT32* w, UINT32* h, DataT** data) {
|
||||
ASSERT(srcLevel > 0 && srcLevel < m_nLevels);
|
||||
const int destLevel = srcLevel - 1;
|
||||
ASSERT(m_subband[destLevel]);
|
||||
CSubband* destBand = &m_subband[destLevel][LL];
|
||||
UINT32 width, height;
|
||||
|
||||
// allocate memory for the results of the inverse transform
|
||||
if (!destBand->AllocMemory()) return InsufficientMemory;
|
||||
DataT *origin = destBand->GetBuffer(), *row0, *row1, *row2, *row3;
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
PGFRect destROI = destBand->GetAlignedROI();
|
||||
const UINT32 destWidth = destROI.Width(); // destination buffer width
|
||||
const UINT32 destHeight = destROI.Height(); // destination buffer height
|
||||
width = destWidth; // destination working width
|
||||
height = destHeight; // destination working height
|
||||
|
||||
// update destination ROI
|
||||
if (destROI.top & 1) {
|
||||
destROI.top++;
|
||||
origin += destWidth;
|
||||
height--;
|
||||
}
|
||||
if (destROI.left & 1) {
|
||||
destROI.left++;
|
||||
origin++;
|
||||
width--;
|
||||
}
|
||||
|
||||
// init source buffer position
|
||||
const UINT32 leftD = destROI.left >> 1;
|
||||
const UINT32 left0 = m_subband[srcLevel][LL].GetAlignedROI().left;
|
||||
const UINT32 left1 = m_subband[srcLevel][HL].GetAlignedROI().left;
|
||||
const UINT32 topD = destROI.top >> 1;
|
||||
const UINT32 top0 = m_subband[srcLevel][LL].GetAlignedROI().top;
|
||||
const UINT32 top1 = m_subband[srcLevel][LH].GetAlignedROI().top;
|
||||
ASSERT(m_subband[srcLevel][LH].GetAlignedROI().left == left0);
|
||||
ASSERT(m_subband[srcLevel][HH].GetAlignedROI().left == left1);
|
||||
ASSERT(m_subband[srcLevel][HL].GetAlignedROI().top == top0);
|
||||
ASSERT(m_subband[srcLevel][HH].GetAlignedROI().top == top1);
|
||||
|
||||
UINT32 srcOffsetX[2] = { 0, 0 };
|
||||
UINT32 srcOffsetY[2] = { 0, 0 };
|
||||
|
||||
if (leftD >= __max(left0, left1)) {
|
||||
srcOffsetX[0] = leftD - left0;
|
||||
srcOffsetX[1] = leftD - left1;
|
||||
} else {
|
||||
if (left0 <= left1) {
|
||||
const UINT32 dx = (left1 - leftD) << 1;
|
||||
destROI.left += dx;
|
||||
origin += dx;
|
||||
width -= dx;
|
||||
srcOffsetX[0] = left1 - left0;
|
||||
} else {
|
||||
const UINT32 dx = (left0 - leftD) << 1;
|
||||
destROI.left += dx;
|
||||
origin += dx;
|
||||
width -= dx;
|
||||
srcOffsetX[1] = left0 - left1;
|
||||
}
|
||||
}
|
||||
if (topD >= __max(top0, top1)) {
|
||||
srcOffsetY[0] = topD - top0;
|
||||
srcOffsetY[1] = topD - top1;
|
||||
} else {
|
||||
if (top0 <= top1) {
|
||||
const UINT32 dy = (top1 - topD) << 1;
|
||||
destROI.top += dy;
|
||||
origin += dy*destWidth;
|
||||
height -= dy;
|
||||
srcOffsetY[0] = top1 - top0;
|
||||
} else {
|
||||
const UINT32 dy = (top0 - topD) << 1;
|
||||
destROI.top += dy;
|
||||
origin += dy*destWidth;
|
||||
height -= dy;
|
||||
srcOffsetY[1] = top0 - top1;
|
||||
}
|
||||
}
|
||||
|
||||
m_subband[srcLevel][LL].InitBuffPos(srcOffsetX[0], srcOffsetY[0]);
|
||||
m_subband[srcLevel][HL].InitBuffPos(srcOffsetX[1], srcOffsetY[0]);
|
||||
m_subband[srcLevel][LH].InitBuffPos(srcOffsetX[0], srcOffsetY[1]);
|
||||
m_subband[srcLevel][HH].InitBuffPos(srcOffsetX[1], srcOffsetY[1]);
|
||||
|
||||
#else
|
||||
width = destBand->GetWidth();
|
||||
height = destBand->GetHeight();
|
||||
PGFRect destROI(0, 0, width, height);
|
||||
const UINT32 destWidth = width; // destination buffer width
|
||||
const UINT32 destHeight = height; // destination buffer height
|
||||
|
||||
// init source buffer position
|
||||
for (int i = 0; i < NSubbands; i++) {
|
||||
m_subband[srcLevel][i].InitBuffPos();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (destHeight >= FilterSize) { // changed from FilterSizeH to FilterSize
|
||||
// top border handling
|
||||
row0 = origin; row1 = row0 + destWidth;
|
||||
SubbandsToInterleaved(srcLevel, row0, row1, width);
|
||||
for (UINT32 k = 0; k < width; k++) {
|
||||
row0[k] -= ((row1[k] + c1) >> 1); // even
|
||||
}
|
||||
|
||||
// middle part
|
||||
row2 = row1 + destWidth; row3 = row2 + destWidth;
|
||||
for (UINT32 i = destROI.top + 2; i < destROI.bottom - 1; i += 2) {
|
||||
SubbandsToInterleaved(srcLevel, row2, row3, width);
|
||||
for (UINT32 k = 0; k < width; k++) {
|
||||
row2[k] -= ((row1[k] + row3[k] + c2) >> 2); // even
|
||||
row1[k] += ((row0[k] + row2[k] + c1) >> 1); // odd
|
||||
}
|
||||
InverseRow(row0, width);
|
||||
InverseRow(row1, width);
|
||||
row0 = row2; row1 = row3; row2 = row1 + destWidth; row3 = row2 + destWidth;
|
||||
}
|
||||
|
||||
// bottom border handling
|
||||
if (height & 1) {
|
||||
SubbandsToInterleaved(srcLevel, row2, nullptr, width);
|
||||
for (UINT32 k = 0; k < width; k++) {
|
||||
row2[k] -= ((row1[k] + c1) >> 1); // even
|
||||
row1[k] += ((row0[k] + row2[k] + c1) >> 1); // odd
|
||||
}
|
||||
InverseRow(row0, width);
|
||||
InverseRow(row1, width);
|
||||
InverseRow(row2, width);
|
||||
row0 = row1; row1 = row2; row2 += destWidth;
|
||||
} else {
|
||||
for (UINT32 k = 0; k < width; k++) {
|
||||
row1[k] += row0[k];
|
||||
}
|
||||
InverseRow(row0, width);
|
||||
InverseRow(row1, width);
|
||||
row0 = row1; row1 += destWidth;
|
||||
}
|
||||
} else {
|
||||
// height is too small
|
||||
row0 = origin; row1 = row0 + destWidth;
|
||||
// first part
|
||||
for (UINT32 k = 0; k < height; k += 2) {
|
||||
SubbandsToInterleaved(srcLevel, row0, row1, width);
|
||||
InverseRow(row0, width);
|
||||
InverseRow(row1, width);
|
||||
row0 += destWidth << 1; row1 += destWidth << 1;
|
||||
}
|
||||
// bottom
|
||||
if (height & 1) {
|
||||
SubbandsToInterleaved(srcLevel, row0, nullptr, width);
|
||||
InverseRow(row0, width);
|
||||
}
|
||||
}
|
||||
|
||||
// free memory of the current srcLevel
|
||||
for (int i = 0; i < NSubbands; i++) {
|
||||
m_subband[srcLevel][i].FreeMemory();
|
||||
}
|
||||
|
||||
// return info
|
||||
*w = destWidth;
|
||||
*h = destHeight;
|
||||
*data = destBand->GetBuffer();
|
||||
return NoError;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Inverse Wavelet Transform of one row
|
||||
// low-pass coefficients at even positions, high-pass coefficients at odd positions
|
||||
// inverse filter for even positions: 1/4[-1, (4), -1]
|
||||
// inverse filter for odd positions: 1/8[-1, 4, (6), 4, -1]
|
||||
void CWaveletTransform::InverseRow(DataT* dest, UINT32 width) {
|
||||
if (width >= FilterSize) {
|
||||
UINT32 i = 2;
|
||||
|
||||
// left border handling
|
||||
dest[0] -= ((dest[1] + c1) >> 1); // even
|
||||
|
||||
// middle part
|
||||
for (; i < width - 1; i += 2) {
|
||||
dest[i] -= ((dest[i-1] + dest[i+1] + c2) >> 2); // even
|
||||
dest[i-1] += ((dest[i-2] + dest[i] + c1) >> 1); // odd
|
||||
}
|
||||
|
||||
// right border handling
|
||||
if (width & 1) {
|
||||
dest[i] -= ((dest[i-1] + c1) >> 1); // even
|
||||
dest[i-1] += ((dest[i-2] + dest[i] + c1) >> 1); // odd
|
||||
} else {
|
||||
dest[i-1] += dest[i-2]; // odd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// Copy transformed coefficients from subbands LL,HL,LH,HH to interleaved format (L,H,L,H,...)
|
||||
void CWaveletTransform::SubbandsToInterleaved(int srcLevel, DataT* loRow, DataT* hiRow, UINT32 width) {
|
||||
const UINT32 wquot = width >> 1;
|
||||
const bool wrem = (width & 1);
|
||||
CSubband &ll = m_subband[srcLevel][LL], &hl = m_subband[srcLevel][HL];
|
||||
CSubband &lh = m_subband[srcLevel][LH], &hh = m_subband[srcLevel][HH];
|
||||
|
||||
if (hiRow) {
|
||||
#ifdef __PGFROISUPPORT__
|
||||
const bool storePos = wquot < ll.BufferWidth();
|
||||
UINT32 llPos = 0, hlPos = 0, lhPos = 0, hhPos = 0;
|
||||
|
||||
if (storePos) {
|
||||
// save current src buffer positions
|
||||
llPos = ll.GetBuffPos();
|
||||
hlPos = hl.GetBuffPos();
|
||||
lhPos = lh.GetBuffPos();
|
||||
hhPos = hh.GetBuffPos();
|
||||
}
|
||||
#endif
|
||||
|
||||
for (UINT32 i=0; i < wquot; i++) {
|
||||
*loRow++ = ll.ReadBuffer();// first access, than increment
|
||||
*loRow++ = hl.ReadBuffer();// first access, than increment
|
||||
*hiRow++ = lh.ReadBuffer();// first access, than increment
|
||||
*hiRow++ = hh.ReadBuffer();// first access, than increment
|
||||
}
|
||||
|
||||
if (wrem) {
|
||||
*loRow++ = ll.ReadBuffer();// first access, than increment
|
||||
*hiRow++ = lh.ReadBuffer();// first access, than increment
|
||||
}
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
if (storePos) {
|
||||
// increment src buffer positions
|
||||
ll.IncBuffRow(llPos);
|
||||
hl.IncBuffRow(hlPos);
|
||||
lh.IncBuffRow(lhPos);
|
||||
hh.IncBuffRow(hhPos);
|
||||
}
|
||||
#endif
|
||||
|
||||
} else {
|
||||
#ifdef __PGFROISUPPORT__
|
||||
const bool storePos = wquot < ll.BufferWidth();
|
||||
UINT32 llPos = 0, hlPos = 0;
|
||||
|
||||
if (storePos) {
|
||||
// save current src buffer positions
|
||||
llPos = ll.GetBuffPos();
|
||||
hlPos = hl.GetBuffPos();
|
||||
}
|
||||
#endif
|
||||
|
||||
for (UINT32 i=0; i < wquot; i++) {
|
||||
*loRow++ = ll.ReadBuffer();// first access, than increment
|
||||
*loRow++ = hl.ReadBuffer();// first access, than increment
|
||||
}
|
||||
if (wrem) *loRow++ = ll.ReadBuffer();
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
if (storePos) {
|
||||
// increment src buffer positions
|
||||
ll.IncBuffRow(llPos);
|
||||
hl.IncBuffRow(hlPos);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Compute and store ROIs for nLevels
|
||||
/// @param roi rectangular region of interest at level 0
|
||||
void CWaveletTransform::SetROI(PGFRect roi) {
|
||||
const UINT32 delta = (FilterSize >> 1) << m_nLevels;
|
||||
|
||||
// create tile indices
|
||||
delete[] m_indices;
|
||||
m_indices = new PGFRect[m_nLevels];
|
||||
|
||||
// enlarge rect: add margin
|
||||
roi.left = (roi.left > delta) ? roi.left - delta : 0;
|
||||
roi.top = (roi.top > delta) ? roi.top - delta : 0;
|
||||
roi.right += delta;
|
||||
roi.bottom += delta;
|
||||
|
||||
for (int l = 0; l < m_nLevels; l++) {
|
||||
PGFRect alignedROI;
|
||||
PGFRect& indices = m_indices[l];
|
||||
UINT32 nTiles = GetNofTiles(l);
|
||||
CSubband& subband = m_subband[l][LL];
|
||||
|
||||
// use roi to determine the necessary tile indices (for all subbands the same) and aligned ROI for LL subband
|
||||
subband.SetNTiles(nTiles); // must be called before TileIndex()
|
||||
subband.TileIndex(true, roi.left, roi.top, indices.left, indices.top, alignedROI.left, alignedROI.top);
|
||||
subband.TileIndex(false, roi.right, roi.bottom, indices.right, indices.bottom, alignedROI.right, alignedROI.bottom);
|
||||
subband.SetAlignedROI(alignedROI);
|
||||
ASSERT(l == 0 ||
|
||||
(m_indices[l-1].left >= 2*m_indices[l].left &&
|
||||
m_indices[l-1].top >= 2*m_indices[l].top &&
|
||||
m_indices[l-1].right <= 2*m_indices[l].right &&
|
||||
m_indices[l-1].bottom <= 2*m_indices[l].bottom));
|
||||
|
||||
// determine aligned ROI of other three subbands
|
||||
PGFRect aroi;
|
||||
UINT32 w, h;
|
||||
for (int b = 1; b < NSubbands; b++) {
|
||||
CSubband& sb = m_subband[l][b];
|
||||
sb.SetNTiles(nTiles); // must be called before TilePosition()
|
||||
sb.TilePosition(indices.left, indices.top, aroi.left, aroi.top, w, h);
|
||||
sb.TilePosition(indices.right - 1, indices.bottom - 1, aroi.right, aroi.bottom, w, h);
|
||||
aroi.right += w;
|
||||
aroi.bottom += h;
|
||||
sb.SetAlignedROI(aroi);
|
||||
}
|
||||
|
||||
// use aligned ROI of LL subband for next level
|
||||
roi.left = alignedROI.left >> 1;
|
||||
roi.top = alignedROI.top >> 1;
|
||||
roi.right = (alignedROI.right + 1) >> 1;
|
||||
roi.bottom = (alignedROI.bottom + 1) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __PGFROISUPPORT__
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* The Progressive Graphics File; http://www.libpgf.org
|
||||
*
|
||||
* $Date: 2006-05-18 16:03:32 +0200 (Do, 18 Mai 2006) $
|
||||
* $Revision: 194 $
|
||||
*
|
||||
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
* as published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// @file WaveletTransform.h
|
||||
/// @brief PGF wavelet transform class
|
||||
/// @author C. Stamm
|
||||
|
||||
#ifndef PGF_WAVELETTRANSFORM_H
|
||||
#define PGF_WAVELETTRANSFORM_H
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wignored-qualifiers"
|
||||
#endif
|
||||
|
||||
#include "PGFtypes.h"
|
||||
#include "Subband.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Constants
|
||||
const UINT32 FilterSizeL = 5; ///< number of coefficients of the low pass filter
|
||||
const UINT32 FilterSizeH = 3; ///< number of coefficients of the high pass filter
|
||||
const UINT32 FilterSize = __max(FilterSizeL, FilterSizeH);
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// PGF ROI and tile support. This is a helper class for CWaveletTransform.
|
||||
/// @author C. Stamm
|
||||
/// @brief ROI indices
|
||||
class CRoiIndices {
|
||||
};
|
||||
#endif //__PGFROISUPPORT__
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// PGF wavelet transform class.
|
||||
/// @author C. Stamm, R. Spuler
|
||||
/// @brief PGF wavelet transform
|
||||
class CWaveletTransform {
|
||||
friend class CSubband;
|
||||
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Constructor: Constructs a wavelet transform pyramid of given size and levels.
|
||||
/// @param width The width of the original image (at level 0) in pixels
|
||||
/// @param height The height of the original image (at level 0) in pixels
|
||||
/// @param levels The number of levels (>= 0)
|
||||
/// @param data Input data of subband LL at level 0
|
||||
CWaveletTransform(UINT32 width, UINT32 height, int levels, DataT* data = nullptr);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Destructor
|
||||
~CWaveletTransform() { Destroy(); }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Compute fast forward wavelet transform of LL subband at given level and
|
||||
/// stores result in all 4 subbands of level + 1.
|
||||
/// @param level A wavelet transform pyramid level (>= 0 && < Levels())
|
||||
/// @param quant A quantization value (linear scalar quantization)
|
||||
/// @return error in case of a memory allocation problem
|
||||
OSError ForwardTransform(int level, int quant);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Compute fast inverse wavelet transform of all 4 subbands of given level and
|
||||
/// stores result in LL subband of level - 1.
|
||||
/// @param level A wavelet transform pyramid level (> 0 && <= Levels())
|
||||
/// @param width A pointer to the returned width of subband LL (in pixels)
|
||||
/// @param height A pointer to the returned height of subband LL (in pixels)
|
||||
/// @param data A pointer to the returned array of image data
|
||||
/// @return error in case of a memory allocation problem
|
||||
OSError InverseTransform(int level, UINT32* width, UINT32* height, DataT** data);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Get pointer to one of the 4 subband at a given level.
|
||||
/// @param level A wavelet transform pyramid level (>= 0 && <= Levels())
|
||||
/// @param orientation A quarter of the subband (LL, LH, HL, HH)
|
||||
CSubband* GetSubband(int level, Orientation orientation) {
|
||||
ASSERT(level >= 0 && level < m_nLevels);
|
||||
return &m_subband[level][orientation];
|
||||
}
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Compute and store ROIs for nLevels
|
||||
/// @param rect rectangular region of interest (ROI) at level 0
|
||||
void SetROI(PGFRect rect);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Checks the relevance of a given tile at given level.
|
||||
/// @param level A valid subband level.
|
||||
/// @param tileX x-index of the given tile
|
||||
/// @param tileY y-index of the given tile
|
||||
const bool TileIsRelevant(int level, UINT32 tileX, UINT32 tileY) const { ASSERT(m_indices); ASSERT(level >= 0 && level < m_nLevels); return m_indices[level].IsInside(tileX, tileY); }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Get number of tiles in x- or y-direction at given level.
|
||||
/// This number is independent of the given ROI.
|
||||
/// @param level A valid subband level.
|
||||
UINT32 GetNofTiles(int level) const { ASSERT(level >= 0 && level < m_nLevels); return 1 << (m_nLevels - level - 1); }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/// Return ROI at given level.
|
||||
/// @param level A valid subband level.
|
||||
const PGFRect& GetAlignedROI(int level) const { return m_subband[level][LL].GetAlignedROI(); }
|
||||
|
||||
#endif // __PGFROISUPPORT__
|
||||
|
||||
private:
|
||||
void Destroy() {
|
||||
delete[] m_subband; m_subband = nullptr;
|
||||
#ifdef __PGFROISUPPORT__
|
||||
delete[] m_indices; m_indices = nullptr;
|
||||
#endif
|
||||
}
|
||||
void InitSubbands(UINT32 width, UINT32 height, DataT* data);
|
||||
void ForwardRow(DataT* buff, UINT32 width);
|
||||
void InverseRow(DataT* buff, UINT32 width);
|
||||
void InterleavedToSubbands(int destLevel, DataT* loRow, DataT* hiRow, UINT32 width);
|
||||
void SubbandsToInterleaved(int srcLevel, DataT* loRow, DataT* hiRow, UINT32 width);
|
||||
|
||||
#ifdef __PGFROISUPPORT__
|
||||
PGFRect *m_indices; ///< array of length m_nLevels of tile indices
|
||||
#endif //__PGFROISUPPORT__
|
||||
|
||||
int m_nLevels; ///< number of LL levels: one more than header.nLevels in PGFimage
|
||||
CSubband (*m_subband)[NSubbands]; ///< quadtree of subbands: LL HL LH HH
|
||||
};
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif //PGF_WAVELETTRANSFORM_H
|
||||
@@ -0,0 +1,155 @@
|
||||
/* ============================================================
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* https://www.digikam.org
|
||||
*
|
||||
* Date : 2009-05-29
|
||||
* Description : static helper methods for PGF image format.
|
||||
*
|
||||
* Copyright (C) 2009-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
||||
*
|
||||
* This program 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 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#include "pgfutils.h"
|
||||
|
||||
// C Ansi includes
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#ifndef Q_CC_MSVC
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
}
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QDebug>
|
||||
#include <QImage>
|
||||
#include <QByteArray>
|
||||
#include <QFile>
|
||||
#include <qplatformdefs.h>
|
||||
|
||||
// Windows includes
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
// LibPGF includes
|
||||
|
||||
#if defined(Q_CC_CLANG)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wkeyword-macro"
|
||||
#endif
|
||||
|
||||
#include "PGFimage.h"
|
||||
|
||||
#if defined(Q_CC_CLANG)
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
namespace Digikam
|
||||
{
|
||||
|
||||
namespace PGFUtils
|
||||
{
|
||||
|
||||
bool readPGFImageData(const QByteArray& data,
|
||||
QImage& img,
|
||||
bool verbose)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (data.isEmpty())
|
||||
{
|
||||
qWarning() << "PGFUtils: PGF image data to decode : size is null";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CPGFMemoryStream stream((UINT8*)data.data(), (size_t)data.size());
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
qInfo() << "PGFUtils: image data stream size is : " << stream.GetSize();
|
||||
}
|
||||
|
||||
CPGFImage pgfImg;
|
||||
|
||||
// NOTE: see bug #273765 : Loading PGF thumbs with OpenMP support through a separated thread do not work properly with libppgf 6.11.24
|
||||
|
||||
pgfImg.ConfigureDecoder(false);
|
||||
|
||||
pgfImg.Open(&stream);
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
qInfo() << "PGFUtils: PGF image is open";
|
||||
}
|
||||
|
||||
if (pgfImg.Channels() != 4)
|
||||
{
|
||||
qWarning() << "PGFUtils: PGF channels not supported";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
img = QImage(pgfImg.Width(), pgfImg.Height(), QImage::Format_ARGB32);
|
||||
pgfImg.Read();
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
qInfo() << "PGFUtils: PGF image is read";
|
||||
}
|
||||
|
||||
if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
|
||||
{
|
||||
int map[] = {3, 2, 1, 0};
|
||||
pgfImg.GetBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map);
|
||||
}
|
||||
else
|
||||
{
|
||||
int map[] = {0, 1, 2, 3};
|
||||
pgfImg.GetBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map);
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
qInfo() << "PGFUtils: PGF image is decoded";
|
||||
}
|
||||
}
|
||||
catch (IOException& e)
|
||||
{
|
||||
int err = e.error;
|
||||
|
||||
if (err >= AppError)
|
||||
{
|
||||
err -= AppError;
|
||||
}
|
||||
|
||||
qWarning() << "PGFUtils: Error running libpgf (" << err << ")!";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace PGFUtils
|
||||
|
||||
} // namespace Digikam
|
||||
@@ -0,0 +1,51 @@
|
||||
/* ============================================================
|
||||
*
|
||||
* This file is a part of digiKam project
|
||||
* https://www.digikam.org
|
||||
*
|
||||
* Date : 2009-05-29
|
||||
* Description : static helper methods for PGF image format.
|
||||
*
|
||||
* Copyright (C) 2009-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
||||
*
|
||||
* This program 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 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* ============================================================ */
|
||||
|
||||
#ifndef DIGIKAM_PGF_UTILS_H
|
||||
#define DIGIKAM_PGF_UTILS_H
|
||||
|
||||
// Qt includes
|
||||
|
||||
#include <QString>
|
||||
#include <QImage>
|
||||
#include <QByteArray>
|
||||
|
||||
namespace Digikam
|
||||
{
|
||||
|
||||
namespace PGFUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* PGF image data to QImage using memory stream.
|
||||
* NOTE: Only use this method to manage PGF thumbnails stored in database.
|
||||
*/
|
||||
bool readPGFImageData(const QByteArray& data,
|
||||
QImage& img,
|
||||
bool verbose=false);
|
||||
|
||||
} // namespace PGFUtils
|
||||
|
||||
} // namespace Digikam
|
||||
|
||||
#endif // DIGIKAM_PGF_UTILS_H
|
||||
Reference in New Issue
Block a user