Commit 1b062e1e authored by Philipp Erhardt's avatar Philipp Erhardt
Browse files

added layout class

resource manager now scales available images
parent 01582859
#include "layout.h"
using namespace std;
//==[ Layout ]=================================================================
Layout::Layout(ResourceManager *_res, int _page) :
res(_res), page(_page), off_x(0), off_y(0), width(0), height(0) {
}
void Layout::scroll_smooth(int dx, int dy) {
off_x += dx;
off_y += dy;
}
void Layout::scroll_page(int new_page, bool relative) {
if (relative) {
page += new_page;
} else {
page = new_page;
}
if (page < 0) {
page = 0;
off_y = 0;
}
if (page > res->get_page_count() - 1) {
page = res->get_page_count() - 1;
}
}
void Layout::resize(float w, float h) {
width = w;
height = h;
}
//==[ PresentationLayout ]===========================================================
PresentationLayout::PresentationLayout(ResourceManager *_res, int page) :
Layout(_res, page) {
}
PresentationLayout::PresentationLayout(Layout& old_layout) :
Layout(old_layout) {
}
void PresentationLayout::scroll_smooth(int /*dx*/, int /*dy*/) {
// ignore smooth scrolling
}
void PresentationLayout::render(QPainter *painter) {
res->set_cache_size(5);
float render_width;
int center_x = 0, center_y = 0;
if (width / height > res->get_page_aspect(page)) {
render_width = res->get_page_aspect(page) * height;
center_x = (width - render_width) / 2;
} else {
render_width = width;
center_y = (height - render_width / res->get_page_aspect(page)) / 2;
}
QImage *img = res->get_page(page, render_width);
if (img != NULL) {
painter->drawImage(center_x, center_y, *img);
res->unlock_page(page);
}
// prefetch
if (res->get_page(page + 1, render_width) != NULL) {
res->unlock_page(page + 1);
}
if (res->get_page(page - 1, render_width) != NULL) {
res->unlock_page(page - 1);
}
}
//==[ SequentialLayout ]=======================================================
SequentialLayout::SequentialLayout(ResourceManager *_res, int page) :
Layout(_res, page) {
}
SequentialLayout::SequentialLayout(Layout& old_layout) :
Layout(old_layout) {
}
void SequentialLayout::scroll_smooth(int dx, int dy) {
Layout::scroll_smooth(dx, dy);
while (off_y <= -width / res->get_page_aspect(page) && page < res->get_page_count() - 1) {
off_y += width / res->get_page_aspect(page);
page++;
}
while (off_y > 0 && page > 0) {
page--;
off_y -= width / res->get_page_aspect(page);
}
if (page == 0 && off_y > 0) {
off_y = 0;
}
off_x = 0;
}
void SequentialLayout::render(QPainter *painter) {
float page_width = width;
float page_height; // = page_width / res->get_page_aspect(page);
int cur_page = page, cur_offset = off_y;
while (cur_offset < height && cur_page < res->get_page_count()) {
page_height = page_width / res->get_page_aspect(cur_page);
cur_offset += page_height;
cur_page++;
}
res->set_cache_size((cur_page - page + 1) * 1.5);
cur_page = page;
cur_offset = off_y;
while (cur_offset < height && cur_page < res->get_page_count()) {
page_height = page_width / res->get_page_aspect(cur_page);
QImage *img = res->get_page(cur_page, page_width);
if (img != NULL) {
painter->drawImage(off_x, cur_offset, *img);
res->unlock_page(cur_page);
}
cur_offset += page_height;
cur_page++;
}
// prefetch
if (res->get_page(cur_page, page_width) != NULL) {
res->unlock_page(cur_page);
}
if (res->get_page(page - 1, page_width) != NULL) {
res->unlock_page(page - 1);
}
}
#ifndef LAYOUT_H
#define LAYOUT_H
#include <iostream>
#include <QPainter>
#include <QImage>
#include "resourcemanager.h"
class Layout {
public:
Layout(ResourceManager *_res, int _page = 0);
virtual ~Layout() {};
virtual void scroll_smooth(int dx, int dy);
virtual void scroll_page(int new_page, bool relative = true);
virtual void resize(float w, float h);
virtual void render(QPainter *painter) = 0;
protected:
ResourceManager *res;
int page;
int off_x, off_y;
float width, height;
};
class PresentationLayout : public Layout {
public:
PresentationLayout(ResourceManager *res, int page = 0);
PresentationLayout(Layout& old_layout);
~PresentationLayout() {};
void scroll_smooth(int dx, int dy);
void render(QPainter *painter);
};
class SequentialLayout : public Layout {
public:
SequentialLayout(ResourceManager *res, int page = 0);
SequentialLayout(Layout& old_layout);
~SequentialLayout() {};
void scroll_smooth(int dx, int dy);
void render(QPainter *painter);
};
#endif
#include <QApplication>
#include <QImage>
#include <QString>
#include <QPalette>
#include <iostream>
#include "resourcemanager.h"
#include "pdfviewer.h"
#include "viewer.h"
int main(int argc, char *argv[]) {
using namespace std;
QApplication app(argc, argv);
app.setPalette(QPalette(QColor(255, 255, 255), QColor(0, 0, 0)));
if (argc != 2) {
cerr << "usage: " << argv[0] << " <path>" << endl;
......@@ -16,9 +18,10 @@ int main(int argc, char *argv[]) {
}
ResourceManager res(argv[1]);
if (res.isNull())
if (res.is_null()) {
return 1;
PdfViewer pdf(&res);
}
Viewer pdf(&res);
pdf.show();
return app.exec();
......
......@@ -2,8 +2,9 @@ TEMPLATE = app
TARGET = pdf
DEPENDPATH += .
INCLUDEPATH += .
CONFIG += qt debug
# Input
HEADERS += pdfviewer.h resourcemanager.h
SOURCES += main.cpp pdfviewer.cpp resourcemanager.cpp
HEADERS += layout.h viewer.h resourcemanager.h
SOURCES += main.cpp layout.cpp viewer.cpp resourcemanager.cpp
unix:LIBS += -lpoppler-qt4
#include "pdfviewer.h"
PdfViewer::PdfViewer(ResourceManager *_res, QWidget *parent) :
QWidget(parent),
res(_res),
page(0),
scroll(0.0),
fit(false) {
setFocusPolicy(Qt::StrongFocus);
res->setFinishedCallback(&scheduleUpdate, this);
}
PdfViewer::~PdfViewer() {
}
void PdfViewer::paintEvent(QPaintEvent * /*event*/) {
using namespace std;
cout << "redraw" << endl;
QPainter painter(this);
for (int i = -1; i < 3; ++i) {
float pageWidth, pageHeight;
if (!fit) {
pageWidth = width();
pageHeight = pageWidth / res->getPageAspect(page + i);
} else {
pageHeight = height();
pageWidth = res->getPageAspect(page + i) * pageHeight;
}
QImage *img = res->getPage(page + i, pageWidth);
if (img != NULL) {
QRect target(0, 0, width(), height());
QRect source(0, scroll * pageHeight - (pageHeight + 2) * i, width(), height());
painter.drawImage(target, *img, source);
} else {
// cout << "null: " << page << " " << i << endl;
}
}
}
void PdfViewer::keyPressEvent(QKeyEvent *event) {
switch (event->key()) {
case Qt::Key_Q:
case Qt::Key_Escape:
res->wait();
QCoreApplication::exit(0);
break;
case Qt::Key_Space:
case Qt::Key_Down:
scroll = 0;
setPage(1, true);
break;
case Qt::Key_Backspace:
case Qt::Key_Up:
scroll = 0;
setPage(-1, true);
break;
case Qt::Key_J:
scrollPage(0, -50);
break;
case Qt::Key_K:
scrollPage(0, 50);
break;
case Qt::Key_G:
scroll = 0;
setPage(0);
break;
case Qt::Key_B:
scroll = 0;
setPage(res->numPages() - 1);
break;
case Qt::Key_1:
toggleFit();
break;
default:
QWidget::keyPressEvent(event);
break;
}
}
void PdfViewer::mousePressEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
mx = event->x();
my = event->y();
}
}
void PdfViewer::mouseMoveEvent(QMouseEvent *event) {
scrollPage(event->x() - mx, event->y() - my);
mx = event->x();
my = event->y();
}
void PdfViewer::resizeEvent(QResizeEvent * /*event*/) {
update();
}
void PdfViewer::scheduleUpdate(PdfViewer *_this) {
_this->update();
}
void PdfViewer::setPage(int newPage, bool relative) {
if (relative) {
newPage += page;
}
if (newPage < 0) {
newPage = 0;
scroll = 0;
} else if (newPage >= res->numPages()) {
newPage = res->numPages() - 1;
}
if (page != newPage) {
QString str = "page ";
str.append(QString::number(newPage + 1));
str.append("/");
str.append(QString::number(res->numPages()));
setWindowTitle(str);
page = newPage;
update();
}
}
void PdfViewer::toggleFit() {
fit = not fit;
update();
}
void PdfViewer::scrollPage(int dx, int dy) {
if (dy == 0)
return;
if (!fit) {
scroll -= dy / (width() / res->getPageAspect(page));
} else {
scroll -= (float) dy / height();
}
if (scroll < 0) {
scroll++;
setPage(-1, true);
} else if (scroll >= 1) {
scroll--;
setPage(1, true);
} else {
update();
}
}
......@@ -21,17 +21,24 @@ void Worker::run() {
// get page number
res->requestMutex.lock();
int page = res->requests.front();
res->requests.pop();
int page = res->requests.front().first;
int width = res->requests.front().second;
res->requests.pop_front();
res->requestMutex.unlock();
// check for duplicate requests
res->imgMutex.lock();
res->imgMutex[page].lock();
if (res->image_status[page] == width) {
res->imgMutex[page].unlock();
continue;
}
res->imgMutex[page].unlock();
/* res->imgMutex.lock();
if (res->image[page] != NULL) {
res->imgMutex.unlock();
continue;
}
res->imgMutex.unlock();
res->imgMutex.unlock(); */
// open page
cout << " rendering page " << page << endl;
......@@ -42,24 +49,34 @@ void Worker::run() {
}
// render page
float dpi = 72.0 * res->width / p->pageSizeF().width();
QImage img = p->renderToImage(dpi, dpi);
float dpi = 72.0 * width / res->get_page_width(page);
QImage *img = new QImage;
*img = p->renderToImage(dpi, dpi);
delete p;
if (img.isNull()) {
if (img->isNull()) {
cerr << "failed to render page " << page << endl;
continue;
}
// put page
QImage *tmp = new QImage;
*tmp = img;
res->imgMutex.lock();
res->image[page] = tmp;
res->imgMutex.unlock();
res->imgMutex[page].lock();
if (res->image[page] != NULL) {
delete res->image[page];
}
res->image[page] = img;
res->image_status[page] = width;
res->imgMutex[page].unlock();
res->garbageMutex.lock();
res->garbage.push(page);
// TODO this might be slow
for (list<int>::iterator it = res->garbage.begin(); it != res->garbage.end(); ++it) {
if (*it == page) {
res->garbage.erase(it);
break;
}
}
res->garbage.push_back(page);
res->garbageMutex.unlock();
res->callback(res->caller);
}
......@@ -67,14 +84,9 @@ void Worker::run() {
//==[ ResourceManager ]========================================================
ResourceManager::ResourceManager(QString file) {
ResourceManager::ResourceManager(QString file) :
cache_size(5) {
doc = Poppler::Document::load(file);
doc->setRenderHint(Poppler::Document::Antialiasing, true);
doc->setRenderHint(Poppler::Document::TextAntialiasing, true);
doc->setRenderHint(Poppler::Document::TextHinting, true);
// if (POPPLER_CHECK_VERSION(0, 18, 0)) {
doc->setRenderHint(Poppler::Document::TextSlightHinting, true);
// }
if (doc == NULL) {
cerr << "failed to open file" << endl;
return;
......@@ -85,20 +97,31 @@ ResourceManager::ResourceManager(QString file) {
doc = NULL;
return;
}
doc->setRenderHint(Poppler::Document::Antialiasing, true);
doc->setRenderHint(Poppler::Document::TextAntialiasing, true);
doc->setRenderHint(Poppler::Document::TextHinting, true);
// if (POPPLER_CHECK_VERSION(0, 18, 0)) {
doc->setRenderHint(Poppler::Document::TextSlightHinting, true);
// }
pageAspect = new float[doc->numPages()];
for (int i = 0; i < doc->numPages(); ++i) {
page_width = new float[get_page_count()];
page_height = new float[get_page_count()];
for (int i = 0; i < get_page_count(); i++) {
Poppler::Page *p = doc->page(i);
if (p == NULL) {
cerr << "failed to load page " << i << endl;
continue;
}
pageAspect[i] = p->pageSizeF().width() / p->pageSizeF().height();
page_width[i] = p->pageSizeF().width();
page_height[i] = p->pageSizeF().height();
delete p;
}
image = new QImage *[doc->numPages()];
memset(image, 0, doc->numPages() * sizeof(QImage *));
image = new QImage *[get_page_count()];
memset(image, 0, get_page_count() * sizeof(QImage *));
image_status = new int[get_page_count()];
imgMutex = new QMutex[get_page_count()];
worker.setResManager(this);
worker.start();
......@@ -108,86 +131,85 @@ ResourceManager::~ResourceManager() {
garbageMutex.lock();
while (!garbage.empty()) {
int page = garbage.front();
garbage.pop();
garbage.pop_front();
garbageMutex.unlock();
imgMutex.lock();
imgMutex[page].lock();
delete image[page];
image[page] = NULL;
imgMutex.unlock();
imgMutex[page].unlock();
garbageMutex.lock();
}
garbageMutex.unlock();
if (doc == NULL) {
return;
}
delete doc;
delete image;
delete pageAspect;
delete[] imgMutex;
delete[] image;
delete[] image_status;
delete[] page_width;
delete[] page_height;
}
bool ResourceManager::isNull() const {
bool ResourceManager::is_null() const {
return (doc == NULL);
}
void ResourceManager::setFinishedCallback(void (*_callback)(PdfViewer *), PdfViewer *arg) {
void ResourceManager::setFinishedCallback(void (*_callback)(Viewer *), Viewer *arg) {
callback = _callback;
caller = arg;
}
QImage *ResourceManager::getPage(int page, int newWidth) {
if (page < 0 || page >= doc->numPages()) {
QImage *ResourceManager::get_page(int page, int width) {
if (page < 0 || page >= get_page_count()) {
return NULL;
}
// new image size, old images are obsolete
if (newWidth != width) {
cout << "width changed from " << width << " to " << newWidth << endl;
width = newWidth;
garbageMutex.lock();
while (!garbage.empty()) {
int page = garbage.front();
garbage.pop();
garbageMutex.unlock();
imgMutex.lock();
delete image[page];
image[page] = NULL;
imgMutex.unlock();
garbageMutex.lock();
}
garbageMutex.unlock();
}
imgMutex.lock();
imgMutex[page].lock();
if (image[page] != NULL) {
QImage *tmp = image[page];
imgMutex.unlock();
return tmp;
QImage *img = image[page];
// scale image
if (img->width() != width) {
QImage *tmp = new QImage;
*tmp = img->scaledToWidth(width);
delete img;
image[page] = tmp;
img = tmp;
image_status[page] = -width;
enqueue(page, width);
// redo dropped requests because of fast scrolling
} else if (image_status[page] == -width) {
enqueue(page, width);
}
// the page is still locked
// draw it, then call unlock_page
return img;
}
imgMutex.unlock();
image_status[page] = 0;
imgMutex[page].unlock();
requestMutex.lock();
requests.push(page);
if (requests.size() > 5) {
requests.pop();
} else {
requestSemaphore.release(1);
}
requestMutex.unlock();
enqueue(page, width);
// "garbage collection" - keep only the 5 newest pages
// "garbage collection" - keep only the cache_size newest pages