summaryrefslogtreecommitdiffstats
path: root/src/helper/pixbuf-ops.cpp
blob: 82d85e9c1b06699711337e51eb79989ac48228fc (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
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Helpers for SPItem -> gdk_pixbuf related stuff
 *
 * Authors:
 *   John Cliff <simarilius@yahoo.com>
 *   Jon A. Cruz <jon@joncruz.org>
 *   Abhishek Sharma
 *
 * Copyright (C) 2008 John Cliff
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#include <2geom/transforms.h>
#include <gdk/gdk.h>

#include "helper/pixbuf-ops.h"
#include "helper/png-write.h"
#include "display/cairo-utils.h"
#include "display/drawing.h"
#include "display/drawing-context.h"
#include "document.h"
#include "object/sp-root.h"
#include "object/sp-defs.h"
#include "object/sp-use.h"
#include "util/units.h"
#include "util/scope_exit.h"
#include "inkscape.h"

/**
    Generates a bitmap from given items. The bitmap is stored in RAM and not written to file.
    @param document Inkscape document.
    @param area     Export area in document units.
    @param dpi      Resolution.
    @param items    Vector of pointers to SPItems to export. Export all items if empty.
    @param opaque   Set items opacity to 1 (used by Cairo renderer for filtered objects rendered as bitmaps).
    @return The created GdkPixbuf structure or nullptr if rendering failed.
*/
Inkscape::Pixbuf *sp_generate_internal_bitmap(SPDocument *document,
                                              Geom::Rect const &area,
                                              double dpi,
                                              std::vector<SPItem *> items,
                                              bool opaque,
                                              uint32_t const *checkerboard_color,
                                              double device_scale)
{
    // Geometry
    if (area.hasZeroArea()) {
        return nullptr;
    }

    Geom::Point origin = area.min();
    double scale_factor = Inkscape::Util::Quantity::convert(dpi, "px", "in");
    Geom::Affine affine = Geom::Translate(-origin) * Geom::Scale (scale_factor, scale_factor);

    int width  = std::ceil(scale_factor * area.width());
    int height = std::ceil(scale_factor * area.height());

    // Document
    document->ensureUpToDate();
    unsigned dkey = SPItem::display_key_new(1);

    // Drawing
    Inkscape::Drawing drawing; // New drawing for offscreen rendering.
    drawing.setRoot(document->getRoot()->invoke_show(drawing, dkey, SP_ITEM_SHOW_DISPLAY));
    auto invoke_hide_guard = scope_exit([&] { document->getRoot()->invoke_hide(dkey); });
    drawing.root()->setTransform(affine);
    drawing.setExact(); // Maximum quality for blurs.

    // Hide all items we don't want, instead of showing only requested items,
    // because that would not work if the shown item references something in defs.
    if (!items.empty()) {
        document->getRoot()->invoke_hide_except(dkey, items);
    }

    auto final_area = Geom::IntRect::from_xywh(0, 0, width, height);
    drawing.update(final_area);

    if (opaque) {
        // Required by sp_asbitmap_render().
        for (auto item : items) {
            if (item->get_arenaitem(dkey)) {
                item->get_arenaitem(dkey)->setOpacity(1.0);
            }
        }
    }

    // Rendering
    cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);

    if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
        long long size = (long long)height * (long long)cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
        g_warning("sp_generate_internal_bitmap: not enough memory to create pixel buffer. Need %lld.", size);
        cairo_surface_destroy(surface);
        return nullptr;
    }

    Inkscape::DrawingContext dc(surface, Geom::Point(0, 0));

    if (checkerboard_color) {
        auto pattern = ink_cairo_pattern_create_checkerboard(*checkerboard_color);
        dc.save();
        dc.transform(Geom::Scale(device_scale));
        dc.setOperator(CAIRO_OPERATOR_SOURCE);
        dc.setSource(pattern);
        dc.paint();
        dc.restore();
        cairo_pattern_destroy(pattern);
    }

    // render items
    drawing.render(dc, final_area, Inkscape::DrawingItem::RENDER_BYPASS_CACHE);

    if (device_scale != 1.0) {
        cairo_surface_set_device_scale(surface, device_scale, device_scale);
    }

    return new Inkscape::Pixbuf(surface);
}

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