Commit 4f2a01f2 authored by Philipp Erhardt's avatar Philipp Erhardt
Browse files

Implement fancy color inversion

Contrast and brightness of inverted rendering can be configured.
parent 58f09c95
......@@ -150,8 +150,8 @@ VARIABLES
'string' *background_color_fullscreen* ::
0xFF000000: Fullscreen background color in ARGB Format.
'string' *unrendered_page_color* ::
0x80808080: Color that gets drawn instead of a page that hasn't been
rendered yet.
0x40FFFFFF: Color that gets drawn instead of a page that hasn't been
rendered yet. If colors are inverted, this gets inverted as well.
'int' *click_link_button* ::
1: The mouse button used for clicking links. Buttons 1-5 are supported.
'int' *drag_view_button* ::
......@@ -212,6 +212,12 @@ GotoLine {
'int' *prefetch_count* ::
4: Number of pages exceeding the currently visible ones to render, back-
and forwards respectively.
'float' *inverted_color_contrast* ::
0.5: the contrast when using inverted colors to avoid too much
brightness.
'float' *inverted_color_brightening* ::
0.15: amount of brightening when using inverted colors to shift black to
gray.
'int' *mouse_wheel_factor* ::
120: QT delta for turning the mouse wheel 1 click. Shouldn't need to be
touched.
......
......@@ -22,6 +22,8 @@ stylesheet=
page_overlay_text=Page %1/%2
icon_theme=
prefetch_count=4
inverted_color_contrast=0.5
inverted_color_brightening=0.15
mouse_wheel_factor=120
thumbnail_filter=true
thumbnail_size=32
......
......@@ -45,6 +45,8 @@ void CFG::init_defaults() {
vd.push_back("Settings/icon_theme"); defaults[vd.back()] = "";
// internal
vd.push_back("Settings/prefetch_count"); defaults[vd.back()] = 4;
vd.push_back("Settings/inverted_color_contrast"); defaults[vd.back()] = 0.5;
vd.push_back("Settings/inverted_color_brightening"); defaults[vd.back()] = 0.15;
vd.push_back("Settings/mouse_wheel_factor"); defaults[vd.back()] = 120; // (qt-)delta for turning the mouse wheel 1 click
vd.push_back("Settings/thumbnail_filter"); defaults[vd.back()] = true; // filter when creating thumbnail image
vd.push_back("Settings/thumbnail_size"); defaults[vd.back()] = 32;
......
......@@ -44,11 +44,23 @@ const QImage *KPage::get_image(int index) const {
}
int KPage::get_width(int index) const {
return status[index];
// status might contain the information for img_other, but no inverted version is available yet
if (img[index].isNull()) {
return 0;
} else {
return status[index];
}
}
char KPage::get_rotation(int index) const {
return rotation[index];
// return rotation of next available image, try the right index first
for (int i = 3; i > 0; i--) {
if (!img[(index + i) % 3].isNull()) {
return rotation[(index + i) % 3];
}
}
// no image available? use thumbnail (always 0)
return 0;
}
const QList<SelectionLine *> *KPage::get_text() const {
......@@ -59,4 +71,11 @@ const QList<SelectionLine *> *KPage::get_text() const {
// return label;
//}
void KPage::toggle_invert_colors() {
for (int i = 0; i < 3; i++) {
img[i].swap(img_other[i]);
}
thumbnail.swap(thumbnail_other);
inverted_colors = !inverted_colors;
}
......@@ -22,10 +22,18 @@ public:
// QString get_label() const;
private:
void toggle_invert_colors();
float width;
float height;
QImage img[3];
QImage thumbnail;
// for inverted colors with reduced contrast
// img_other contain the currently not needed color versions
// img store the current versions to be displayed
QImage img_other[3];
QImage thumbnail_other;
// QString label;
QList<Poppler::Link *> *links;
QMutex mutex;
......
......@@ -9,7 +9,6 @@
#endif
#include "resourcemanager.h"
#include "canvas.h"
#include "config.h"
#include "util.h"
#include "kpage.h"
#include "worker.h"
......@@ -32,11 +31,6 @@ ResourceManager::ResourceManager(const QString &file, Viewer *v) :
#endif
inverted_colors(false),
cur_jump_pos(jumplist.end()) {
// load config options
CFG *config = CFG::get_instance();
smooth_downscaling = config->get_value("Settings/thumbnail_filter").toBool();
thumbnail_size = config->get_value("Settings/thumbnail_size").toInt();
initialize(file, QByteArray());
}
......@@ -181,20 +175,20 @@ const KPage *ResourceManager::get_page(int page, int width, int index) {
return NULL;
}
// page not available or wrong size/rotation
// page not available or wrong size/rotation/color
k_page[page].mutex.lock();
bool must_invert_colors = k_page[page].inverted_colors != inverted_colors;
if (must_invert_colors) {
k_page[page].toggle_invert_colors();
}
if (k_page[page].img[index].isNull() ||
k_page[page].status[index] != width ||
k_page[page].rotation[index] != rotation) {
k_page[page].rotation[index] != rotation ||
must_invert_colors) {
enqueue(page, width, index);
}
if (inverted_colors != k_page[page].inverted_colors) {
k_page[page].inverted_colors = inverted_colors;
for (int i = 0; i < 3; i++) {
k_page[page].img[i].invertPixels();
}
k_page[page].thumbnail.invertPixels();
}
return &k_page[page];
}
......@@ -240,33 +234,9 @@ void ResourceManager::collect_garbage(int keep_min, int keep_max) {
cerr << " removing page " << page << endl;
#endif
k_page[page].mutex.lock();
// create thumbnail
if (k_page[page].thumbnail.isNull()) {
Qt::TransformationMode mode = Qt::FastTransformation;
if (smooth_downscaling) {
mode = Qt::SmoothTransformation;
}
// find the index of the rendered image
for (int i = 0; i < 3; i++) {
if (!k_page[page].img[i].isNull()) {
// k_page[page].inverted_colors = inverted_colors;
// scale
k_page[page].thumbnail = k_page[page].img[i].scaled(
QSize(thumbnail_size, thumbnail_size),
Qt::IgnoreAspectRatio, mode);
// rotate
if (k_page[page].rotation[i] != 0) {
QTransform trans;
trans.rotate(-k_page[page].rotation[i] * 90);
k_page[page].thumbnail = k_page[page].thumbnail.transformed(
trans);
}
break;
}
}
}
for (int i = 0; i < 3; i++) {
k_page[page].img[i] = QImage();
k_page[page].img_other[i] = QImage();
k_page[page].status[i] = 0;
k_page[page].rotation[i] = 0;
}
......
......@@ -107,9 +107,6 @@ private:
QSocketNotifier *i_notifier;
#endif
// config options
bool smooth_downscaling;
int thumbnail_size;
bool inverted_colors;
std::list<int> jumplist;
......
#include <QAction>
#include <QObject>
#include <QImage>
//#include <QTime>
//#include <iostream>
#include "util.h"
#include "config.h"
using namespace std;
const QRectF rotate_rect(const QRectF &rect, float w, float h, int rotation) {
if (rotation == 0) {
return rect;
......@@ -53,3 +59,28 @@ void add_action(QWidget *base, const char *action, const char *slot, QWidget *re
}
}
void invert_image(QImage *img) {
static QRgb invert_mask = qRgba(255, 255, 255, 0);
static float inverted_contrast =
CFG::get_instance()->get_value("Settings/inverted_color_contrast").toFloat();
static int offset = 255 *
CFG::get_instance()->get_value("Settings/inverted_color_brightening").toFloat();
// QTime time;
// time.start();
// img->invertPixels();
QRgb *pixels = reinterpret_cast<QRgb *>(img->bits());
QRgb *pixels_end = pixels + img->width() * img->height();
while (pixels < pixels_end) {
*pixels ^= invert_mask;
*pixels = qRgb(
(qRed(*pixels)) * inverted_contrast + offset,
(qGreen(*pixels)) * inverted_contrast + offset,
(qBlue(*pixels)) * inverted_contrast + offset);
++pixels;
}
// cout << time.elapsed() << "ms elapsed" << endl;
}
......@@ -4,6 +4,10 @@
#include <QRect>
#include <QRectF>
class QImage;
#define POPPLER_VERSION ((POPPLER_VERSION_MAJOR << 16) | (POPPLER_VERSION_MINOR << 8) | (POPPLER_VERSION_MICRO))
#define POPPLER_VERSION_CHECK(major,minor,micro) ((major << 16) | (minor << 8) | (micro))
......@@ -21,5 +25,7 @@ QRect transform_rect_expand(const QRectF &rect, float scale, int off_x, int off_
void add_action(QWidget *base, const char *action, const char *slot, QWidget *receiver);
void invert_image(QImage *img);
#endif
......@@ -3,6 +3,8 @@
#include "kpage.h"
#include "canvas.h"
#include "selection.h"
#include "util.h"
#include "config.h"
#include <list>
#include <iostream>
#include <poppler/qt4/poppler-qt4.h>
......@@ -13,6 +15,10 @@ using namespace std;
Worker::Worker(ResourceManager *res) :
die(false),
res(res) {
// load config options
CFG *config = CFG::get_instance();
smooth_downscaling = config->get_value("Settings/thumbnail_filter").toBool();
thumbnail_size = config->get_value("Settings/thumbnail_size").toInt();
}
void Worker::run() {
......@@ -57,58 +63,90 @@ void Worker::run() {
res->requestMutex.unlock();
// check for duplicate requests
res->k_page[page].mutex.lock();
if (res->k_page[page].status[index] == width &&
res->k_page[page].rotation[index] == res->rotation) {
res->k_page[page].mutex.unlock();
continue;
KPage &kp = res->k_page[page];
kp.mutex.lock();
bool render_new = true;
if (kp.status[index] == width && kp.rotation[index] == res->rotation) {
if (kp.img[index].isNull()) { // only invert colors
render_new = false;
} else { // nothing to do
kp.mutex.unlock();
continue;
}
}
int rotation = res->rotation;
res->k_page[page].mutex.unlock();
kp.mutex.unlock();
// open page
#ifdef DEBUG
cerr << " rendering page " << page << " for index " << index << endl;
#endif
Poppler::Page *p = res->doc->page(page);
if (p == NULL) {
cerr << "failed to load page " << page << endl;
continue;
}
Poppler::Page *p = NULL;
if (render_new) {
p = res->doc->page(page);
if (p == NULL) {
cerr << "failed to load page " << page << endl;
continue;
}
// render page
float dpi = 72.0 * width / res->get_page_width(page);
QImage img = p->renderToImage(dpi, dpi, -1, -1, -1, -1,
static_cast<Poppler::Page::Rotation>(rotation));
// render page
float dpi = 72.0 * width / res->get_page_width(page);
QImage img = p->renderToImage(dpi, dpi, -1, -1, -1, -1,
static_cast<Poppler::Page::Rotation>(rotation));
if (img.isNull()) {
cerr << "failed to render page " << page << endl;
continue;
}
if (img.isNull()) {
cerr << "failed to render page " << page << endl;
continue;
}
// invert to current color setting
if (res->inverted_colors) {
img.invertPixels();
// insert new image
kp.mutex.lock();
if (kp.inverted_colors) {
kp.img[index] = QImage();
kp.img_other[index] = img;
} else {
kp.img[index] = img;
kp.img_other[index] = QImage();
}
kp.status[index] = width;
kp.rotation[index] = rotation;
} else {
// image already exists
kp.mutex.lock();
}
// put page
res->k_page[page].mutex.lock();
if (!res->k_page[page].img[index].isNull()) {
res->k_page[page].img[index] = QImage(); // assign null image
if (kp.inverted_colors) {
// generate inverted image
kp.img[index] = kp.img_other[index];
invert_image(&kp.img[index]);
}
// adjust all available images to current color setting
if (res->k_page[page].inverted_colors != res->inverted_colors) {
res->k_page[page].inverted_colors = res->inverted_colors;
for (int i = 0; i < 3; i++) {
res->k_page[page].img[i].invertPixels();
// create thumbnail
if (kp.thumbnail.isNull()) {
Qt::TransformationMode mode = Qt::FastTransformation;
if (smooth_downscaling) {
mode = Qt::SmoothTransformation;
}
// scale
if (kp.inverted_colors) {
kp.thumbnail = kp.img_other[index].scaled(QSize(thumbnail_size, thumbnail_size), Qt::IgnoreAspectRatio, mode);
} else {
kp.thumbnail = kp.img[index].scaled(QSize(thumbnail_size, thumbnail_size), Qt::IgnoreAspectRatio, mode);
}
// rotate
if (kp.rotation[index] != 0) {
QTransform trans;
trans.rotate(-kp.rotation[index] * 90);
kp.thumbnail = kp.thumbnail.transformed(trans);
}
kp.thumbnail_other = kp.thumbnail;
invert_image(&kp.thumbnail_other);
if (kp.inverted_colors) {
kp.thumbnail.swap(kp.thumbnail_other);
}
res->k_page[page].thumbnail.invertPixels();
}
res->k_page[page].img[index] = img;
res->k_page[page].status[index] = width;
res->k_page[page].rotation[index] = rotation;
res->k_page[page].mutex.unlock();
kp.mutex.unlock();
res->garbageMutex.lock();
res->garbage.insert(page); // TODO add index information?
......@@ -118,7 +156,7 @@ void Worker::run() {
// collect goto links
res->link_mutex.lock();
if (res->k_page[page].links == NULL) {
if (kp.links == NULL) {
res->link_mutex.unlock();
QList<Poppler::Link *> *links = new QList<Poppler::Link *>;
......@@ -126,9 +164,9 @@ void Worker::run() {
links->swap(l);
res->link_mutex.lock();
res->k_page[page].links = links;
kp.links = links;
}
if (res->k_page[page].text == NULL) {
if (kp.text == NULL) {
res->link_mutex.unlock();
QList<Poppler::TextBox *> text = p->textList();
......@@ -190,7 +228,7 @@ void Worker::run() {
}
res->link_mutex.lock();
res->k_page[page].text = lines;
kp.text = lines;
}
res->link_mutex.unlock();
......@@ -198,4 +236,3 @@ void Worker::run() {
}
}
......@@ -22,6 +22,10 @@ signals:
private:
ResourceManager *res;
// config options
bool smooth_downscaling;
int thumbnail_size;
};
#endif
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment