[-]
[+]
|
Changed |
_service:tar_git:harbour-stopmotion.spec
|
|
[-]
[+]
|
Changed |
_service
^
|
@@ -1,8 +1,8 @@
<services>
<service name="tar_git">
<param name="url">https://github.com/poetaster/harbour-stopmotion.git</param>
- <param name="branch">main</param>
- <param name="revision">0.2.1</param>
+ <param name="branch">imageprovider</param>
+ <param name="revision">0.3.0</param>
<param name="debian">N</param>
<param name="dumb">N</param>
</service>
|
|
Deleted |
_service:tar_git:harbour-stopmotion-0.2.1.tar.bz2/cover.png
^
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-stopmotion-0.3.0.tar.bz2/harbour-stopmotion.desktop
^
|
@@ -2,7 +2,7 @@
Type=Application
X-Nemo-Application-Type=silica-qt5
Icon=harbour-stopmotion
-Exec=sailfish-qml harbour-stopmotion
+Exec=harbour-stopmotion
Name=Stopmotion
# translation example:
# your app name in German locale (de)
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-stopmotion-0.3.0.tar.bz2/harbour-stopmotion.pro
^
|
@@ -12,7 +12,15 @@
# The name of your application
TARGET = harbour-stopmotion
-CONFIG += sailfishapp_qml
+CONFIG += sailfishapp
+
+SOURCES += \
+ src/harbour-stopmotion.cpp \
+ src/nemoimagemetadata.cpp
+HEADERS += \
+ src/IconProvider.h \
+ src/ImageProvider.h \
+ src/nemoimagemetadata.h
DISTFILES += qml/harbour-stopmotion.qml \
qml/components/Banner.qml \
@@ -26,7 +34,6 @@
qml/pages/SlideShowPage.qml \
qml/pages/PlaySlideShowPage.qml \
qml/img/*.png \
- qml/pages/SlideshowViewPage.qml \
qml/utils/localdb.js \
qml/utils/constants.js \
qml/sound/*.wav \
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-stopmotion-0.3.0.tar.bz2/qml/harbour-stopmotion.qml
^
|
@@ -10,11 +10,14 @@
{
property bool debug:false
- initialPage: Component {
+ initialPage: Component
+ {
id : sscr
- ShootScreen {
+ ShootScreen
+ {
id:shootScr
- Component.onCompleted: {
+ Component.onCompleted:
+ {
videoOutput.source=oCamera;
}
}
@@ -25,36 +28,40 @@
_defaultPageOrientations: Orientation.All
// not using yet, but might add
- Connections {
+ Connections
+ {
id: mainWinConnections
target: null
ignoreUnknownSignals: true
- onImageChanged: {
+ onImageChanged:
+ {
if (debug) console.log("Image changed, current image:", url)
//coverPage.setImage(url)
}
- onSlideshowRunningToggled: {
+ onSlideshowRunningToggled:
+ {
if (debug) console.log("Slideshow running:", runningStatus)
//coverPage.toggleSlideshowRunning(runningStatus)
}
}
- QtObject {
+ QtObject
+ {
id: cameraState
signal slidesShow(bool slideshowRunning)
}
- Connections {
+ Connections
+ {
target: cameraState
- onSlidesShow: {
+ onSlidesShow:
+ {
if (debug) console.log("cameraState:", slideshowRunning)
videoOutput.visible = !slideshowRunning
}
}
-
-
-
- VideoOutput {
+ VideoOutput
+ {
id:videoOutput
anchors.fill: parent
z : -1
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-stopmotion-0.3.0.tar.bz2/qml/pages/CanvasSlideshowPage.qml
^
|
@@ -21,7 +21,7 @@
property string imageSource: ""
property string imageSource2: ""
property int imageIndex: -1
- property bool slideshowRunning:true
+ property bool slideshowRunning:false
property ListModel imageModel
property ListModel musicModel
property var slideshowOrderArray: []
@@ -29,11 +29,11 @@
// Settings.
property int slideshowInterval: 200 //Settings.getIntSetting(Constants.intervalKey, 5) * 1000
- property bool loopMusic: false //Settings.getBooleanSetting(Constants.loopMusicKey, true)
property int loop: 0 //Settings.getBooleanSetting(Constants.loopKey, true)
+ property bool loopMusic: false //Settings.getBooleanSetting(Constants.loopMusicKey, true)
property int fpsMode
property int saveFps
- property bool debug: true
+ property bool debug: false
property var portrait
property int dx: 0
property int dy: 0
@@ -175,20 +175,32 @@
Component.onCompleted: {
// preload images.
+ busyIndicator.running = true
var img = ""
for (var i=0;i<imageModel.count; i++)
{
img = imageModel.get(i).url
if (i==0)
infoLoad.source = img
- loadImage(img);
+ var insertImagePath = "image://paintImage/" + img
+ loadImage(insertImagePath);
}
+ slideshowRunning = true
+ busyIndicator.running = false
+
}
}
-
- /*
- Pause indicators.
- */
+ BusyIndicator
+ {
+ id:busyIndicator
+ size: BusyIndicatorSize.Large
+ anchors {
+ bottom: parent.bottom
+ right: parent.left
+ }
+ running: false
+ }
+ /* Pause indicators. */
IconButton
{
id: recordButton
@@ -297,7 +309,7 @@
}
}
- imageSource = imageModel.get(slideshowOrderArray[imageIndex]).url
+ imageSource = "image://paintImage/" + imageModel.get(slideshowOrderArray[imageIndex]).url
imageChanged(imageModel.get(slideshowOrderArray[imageIndex]).url)
var ctx = drawingCanvas.getContext('2d')
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-stopmotion-0.3.0.tar.bz2/qml/pages/ShootScreen.qml
^
|
@@ -21,8 +21,7 @@
property bool debug: false
- QtObject
- {
+ QtObject {
id:d
property real cDOCK_PANEL_SIZE: 800
}
@@ -37,8 +36,7 @@
}
- onStatusChanged:
- {
+ onStatusChanged: {
if(status === PageStatus.Active)
{
// not quite! but sometimes
@@ -49,8 +47,7 @@
}
}
- Camera
- {
+ Camera {
id: camera
imageProcessing.whiteBalanceMode: CameraImageProcessing.WhiteBalanceAuto
exposure {
@@ -65,8 +62,7 @@
}
- onOrientationChanged:
- {
+ onOrientationChanged: {
if (orientation===Orientation.Landscape){
if (debug) console.log("inverted image");
@@ -87,22 +83,19 @@
}
- Component
- {
+ Component {
id: internalPicker
- FolderPickerDialog
- {
+
+ FolderPickerDialog {
id: folderiDialog
title: "Save to:"
onAccepted: savePath = selectedPath
onRejected: savePath = StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
}
}
- Component
- {
+ Component {
id: externalPicker
- FolderPickerDialog
- {
+ FolderPickerDialog {
id: foldereDialog
path: "/run/media/defaultuser"
title: "Save to:"
@@ -111,16 +104,13 @@
}
}
- PropertyAnimation
- {
- id: closeDockAnimation;
+ PropertyAnimation { id: closeDockAnimation;
target: panel;
property: "x";
to: -panel.width;
duration: 300
}
- PropertyAnimation
- {
+ PropertyAnimation {
id: openDockAnimation;
target: panel;
property: "x";
@@ -128,8 +118,7 @@
duration: 300
}
- MouseArea
- {
+ MouseArea {
id : mA
anchors.fill: parent
property real downX : 0
@@ -140,13 +129,12 @@
drag.minimumX: -panel.width;
drag.maximumX: 0
drag.threshold: 3.0
- onPressed:
- {
+ onPressed: {
downX = mouse.x
gesture = "none"
}
- onMouseXChanged:
- {
+
+ onMouseXChanged: {
if (Math.abs(downX-mouse.x)>3.0){
if (downX<mouse.x)
gesture = "swiperight"
@@ -156,8 +144,7 @@
}
}
- onReleased:
- {
+ onReleased: {
if (gesture=="swiperight") {
if (panel.x < 0)
openDockAnimation.running=true;
@@ -180,8 +167,7 @@
visible: camera.imageCapture.ready
onClicked: camera.imageCapture.capture()
}*/
- IconButton
- {
+ IconButton {
id: recordButton
icon.source: Qt.resolvedUrl("../img/play-button.png")
scale: 0.75
@@ -195,17 +181,13 @@
visible: !pStopmotion.busyEncoding
- onClicked:
- {
- if (mA.state==="Ready")
- {
+ onClicked: {
+ if (mA.state==="Ready"){
pStopmotion.start()
mA.state = "Recording";
// use autofocus
//camera.searchAndLock();
- }
- else
- {
+ } else {
pStopmotion.stop();
mA.state= "Ready";
// reset counter
@@ -220,13 +202,11 @@
}
states:[
- State
- {
+ State {
name:"Horizontal"
when:orientation === Orientation.Landscape || orientation === Orientation.LandscapeInverted
- AnchorChanges
- {
+ AnchorChanges {
target: recordButton
anchors {
bottom: undefined
@@ -238,9 +218,7 @@
}
]
}
-
- BusyIndicator
- {
+ BusyIndicator {
id:busyIndicator
size: BusyIndicatorSize.Large
anchors.centerIn: parent
@@ -248,40 +226,54 @@
running: pStopmotion.running
}
- Label
- {
+ Label {
id : busyText
//text: qsTr("Processing video encoding\nYou can hide app now, we inform you when it finished");
text: counter
color: Theme.highlightColor
font.pixelSize: Theme.fontSizeTiny
- anchors
- {
+ anchors{
horizontalCenter:recordButton.horizontalCenter
verticalCenter: recordButton.verticalCenter
}
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-stopmotion-0.3.0.tar.bz2/qml/pages/SlideshowPage.qml
^
|
@@ -5,14 +5,12 @@
import "../components"
import "../utils/localdb.js" as Database
-import "../utils/constants.js" as Constants
+//import "../utils/constants.js" as Constants
import io.thp.pyotherside 1.5
Page {
id: slideshowDialog
-
- QtObject
- {
+ QtObject {
id:slides
property real cDOCK_PANEL_SIZE: 800
}
@@ -37,7 +35,7 @@
property Item remorse
- property bool debug: true
+ property bool debug: false
// python / export specific vars
@@ -279,25 +277,21 @@
}
}
- ComboBox
- {
+ ComboBox{
id: loopSwitch
width: parent.width * .33
- menu: ContextMenu
- {
+ menu: ContextMenu {
MenuItem { text: "Loop off" ;
onClicked: loop = 0 }
MenuItem { text: "Loop on" ;
onClicked: loop = 1 }
}
- onCurrentIndexChanged:
- {
+ onCurrentIndexChanged: {
console.log(currentIndex)
Database.setProp('loop',String(currentIndex))
}
- Component.onCompleted:
- {
+ Component.onCompleted: {
loop = Database.getProp('loop')
loopSwitch.currentIndex = loop
}
@@ -365,8 +359,7 @@
}
*/
- CollapsingHeader
- {
+ CollapsingHeader {
id: slideshowImagesCollapsingHeader
text: qsTrId("slideshow-images") + "(" + imageListModel.count + ")"
collapsingItem: imageGrid
@@ -374,8 +367,7 @@
interactive: imageListModel.count > 0
menuItems: [clearImages]
- MenuItem
- {
+ MenuItem {
id: clearImages
text: qsTrId("menu-clear")
onClicked: {
@@ -385,9 +377,9 @@
}
}
- SilicaGridView
- {
+ SilicaGridView {
id: imageGrid
+
property Item expandedItem
width: parent.width
@@ -399,15 +391,13 @@
Behavior on height { SmoothedAnimation { duration: 300 } }
- delegate: Item
- {
+ delegate: Item {
id: dummy
width: slideshowDialog.imageWidth
height: thumbnail.isExpanded ? thumbnail.height + gridContextMenu.height : thumbnail.height
z: thumbnail.isExpanded ? 1000 : 1
- Thumbnail
- {
+ Thumbnail {
id: thumbnail
property bool isExpanded: imageGrid.expandedItem == thumbnail
@@ -426,8 +416,7 @@
infoLoad.source = url
}
- MouseArea
- {
+ MouseArea {
anchors.fill: parent
onPressAndHold: {
imageGrid.expandedItem = thumbnail
@@ -441,8 +430,7 @@
}
}
/* we need this to obtain the orientation to pass to ffmpeg */
- Image
- {
+ Image {
id: infoLoad
visible: false
asynchronous: true
@@ -458,14 +446,12 @@
}
}
- ContextMenu
- {
+ ContextMenu {
id: gridContextMenu
property int index: -1
- MenuItem
- {
+ MenuItem {
text: qsTr("Remove image")
onClicked: {
if (debug) console.log("Remove image from the slideshow...")
@@ -479,8 +465,7 @@
}
}
}
- Rectangle
- {
+ Rectangle {
id: progressDisplay
visible: (finishedLoading === false)
anchors.right: parent.right
@@ -505,11 +490,9 @@
}
}
*/
- Component
- {
+ Component {
id: multiImagePickerDialog
- MultiImagePickerDialog
- {
+ MultiImagePickerDialog {
onAccepted: {
var urls = []
var index = 0;
@@ -524,11 +507,9 @@
}
}
- Component
- {
+ Component {
id: filesystemImagePickerDialog
- MultiFilePickerDialog
- {
+ MultiFilePickerDialog {
nameFilters: imageFileFilters
onAccepted: {
if (debug) console.log("File system image picker accepted...")
@@ -590,8 +571,7 @@
// These are ALL the functions from clipper (Videoworks).
Python {
id: py
- Component.onCompleted:
- {
+ Component.onCompleted: {
addImportPath(Qt.resolvedUrl('../py'));
importModule('videox', function () {});
|
[-]
[+]
|
Added |
_service:tar_git:harbour-stopmotion-0.3.0.tar.bz2/src/IconProvider.h
^
|
@@ -0,0 +1,41 @@
+#ifndef ICONPROVIDER_H
+#define ICONPROVIDER_H
+
+#include <sailfishapp.h>
+#include <QQuickImageProvider>
+#include <QPainter>
+#include <QColor>
+
+class IconProvider : public QQuickImageProvider
+{
+public:
+ IconProvider() : QQuickImageProvider(QQuickImageProvider::Pixmap)
+ {
+ }
+
+ QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
+ {
+ QStringList parts = id.split('?');
+
+ QPixmap sourcePixmap(SailfishApp::pathTo("qml/icons/" + parts.at(0) + ".png").toString(QUrl::RemoveScheme));
+
+ if (size)
+ *size = sourcePixmap.size();
+
+ if (parts.length() > 1)
+ if (QColor::isValidColor(parts.at(1)))
+ {
+ QPainter painter(&sourcePixmap);
+ painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
+ painter.fillRect(sourcePixmap.rect(), parts.at(1));
+ painter.end();
+ }
+
+ if (requestedSize.isValid())
+ return sourcePixmap.scaled(requestedSize.width(), requestedSize.height(), Qt::IgnoreAspectRatio);
+ else
+ return sourcePixmap;
+ }
+};
+
+#endif // ICONPROVIDER_H
|
[-]
[+]
|
Added |
_service:tar_git:harbour-stopmotion-0.3.0.tar.bz2/src/ImageProvider.h
^
|
@@ -0,0 +1,109 @@
+#ifndef IMAGEPROVIDER_H
+#define IMAGEPROVIDER_H
+
+#include <sailfishapp.h>
+#include <QQuickImageProvider>
+#include <QImageReader>
+#include "nemoimagemetadata.h"
+#include <QDebug>
+
+class ImageProvider : public QQuickImageProvider
+{
+public:
+ ImageProvider() : QQuickImageProvider(QQuickImageProvider::Image)
+ {
+ }
+
+ static QImage rotate(const QImage &src, NemoImageMetadata::Orientation orientation)
+ {
+ QTransform trans;
+ QImage dst, tmp;
+
+ /* For square images 90-degree rotations of the pixel could be
+ done in-place, and flips could be done in-place for any image
+ instead of using the QImage routines which make copies of the
+ data. */
+
+ switch (orientation)
+ {
+ case NemoImageMetadata::TopRight:
+ /* horizontal flip */
+ dst = src.mirrored(true, false);
+ break;
+ case NemoImageMetadata::BottomRight:
+ /* horizontal flip, vertical flip */
+ dst = src.mirrored(true, true);
+ break;
+ case NemoImageMetadata::BottomLeft:
+ /* vertical flip */
+ dst = src.mirrored(false, true);
+ break;
+ case NemoImageMetadata::LeftTop:
+ /* rotate 90 deg clockwise and flip horizontally */
+ trans.rotate(90.0);
+ tmp = src.transformed(trans);
+ dst = tmp.mirrored(true, false);
+ break;
+ case NemoImageMetadata::RightTop:
+ /* rotate 90 deg anticlockwise */
+ trans.rotate(90.0);
+ dst = src.transformed(trans);
+ break;
+ case NemoImageMetadata::RightBottom:
+ /* rotate 90 deg anticlockwise and flip horizontally */
+ trans.rotate(-90.0);
+ tmp = src.transformed(trans);
+ dst = tmp.mirrored(true, false);
+ break;
+ case NemoImageMetadata::LeftBottom:
+ /* rotate 90 deg clockwise */
+ trans.rotate(-90.0);
+ dst = src.transformed(trans);
+ break;
+ default:
+ dst = src;
+ break;
+ }
+
+ return dst;
+ }
+
+ QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize)
+ {
+ Q_UNUSED(requestedSize)
+
+ QString filename = QUrl(id).toString(QUrl::RemoveScheme);
+ QImage img;
+ QSize originalSize;
+ QByteArray format;
+ QImageReader ir(filename);
+
+ if (!ir.canRead())
+ return img;
+
+ originalSize = ir.size();
+ format = ir.format();
+
+ if (size)
+ *size = originalSize;
+
+ img = ir.read();
+
+ qDebug() << Q_FUNC_INFO << "image format:" << format;
+
+ // @attah's concise solution to r<->b channel swap issues
+ img = img.convertToFormat(QImage::Format_RGB888);
+
+ //img.convertToFormat()
+ NemoImageMetadata meta(filename, format);
+
+ if (meta.orientation() != NemoImageMetadata::TopLeft)
+ img = rotate(img, meta.orientation());
+ if (requestedSize.isValid())
+ return img.scaled(requestedSize.width(), requestedSize.height(), Qt::KeepAspectRatio);
+ else
+ return img;
+ }
+};
+
+#endif // IMAGEPROVIDER_H
|
[-]
[+]
|
Added |
_service:tar_git:harbour-stopmotion-0.3.0.tar.bz2/src/harbour-stopmotion.cpp
^
|
@@ -0,0 +1,85 @@
+/*
+Copyright (c) 2014-2015 kimmoli kimmo.lindholm@gmail.com @likimmo
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <sailfishapp.h>
+#include <QtQml>
+#include <QScopedPointer>
+#include <QQuickView>
+#include <QQmlEngine>
+#include <QGuiApplication>
+#include <QQmlContext>
+#include <QCoreApplication>
+#include "IconProvider.h"
+#include "ImageProvider.h"
+
+QList<int> GetSailfishVersion();
+
+int main(int argc, char *argv[])
+{
+
+ QScopedPointer<QGuiApplication> app(SailfishApp::application(argc, argv));
+
+ QTranslator translator;
+ translator.load(QLocale::system().name(), SailfishApp::pathTo("i18n").toString(QUrl::RemoveScheme));
+ app->installTranslator(&translator);
+
+ QScopedPointer<QQuickView> view(SailfishApp::createView());
+
+ QQmlEngine *engine = view->engine();
+ engine->addImageProvider(QLatin1String("paintIcons"), new IconProvider);
+ engine->addImageProvider(QLatin1String("paintImage"), new ImageProvider);
+
+ view->setSource(SailfishApp::pathTo("qml/harbour-stopmotion.qml"));
+
+ /*
+ if (GetSailfishVersion().at(1) > 0 || GetSailfishVersion().at(0) > 1)
+ {
+ // Revert some defaults due Sailfish moving to Qt5.2 not to allow releasing graphics resources.
+ // source https://lists.sailfishos.org/pipermail/devel/2014-May/004123.html
+ view->setPersistentOpenGLContext(true);
+ view->setPersistentSceneGraph(true);
+ }*/
+
+ view->show();
+ return app->exec();
+}
+
+QList<int> GetSailfishVersion()
+{
+ QList<int> version;
+
+ version << -1 << -1 << -1 << -1;
+
+ QFile inputFile( "/etc/sailfish-release" );
+
+ if ( inputFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
+ {
+ QTextStream in( &inputFile );
+
+ while (not in.atEnd())
+ {
+ QString line = in.readLine();
+ if (line.startsWith("VERSION_ID="))
+ {
+ version.clear();
+ foreach (QString tmp, line.split('=').at(1).split('.'))
+ {
+ version << tmp.toInt();
+ }
+ break;
+ }
+ }
+ inputFile.close();
+ }
+
+ return version;
+}
+
+
|
[-]
[+]
|
Added |
_service:tar_git:harbour-stopmotion-0.3.0.tar.bz2/src/nemoimagemetadata.cpp
^
|
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2012 Hannu Mallat <hmallat@gmail.com>
+ *
+ * You may use this file under the terms of the BSD license as follows:
+ *
+ * "Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Nemo Mobile nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+ */
+
+#include "nemoimagemetadata.h"
+
+#include <QFile>
+#include <QString>
+#include <QtEndian>
+
+#define EXIF_TIFF_LSB_MAGIC "Exif\x00\x00II\x2a\x00"
+#define EXIF_TIFF_MSB_MAGIC "Exif\x00\x00MM\x00\x2a"
+#define EXIF_TIFF_MAGIC_LEN 10
+
+#define TIFF_HEADER_LEN 8
+#define TIFF_IFD_ENTRY_LEN 12
+
+#define EXIF_IDENTIFIER_LEN 6
+
+#define EXIF_TYPE_SHORT 3
+
+#define EXIF_TAG_ORIENTATION 0x112
+
+/* Standalone markers without length information */
+#define JPEG_MARKER_TEM 0x01
+#define JPEG_MARKER_RST0 0xd0
+#define JPEG_MARKER_RST1 0xd1
+#define JPEG_MARKER_RST2 0xd2
+#define JPEG_MARKER_RST3 0xd3
+#define JPEG_MARKER_RST4 0xd4
+#define JPEG_MARKER_RST5 0xd5
+#define JPEG_MARKER_RST6 0xd6
+#define JPEG_MARKER_RST7 0xd7
+#define JPEG_MARKER_SOI 0xd8
+#define JPEG_MARKER_EOI 0xd9
+
+#define JPEG_MARKER_APP1 0xe1
+
+#define JPEG_MARKER_PREFIX 0xff
+
+static uchar getMarker(QFile &f)
+{
+ /* CCITT T.81 Annex B: "All markers are assigned two-byte
+ codes: an XFF byte followed by a byte which is not equal
+ to 0 or XFF (see Table B.1). Any marker may optionally be
+ preceded by any number of fill bytes, which are bytes
+ assigned code XFF." */
+
+ uchar c;
+
+ if (!f.getChar(reinterpret_cast<char*>(&c)) || c != JPEG_MARKER_PREFIX)
+ return 0;
+
+ while (c == JPEG_MARKER_PREFIX)
+ if (f.getChar(reinterpret_cast<char*>(&c)) == false)
+ return 0;
+
+ if (c == 0) /* Not a marker */
+ return 0;
+
+ return c;
+}
+
+static quint16 getMarkerLength(QFile &f)
+{
+ char buf[2];
+
+ if (f.read(buf, 2) != 2)
+ return 0;
+ return qFromBigEndian<quint16>(reinterpret_cast<uchar *>(buf));
+}
+
+static bool getExifData(QFile &f, QByteArray &data)
+{
+ uchar marker;
+ quint16 len;
+ qint64 skip;
+
+ f.seek(0);
+
+ marker = getMarker(f);
+ if (marker != JPEG_MARKER_SOI)
+ return false;
+ while (true) {
+ marker = getMarker(f);
+ if (marker == 0)
+ return false;
+
+ switch (marker) {
+
+ case JPEG_MARKER_SOI: /* shouldn't see this anymore */
+ case JPEG_MARKER_EOI: /* end of the line, no EXIF in sight */
+ return false;
+
+ case JPEG_MARKER_TEM:
+ case JPEG_MARKER_RST0:
+ case JPEG_MARKER_RST1:
+ case JPEG_MARKER_RST2:
+ case JPEG_MARKER_RST3:
+ case JPEG_MARKER_RST4:
+ case JPEG_MARKER_RST5:
+ case JPEG_MARKER_RST6:
+ case JPEG_MARKER_RST7:
+ /* Standalones, just skip */
+ break;
+
+ case JPEG_MARKER_APP1:
+ /* CCITT T.81 Annex B:
+ The first parameter in a marker segment is the
+ two-byte length parameter. This length parameter
+ encodes the number of bytes in the marker segment,
+ including the length parameter and excluding the
+ two-byte marker. */
+ len = getMarkerLength(f);
+ if (len < 2)
+ return false;
+ data.resize(len - 2);
+ if (f.read(data.data(), len - 2) != len - 2)
+ return false;
+ return true;
+
+ default:
+ /* Marker segment, just skip. */
+ len = getMarkerLength(f);
+ if (len < 2)
+ return false;
+ skip = f.pos() + static_cast<qint64>(len) - 2;
+ f.seek(skip);
+ if (f.pos() != skip)
+ return false;
+ break;
+ }
+ }
+}
+
+static
+NemoImageMetadata::Orientation exifOrientationFromJpeg(const QString &fname)
+{
+ QByteArray data;
+ const uchar *ptr;
+ quint32 len;
+ quint32 pos;
+ bool msbFirst;
+ quint32 ifdOff;
+ quint16 fieldCount;
+ quint16 o = static_cast<quint16>(NemoImageMetadata::TopLeft);
+
+ QFile f(fname);
+ if (f.open(QIODevice::ReadOnly) == false || getExifData(f, data) == false)
+ goto exit;
+
+ ptr = reinterpret_cast<const uchar *>(data.constData());
+ len = data.length();
+ pos = 0;
+
+ /* 6 bytes for Exif identifier, 8 bytes for TIFF header */
+ if (len < EXIF_IDENTIFIER_LEN + TIFF_HEADER_LEN)
+ goto exit;
+
+ if (memcmp(ptr + pos, EXIF_TIFF_LSB_MAGIC, EXIF_TIFF_MAGIC_LEN) == 0)
+ msbFirst = false;
+ else if (memcmp(ptr + pos, EXIF_TIFF_MSB_MAGIC, EXIF_TIFF_MAGIC_LEN) == 0)
+ msbFirst = true;
+ else
+ goto exit;
+
+ ifdOff = msbFirst
+ ? qFromBigEndian<quint32>(ptr + pos + EXIF_TIFF_MAGIC_LEN)
+ : qFromLittleEndian<quint32>(ptr + pos + EXIF_TIFF_MAGIC_LEN);
+
+ /* IFD offset is measured from TIFF header and can't go backwards */
+ if (ifdOff < TIFF_HEADER_LEN)
|
[-]
[+]
|
Added |
_service:tar_git:harbour-stopmotion-0.3.0.tar.bz2/src/nemoimagemetadata.h
^
|
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 Hannu Mallat <hmallat@gmail.com>
+ *
+ * You may use this file under the terms of the BSD license as follows:
+ *
+ * "Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Nemo Mobile nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+ */
+
+#ifndef NEMOIMAGEMETADATA_H
+#define NEMOIMAGEMETADATA_H
+
+class QString;
+class QByteArray;
+
+#include <QMetaType>
+
+class NemoImageMetadata
+{
+public:
+
+ enum Orientation {
+ TopLeft = 1,
+ TopRight,
+ BottomRight,
+ BottomLeft,
+ LeftTop,
+ RightTop,
+ RightBottom,
+ LeftBottom
+ };
+
+ NemoImageMetadata();
+
+ NemoImageMetadata(const QString &filename, const QByteArray &format);
+
+ NemoImageMetadata(const NemoImageMetadata &other);
+
+ ~NemoImageMetadata();
+
+ NemoImageMetadata &operator=(const NemoImageMetadata &other);
+
+ Orientation orientation(void) const {
+ return m_orientation;
+ }
+
+private:
+
+ Orientation m_orientation;
+
+};
+
+Q_DECLARE_METATYPE(NemoImageMetadata)
+
+Q_DECLARE_METATYPE(NemoImageMetadata::Orientation)
+
+#endif // NEMOIMAGEMETADATA_H
|