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