A refactoring attempt for the Retina display issue (#94)

This is what's been done:
 - remove the old double and single buffering /w mask approach
 - modify the bitmap rendering so it's done in a offscreen
   image before subsampling
   (effect: rulers display smoothly in subsampling mode)
 - refactoring the "device pixel ratio" topic:
   Made the DPR a variable, viewport width is scaled up
   to reflect the true image size, inserted #ifdef's for Qt4.

DISCLAIMER: I don't know whether this still works - I don't
have a Retina display :-(
This commit is contained in:
Matthias Koefferlein 2018-03-19 22:22:24 +01:00 committed by Thomas Ferreira de Lima
parent a6738f5be4
commit 0a01946202
4 changed files with 126 additions and 210 deletions

View File

@ -424,6 +424,7 @@ bitmaps_to_image_rgb (const std::vector<lay::ViewOp> &view_ops_in,
const lay::LineStyles &ls, const lay::LineStyles &ls,
QImage *pimage, unsigned int width, unsigned int height, QImage *pimage, unsigned int width, unsigned int height,
bool use_bitmap_index, bool use_bitmap_index,
bool transparent,
QMutex *mutex) QMutex *mutex)
{ {
unsigned int n_in = view_ops_in.size (); unsigned int n_in = view_ops_in.size ();
@ -583,11 +584,15 @@ bitmaps_to_image_rgb (const std::vector<lay::ViewOp> &view_ops_in,
unsigned int i = 0; unsigned int i = 0;
for (unsigned int x = 0; x < width; x += 32, ++i) { for (unsigned int x = 0; x < width; x += 32, ++i) {
lay::color_t y[32] = { lay::color_t y[32];
fill_bits, fill_bits, fill_bits, fill_bits, fill_bits, fill_bits, fill_bits, fill_bits, if (transparent) {
fill_bits, fill_bits, fill_bits, fill_bits, fill_bits, fill_bits, fill_bits, fill_bits, for (int i = 0; i < 32; ++i) {
fill_bits, fill_bits, fill_bits, fill_bits, fill_bits, fill_bits, fill_bits, fill_bits, y[i] = 0;
fill_bits, fill_bits, fill_bits, fill_bits, fill_bits, fill_bits, fill_bits, fill_bits, }
} else {
for (int i = 0; i < 32; ++i) {
y[i] = fill_bits;
}
}; };
lay::color_t z[32] = { lay::color_t z[32] = {
@ -603,17 +608,32 @@ bitmaps_to_image_rgb (const std::vector<lay::ViewOp> &view_ops_in,
dptr = dptr_end - nwords + i; dptr = dptr_end - nwords + i;
for (int j = masks.size () - 1; j >= 0; --j) { for (int j = masks.size () - 1; j >= 0; --j) {
uint32_t d = *dptr; uint32_t d = *dptr;
if (d != 0) { if (d != 0) {
uint32_t m = 1;
for (unsigned int k = 0; k < 32 && x + k < width; ++k, m <<= 1) { if (transparent) {
if ((d & m) != 0) { uint32_t m = 1;
y [k] |= masks [j].first & z [k]; for (unsigned int k = 0; k < 32 && x + k < width; ++k, m <<= 1) {
z [k] &= masks [j].second; if ((d & m) != 0) {
y [k] |= (masks [j].first & z [k]) | fill_bits;
z [k] &= masks [j].second;
}
}
} else {
uint32_t m = 1;
for (unsigned int k = 0; k < 32 && x + k < width; ++k, m <<= 1) {
if ((d & m) != 0) {
y [k] |= masks [j].first & z [k];
z [k] &= masks [j].second;
}
} }
} }
} }
dptr -= nwords; dptr -= nwords;
} }
for (unsigned int k = 0; k < 32 && x + k < width; ++k) { for (unsigned int k = 0; k < 32 && x + k < width; ++k) {
@ -843,7 +863,8 @@ bitmaps_to_image (const std::vector<lay::ViewOp> &view_ops_in,
if (pimage->depth () <= 1) { if (pimage->depth () <= 1) {
bitmaps_to_image_mono (view_ops_in, pbitmaps_in, dp, ls, pimage, width, height, use_bitmap_index, mutex); bitmaps_to_image_mono (view_ops_in, pbitmaps_in, dp, ls, pimage, width, height, use_bitmap_index, mutex);
} else { } else {
bitmaps_to_image_rgb (view_ops_in, pbitmaps_in, dp, ls, pimage, width, height, use_bitmap_index, mutex); bool transparent = (pimage->format () == QImage::Format_ARGB32);
bitmaps_to_image_rgb (view_ops_in, pbitmaps_in, dp, ls, pimage, width, height, use_bitmap_index, transparent, mutex);
} }
} }

View File

@ -27,6 +27,7 @@
#include <QPainter> #include <QPainter>
#include <QApplication> #include <QApplication>
#include <QBuffer> #include <QBuffer>
#include <QWheelEvent>
#include "tlTimer.h" #include "tlTimer.h"
#include "tlLog.h" #include "tlLog.h"
@ -130,19 +131,6 @@ std::string ImageCacheEntry::to_string () const
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
#if defined (_WIN32) || defined (Q_WS_MAC) || QT_VERSION >= 0x40600
// Windows build runs well with single buffering
// and Qt built with double buffering.
// The same is true vor MAC OS X
// In Qt 4.8 the SINGLE_BUFFERED_MASK implementation no longer
// works properly and double buffering is default anyway.
# define SINGLE_BUFFERED
#else
// X11 runs better with the SINGLE_BUFFERED_MASK scheme
// # define SINGLE_BUFFERED
# define SINGLE_BUFFERED_MASK
#endif
static void static void
blowup (const QImage &src, QImage &dest, unsigned int os) blowup (const QImage &src, QImage &dest, unsigned int os)
{ {
@ -164,28 +152,46 @@ blowup (const QImage &src, QImage &dest, unsigned int os)
} }
static void static void
subsample (const QImage &src, QImage &dest, unsigned int os) subsample (const QImage &src, QImage &dest, unsigned int os, double g)
{ {
// TODO: this is probably not compatible with the endianess of SPARC .. // TODO: this is probably not compatible with the endianess of SPARC ..
double g = 2.0; // is something 1.8 .. 2.2 // LUT's for combining the RGB channels
// forward transformation table
unsigned short lut1[256]; unsigned short lut1[256];
for (unsigned int i = 0; i < 256; ++i) { for (unsigned int i = 0; i < 256; ++i) {
double f = (65536 / (os * os)) - 1; double f = (65536 / (os * os)) - 1;
lut1[i] = (unsigned short)std::min (f, std::max (0.0, floor (0.5 + pow (i / 255.0, g) * f))); lut1[i] = (unsigned short)std::min (f, std::max (0.0, floor (0.5 + pow (i / 255.0, g) * f)));
} }
// backward transformation table
unsigned char lut2[65536]; unsigned char lut2[65536];
for (unsigned int i = 0; i < 65536; ++i) { for (unsigned int i = 0; i < 65536; ++i) {
double f = os * os * ((65536 / (os * os)) - 1); double f = os * os * ((65536 / (os * os)) - 1);
lut2[i] = (unsigned char)std::min (255.0, std::max (0.0, floor (0.5 + pow (i / f, 1.0 / g) * 255.0))); lut2[i] = (unsigned char)std::min (255.0, std::max (0.0, floor (0.5 + pow (i / f, 1.0 / g) * 255.0)));
} }
// LUT's for alpha channel
// forward transformation table
unsigned short luta1[256];
for (unsigned int i = 0; i < 256; ++i) {
double f = (65536 / (os * os)) - 1;
luta1[i] = (unsigned short)std::min (f, std::max (0.0, floor (0.5 + (i / 255.0) * f)));
}
// backward transformation table
unsigned char luta2[65536];
for (unsigned int i = 0; i < 65536; ++i) {
double f = os * os * ((65536 / (os * os)) - 1);
luta2[i] = (unsigned char)std::min (255.0, std::max (0.0, floor (0.5 + (i / f) * 255.0)));
}
unsigned int ymax = dest.height (); unsigned int ymax = dest.height ();
unsigned int xmax = dest.width (); unsigned int xmax = dest.width ();
unsigned short *buffer = new unsigned short[xmax * 3]; unsigned short *buffer = new unsigned short[xmax * 4];
for (unsigned int y = 0; y < ymax; ++y) { for (unsigned int y = 0; y < ymax; ++y) {
@ -199,16 +205,18 @@ subsample (const QImage &src, QImage &dest, unsigned int os)
pdest[0] = lut1[psrc[0]]; pdest[0] = lut1[psrc[0]];
pdest[1] = lut1[psrc[1]]; pdest[1] = lut1[psrc[1]];
pdest[2] = lut1[psrc[2]]; pdest[2] = lut1[psrc[2]];
pdest[3] = luta1[psrc[3]];
psrc += 4; psrc += 4;
for (unsigned int j = os; j > 1; j--) { for (unsigned int j = os; j > 1; j--) {
pdest[0] += lut1[psrc[0]]; pdest[0] += lut1[psrc[0]];
pdest[1] += lut1[psrc[1]]; pdest[1] += lut1[psrc[1]];
pdest[2] += lut1[psrc[2]]; pdest[2] += lut1[psrc[2]];
pdest[3] += luta1[psrc[3]];
psrc += 4; psrc += 4;
} }
pdest += 3; pdest += 4;
} }
@ -225,10 +233,11 @@ subsample (const QImage &src, QImage &dest, unsigned int os)
pdest[0] += lut1[psrc[0]]; pdest[0] += lut1[psrc[0]];
pdest[1] += lut1[psrc[1]]; pdest[1] += lut1[psrc[1]];
pdest[2] += lut1[psrc[2]]; pdest[2] += lut1[psrc[2]];
pdest[3] += luta1[psrc[3]];
psrc += 4; psrc += 4;
} }
pdest += 3; pdest += 4;
} }
@ -243,7 +252,7 @@ subsample (const QImage &src, QImage &dest, unsigned int os)
*pdest++ = lut2[*psrc++]; *pdest++ = lut2[*psrc++];
*pdest++ = lut2[*psrc++]; *pdest++ = lut2[*psrc++];
*pdest++ = lut2[*psrc++]; *pdest++ = lut2[*psrc++];
*pdest++ = 0xff; *pdest++ = luta2[*psrc++];
} }
} }
@ -253,64 +262,6 @@ subsample (const QImage &src, QImage &dest, unsigned int os)
delete[] buffer; delete[] buffer;
} }
void
subsample (unsigned char *data, unsigned int width, unsigned int height, unsigned int os)
{
if (os == 1) {
return;
}
unsigned int nbytes = (width * os + 7) / 8;
unsigned char *psrc = data;
unsigned char *pdest = psrc;
for (unsigned int y = 0; y < height; ++y) {
unsigned int bdest = 0;
unsigned int bsrc = 0;
unsigned char dd = 0;
unsigned char m = 0x01;
for (unsigned int i = 0; i < nbytes; ++i) {
unsigned char *p = psrc++;
unsigned char d = *p;
for (unsigned int j = 1; j < os; ++j) {
p += nbytes;
d |= *p;
}
for (unsigned int b = 0; b < 8; ++b) {
if ((d & 1) != 0) {
dd |= m;
}
d >>= 1;
if (++bsrc == os) {
bsrc = 0;
++bdest;
m <<= 1;
if (bdest == 8) {
*pdest++ = dd;
bdest = 0;
dd = 0;
m = 0x01;
}
}
}
}
psrc += nbytes * (os - 1);
if (bdest > 0) {
*pdest++ = dd;
}
}
}
void void
invert (unsigned char *data, unsigned int width, unsigned int height) invert (unsigned char *data, unsigned int width, unsigned int height)
{ {
@ -329,6 +280,7 @@ LayoutCanvas::LayoutCanvas (QWidget *parent, lay::LayoutView *view, const char *
mp_image (0), mp_image_bg (0), mp_pixmap (0), mp_image (0), mp_image_bg (0), mp_pixmap (0),
m_background (0), m_foreground (0), m_active (0), m_background (0), m_foreground (0), m_active (0),
m_oversampling (1), m_oversampling (1),
m_dpr (1),
m_need_redraw (false), m_need_redraw (false),
m_redraw_clearing (false), m_redraw_clearing (false),
m_redraw_force_update (true), m_redraw_force_update (true),
@ -337,9 +289,18 @@ LayoutCanvas::LayoutCanvas (QWidget *parent, lay::LayoutView *view, const char *
m_do_end_of_drawing_dm (this, &LayoutCanvas::do_end_of_drawing), m_do_end_of_drawing_dm (this, &LayoutCanvas::do_end_of_drawing),
m_image_cache_size (1) m_image_cache_size (1)
{ {
#if QT_VERSION > 0x050000
m_dpr = devicePixelRatio ();
#else
m_dpr = 1;
#endif
// The gamma value used for subsampling: something between 1.8 and 2.2.
m_gamma = 2.0;
// some reasonable initializations for the size // some reasonable initializations for the size
m_viewport.set_size (100, 100); m_viewport.set_size (100, 100);
m_viewport_l.set_size (m_viewport.width () * m_oversampling * 2, m_viewport.height () * m_oversampling * 2); m_viewport_l.set_size (m_viewport.width () * m_oversampling, m_viewport.height () * m_oversampling);
mp_redraw_thread = new lay::RedrawThread (this, view); mp_redraw_thread = new lay::RedrawThread (this, view);
@ -347,9 +308,6 @@ LayoutCanvas::LayoutCanvas (QWidget *parent, lay::LayoutView *view, const char *
set_colors (palette ().color (QPalette::Normal, QPalette::Background), set_colors (palette ().color (QPalette::Normal, QPalette::Background),
palette ().color (QPalette::Normal, QPalette::Text), palette ().color (QPalette::Normal, QPalette::Text),
palette ().color (QPalette::Normal, QPalette::Mid)); palette ().color (QPalette::Normal, QPalette::Mid));
#ifndef SINGLE_BUFFERED
setAttribute (Qt::WA_PaintOnScreen);
#endif
setAttribute (Qt::WA_NoSystemBackground); setAttribute (Qt::WA_NoSystemBackground);
} }
@ -416,7 +374,7 @@ LayoutCanvas::set_oversampling (unsigned int os)
if (os != m_oversampling) { if (os != m_oversampling) {
m_image_cache.clear (); m_image_cache.clear ();
m_oversampling = os; m_oversampling = os;
m_viewport_l.set_size (m_viewport.width () * m_oversampling * 2, m_viewport.height () * m_oversampling * 2); m_viewport_l.set_size (m_viewport.width () * m_oversampling, m_viewport.height () * m_oversampling);
do_redraw_all (); do_redraw_all ();
} }
} }
@ -469,7 +427,7 @@ LayoutCanvas::prepare_drawing ()
{ {
if (m_need_redraw) { if (m_need_redraw) {
BitmapViewObjectCanvas::set_size (m_viewport_l.width (), m_viewport_l.height (), 1.0 / double (m_oversampling) / 2); BitmapViewObjectCanvas::set_size (m_viewport_l.width (), m_viewport_l.height (), 1.0 / double (m_oversampling * m_dpr));
if (! mp_image || if (! mp_image ||
(unsigned int) mp_image->width () != m_viewport_l.width () || (unsigned int) mp_image->width () != m_viewport_l.width () ||
@ -478,7 +436,9 @@ LayoutCanvas::prepare_drawing ()
delete mp_image; delete mp_image;
} }
mp_image = new QImage (m_viewport_l.width (), m_viewport_l.height (), QImage::Format_RGB32); mp_image = new QImage (m_viewport_l.width (), m_viewport_l.height (), QImage::Format_RGB32);
mp_image->setDevicePixelRatio(2.0); #if QT_VERSION > 0x050000
mp_image->setDevicePixelRatio (double (m_dpr));
#endif
if (mp_pixmap) { if (mp_pixmap) {
delete mp_pixmap; delete mp_pixmap;
mp_pixmap = 0; mp_pixmap = 0;
@ -506,7 +466,7 @@ LayoutCanvas::prepare_drawing ()
++c; ++c;
} }
mp_redraw_thread->commit (m_layers, m_viewport_l, 1.0 / double (m_oversampling) / 2); mp_redraw_thread->commit (m_layers, m_viewport_l, 1.0 / double (m_oversampling * m_dpr));
if (tl::verbosity () >= 20) { if (tl::verbosity () >= 20) {
tl::info << "Restored image from cache"; tl::info << "Restored image from cache";
@ -556,7 +516,7 @@ LayoutCanvas::prepare_drawing ()
} }
if (m_redraw_clearing) { if (m_redraw_clearing) {
mp_redraw_thread->start (mp_view->synchronous () ? 0 : mp_view->drawing_workers (), m_layers, m_viewport_l, 1.0 / double (m_oversampling) / 2, m_redraw_force_update); mp_redraw_thread->start (mp_view->synchronous () ? 0 : mp_view->drawing_workers (), m_layers, m_viewport_l, 1.0 / double (m_oversampling * m_dpr), m_redraw_force_update);
} else { } else {
mp_redraw_thread->restart (m_need_redraw_layer); mp_redraw_thread->restart (m_need_redraw_layer);
} }
@ -637,11 +597,6 @@ LayoutCanvas::paintEvent (QPaintEvent *)
} }
// prepare a buffer
size_t nbytes = (m_viewport_l.width () + 7) / 8 * m_viewport_l.height ();
unsigned char *p_data = new unsigned char [nbytes];
memset (p_data, 0, nbytes);
// create a base pixmap consisting of the layout with background // create a base pixmap consisting of the layout with background
// and static foreground objects // and static foreground objects
@ -661,16 +616,20 @@ LayoutCanvas::paintEvent (QPaintEvent *)
if (fg_bitmaps () > 0) { if (fg_bitmaps () > 0) {
QImage full_image (*mp_image); QImage full_image (*mp_image);
full_image.setDevicePixelRatio(2.0); #if QT_VERSION > 0x050000
full_image.setDevicePixelRatio (double (m_dpr));
#endif
bitmaps_to_image (fg_view_op_vector (), fg_bitmap_vector (), dither_pattern (), line_styles (), &full_image, m_viewport_l.width (), m_viewport_l.height (), false, &m_mutex); bitmaps_to_image (fg_view_op_vector (), fg_bitmap_vector (), dither_pattern (), line_styles (), &full_image, m_viewport_l.width (), m_viewport_l.height (), false, &m_mutex);
// render the foreground parts .. // render the foreground parts ..
if (m_oversampling == 1) { if (m_oversampling == 1) {
*mp_pixmap = QPixmap::fromImage (full_image); // Qt 4.6.0 workaround *mp_pixmap = QPixmap::fromImage (full_image); // Qt 4.6.0 workaround
} else { } else {
QImage subsampled_image (m_viewport.width () * 2, m_viewport.height () * 2, mp_image->format ()); QImage subsampled_image (m_viewport.width (), m_viewport.height (), mp_image->format ());
subsampled_image.setDevicePixelRatio(2.0); #if QT_VERSION > 0x050000
subsample (full_image, subsampled_image, m_oversampling); subsampled_image.setDevicePixelRatio (double (m_dpr));
#endif
subsample (full_image, subsampled_image, m_oversampling, m_gamma);
*mp_pixmap = QPixmap::fromImage (subsampled_image); // Qt 4.6.0 workaround *mp_pixmap = QPixmap::fromImage (subsampled_image); // Qt 4.6.0 workaround
} }
@ -680,9 +639,11 @@ LayoutCanvas::paintEvent (QPaintEvent *)
} else { } else {
QImage subsampled_image (m_viewport.width () * 2, m_viewport.height () * 2, mp_image->format ()); QImage subsampled_image (m_viewport.width (), m_viewport.height (), mp_image->format ());
subsampled_image.setDevicePixelRatio(2.0); #if QT_VERSION > 0x050000
subsample (*mp_image, subsampled_image, m_oversampling); subsampled_image.setDevicePixelRatio (double (m_dpr));
#endif
subsample (*mp_image, subsampled_image, m_oversampling, m_gamma);
*mp_pixmap = QPixmap::fromImage (subsampled_image); *mp_pixmap = QPixmap::fromImage (subsampled_image);
} }
@ -692,109 +653,36 @@ LayoutCanvas::paintEvent (QPaintEvent *)
// erase any previous data // erase any previous data
clear_fg_bitmaps (); clear_fg_bitmaps ();
// render foreground content // render dynamic foreground content
do_render (m_viewport_l, *this, false); do_render (m_viewport_l, *this, false);
#if defined(SINGLE_BUFFERED)
// Single-buffering scheme - somewhat more flickering
// but less resource requirements.
// produce the pixmap first and then overdraw with dynamic content. // produce the pixmap first and then overdraw with dynamic content.
QPainter painter (this); QPainter painter (this);
painter.drawPixmap (QPoint (0, 0), *mp_pixmap); painter.drawPixmap (QPoint (0, 0), *mp_pixmap);
// dynamic bitmaps are always drawn in transparent mode .. if (fg_bitmaps () > 0) {
painter.setBackgroundMode (Qt::TransparentMode);
// create QBitmap objects and paint them for the bitmaps QImage full_image (mp_image->size ().width (), mp_image->size ().height (), QImage::Format_ARGB32);
// present full_image.fill (0);
for (unsigned int n = 0; n < fg_bitmaps (); ++n) {
if (fg_bitmap (n) != 0) {
memset (p_data, 0, nbytes);
bitmap_to_bitmap (fg_style (n), *fg_bitmap (n), p_data, m_viewport_l.width (), m_viewport_l.height (), dither_pattern (), line_styles ());
subsample (p_data, m_viewport.width (), m_viewport.height (), m_oversampling * 2);
QBitmap bitmap = QBitmap::fromData (QSize (m_viewport.width (), m_viewport.height ()), p_data);
QPen pen (QRgb (fg_style (n).ormask ()));
//pen.setWidthF(1.5);
painter.setPen (pen);
painter.drawPixmap (0, 0, bitmap);
}
}
#elif defined(SINGLE_BUFFERED_MASK)
// Single-buffering scheme with mask - somewhat more flickering
// but less resource requirements.
// combine the bitmaps into a mask
for (unsigned int n = 0; n < fg_bitmaps (); ++n) {
if (fg_bitmap (n) != 0) {
bitmap_to_bitmap (fg_style (n), *fg_bitmap (n), p_data, m_viewport_l.width (), m_viewport_l.height (), dither_pattern ());
}
}
// invert and subsample the mask
subsample (p_data, m_viewport.width (), m_viewport.height (), m_oversampling * 2);
invert (p_data, m_viewport.width (), m_viewport.height ());
// create the mask
QBitmap mask = QBitmap::fromData (QSize (m_viewport.width (), m_viewport.height ()), p_data);
mp_pixmap->setMask (mask);
// produce the pixmap with the mask
QPainter painter (this);
painter.drawPixmap (QPoint (0, 0), *mp_pixmap);
// dynamic bitmaps are always drawn in transparent mode ..
painter.setBackgroundMode (Qt::TransparentMode);
// create QBitmap objects and paint them for the bitmaps
// present
for (unsigned int n = 0; n < fg_bitmaps (); ++n) {
if (fg_bitmap (n) != 0) {
memset (p_data, 0, nbytes);
bitmap_to_bitmap (fg_style (n), *fg_bitmap (n), p_data, m_viewport_l.width (), m_viewport_l.height (), dither_pattern ());
subsample (p_data, m_viewport.width (), m_viewport.height (), m_oversampling * 2);
QBitmap bitmap = QBitmap::fromData (QSize (m_viewport.width (), m_viewport.height ()), p_data);
painter.setPen (QRgb (fg_style (n).ormask ()));
painter.drawPixmap (0, 0, bitmap);
}
}
#else
// Double-buffering scheme - somewhat less flickering but
// only efficient if the QImage has the precise size and
// at the cost of twice the X resource requirements.
// create an intermediate pixmap
QPixmap px (*mp_pixmap);
// dynamic bitmaps are always drawn in transparent mode ..
QPainter painter (&px);
painter.setBackgroundMode (Qt::TransparentMode);
// create QBitmap objects and paint them for the bitmaps
// present
for (unsigned int n = 0; n < fg_bitmaps (); ++n) {
if (fg_bitmap (n) != 0) {
memset (p_data, 0, nbytes);
bitmap_to_bitmap (fg_style (n), *fg_bitmap (n), p_data, m_viewport.width (), m_viewport.height (), dither_pattern ());
QBitmap bitmap = QBitmap::fromData (QSize (m_viewport.width (), m_viewport.height ()), p_data);
painter.setPen (QRgb (fg_style (n).ormask ()));
painter.drawPixmap (0, 0, bitmap);
}
}
// produce the combined pixmap on the window
QPainter screen_painter (this);
screen_painter.drawPixmap (QPoint (0, 0), px);
#if QT_VERSION > 0x050000
full_image.setDevicePixelRatio (double (m_dpr));
#endif #endif
bitmaps_to_image (fg_view_op_vector (), fg_bitmap_vector (), dither_pattern (), line_styles (), &full_image, m_viewport_l.width (), m_viewport_l.height (), false, &m_mutex);
// delete buffer // render the foreground parts ..
delete [] p_data; if (m_oversampling == 1) {
painter.drawPixmap (QPoint (0, 0), QPixmap::fromImage (full_image));
} else {
QImage subsampled_image (m_viewport.width (), m_viewport.height (), QImage::Format_ARGB32);
#if QT_VERSION > 0x050000
subsampled_image.setDevicePixelRatio (double (m_dpr));
#endif
subsample (full_image, subsampled_image, m_oversampling, m_gamma);
painter.drawPixmap (QPoint (0, 0), QPixmap::fromImage (subsampled_image));
}
}
// erase dynamic bitmaps // erase dynamic bitmaps
clear_fg_bitmaps (); clear_fg_bitmaps ();
@ -815,6 +703,9 @@ public:
: BitmapViewObjectCanvas (width_l, height_l, resolution), : BitmapViewObjectCanvas (width_l, height_l, resolution),
m_bg (bg), m_fg (fg), m_ac (ac), mp_image (img) m_bg (bg), m_fg (fg), m_ac (ac), mp_image (img)
{ {
// TODO: Good choice?
m_gamma = 2.0;
if (img->width () != int (width_l) || img->height () != int (height_l)) { if (img->width () != int (width_l) || img->height () != int (height_l)) {
mp_image_l = new QImage (width_l, height_l, img->format ()); mp_image_l = new QImage (width_l, height_l, img->format ());
mp_image_l->fill (bg.rgb ()); mp_image_l->fill (bg.rgb ());
@ -859,7 +750,7 @@ public:
unsigned int os = mp_image_l->width () / width; unsigned int os = mp_image_l->width () / width;
blowup (*mp_image, *mp_image_l, os); blowup (*mp_image, *mp_image_l, os);
bitmaps_to_image (fg_view_op_vector (), fg_bitmap_vector (), dp, ls, mp_image_l, mp_image_l->width (), mp_image_l->height (), false, 0); bitmaps_to_image (fg_view_op_vector (), fg_bitmap_vector (), dp, ls, mp_image_l, mp_image_l->width (), mp_image_l->height (), false, 0);
subsample (*mp_image_l, *mp_image, os); subsample (*mp_image_l, *mp_image, os, m_gamma);
} else { } else {
bitmaps_to_image (fg_view_op_vector (), fg_bitmap_vector (), dp, ls, mp_image, width, height, false, 0); bitmaps_to_image (fg_view_op_vector (), fg_bitmap_vector (), dp, ls, mp_image, width, height, false, 0);
} }
@ -870,7 +761,7 @@ public:
{ {
if (mp_image_l && mp_image->width () > 0) { if (mp_image_l && mp_image->width () > 0) {
unsigned int os = mp_image_l->width () / mp_image->width (); unsigned int os = mp_image_l->width () / mp_image->width ();
subsample (*mp_image_l, *mp_image, os); subsample (*mp_image_l, *mp_image, os, m_gamma);
} }
} }
@ -878,6 +769,7 @@ private:
QColor m_bg, m_fg, m_ac; QColor m_bg, m_fg, m_ac;
QImage *mp_image; QImage *mp_image;
QImage *mp_image_l; QImage *mp_image_l;
double m_gamma;
}; };
QImage QImage
@ -896,7 +788,7 @@ LayoutCanvas::image_with_options (unsigned int width, unsigned int height, int l
linewidth = 1; linewidth = 1;
} }
if (resolution <= 0.0) { if (resolution <= 0.0) {
resolution = 2.0 / oversampling; resolution = 1.0 / oversampling;
} }
if (background == QColor ()) { if (background == QColor ()) {
background = background_color (); background = background_color ();
@ -986,7 +878,7 @@ LayoutCanvas::screenshot ()
QImage img (m_viewport.width (), m_viewport.height (), QImage::Format_RGB32); QImage img (m_viewport.width (), m_viewport.height (), QImage::Format_RGB32);
img.fill (m_background); img.fill (m_background);
DetachedViewObjectCanvas vo_canvas (background_color (), foreground_color (), active_color (), m_viewport_l.width (), m_viewport_l.height (), 1.0 / double (m_oversampling) / 2, &img); DetachedViewObjectCanvas vo_canvas (background_color (), foreground_color (), active_color (), m_viewport_l.width (), m_viewport_l.height (), 1.0 / double (m_oversampling * m_dpr), &img);
// and paint the background objects. It uses "img" to paint on. // and paint the background objects. It uses "img" to paint on.
do_render_bg (m_viewport_l, vo_canvas); do_render_bg (m_viewport_l, vo_canvas);
@ -1014,8 +906,8 @@ LayoutCanvas::resizeEvent (QResizeEvent *)
m_image_cache.clear (); m_image_cache.clear ();
// set the viewport to the new size // set the viewport to the new size
m_viewport.set_size (width (), height ()); m_viewport.set_size (width () * m_dpr, height () * m_dpr);
m_viewport_l.set_size (width () * m_oversampling * 2, height () * m_oversampling * 2); m_viewport_l.set_size (width () * m_oversampling * m_dpr, height () * m_oversampling * m_dpr);
mouse_event_trans (m_viewport.trans ()); mouse_event_trans (m_viewport.trans ());
do_redraw_all (true); do_redraw_all (true);
viewport_changed_event (); viewport_changed_event ();

View File

@ -367,6 +367,8 @@ private:
lay::DitherPattern m_dither_pattern; lay::DitherPattern m_dither_pattern;
lay::LineStyles m_line_styles; lay::LineStyles m_line_styles;
unsigned int m_oversampling; unsigned int m_oversampling;
unsigned int m_dpr;
double m_gamma;
bool m_need_redraw; bool m_need_redraw;
bool m_redraw_clearing; bool m_redraw_clearing;

View File

@ -59,7 +59,8 @@ void
RubberBox::render (const Viewport &vp, ViewObjectCanvas &canvas) RubberBox::render (const Viewport &vp, ViewObjectCanvas &canvas)
{ {
lay::Renderer &r = canvas.renderer (); lay::Renderer &r = canvas.renderer ();
lay::CanvasPlane *plane = canvas.plane (lay::ViewOp (m_color, lay::ViewOp::Copy, 0, m_stipple, 0)); int lw = int (0.5 + 1.0 / r.resolution ());
lay::CanvasPlane *plane = canvas.plane (lay::ViewOp (m_color, lay::ViewOp::Copy, 0, m_stipple, 0, lay::ViewOp::Rect, lw));
if (plane) { if (plane) {
r.draw (vp.trans () * db::DBox (m_p1, m_p2), 0, plane, 0, 0); r.draw (vp.trans () * db::DBox (m_p1, m_p2), 0, plane, 0, 0);
} }