1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */
2 /* AbiWord
3 * Copyright (C) 2008 Dominic Lachowicz
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301 USA.
19 */
20
21 #include "gr_CairoImage.h"
22 #include "gr_UnixImage.h"
23 #include "ut_assert.h"
24 #include "ut_bytebuf.h"
25 #include "ut_debugmsg.h"
26 #include "gr_CairoGraphics.h"
27
28 #include <stdlib.h>
29
GR_RSVGVectorImage(const char * name)30 GR_RSVGVectorImage::GR_RSVGVectorImage(const char* name)
31 : GR_CairoVectorImage(),
32 m_graphics(0),
33 m_surface(0),
34 m_image_surface(0),
35 m_svg(0),
36 m_scaleX(1.0),
37 m_scaleY(1.0),
38 m_needsNewSurface(false),
39 m_rasterImage(NULL)
40 {
41 if (name)
42 {
43 setName(name);
44 }
45 else
46 {
47 setName("SVGImage");
48 }
49 }
50
~GR_RSVGVectorImage()51 GR_RSVGVectorImage::~GR_RSVGVectorImage()
52 {
53 reset();
54 }
55
convertToBuffer(UT_ByteBuf ** ppBB) const56 bool GR_RSVGVectorImage::convertToBuffer(UT_ByteBuf** ppBB) const
57 {
58 UT_ByteBuf* pBB = new UT_ByteBuf;
59
60 bool bCopied = pBB->append(m_data.getPointer(0), m_data.getLength());
61
62 if (!bCopied) DELETEP(pBB);
63
64 *ppBB = pBB;
65
66 return bCopied;
67 }
68
convertFromBuffer(const UT_ByteBuf * pBB,const std::string &,UT_sint32 iDisplayWidth,UT_sint32 iDisplayHeight)69 bool GR_RSVGVectorImage::convertFromBuffer(const UT_ByteBuf* pBB,
70 const std::string& /*mimetype*/,
71 UT_sint32 iDisplayWidth,
72 UT_sint32 iDisplayHeight) {
73 reset();
74
75 m_data.append(pBB->getPointer(0), pBB->getLength());
76
77 bool forceScale = (iDisplayWidth != -1 && iDisplayHeight != -1);
78
79 gboolean result;
80
81 m_svg = rsvg_handle_new();
82
83 result = rsvg_handle_write(m_svg, pBB->getPointer(0), pBB->getLength(), NULL);
84 if (!result) {
85 g_object_unref(G_OBJECT(m_svg));
86 m_svg = 0;
87
88 return false;
89 }
90
91 result = rsvg_handle_close(m_svg, NULL);
92
93 if (!result) {
94 g_object_unref(G_OBJECT(m_svg));
95 m_svg = 0;
96
97 return false;
98 }
99
100 rsvg_handle_get_dimensions(m_svg, &m_size);
101
102 if (!forceScale)
103 setupScale(m_size.width, m_size.height);
104 else
105 setupScale(iDisplayWidth, iDisplayHeight);
106
107
108 return true;
109 }
110
cairoSetSource(cairo_t * cr)111 void GR_RSVGVectorImage::cairoSetSource(cairo_t *cr)
112 {
113 createSurface(cr);
114 if (m_surface == NULL)
115 {
116 return;
117 }
118
119 cairo_set_source_surface(cr, m_surface, 0, 0);
120 }
121
scaleImageTo(GR_Graphics * pG,const UT_Rect & rec)122 void GR_RSVGVectorImage::scaleImageTo(GR_Graphics * pG, const UT_Rect & rec)
123 {
124 setupScale(pG->tdu(rec.width), pG->tdu(rec.height));
125 }
126
reset()127 void GR_RSVGVectorImage::reset()
128 {
129 m_data.truncate(0);
130 if (m_svg)
131 {
132 g_object_unref(G_OBJECT(m_svg));
133 m_svg = 0;
134 }
135
136 if (m_surface)
137 {
138 cairo_surface_destroy(m_surface);
139 m_surface = 0;
140 }
141
142 if (m_image_surface) {
143 cairo_surface_destroy(m_image_surface);
144 m_image_surface = 0;
145 }
146
147 m_scaleX = m_scaleY = 1.0;
148 m_graphics = 0;
149 m_needsNewSurface = false;
150 memset(&m_size, 0, sizeof(RsvgDimensionData));
151 DELETEP(m_rasterImage);
152 }
153
setupScale(UT_sint32 w,UT_sint32 h)154 void GR_RSVGVectorImage::setupScale(UT_sint32 w, UT_sint32 h) {
155 setDisplaySize(w, h);
156
157 m_scaleX = (double)w / m_size.width;
158 m_scaleY = (double)h / m_size.height;
159
160 m_needsNewSurface = true;
161 }
162
renderToSurface(cairo_surface_t * surf)163 void GR_RSVGVectorImage::renderToSurface(cairo_surface_t* surf) {
164 cairo_t* cr = cairo_create(surf);
165 cairo_scale(cr, m_scaleX, m_scaleY);
166 rsvg_handle_render_cairo(m_svg, cr);
167 //
168 // Setup Raster Image too
169 //
170 UT_String name;
171 getName(name);
172 DELETEP(m_rasterImage);
173 m_rasterImage = new GR_UnixImage(name.c_str(), rsvg_handle_get_pixbuf(m_svg));
174 m_rasterImage->scale(getDisplayWidth(), getDisplayHeight());
175 cairo_destroy(cr);
176 }
177
renderToCairo(cairo_t * cr)178 void GR_RSVGVectorImage::renderToCairo(cairo_t* cr) {
179 cairo_scale(cr, m_scaleX, m_scaleY);
180 rsvg_handle_render_cairo(m_svg, cr);
181 cairo_new_path(cr);
182 }
183
createImageSurface()184 void GR_RSVGVectorImage::createImageSurface() {
185 if (!m_needsNewSurface)
186 return;
187
188 if (m_image_surface != 0)
189 { // get rid of any previous surface
190 cairo_surface_destroy(m_image_surface);
191 m_image_surface = 0;
192 }
193
194 m_image_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
195 getDisplayWidth(),
196 getDisplayHeight());
197
198 renderToSurface(m_image_surface);
199 m_needsNewSurface = false;
200 }
201
createSurface(cairo_t * cairo)202 void GR_RSVGVectorImage::createSurface(cairo_t* cairo) {
203 if (!m_needsNewSurface && cairo == m_graphics)
204 return; // already have a similar surface for this graphics at this size
205
206 if (m_surface != 0) { // get rid of any previous surface
207 cairo_surface_destroy(m_surface);
208 m_surface = 0;
209 }
210
211 m_surface = cairo_surface_create_similar(cairo_get_target(cairo),
212 CAIRO_CONTENT_COLOR_ALPHA,
213 getDisplayWidth(),
214 getDisplayHeight());
215
216 // render to the similar surface once. blit subsequently.
217 renderToSurface(m_surface);
218 createImageSurface();
219 }
220
hasAlpha(void) const221 bool GR_RSVGVectorImage::hasAlpha(void) const
222 {
223 return true; // assume that any SVG could contain an alpha channel
224 }
225
isTransparentAt(UT_sint32 x,UT_sint32 y)226 bool GR_RSVGVectorImage::isTransparentAt(UT_sint32 x, UT_sint32 y)
227 {
228 if(!hasAlpha())
229 {
230 return false;
231 }
232
233 if (!m_image_surface)
234 createImageSurface();
235 UT_return_val_if_fail(m_image_surface,false);
236 UT_return_val_if_fail(cairo_image_surface_get_format(m_image_surface) == CAIRO_FORMAT_ARGB32, false);
237
238 UT_sint32 iRowStride = cairo_image_surface_get_stride(m_image_surface);
239 UT_sint32 iWidth = cairo_image_surface_get_width(m_image_surface);
240 UT_sint32 iHeight = cairo_image_surface_get_height(m_image_surface);
241 UT_ASSERT(iRowStride/iWidth == 4);
242 UT_return_val_if_fail((x>= 0) && (x < iWidth), false);
243 UT_return_val_if_fail((y>= 0) && (y < iHeight), false);
244
245 guchar * pData = cairo_image_surface_get_data(m_image_surface);
246 UT_sint32 iOff = iRowStride*y;
247 guchar pix0 = pData[iOff+ x*4];
248 if(pix0 == 0) // the data is not pre-multiplied, ARGB. if the first 8bits are 0, the image is fully transparent
249 {
250 return true;
251 }
252 return false;
253 }
254
createImageSegment(GR_Graphics * pG,const UT_Rect & rec)255 GR_Image *GR_RSVGVectorImage::createImageSegment(GR_Graphics * pG, const UT_Rect & rec)
256 {
257 #if 1
258 // we need createImageSegment for converting inline images to positioned images via the context menu and for drawing on background images
259
260 // TODO: can we draw the RsvgHandle to a cairo SVG surface, clip it, and return a new GR_RSVGVectorImage?
261 // TODO: or do we just rasterize it?
262
263 // For now we rasterize it
264 if(!m_rasterImage || m_needsNewSurface)
265 {
266 createImageSurface();
267 }
268 return m_rasterImage->createImageSegment(pG, rec);
269 #else
270 return NULL;
271 #endif
272 }
273