summaryrefslogtreecommitdiffstats
path: root/src/display/nr-filter-tile.cpp
blob: 81966baac0e6a00c770992128f6fa86a870d1928 (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
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * feTile filter primitive renderer
 *
 * Authors:
 *   Felipe Corrêa da Silva Sanches <juca@members.fsf.org>
 *
 * Copyright (C) 2007 authors
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#include <glib.h>

#include "display/cairo-utils.h"
#include "display/nr-filter-tile.h"
#include "display/nr-filter-slot.h"
#include "display/nr-filter-units.h"

namespace Inkscape {
namespace Filters {

FilterTile::FilterTile() = default;

FilterTile::~FilterTile() = default;

void FilterTile::render_cairo(FilterSlot &slot) const
{
    // This input source contains only the "rendering" tile.
    cairo_surface_t *in = slot.getcairo(_input);

    // For debugging
    // static int i = 0;
    // ++i;
    // std::stringstream filename;
    // filename << "dump." << i << ".png";
    // cairo_surface_write_to_png( in, filename.str().c_str() );

    // This is the feTile source area as determined by the input primitive area (see SVG spec).
    Geom::Rect tile_area = slot.get_primitive_area(_input);

    if (tile_area.width() == 0.0 || tile_area.height() == 0.0) {

        slot.set(_output, in);
        std::cerr << "FileTile::render_cairo: tile has zero width or height" << std::endl;

    } else {

        cairo_surface_t *out = ink_cairo_surface_create_identical(in);
        // color_interpolation_filters for out same as in.
        copy_cairo_surface_ci(in, out);
        cairo_t *ct = cairo_create(out);

        // The rectangle of the "rendering" tile.
        Geom::Rect sa = slot.get_slot_area();

        Geom::Affine trans = slot.get_units().get_matrix_user2pb();

        // Create feTile tile ----------------

        // Get tile area in pixbuf units (tile transformed).
        Geom::Rect tt = tile_area * trans;
        
        // Shift between "rendering" tile and feTile tile
        Geom::Point shift = sa.min() - tt.min(); 

        // Create feTile tile surface
        cairo_surface_t *tile = cairo_surface_create_similar(in, cairo_surface_get_content(in),
                                                             tt.width(), tt.height());
        cairo_t *ct_tile = cairo_create(tile);
        cairo_set_source_surface(ct_tile, in, shift[Geom::X], shift[Geom::Y]);
        cairo_paint(ct_tile);

        // Paint tiles ------------------
        
        // For debugging
        // std::stringstream filename;
        // filename << "tile." << i << ".png";
        // cairo_surface_write_to_png( tile, filename.str().c_str() );
        
        // Determine number of feTile rows and columns
        Geom::Rect pr = filter_primitive_area(slot.get_units());
        int tile_cols = std::ceil(pr.width()  / tile_area.width());
        int tile_rows = std::ceil(pr.height() / tile_area.height());

        // Do tiling (TO DO: restrict to slot area.)
        for (int col = 0; col < tile_cols; ++col) {
            for (int row = 0; row < tile_rows; ++row) {
                Geom::Point offset(col * tile_area.width(), row * tile_area.height());
                offset *= trans;
                offset[Geom::X] -= trans[4];
                offset[Geom::Y] -= trans[5];
        
                cairo_set_source_surface(ct, tile, offset[Geom::X], offset[Geom::Y]);
                cairo_paint(ct);
            }
        }
        slot.set(_output, out);

        // Clean up
        cairo_destroy(ct);
        cairo_surface_destroy(out);
        cairo_destroy(ct_tile);
        cairo_surface_destroy(tile);
    }
}

void FilterTile::area_enlarge(Geom::IntRect &area, Geom::Affine const &trans) const
{
    // Set to very large rectangle so we get tile source. It will be clipped later.

    // Note, setting to infinite using Geom::IntRect::infinite() causes overflow/underflow problems.
    Geom::IntCoord max = std::numeric_limits<Geom::IntCoord>::max() / 4;
    area = Geom::IntRect(-max, -max, max, max);
}

double FilterTile::complexity(Geom::Affine const &) const
{
    return 1.0;
}

} // 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 :