]> www.fi.muni.cz Git - evince.git/blob - backend/impress/r_gradient.c
Bump api and libtool versions and rename libview and libdocument libraries
[evince.git] / backend / impress / r_gradient.c
1 /* imposter (OO.org Impress viewer)
2 ** Copyright (C) 2003-2005 Gurer Ozen
3 ** This code is free software; you can redistribute it and/or
4 ** modify it under the terms of GNU General Public License.
5 */
6
7 #include <config.h>
8 #include "common.h"
9 #include "internal.h"
10 #include <math.h>
11
12 #define GRAD_LINEAR 0
13 #define GRAD_AXIAL 1
14 #define GRAD_SQUARE 2
15 #define GRAD_RECTANGULAR 3
16 #define GRAD_RADIAL 4
17 #define GRAD_ELLIPTICAL 5
18
19 typedef struct Gradient_s {
20         int type;
21         ImpColor start;
22         int start_intensity;
23         ImpColor end;
24         int end_intensity;
25         int angle;
26         int border;
27         int steps;
28         int offset_x;
29         int offset_y;
30 } Gradient;
31
32 typedef struct Rectangle_s {
33         int Left;
34         int Top;
35         int Right;
36         int Bottom;
37 } Rectangle;
38
39 static void
40 poly_rotate (ImpPoint *poly, int n, int cx, int cy, double fAngle)
41 {
42         int i;
43         long nX, nY;
44
45         for (i = 0; i < n; i++) {
46                 nX = poly->x - cx;
47                 nY = poly->y - cy;
48                 poly->x = (cos(fAngle) * nX + sin(fAngle) * nY) + cx;
49                 poly->y = - (sin(fAngle)* nX - cos(fAngle) * nY) + cy;
50                 poly++;
51         }
52 }
53
54 static void
55 r_draw_gradient_simple (ImpRenderCtx *ctx, void *drw_data, Gradient *grad)
56 {
57         Rectangle rRect = { 0, 0, ctx->pix_w - 1, ctx->pix_h - 1 };
58         Rectangle aRect, aFullRect;
59         ImpPoint poly[4], tempoly[2];
60         ImpColor gcol;
61         double fW, fH, fDX, fDY, fAngle;
62         double fScanLine, fScanInc;
63         long redSteps, greenSteps, blueSteps;
64         long nBorder;
65         int i, nSteps, nSteps2;
66         int cx, cy;
67
68         cx = rRect.Left + (rRect.Right - rRect.Left) / 2;
69         cy = rRect.Top + (rRect.Bottom - rRect.Top) / 2;
70
71         aRect = rRect;
72         aRect.Top--; aRect.Left--; aRect.Bottom++; aRect.Right++;
73         fW = rRect.Right - rRect.Left;
74         fH = rRect.Bottom - rRect.Top;
75         fAngle = (((double) grad->angle) * 3.14 / 1800.0);
76         fDX = fW * fabs (cos (fAngle)) + fH * fabs (sin (fAngle));
77         fDY = fH * fabs (cos (fAngle)) + fW * fabs (sin (fAngle));
78         fDX = (fDX - fW) * 0.5 - 0.5;
79         fDY = (fDY - fH) * 0.5 - 0.5;
80         aRect.Left -= fDX;
81         aRect.Right += fDX;
82         aRect.Top -= fDY;
83         aRect.Bottom += fDY;
84         aFullRect = aRect;
85
86         nBorder = grad->border * (aRect.Bottom - aRect.Top) / 100;
87         if (grad->type == GRAD_LINEAR) {
88                 aRect.Top += nBorder;
89         } else {
90                 nBorder >>= 1;
91                 aRect.Top += nBorder;
92                 aRect.Bottom -= nBorder;
93         }
94
95         if (aRect.Top > (aRect.Bottom - 1))
96                 aRect.Top = aRect.Bottom - 1;
97
98         poly[0].x = aFullRect.Left;
99         poly[0].y = aFullRect.Top;
100         poly[1].x = aFullRect.Right;
101         poly[1].y = aFullRect.Top;
102         poly[2].x = aRect.Right;
103         poly[2].y = aRect.Top;
104         poly[3].x = aRect.Left;
105         poly[3].y = aRect.Top;
106         poly_rotate (&poly[0], 4, cx, cy, fAngle);
107
108         redSteps = grad->end.red - grad->start.red;
109         greenSteps = grad->end.green - grad->start.green;
110         blueSteps = grad->end.blue - grad->start.blue;
111         nSteps = grad->steps;
112         if (nSteps == 0) {
113                 long mr;
114                 mr = aRect.Bottom - aRect.Top;
115                 if (mr < 50)
116                         nSteps = mr / 2;
117                 else
118                         nSteps = mr / 4;
119                 mr = abs(redSteps);
120                 if (abs(greenSteps) > mr) mr = abs(greenSteps);
121                 if (abs(blueSteps) > mr) mr = abs(blueSteps);
122                 if (mr < nSteps) nSteps = mr;
123         }
124
125         if (grad->type == GRAD_AXIAL) {
126                 if (nSteps & 1) nSteps++;
127                 nSteps2 = nSteps + 2;
128                 gcol = grad->end;
129                 redSteps <<= 1;
130                 greenSteps <<= 1;
131                 blueSteps <<= 1;
132         } else {
133                 nSteps2 = nSteps + 1;
134                 gcol = grad->start;
135         }
136
137         fScanLine = aRect.Top;
138         fScanInc  = (double)(aRect.Bottom - aRect.Top) / (double)nSteps;
139
140         for (i = 0; i < nSteps2; i++) {
141                 // draw polygon
142                 ctx->drw->set_fg_color(drw_data, &gcol);
143                 ctx->drw->draw_polygon(drw_data, 1, &poly[0], 4);
144                 // calc next polygon
145                 aRect.Top = (long)(fScanLine += fScanInc);
146                 if (i == nSteps) {
147                         tempoly[0].x = aFullRect.Left;
148                         tempoly[0].y = aFullRect.Bottom;
149                         tempoly[1].x = aFullRect.Right;
150                         tempoly[1].y = aFullRect.Bottom;
151                 } else {
152                         tempoly[0].x = aRect.Left;
153                         tempoly[0].y = aRect.Top;
154                         tempoly[1].x = aRect.Right;
155                         tempoly[1].y = aRect.Top;
156                 }
157                 poly_rotate (&tempoly[0], 2, cx, cy, fAngle);
158                 poly[0] = poly[3];
159                 poly[1] = poly[2];
160                 poly[2] = tempoly[1];
161                 poly[3] = tempoly[0];
162                 // calc next color
163                 if (grad->type == GRAD_LINEAR) {
164                         gcol.red = grad->start.red + ((redSteps * i) / nSteps2);
165                         gcol.green = grad->start.green + ((greenSteps * i) / nSteps2);
166                         gcol.blue = grad->start.blue + ((blueSteps * i) / nSteps2);
167                 } else {
168                         if (i >= nSteps) {
169                                 gcol.red = grad->end.red;
170                                 gcol.green = grad->end.green;
171                                 gcol.blue = grad->end.blue;
172                         } else {
173                                 if (i <= (nSteps / 2)) {
174                                         gcol.red = grad->end.red - ((redSteps * i) / nSteps2);
175                                         gcol.green = grad->end.green - ((greenSteps * i) / nSteps2);
176                                         gcol.blue = grad->end.blue - ((blueSteps * i) / nSteps2);
177                                 } else {
178                                         int i2 = i - nSteps / 2;
179                                         gcol.red = grad->start.red + ((redSteps * i2) / nSteps2);
180                                         gcol.green = grad->start.green + ((greenSteps * i2) / nSteps2);
181                                         gcol.blue = grad->start.blue + ((blueSteps * i2) / nSteps2);
182                                 }
183                         }
184                 }
185         }
186 }
187
188 static void
189 r_draw_gradient_complex (ImpRenderCtx *ctx, void *drw_data, Gradient *grad)
190 {
191         Rectangle rRect = { 0, 0, ctx->pix_w - 1, ctx->pix_h - 1 };
192         Rectangle aRect = rRect;
193         ImpColor gcol;
194         ImpPoint poly[4];
195         double fAngle = (((double) grad->angle) * 3.14 / 1800.0);
196         long redSteps, greenSteps, blueSteps;
197         long nZW, nZH;
198         long bX, bY;
199         long sW, sH;
200         long cx, cy;
201         int i;
202         long nSteps;
203         double sTop, sLeft, sRight, sBottom, sInc;
204         int minRect;
205
206         redSteps = grad->end.red - grad->start.red;
207         greenSteps = grad->end.green - grad->start.green;
208         blueSteps = grad->end.blue - grad->start.blue;
209
210         if (grad->type == GRAD_SQUARE || grad->type == GRAD_RECTANGULAR) {
211                 double fW = aRect.Right - aRect.Left;
212                 double fH = aRect.Bottom - aRect.Top;
213                 double fDX = fW * fabs (cos (fAngle)) + fH * fabs (sin (fAngle));
214                 double fDY = fH * fabs (cos (fAngle)) + fW * fabs (sin (fAngle));
215                 fDX = (fDX - fW) * 0.5 - 0.5;
216                 fDY = (fDY - fH) * 0.5 - 0.5;
217                 aRect.Left -= fDX;
218                 aRect.Right += fDX;
219                 aRect.Top -= fDY;
220                 aRect.Bottom += fDY;
221         }
222
223         sW = aRect.Right - aRect.Left;
224         sH = aRect.Bottom - aRect.Top;
225
226         if (grad->type == GRAD_SQUARE) {
227                 if (sW > sH) sH = sW; else sW = sH;
228         } else if (grad->type == GRAD_RADIAL) {
229                 sW = 0.5 + sqrt ((double)sW*(double)sW + (double)sH*(double)sH);
230                 sH = sW;
231         } else if (grad->type == GRAD_ELLIPTICAL) {
232                 sW = 0.5 + (double)sW * 1.4142;
233                 sH = 0.5 + (double)sH * 1.4142;
234         }
235
236         nZW = (aRect.Right - aRect.Left) * grad->offset_x / 100;
237         nZH = (aRect.Bottom - aRect.Top) * grad->offset_y / 100;
238         bX = grad->border * sW / 100;
239         bY = grad->border * sH / 100;
240         cx = aRect.Left + nZW;
241         cy = aRect.Top + nZH;
242
243         sW -= bX;
244         sH -= bY;
245
246         aRect.Left = cx - ((aRect.Right - aRect.Left) >> 1);
247         aRect.Top = cy - ((aRect.Bottom - aRect.Top) >> 1);
248
249         nSteps = grad->steps;
250         minRect = aRect.Right - aRect.Left;
251         if (aRect.Bottom - aRect.Top < minRect) minRect = aRect.Bottom - aRect.Top;
252         if (nSteps == 0) {
253                 long mr;
254                 if (minRect < 50)
255                         nSteps = minRect / 2;
256                 else
257                         nSteps = minRect / 4;
258                 mr = abs(redSteps);
259                 if (abs(greenSteps) > mr) mr = abs(greenSteps);
260                 if (abs(blueSteps) > mr) mr = abs(blueSteps);
261                 if (mr < nSteps) nSteps = mr;
262         }
263
264         sLeft = aRect.Left;
265         sTop = aRect.Top;
266         sRight = aRect.Right;
267         sBottom = aRect.Bottom;
268         sInc = (double) minRect / (double) nSteps * 0.5;
269
270         gcol = grad->start;
271         poly[0].x = rRect.Left;
272         poly[0].y = rRect.Top;
273         poly[1].x = rRect.Right;
274         poly[1].y = rRect.Top;
275         poly[2].x = rRect.Right;
276         poly[2].y = rRect.Bottom;
277         poly[3].x = rRect.Left;
278         poly[3].y = rRect.Bottom;
279         ctx->drw->set_fg_color(drw_data, &gcol);
280         ctx->drw->draw_polygon(drw_data, 1, &poly[0], 4);
281
282         for (i = 0; i < nSteps; i++) {
283                 aRect.Left = (long) (sLeft += sInc);
284                 aRect.Top = (long) (sTop += sInc);
285                 aRect.Right = (long) (sRight -= sInc);
286                 aRect.Bottom = (long) (sBottom -= sInc);
287                 if (aRect.Bottom - aRect.Top < 2 || aRect.Right - aRect.Left < 2)
288                         break;
289
290                 gcol.red = grad->start.red + (redSteps * (i+1) / nSteps);
291                 gcol.green = grad->start.green + (greenSteps * (i+1) / nSteps);
292                 gcol.blue = grad->start.blue + (blueSteps * (i+1) / nSteps);
293                 ctx->drw->set_fg_color(drw_data, &gcol);
294
295                 if (grad->type == GRAD_RADIAL || grad->type == GRAD_ELLIPTICAL) {
296                         ctx->drw->draw_arc(drw_data, 1, aRect.Left, aRect.Top,
297                                 aRect.Right - aRect.Left, aRect.Bottom - aRect.Top,
298                                 0, 360);
299                 } else {
300                         poly[0].x = aRect.Left;
301                         poly[0].y = aRect.Top;
302                         poly[1].x = aRect.Right;
303                         poly[1].y = aRect.Top;
304                         poly[2].x = aRect.Right;
305                         poly[2].y = aRect.Bottom;
306                         poly[3].x = aRect.Left;
307                         poly[3].y = aRect.Bottom;
308                         poly_rotate (&poly[0], 4, cx, cy, fAngle);
309                         ctx->drw->draw_polygon(drw_data, 1, &poly[0], 4);
310                 }
311         }
312 }
313
314 void
315 r_draw_gradient (ImpRenderCtx *ctx, void *drw_data, iks *node)
316 {
317 //      GdkGC *gc;
318         Gradient grad;
319         char *stil, *tmp;
320         iks *x;
321
322         stil = r_get_style (ctx, node, "draw:fill-gradient-name");
323         x = iks_find_with_attrib (iks_find (ctx->styles, "office:styles"),
324                 "draw:gradient", "draw:name", stil);
325         if (x) {
326                 memset (&grad, 0, sizeof (Gradient));
327                 grad.type = -1;
328                 grad.offset_x = 50;
329                 grad.offset_y = 50;
330
331                 tmp = iks_find_attrib (x, "draw:start-color");
332                 if (tmp) r_parse_color (tmp, &grad.start);
333                 tmp = iks_find_attrib (x, "draw:start-intensity");
334                 if (tmp) {
335                         int val = atoi (tmp);
336                         grad.start.red = grad.start.red * val / 100;
337                         grad.start.green = grad.start.green * val / 100;
338                         grad.start.blue = grad.start.blue * val / 100;
339                 }
340                 tmp = iks_find_attrib (x, "draw:end-color");
341                 if (tmp) r_parse_color (tmp, &grad.end);
342                 tmp = iks_find_attrib (x, "draw:end-intensity");
343                 if (tmp) {
344                         int val = atoi (tmp);
345                         grad.end.red = grad.end.red * val / 100;
346                         grad.end.green = grad.end.green * val / 100;
347                         grad.end.blue = grad.end.blue * val / 100;
348                 }
349                 tmp = iks_find_attrib (x, "draw:angle");
350                 if (tmp) grad.angle = atoi(tmp) % 3600;
351                 tmp = iks_find_attrib (x, "draw:border");
352                 if (tmp) grad.border = atoi(tmp);
353                 tmp = r_get_style (ctx, node, "draw:gradient-step-count");
354                 if (tmp) grad.steps = atoi (tmp);
355                 tmp = iks_find_attrib (x, "draw:cx");
356                 if (tmp) grad.offset_x = atoi (tmp);
357                 tmp = iks_find_attrib (x, "draw:cy");
358                 if (tmp) grad.offset_y = atoi (tmp);
359                 tmp = iks_find_attrib (x, "draw:style");
360                 if (iks_strcmp (tmp, "linear") == 0)
361                         grad.type = GRAD_LINEAR;
362                 else if (iks_strcmp (tmp, "axial") == 0)
363                         grad.type = GRAD_AXIAL;
364                 else if (iks_strcmp (tmp, "radial") == 0)
365                         grad.type = GRAD_RADIAL;
366                 else if (iks_strcmp (tmp, "rectangular") == 0)
367                         grad.type = GRAD_RECTANGULAR;
368                 else if (iks_strcmp (tmp, "ellipsoid") == 0)
369                         grad.type = GRAD_ELLIPTICAL;
370                 else if (iks_strcmp (tmp, "square") == 0)
371                         grad.type = GRAD_SQUARE;
372
373                 if (grad.type == -1) return;
374
375 //              gc = ctx->gc;
376 //              ctx->gc = gdk_gc_new (ctx->d);
377 //              gdk_gc_copy (ctx->gc, gc);
378
379                 if (grad.type == GRAD_LINEAR || grad.type == GRAD_AXIAL)
380                         r_draw_gradient_simple (ctx, drw_data, &grad);
381                 else
382                         r_draw_gradient_complex (ctx, drw_data, &grad);
383
384 //              g_object_unref (ctx->gc);
385 //              ctx->gc = gc;
386         }
387 }