1 /**
2 * This file is a part of the Cairo-Dock project
3 *
4 * Copyright : (C) see the 'copyright' file.
5 * E-mail    : see the 'copyright' file.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 3
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include <string.h>
21 #include <math.h>
22 #include <pango/pango.h>
23 
24 #include "cairo-dock-log.h"
25 #include "cairo-dock-draw.h"
26 #include "cairo-dock-launcher-manager.h"
27 #include "cairo-dock-container.h"
28 #include "cairo-dock-image-buffer.h"
29 #include "cairo-dock-desktop-manager.h"
30 #include "cairo-dock-icon-manager.h"  // cairo_dock_search_icon_s_path
31 #include "cairo-dock-dialog-manager.h"
32 #include "cairo-dock-style-manager.h"
33 #include "cairo-dock-surface-factory.h"
34 
35 extern GldiContainer *g_pPrimaryContainer;
36 extern gboolean g_bUseOpenGL;
37 
38 
39 /* Calcule la taille d'une image selon une contrainte en largeur et hauteur de manière à remplir l'espace donné.
40 *@param fImageWidth the width of the image. Contient initialement the width of the image, et sera écrasée avec la largeur obtenue.
41 *@param fImageHeight the height of the image. Contient initialement the height of the image, et sera écrasée avec la hauteur obtenue.
42 *@param iWidthConstraint contrainte en largeur (0 <=> pas de contrainte).
43 *@param iHeightConstraint contrainte en hauteur (0 <=> pas de contrainte).
44 *@param bNoZoomUp TRUE ssi on ne doit pas agrandir the image (seulement la rétrécir).
45 *@param fZoomWidth sera renseigné avec le facteur de zoom en largeur qui a été appliqué.
46 *@param fZoomHeight sera renseigné avec le facteur de zoom en hauteur qui a été appliqué.
47 */
_cairo_dock_calculate_size_fill(double * fImageWidth,double * fImageHeight,int iWidthConstraint,int iHeightConstraint,gboolean bNoZoomUp,double * fZoomWidth,double * fZoomHeight)48 static void _cairo_dock_calculate_size_fill (double *fImageWidth, double *fImageHeight, int iWidthConstraint, int iHeightConstraint, gboolean bNoZoomUp, double *fZoomWidth, double *fZoomHeight)
49 {
50 	if (iWidthConstraint != 0)
51 	{
52 		*fZoomWidth = 1. * iWidthConstraint / (*fImageWidth);
53 		if (bNoZoomUp && *fZoomWidth > 1)
54 			*fZoomWidth = 1;
55 		else
56 			*fImageWidth = (double) iWidthConstraint;
57 	}
58 	else
59 		*fZoomWidth = 1.;
60 	if (iHeightConstraint != 0)
61 	{
62 		*fZoomHeight = 1. * iHeightConstraint / (*fImageHeight);
63 		if (bNoZoomUp && *fZoomHeight > 1)
64 			*fZoomHeight = 1;
65 		else
66 			*fImageHeight = (double) iHeightConstraint;
67 	}
68 	else
69 		*fZoomHeight = 1.;
70 }
71 
72 /* Calcule la taille d'une image selon une contrainte en largeur et hauteur en gardant le ratio hauteur/largeur constant.
73 *@param fImageWidth the width of the image. Contient initialement the width of the image, et sera écrasée avec la largeur obtenue.
74 *@param fImageHeight the height of the image. Contient initialement the height of the image, et sera écrasée avec la hauteur obtenue.
75 *@param iWidthConstraint contrainte en largeur (0 <=> pas de contrainte).
76 *@param iHeightConstraint contrainte en hauteur (0 <=> pas de contrainte).
77 *@param bNoZoomUp TRUE ssi on ne doit pas agrandir the image (seulement la rétrécir).
78 *@param fZoom sera renseigné avec le facteur de zoom qui a été appliqué.
79 */
_cairo_dock_calculate_size_constant_ratio(double * fImageWidth,double * fImageHeight,int iWidthConstraint,int iHeightConstraint,gboolean bNoZoomUp,double * fZoom)80 static void _cairo_dock_calculate_size_constant_ratio (double *fImageWidth, double *fImageHeight, int iWidthConstraint, int iHeightConstraint, gboolean bNoZoomUp, double *fZoom)
81 {
82 	if (iWidthConstraint != 0 && iHeightConstraint != 0)
83 		*fZoom = MIN (iWidthConstraint / (*fImageWidth), iHeightConstraint / (*fImageHeight));
84 	else if (iWidthConstraint != 0)
85 		*fZoom = iWidthConstraint / (*fImageWidth);
86 	else if (iHeightConstraint != 0)
87 		*fZoom = iHeightConstraint / (*fImageHeight);
88 	else
89 		*fZoom = 1.;
90 	if (bNoZoomUp && *fZoom > 1)
91 		*fZoom = 1.;
92 	*fImageWidth = (*fImageWidth) * (*fZoom);
93 	*fImageHeight = (*fImageHeight) * (*fZoom);
94 }
95 
96 
97 /* Calculate the size of an image according to a constraint on width and height, and a loading modifier.
98 *@param fImageWidth pointer to the width of the image. Initially contains the width of the original image, and is updated with the resulting width.
99 *@param fImageHeight pointer to the height of the image. Initially contains the height of the original image, and is updated with the resulting height.
100 *@param iWidthConstraint constraint on width (0 <=> no constraint).
101 *@param iHeightConstraint constraint on height (0 <=> no constraint).
102 *@param iLoadingModifier a mask of different loading modifiers.
103 *@param fZoomWidth will be filled with the zoom that has been applied on width.
104 *@param fZoomHeight will be filled with the zoom that has been applied on height.
105 */
_cairo_dock_calculate_constrainted_size(double * fImageWidth,double * fImageHeight,int iWidthConstraint,int iHeightConstraint,CairoDockLoadImageModifier iLoadingModifier,double * fZoomWidth,double * fZoomHeight)106 static void _cairo_dock_calculate_constrainted_size (double *fImageWidth, double *fImageHeight, int iWidthConstraint, int iHeightConstraint, CairoDockLoadImageModifier iLoadingModifier, double *fZoomWidth, double *fZoomHeight)
107 {
108 	gboolean bFillSpace = iLoadingModifier & CAIRO_DOCK_FILL_SPACE;
109 	gboolean bKeepRatio = iLoadingModifier & CAIRO_DOCK_KEEP_RATIO;
110 	gboolean bNoZoomUp = iLoadingModifier & CAIRO_DOCK_DONT_ZOOM_IN;
111 	gboolean bAnimated = iLoadingModifier & CAIRO_DOCK_ANIMATED_IMAGE;
112 	gint iOrientation = iLoadingModifier & CAIRO_DOCK_ORIENTATION_MASK;
113 	if (iOrientation > CAIRO_DOCK_ORIENTATION_VFLIP)  // inversion x/y
114 	{
115 		double tmp = *fImageWidth;
116 		*fImageWidth = *fImageHeight;
117 		*fImageHeight = tmp;
118 	}
119 
120 	if (bAnimated)
121 	{
122 		if (*fImageWidth > *fImageHeight)
123 		{
124 			if (((int)*fImageWidth) % ((int)*fImageHeight) == 0)  // w = k*h
125 			{
126 				iWidthConstraint = *fImageWidth / *fImageHeight * iHeightConstraint;
127 			}
128 			else if (*fImageWidth > 2 * *fImageHeight)  // if we're confident this image is an animated one, try to be smart, to handle the case of non-square frames.
129 			{
130 				// assume we have wide frames => w > h
131 				int w = *fImageHeight + 1;
132 				do
133 				{
134 					if ((int)*fImageWidth % w == 0)
135 					{
136 						iWidthConstraint = *fImageWidth / w * iHeightConstraint;
137 						//g_print ("frame: %d, %d\n", w, iWidthConstraint);
138 						break;
139 					}
140 					w ++;
141 				} while (w < (*fImageWidth) / 2);
142 			}
143 		}
144 	}
145 
146 	if (bKeepRatio)
147 	{
148 		_cairo_dock_calculate_size_constant_ratio (fImageWidth,
149 			fImageHeight,
150 			iWidthConstraint,
151 			iHeightConstraint,
152 			bNoZoomUp,
153 			fZoomWidth);
154 		*fZoomHeight = *fZoomWidth;
155 		if (bFillSpace)
156 		{
157 			//double fUsefulWidth = *fImageWidth;
158 			//double fUsefulHeight = *fImageHeight;
159 			if (iWidthConstraint != 0)
160 				*fImageWidth = iWidthConstraint;
161 			if (iHeightConstraint != 0)
162 				*fImageHeight = iHeightConstraint;
163 		}
164 	}
165 	else
166 	{
167 		_cairo_dock_calculate_size_fill (fImageWidth,
168 			fImageHeight,
169 			iWidthConstraint,
170 			iHeightConstraint,
171 			bNoZoomUp,
172 			fZoomWidth,
173 			fZoomHeight);
174 	}
175 }
176 
177 
_get_source_context(void)178 static inline cairo_t *_get_source_context (void)
179 {
180 	cairo_t *pSourceContext = NULL;
181 	if (g_pPrimaryContainer != NULL)
182 	{
183 		gtk_widget_realize (g_pPrimaryContainer->pWidget);  // ensure the widget is realized
184 		pSourceContext = gdk_cairo_create (gldi_container_get_gdk_window(g_pPrimaryContainer));
185 	}
186 	return pSourceContext;  // Note: we can't keep the context alive and reuse it later, because under Wayland it will make the container invisible
187 }
188 
cairo_dock_create_blank_surface(int iWidth,int iHeight)189 cairo_surface_t *cairo_dock_create_blank_surface (int iWidth, int iHeight)
190 {
191 	cairo_t *pSourceContext = NULL;
192 	if (! g_bUseOpenGL)
193 		pSourceContext = _get_source_context ();
194 	cairo_surface_t *pSurface;
195 	if (pSourceContext != NULL && cairo_status (pSourceContext) == CAIRO_STATUS_SUCCESS)
196 		pSurface = cairo_surface_create_similar (cairo_get_target (pSourceContext),
197 			CAIRO_CONTENT_COLOR_ALPHA,
198 			iWidth,
199 			iHeight);
200 	else  // opengl or invalid context -> create an image that is a mere ARGB buffer that can be mapped into a texture
201 		pSurface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
202 			iWidth,
203 			iHeight);
204 	cairo_destroy (pSourceContext);
205 	return pSurface;
206 }
207 
_apply_orientation_and_scale(cairo_t * pCairoContext,CairoDockLoadImageModifier iLoadingModifier,double fImageWidth,double fImageHeight,double fZoomX,double fZoomY,double fUsefulWidth,double fUsefulheight)208 static inline void _apply_orientation_and_scale (cairo_t *pCairoContext, CairoDockLoadImageModifier iLoadingModifier, double fImageWidth, double fImageHeight, double fZoomX, double fZoomY, double fUsefulWidth, double fUsefulheight)
209 {
210 	int iOrientation = iLoadingModifier & CAIRO_DOCK_ORIENTATION_MASK;
211 
212 	cairo_translate (pCairoContext,
213 		fImageWidth/2,
214 		fImageHeight/2);
215 	cairo_scale (pCairoContext,
216 		fZoomX,
217 		fZoomY);
218 	switch (iOrientation)
219 	{
220 		case CAIRO_DOCK_ORIENTATION_HFLIP :
221 			cd_debug ("orientation : HFLIP");
222 			cairo_scale (pCairoContext, -1., 1.);
223 		break ;
224 		case CAIRO_DOCK_ORIENTATION_ROT_180 :
225 			cd_debug ("orientation : ROT_180");
226 			cairo_rotate (pCairoContext, G_PI);
227 		break ;
228 		case CAIRO_DOCK_ORIENTATION_VFLIP :
229 			cd_debug ("orientation : VFLIP");
230 			cairo_scale (pCairoContext, 1., -1.);
231 		break ;
232 		case CAIRO_DOCK_ORIENTATION_ROT_90_HFLIP :
233 			cd_debug ("orientation : ROT_90_HFLIP");
234 			cairo_scale (pCairoContext, -1., 1.);
235 			cairo_rotate (pCairoContext, + G_PI/2);
236 		break ;
237 		case CAIRO_DOCK_ORIENTATION_ROT_90 :
238 			cd_debug ("orientation : ROT_90");
239 			cairo_rotate (pCairoContext, + G_PI/2);
240 		break ;
241 		case CAIRO_DOCK_ORIENTATION_ROT_90_VFLIP :
242 			cd_debug ("orientation : ROT_90_VFLIP");
243 			cairo_scale (pCairoContext, 1., -1.);
244 			cairo_rotate (pCairoContext, + G_PI/2);
245 		break ;
246 		case CAIRO_DOCK_ORIENTATION_ROT_270 :
247 			cd_debug ("orientation : ROT_270");
248 			cairo_rotate (pCairoContext, - G_PI/2);
249 		break ;
250 		default :
251 		break ;
252 	}
253 	if (iOrientation < CAIRO_DOCK_ORIENTATION_ROT_90_HFLIP)
254 		cairo_translate (pCairoContext,
255 			- fUsefulWidth/2/fZoomX,
256 			- fUsefulheight/2/fZoomY);
257 	else
258 		cairo_translate (pCairoContext,
259 			- fUsefulheight/2/fZoomY,
260 			- fUsefulWidth/2/fZoomX);
261 }
262 
263 
cairo_dock_create_surface_from_xicon_buffer(gulong * pXIconBuffer,int iBufferNbElements,int iWidth,int iHeight)264 cairo_surface_t *cairo_dock_create_surface_from_xicon_buffer (gulong *pXIconBuffer, int iBufferNbElements, int iWidth, int iHeight)
265 {
266 	//\____________________ On recupere la plus grosse des icones presentes dans le tampon (meilleur rendu).
267 	int iIndex = 0, iBestIndex = 0;
268 	while (iIndex + 2 < iBufferNbElements)
269 	{
270 		if (pXIconBuffer[iIndex] == 0 || pXIconBuffer[iIndex+1] == 0)  // precaution au cas ou un buffer foirreux nous serait retourne, on risque de boucler sans fin.
271 		{
272 			cd_warning ("This icon is broken !\nThis means that one of the current applications has sent a buggy icon to X.");
273 			if (iIndex == 0)  // tout le buffer est a jeter.
274 				return NULL;
275 			break;
276 		}
277 		if (pXIconBuffer[iIndex] > pXIconBuffer[iBestIndex])
278 			iBestIndex = iIndex;
279 		iIndex += 2 + pXIconBuffer[iIndex] * pXIconBuffer[iIndex+1];
280 	}
281 
282 	//\____________________ On pre-multiplie chaque composante par le alpha (necessaire pour libcairo).
283 	int w = pXIconBuffer[iBestIndex];
284 	int h = pXIconBuffer[iBestIndex+1];
285 	iBestIndex += 2;
286 	//g_print ("%s (%dx%d)\n", __func__, w, h);
287 
288 	int i, n = w * h;
289 	if (iBestIndex + n > iBufferNbElements)  // precaution au cas ou le nombre d'elements dans le buffer serait incorrect.
290 	{
291 		cd_warning ("This icon is broken !\nThis means that one of the current applications has sent a buggy icon to X.");
292 		return NULL;
293 	}
294 	gint pixel, alpha, red, green, blue;
295 	float fAlphaFactor;
296 	gint *pPixelBuffer = (gint *) &pXIconBuffer[iBestIndex];  // on va ecrire le resultat du filtre directement dans le tableau fourni en entree. C'est ok car sizeof(gulong) >= sizeof(gint), donc le tableau de pixels est plus petit que le buffer fourni en entree. merci a Hannemann pour ses tests et ses screenshots ! :-)
297 	for (i = 0; i < n; i ++)
298 	{
299 		pixel = (gint) pXIconBuffer[iBestIndex+i];
300 		alpha = (pixel & 0xFF000000) >> 24;
301 		red   = (pixel & 0x00FF0000) >> 16;
302 		green = (pixel & 0x0000FF00) >> 8;
303 		blue  = (pixel & 0x000000FF);
304 		fAlphaFactor = (float) alpha / 255;
305 		red *= fAlphaFactor;
306 		green *= fAlphaFactor;
307 		blue *= fAlphaFactor;
308 		pPixelBuffer[i] = (pixel & 0xFF000000) + (red << 16) + (green << 8) + blue;
309 	}
310 
311 	//\____________________ On cree la surface a partir du tampon.
312 	int iStride = w * sizeof (gint);  // nbre d'octets entre le debut de 2 lignes.
313 	cairo_surface_t *surface_ini = cairo_image_surface_create_for_data ((guchar *)pPixelBuffer,
314 		CAIRO_FORMAT_ARGB32,
315 		w,
316 		h,
317 		iStride);
318 
319 	double fWidth = w, fHeight = h;
320 	double fIconWidthSaturationFactor = 1., fIconHeightSaturationFactor = 1.;
321 	_cairo_dock_calculate_constrainted_size (&fWidth,
322 		&fHeight,
323 		iWidth,
324 		iHeight,
325 		CAIRO_DOCK_KEEP_RATIO | CAIRO_DOCK_FILL_SPACE,
326 		&fIconWidthSaturationFactor,
327 		&fIconHeightSaturationFactor);
328 
329 	cairo_surface_t *pNewSurface = cairo_dock_create_blank_surface (
330 		iWidth,
331 		iHeight);
332 	cairo_t *pCairoContext = cairo_create (pNewSurface);
333 
334 	double fUsefulWidth = w * fIconWidthSaturationFactor;  // a part dans le cas fill && keep ratio, c'est la meme chose que fImageWidth et fImageHeight.
335 	double fUsefulHeight = h * fIconHeightSaturationFactor;
336 	_apply_orientation_and_scale (pCairoContext,
337 		CAIRO_DOCK_KEEP_RATIO | CAIRO_DOCK_FILL_SPACE,
338 		iWidth, iHeight,
339 		fIconWidthSaturationFactor, fIconHeightSaturationFactor,
340 		fUsefulWidth, fUsefulHeight);
341 	cairo_set_source_surface (pCairoContext, surface_ini, 0, 0);
342 	cairo_paint (pCairoContext);
343 
344 	cairo_surface_destroy (surface_ini);
345 	cairo_destroy (pCairoContext);
346 
347 	return pNewSurface;
348 }
349 
350 
cairo_dock_create_surface_from_pixbuf(GdkPixbuf * pixbuf,double fMaxScale,int iWidthConstraint,int iHeightConstraint,CairoDockLoadImageModifier iLoadingModifier,double * fImageWidth,double * fImageHeight,double * fZoomX,double * fZoomY)351 cairo_surface_t *cairo_dock_create_surface_from_pixbuf (GdkPixbuf *pixbuf, double fMaxScale, int iWidthConstraint, int iHeightConstraint, CairoDockLoadImageModifier iLoadingModifier, double *fImageWidth, double *fImageHeight, double *fZoomX, double *fZoomY)
352 {
353 	*fImageWidth = gdk_pixbuf_get_width (pixbuf);
354 	*fImageHeight = gdk_pixbuf_get_height (pixbuf);
355 	double fIconWidthSaturationFactor = 1., fIconHeightSaturationFactor = 1.;
356 	_cairo_dock_calculate_constrainted_size (fImageWidth,
357 			fImageHeight,
358 			iWidthConstraint,
359 			iHeightConstraint,
360 			iLoadingModifier,
361 			&fIconWidthSaturationFactor,
362 			&fIconHeightSaturationFactor);
363 
364 	GdkPixbuf *pPixbufWithAlpha = pixbuf;
365 	if (! gdk_pixbuf_get_has_alpha (pixbuf))  // on lui rajoute un canal alpha s'il n'en a pas.
366 	{
367 		//g_print ("  ajout d'un canal alpha\n");
368 		pPixbufWithAlpha = gdk_pixbuf_add_alpha (pixbuf, FALSE, 255, 255, 255);  // TRUE <=> les pixels blancs deviennent transparents.
369 	}
370 
371 	//\____________________ On pre-multiplie chaque composante par le alpha (necessaire pour libcairo).
372 	int iNbChannels = gdk_pixbuf_get_n_channels (pPixbufWithAlpha);
373 	int iRowstride = gdk_pixbuf_get_rowstride (pPixbufWithAlpha);
374 	int w = gdk_pixbuf_get_width (pPixbufWithAlpha);
375 	guchar *p, *pixels = gdk_pixbuf_get_pixels (pPixbufWithAlpha);
376 	int h = gdk_pixbuf_get_height (pPixbufWithAlpha);
377 	int x, y;
378 	int red, green, blue;
379 	float fAlphaFactor;
380 	for (y = 0; y < h; y ++)
381 	{
382 		for (x = 0; x < w; x ++)
383 		{
384 			p = pixels + y * iRowstride + x * iNbChannels;
385 			fAlphaFactor = (float) p[3] / 255;
386 			red = p[0] * fAlphaFactor;
387 			green = p[1] * fAlphaFactor;
388 			blue = p[2] * fAlphaFactor;
389 			p[0] = blue;
390 			p[1] = green;
391 			p[2] = red;
392 		}
393 	}
394 
395 	cairo_surface_t *surface_ini = cairo_image_surface_create_for_data (pixels,
396 		CAIRO_FORMAT_ARGB32,
397 		w,
398 		h,
399 		iRowstride);
400 
401 	cairo_surface_t *pNewSurface = cairo_dock_create_blank_surface (
402 		ceil ((*fImageWidth) * fMaxScale),
403 		ceil ((*fImageHeight) * fMaxScale));
404 	cairo_t *pCairoContext = cairo_create (pNewSurface);
405 
406 	double fUsefulWidth = w * fIconWidthSaturationFactor;  // a part dans le cas fill && keep ratio, c'est la meme chose que fImageWidth et fImageHeight.
407 	double fUsefulHeight = h * fIconHeightSaturationFactor;
408 	_apply_orientation_and_scale (pCairoContext,
409 		iLoadingModifier,
410 		ceil ((*fImageWidth) * fMaxScale), ceil ((*fImageHeight) * fMaxScale),
411 		fMaxScale * fIconWidthSaturationFactor, fMaxScale * fIconHeightSaturationFactor,
412 		fUsefulWidth * fMaxScale, fUsefulHeight * fMaxScale);
413 
414 	cairo_set_source_surface (pCairoContext, surface_ini, 0, 0);
415 	cairo_paint (pCairoContext);
416 
417 	cairo_destroy (pCairoContext);
418 	cairo_surface_destroy (surface_ini);
419 	if (pPixbufWithAlpha != pixbuf)
420 		g_object_unref (pPixbufWithAlpha);
421 
422 	if (fZoomX != NULL)
423 		*fZoomX = fIconWidthSaturationFactor;
424 	if (fZoomY != NULL)
425 		*fZoomY = fIconHeightSaturationFactor;
426 
427 	return pNewSurface;
428 }
429 
430 
cairo_dock_create_surface_from_image(const gchar * cImagePath,double fMaxScale,int iWidthConstraint,int iHeightConstraint,CairoDockLoadImageModifier iLoadingModifier,double * fImageWidth,double * fImageHeight,double * fZoomX,double * fZoomY)431 cairo_surface_t *cairo_dock_create_surface_from_image (const gchar *cImagePath, double fMaxScale, int iWidthConstraint, int iHeightConstraint, CairoDockLoadImageModifier iLoadingModifier, double *fImageWidth, double *fImageHeight, double *fZoomX, double *fZoomY)
432 {
433 	//g_print ("%s (%s, %dx%dx%.2f, %d)\n", __func__, cImagePath, iWidthConstraint, iHeightConstraint, fMaxScale, iLoadingModifier);
434 	g_return_val_if_fail (cImagePath != NULL, NULL);
435 	GError *erreur = NULL;
436 	RsvgDimensionData rsvg_dimension_data;
437 	RsvgHandle *rsvg_handle = NULL;
438 	cairo_surface_t* surface_ini;
439 	cairo_surface_t* pNewSurface = NULL;
440 	cairo_t* pCairoContext = NULL;
441 	double fIconWidthSaturationFactor = 1.;
442 	double fIconHeightSaturationFactor = 1.;
443 
444 	//\_______________ On cherche a determiner le type de l'image. En effet, les SVG et les PNG sont charges differemment des autres.
445 	gboolean bIsSVG = FALSE, bIsPNG = FALSE, bIsXPM = FALSE;
446 	FILE *fd = fopen (cImagePath, "r");
447 	if (fd != NULL)
448 	{
449 		char buffer[8];
450 		if (fgets (buffer, 7, fd) != NULL)
451 		{
452 			if (strncmp (buffer+2, "xml", 3) == 0)
453 				bIsSVG = TRUE;
454 			else if (strncmp (buffer+1, "PNG", 3) == 0)
455 				bIsPNG = TRUE;
456 			else if (strncmp (buffer+3, "XPM", 3) == 0)
457 				bIsXPM = TRUE;
458 			//cd_debug ("  format : %d;%d;%d", bIsSVG, bIsPNG, bIsXPM);
459 		}
460 		fclose (fd);
461 	}
462 	else
463 	{
464 		cd_warning ("This file (%s) doesn't exist or is not readable.", cImagePath);
465 		return NULL;
466 	}
467 	if (! bIsSVG && ! bIsPNG && ! bIsXPM)  // sinon en desespoir de cause on se base sur l'extension.
468 	{
469 		//cd_debug ("  on se base sur l'extension en desespoir de cause.");
470 		if (g_str_has_suffix (cImagePath, ".svg"))
471 			bIsSVG = TRUE;
472 		else if (g_str_has_suffix (cImagePath, ".png"))
473 			bIsPNG = TRUE;
474 	}
475 
476 	bIsPNG = FALSE;  /// libcairo 1.6 - 1.8 est bugguee !!!...
477 	if (bIsSVG)
478 	{
479 		rsvg_handle = rsvg_handle_new_from_file (cImagePath, &erreur);
480 		if (erreur != NULL)
481 		{
482 			cd_warning (erreur->message);
483 			g_error_free (erreur);
484 			return NULL;
485 		}
486 		else
487 		{
488 			g_return_val_if_fail (rsvg_handle != NULL, NULL);
489 			rsvg_handle_get_dimensions (rsvg_handle, &rsvg_dimension_data);
490 			int w = rsvg_dimension_data.width;
491 			int h = rsvg_dimension_data.height;
492 			*fImageWidth = (gdouble) w;
493 			*fImageHeight = (gdouble) h;
494 			//g_print ("%.2fx%.2f\n", *fImageWidth, *fImageHeight);
495 			_cairo_dock_calculate_constrainted_size (fImageWidth,
496 				fImageHeight,
497 				iWidthConstraint,
498 				iHeightConstraint,
499 				iLoadingModifier,
500 				&fIconWidthSaturationFactor,
501 				&fIconHeightSaturationFactor);
502 
503 			pNewSurface = cairo_dock_create_blank_surface (
504 				ceil ((*fImageWidth) * fMaxScale),
505 				ceil ((*fImageHeight) * fMaxScale));
506 
507 			pCairoContext = cairo_create (pNewSurface);
508 			double fUsefulWidth = w * fIconWidthSaturationFactor;  // a part dans le cas fill && keep ratio, c'est la meme chose que fImageWidth et fImageHeight.
509 			double fUsefulHeight = h * fIconHeightSaturationFactor;
510 			_apply_orientation_and_scale (pCairoContext,
511 				iLoadingModifier,
512 				ceil ((*fImageWidth) * fMaxScale), ceil ((*fImageHeight) * fMaxScale),
513 				fMaxScale * fIconWidthSaturationFactor, fMaxScale * fIconHeightSaturationFactor,
514 				fUsefulWidth * fMaxScale, fUsefulHeight * fMaxScale);
515 
516 			rsvg_handle_render_cairo (rsvg_handle, pCairoContext);
517 			cairo_destroy (pCairoContext);
518 			g_object_unref (rsvg_handle);
519 		}
520 	}
521 	else if (bIsPNG)
522 	{
523 		surface_ini = cairo_image_surface_create_from_png (cImagePath);  // cree un fond noir :-(
524 		if (cairo_surface_status (surface_ini) == CAIRO_STATUS_SUCCESS)
525 		{
526 			int w = cairo_image_surface_get_width (surface_ini);
527 			int h = cairo_image_surface_get_height (surface_ini);
528 			*fImageWidth = (double) w;
529 			*fImageHeight = (double) h;
530 			_cairo_dock_calculate_constrainted_size (fImageWidth,
531 				fImageHeight,
532 				iWidthConstraint,
533 				iHeightConstraint,
534 				iLoadingModifier,
535 				&fIconWidthSaturationFactor,
536 				&fIconHeightSaturationFactor);
537 
538 			pNewSurface = cairo_dock_create_blank_surface (
539 				ceil ((*fImageWidth) * fMaxScale),
540 				ceil ((*fImageHeight) * fMaxScale));
541 			pCairoContext = cairo_create (pNewSurface);
542 			cairo_set_operator (pCairoContext, CAIRO_OPERATOR_SOURCE);
543 			cairo_set_source_rgba (pCairoContext, 0., 0., 0., 0.);
544 			cairo_paint (pCairoContext);
545 			cairo_set_operator (pCairoContext, CAIRO_OPERATOR_OVER);
546 
547 			double fUsefulWidth = w * fIconWidthSaturationFactor;  // a part dans le cas fill && keep ratio, c'est la meme chose que fImageWidth et fImageHeight.
548 			double fUsefulHeight = h * fIconHeightSaturationFactor;
549 			_apply_orientation_and_scale (pCairoContext,
550 				iLoadingModifier,
551 				ceil ((*fImageWidth) * fMaxScale), ceil ((*fImageHeight) * fMaxScale),
552 				fMaxScale * fIconWidthSaturationFactor, fMaxScale * fIconHeightSaturationFactor,
553 				fUsefulWidth * fMaxScale, fUsefulHeight * fMaxScale);
554 
555 			cairo_set_source_surface (pCairoContext, surface_ini, 0, 0);
556 			cairo_paint (pCairoContext);
557 			cairo_destroy (pCairoContext);
558 		}
559 		cairo_surface_destroy (surface_ini);
560 	}
561 	else  // le code suivant permet de charger tout type d'image, mais en fait c'est un peu idiot d'utiliser des icones n'ayant pas de transparence.
562 	{
563 		GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (cImagePath, &erreur);  // semble se baser sur l'extension pour definir le type !
564 		if (erreur != NULL)
565 		{
566 			cd_warning (erreur->message);
567 			g_error_free (erreur);
568 			return NULL;
569 		}
570 		pNewSurface = cairo_dock_create_surface_from_pixbuf (pixbuf,
571 			fMaxScale,
572 			iWidthConstraint,
573 			iHeightConstraint,
574 			iLoadingModifier,
575 			fImageWidth,
576 			fImageHeight,
577 			&fIconWidthSaturationFactor,
578 			&fIconHeightSaturationFactor);
579 		g_object_unref (pixbuf);
580 
581 	}
582 
583 	if (fZoomX != NULL)
584 		*fZoomX = fIconWidthSaturationFactor;
585 	if (fZoomY != NULL)
586 		*fZoomY = fIconHeightSaturationFactor;
587 
588 	return pNewSurface;
589 }
590 
cairo_dock_create_surface_from_image_simple(const gchar * cImageFile,double fImageWidth,double fImageHeight)591 cairo_surface_t *cairo_dock_create_surface_from_image_simple (const gchar *cImageFile, double fImageWidth, double fImageHeight)
592 {
593 	g_return_val_if_fail (cImageFile != NULL, NULL);
594 	double fImageWidth_ = fImageWidth, fImageHeight_ = fImageHeight;
595 	gchar *cImagePath;
596 	if (*cImageFile == '/')
597 		cImagePath = (gchar *)cImageFile;
598 	else
599 		cImagePath = cairo_dock_search_image_s_path (cImageFile);
600 
601 	cairo_surface_t *pSurface = cairo_dock_create_surface_from_image (cImagePath,
602 		1.,
603 		fImageWidth,
604 		fImageHeight,
605 		CAIRO_DOCK_FILL_SPACE,
606 		&fImageWidth_,
607 		&fImageHeight_,
608 		NULL,
609 		NULL);
610 	if (cImagePath != cImageFile)
611 		g_free (cImagePath);
612 	return pSurface;
613 }
614 
cairo_dock_create_surface_from_icon(const gchar * cImageFile,double fImageWidth,double fImageHeight)615 cairo_surface_t *cairo_dock_create_surface_from_icon (const gchar *cImageFile, double fImageWidth, double fImageHeight)
616 {
617 	g_return_val_if_fail (cImageFile != NULL, NULL);
618 	double fImageWidth_ = fImageWidth, fImageHeight_ = fImageHeight;
619 	gchar *cIconPath;
620 	if (*cImageFile == '/')
621 		cIconPath = (gchar *)cImageFile;
622 	else
623 		cIconPath = cairo_dock_search_icon_s_path (cImageFile, (gint) MAX (fImageWidth, fImageHeight));
624 
625 	cairo_surface_t *pSurface = cairo_dock_create_surface_from_image (cIconPath,
626 		1.,
627 		fImageWidth,
628 		fImageHeight,
629 		CAIRO_DOCK_FILL_SPACE,
630 		&fImageWidth_,
631 		&fImageHeight_,
632 		NULL,
633 		NULL);
634 	if (cIconPath != cImageFile)
635 		g_free (cIconPath);
636 	return pSurface;
637 }
638 
cairo_dock_create_surface_from_pattern(const gchar * cImageFile,double fImageWidth,double fImageHeight,double fAlpha)639 cairo_surface_t *cairo_dock_create_surface_from_pattern (const gchar *cImageFile, double fImageWidth, double fImageHeight, double fAlpha)
640 {
641 	cairo_surface_t *pNewSurface = NULL;
642 
643 	if (cImageFile != NULL)
644 	{
645 		gchar *cImagePath = cairo_dock_search_image_s_path (cImageFile);
646 		double w, h;
647 		cairo_surface_t *pPatternSurface = cairo_dock_create_surface_from_image (cImagePath,
648 			1.,
649 			0,  // no constraint on the width of the pattern => the pattern will repeat on the width
650 			fImageHeight,  // however, we want to see all the height of the pattern with no repetition => constraint on the height
651 			CAIRO_DOCK_FILL_SPACE | CAIRO_DOCK_KEEP_RATIO,
652 			&w,
653 			&h,
654 			NULL, NULL);
655 		g_free (cImagePath);
656 		if (pPatternSurface == NULL)
657 			return NULL;
658 
659 		pNewSurface = cairo_dock_create_blank_surface (
660 			fImageWidth,
661 			fImageHeight);
662 		cairo_t *pCairoContext = cairo_create (pNewSurface);
663 
664 		cairo_pattern_t* pPattern = cairo_pattern_create_for_surface (pPatternSurface);
665 		g_return_val_if_fail (cairo_pattern_status (pPattern) == CAIRO_STATUS_SUCCESS, NULL);
666 		cairo_pattern_set_extend (pPattern, CAIRO_EXTEND_REPEAT);
667 
668 		cairo_set_source (pCairoContext, pPattern);
669 		cairo_paint_with_alpha (pCairoContext, fAlpha);
670 		cairo_destroy (pCairoContext);
671 		cairo_pattern_destroy (pPattern);
672 
673 		cairo_surface_destroy (pPatternSurface);
674 	}
675 
676 	return pNewSurface;
677 }
678 
679 
680 
cairo_dock_rotate_surface(cairo_surface_t * pSurface,double fImageWidth,double fImageHeight,double fRotationAngle)681 cairo_surface_t * cairo_dock_rotate_surface (cairo_surface_t *pSurface, double fImageWidth, double fImageHeight, double fRotationAngle)
682 {
683 	g_return_val_if_fail (pSurface != NULL, NULL);
684 	if (fRotationAngle != 0)
685 	{
686 		cairo_surface_t *pNewSurfaceRotated;
687 		cairo_t *pCairoContext;
688 		if (fabs (fRotationAngle) > G_PI / 2)
689 		{
690 			pNewSurfaceRotated = cairo_dock_create_blank_surface (
691 				fImageWidth,
692 				fImageHeight);
693 			pCairoContext = cairo_create (pNewSurfaceRotated);
694 
695 			cairo_translate (pCairoContext, 0, fImageHeight);
696 			cairo_scale (pCairoContext, 1, -1);
697 		}
698 		else
699 		{
700 			pNewSurfaceRotated = cairo_dock_create_blank_surface (
701 				fImageHeight,
702 				fImageWidth);
703 			pCairoContext = cairo_create (pNewSurfaceRotated);
704 
705 			if (fRotationAngle < 0)
706 			{
707 				cairo_move_to (pCairoContext, fImageHeight, 0);
708 				cairo_rotate (pCairoContext, fRotationAngle);
709 				cairo_translate (pCairoContext, - fImageWidth, 0);
710 			}
711 			else
712 			{
713 				cairo_move_to (pCairoContext, 0, 0);
714 				cairo_rotate (pCairoContext, fRotationAngle);
715 				cairo_translate (pCairoContext, 0, - fImageHeight);
716 			}
717 		}
718 		cairo_set_source_surface (pCairoContext, pSurface, 0, 0);
719 		cairo_paint (pCairoContext);
720 
721 		cairo_destroy (pCairoContext);
722 		return pNewSurfaceRotated;
723 	}
724 	else
725 	{
726 		return NULL;
727 	}
728 }
729 
730 
cairo_dock_create_surface_from_text_full(const gchar * cText,GldiTextDescription * pTextDescription,double fMaxScale,int iMaxWidth,int * iTextWidth,int * iTextHeight)731 cairo_surface_t *cairo_dock_create_surface_from_text_full (const gchar *cText, GldiTextDescription *pTextDescription, double fMaxScale, int iMaxWidth, int *iTextWidth, int *iTextHeight)
732 {
733 	g_return_val_if_fail (cText != NULL && pTextDescription != NULL, NULL);
734 	cairo_t *pSourceContext = _get_source_context ();
735 	g_return_val_if_fail (pSourceContext != NULL && cairo_status (pSourceContext) == CAIRO_STATUS_SUCCESS, NULL);
736 
737 	//\_________________ get the font description
738 	PangoFontDescription *pDesc = gldi_text_description_get_description (pTextDescription);
739 	if (!pDesc)
740 		cd_debug ("no text desc for '%s'", cText);
741 	int iSize = gldi_text_description_get_size (pTextDescription);
742 	pango_font_description_set_absolute_size (pDesc, fMaxScale * iSize * PANGO_SCALE);
743 
744 	//\_________________ create a layout
745 	PangoLayout *pLayout = pango_cairo_create_layout (pSourceContext);
746 	pango_layout_set_font_description (pLayout, pDesc);
747 
748 	if (pTextDescription->bUseMarkup)
749 		pango_layout_set_markup (pLayout, cText, -1);
750 	else
751 		pango_layout_set_text (pLayout, cText, -1);
752 
753 	//\_________________ handle max width
754 	if (pTextDescription->fMaxRelativeWidth != 0)
755 	{
756 		int iMaxLineWidth = pTextDescription->fMaxRelativeWidth * gldi_desktop_get_width() / g_desktopGeometry.iNbScreens;  // use the mean screen width since the text might be placed anywhere on the X screen.
757 		pango_layout_set_width (pLayout, iMaxLineWidth * PANGO_SCALE);  // PANGO_WRAP_WORD by default
758 	}
759 	PangoRectangle log;
760 	pango_layout_get_pixel_extents (pLayout, NULL, &log);
761 
762 	//\_________________ load the layout into a surface
763 	gboolean bDrawBackground = ! pTextDescription->bNoDecorations;
764 	double fRadius = (pTextDescription->bUseDefaultColors ? MIN (myStyleParam.iCornerRadius * .75, iSize/2) : fMaxScale * MAX (pTextDescription->iMargin, MIN (6, iSize/2)));  // permet d'avoir un rayon meme si on n'a pas de marge.
765 	int iOutlineMargin = 2*pTextDescription->iMargin * fMaxScale + (pTextDescription->bOutlined ? 2 : 0);  // outlined => +1 tout autour des lettres.
766 	double fZoomX = ((iMaxWidth != 0 && log.width + iOutlineMargin > iMaxWidth) ? (double)iMaxWidth / (log.width + iOutlineMargin) : 1.);
767 	double fLineWidth = 1;
768 
769 	*iTextWidth = (log.width + iOutlineMargin) * fZoomX + 2*fLineWidth;  // le texte + la marge de chaque cote.
770 	if (bDrawBackground)  // quand on trace le cadre, on evite qu'avec des petits textes genre "1" on obtienne un fond tout rond.
771 	{
772 		*iTextWidth = MAX (*iTextWidth, 2 * fRadius + 10);
773 		if (iMaxWidth != 0 && *iTextWidth > iMaxWidth)
774 			*iTextWidth = iMaxWidth;
775 	}
776 	*iTextHeight = log.height + iOutlineMargin + 2*fLineWidth;
777 
778 	cairo_surface_t* pNewSurface = cairo_dock_create_blank_surface (
779 		*iTextWidth,
780 		*iTextHeight);
781 	cairo_t* pCairoContext = cairo_create (pNewSurface);
782 
783 	//\_________________ draw the background
784 	if (bDrawBackground)  // non transparent.
785 	{
786 		cairo_save (pCairoContext);
787 		double fFrameWidth = *iTextWidth - 2 * fRadius - fLineWidth;
788 		double fFrameHeight = *iTextHeight - fLineWidth;
789 		cairo_dock_draw_rounded_rectangle (pCairoContext, fRadius, fLineWidth, fFrameWidth, fFrameHeight);
790 
791 		if (pTextDescription->bUseDefaultColors)
792 			gldi_style_colors_set_bg_color (pCairoContext);
793 		else
794 			gldi_color_set_cairo (pCairoContext, &pTextDescription->fBackgroundColor);
795 		cairo_fill_preserve (pCairoContext);
796 
797 		if (pTextDescription->bUseDefaultColors)
798 			gldi_style_colors_set_line_color (pCairoContext);
799 		else
800 			gldi_color_set_cairo (pCairoContext, &pTextDescription->fLineColor);
801 		cairo_set_line_width (pCairoContext, fLineWidth);
802 		cairo_stroke (pCairoContext);
803 
804 		cairo_restore(pCairoContext);
805 	}
806 
807 	//g_print ("%s : log = %d;%d\n", cText, (int) log.x, (int) log.y);
808 	int dx = (*iTextWidth - log.width * fZoomX)/2;  // pour se centrer.
809 	int dy = (*iTextHeight - log.height)/2;  // pour se centrer.
810 	cairo_translate (pCairoContext,
811 		-log.x*fZoomX + dx,
812 		-log.y + dy);
813 
814 	//\_________________ On dessine les contours du texte.
815 	if (pTextDescription->bOutlined)
816 	{
817 		cairo_save (pCairoContext);
818 		if (fZoomX != 1)
819 			cairo_scale (pCairoContext, fZoomX, 1.);
820 		cairo_push_group (pCairoContext);
821 		cairo_set_source_rgb (pCairoContext, 0.2, 0.2, 0.2);
822 		int i;
823 		for (i = 0; i < 2; i++)
824 		{
825 			cairo_move_to (pCairoContext, 0, 2*i-1);
826 			pango_cairo_show_layout (pCairoContext, pLayout);
827 		}
828 		for (i = 0; i < 2; i++)
829 		{
830 			cairo_move_to (pCairoContext, 2*i-1, 0);
831 			pango_cairo_show_layout (pCairoContext, pLayout);
832 		}
833 		cairo_pop_group_to_source (pCairoContext);
834 		cairo_paint (pCairoContext);
835 		cairo_restore(pCairoContext);
836 	}
837 
838 	//\_________________ On remplit l'interieur du texte.
839 	if (pTextDescription->bUseDefaultColors)
840 		gldi_style_colors_set_text_color (pCairoContext);
841 	else
842 		gldi_color_set_cairo_rgb (pCairoContext, &pTextDescription->fColorStart);
843 	cairo_move_to (pCairoContext, 0, 0);
844 	if (fZoomX != 1)
845 		cairo_scale (pCairoContext, fZoomX, 1.);
846 	//if (pTextDescription->bOutlined)
847 	//	cairo_move_to (pCairoContext, 1,1);
848 	pango_cairo_show_layout (pCairoContext, pLayout);
849 
850 	cairo_destroy (pCairoContext);
851 
852 	*iTextWidth = *iTextWidth/** / fMaxScale*/;
853 	*iTextHeight = *iTextHeight/** / fMaxScale*/;
854 
855 	g_object_unref (pLayout);
856 	pango_font_description_set_absolute_size (pDesc, iSize * PANGO_SCALE);
857 	cairo_destroy (pSourceContext);
858 	return pNewSurface;
859 }
860 
861 
cairo_dock_duplicate_surface(cairo_surface_t * pSurface,double fWidth,double fHeight,double fDesiredWidth,double fDesiredHeight)862 cairo_surface_t * cairo_dock_duplicate_surface (cairo_surface_t *pSurface, double fWidth, double fHeight, double fDesiredWidth, double fDesiredHeight)
863 {
864 	g_return_val_if_fail (pSurface != NULL, NULL);
865 
866 	//\_______________ On cree la surface de la taille desiree.
867 	if (fDesiredWidth == 0)
868 		fDesiredWidth = fWidth;
869 	if (fDesiredHeight == 0)
870 		fDesiredHeight = fHeight;
871 
872 	//g_print ("%s (%.2fx%.2f -> %.2fx%.2f)\n", __func__, fWidth, fHeight, fDesiredWidth, fDesiredHeight);
873 	cairo_surface_t *pNewSurface = cairo_dock_create_blank_surface (
874 		fDesiredWidth,
875 		fDesiredHeight);
876 	cairo_t *pCairoContext = cairo_create (pNewSurface);
877 
878 	//\_______________ On plaque la surface originale dessus.
879 	cairo_set_operator (pCairoContext, CAIRO_OPERATOR_OVER);
880 	cairo_scale (pCairoContext,
881 		fDesiredWidth / fWidth,
882 		fDesiredHeight / fHeight);
883 
884 	cairo_set_source_surface (pCairoContext, pSurface, 0., 0.);
885 	cairo_paint (pCairoContext);
886 	cairo_destroy (pCairoContext);
887 
888 	return pNewSurface;
889 }
890