Commit 1421157d authored by Halla Rempt's avatar Halla Rempt

Add import/export for R16 heightmaps

These are used for games. The format is undocumented, but basically
just a stream of 16 bit integers.
parent 2549a921
......@@ -19,4 +19,11 @@
</magic>
<glob pattern="*.krassistant"/>
</mime-type>
<mime-type type="image/x-r16">
<comment xml:lang="en">r16 heightmap</comment>
<magic priority="50">
<match type="string" value="r16heightmap" offset="40"/>
</magic>
<glob pattern="*.r16"/>
</mime-type>
</mime-info>
......@@ -59,6 +59,7 @@ add_subdirectory(odg)
add_subdirectory(flipbook)
add_subdirectory(qml)
add_subdirectory(tga)
add_subdirectory(heightmap)
#if(OIIO_FOUND)
# add_subdirectory(oiio)
......
include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
set(kritaheightmapimport_PART_SRCS
kis_heightmap_import.cpp
)
kde4_add_plugin(kritaheightmapimport ${kritaheightmapimport_PART_SRCS})
kde4_add_ui_files(kritaheightmapimport_PART_SRCS kis_wdg_options_heightmap.ui )
target_link_libraries(kritaheightmapimport kritaui )
install(TARGETS kritaheightmapimport DESTINATION ${PLUGIN_INSTALL_DIR})
########### next target ###############
set(kritaheightmapexport_PART_SRCS
kis_heightmap_export.cpp
)
kde4_add_ui_files(kritaheightmapexport_PART_SRCS kis_wdg_options_heightmap.ui )
kde4_add_plugin(kritaheightmapexport ${kritaheightmapexport_PART_SRCS})
target_link_libraries(kritaheightmapexport kritaui )
install(TARGETS kritaheightmapexport DESTINATION ${PLUGIN_INSTALL_DIR})
########### install files ###############
install( FILES krita_heightmap_import.desktop krita_heightmap_export.desktop DESTINATION ${SERVICES_INSTALL_DIR})
install( PROGRAMS krita_heightmap.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
/*
* Copyright (c) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* 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; version 2 of the License.
*
* 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 Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_heightmap_export.h"
#include <qendian.h>
#include <QDataStream>
#include <kpluginfactory.h>
#include <kapplication.h>
#include <KoColorSpace.h>
#include <KoColorSpaceConstants.h>
#include <KoFilterChain.h>
#include <KoFilterManager.h>
#include <KoColorSpaceTraits.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <kdialog.h>
#include <kmessagebox.h>
#include <kis_debug.h>
#include <kis_doc2.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <kis_properties_configuration.h>
#include <kis_config.h>
#include <kis_iterator_ng.h>
#include <kis_random_accessor_ng.h>
#include "ui_kis_wdg_options_heightmap.h"
K_PLUGIN_FACTORY(KisHeightMapExportFactory, registerPlugin<KisHeightMapExport>();)
K_EXPORT_PLUGIN(KisHeightMapExportFactory("krita"))
KisHeightMapExport::KisHeightMapExport(QObject *parent, const QVariantList &) : KoFilter(parent)
{
}
KisHeightMapExport::~KisHeightMapExport()
{
}
KoFilter::ConversionStatus KisHeightMapExport::convert(const QByteArray& from, const QByteArray& to)
{
dbgFile << "HeightMap export! From:" << from << ", To:" << to << "";
if (from != "application/x-krita")
return KoFilter::NotImplemented;
KisDoc2 *inputDoc = dynamic_cast<KisDoc2*>(m_chain->inputDocument());
QString filename = m_chain->outputFile();
if (!inputDoc)
return KoFilter::NoDocumentCreated;
if (filename.isEmpty()) return KoFilter::FileNotFound;
KisImageWSP image = inputDoc->image();
Q_CHECK_PTR(image);
if (inputDoc->image()->width() != inputDoc->image()->height()) {
inputDoc->setErrorMessage(i18n("Cannot export this image to a heightmap: it is not square"));
return KoFilter::WrongFormat;
}
if (inputDoc->image()->colorSpace()->colorModelId() != GrayAColorModelID) {
inputDoc->setErrorMessage(i18n("Cannot export this image to a heightmap: it is not grayscale"));
return KoFilter::WrongFormat;
}
if (inputDoc->image()->colorSpace()->colorDepthId() != Integer16BitsColorDepthID) {
inputDoc->setErrorMessage(i18n("Cannot export this image to a heightmap: it is not 16 bits integer"));
return KoFilter::WrongFormat;
}
KDialog* kdb = new KDialog(0);
kdb->setWindowTitle(i18n("HeightMap Export Options"));
kdb->setButtons(KDialog::Ok | KDialog::Cancel);
Ui::WdgOptionsHeightMap optionsHeightMap;
QWidget* wdg = new QWidget(kdb);
optionsHeightMap.setupUi(wdg);
kdb->setMainWidget(wdg);
kapp->restoreOverrideCursor();
QString filterConfig = KisConfig().exportConfiguration("HeightMap");
KisPropertiesConfiguration cfg;
cfg.fromXML(filterConfig);
optionsHeightMap.intSize->setValue(image->width());
int endianness = cfg.getInt("endianness", 0);
QDataStream::ByteOrder bo = QDataStream::LittleEndian;
optionsHeightMap.radioPC->setChecked(true);
if (endianness == 0) {
bo = QDataStream::BigEndian;
optionsHeightMap.radioMac->setChecked(true);
}
if (!m_chain->manager()->getBatchMode()) {
if (kdb->exec() == QDialog::Rejected) {
return KoFilter::OK; // FIXME Cancel doesn't exist :(
}
}
else {
qApp->processEvents(); // For vector layers to be updated
}
inputDoc->image()->waitForDone();
if (optionsHeightMap.radioMac->isChecked()) {
cfg.setProperty("endianness", 0);
bo = QDataStream::BigEndian;
}
else {
cfg.setProperty("endianness", 1);
bo = QDataStream::LittleEndian;
}
KisConfig().setExportConfiguration("HeightMap", cfg);
image->refreshGraph();
image->lock();
KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
image->unlock();
QFile f(filename);
f.open(QIODevice::WriteOnly);
QDataStream s(&f);
s.setByteOrder(bo);
KisRandomConstAccessorSP it = pd->createRandomConstAccessorNG(0, 0);
for (int i = 0; i < image->height(); ++i) {
for (int j = 0; j < image->width(); ++j) {
it->moveTo(i, j);
s << KoGrayU16Traits::gray(const_cast<quint8*>(it->rawDataConst()));
}
}
f.close();
return KoFilter::OK;
}
/*
* Copyright (c) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* 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; version 2 of the License.
*
* 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 Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _KIS_HeightMap_EXPORT_H_
#define _KIS_HeightMap_EXPORT_H_
#include <QVariant>
#include <KoFilter.h>
class KisHeightMapExport : public KoFilter
{
Q_OBJECT
public:
KisHeightMapExport(QObject *parent, const QVariantList &);
virtual ~KisHeightMapExport();
public:
virtual KoFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to);
};
#endif
/*
* Copyright (c) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* 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 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 Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_heightmap_import.h"
#include <ctype.h>
#include <QApplication>
#include <QFile>
#include <qendian.h>
#include <QCursor>
#include <kapplication.h>
#include <kpluginfactory.h>
#include <kio/netaccess.h>
#include <kio/deletejob.h>
#include <KoFilterManager.h>
#include <KoColorSpaceRegistry.h>
#include <KoFilterChain.h>
#include <KoColorModelStandardIds.h>
#include <KoColorSpace.h>
#include <KoColorSpaceTraits.h>
#include <kis_debug.h>
#include <kis_doc2.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_paint_layer.h>
#include <kis_paint_device.h>
#include <kis_transaction.h>
#include <kis_iterator_ng.h>
#include <kis_random_accessor_ng.h>
#include <kis_config.h>
#include "ui_kis_wdg_options_heightmap.h"
K_PLUGIN_FACTORY(HeightMapImportFactory, registerPlugin<KisHeightMapImport>();)
K_EXPORT_PLUGIN(HeightMapImportFactory("krita"))
KisHeightMapImport::KisHeightMapImport(QObject *parent, const QVariantList &) : KoFilter(parent)
{
}
KisHeightMapImport::~KisHeightMapImport()
{
}
KoFilter::ConversionStatus KisHeightMapImport::convert(const QByteArray& from, const QByteArray& to)
{
Q_UNUSED(from);
dbgFile << "Importing using HeightMapImport!";
if (to != "application/x-krita")
return KoFilter::BadMimeType;
KisDoc2 * doc = dynamic_cast<KisDoc2*>(m_chain -> outputDocument());
if (!doc)
return KoFilter::NoDocumentCreated;
QString filename = m_chain -> inputFile();
if (filename.isEmpty()) {
return KoFilter::FileNotFound;
}
KUrl url(filename);
dbgFile << "Import: " << url;
if (url.isEmpty())
return KoFilter::FileNotFound;
if (!url.isLocalFile()) {
return KoFilter::FileNotFound;
}
kapp->restoreOverrideCursor();
KDialog* kdb = new KDialog(0);
kdb->setWindowTitle(i18n("R16 HeightMap Import Options"));
kdb->setButtons(KDialog::Ok | KDialog::Cancel);
Ui::WdgOptionsHeightMap optionsHeightMap;
QWidget* wdg = new QWidget(kdb);
optionsHeightMap.setupUi(wdg);
kdb->setMainWidget(wdg);
QString filterConfig = KisConfig().exportConfiguration("HeightMap");
KisPropertiesConfiguration cfg;
cfg.fromXML(filterConfig);
QFile f(url.toLocalFile());
int w = 0;
int h = 0;
if (!f.exists()) {
doc->setErrorMessage(i18n("File does not exist."));
return KoFilter::CreationError;
}
if (!f.isOpen()) {
f.open(QFile::ReadOnly);
}
optionsHeightMap.intSize->setValue(0);
int endianness = cfg.getInt("endianness", 0);
if (endianness == 0) {
optionsHeightMap.radioMac->setChecked(true);
}
else {
optionsHeightMap.radioPC->setChecked(true);
}
if (!m_chain->manager()->getBatchMode()) {
if (kdb->exec() == QDialog::Rejected) {
return KoFilter::OK; // FIXME Cancel doesn't exist :(
}
}
w = h = optionsHeightMap.intSize->value();
if ((w * h * 2) != f.size()) {
doc->setErrorMessage(i18n("Source file is not the right size for the specified width and height."));
return KoFilter::WrongFormat;
}
QDataStream::ByteOrder bo = QDataStream::LittleEndian;
cfg.setProperty("endianness", 1);
if (optionsHeightMap.radioMac->isChecked()) {
bo = QDataStream::BigEndian;
cfg.setProperty("endianness", 0);
}
KisConfig().setExportConfiguration("HeightMap", cfg);
doc->prepareForImport();
QDataStream s(&f);
s.setByteOrder(bo);
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), 0);
KisImageSP image = new KisImage(doc->createUndoStore(), w, h, colorSpace, "imported heightmap");
KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), 255);
KisRandomAccessorSP it = layer->paintDevice()->createRandomAccessorNG(0, 0);
for (int i = 0; i < h; ++i) {
for (int j = 0; j < w; ++j) {
it->moveTo(i, j);
quint16 pixel;
s >> pixel;
KoGrayU16Traits::setGray(it->rawData(), pixel);
KoGrayU16Traits::setOpacity(it->rawData(), OPACITY_OPAQUE_F, 1);
}
}
image->addNode(layer.data(), image->rootLayer().data());
doc->setCurrentImage(image);
return KoFilter::OK;
}
/*
* Copyright (c) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* 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 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 Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _KIS_HeightMap_IMPORT_H_
#define _KIS_HeightMap_IMPORT_H_
#include <QVariant>
#include <KoFilter.h>
class KisDoc2;
class KisHeightMapImport : public KoFilter
{
Q_OBJECT
public:
KisHeightMapImport(QObject *parent, const QVariantList &);
virtual ~KisHeightMapImport();
public:
virtual KoFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to);
};
#endif
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgOptionsHeightMap</class>
<widget class="QWidget" name="WdgOptionsHeightMap">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>242</width>
<height>122</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QRadioButton" name="radioMac">
<property name="toolTip">
<string>Big Endian</string>
</property>
<property name="text">
<string>Mac</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioPC">
<property name="toolTip">
<string>Little Endian</string>
</property>
<property name="text">
<string>PC</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Size:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="KIntNumInput" name="intSize"/>
</item>
<item row="2" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Endianness:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KIntNumInput</class>
<extends>QWidget</extends>
<header>knuminput.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
[Desktop Entry]
Categories=Qt;KDE;Office;Graphics;
Exec=krita %u
GenericName=
Icon=krita
MimeType=image/x-r16
Name=Krita
NoDisplay=true
StartupNotify=true
Terminal=false
Type=Application
X-KDE-ServiceTypes=Calligra/Application
X-Calligra-DefaultMimeTypes=image/x-r16
X-DBUS-ServiceName=org.krita.krita
X-DBUS-StartupType=multi
X-KDE-SubstituteUID=false
X-KDE-Username=
[Desktop Entry]
Icon=
Name=Krita HeightMap Export Filter
X-KDE-ServiceTypes=Calligra/Filter
Type=Service
X-KDE-Export=image/x-r16
X-KDE-Import=application/x-krita
X-KDE-Library=kritaheightmapexport
X-KDE-Weight=1
NoDisplay=true