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
47 GuiPainter::GuiPainter(QPaintDevice * device)
48 : QPainter(device), Painter(),
49 use_pixmap_cache_(lyxrc.use_pixmap_cache && USE_PIXMAP_CACHE)
51 // new QPainter has default QPen:
52 current_color_ = guiApp->colorCache().get(Color_black);
53 current_ls_ = line_solid;
54 current_lw_ = line_thin;
58 GuiPainter::~GuiPainter()
61 //lyxerr << "GuiPainter::end()" << endl;
65 void GuiPainter::setQPainterPen(QColor const & col,
66 Painter::line_style ls, Painter::line_width lw)
68 if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
75 QPen pen = QPainter::pen();
79 case line_solid: pen.setStyle(Qt::SolidLine); break;
80 case line_onoffdash: pen.setStyle(Qt::DotLine); break;
84 case line_thin: pen.setWidth(0); break;
85 case line_thick: pen.setWidth(3); break;
92 QString GuiPainter::generateStringSignature(QString const & str, FontInfo const & f)
95 sig.append(QChar(static_cast<short>(f.family())));
96 sig.append(QChar(static_cast<short>(f.series())));
97 sig.append(QChar(static_cast<short>(f.realShape())));
98 sig.append(QChar(static_cast<short>(f.size())));
99 sig.append(QChar(static_cast<short>(f.color())));
100 if (!monochrome_min_.empty()) {
101 QColor const & min = monochrome_min_.top();
102 QColor const & max = monochrome_max_.top();
103 sig.append(QChar(static_cast<short>(min.red())));
104 sig.append(QChar(static_cast<short>(min.green())));
105 sig.append(QChar(static_cast<short>(min.blue())));
106 sig.append(QChar(static_cast<short>(max.red())));
107 sig.append(QChar(static_cast<short>(max.green())));
108 sig.append(QChar(static_cast<short>(max.blue())));
114 QColor GuiPainter::computeColor(ColorCode col)
116 return filterColor(guiApp->colorCache().get(col));
120 QColor GuiPainter::filterColor(QColor const & col)
122 if (monochrome_min_.empty())
125 // map into [min,max] interval
126 QColor const & min = monochrome_min_.top();
127 QColor const & max = monochrome_max_.top();
129 qreal v = col.valueF();
130 v = v * v; // make it a bit steeper (i.e. darker)
132 qreal minr, ming, minb;
133 qreal maxr, maxg, maxb;
134 min.getRgbF(&minr, &ming, &minb);
135 max.getRgbF(&maxr, &maxg, &maxb);
138 c.setRgbF(v*minr+(1-v)*maxr, v*ming+(1-v)*maxg, v*minb+(1-v)*maxb);
143 void GuiPainter::enterMonochromeMode(ColorCode const & min, ColorCode const & max)
145 QColor qmin = filterColor(guiApp->colorCache().get(min));
146 QColor qmax = filterColor(guiApp->colorCache().get(max));
147 monochrome_min_.push(qmin);
148 monochrome_max_.push(qmax);
152 void GuiPainter::leaveMonochromeMode()
154 BOOST_ASSERT(!monochrome_min_.empty());
155 monochrome_min_.pop();
156 monochrome_max_.pop();
160 void GuiPainter::point(int x, int y, ColorCode col)
162 if (!isDrawingEnabled())
165 setQPainterPen(computeColor(col));
170 void GuiPainter::line(int x1, int y1, int x2, int y2,
175 if (!isDrawingEnabled())
178 setQPainterPen(computeColor(col), ls, lw);
179 bool const do_antialiasing = renderHints() & TextAntialiasing
180 && x1 != x2 && y1 != y2;
181 setRenderHint(Antialiasing, do_antialiasing);
182 drawLine(x1, y1, x2, y2);
183 setRenderHint(Antialiasing, false);
187 void GuiPainter::lines(int const * xp, int const * yp, int np,
192 if (!isDrawingEnabled())
195 // double the size if needed
196 static QVector<QPoint> points(32);
197 if (np > points.size())
198 points.resize(2 * np);
200 bool antialias = false;
201 for (int i = 0; i < np; ++i) {
202 points[i].setX(xp[i]);
203 points[i].setY(yp[i]);
205 antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
207 setQPainterPen(computeColor(col), ls, lw);
208 bool const text_is_antialiased = renderHints() & TextAntialiasing;
209 setRenderHint(Antialiasing, antialias && text_is_antialiased);
210 drawPolyline(points.data(), np);
211 setRenderHint(Antialiasing, false);
215 void GuiPainter::rectangle(int x, int y, int w, int h,
220 if (!isDrawingEnabled())
223 setQPainterPen(computeColor(col), ls, lw);
224 drawRect(x, y, w, h);
228 void GuiPainter::fillRectangle(int x, int y, int w, int h, ColorCode col)
230 fillRect(x, y, w, h, guiApp->colorCache().get(col));
234 void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
235 int a1, int a2, ColorCode col)
237 if (!isDrawingEnabled())
240 // LyX usings 1/64ths degree, Qt usings 1/16th
241 setQPainterPen(computeColor(col));
242 bool const do_antialiasing = renderHints() & TextAntialiasing;
243 setRenderHint(Antialiasing, do_antialiasing);
244 drawArc(x, y, w, h, a1 / 4, a2 / 4);
245 setRenderHint(Antialiasing, false);
249 void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
251 graphics::GuiImage const & qlimage =
252 static_cast<graphics::GuiImage const &>(i);
254 fillRectangle(x, y, w, h, Color_graphicsbg);
256 if (!isDrawingEnabled())
259 drawImage(x, y, qlimage.qimage(), 0, 0, w, h);
263 int GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
266 return text(x, y, s, f);
270 int GuiPainter::smallCapsText(int x, int y,
271 QString const & s, FontInfo const & f)
273 FontInfo smallfont(f);
274 smallfont.decSize().decSize().setShape(UP_SHAPE);
276 QFont const & qfont = guiApp->guiFontLoader().get(f);
277 QFont const & qsmallfont = guiApp->guiFontLoader().get(smallfont);
279 setQPainterPen(computeColor(f.realColor()));
281 size_t const ls = s.length();
282 for (unsigned int i = 0; i < ls; ++i) {
283 QChar const c = s[i].toUpper();
289 if (isDrawingEnabled())
290 drawText(x + textwidth, y, c);
291 textwidth += fontMetrics().width(c);
297 int GuiPainter::text(int x, int y, docstring const & s,
303 /* Caution: The following ucs4 to QString conversions work for symbol fonts
304 only because they are no real conversions but simple casts in reality.
305 When we want to draw a symbol or calculate the metrics we pass the position
306 of the symbol in the font (as given in lib/symbols) as a char_type to the
307 frontend. This is just wrong, because the symbol is no UCS4 character at
308 all. You can think of this number as the code point of the symbol in a
309 custom symbol encoding. It works because this char_type is lateron again
310 interpreted as a position in the font again.
311 The correct solution would be to have extra functions for symbols, but that
312 would require to duplicate a lot of frontend and mathed support code.
314 QString str = toqstr(s);
317 // HACK: QT3 refuses to show single compose characters
318 // Still needed with Qt4?
319 if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
323 GuiFontInfo & fi = guiApp->guiFontLoader().fontinfo(f);
327 if (f.realShape() == SMALLCAPS_SHAPE) {
328 textwidth = smallCapsText(x, y, str, f);
329 if (f.underbar() == FONT_ON)
330 underline(f, x, y, textwidth);
334 // Here we use the font width cache instead of
335 // textwidth = fontMetrics().width(str);
336 // because the above is awfully expensive on MacOSX
337 textwidth = fi.metrics->width(s);
338 if (f.underbar() == FONT_ON)
339 underline(f, x, y, textwidth);
341 if (!isDrawingEnabled())
344 // Qt4 does not display a glyph whose codepoint is the
345 // same as that of a soft-hyphen (0x00ad), unless it
346 // occurs at a line-break. As a kludge, we force Qt to
347 // render this glyph using a one-column line.
348 if (s.size() == 1 && str[0].unicode() == 0x00ad) {
349 setQPainterPen(computeColor(f.realColor()));
350 QTextLayout adsymbol(str);
351 adsymbol.setFont(fi.font);
352 adsymbol.beginLayout();
353 QTextLine line = adsymbol.createLine();
354 line.setNumColumns(1);
355 line.setPosition(QPointF(0, -line.ascent()));
356 adsymbol.endLayout();
357 line.draw(this, QPointF(x, y));
361 if (!use_pixmap_cache_) {
362 // don't use the pixmap cache,
363 // draw directly onto the painting device
364 setQPainterPen(computeColor(f.realColor()));
365 if (font() != fi.font)
367 // We need to draw the text as LTR as we use our own bidi code.
368 setLayoutDirection(Qt::LeftToRight);
369 // We need to draw the text as LTR as we use our own bidi code.
370 setLayoutDirection(Qt::LeftToRight);
372 //LYXERR(Debug::PAINTING) << "draw " << std::string(str.toUtf8())
373 // << " at " << x << "," << y << std::endl;
378 QString key = generateStringSignature(str, f);
379 // Warning: Left bearing is in general negative! Only the case
380 // where left bearing is negative is of interest WRT the the
381 // pixmap width and the text x-position.
382 // Only the left bearing of the first character is important
383 // as we always write from left to right, even for
384 // right-to-left languages.
385 int const lb = std::min(fi.metrics->lbearing(s[0]), 0);
386 int const mA = fi.metrics->maxAscent();
387 if (!QPixmapCache::find(key, pm)) {
388 // Only the right bearing of the last character is
389 // important as we always write from left to right,
390 // even for right-to-left languages.
391 int const rb = fi.metrics->rbearing(s[s.size()-1]);
392 int const w = textwidth + rb - lb;
393 int const mD = fi.metrics->maxDescent();
394 int const h = mA + mD;
396 pm.fill(Qt::transparent);
398 p.setQPainterPen(computeColor(f.realColor()));
399 if (p.font() != fi.font)
401 // We need to draw the text as LTR as we use our own bidi code.
402 p.setLayoutDirection(Qt::LeftToRight);
403 p.drawText(-lb, mA, str);
404 QPixmapCache::insert(key, pm);
405 //LYXERR(Debug::PAINTING) << "h=" << h << " mA=" << mA << " mD=" << mD
406 // << " w=" << w << " lb=" << lb << " tw=" << textwidth
407 // << " rb=" << rb << endl;
409 // Draw the cached pixmap.
410 drawPixmap(x + lb, y - mA, pm);
416 } // namespace frontend