summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJabier Arraiza <jabier.arraiza@marker.es>2017-12-12 21:53:22 +0000
committerJabier Arraiza <jabier.arraiza@marker.es>2017-12-12 21:53:22 +0000
commitcfecf64d6adad14f73f9c169b181ec59bf54c8f3 (patch)
treed976a56df96484ab7d4efe1565c372218db6fdae /src
parentMoving tolerance (diff)
parentName a few more widgets. (diff)
downloadinkscape-cfecf64d6adad14f73f9c169b181ec59bf54c8f3.tar.gz
inkscape-cfecf64d6adad14f73f9c169b181ec59bf54c8f3.zip
Merge branch 'master' into powerpencilII
Diffstat (limited to 'src')
-rw-r--r--src/display/cairo-utils.cpp22
-rw-r--r--src/display/drawing-item.cpp11
-rw-r--r--src/display/drawing-surface.cpp44
-rw-r--r--src/display/drawing-surface.h8
-rw-r--r--src/display/nr-filter-diffuselighting.cpp23
-rw-r--r--src/display/nr-filter-displacement-map.cpp11
-rw-r--r--src/display/nr-filter-gaussian.cpp14
-rw-r--r--src/display/nr-filter-image.cpp23
-rw-r--r--src/display/nr-filter-morphology.cpp5
-rw-r--r--src/display/nr-filter-slot.cpp8
-rw-r--r--src/display/nr-filter-slot.h7
-rw-r--r--src/display/nr-filter-specularlighting.cpp20
-rw-r--r--src/display/nr-filter-turbulence.cpp23
-rw-r--r--src/display/nr-filter.cpp5
-rw-r--r--src/display/nr-light.cpp22
-rw-r--r--src/display/nr-light.h6
-rw-r--r--src/display/sodipodi-ctrl.cpp327
-rw-r--r--src/display/sp-canvas.cpp120
-rw-r--r--src/display/sp-canvas.h3
-rw-r--r--src/ui/tools/tool-base.cpp3
-rw-r--r--src/ui/widget/notebook-page.cpp1
-rw-r--r--src/ui/widget/object-composite-settings.cpp1
-rw-r--r--src/ui/widget/spin-scale.cpp2
-rw-r--r--src/widgets/paint-selector.cpp1
24 files changed, 479 insertions, 231 deletions
diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp
index 8045007e7..aeda3f379 100644
--- a/src/display/cairo-utils.cpp
+++ b/src/display/cairo-utils.cpp
@@ -842,8 +842,19 @@ ink_cairo_surface_create_identical(cairo_surface_t *s)
cairo_surface_t *
ink_cairo_surface_create_same_size(cairo_surface_t *s, cairo_content_t c)
{
- cairo_surface_t *ns = cairo_surface_create_similar(s, c,
- ink_cairo_surface_get_width(s), ink_cairo_surface_get_height(s));
+ // ink_cairo_surface_get_width()/height() returns value in pixels
+ // cairo_surface_create_similar() uses device units
+ double x_scale = 0;
+ double y_scale = 0;
+ cairo_surface_get_device_scale( s, &x_scale, &y_scale );
+
+ assert (x_scale > 0);
+ assert (y_scale > 0);
+
+ cairo_surface_t *ns =
+ cairo_surface_create_similar(s, c,
+ ink_cairo_surface_get_width(s)/x_scale,
+ ink_cairo_surface_get_height(s)/y_scale);
return ns;
}
@@ -908,6 +919,9 @@ ink_cairo_surface_blit(cairo_surface_t *src, cairo_surface_t *dest)
}
}
+/**
+ * Return width in pixels.
+ */
int
ink_cairo_surface_get_width(cairo_surface_t *surface)
{
@@ -916,6 +930,10 @@ ink_cairo_surface_get_width(cairo_surface_t *surface)
assert(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE);
return cairo_image_surface_get_width(surface);
}
+
+/**
+ * Return height in pixels.
+ */
int
ink_cairo_surface_get_height(cairo_surface_t *surface)
{
diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp
index 43db4f259..cc4673bc8 100644
--- a/src/display/drawing-item.cpp
+++ b/src/display/drawing-item.cpp
@@ -667,7 +667,7 @@ struct MaskLuminanceToAlpha {
/**
* Rasterize items.
- * This method submits the drawing opeartions required to draw this item
+ * This method submits the drawing operations required to draw this item
* to the supplied DrawingContext, restricting drawing the specified area.
*
* This method does some common tasks and calls the item-specific rendering
@@ -699,6 +699,9 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag
Geom::OptIntRect carea = Geom::intersect(area, _drawbox);
if (!carea) return RENDER_OK;
+ // Device scale for HiDPI screens (typically 1 or 2)
+ int device_scale = dc.surface()->device_scale();
+
switch(_antialias){
case 0:
cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_NONE);
@@ -731,7 +734,7 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag
Geom::OptIntRect cl = _drawing.cacheLimit();
cl.intersectWith(_drawbox);
if (cl) {
- _cache = new DrawingCache(*cl);
+ _cache = new DrawingCache(*cl, device_scale);
}
}
} else {
@@ -785,7 +788,7 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag
iarea.intersectWith(_drawbox);
}
- DrawingSurface intermediate(*iarea);
+ DrawingSurface intermediate(*iarea, device_scale);
DrawingContext ict(intermediate);
unsigned render_result = RENDER_OK;
@@ -832,7 +835,7 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag
if (bg_root->_background_new) break;
}
if (bg_root) {
- DrawingSurface bg(*iarea);
+ DrawingSurface bg(*iarea, device_scale);
DrawingContext bgdc(bg);
bg_root->render(bgdc, *iarea, flags | RENDER_FILTER_BACKGROUND, this);
_filter->render(this, ict, &bgdc);
diff --git a/src/display/drawing-surface.cpp b/src/display/drawing-surface.cpp
index d2540de66..2752789e2 100644
--- a/src/display/drawing-surface.cpp
+++ b/src/display/drawing-surface.cpp
@@ -41,12 +41,15 @@ using Geom::Y;
* When a drawing context is created for this surface, its pixels
* will cover the area under the given rectangle.
*/
-DrawingSurface::DrawingSurface(Geom::IntRect const &area)
+DrawingSurface::DrawingSurface(Geom::IntRect const &area, int device_scale)
: _surface(NULL)
, _origin(area.min())
, _scale(1, 1)
, _pixels(area.dimensions())
-{}
+ , _device_scale(device_scale)
+{
+ assert(_device_scale > 0);
+}
/**
* Creates a surface with the given logical and physical extents.
@@ -56,12 +59,15 @@ DrawingSurface::DrawingSurface(Geom::IntRect const &area)
* @param logbox Logical extents of the surface
* @param pixdims Pixel dimensions of the surface.
*/
-DrawingSurface::DrawingSurface(Geom::Rect const &logbox, Geom::IntPoint const &pixdims)
+DrawingSurface::DrawingSurface(Geom::Rect const &logbox, Geom::IntPoint const &pixdims, int device_scale)
: _surface(NULL)
, _origin(logbox.min())
, _scale(pixdims[X] / logbox.width(), pixdims[Y] / logbox.height())
, _pixels(pixdims)
-{}
+ , _device_scale(device_scale)
+{
+ assert(_device_scale > 0);
+}
/**
* Wrap a cairo_surface_t.
@@ -74,8 +80,18 @@ DrawingSurface::DrawingSurface(cairo_surface_t *surface, Geom::Point const &orig
, _scale(1, 1)
{
cairo_surface_reference(surface);
- _pixels[X] = cairo_image_surface_get_width(surface);
- _pixels[Y] = cairo_image_surface_get_height(surface);
+
+ double x_scale = 0;
+ double y_scale = 0;
+ cairo_surface_get_device_scale( surface, &x_scale, &y_scale);
+ if (x_scale != y_scale) {
+ std::cerr << "DrawingSurface::DrawingSurface: non-uniform device scale!" << std::endl;
+ }
+ _device_scale = x_scale;
+ assert(_device_scale > 0);
+
+ _pixels[X] = cairo_image_surface_get_width(surface)/_device_scale;
+ _pixels[Y] = cairo_image_surface_get_height(surface)/_device_scale;
}
DrawingSurface::~DrawingSurface()
@@ -119,6 +135,13 @@ DrawingSurface::scale() const
return _scale;
}
+/// Device scale for HiDPI screens
+int
+DrawingSurface::device_scale() const
+{
+ return _device_scale;
+}
+
/// Get the transformation applied to the drawing context on construction.
Geom::Affine
DrawingSurface::drawingTransform() const
@@ -153,7 +176,10 @@ DrawingSurface::createRawContext()
{
// deferred allocation
if (!_surface) {
- _surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, _pixels[X], _pixels[Y]);
+ _surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ _pixels[X] * _device_scale,
+ _pixels[Y] * _device_scale);
+ cairo_surface_set_device_scale(_surface, _device_scale, _device_scale);
}
cairo_t *ct = cairo_create(_surface);
if (_scale != Geom::Scale::identity()) {
@@ -172,8 +198,8 @@ DrawingSurface::pixelArea() const
//////////////////////////////////////////////////////////////////////////////
-DrawingCache::DrawingCache(Geom::IntRect const &area)
- : DrawingSurface(area)
+DrawingCache::DrawingCache(Geom::IntRect const &area, int device_scale)
+ : DrawingSurface(area, device_scale)
, _clean_region(cairo_region_create())
, _pending_area(area)
{}
diff --git a/src/display/drawing-surface.h b/src/display/drawing-surface.h
index 78471649a..569964721 100644
--- a/src/display/drawing-surface.h
+++ b/src/display/drawing-surface.h
@@ -29,8 +29,8 @@ class DrawingContext;
class DrawingSurface
{
public:
- explicit DrawingSurface(Geom::IntRect const &area);
- DrawingSurface(Geom::Rect const &logbox, Geom::IntPoint const &pixdims);
+ explicit DrawingSurface(Geom::IntRect const &area, int device_scale = 1);
+ DrawingSurface(Geom::Rect const &logbox, Geom::IntPoint const &pixdims, int device_scale = 1);
DrawingSurface(cairo_surface_t *surface, Geom::Point const &origin);
virtual ~DrawingSurface();
@@ -39,6 +39,7 @@ public:
Geom::Point dimensions() const;
Geom::Point origin() const;
Geom::Scale scale() const;
+ int device_scale() const;
Geom::Affine drawingTransform() const;
cairo_surface_type_t type() const;
void dropContents();
@@ -53,6 +54,7 @@ protected:
Geom::Point _origin;
Geom::Scale _scale;
Geom::IntPoint _pixels;
+ int _device_scale; // To support HiDPI screens
bool _has_context;
friend class DrawingContext;
@@ -62,7 +64,7 @@ class DrawingCache
: public DrawingSurface
{
public:
- explicit DrawingCache(Geom::IntRect const &area);
+ explicit DrawingCache(Geom::IntRect const &area, int device_scale = 1);
~DrawingCache();
void markDirty(Geom::IntRect const &area = Geom::IntRect::infinite());
diff --git a/src/display/nr-filter-diffuselighting.cpp b/src/display/nr-filter-diffuselighting.cpp
index c6724e3ba..ed5afa82a 100644
--- a/src/display/nr-filter-diffuselighting.cpp
+++ b/src/display/nr-filter-diffuselighting.cpp
@@ -83,9 +83,10 @@ private:
struct DiffusePointLight : public DiffuseLight {
DiffusePointLight(cairo_surface_t *bumpmap, SPFePointLight *light, guint32 color,
- Geom::Affine const &trans, double scale, double diffuse_constant, double x0, double y0)
+ Geom::Affine const &trans, double scale, double diffuse_constant,
+ double x0, double y0, int device_scale)
: DiffuseLight(bumpmap, scale, diffuse_constant)
- , _light(light, color, trans)
+ , _light(light, color, trans, device_scale)
, _x0(x0)
, _y0(y0)
{
@@ -105,9 +106,10 @@ private:
struct DiffuseSpotLight : public DiffuseLight {
DiffuseSpotLight(cairo_surface_t *bumpmap, SPFeSpotLight *light, guint32 color,
- Geom::Affine const &trans, double scale, double diffuse_constant, double x0, double y0)
+ Geom::Affine const &trans, double scale, double diffuse_constant,
+ double x0, double y0, int device_scale)
: DiffuseLight(bumpmap, scale, diffuse_constant)
- , _light(light, color, trans)
+ , _light(light, color, trans, device_scale)
, _x0(x0)
, _y0(y0)
{}
@@ -160,11 +162,18 @@ void FilterDiffuseLighting::render_cairo(FilterSlot &slot)
set_cairo_surface_ci(out, ci_fp );
guint32 color = SP_RGBA32_F_COMPOSE( r, g, b, 1.0 );
+ int device_scale = slot.get_device_scale();
+
Geom::Rect slot_area = slot.get_slot_area();
Geom::Point p = slot_area.min();
+
+ // trans has inverse y... so we can't just scale by device_scale! We must instead explicitly
+ // scale the point and spot light coordinates (as well as "scale").
+
Geom::Affine trans = slot.get_units().get_matrix_primitiveunits2pb();
+
double x0 = p[Geom::X], y0 = p[Geom::Y];
- double scale = surfaceScale * trans.descrim();
+ double scale = surfaceScale * trans.descrim() * device_scale;
switch (light_type) {
case DISTANT_LIGHT:
@@ -173,11 +182,11 @@ void FilterDiffuseLighting::render_cairo(FilterSlot &slot)
break;
case POINT_LIGHT:
ink_cairo_surface_synthesize(out,
- DiffusePointLight(input, light.point, color, trans, scale, diffuseConstant, x0, y0));
+ DiffusePointLight(input, light.point, color, trans, scale, diffuseConstant, x0, y0, device_scale));
break;
case SPOT_LIGHT:
ink_cairo_surface_synthesize(out,
- DiffuseSpotLight(input, light.spot, color, trans, scale, diffuseConstant, x0, y0));
+ DiffuseSpotLight(input, light.spot, color, trans, scale, diffuseConstant, x0, y0, device_scale));
break;
default: {
cairo_t *ct = cairo_create(out);
diff --git a/src/display/nr-filter-displacement-map.cpp b/src/display/nr-filter-displacement-map.cpp
index c0d1ae411..fb2f3a0f0 100644
--- a/src/display/nr-filter-displacement-map.cpp
+++ b/src/display/nr-filter-displacement-map.cpp
@@ -71,12 +71,17 @@ private:
void FilterDisplacementMap::render_cairo(FilterSlot &slot)
{
+ std::cout << "FilterDisplacementMap:" << std::endl;
cairo_surface_t *texture = slot.getcairo(_input);
cairo_surface_t *map = slot.getcairo(_input2);
cairo_surface_t *out = ink_cairo_surface_create_identical(texture);
// color_interpolation_filters for out same as texture. See spec.
copy_cairo_surface_ci( texture, out );
+ std::cout << " texture: " << cairo_image_surface_get_width(texture) << std::endl;
+ std::cout << " map: " << cairo_image_surface_get_width(map) << std::endl;
+ std::cout << " out: " << cairo_image_surface_get_width(out) << std::endl;
+
// We may need to transform map surface to correct color interpolation space. The map surface
// might be used as input to another primitive but it is likely that all the primitives in a given
// filter use the same color interpolation space so we don't copy the map before converting.
@@ -87,8 +92,10 @@ void FilterDisplacementMap::render_cairo(FilterSlot &slot)
set_cairo_surface_ci( map, ci_fp );
Geom::Affine trans = slot.get_units().get_matrix_primitiveunits2pb();
- double scalex = scale * trans.expansionX();
- double scaley = scale * trans.expansionY();
+
+ int device_scale = slot.get_device_scale();
+ double scalex = scale * trans.expansionX() * device_scale;
+ double scaley = scale * trans.expansionY() * device_scale;
ink_cairo_surface_synthesize(out, Displace(texture, map, Xchannel, Ychannel, scalex, scaley));
diff --git a/src/display/nr-filter-gaussian.cpp b/src/display/nr-filter-gaussian.cpp
index cdd01e75e..2227edfef 100644
--- a/src/display/nr-filter-gaussian.cpp
+++ b/src/display/nr-filter-gaussian.cpp
@@ -583,6 +583,12 @@ void FilterGaussian::render_cairo(FilterSlot &slot)
double deviation_x_orig = dx * trans.expansionX();
double deviation_y_orig = dy * trans.expansionY();
+
+ int device_scale = slot.get_device_scale();
+
+ deviation_x_orig *= device_scale;
+ deviation_y_orig *= device_scale;
+
cairo_format_t fmt = cairo_image_surface_get_format(in);
int bytes_per_pixel = 0;
switch (fmt) {
@@ -604,7 +610,7 @@ void FilterGaussian::render_cairo(FilterSlot &slot)
int x_step = 1 << _effect_subsample_step_log2(deviation_x_orig, quality);
int y_step = 1 << _effect_subsample_step_log2(deviation_y_orig, quality);
bool resampling = x_step > 1 || y_step > 1;
- int w_orig = ink_cairo_surface_get_width(in);
+ int w_orig = ink_cairo_surface_get_width(in); // Pixels
int h_orig = ink_cairo_surface_get_height(in);
int w_downsampled = resampling ? static_cast<int>(ceil(static_cast<double>(w_orig)/x_step))+1 : w_orig;
int h_downsampled = resampling ? static_cast<int>(ceil(static_cast<double>(h_orig)/y_step))+1 : h_orig;
@@ -633,8 +639,10 @@ void FilterGaussian::render_cairo(FilterSlot &slot)
cairo_surface_t *downsampled = NULL;
if (resampling) {
+ // Divide by device scale as w_downsampled is in pixels while
+ // cairo_surface_create_similar() uses device units.
downsampled = cairo_surface_create_similar(in, cairo_surface_get_content(in),
- w_downsampled, h_downsampled);
+ w_downsampled/device_scale, h_downsampled/device_scale);
cairo_t *ct = cairo_create(downsampled);
cairo_scale(ct, static_cast<double>(w_downsampled)/w_orig, static_cast<double>(h_downsampled)/h_orig);
cairo_set_source_surface(ct, in, 0, 0);
@@ -671,7 +679,7 @@ void FilterGaussian::render_cairo(FilterSlot &slot)
cairo_surface_mark_dirty(downsampled);
if (resampling) {
cairo_surface_t *upsampled = cairo_surface_create_similar(downsampled, cairo_surface_get_content(downsampled),
- w_orig, h_orig);
+ w_orig/device_scale, h_orig/device_scale);
cairo_t *ct = cairo_create(upsampled);
cairo_scale(ct, static_cast<double>(w_orig)/w_downsampled, static_cast<double>(h_orig)/h_downsampled);
cairo_set_source_surface(ct, downsampled, 0, 0);
diff --git a/src/display/nr-filter-image.cpp b/src/display/nr-filter-image.cpp
index 7e859314a..86ebb49cb 100644
--- a/src/display/nr-filter-image.cpp
+++ b/src/display/nr-filter-image.cpp
@@ -47,6 +47,7 @@ FilterImage::~FilterImage()
void FilterImage::render_cairo(FilterSlot &slot)
{
+ std::cout << "FilterImage::render_cairo: Entrance" << std::endl;
if (!feImageHref)
return;
@@ -78,8 +79,11 @@ void FilterImage::render_cairo(FilterSlot &slot)
if( feImageWidth == 0 ) feImageWidth = bbox_width;
if( feImageHeight == 0 ) feImageHeight = bbox_height;
+ int device_scale = slot.get_device_scale();
+
// Internal image, like <use>
if (from_element) {
+ std::cout << " Internal image" << std::endl;
if (!SVGElem) return;
// TODO: do not recreate the rendering tree every time
@@ -108,8 +112,12 @@ void FilterImage::render_cairo(FilterSlot &slot)
*/
Geom::Rect sa = slot.get_slot_area();
- cairo_surface_t *out = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
- sa.width(), sa.height());
+ cairo_surface_t *out =
+ cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ sa.width() * device_scale,
+ sa.height() * device_scale);
+ cairo_surface_set_device_scale(out, device_scale, device_scale);
+
Inkscape::DrawingContext dc(out, sa.min());
dc.transform(user2pb); // we are now in primitive units
dc.translate(feImageX, feImageY);
@@ -128,11 +136,14 @@ void FilterImage::render_cairo(FilterSlot &slot)
slot.set(_output, out);
cairo_surface_destroy(out);
+ std::cout << " feImage: out: " << cairo_image_surface_get_width( out) << std::endl;
+ std::cout << "FilterImage::render_cairo: Exit 2" << std::endl;
return;
}
// External image, like <image>
if (!image && !broken_ref) {
+ std::cout << " External image" << std::endl;
broken_ref = true;
/* TODO: If feImageHref is absolute, then use that (preferably handling the
@@ -170,10 +181,12 @@ void FilterImage::render_cairo(FilterSlot &slot)
}
cairo_surface_t *image_surface = image->getSurfaceRaw();
-
+ std::cout << " image: " << cairo_image_surface_get_width(image_surface) << std::endl;
Geom::Rect sa = slot.get_slot_area();
cairo_surface_t *out = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
- sa.width(), sa.height());
+ sa.width() * device_scale, sa.height() * device_scale);
+ cairo_surface_set_device_scale( out, device_scale, device_scale );
+ std::cout << " out: " << cairo_image_surface_get_width(out) << std::endl;
// For the moment, we'll assume that any image is in sRGB color space
// set_cairo_surface_ci(out, SP_CSS_COLOR_INTERPOLATION_SRGB);
@@ -278,8 +291,8 @@ void FilterImage::render_cairo(FilterSlot &slot)
cairo_set_source_surface(ct, image_surface, 0, 0);
cairo_paint(ct);
cairo_destroy(ct);
-
slot.set(_output, out);
+ std::cout << "FilterImage::render_cairo: Exit 2" << std::endl;
}
bool FilterImage::can_handle_affine(Geom::Affine const &)
diff --git a/src/display/nr-filter-morphology.cpp b/src/display/nr-filter-morphology.cpp
index b6e5052e1..a06033e80 100644
--- a/src/display/nr-filter-morphology.cpp
+++ b/src/display/nr-filter-morphology.cpp
@@ -171,9 +171,10 @@ void FilterMorphology::render_cairo(FilterSlot &slot)
return;
}
+ int device_scale = slot.get_device_scale();
Geom::Affine p2pb = slot.get_units().get_matrix_primitiveunits2pb();
- double xr = fabs(xradius * p2pb.expansionX());
- double yr = fabs(yradius * p2pb.expansionY());
+ double xr = fabs(xradius * p2pb.expansionX()) * device_scale;
+ double yr = fabs(yradius * p2pb.expansionY()) * device_scale;
int bpp = cairo_image_surface_get_format(input) == CAIRO_FORMAT_A8 ? 1 : 4;
cairo_surface_t *interm = ink_cairo_surface_create_identical(input);
diff --git a/src/display/nr-filter-slot.cpp b/src/display/nr-filter-slot.cpp
index a6e0c5c4e..9d76462c0 100644
--- a/src/display/nr-filter-slot.cpp
+++ b/src/display/nr-filter-slot.cpp
@@ -270,6 +270,14 @@ int FilterSlot::get_blurquality(void) {
return blurquality;
}
+void FilterSlot::set_device_scale(int const s) {
+ device_scale = s;
+}
+
+int FilterSlot::get_device_scale() {
+ return device_scale;
+}
+
Geom::Rect FilterSlot::get_slot_area() const {
Geom::Point p(_slot_x, _slot_y);
Geom::Point dim(_slot_w, _slot_h);
diff --git a/src/display/nr-filter-slot.h b/src/display/nr-filter-slot.h
index 166b2e718..d73e9d91c 100644
--- a/src/display/nr-filter-slot.h
+++ b/src/display/nr-filter-slot.h
@@ -72,6 +72,12 @@ public:
/** Gets the gaussian filtering quality. Affects used interpolation methods */
int get_blurquality(void);
+ /** Sets the device scale; for high DPI monitors. */
+ void set_device_scale(int const s);
+
+ /** Gets the device scale; for high DPI monitors. */
+ int get_device_scale();
+
FilterUnits const &get_units() const { return _units; }
Geom::Rect get_slot_area() const;
@@ -98,6 +104,7 @@ private:
int _last_out;
FilterQuality filterquality;
int blurquality;
+ int device_scale;
cairo_surface_t *_get_transformed_source_graphic();
cairo_surface_t *_get_transformed_background();
diff --git a/src/display/nr-filter-specularlighting.cpp b/src/display/nr-filter-specularlighting.cpp
index 2ce02adee..e8d03d42e 100644
--- a/src/display/nr-filter-specularlighting.cpp
+++ b/src/display/nr-filter-specularlighting.cpp
@@ -93,9 +93,9 @@ private:
struct SpecularPointLight : public SpecularLight {
SpecularPointLight(cairo_surface_t *bumpmap, SPFePointLight *light, guint32 color,
Geom::Affine const &trans, double scale, double specular_constant,
- double specular_exponent, double x0, double y0)
+ double specular_exponent, double x0, double y0, int device_scale)
: SpecularLight(bumpmap, scale, specular_constant, specular_exponent)
- , _light(light, color, trans)
+ , _light(light, color, trans, device_scale)
, _x0(x0)
, _y0(y0)
{
@@ -117,9 +117,9 @@ private:
struct SpecularSpotLight : public SpecularLight {
SpecularSpotLight(cairo_surface_t *bumpmap, SPFeSpotLight *light, guint32 color,
Geom::Affine const &trans, double scale, double specular_constant,
- double specular_exponent, double x0, double y0)
+ double specular_exponent, double x0, double y0, int device_scale)
: SpecularLight(bumpmap, scale, specular_constant, specular_exponent)
- , _light(light, color, trans)
+ , _light(light, color, trans, device_scale)
, _x0(x0)
, _y0(y0)
{}
@@ -173,11 +173,17 @@ void FilterSpecularLighting::render_cairo(FilterSlot &slot)
set_cairo_surface_ci(out, ci_fp );
guint32 color = SP_RGBA32_F_COMPOSE( r, g, b, 1.0 );
+ int device_scale = slot.get_device_scale();
+
+ // trans has inverse y... so we can't just scale by device_scale! We must instead explicitly
+ // scale the point and spot light coordinates (as well as "scale").
+
Geom::Affine trans = slot.get_units().get_matrix_primitiveunits2pb();
+
Geom::Point p = slot.get_slot_area().min();
double x0 = p[Geom::X];
double y0 = p[Geom::Y];
- double scale = surfaceScale * trans.descrim();
+ double scale = surfaceScale * trans.descrim() * device_scale;
double ks = specularConstant;
double se = specularExponent;
@@ -188,11 +194,11 @@ void FilterSpecularLighting::render_cairo(FilterSlot &slot)
break;
case POINT_LIGHT:
ink_cairo_surface_synthesize(out,
- SpecularPointLight(input, light.point, color, trans, scale, ks, se, x0, y0));
+ SpecularPointLight(input, light.point, color, trans, scale, ks, se, x0, y0, device_scale));
break;
case SPOT_LIGHT:
ink_cairo_surface_synthesize(out,
- SpecularSpotLight(input, light.spot, color, trans, scale, ks, se, x0, y0));
+ SpecularSpotLight(input, light.spot, color, trans, scale, ks, se, x0, y0, device_scale));
break;
default: {
cairo_t *ct = cairo_create(out);
diff --git a/src/display/nr-filter-turbulence.cpp b/src/display/nr-filter-turbulence.cpp
index 1397c0f34..349bcc242 100644
--- a/src/display/nr-filter-turbulence.cpp
+++ b/src/display/nr-filter-turbulence.cpp
@@ -378,6 +378,19 @@ void FilterTurbulence::render_cairo(FilterSlot &slot)
cairo_surface_t *input = slot.getcairo(_input);
cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA);
+ // It is probably possible to render at a device scale greater than one
+ // but for the moment rendering at a device scale of one is the easiest.
+ // cairo_image_surface_get_width() returns width in pixels but
+ // cairo_surface_create_similar() requires width in device units so divide by device scale.
+ // We are rendering at a device scale of 1... so divide by device scale again!
+ double x_scale = 0;
+ double y_scale = 0;
+ cairo_surface_get_device_scale(input, &x_scale, &y_scale);
+ int width = ceil(cairo_image_surface_get_width( input)/x_scale/x_scale);
+ int height = ceil(cairo_image_surface_get_height(input)/y_scale/y_scale);
+ cairo_surface_t *temp = cairo_surface_create_similar (input, CAIRO_CONTENT_COLOR_ALPHA, width, height);
+ cairo_surface_set_device_scale( temp, 1, 1 );
+
// color_interpolation_filter is determined by CSS value (see spec. Turbulence).
if( _style ) {
set_cairo_surface_ci(out, (SPColorInterpolation)_style->color_interpolation_filters.computed );
@@ -395,8 +408,16 @@ void FilterTurbulence::render_cairo(FilterSlot &slot)
Geom::Rect slot_area = slot.get_slot_area();
double x0 = slot_area.min()[Geom::X];
double y0 = slot_area.min()[Geom::Y];
+ ink_cairo_surface_synthesize(temp, Turbulence(*gen, unit_trans, x0, y0));
+
+ // cairo_surface_write_to_png( temp, "turbulence0.png" );
+
+ cairo_t *ct = cairo_create(out);
+ cairo_set_source_surface(ct, temp, 0, 0);
+ cairo_paint(ct);
+ cairo_destroy(ct);
- ink_cairo_surface_synthesize(out, Turbulence(*gen, unit_trans, x0, y0));
+ cairo_surface_destroy(temp);
cairo_surface_mark_dirty(out);
diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp
index d430553d4..a1bea4fce 100644
--- a/src/display/nr-filter.cpp
+++ b/src/display/nr-filter.cpp
@@ -42,6 +42,7 @@
#include "display/drawing.h"
#include "display/drawing-item.h"
#include "display/drawing-context.h"
+#include "display/drawing-surface.h"
#include <2geom/affine.h>
#include <2geom/rect.h>
#include "svg/svg-length.h"
@@ -95,6 +96,9 @@ Filter::~Filter()
int Filter::render(Inkscape::DrawingItem const *item, DrawingContext &graphic, DrawingContext *bgdc)
{
+ // std::cout << "Filter::render() for: " << const_cast<Inkscape::DrawingItem *>(item)->name() << std::endl;
+ // std::cout << " graphic drawing_scale: " << graphic.surface()->device_scale() << std::endl;
+
if (_primitive.empty()) {
// when no primitives are defined, clear source graphic
graphic.setSource(0,0,0,0);
@@ -150,6 +154,7 @@ int Filter::render(Inkscape::DrawingItem const *item, DrawingContext &graphic, D
FilterSlot slot(const_cast<Inkscape::DrawingItem*>(item), bgdc, graphic, units);
slot.set_quality(filterquality);
slot.set_blurquality(blurquality);
+ slot.set_device_scale(graphic.surface()->device_scale());
for (unsigned i = 0 ; i < _primitive.size() ; i++) {
_primitive[i]->render_cairo(slot);
diff --git a/src/display/nr-light.cpp b/src/display/nr-light.cpp
index 0e9a55a9f..791ae53da 100644
--- a/src/display/nr-light.cpp
+++ b/src/display/nr-light.cpp
@@ -41,11 +41,11 @@ void DistantLight::light_components(NR::Fvector &lc) {
lc[LIGHT_BLUE] = SP_RGBA32_B_U(color);
}
-PointLight::PointLight(SPFePointLight *light, guint32 lighting_color, const Geom::Affine &trans) {
+PointLight::PointLight(SPFePointLight *light, guint32 lighting_color, const Geom::Affine &trans, int device_scale) {
color = lighting_color;
- l_x = light->x;
- l_y = light->y;
- l_z = light->z;
+ l_x = light->x * device_scale;
+ l_y = light->y * device_scale;
+ l_z = light->z * device_scale;
NR::convert_coord(l_x, l_y, l_z, trans);
}
@@ -64,15 +64,15 @@ void PointLight::light_components(NR::Fvector &lc) {
lc[LIGHT_BLUE] = SP_RGBA32_B_U(color);
}
-SpotLight::SpotLight(SPFeSpotLight *light, guint32 lighting_color, const Geom::Affine &trans) {
+SpotLight::SpotLight(SPFeSpotLight *light, guint32 lighting_color, const Geom::Affine &trans, int device_scale) {
double p_x, p_y, p_z;
color = lighting_color;
- l_x = light->x;
- l_y = light->y;
- l_z = light->z;
- p_x = light->pointsAtX;
- p_y = light->pointsAtY;
- p_z = light->pointsAtZ;
+ l_x = light->x * device_scale;
+ l_y = light->y * device_scale;
+ l_z = light->z * device_scale;
+ p_x = light->pointsAtX * device_scale;
+ p_y = light->pointsAtY * device_scale;
+ p_z = light->pointsAtZ * device_scale;
cos_lca = std::cos(M_PI / 180 * light->limitingConeAngle);
speExp = light->specularExponent;
NR::convert_coord(l_x, l_y, l_z, trans);
diff --git a/src/display/nr-light.h b/src/display/nr-light.h
index 57c421f4a..2eacdc92b 100644
--- a/src/display/nr-light.h
+++ b/src/display/nr-light.h
@@ -69,8 +69,9 @@ class PointLight {
* \param trans the transformation between absolute coordinate (those
* employed in the sp light object) and current coordinate (those
* employed in the rendering)
+ * \param device_scale for high DPI monitors.
*/
- PointLight(SPFePointLight *light, guint32 lighting_color, const Geom::Affine &trans);
+ PointLight(SPFePointLight *light, guint32 lighting_color, const Geom::Affine &trans, int device_scale = 1);
virtual ~PointLight();
/**
* Computes the light vector of the distant light at point (x,y,z).
@@ -109,8 +110,9 @@ class SpotLight {
* \param trans the transformation between absolute coordinate (those
* employed in the sp light object) and current coordinate (those
* employed in the rendering)
+ * \param device_scale for high DPI monitors.
*/
- SpotLight(SPFeSpotLight *light, guint32 lighting_color, const Geom::Affine &trans);
+ SpotLight(SPFeSpotLight *light, guint32 lighting_color, const Geom::Affine &trans, int device_scale = 1);
virtual ~SpotLight();
/**
diff --git a/src/display/sodipodi-ctrl.cpp b/src/display/sodipodi-ctrl.cpp
index 27b6988c5..04ec947f6 100644
--- a/src/display/sodipodi-ctrl.cpp
+++ b/src/display/sodipodi-ctrl.cpp
@@ -7,6 +7,7 @@
*/
#include <2geom/transforms.h>
+#include <2geom/line.h>
#include "sp-canvas-util.h"
#include "sodipodi-ctrl.h"
#include "display/cairo-utils.h"
@@ -303,9 +304,9 @@ sp_ctrl_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
}
bool
-sp_point_inside_line(Geom::Point a, Geom::Point b, Geom::Point c, double tolerance = 0.1){
- //http://stackoverflow.com/questions/328107/how-can-you-determine-a-point-is-between-two-other-points-on-a-line-segment
- return Geom::are_near(Geom::distance(a,c) + Geom::distance(c,b) , Geom::distance(a,b), tolerance);
+sp_point_inside_line(Geom::Point a, Geom::Point b, Geom::Point c, double tolerance = 0.1) {
+ Geom::LineSegment segment(a, b);
+ return Geom::are_near(c, segment, tolerance);
}
bool
@@ -320,12 +321,9 @@ sp_point_inside_triangle(Geom::Point p1,Geom::Point p2,Geom::Point p3, Geom::Poi
}
static void
-sp_ctrl_build_cache (SPCtrl *ctrl)
+sp_ctrl_build_cache (SPCtrl *ctrl, int device_scale)
{
- guint32 *p, *q;
- gint size, x, y, z, s, a, width, height, c;
guint32 stroke_color, fill_color;
-
if (ctrl->filled) {
if (ctrl->mode == SP_CTRL_MODE_XOR) {
fill_color = ctrl->fill_color;
@@ -335,6 +333,7 @@ sp_ctrl_build_cache (SPCtrl *ctrl)
} else {
fill_color = 0;
}
+
if (ctrl->stroked) {
if (ctrl->mode == SP_CTRL_MODE_XOR) {
stroke_color = ctrl->stroke_color;
@@ -344,203 +343,226 @@ sp_ctrl_build_cache (SPCtrl *ctrl)
} else {
stroke_color = fill_color;
}
- gint32 stroke_color_smooth = SP_RGBA32_F_COMPOSE(SP_RGBA32_R_F(stroke_color), SP_RGBA32_G_F(stroke_color), SP_RGBA32_B_F(stroke_color), 0.15);
- width = (ctrl->width * 2 +1);
- height = (ctrl->height * 2 +1);
- c = ctrl->width; // Only used for pre-set square drawing
- size = width * height;
+
+ gint width = (ctrl->width * 2 + 1) * device_scale;
+ gint height = (ctrl->height * 2 + 1) * device_scale;
if (width < 2) return;
+ gint size = width * height;
if (ctrl->cache) delete[] ctrl->cache;
ctrl->cache = new guint32[size];
- Geom::Point point;
- Geom::Point p1;
- Geom::Point p2;
- Geom::Point p3;
- if(ctrl->shape == SP_CTRL_SHAPE_TRIANGLE){
- Geom::Affine m = Geom::Translate(Geom::Point(-width/2.0,-height/2.0));
- m *= Geom::Rotate(-ctrl->angle);
- m *= Geom::Translate(Geom::Point(width/2.0, height/2.0));
- p1 = Geom::Point(0,height/2);
- p2 = Geom::Point(width - (width/M_PI), height/M_PI);
- p3 = Geom::Point(width - (width/M_PI), height-(height/M_PI));
- p1 *= m;
- p2 *= m;
- p3 *= m;
- p1 = p1.floor();
- p2 = p2.floor();
- p3 = p3.floor();
- }
+
switch (ctrl->shape) {
case SP_CTRL_SHAPE_SQUARE:
- p = ctrl->cache;
- // top edge
- for (x=0; x < width; x++) {
- *p++ = stroke_color;
- }
- // middle
- for (y = 2; y < height; y++) {
- *p++ = stroke_color; // stroke at first and last pixel
- for (x=2; x < width; x++) {
- *p++ = fill_color; // fill in the middle
+ {
+ guint32* p = ctrl->cache;
+
+ for (int i = 0; i < width; ++i) {
+ for (int j = 0; j < height; ++j) {
+ if ( i > device_scale - 1 &&
+ j > device_scale - 1 &&
+ width - i > device_scale &&
+ height -j > device_scale) {
+ *p++ = fill_color;
+ } else {
+ *p++ = stroke_color;
+ }
}
- *p++ = stroke_color;
- }
- // bottom edge
- for (x=0; x < width; x++) {
- *p++ = stroke_color;
}
+
ctrl->build = TRUE;
break;
+ }
case SP_CTRL_SHAPE_DIAMOND:
- p = ctrl->cache;
- for (y = 0; y < height; y++) {
- z = abs (c - y);
- for (x = 0; x < z; x++) {
- *p++ = 0;
- }
- *p++ = stroke_color; x++;
- for (; x < width - z -1; x++) {
- *p++ = fill_color;
- }
- if (z != c) {
- *p++ = stroke_color; x++;
- }
- for (; x < width; x++) {
- *p++ = 0;
+ {
+ // width == height
+ guint32* p = ctrl->cache;
+ int m = (width+1)/2;
+
+ for (int i = 0; i < width; ++i) {
+ for (int j = 0; j < height; ++j) {
+ if ( i + j > m-1+device_scale &&
+ (width-1-i) + j > m-1+device_scale &&
+ (width-1-i) + (height-1-j) > m-1+device_scale &&
+ i + (height-1-j) > m-1+device_scale ) {
+ *p++ = fill_color;
+ } else
+ if ( i + j > m-2 &&
+ (width-1-i) + j > m-2 &&
+ (width-1-i) + (height-1-j) > m-2 &&
+ i + (height-1-j) > m-2 ) {
+ *p++ = stroke_color;
+ } else {
+ *p++ = 0;
+ }
}
}
+
ctrl->build = TRUE;
break;
+ }
case SP_CTRL_SHAPE_CIRCLE:
- p = ctrl->cache;
- q = p + size -1;
- s = -1;
- for (y = 0; y <= c ; y++) {
- a = abs (c - y);
- z = (gint)(0.0 + sqrt ((c+.4)*(c+.4) - a*a));
- x = 0;
- while (x < c-z) {
- *p++ = 0;
- *q-- = 0;
- x++;
- }
- do {
- *p++ = stroke_color;
- *q-- = stroke_color;
- x++;
- } while (x < c-s);
- while (x < MIN(c+s+1, c+z)) {
- *p++ = fill_color;
- *q-- = fill_color;
- x++;
- }
- do {
- *p++ = stroke_color;
- *q-- = stroke_color;
- x++;
- } while (x <= c+z);
- while (x < width) {
- *p++ = 0;
- *q-- = 0;
- x++;
+ {
+ guint32* p = ctrl->cache;
+
+ double rs = width/2.0;
+ double rs2 = rs*rs;
+ double rf = rs-device_scale;
+ double rf2 = rf*rf;
+
+ for (int i = 0; i < width; ++i) {
+ for (int j = 0; j < height; ++j) {
+
+ double rx = i - (width /2.0) + 0.5;
+ double ry = j - (height/2.0) + 0.5;
+ double r2 = rx*rx + ry*ry;
+
+ if (r2 < rf2) {
+ *p++ = fill_color;
+ } else if (r2 < rs2) {
+ *p++ = stroke_color;
+ } else {
+ *p++ = 0;
+ }
}
- s = z;
}
+
ctrl->build = TRUE;
break;
+ }
case SP_CTRL_SHAPE_TRIANGLE:
- p = ctrl->cache;
- for(y = 0; y < height; y++) {
- for(x = 0; x < width; x++) {
- point = Geom::Point(x,y);
- if (sp_point_inside_triangle(p1, p2, p3, point)) {
+ {
+ guint* p = ctrl->cache;
+
+ Geom::Affine m = Geom::Translate(Geom::Point(-width/2.0,-height/2.0));
+ m *= Geom::Rotate(-ctrl->angle);
+ m *= Geom::Translate(Geom::Point(width/2.0, height/2.0));
+
+ // Construct an arrowhead (triangle) of maximum size that won't leak out of rectangle
+ // defined by width and height, assuming width == height.
+ double w2 = width/2.0;
+ double h2 = height/2.0;
+ double w2cos = w2 * cos( M_PI/6 );
+ double h2sin = h2 * sin( M_PI/6 );
+ Geom::Point p1s(0, h2);
+ Geom::Point p2s(w2 + w2cos, h2 + h2sin);
+ Geom::Point p3s(w2 + w2cos, h2 - h2sin);
+ // Needed for constructing smaller arrowhead below.
+ double theta = atan2( Geom::Point( p2s - p1s ) );
+ p1s *= m;
+ p2s *= m;
+ p3s *= m;
+
+ // Construct a smaller arrow head for fill.
+ Geom::Point p1f(device_scale/sin(theta), h2);
+ Geom::Point p2f(w2 + w2cos, h2 - h2sin + device_scale/cos(theta));
+ Geom::Point p3f(w2 + w2cos, h2 + h2sin - device_scale/cos(theta));
+ p1f *= m;
+ p2f *= m;
+ p3f *= m;
+
+ for(int y = 0; y < height; y++) {
+ for(int x = 0; x < width; x++) {
+ Geom::Point point = Geom::Point(x+0.5,y+0.5);
+ if (sp_point_inside_triangle(p1f, p2f, p3f, point)) {
p[(y*width)+x] = fill_color;
- } else if (point == p1 || point == p2 || point == p3 || sp_point_inside_line(p1, p2, point, 0.2) ||
- sp_point_inside_line(p3, p1, point, 0.2))
- {
+ } else
+ if (sp_point_inside_triangle(p1s, p2s, p3s, point)) {
p[(y*width)+x] = stroke_color;
- } else if (sp_point_inside_line(p1, p2, point, 0.5) ||
- sp_point_inside_line(p3, p1, point, 0.5))
- {
- p[(y*width)+x] = stroke_color_smooth;
} else {
p[(y*width)+x] = 0;
}
}
}
+
ctrl->build = TRUE;
break;
+ }
case SP_CTRL_SHAPE_CROSS:
- p = ctrl->cache;
- for (y = 0; y < height; y++) {
- z = abs (c - y);
- for (x = 0; x < c-z; x++) {
- *p++ = 0;
- }
- *p++ = stroke_color; x++;
- for (; x < c + z; x++) {
- *p++ = 0;
- }
- if (z != 0) {
- *p++ = stroke_color; x++;
- }
- for (; x < width; x++) {
- *p++ = 0;
+ {
+ guint* p = ctrl->cache;
+ for(int y = 0; y < height; y++) {
+ for(int x = 0; x < width; x++) {
+ if ( abs(x - y) < device_scale ||
+ abs(width - 1 - x - y) < device_scale ) {
+ *p++ = stroke_color;
+ } else {
+ *p++ = 0;
+ }
}
}
+
ctrl->build = TRUE;
break;
+ }
case SP_CTRL_SHAPE_BITMAP:
+ {
if (ctrl->pixbuf) {
- unsigned char *px;
- unsigned int rs;
- px = gdk_pixbuf_get_pixels (ctrl->pixbuf);
- rs = gdk_pixbuf_get_rowstride (ctrl->pixbuf);
- for (y = 0; y < height; y++){
- guint32 *d;
- unsigned char *s;
- s = px + y * rs;
- d = ctrl->cache + height * y;
- for (x = 0; x < width; x++) {
+ unsigned char* px = gdk_pixbuf_get_pixels (ctrl->pixbuf);
+ unsigned int rs = gdk_pixbuf_get_rowstride (ctrl->pixbuf);
+ for (int y = 0; y < height/device_scale; y++){
+ for (int x = 0; x < width/device_scale; x++) {
+ unsigned char *s = px + rs*y + 4*x;
+ guint32 color;
if (s[3] < 0x80) {
- *d++ = 0;
+ color = 0;
} else if (s[0] < 0x80) {
- *d++ = stroke_color;
+ color = stroke_color;
} else {
- *d++ = fill_color;
+ color = fill_color;
+ }
+
+ // Fill in device_scale x device_scale block
+ for (int i = 0; i < device_scale; ++i) {
+ for (int j = 0; j < device_scale; ++j) {
+ guint* p = ctrl->cache +
+ (x * device_scale + i) + // Column
+ (y * device_scale + j) * width; // Row
+ *p = color;
+ }
}
- s += 4;
}
}
} else {
- g_print ("control has no pixmap\n");
+ g_print ("control has no pixbuf\n");
}
+
ctrl->build = TRUE;
break;
+ }
case SP_CTRL_SHAPE_IMAGE:
+ {
if (ctrl->pixbuf) {
- guint r = gdk_pixbuf_get_rowstride (ctrl->pixbuf);
- guint32 *px;
+ guint rs = gdk_pixbuf_get_rowstride (ctrl->pixbuf);
guchar *data = gdk_pixbuf_get_pixels (ctrl->pixbuf);
- p = ctrl->cache;
- for (y = 0; y < height; y++){
- px = reinterpret_cast<guint32*>(data + y * r);
- for (x = 0; x < width; x++) {
- *p++ = *px++;
+
+ for (int y = 0; y < height/device_scale; y++){
+ for (int x = 0; x < width/device_scale; x++) {
+ guint32 *px = reinterpret_cast<guint32*>(data + rs*y + 4*x);
+
+ // Fill in device_scale x device_scale block
+ for (int i = 0; i < device_scale; ++i) {
+ for (int j = 0; j < device_scale; ++j) {
+ guint* p = ctrl->cache +
+ (x * device_scale + i) + // Column
+ (y * device_scale + j) * width; // Row
+ *p = *px;
+ }
+ }
}
}
} else {
- g_print ("control has no pixmap\n");
+ g_print ("control has no pixbuf\n");
}
ctrl->build = TRUE;
break;
+ }
default:
break;
@@ -556,9 +578,6 @@ static inline guint32 compose_xor(guint32 bg, guint32 fg, guint32 a)
static void
sp_ctrl_render (SPCanvasItem *item, SPCanvasBuf *buf)
{
- //gint y0, y1, y, x0,x1,x;
- //guchar *p, *q, a;
-
SPCtrl *ctrl = SP_CTRL (item);
if (!ctrl->defined) return;
@@ -566,26 +585,34 @@ sp_ctrl_render (SPCanvasItem *item, SPCanvasBuf *buf)
// the control-image is rendered into ctrl->cache
if (!ctrl->build) {
- sp_ctrl_build_cache (ctrl);
+ sp_ctrl_build_cache (ctrl, buf->device_scale);
}
- int w = (ctrl->width * 2 + 1);
- int h = (ctrl->height * 2 + 1);
+ // Must match width/height sp_ctrl_build_cache.
+ int w = (ctrl->width * 2 + 1) * buf->device_scale;
+ int h = (ctrl->height * 2 + 1) * buf->device_scale;
// The code below works even when the target is not an image surface
if (ctrl->mode == SP_CTRL_MODE_XOR) {
+
// 1. Copy the affected part of output to a temporary surface
+
+ // Size in device pixels. Does not set device scale.
cairo_surface_t *work = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
+ cairo_surface_set_device_scale(work, buf->device_scale, buf->device_scale);
+
cairo_t *cr = cairo_create(work);
cairo_translate(cr, -ctrl->box.left(), -ctrl->box.top());
cairo_set_source_surface(cr, cairo_get_target(buf->ct), buf->rect.left(), buf->rect.top());
cairo_paint(cr);
cairo_destroy(cr);
+ // cairo_surface_write_to_png( work, "ctrl0.png" );
// 2. Composite the control on a temporary surface
cairo_surface_flush(work);
int strideb = cairo_image_surface_get_stride(work);
unsigned char *pxb = cairo_image_surface_get_data(work);
+
guint32 *p = ctrl->cache;
for (int i=0; i<h; ++i) {
guint32 *pb = reinterpret_cast<guint32*>(pxb + i*strideb);
@@ -605,12 +632,13 @@ sp_ctrl_render (SPCanvasItem *item, SPCanvasBuf *buf)
}
}
cairo_surface_mark_dirty(work);
+ // cairo_surface_write_to_png( work, "ctrl1.png" );
// 3. Replace the affected part of output with contents of temporary surface
cairo_save(buf->ct);
cairo_set_source_surface(buf->ct, work,
ctrl->box.left() - buf->rect.left(), ctrl->box.top() - buf->rect.top());
- cairo_rectangle(buf->ct, ctrl->box.left() - buf->rect.left(), ctrl->box.top() - buf->rect.top(), w, h);
+ cairo_rectangle(buf->ct, ctrl->box.left() - buf->rect.left(), ctrl->box.top() - buf->rect.top(), w/buf->device_scale, h/buf->device_scale);
cairo_clip(buf->ct);
cairo_set_operator(buf->ct, CAIRO_OPERATOR_SOURCE);
cairo_paint(buf->ct);
@@ -619,6 +647,7 @@ sp_ctrl_render (SPCanvasItem *item, SPCanvasBuf *buf)
} else {
cairo_surface_t *cache = cairo_image_surface_create_for_data(
reinterpret_cast<unsigned char*>(ctrl->cache), CAIRO_FORMAT_ARGB32, w, h, w*4);
+ cairo_surface_set_device_scale(cache, buf->device_scale, buf->device_scale);
cairo_set_source_surface(buf->ct, cache,
ctrl->box.left() - buf->rect.left(), ctrl->box.top() - buf->rect.top());
cairo_paint(buf->ct);
diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp
index 7acd645c8..20ad27bf5 100644
--- a/src/display/sp-canvas.cpp
+++ b/src/display/sp-canvas.cpp
@@ -1123,25 +1123,42 @@ void SPCanvas::handle_get_preferred_height(GtkWidget *widget, gint *minimum_heig
void SPCanvas::handle_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
{
SPCanvas *canvas = SP_CANVAS (widget);
- GtkAllocation old_allocation;
+ // Allocation does not depend on device scale.
+ GtkAllocation old_allocation;
gtk_widget_get_allocation(widget, &old_allocation);
-// Geom::IntRect old_area = Geom::IntRect::from_xywh(canvas->_x0, canvas->_y0,
-// old_allocation.width, old_allocation.height);
+ // For HiDPI monitors.
+ canvas->_device_scale = gtk_widget_get_scale_factor( widget );
Geom::IntRect new_area = Geom::IntRect::from_xywh(canvas->_x0, canvas->_y0,
allocation->width, allocation->height);
- // resize backing store
+ // Resize backing store.
cairo_surface_t *new_backing_store = NULL;
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
- if (canvas->_surface_for_similar != NULL)
- new_backing_store = cairo_surface_create_similar_image(canvas->_surface_for_similar,
- CAIRO_FORMAT_ARGB32, allocation->width, allocation->height);
+ if (canvas->_surface_for_similar != NULL) {
+
+ // Size in device pixels. Does not set device scale.
+ new_backing_store =
+ cairo_surface_create_similar_image(canvas->_surface_for_similar,
+ CAIRO_FORMAT_ARGB32,
+ allocation->width * canvas->_device_scale,
+ allocation->height * canvas->_device_scale);
+ }
#endif
- if (new_backing_store == NULL)
- new_backing_store = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation->width, allocation->height);
+ if (new_backing_store == NULL) {
+
+ // Size in device pixels. Does not set device scale.
+ new_backing_store =
+ cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ allocation->width * canvas->_device_scale,
+ allocation->height * canvas->_device_scale);
+ }
+
+ // Set device scale
+ cairo_surface_set_device_scale(new_backing_store, canvas->_device_scale, canvas->_device_scale);
+
if (canvas->_backing_store) {
cairo_t *cr = cairo_create(new_backing_store);
cairo_translate(cr, -canvas->_x0, -canvas->_y0);
@@ -1521,7 +1538,6 @@ void SPCanvas::paintSingleBuffer(Geom::IntRect const &paint_rect, Geom::IntRect
// Prevent crash if paintSingleBuffer is called before _backing_store is
// initialized.
-
if (_backing_store == NULL)
return;
@@ -1530,9 +1546,11 @@ void SPCanvas::paintSingleBuffer(Geom::IntRect const &paint_rect, Geom::IntRect
buf.buf_rowstride = 0;
buf.rect = paint_rect;
buf.canvas_rect = canvas_rect;
+ buf.device_scale = _device_scale;
buf.is_empty = true;
// Make sure the following code does not go outside of _backing_store's data
+ // FIXME for device_scale.
assert(cairo_image_surface_get_format(_backing_store) == CAIRO_FORMAT_ARGB32);
assert(paint_rect.left() - _x0 >= 0);
assert(paint_rect.top() - _y0 >= 0);
@@ -1541,14 +1559,28 @@ void SPCanvas::paintSingleBuffer(Geom::IntRect const &paint_rect, Geom::IntRect
// Create a temporary surface that draws directly to _backing_store
cairo_surface_flush(_backing_store);
+ // cairo_surface_write_to_png( _backing_store, "debug0.png" );
unsigned char *data = cairo_image_surface_get_data(_backing_store);
int stride = cairo_image_surface_get_stride(_backing_store);
+
+ // Check we are using correct device scale
+ double x_scale = 0;
+ double y_scale = 0;
+ cairo_surface_get_device_scale(_backing_store, &x_scale, &y_scale);
+ assert (_device_scale == (int)x_scale);
+ assert (_device_scale == (int)y_scale);
+
// Move to the right row
- data += stride * (paint_rect.top() - _y0);
+ data += stride * (paint_rect.top() - _y0) * (int)y_scale;
// Move to the right pixel inside of that row
- data += 4 * (paint_rect.left() - _x0);
- cairo_surface_t *imgs = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32,
- paint_rect.width(), paint_rect.height(), stride);
+ data += 4 * (paint_rect.left() - _x0) * (int)x_scale;
+ cairo_surface_t *imgs =
+ cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32,
+ paint_rect.width() * _device_scale,
+ paint_rect.height() * _device_scale,
+ stride);
+ cairo_surface_set_device_scale(imgs, _device_scale, _device_scale);
+
buf.ct = cairo_create(imgs);
cairo_save(buf.ct);
@@ -1557,10 +1589,12 @@ void SPCanvas::paintSingleBuffer(Geom::IntRect const &paint_rect, Geom::IntRect
cairo_set_operator(buf.ct, CAIRO_OPERATOR_SOURCE);
cairo_paint(buf.ct);
cairo_restore(buf.ct);
+ // cairo_surface_write_to_png( imgs, "debug1.png" );
if (_root->visible) {
SP_CANVAS_ITEM_GET_CLASS(_root)->render(_root, &buf);
}
+ // cairo_surface_write_to_png( imgs, "debug2.png" );
// output to X
cairo_destroy(buf.ct);
@@ -1590,6 +1624,7 @@ void SPCanvas::paintSingleBuffer(Geom::IntRect const &paint_rect, Geom::IntRect
#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
cairo_surface_mark_dirty(_backing_store);
+ // cairo_surface_write_to_png( _backing_store, "debug3.png" );
// Mark the painted rectangle clean
markRect(paint_rect, 0);
@@ -1730,7 +1765,6 @@ bool SPCanvas::paintRect(int xx0, int yy0, int xx1, int yy1)
Geom::OptIntRect area = paint_rect & canvas_rect;
if (!area || area->hasZeroArea()) return 0;
-
paint_rect = *area;
PaintRectSetup setup;
@@ -1787,19 +1821,34 @@ void SPCanvas::endForcedFullRedraws()
}
gboolean SPCanvas::handle_draw(GtkWidget *widget, cairo_t *cr) {
+
SPCanvas *canvas = SP_CANVAS(widget);
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
if (canvas->_surface_for_similar == NULL && canvas->_backing_store != NULL) {
- canvas->_surface_for_similar = cairo_surface_create_similar(
- cairo_get_target(cr), CAIRO_CONTENT_COLOR_ALPHA, 1, 1);
+
+ // Device scale is copied but since this is only created one time, we'll
+ // need to check/set device scale anytime it is used in case window moved
+ // to monitor with different scale.
+ canvas->_surface_for_similar =
+ cairo_surface_create_similar(cairo_get_target(cr), CAIRO_CONTENT_COLOR_ALPHA, 1, 1);
+
+ // Check we are using correct device scale
+ double x_scale = 0;
+ double y_scale = 0;
+ cairo_surface_get_device_scale(canvas->_backing_store, &x_scale, &y_scale);
+ assert (canvas->_device_scale == (int)x_scale);
+ assert (canvas->_device_scale == (int)y_scale);
// Reallocate backing store so that cairo can use shared memory
+ // Function does NOT copy device scale! Width and height are in device pixels.
cairo_surface_t *new_backing_store = cairo_surface_create_similar_image(
canvas->_surface_for_similar, CAIRO_FORMAT_ARGB32,
cairo_image_surface_get_width(canvas->_backing_store),
cairo_image_surface_get_height(canvas->_backing_store));
+ cairo_surface_set_device_scale(new_backing_store, canvas->_device_scale, canvas->_device_scale);
+
// Copy the old backing store contents
cairo_t *cr = cairo_create(new_backing_store);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
@@ -1824,6 +1873,7 @@ gboolean SPCanvas::handle_draw(GtkWidget *widget, cairo_t *cr) {
cairo_rectangle_t rectangle = rects->rectangles[i];
Geom::Rect dr = Geom::Rect::from_xywh(rectangle.x + canvas->_x0, rectangle.y + canvas->_y0,
rectangle.width, rectangle.height);
+
Geom::IntRect ir = dr.roundOutwards();
cairo_rectangle_int_t irect = { ir.left(), ir.top(), ir.width(), ir.height() };
cairo_region_union_rectangle(dirty_region, &irect);
@@ -1837,7 +1887,7 @@ gboolean SPCanvas::handle_draw(GtkWidget *widget, cairo_t *cr) {
}
cairo_region_destroy(dirty_region);
- return TRUE;
+ return TRUE;
}
gint SPCanvas::handle_key_event(GtkWidget *widget, GdkEventKey *event)
@@ -1979,7 +2029,11 @@ SPCanvasGroup *SPCanvas::getRoot()
*/
void SPCanvas::scrollTo( Geom::Point const &c, unsigned int clear, bool is_scrolling)
{
- GtkAllocation allocation;
+ // To do: extract out common code with SPCanvas::handle_size_allocate()
+
+ // For HiDPI monitors
+ int device_scale = gtk_widget_get_scale_factor(GTK_WIDGET(this));
+ assert( device_scale == _device_scale);
double cx = c[Geom::X];
double cy = c[Geom::Y];
@@ -1992,18 +2046,34 @@ void SPCanvas::scrollTo( Geom::Point const &c, unsigned int clear, bool is_scrol
Geom::IntRect old_area = getViewboxIntegers();
Geom::IntRect new_area = old_area + Geom::IntPoint(dx, dy);
+ GtkAllocation allocation;
gtk_widget_get_allocation(&_widget, &allocation);
- // adjust backing store contents
+ // Adjust backing store contents
assert(_backing_store);
+
cairo_surface_t *new_backing_store = NULL;
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
if (_surface_for_similar != NULL)
- new_backing_store = cairo_surface_create_similar_image(
- _surface_for_similar, CAIRO_FORMAT_ARGB32, allocation.width, allocation.height);
+
+ // Size in device pixels. Does not set device scale.
+ new_backing_store =
+ cairo_surface_create_similar_image(_surface_for_similar,
+ CAIRO_FORMAT_ARGB32,
+ allocation.width * _device_scale,
+ allocation.height * _device_scale);
#endif
if (new_backing_store == NULL)
- new_backing_store = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height);
+
+ // Size in device pixels. Does not set device scale.
+ new_backing_store =
+ cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ allocation.width * _device_scale,
+ allocation.height * _device_scale);
+
+ // Set device scale
+ cairo_surface_set_device_scale(new_backing_store, _device_scale, _device_scale);
+
cairo_t *cr = cairo_create(new_backing_store);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
// Paint the background
@@ -2011,6 +2081,8 @@ void SPCanvas::scrollTo( Geom::Point const &c, unsigned int clear, bool is_scrol
cairo_set_source(cr, _background);
cairo_paint(cr);
+ // cairo_surface_write_to_png( _backing_store, "scroll0.png" );
+
// Copy the old backing store contents
cairo_set_source_surface(cr, _backing_store, _x0, _y0);
cairo_rectangle(cr, _x0, _y0, allocation.width, allocation.height);
@@ -2020,6 +2092,8 @@ void SPCanvas::scrollTo( Geom::Point const &c, unsigned int clear, bool is_scrol
cairo_surface_destroy(_backing_store);
_backing_store = new_backing_store;
+ // cairo_surface_write_to_png( _backing_store, "scroll1.png" );
+
_dx0 = cx; // here the 'd' stands for double, not delta!
_dy0 = cy;
_x0 = ix;
diff --git a/src/display/sp-canvas.h b/src/display/sp-canvas.h
index 92a50da11..d30e0a485 100644
--- a/src/display/sp-canvas.h
+++ b/src/display/sp-canvas.h
@@ -55,6 +55,7 @@ struct SPCanvasBuf {
unsigned char *buf;
int buf_rowstride;
+ int device_scale; // For high DPI monitors.
bool is_empty;
};
@@ -177,6 +178,8 @@ public:
int _x0; ///< World coordinate of the leftmost pixels of window
int _y0; ///< World coordinate of the topmost pixels of window
+ int _device_scale; ///< Scale for high DPI montiors
+
/// Image surface storing the contents of the widget
cairo_surface_t *_backing_store;
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
diff --git a/src/ui/tools/tool-base.cpp b/src/ui/tools/tool-base.cpp
index 7b4f67da9..79e1cbc10 100644
--- a/src/ui/tools/tool-base.cpp
+++ b/src/ui/tools/tool-base.cpp
@@ -1289,8 +1289,9 @@ void sp_event_context_snap_delay_handler(ToolBase *ec,
// The snap delay will repeat the last motion event, which will lead to
// erroneous points in the calligraphy context. And because we don't snap
// in this context, we might just as well disable the snap delay all together
+ bool const c4 = ec->space_panning; // Don't snap while panning with the spacebar
- if (c1 || c2 || c3) {
+ if (c1 || c2 || c3 || c4) {
// Make sure that we don't send any pending snap events to a context if we know in advance
// that we're not going to snap any way (e.g. while scrolling with middle mouse button)
// Any motion event might affect the state of the context, leading to unexpected behavior
diff --git a/src/ui/widget/notebook-page.cpp b/src/ui/widget/notebook-page.cpp
index 6d8ff1d75..6bb84014c 100644
--- a/src/ui/widget/notebook-page.cpp
+++ b/src/ui/widget/notebook-page.cpp
@@ -20,6 +20,7 @@ namespace Widget {
NotebookPage::NotebookPage(int n_rows, int n_columns, bool expand, bool fill, guint padding)
:_table(Gtk::manage(new Gtk::Grid()))
{
+ set_name("NotebookPage");
set_border_width(2);
_table->set_row_spacing(2);
diff --git a/src/ui/widget/object-composite-settings.cpp b/src/ui/widget/object-composite-settings.cpp
index 5f5b801d1..fa3f6e905 100644
--- a/src/ui/widget/object-composite-settings.cpp
+++ b/src/ui/widget/object-composite-settings.cpp
@@ -41,6 +41,7 @@ ObjectCompositeSettings::ObjectCompositeSettings(unsigned int verb_code, char co
_fe_vbox(false, 0),
_blocked(false)
{
+ set_name( "CompositeSettings");
// Filter Effects
pack_start(_fe_vbox, false, false, 2);
diff --git a/src/ui/widget/spin-scale.cpp b/src/ui/widget/spin-scale.cpp
index f74626d9b..c658b4756 100644
--- a/src/ui/widget/spin-scale.cpp
+++ b/src/ui/widget/spin-scale.cpp
@@ -21,6 +21,7 @@ SpinScale::SpinScale(const char* label, double value, double lower, double upper
double /*climb_rate*/, int digits, const SPAttributeEnum a, const char* tip_text)
: AttrWidget(a, value)
{
+ set_name("SpinScale");
_adjustment = Gtk::Adjustment::create(value, lower, upper, step_inc);
_spinscale = gimp_spin_scale_new (_adjustment->gobj(), label, digits);
@@ -122,6 +123,7 @@ DualSpinScale::DualSpinScale(const char* label1, const char* label2, double valu
//TRANSLATORS: "Link" means to _link_ two sliders together
_link(C_("Sliders", "Link"))
{
+ set_name("DualSpinScale");
signal_value_changed().connect(signal_attr_changed().make_slot());
_s1.get_adjustment()->signal_value_changed().connect(_signal_value_changed.make_slot());
diff --git a/src/widgets/paint-selector.cpp b/src/widgets/paint-selector.cpp
index 840a64f95..557dc8da2 100644
--- a/src/widgets/paint-selector.cpp
+++ b/src/widgets/paint-selector.cpp
@@ -199,6 +199,7 @@ sp_paint_selector_init(SPPaintSelector *psel)
/* Paint style button box */
psel->style = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_set_homogeneous(GTK_BOX(psel->style), FALSE);
+ gtk_widget_set_name(psel->style,"PaintSelector");
gtk_widget_show(psel->style);
gtk_container_set_border_width(GTK_CONTAINER(psel->style), 4);
gtk_box_pack_start(GTK_BOX(psel), psel->style, FALSE, FALSE, 0);