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.
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; // 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);
139 v * (minr - maxr) + maxr,
140 v * (ming - maxg) + maxg,
141 v * (minb - maxb) + maxb);
146 void GuiPainter::enterMonochromeMode(ColorCode const & min, ColorCode const & max)
148 QColor qmin = filterColor(guiApp->colorCache().get(min));
149 QColor qmax = filterColor(guiApp->colorCache().get(max));
150 monochrome_min_.push(qmin);
151 monochrome_max_.push(qmax);
155 void GuiPainter::leaveMonochromeMode()
157 BOOST_ASSERT(!monochrome_min_.empty());
158 monochrome_min_.pop();
159 monochrome_max_.pop();
163 void GuiPainter::point(int x, int y, ColorCode col)
165 if (!isDrawingEnabled())
168 setQPainterPen(computeColor(col));
173 void GuiPainter::line(int x1, int y1, int x2, int y2,
178 if (!isDrawingEnabled())
181 setQPainterPen(computeColor(col), ls, lw);
182 bool const do_antialiasing = renderHints() & TextAntialiasing
183 && x1 != x2 && y1 != y2;
184 setRenderHint(Antialiasing, do_antialiasing);
185 drawLine(x1, y1, x2, y2);
186 setRenderHint(Antialiasing, false);
190 void GuiPainter::lines(int const * xp, int const * yp, int np,
195 if (!isDrawingEnabled())
198 // double the size if needed
199 static QVector<QPoint> points(32);
200 if (np > points.size())
201 points.resize(2 * np);
203 bool antialias = false;
204 for (int i = 0; i < np; ++i) {
205 points[i].setX(xp[i]);
206 points[i].setY(yp[i]);
208 antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
210 setQPainterPen(computeColor(col), ls, lw);
211 bool const text_is_antialiased = renderHints() & TextAntialiasing;
212 setRenderHint(Antialiasing, antialias && text_is_antialiased);
213 drawPolyline(points.data(), np);
214 setRenderHint(Antialiasing, false);
218 void GuiPainter::rectangle(int x, int y, int w, int h,
223 if (!isDrawingEnabled())
226 setQPainterPen(computeColor(col), ls, lw);
227 drawRect(x, y, w, h);
231 void GuiPainter::fillRectangle(int x, int y, int w, int h, ColorCode col)
233 fillRect(x, y, w, h, guiApp->colorCache().get(col));
237 void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
238 int a1, int a2, ColorCode col)
240 if (!isDrawingEnabled())
243 // LyX usings 1/64ths degree, Qt usings 1/16th
244 setQPainterPen(computeColor(col));
245 bool const do_antialiasing = renderHints() & TextAntialiasing;
246 setRenderHint(Antialiasing, do_antialiasing);
247 drawArc(x, y, w, h, a1 / 4, a2 / 4);
248 setRenderHint(Antialiasing, false);
252 void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
254 graphics::GuiImage const & qlimage =
255 static_cast<graphics::GuiImage const &>(i);
257 fillRectangle(x, y, w, h, Color_graphicsbg);
259 if (!isDrawingEnabled())
262 drawImage(x, y, qlimage.qimage(), 0, 0, w, h);
266 int GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
269 return text(x, y, s, f);
273 int GuiPainter::smallCapsText(int x, int y,
274 QString const & s, FontInfo const & f)
276 FontInfo smallfont(f);
277 smallfont.decSize().decSize().setShape(UP_SHAPE);
279 QFont const & qfont = guiApp->guiFontLoader().get(f);
280 QFont const & qsmallfont = guiApp->guiFontLoader().get(smallfont);
282 setQPainterPen(computeColor(f.realColor()));
284 size_t const ls = s.length();
285 for (unsigned int i = 0; i < ls; ++i) {
286 QChar const c = s[i].toUpper();
292 if (isDrawingEnabled())
293 drawText(x + textwidth, y, c);
294 textwidth += fontMetrics().width(c);
300 int GuiPainter::text(int x, int y, docstring const & s,
306 /* Caution: The following ucs4 to QString conversions work for symbol fonts
307 only because they are no real conversions but simple casts in reality.
308 When we want to draw a symbol or calculate the metrics we pass the position
309 of the symbol in the font (as given in lib/symbols) as a char_type to the
310 frontend. This is just wrong, because the symbol is no UCS4 character at
311 all. You can think of this number as the code point of the symbol in a
312 custom symbol encoding. It works because this char_type is lateron again
313 interpreted as a position in the font again.
314 The correct solution would be to have extra functions for symbols, but that
315 would require to duplicate a lot of frontend and mathed support code.
317 QString str = toqstr(s);
320 // HACK: QT3 refuses to show single compose characters
321 // Still needed with Qt4?
322 if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
326 GuiFontInfo & fi = guiApp->guiFontLoader().fontinfo(f);
330 if (f.realShape() == SMALLCAPS_SHAPE) {
331 textwidth = smallCapsText(x, y, str, f);
332 if (f.underbar() == FONT_ON)
333 underline(f, x, y, textwidth);
337 // Here we use the font width cache instead of
338 // textwidth = fontMetrics().width(str);
339 // because the above is awfully expensive on MacOSX
340 textwidth = fi.metrics->width(s);
341 if (f.underbar() == FONT_ON)
342 underline(f, x, y, textwidth);
344 if (!isDrawingEnabled())
347 // Qt4 does not display a glyph whose codepoint is the
348 // same as that of a soft-hyphen (0x00ad), unless it
349 // occurs at a line-break. As a kludge, we force Qt to
350 // render this glyph using a one-column line.
351 if (s.size() == 1 && str[0].unicode() == 0x00ad) {
352 setQPainterPen(computeColor(f.realColor()));
353 QTextLayout adsymbol(str);
354 adsymbol.setFont(fi.font);
355 adsymbol.beginLayout();
356 QTextLine line = adsymbol.createLine();
357 line.setNumColumns(1);
358 line.setPosition(QPointF(0, -line.ascent()));
359 adsymbol.endLayout();
360 line.draw(this, QPointF(x, y));
364 if (!use_pixmap_cache_) {
365 // don't use the pixmap cache,
366 // draw directly onto the painting device
367 setQPainterPen(computeColor(f.realColor()));
368 if (font() != fi.font)
370 // We need to draw the text as LTR as we use our own bidi code.
371 setLayoutDirection(Qt::LeftToRight);
372 // We need to draw the text as LTR as we use our own bidi code.
373 setLayoutDirection(Qt::LeftToRight);
375 //LYXERR(Debug::PAINTING, "draw " << std::string(str.toUtf8())
376 // << " at " << x << "," << y);
381 QString key = generateStringSignature(str, f);
382 // Warning: Left bearing is in general negative! Only the case
383 // where left bearing is negative is of interest WRT the the
384 // pixmap width and the text x-position.
385 // Only the left bearing of the first character is important
386 // as we always write from left to right, even for
387 // right-to-left languages.
388 int const lb = std::min(fi.metrics->lbearing(s[0]), 0);
389 int const mA = fi.metrics->maxAscent();
390 if (!QPixmapCache::find(key, pm)) {
391 // Only the right bearing of the last character is
392 // important as we always write from left to right,
393 // even for right-to-left languages.
394 int const rb = fi.metrics->rbearing(s[s.size()-1]);
395 int const w = textwidth + rb - lb;
396 int const mD = fi.metrics->maxDescent();
397 int const h = mA + mD;
399 pm.fill(Qt::transparent);
401 p.setQPainterPen(computeColor(f.realColor()));
402 if (p.font() != fi.font)
404 // We need to draw the text as LTR as we use our own bidi code.
405 p.setLayoutDirection(Qt::LeftToRight);
406 p.drawText(-lb, mA, str);
407 QPixmapCache::insert(key, pm);
408 //LYXERR(Debug::PAINTING, "h=" << h << " mA=" << mA << " mD=" << mD
409 // << " w=" << w << " lb=" << lb << " tw=" << textwidth
412 // Draw the cached pixmap.
413 drawPixmap(x + lb, y - mA, pm);
419 } // namespace frontend