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