Skip to content

Commit

Permalink
[Histogram] Replace QtCharts module with custom drawing implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
luspi committed Jan 16, 2017
1 parent 8c13ac7 commit 38b0478
Show file tree
Hide file tree
Showing 8 changed files with 298 additions and 281 deletions.
4 changes: 2 additions & 2 deletions CMake/ListFilesCPlusPlus.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ SET(photoqt_SOURCES ${photoqt_SOURCES} ${d}/loaddir.cpp)

SET(d "cplusplus/imageprovider")
SET(photoqt_SOURCES ${photoqt_SOURCES} ${d}/imageproviderfull.cpp ${d}/imageproviderthumbnail.cpp ${d}/imageprovidericon.h)
SET(photoqt_SOURCES ${photoqt_SOURCES} ${d}/pixmapcache.h ${d}/imageproviderempty.h)
SET(photoqt_SOURCES ${photoqt_SOURCES} ${d}/pixmapcache.h ${d}/imageproviderempty.h ${d}/imageproviderhistogram.h)

SET(d "cplusplus/imageprovider/loader")
SET(photoqt_SOURCES ${photoqt_SOURCES} ${d}/loadimage_qt.h ${d}/loadimage_gm.h ${d}/loadimage_xcf.h ${d}/loadimage_raw.h)
Expand Down Expand Up @@ -58,7 +58,7 @@ SET(photoqt_HEADERS ${photoqt_HEADERS} ${d}/loaddir.h)

SET(d "cplusplus/imageprovider")
SET(photoqt_HEADERS ${photoqt_HEADERS} ${d}/imageproviderthumbnail.h ${d}/imageproviderfull.h ${d}/imageprovidericon.h)
SET(photoqt_HEADERS ${photoqt_HEADERS} ${d}/pixmapcache.h ${d}/imageproviderempty.h)
SET(photoqt_HEADERS ${photoqt_HEADERS} ${d}/pixmapcache.h ${d}/imageproviderempty.h ${d}/imageproviderhistogram.h)

SET(d "cplusplus/imageprovider/loader")
SET(photoqt_HEADERS ${photoqt_HEADERS} ${d}/loadimage_qt.h ${d}/loadimage_gm.h ${d}/loadimage_xcf.h ${d}/loadimage_raw.h)
Expand Down
258 changes: 258 additions & 0 deletions cplusplus/imageprovider/imageproviderhistogram.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
/*
* 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 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef IMAGEPROVIDERHISTOGRAM_H
#define IMAGEPROVIDERHISTOGRAM_H

#include <QQuickImageProvider>
#include <QIcon>
#include <QFile>
#include <QPainter>
#include "imageproviderfull.h"
#include "../logger.h"


class ImageProviderHistogram : public QQuickImageProvider {

public:
explicit ImageProviderHistogram() : QQuickImageProvider(QQuickImageProvider::Pixmap) { }
~ImageProviderHistogram() { }

QPixmap requestPixmap(const QString &fpath, QSize *, const QSize &requestedSize) {

// Obtain type of histogram
bool color = false;
QString tmp = fpath;
if(tmp.startsWith("color")) {
color = true;
tmp = tmp.remove(0,5);
} else if(tmp.startsWith("grey")) {
color = false;
tmp = tmp.remove(0,4);
}

// If no path specified, return empty transparent image
if(tmp.trimmed() == "") {
QPixmap pix = QPixmap(1,1);
pix.fill(Qt::transparent);
return pix;
}

bool recalcvalues_filepath = false;
bool recalcvalues_color = false;
if(tmp != filepath)
recalcvalues_filepath = true;
if(color != colorversion)
recalcvalues_color = true;
filepath = tmp;
colorversion = color;

// Get width and height
int w = requestedSize.width();
int h = requestedSize.height();
if(w%256 != 0)
w = (w/256 +1)*256;

// Get the spacing of the data points
int interval = w/256;

if(recalcvalues_filepath || recalcvalues_color) {

// Retrieve the current image
if(recalcvalues_filepath) {
ImageProviderFull *prov = new ImageProviderFull;
QSize *tmp = new QSize();
histimg = prov->requestImage(filepath, tmp, QSize());
delete tmp;
}

// Read and store image dimensions
int wid = histimg.width();
int hei = histimg.height();

// Prepare the lists for the levels
levels_grey.clear();
levels_red.clear();
levels_green.clear();
levels_blue.clear();
for(int i = 0; i < 256; ++i)
levels_grey.push_back(0);
for(int i = 0; i < 256; ++i)
levels_red.push_back(0);
for(int i = 0; i < 256; ++i)
levels_green.push_back(0);
for(int i = 0; i < 256; ++i)
levels_blue.push_back(0);

// Loop over all rows of the image
for(int i = 0; i < hei; ++i) {

// Get the pixel data of row i of the image
QRgb *rowData = (QRgb*)histimg.scanLine(i);

// Loop over all columns
for(int j = 0; j < wid; ++j) {

// Get pixel data of pixel at column j in row i
QRgb pixelData = rowData[j];

// Get RGB values
int red = qRed(pixelData);
int green = qGreen(pixelData);
int blue = qBlue(pixelData);

// Add a pixel at current gray level
if(!colorversion) {
// Compute the gray level
int gray_level = qGray(red,green,blue);
++levels_grey[gray_level];
} else {
++levels_red[red];
++levels_green[green];
++levels_blue[blue];
}

}

}

// Figure out the greatest value for normalisation
greatestvalue = 0;
if(!colorversion) {
for(int i = 0; i < 256; ++i)
if(levels_grey[i] > greatestvalue)
greatestvalue = levels_grey[i];
} else {
for(int i = 0; i < 256; ++i)
if(levels_red[i] > greatestvalue)
greatestvalue = levels_red[i];
for(int i = 0; i < 256; ++i)
if(levels_green[i] > greatestvalue)
greatestvalue = levels_green[i];
for(int i = 0; i < 256; ++i)
if(levels_blue[i] > greatestvalue)
greatestvalue = levels_blue[i];
}

}

// Set up the needed polygons for filling them with color
// This has to ALWAYS been done even if only the size changed, as then the interval changes, too
polyGREY.clear();
polyRED.clear();
polyGREEN.clear();
polyBLUE.clear();
if(!colorversion) {
polyGREY << QPoint(0,h);
for(int i = 0; i < 256; ++i)
polyGREY << QPoint(i*interval,h*(1-((double)levels_grey[i]/(double)greatestvalue)));
polyGREY << QPoint(w,h);
} else {
polyRED << QPoint(0,h);
for(int i = 0; i < 256; ++i)
polyRED << QPoint(i*interval,h*(1-((double)levels_red[i]/(double)greatestvalue)));
polyRED << QPoint(w,h);
polyGREEN << QPoint(0,h);
for(int i = 0; i < 256; ++i)
polyGREEN << QPoint(i*interval,h*(1-((double)levels_green[i]/(double)greatestvalue)));
polyGREEN << QPoint(w,h);
polyBLUE << QPoint(0,h);
for(int i = 0; i < 256; ++i)
polyBLUE << QPoint(i*interval,h*(1-((double)levels_blue[i]/(double)greatestvalue)));
polyBLUE << QPoint(w,h);
}

// Create pixmap...
QPixmap pix(w,h);
// ... and fill it with transparent color
pix.fill(QColor(0,0,0,0));

// Start painter on return pixmap
QPainter paint(&pix);

// set lightly grey colored pen
paint.setPen(QColor(255,255,255,50));

// draw outside rectangle
paint.drawRect(1,1,w-2,h-2);

// draw mesh lines
int verticallines = 10;
int horizontallines = 5;
for(int i = 0; i < verticallines; ++i)
paint.drawLine(QPointF((i+1)*(w/(verticallines+1)), 0), QPointF((i+1)*(w/(verticallines+1)), h));
for(int i = 0; i < horizontallines; ++i)
paint.drawLine(QPointF(0, (i+1)*(h/(horizontallines+1))), QPointF(w, (i+1)*(h/(horizontallines+1))));

if(!colorversion) {

// set pen color
paint.setPen(QPen(QColor(50,50,50,255),2));
// draw values
paint.drawPolygon(polyGREY);
QPainterPath pathGREY;
pathGREY.addPolygon(polyGREY);
paint.fillPath(pathGREY,QColor(150,150,150,180));

} else {

// draw red part
paint.setPen(QPen(QColor(50,50,50,255),2));
paint.drawPolygon(polyRED);
QPainterPath pathRED;
pathRED.addPolygon(polyRED);
paint.fillPath(pathRED,QColor(255,0,0,120));

// draw green part
paint.setPen(QPen(QColor(50,50,50,255),2));
paint.drawPolygon(polyGREEN);
QPainterPath pathGREEN;
pathGREEN.addPolygon(polyGREEN);
paint.fillPath(pathGREEN,QColor(0,255,0,120));

// draw blue part
paint.setPen(QPen(QColor(50,50,50,255),2));
paint.drawPolygon(polyBLUE);
QPainterPath pathBLUE;
pathBLUE.addPolygon(polyBLUE);
paint.fillPath(pathBLUE,QColor(0,0,255,120));

}

paint.end();

return pix;

}

private:
QList<int> levels_grey;
QList<int> levels_red;
QList<int> levels_green;
QList<int> levels_blue;
QPolygon polyGREY;
QPolygon polyRED;
QPolygon polyGREEN;
QPolygon polyBLUE;
QString filepath;
bool colorversion;
QImage histimg;
int greatestvalue;

};


#endif
1 change: 1 addition & 0 deletions cplusplus/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ MainWindow::MainWindow(bool verbose, QWindow *parent) : QQuickView(parent) {
this->engine()->addImageProvider("full",new ImageProviderFull);
this->engine()->addImageProvider("icon",new ImageProviderIcon);
this->engine()->addImageProvider("empty", new ImageProviderEmpty);
this->engine()->addImageProvider("hist", new ImageProviderHistogram);

// Add settings access to QML
qmlRegisterType<Settings>("Settings", 1, 0, "Settings");
Expand Down
1 change: 1 addition & 0 deletions cplusplus/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "imageprovider/imageproviderfull.h"
#include "imageprovider/imageproviderempty.h"
#include "imageprovider/imageprovidericon.h"
#include "imageprovider/imageproviderhistogram.h"
#include "settings/settings.h"
#include "settings/fileformats.h"
#include "settings/colour.h"
Expand Down
2 changes: 0 additions & 2 deletions cplusplus/scripts/getanddostuff.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,6 @@ class GetAndDoStuff : public QObject {
// IMAGE INFO
Q_INVOKABLE bool isImageAnimated(QString path) { return imageinfo->isImageAnimated(path); }
Q_INVOKABLE QSize getAnimatedImageSize(QString path) { return imageinfo->getAnimatedImageSize(path); }
Q_INVOKABLE QList<int> getGreyscaleHistogramValues(QString filename) { return imageinfo->getGreyscaleHistogramValues(filename); }
Q_INVOKABLE QList<int> getColorHistogramValues(QString filename) { return imageinfo->getColorHistogramValues(filename); }
Q_INVOKABLE QList<int> getNumFramesAndDuration(QString filename) { return imageinfo->getNumFramesAndDuration(filename); }
Q_INVOKABLE QString getLastModified(QString filename) { return imageinfo->getLastModified(filename); }

Expand Down
91 changes: 0 additions & 91 deletions cplusplus/scripts/getanddostuff/imageinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,97 +32,6 @@ QSize GetAndDoStuffImageInfo::getAnimatedImageSize(QString path) {

}

QList<int> GetAndDoStuffImageInfo::getGreyscaleHistogramValues(QString filename) {

QSize *tmp = new QSize();
QImage img = provider->requestImage(filename, tmp, QSize());
delete tmp;

// Read and store image dimensions
int w = img.width();
int h = img.height();

// In here we store how many pixels there are per grayscale value.
QList<int> levels;
for(int i = 0; i < 256; ++i)
levels.push_back(0);

// Loop over all rows of the image
for(int i = 0; i < h; ++i) {

// Get the pixel data of row i of the image
QRgb *rowData = (QRgb*)img.scanLine(i);

// Loop over all columns
for(int j = 0; j < w; ++j) {

// Get pixel data of pixel at column j in row i
QRgb pixelData = rowData[j];

// Get RGB values
int red = qRed(pixelData);
int green = qGreen(pixelData);
int blue = qBlue(pixelData);

// Compute the gray level
int gray_level = qGray(red,green,blue);

// Add a pixel at current gray level
++levels[gray_level];

}

}

return levels;

}

QList<int> GetAndDoStuffImageInfo::getColorHistogramValues(QString filename) {

QSize *tmp = new QSize();
QImage img = provider->requestImage(filename, tmp, QSize());
delete tmp;

// Read and store image dimensions
int w = img.width();
int h = img.height();

// In here we store how many pixels there are per grayscale value.
QList<int> levels;
for(int i = 0; i < 3*256; ++i)
levels.push_back(0);

// Loop over all rows of the image
for(int i = 0; i < h; ++i) {

// Get the pixel data of row i of the image
QRgb *rowData = (QRgb*)img.scanLine(i);

// Loop over all columns
for(int j = 0; j < w; ++j) {

// Get pixel data of pixel at column j in row i
QRgb pixelData = rowData[j];

// Get RGB values
int red = qRed(pixelData);
int green = qGreen(pixelData);
int blue = qBlue(pixelData);

// Add a pixel at current gray level
++levels[red];
++levels[256+green];
++levels[2*256+blue];

}

}

return levels;

}

QList<int> GetAndDoStuffImageInfo::getNumFramesAndDuration(QString filename) {

if(filename.startsWith("image://full/"))
Expand Down
Loading

0 comments on commit 38b0478

Please sign in to comment.