summaryrefslogtreecommitdiffstats
path: root/src/display/drawing-surface.cpp
blob: 41ff1416772377406ac41a72161401af748f3daf (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
/**
 * @file
 * @brief Cairo surface that remembers its origin
 *//*
 * Authors:
 *   Krzysztof Kosiński <tweenk.pl@gmail.com>
 *
 * Copyright (C) 2011 Authors
 * Released under GNU GPL, read the file 'COPYING' for more information
 */

#include "display/drawing-surface.h"
#include "display/cairo-utils.h"

namespace Inkscape {

using Geom::X;
using Geom::Y;


/** @class DrawingSurface
 * @brief Drawing surface that remembers its origin.
 *
 * This is a very minimalistic wrapper over cairo_surface_t. The main
 * extra functionality provided by this class is that it automates
 * the mapping from "logical space" (coordinates in the rendering)
 * and the "physical space" (surface pixels). For example, patterns
 * have to be rendered on tiles which have possibly non-integer
 * widths and heights.
 *
 * This class has delayed allocation functionality - it creates
 * the Cairo surface it wraps on the first call to createRawContext()
 * of when a DrawingContext is constructed.
 */

/** @brief Creates a surface with the given physical extents.
 * 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)
    : _surface(NULL)
    , _origin(area.min())
    , _scale(1, 1)
    , _pixels(area.dimensions())
{}

/** @brief Creates a surface with the given logical extents.
 * When a drawing context is created for this surface, its pixels
 * will cover the area under the given rectangle. If the rectangle
 * has non-integer width, there will be slightly more than 1 pixel
 * per logical unit. */
DrawingSurface::DrawingSurface(Geom::Rect const &area)
    : _surface(NULL)
    , _origin(area.min())
    , _scale(ceil(area.width()) / area.width(), ceil(area.height()) / area.height())
    , _pixels(area.dimensions().ceil())
{}

/** @brief Creates a surface with the given logical and physical extents.
 * When a drawing context is created for this surface, its pixels
 * will cover the area under the given rectangle. IT will contain
 * the number of pixels specified by the second argument.
 * @param logbox Logical extents of the surface
 * @param pixdims Pixel dimensions of the surface. */
DrawingSurface::DrawingSurface(Geom::Rect const &logbox, Geom::IntPoint const &pixdims)
    : _surface(NULL)
    , _origin(logbox.min())
    , _scale(pixdims[X] / logbox.width(), pixdims[Y] / logbox.height())
    , _pixels(pixdims)
{}

/** @brief Wrap a cairo_surface_t.
 * This constructor will take an extra reference on @a surface, which will
 * be released on destruction. */
DrawingSurface::DrawingSurface(cairo_surface_t *surface, Geom::Point const &origin)
    : _surface(surface)
    , _origin(origin)
    , _scale(1, 1)
{
    cairo_surface_reference(surface);
    _pixels[X] = cairo_image_surface_get_width(surface);
    _pixels[Y] = cairo_image_surface_get_height(surface);
}

DrawingSurface::~DrawingSurface()
{
    if (_surface)
        cairo_surface_destroy(_surface);
}

/// Get the logical extents of the surface.
Geom::Rect
DrawingSurface::area() const
{
    Geom::Rect r = Geom::Rect::from_xywh(_origin, dimensions());
    return r;
}

/// Get the pixel dimensions of the surface
Geom::IntPoint
DrawingSurface::pixels() const
{
    return _pixels;
}

/// Get the logical width and weight of the surface as a point.
Geom::Point
DrawingSurface::dimensions() const
{
    Geom::Point logical_dims(_pixels[X] / _scale[X], _pixels[Y] / _scale[Y]);
    return logical_dims;
}

Geom::Point
DrawingSurface::origin() const
{
    return _origin;
}

Geom::Scale
DrawingSurface::scale() const
{
    return _scale;
}

/// Get the transformation applied to the drawing context on construction.
Geom::Affine
DrawingSurface::drawingTransform() const
{
    Geom::Affine ret = _scale * Geom::Translate(-_origin);
    return ret;
}

cairo_surface_type_t
DrawingSurface::type() const
{
    // currently hardcoded
    return CAIRO_SURFACE_TYPE_IMAGE;
}

/// Drop contents of the surface and release the underlying Cairo object.
void
DrawingSurface::dropContents()
{
    if (_surface) {
        cairo_surface_destroy(_surface);
        _surface = NULL;
    }
}

/** @brief Create a drawing context for this surface.
 * It's better to use the surface constructor of DrawingContext. */
cairo_t *
DrawingSurface::createRawContext()
{
    // deferred allocation
    if (!_surface) {
        _surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, _pixels[X], _pixels[Y]);
    }
    cairo_t *ct = cairo_create(_surface);
    if (_scale != Geom::Scale::identity()) {
        cairo_scale(ct, _scale[X], _scale[Y]);
    }
    cairo_translate(ct, -_origin[X], -_origin[Y]);
    return ct;
}

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