summaryrefslogtreecommitdiffstats
path: root/src/display/nr-filter-image.cpp
blob: efc25b6aa5549fb2a69f97ba59d3fdd72356ffa6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * feImage filter primitive renderer
 *
 * Authors:
 *   Felipe Corrêa da Silva Sanches <juca@members.fsf.org>
 *   Tavmjong Bah <tavmjong@free.fr>
 *   Abhishek Sharma
 *
 * Copyright (C) 2007-2011 authors
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */
#include "display/nr-filter-image.h"
#include "document.h"
#include "object/sp-item.h"
#include "display/cairo-utils.h"
#include "display/drawing-context.h"
#include "display/drawing.h"
#include "display/drawing-item.h"
#include "display/nr-filter.h"
#include "display/nr-filter-slot.h"
#include "display/nr-filter-units.h"
#include "enums.h"
#include <glibmm/fileutils.h>

namespace Inkscape {
namespace Filters {

void FilterImage::update()
{
    if (!item) {
        return;
    }

    item->update();
}

void FilterImage::render_cairo(FilterSlot &slot) const
{
    if (!item) {
        return;
    }

    Geom::OptRect area = item->drawbox();
    if (!area) {
        return;
    }

    // Viewport is filter primitive area (in user coordinates).
    // Note: viewport calculation in non-trivial. Do not rely
    // on get_matrix_primitiveunits2pb().
    Geom::Rect vp = filter_primitive_area(slot.get_units());
    slot.set_primitive_area(_output, vp); // Needed for tiling

    double feImageX      = vp.left();
    double feImageY      = vp.top();
    double feImageWidth  = vp.width();
    double feImageHeight = vp.height();

    // feImage is suppose to use the same parameters as a normal SVG image.
    // If a width or height is set to zero, the image is not suppose to be displayed.
    // This does not seem to be what Firefox or Opera does, nor does the W3C displacement
    // filter test expect this behavior. If the width and/or height are zero, we use
    // the width and height of the object bounding box.
    Geom::Affine m = slot.get_units().get_matrix_user2filterunits().inverse();
    Geom::Point bbox_00 = Geom::Point(0,0) * m;
    Geom::Point bbox_w0 = Geom::Point(1,0) * m;
    Geom::Point bbox_0h = Geom::Point(0,1) * m;
    double bbox_width = Geom::distance(bbox_00, bbox_w0);
    double bbox_height = Geom::distance(bbox_00, bbox_0h);

    if (feImageWidth  == 0) feImageWidth  = bbox_width;
    if (feImageHeight == 0) feImageHeight = bbox_height;

    int device_scale = slot.get_device_scale();

    Geom::Rect sa = slot.get_slot_area();
    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());
    Geom::Affine user2pb = slot.get_units().get_matrix_user2pb();
    dc.transform(user2pb); // we are now in primitive units

    Geom::IntRect render_rect = area->roundOutwards();

    // Internal image, like <use>
    if (from_element) {
        dc.translate(feImageX, feImageY);
        item->render(dc, slot.get_rendercontext(), render_rect);

        // For the moment, we'll assume that any image is in sRGB color space
        set_cairo_surface_ci(out, SP_CSS_COLOR_INTERPOLATION_SRGB);
    } else {
        // For the moment, we'll assume that any image is in sRGB color space
        // set_cairo_surface_ci(out, SP_CSS_COLOR_INTERPOLATION_SRGB);
        // This seemed like a sensible thing to do but it breaks filters-displace-01-f.svg

        // Now that we have the viewport, we must map image inside.
        // Partially copied from sp-image.cpp.

        auto image_width  = area->width();
        auto image_height = area->height();

        // Do nothing if preserveAspectRatio is "none".
        if (aspect_align != SP_ASPECT_NONE) {

            // Check aspect ratio of image vs. viewport
            double feAspect = feImageHeight / feImageWidth;
            double aspect = (double)image_height / image_width;
            bool ratio = feAspect < aspect;

            double ax, ay; // Align side
            switch (aspect_align) {
                case SP_ASPECT_XMIN_YMIN:
                    ax = 0.0;
                    ay = 0.0;
                    break;
                case SP_ASPECT_XMID_YMIN:
                    ax = 0.5;
                    ay = 0.0;
                    break;
                case SP_ASPECT_XMAX_YMIN:
                    ax = 1.0;
                    ay = 0.0;
                    break;
                case SP_ASPECT_XMIN_YMID:
                    ax = 0.0;
                    ay = 0.5;
                    break;
                case SP_ASPECT_XMID_YMID:
                    ax = 0.5;
                    ay = 0.5;
                    break;
                case SP_ASPECT_XMAX_YMID:
                    ax = 1.0;
                    ay = 0.5;
                    break;
                case SP_ASPECT_XMIN_YMAX:
                    ax = 0.0;
                    ay = 1.0;
                    break;
                case SP_ASPECT_XMID_YMAX:
                    ax = 0.5;
                    ay = 1.0;
                    break;
                case SP_ASPECT_XMAX_YMAX:
                    ax = 1.0;
                    ay = 1.0;
                    break;
                default:
                    ax = 0.0;
                    ay = 0.0;
                    break;
            }

            if (aspect_clip == SP_ASPECT_SLICE) {
                // image clipped by viewbox

                if (ratio) {
                    // clip top/bottom
                    feImageY -= ay * (feImageWidth * aspect - feImageHeight);
                    feImageHeight = feImageWidth * aspect;
                } else {
                    // clip sides
                    feImageX -= ax * (feImageHeight / aspect - feImageWidth);
                    feImageWidth = feImageHeight / aspect;
                }

            } else {
                // image fits into viewbox

                if (ratio) {
                    // fit to height
                    feImageX += ax * (feImageWidth - feImageHeight / aspect );
                    feImageWidth = feImageHeight / aspect;
                } else {
                    // fit to width
                    feImageY += ay * (feImageHeight - feImageWidth * aspect);
                    feImageHeight = feImageWidth * aspect;
                }
            }
        }

        double scaleX = feImageWidth / image_width;
        double scaleY = feImageHeight / image_height;

        dc.translate(feImageX, feImageY);
        dc.scale(scaleX, scaleY);
        item->render(dc, slot.get_rendercontext(), render_rect);
    }

    slot.set(_output, out);
    cairo_surface_destroy(out);
}

bool FilterImage::can_handle_affine(Geom::Affine const &) const
{
    return true;
}

double FilterImage::complexity(Geom::Affine const &) const
{
    // TODO: right now we cannot actually measure this in any meaningful way.
    return 1.1;
}

void FilterImage::set_align(unsigned align)
{
    aspect_align = align;
}

void FilterImage::set_clip(unsigned clip)
{
    aspect_clip = clip;
}

} // namespace Filters
} // namespace Inkscape

/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :