1 #include "worker.h"
2 #include "resourcemanager.h"
3 #include "kpage.h"
4 #include "canvas.h"
5 #include "selection.h"
6 #include "util.h"
7 #include "config.h"
8 #include <list>
9 #include <iostream>
10 #if QT_VERSION >= 0x050000
11 #	include <poppler-qt5.h>
12 #else
13 #	include <poppler-qt4.h>
14 #endif
15 
16 using namespace std;
17 
18 
Worker(ResourceManager * res)19 Worker::Worker(ResourceManager *res) :
20 		die(false),
21 		res(res) {
22 	// load config options
23 	CFG *config = CFG::get_instance();
24 	smooth_downscaling = config->get_value("Settings/thumbnail_filter").toBool();
25 	thumbnail_size = config->get_value("Settings/thumbnail_size").toInt();
26 }
27 
run()28 void Worker::run() {
29 	while (1) {
30 		res->requestSemaphore.acquire(1);
31 		if (die) {
32 			break;
33 		}
34 
35 		// get next page to render
36 		res->requestMutex.lock();
37 		int page, width, index;
38 		map<int,Request>::iterator less = res->requests.lower_bound(res->center_page);
39 		map<int,Request>::iterator greater = less--;
40 		map<int,Request>::iterator closest;
41 
42 		if (greater != res->requests.end()) {
43 			if (greater != res->requests.begin()) {
44 				// favour nearby page, go down first
45 				if (greater->first + less->first <= res->center_page * 2) {
46 					closest = greater;
47 				} else {
48 					closest = less;
49 				}
50 			} else {
51 				closest = greater;
52 			}
53 		} else {
54 			closest = less;
55 		}
56 
57 		page = closest->first;
58 		index = closest->second.get_lowest_index();
59 		width = closest->second.width[index];
60 		if (closest->second.remove_index_ok(index)) {
61 			res->requestSemaphore.release(1);
62 		} else {
63 			res->requests.erase(closest);
64 		}
65 		res->requestMutex.unlock();
66 
67 		// check for duplicate requests
68 		KPage &kp = res->k_page[page];
69 
70 		kp.mutex.lock();
71 		bool render_new = true;
72 		if (kp.status[index] == width && kp.rotation[index] == res->rotation) {
73 			if (kp.img[index].isNull()) { // only invert colors
74 				render_new = false;
75 			} else { // nothing to do
76 				kp.mutex.unlock();
77 				continue;
78 			}
79 		}
80 		int rotation = res->rotation;
81 		kp.mutex.unlock();
82 
83 		// open page
84 #ifdef DEBUG
85 		cerr << "    rendering page " << page << " for index " << index << ", center: " << res->center_page << endl;
86 #endif
87 		Poppler::Page *p = NULL;
88 		if (render_new) {
89 			p = res->doc->page(page);
90 			if (p == NULL) {
91 				cerr << "failed to load page " << page << endl;
92 				continue;
93 			}
94 
95 			// render page
96 			float dpi = 72.0 * width / res->get_page_width(page);
97 			QImage img = p->renderToImage(dpi, dpi, -1, -1, -1, -1,
98 					static_cast<Poppler::Page::Rotation>(rotation));
99 
100 			if (img.isNull()) {
101 				cerr << "failed to render page " << page << endl;
102 				continue;
103 			}
104 
105 			// insert new image
106 			kp.mutex.lock();
107 			if (kp.inverted_colors) {
108 				kp.img[index] = QImage();
109 				kp.img_other[index] = img;
110 			} else {
111 				kp.img[index] = img;
112 				kp.img_other[index] = QImage();
113 			}
114 			kp.status[index] = width;
115 			kp.rotation[index] = rotation;
116 		} else {
117 			// image already exists
118 			kp.mutex.lock();
119 		}
120 
121 		if (kp.inverted_colors) {
122 			// generate inverted image
123 			kp.img[index] = kp.img_other[index];
124 			invert_image(&kp.img[index]);
125 		}
126 
127 		// create thumbnail
128 		if (kp.thumbnail.isNull()) {
129 			Qt::TransformationMode mode = Qt::FastTransformation;
130 			if (smooth_downscaling) {
131 				mode = Qt::SmoothTransformation;
132 			}
133 			// scale
134 			if (kp.inverted_colors) {
135 				kp.thumbnail = kp.img_other[index].scaled(QSize(thumbnail_size, thumbnail_size), Qt::IgnoreAspectRatio, mode);
136 			} else {
137 				kp.thumbnail = kp.img[index].scaled(QSize(thumbnail_size, thumbnail_size), Qt::IgnoreAspectRatio, mode);
138 			}
139 			// rotate
140 			if (kp.rotation[index] != 0) {
141 				QTransform trans;
142 				trans.rotate(-kp.rotation[index] * 90);
143 				kp.thumbnail = kp.thumbnail.transformed(trans);
144 			}
145 			kp.thumbnail_other = kp.thumbnail;
146 			invert_image(&kp.thumbnail_other);
147 			if (kp.inverted_colors) {
148 				kp.thumbnail.swap(kp.thumbnail_other);
149 			}
150 		}
151 		kp.mutex.unlock();
152 
153 		res->garbageMutex.lock();
154 		res->garbage[index].insert(page);
155 		res->garbageMutex.unlock();
156 
157 		emit page_rendered(page);
158 
159 		// collect goto links
160 		res->link_mutex.lock();
161 		if (kp.links == NULL) {
162 			res->link_mutex.unlock();
163 
164 			QList<Poppler::Link *> *links = new QList<Poppler::Link *>;
165 			QList<Poppler::Link *> l = p->links();
166 			links->swap(l);
167 
168 			res->link_mutex.lock();
169 			kp.links = links;
170 		}
171 		if (kp.text == NULL) {
172 			res->link_mutex.unlock();
173 
174 			QList<Poppler::TextBox *> text = p->textList();
175 			// assign boxes to lines
176 			// make single parts from chained boxes
177 			set<Poppler::TextBox *> used;
178 			QList<SelectionPart *> selection_parts;
179 			Q_FOREACH(Poppler::TextBox *box, text) {
180 				if (used.find(box) != used.end()) {
181 					continue;
182 				}
183 				used.insert(box);
184 
185 				SelectionPart *p = new SelectionPart(box);
186 				selection_parts.push_back(p);
187 				Poppler::TextBox *next = box->nextWord();
188 				while (next != NULL) {
189 					used.insert(next);
190 					p->add_word(next);
191 					next = next->nextWord();
192 				}
193 			}
194 
195 			// sort by y coordinate
196 			stable_sort(selection_parts.begin(), selection_parts.end(), selection_less_y);
197 
198 			QRectF line_box;
199 			QList<SelectionLine *> *lines = new QList<SelectionLine *>();
200 			Q_FOREACH(SelectionPart *part, selection_parts) {
201 				QRectF box = part->get_bbox();
202 				// box fits into line_box's line
203 				if (!lines->empty() && box.y() <= line_box.center().y() && box.bottom() > line_box.center().y()) {
204 					float ratio_w = box.width() / line_box.width();
205 					float ratio_h = box.height() / line_box.height();
206 					if (ratio_w < 1.0f) {
207 						ratio_w = 1.0f / ratio_w;
208 					}
209 					if (ratio_h < 1.0f) {
210 						ratio_h = 1.0f / ratio_h;
211 					}
212 					if (ratio_w > 1.3f && ratio_h > 1.3f) {
213 						lines->back()->sort();
214 						lines->push_back(new SelectionLine(part));
215 						line_box = part->get_bbox();
216 					} else {
217 						lines->back()->add_part(part);
218 					}
219 				// it doesn't fit, create new line
220 				} else {
221 					if (!lines->empty()) {
222 						lines->back()->sort();
223 					}
224 					lines->push_back(new SelectionLine(part));
225 					line_box = part->get_bbox();
226 				}
227 			}
228 			if (!lines->empty()) {
229 				lines->back()->sort();
230 			}
231 
232 			res->link_mutex.lock();
233 			kp.text = lines;
234 		}
235 		res->link_mutex.unlock();
236 
237 		delete p;
238 	}
239 }
240 
241