3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
7 * \author Abdelrazak Younes
9 * Full author contact details are available in file CREDITS.
14 #include "GuiPainter.h"
16 #include "GuiApplication.h"
17 #include "GuiFontMetrics.h"
20 #include "GuiApplication.h"
21 #include "qt_helpers.h"
27 #include "support/unicode.h"
29 #include <QPixmapCache>
30 #include <QTextLayout>
32 // Set USE_PIXMAP_CACHE to 1 for enabling the use of a Pixmap cache when
33 // drawing text. This is especially useful for older PPC/Mac systems.
34 #if (QT_VERSION < 0x040200) || defined(Q_WS_X11)
35 #define USE_PIXMAP_CACHE 0
37 #define USE_PIXMAP_CACHE 1
51 GuiPainter::GuiPainter(QPaintDevice * device)
52 : QPainter(device), Painter(),
53 use_pixmap_cache_(lyxrc.use_pixmap_cache && USE_PIXMAP_CACHE)
55 // new QPainter has default QPen:
56 current_color_ = guiApp->colorCache().get(Color_black);
57 current_ls_ = line_solid;
58 current_lw_ = line_thin;
62 GuiPainter::~GuiPainter()
65 //lyxerr << "GuiPainter::end()" << endl;
69 void GuiPainter::setQPainterPen(QColor const & col,
70 Painter::line_style ls, Painter::line_width lw)
72 if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
79 QPen pen = QPainter::pen();
83 case line_solid: pen.setStyle(Qt::SolidLine); break;
84 case line_onoffdash: pen.setStyle(Qt::DotLine); break;
88 case line_thin: pen.setWidth(0); break;
89 case line_thick: pen.setWidth(3); break;
96 QString GuiPainter::generateStringSignature(QString const & str, FontInfo const & f)
99 sig.append(QChar(static_cast<short>(f.family())));
100 sig.append(QChar(static_cast<short>(f.series())));
101 sig.append(QChar(static_cast<short>(f.realShape())));
102 sig.append(QChar(static_cast<short>(f.size())));
103 sig.append(QChar(static_cast<short>(f.color())));
104 if (!monochrome_min_.empty()) {
105 QColor const & min = monochrome_min_.top();
106 QColor const & max = monochrome_max_.top();
107 sig.append(QChar(static_cast<short>(min.red())));
108 sig.append(QChar(static_cast<short>(min.green())));
109 sig.append(QChar(static_cast<short>(min.blue())));
110 sig.append(QChar(static_cast<short>(max.red())));
111 sig.append(QChar(static_cast<short>(max.green())));
112 sig.append(QChar(static_cast<short>(max.blue())));
118 QColor GuiPainter::computeColor(ColorCode col)
120 return filterColor(guiApp->colorCache().get(col));
124 QColor GuiPainter::filterColor(QColor const & col)
126 if (monochrome_min_.empty())
129 // map into [min,max] interval
130 QColor const & min = monochrome_min_.top();
131 QColor const & max = monochrome_max_.top();
133 qreal v = col.valueF();
134 v = v * v; // make it a bit steeper (i.e. darker)
136 qreal minr, ming, minb;
137 qreal maxr, maxg, maxb;
138 min.getRgbF(&minr, &ming, &minb);
139 max.getRgbF(&maxr, &maxg, &maxb);
141 return QColor(v*minr+(1-v)*maxr, v*ming+(1-v)*maxg, v*minb+(1-v)*maxb);
145 void GuiPainter::enterMonochromeMode(ColorCode const & min, ColorCode const & max)
147 QColor qmin = filterColor(guiApp->colorCache().get(min));
148 QColor qmax = filterColor(guiApp->colorCache().get(max));
149 monochrome_min_.push(qmin);
150 monochrome_max_.push(qmax);
154 void GuiPainter::leaveMonochromeMode()
156 BOOST_ASSERT(!monochrome_min_.empty());
157 monochrome_min_.pop();
158 monochrome_max_.pop();
162 void GuiPainter::point(int x, int y, ColorCode col)
164 if (!isDrawingEnabled())
167 setQPainterPen(computeColor(col));
172 void GuiPainter::line(int x1, int y1, int x2, int y2,
177 if (!isDrawingEnabled())
180 setQPainterPen(computeColor(col), ls, lw);
181 bool const do_antialiasing = renderHints() & TextAntialiasing
182 && x1 != x2 && y1 != y2;
183 setRenderHint(Antialiasing, do_antialiasing);
184 drawLine(x1, y1, x2, y2);
185 setRenderHint(Antialiasing, false);
189 void GuiPainter::lines(int const * xp, int const * yp, int np,
194 if (!isDrawingEnabled())
197 // double the size if needed
198 static QVector<QPoint> points(32);
199 if (np > points.size())
200 points.resize(2 * np);
202 bool antialias = false;
203 for (int i = 0; i < np; ++i) {
204 points[i].setX(xp[i]);
205 points[i].setY(yp[i]);
207 antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
209 setQPainterPen(computeColor(col), ls, lw);
210 bool const text_is_antialiased = renderHints() & TextAntialiasing;
211 setRenderHint(Antialiasing, antialias && text_is_antialiased);
212 drawPolyline(points.data(), np);
213 setRenderHint(Antialiasing, false);
217 void GuiPainter::rectangle(int x, int y, int w, int h,
222 if (!isDrawingEnabled())
225 setQPainterPen(computeColor(col), ls, lw);
226 drawRect(x, y, w, h);
230 void GuiPainter::fillRectangle(int x, int y, int w, int h, ColorCode col)
232 fillRect(x, y, w, h, guiApp->colorCache().get(col));
236 void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
237 int a1, int a2, ColorCode col)
239 if (!isDrawingEnabled())
242 // LyX usings 1/64ths degree, Qt usings 1/16th
243 setQPainterPen(computeColor(col));
244 bool const do_antialiasing = renderHints() & TextAntialiasing;
245 setRenderHint(Antialiasing, do_antialiasing);
246 drawArc(x, y, w, h, a1 / 4, a2 / 4);
247 setRenderHint(Antialiasing, false);
251 void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
253 graphics::GuiImage const & qlimage =
254 static_cast<graphics::GuiImage const &>(i);
256 fillRectangle(x, y, w, h, Color_graphicsbg);
258 if (!isDrawingEnabled())
261 drawImage(x, y, qlimage.qimage(), 0, 0, w, h);
265 int GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
268 return text(x, y, s, f);
272 int GuiPainter::smallCapsText(int x, int y,
273 QString const & s, FontInfo const & f)
275 FontInfo smallfont(f);
276 smallfont.decSize().decSize().setShape(UP_SHAPE);
278 QFont const & qfont = guiApp->guiFontLoader().get(f);
279 QFont const & qsmallfont = guiApp->guiFontLoader().get(smallfont);
281 setQPainterPen(computeColor(f.realColor()));
283 size_t const ls = s.length();
284 for (unsigned int i = 0; i < ls; ++i) {
285 QChar const c = s[i].toUpper();
291 if (isDrawingEnabled())
292 drawText(x + textwidth, y, c);
293 textwidth += fontMetrics().width(c);
299 int GuiPainter::text(int x, int y, docstring const & s,
305 /* Caution: The following ucs4 to QString conversions work for symbol fonts
306 only because they are no real conversions but simple casts in reality.
307 When we want to draw a symbol or calculate the metrics we pass the position
308 of the symbol in the font (as given in lib/symbols) as a char_type to the
309 frontend. This is just wrong, because the symbol is no UCS4 character at
310 all. You can think of this number as the code point of the symbol in a
311 custom symbol encoding. It works because this char_type is lateron again
312 interpreted as a position in the font again.
313 The correct solution would be to have extra functions for symbols, but that
314 would require to duplicate a lot of frontend and mathed support code.
316 QString str = toqstr(s);
319 // HACK: QT3 refuses to show single compose characters
320 // Still needed with Qt4?
321 if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
325 GuiFontInfo & fi = guiApp->guiFontLoader().fontinfo(f);
329 if (f.realShape() == SMALLCAPS_SHAPE) {
330 textwidth = smallCapsText(x, y, str, f);
331 if (f.underbar() == FONT_ON)
332 underline(f, x, y, textwidth);
336 // Here we use the font width cache instead of
337 // textwidth = fontMetrics().width(str);
338 // because the above is awfully expensive on MacOSX
339 textwidth = fi.metrics->width(s);
340 if (f.underbar() == FONT_ON)
341 underline(f, x, y, textwidth);
343 if (!isDrawingEnabled())
346 // Qt4 does not display a glyph whose codepoint is the
347 // same as that of a soft-hyphen (0x00ad), unless it
348 // occurs at a line-break. As a kludge, we force Qt to
349 // render this glyph using a one-column line.
350 if (s.size() == 1 && str[0].unicode() == 0x00ad) {
351 setQPainterPen(computeColor(f.realColor()));
352 QTextLayout adsymbol(str);
353 adsymbol.setFont(fi.font);
354 adsymbol.beginLayout();
355 QTextLine line = adsymbol.createLine();
356 line.setNumColumns(1);
357 line.setPosition(QPointF(0, -line.ascent()));
358 adsymbol.endLayout();
359 line.draw(this, QPointF(x, y));
363 if (!use_pixmap_cache_) {
364 // don't use the pixmap cache,
365 // draw directly onto the painting device
366 setQPainterPen(computeColor(f.realColor()));
367 if (font() != fi.font)
369 // We need to draw the text as LTR as we use our own bidi code.
370 setLayoutDirection(Qt::LeftToRight);
371 // We need to draw the text as LTR as we use our own bidi code.
372 setLayoutDirection(Qt::LeftToRight);
374 //LYXERR(Debug::PAINTING) << "draw " << std::string(str.toUtf8())
375 // << " at " << x << "," << y << std::endl;
380 QString key = generateStringSignature(str, f);
381 // Warning: Left bearing is in general negative! Only the case
382 // where left bearing is negative is of interest WRT the the
383 // pixmap width and the text x-position.
384 // Only the left bearing of the first character is important
385 // as we always write from left to right, even for
386 // right-to-left languages.
387 int const lb = std::min(fi.metrics->lbearing(s[0]), 0);
388 int const mA = fi.metrics->maxAscent();
389 if (!QPixmapCache::find(key, pm)) {
390 // Only the right bearing of the last character is
391 // important as we always write from left to right,
392 // even for right-to-left languages.
393 int const rb = fi.metrics->rbearing(s[s.size()-1]);
394 int const w = textwidth + rb - lb;
395 int const mD = fi.metrics->maxDescent();
396 int const h = mA + mD;
398 pm.fill(Qt::transparent);
400 p.setQPainterPen(computeColor(f.realColor()));
401 if (p.font() != fi.font)
403 // We need to draw the text as LTR as we use our own bidi code.
404 p.setLayoutDirection(Qt::LeftToRight);
405 p.drawText(-lb, mA, str);
406 QPixmapCache::insert(key, pm);
407 //LYXERR(Debug::PAINTING) << "h=" << h << " mA=" << mA << " mD=" << mD
408 // << " w=" << w << " lb=" << lb << " tw=" << textwidth
409 // << " rb=" << rb << endl;
411 // Draw the cached pixmap.
412 drawPixmap(x + lb, y - mA, pm);
418 } // namespace frontend