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