source: trunk/third/libart_lgpl/art_render_gradient.c @ 20813

Revision 20813, 18.8 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20812, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * art_render_gradient.c: Gradient image source for modular rendering.
3 *
4 * Libart_LGPL - library of basic graphic primitives
5 * Copyright (C) 2000 Raph Levien
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library 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 GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 *
22 * Authors: Raph Levien <raph@acm.org>
23 *          Alexander Larsson <alla@lysator.liu.se>
24 */
25
26#include "config.h"
27#include "art_render_gradient.h"
28
29#include <math.h>
30#include <stdlib.h>
31#include <stdio.h>
32#include <string.h>
33#include <assert.h>
34
35/* Hack to find out how to define alloca on different platforms.
36 * Modified version of glib/galloca.h.
37 */
38
39#ifdef  __GNUC__
40/* GCC does the right thing */
41# undef alloca
42# define alloca(size)   __builtin_alloca (size)
43#elif defined (HAVE_ALLOCA_H)
44/* a native and working alloca.h is there */
45# include <alloca.h>
46#else /* !__GNUC__ && !HAVE_ALLOCA_H */
47# ifdef _MSC_VER
48#  include <malloc.h>
49#  define alloca _alloca
50# else /* !_MSC_VER */
51#  ifdef _AIX
52 #pragma alloca
53#  else /* !_AIX */
54#   ifndef alloca /* predefined by HP cc +Olibcalls */
55char *alloca ();
56#   endif /* !alloca */
57#  endif /* !_AIX */
58# endif /* !_MSC_VER */
59#endif /* !__GNUC__ && !HAVE_ALLOCA_H */
60
61#undef DEBUG_SPEW
62
63typedef struct _ArtImageSourceGradLin ArtImageSourceGradLin;
64typedef struct _ArtImageSourceGradRad ArtImageSourceGradRad;
65
66/* The stops will be copied right after this structure */
67struct _ArtImageSourceGradLin {
68  ArtImageSource super;
69  ArtGradientLinear gradient;
70  ArtGradientStop stops[1];
71};
72
73/* The stops will be copied right after this structure */
74struct _ArtImageSourceGradRad {
75  ArtImageSource super;
76  ArtGradientRadial gradient;
77  double a;
78  ArtGradientStop stops[1];
79};
80
81#define EPSILON 1e-6
82
83#ifndef MAX
84#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
85#endif /* MAX */
86
87#ifndef MIN
88#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
89#endif /* MIN */
90
91static void
92art_rgba_gradient_run (art_u8 *buf,
93                       art_u8 *color1,
94                       art_u8 *color2,
95                       int len)
96{
97  int i;
98  int r, g, b, a;
99  int dr, dg, db, da;
100
101#ifdef DEBUG_SPEW
102  printf ("gradient run from %3d %3d %3d %3d to %3d %3d %3d %3d in %d pixels\n",
103          color1[0], color1[1], color1[2], color1[3],
104          color2[0], color2[1], color2[2], color2[3],
105          len);
106#endif
107 
108  r = (color1[0] << 16) + 0x8000;
109  g = (color1[1] << 16) + 0x8000;
110  b = (color1[2] << 16) + 0x8000;
111  a = (color1[3] << 16) + 0x8000;
112  dr = ((color2[0] - color1[0]) << 16) / len;
113  dg = ((color2[1] - color1[1]) << 16) / len;
114  db = ((color2[2] - color1[2]) << 16) / len;
115  da = ((color2[3] - color1[3]) << 16) / len;
116
117  for (i = 0; i < len; i++)
118    {
119      *buf++ = (r>>16);
120      *buf++ = (g>>16);
121      *buf++ = (b>>16);
122      *buf++ = (a>>16);
123
124      r += dr;
125      g += dg;
126      b += db;
127      a += da;
128    }
129}
130
131static void
132calc_color_at (ArtGradientStop *stops,
133               int n_stops,
134               ArtGradientSpread spread,
135               double offset,
136               double offset_fraction,
137               int favor_start,
138               int ix,
139               art_u8 *color)
140{
141  double off0, off1;
142  int j;
143 
144  if (spread == ART_GRADIENT_PAD)
145    {
146      if (offset < 0.0)
147        {
148          color[0] = ART_PIX_8_FROM_MAX (stops[0].color[0]);
149          color[1] = ART_PIX_8_FROM_MAX (stops[0].color[1]);
150          color[2] = ART_PIX_8_FROM_MAX (stops[0].color[2]);
151          color[3] = ART_PIX_8_FROM_MAX (stops[0].color[3]);
152          return;
153        }
154      if (offset >= 1.0)
155        {
156          color[0] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[0]);
157          color[1] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[1]);
158          color[2] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[2]);
159          color[3] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[3]);
160          return;
161        }
162    }
163
164  if (ix > 0 && ix < n_stops)
165    {
166      off0 = stops[ix - 1].offset;
167      off1 = stops[ix].offset;
168      if (fabs (off1 - off0) > EPSILON)
169        {
170          double interp;
171          double o;
172          o = offset_fraction;
173         
174          if ((fabs (o) < EPSILON) && (!favor_start))
175            o = 1.0;
176          else if ((fabs (o-1.0) < EPSILON) && (favor_start))
177            o = 0.0;
178         
179          /*
180          if (offset_fraction == 0.0  && !favor_start)
181            offset_fraction = 1.0;
182          */
183         
184          interp = (o - off0) / (off1 - off0);
185          for (j = 0; j < 4; j++)
186            {
187              int z0, z1;
188              int z;
189              z0 = stops[ix - 1].color[j];
190              z1 = stops[ix].color[j];
191              z = floor (z0 + (z1 - z0) * interp + 0.5);
192              color[j] = ART_PIX_8_FROM_MAX (z);
193            }
194          return;
195        }
196      /* If offsets are too close to safely do the division, just
197         pick the ix color. */
198      color[0] = ART_PIX_8_FROM_MAX (stops[ix].color[0]);
199      color[1] = ART_PIX_8_FROM_MAX (stops[ix].color[1]);
200      color[2] = ART_PIX_8_FROM_MAX (stops[ix].color[2]);
201      color[3] = ART_PIX_8_FROM_MAX (stops[ix].color[3]);
202      return;
203    }
204
205  printf ("WARNING! bad ix %d in calc_color_at() [internal error]\n", ix);
206  assert (0);
207}
208
209static void
210art_render_gradient_linear_render_8 (ArtRenderCallback *self,
211                                     ArtRender *render,
212                                     art_u8 *dest, int y)
213{
214  ArtImageSourceGradLin *z = (ArtImageSourceGradLin *)self;
215  const ArtGradientLinear *gradient = &(z->gradient);
216  int i;
217  int width = render->x1 - render->x0;
218  int len;
219  double offset, d_offset;
220  double offset_fraction;
221  int next_stop;
222  int ix;
223  art_u8 color1[4], color2[4];
224  int n_stops = gradient->n_stops;
225  int extra_stops;
226  ArtGradientStop *stops = gradient->stops;
227  ArtGradientStop *tmp_stops;
228  art_u8 *bufp = render->image_buf;
229  ArtGradientSpread spread = gradient->spread;
230
231#ifdef DEBUG_SPEW
232  printf ("x1: %d, x2: %d, y: %d\n", render->x0, render->x1, y);
233  printf ("spread: %d, stops:", gradient->spread);
234  for (i=0;i<n_stops;i++)
235    {
236      printf ("%f, ", gradient->stops[i].offset);
237    }
238  printf ("\n");
239  printf ("a: %f, b: %f, c: %f\n", gradient->a, gradient->b, gradient->c);
240#endif
241 
242  offset = render->x0 * gradient->a + y * gradient->b + gradient->c;
243  d_offset = gradient->a;
244
245  /* We need to force the gradient to extend the whole 0..1 segment,
246     because the rest of the code doesn't handle partial gradients
247     correctly */
248  if ((gradient->stops[0].offset > EPSILON /* == 0.0 */) ||
249      (gradient->stops[n_stops-1].offset < (1.0 - EPSILON)))
250  {
251    extra_stops = 0;
252    tmp_stops = stops = alloca (sizeof (ArtGradientStop) * (n_stops + 2));
253    if (gradient->stops[0].offset > EPSILON /* 0.0 */)
254      {
255        memcpy (tmp_stops, gradient->stops, sizeof (ArtGradientStop));
256        tmp_stops[0].offset = 0.0;
257        tmp_stops += 1;
258        extra_stops++;
259      }
260    memcpy (tmp_stops, gradient->stops, sizeof (ArtGradientStop) * n_stops);
261    if (gradient->stops[n_stops-1].offset < (1.0 - EPSILON))
262      {
263        tmp_stops += n_stops;
264        memcpy (tmp_stops, &gradient->stops[n_stops-1], sizeof (ArtGradientStop));
265        tmp_stops[0].offset = 1.0;
266        extra_stops++;
267      }
268    n_stops += extra_stops;
269
270
271#ifdef DEBUG_SPEW
272    printf ("start/stop modified stops:");
273    for (i=0;i<n_stops;i++)
274      {
275        printf ("%f, ", stops[i].offset);
276      }
277    printf ("\n");
278#endif
279
280  }
281 
282  if (spread == ART_GRADIENT_REFLECT)
283    {
284      tmp_stops = stops;
285      stops = alloca (sizeof (ArtGradientStop) * n_stops * 2);
286      memcpy (stops, tmp_stops, sizeof (ArtGradientStop) * n_stops);
287
288      for (i = 0; i< n_stops; i++)
289        {
290          stops[n_stops * 2 - 1 - i].offset = (1.0 - stops[i].offset / 2.0);
291          memcpy (stops[n_stops * 2 - 1 - i].color, stops[i].color, sizeof (stops[i].color));
292          stops[i].offset = stops[i].offset / 2.0;
293        }
294     
295      spread = ART_GRADIENT_REPEAT;
296      offset = offset / 2.0;
297      d_offset = d_offset / 2.0;
298     
299      n_stops = 2 * n_stops;
300
301#ifdef DEBUG_SPEW
302      printf ("reflect modified stops:");
303      for (i=0;i<n_stops;i++)
304        {
305          printf ("%f, ", stops[i].offset);
306        }
307      printf ("\n");
308#endif
309    }
310
311  offset_fraction = offset - floor (offset);
312#ifdef DEBUG_SPEW
313  printf ("inital offset: %f, fraction: %f d_offset: %f\n", offset, offset_fraction, d_offset);
314#endif
315  /* ix is selected so that offset_fraction is
316     stops[ix-1] <= offset_fraction <= stops[ix]
317     If offset_fraction is equal to one of the edges, ix
318     is selected so the the section of the line extending
319     in the same direction as d_offset is between ix-1 and ix.
320  */
321  for (ix = 0; ix < n_stops; ix++)
322    if (stops[ix].offset > offset_fraction ||
323        (d_offset < 0.0 && fabs (stops[ix].offset - offset_fraction) < EPSILON))
324      break;
325  if (ix == 0)
326    ix = n_stops - 1;
327  else if (ix == n_stops)
328    ix = n_stops - 1;
329
330#ifdef DEBUG_SPEW
331  printf ("Initial ix: %d\n", ix);
332#endif
333 
334  assert (ix > 0);
335  assert (ix < n_stops);
336  assert ((stops[ix-1].offset <= offset_fraction + EPSILON) ||
337          ((stops[ix].offset > (1.0 - EPSILON)) && (offset_fraction < EPSILON /* == 0.0*/)));
338  assert (offset_fraction <= stops[ix].offset);
339  /* FIXME: These asserts may be broken, it is for now
340     safer to not use them.  Should be fixed!
341     See bug #121850
342  assert ((offset_fraction != stops[ix-1].offset) ||
343          (d_offset >= 0.0));
344  assert ((offset_fraction != stops[ix].offset) ||
345          (d_offset <= 0.0));
346  */
347 
348  while (width > 0)
349    {
350#ifdef DEBUG_SPEW
351      printf ("ix: %d\n", ix);
352      printf ("start offset: %f\n", offset);
353#endif
354      calc_color_at (stops, n_stops,
355                     spread,
356                     offset,
357                     offset_fraction,
358                     (d_offset > -EPSILON),
359                     ix,
360                     color1);
361
362      if (d_offset > 0)
363        next_stop = ix;
364      else
365        next_stop = ix-1;
366
367#ifdef DEBUG_SPEW
368      printf ("next_stop: %d\n", next_stop);
369#endif
370      if (fabs (d_offset) > EPSILON)
371        {
372          double o;
373          o = offset_fraction;
374         
375          if ((fabs (o) <= EPSILON) && (ix == n_stops - 1))
376            o = 1.0;
377          else if ((fabs (o-1.0) <= EPSILON) && (ix == 1))
378            o = 0.0;
379
380#ifdef DEBUG_SPEW
381          printf ("o: %f\n", o);
382#endif
383          len = (int)floor (fabs ((stops[next_stop].offset - o) / d_offset)) + 1;
384          len = MAX (len, 0);
385          len = MIN (len, width);
386        }
387      else
388        {
389          len = width;
390        }
391#ifdef DEBUG_SPEW
392      printf ("len: %d\n", len);
393#endif
394      if (len > 0)
395        {
396          offset = offset + (len-1) * d_offset;
397          offset_fraction = offset - floor (offset);
398#ifdef DEBUG_SPEW
399          printf ("end offset: %f, fraction: %f\n", offset, offset_fraction);
400#endif   
401          calc_color_at (stops, n_stops,
402                         spread,
403                         offset,
404                         offset_fraction,
405                         (d_offset < EPSILON),
406                         ix,
407                         color2);
408         
409          art_rgba_gradient_run (bufp,
410                                 color1,
411                                 color2,
412                                 len);
413          offset += d_offset;
414          offset_fraction = offset - floor (offset);
415        }
416
417      if (d_offset > 0)
418        {
419          do
420            {
421              ix++;
422              if (ix == n_stops)
423                ix = 1;
424              /* Note: offset_fraction can actually be one here on x86 machines that
425                 does calculations with extended precision, but later rounds to 64bit.
426                 This happens if the 80bit offset_fraction is larger than the
427                 largest 64bit double that is less than one.
428              */
429            }
430          while (!((stops[ix-1].offset <= offset_fraction &&
431                   offset_fraction < stops[ix].offset) ||
432                   (ix == 1 && offset_fraction > (1.0 - EPSILON))));
433        }
434      else
435        {
436          do
437            {
438              ix--;
439              if (ix == 0)
440                ix = n_stops - 1;
441            }
442          while (!((stops[ix-1].offset < offset_fraction &&
443                    offset_fraction <= stops[ix].offset) ||
444                   (ix == n_stops - 1 && offset_fraction < EPSILON /* == 0.0*/)));
445        }
446     
447      bufp += 4*len;
448      width -= len;
449    }
450}
451
452
453/**
454 * art_render_gradient_setpix: Set a gradient pixel.
455 * @render: The render object.
456 * @dst: Pointer to destination (where to store pixel).
457 * @n_stops: Number of stops in @stops.
458 * @stops: The stops for the gradient.
459 * @offset: The offset.
460 *
461 * @n_stops must be > 0.
462 *
463 * Sets a gradient pixel, storing it at @dst.
464 **/
465static void
466art_render_gradient_setpix (ArtRender *render,
467                            art_u8 *dst,
468                            int n_stops, ArtGradientStop *stops,
469                            double offset)
470{
471  int ix;
472  int j;
473  double off0, off1;
474  int n_ch = render->n_chan + 1;
475
476  for (ix = 0; ix < n_stops; ix++)
477    if (stops[ix].offset > offset)
478      break;
479  /* stops[ix - 1].offset < offset < stops[ix].offset */
480  if (ix > 0 && ix < n_stops)
481    {
482      off0 = stops[ix - 1].offset;
483      off1 = stops[ix].offset;
484      if (fabs (off1 - off0) > EPSILON)
485        {
486          double interp;
487
488          interp = (offset - off0) / (off1 - off0);
489          for (j = 0; j < n_ch; j++)
490            {
491              int z0, z1;
492              int z;
493              z0 = stops[ix - 1].color[j];
494              z1 = stops[ix].color[j];
495              z = floor (z0 + (z1 - z0) * interp + 0.5);
496              if (render->buf_depth == 8)
497                dst[j] = ART_PIX_8_FROM_MAX (z);
498              else /* (render->buf_depth == 16) */
499                ((art_u16 *)dst)[j] = z;
500            }
501          return;
502        }
503    }
504  else if (ix == n_stops)
505    ix--;
506
507  for (j = 0; j < n_ch; j++)
508    {
509      int z;
510      z = stops[ix].color[j];
511      if (render->buf_depth == 8)
512        dst[j] = ART_PIX_8_FROM_MAX (z);
513      else /* (render->buf_depth == 16) */
514        ((art_u16 *)dst)[j] = z;
515    }
516}
517
518static void
519art_render_gradient_linear_done (ArtRenderCallback *self, ArtRender *render)
520{
521  art_free (self);
522}
523
524static void
525art_render_gradient_linear_render (ArtRenderCallback *self, ArtRender *render,
526                                   art_u8 *dest, int y)
527{
528  ArtImageSourceGradLin *z = (ArtImageSourceGradLin *)self;
529  const ArtGradientLinear *gradient = &(z->gradient);
530  int pixstride = (render->n_chan + 1) * (render->depth >> 3);
531  int x;
532  int width = render->x1 - render->x0;
533  double offset, d_offset;
534  double actual_offset;
535  int n_stops = gradient->n_stops;
536  ArtGradientStop *stops = gradient->stops;
537  art_u8 *bufp = render->image_buf;
538  ArtGradientSpread spread = gradient->spread;
539
540  offset = render->x0 * gradient->a + y * gradient->b + gradient->c;
541  d_offset = gradient->a;
542
543  for (x = 0; x < width; x++)
544    {
545      if (spread == ART_GRADIENT_PAD)
546        actual_offset = offset;
547      else if (spread == ART_GRADIENT_REPEAT)
548        actual_offset = offset - floor (offset);
549      else /* (spread == ART_GRADIENT_REFLECT) */
550        {
551          double tmp;
552
553          tmp = offset - 2 * floor (0.5 * offset);
554          actual_offset = tmp > 1 ? 2 - tmp : tmp;
555        }
556      art_render_gradient_setpix (render, bufp, n_stops, stops, actual_offset);
557      offset += d_offset;
558      bufp += pixstride;
559    }
560}
561
562static void
563art_render_gradient_linear_negotiate (ArtImageSource *self, ArtRender *render,
564                                      ArtImageSourceFlags *p_flags,
565                                      int *p_buf_depth, ArtAlphaType *p_alpha)
566{
567  if (render->depth == 8 &&
568      render->n_chan == 3)
569    {
570      self->super.render = art_render_gradient_linear_render_8;
571      *p_flags = 0;
572      *p_buf_depth = 8;
573      *p_alpha = ART_ALPHA_PREMUL;
574      return;
575    }
576 
577  self->super.render = art_render_gradient_linear_render;
578  *p_flags = 0;
579  *p_buf_depth = render->depth;
580  *p_alpha = ART_ALPHA_PREMUL;
581}
582
583/**
584 * art_render_gradient_linear: Add a linear gradient image source.
585 * @render: The render object.
586 * @gradient: The linear gradient.
587 *
588 * Adds the linear gradient @gradient as the image source for rendering
589 * in the render object @render.
590 **/
591void
592art_render_gradient_linear (ArtRender *render,
593                            const ArtGradientLinear *gradient,
594                            ArtFilterLevel level)
595{
596  ArtImageSourceGradLin *image_source = art_alloc (sizeof (ArtImageSourceGradLin) +
597                                                   sizeof (ArtGradientStop) * (gradient->n_stops - 1));
598
599  image_source->super.super.render = NULL;
600  image_source->super.super.done = art_render_gradient_linear_done;
601  image_source->super.negotiate = art_render_gradient_linear_negotiate;
602
603  /* copy the gradient into the structure */
604  image_source->gradient = *gradient;
605  image_source->gradient.stops = image_source->stops;
606  memcpy (image_source->gradient.stops, gradient->stops, sizeof (ArtGradientStop) * gradient->n_stops);
607
608  art_render_add_image_source (render, &image_source->super);
609}
610
611static void
612art_render_gradient_radial_done (ArtRenderCallback *self, ArtRender *render)
613{
614  art_free (self);
615}
616
617static void
618art_render_gradient_radial_render (ArtRenderCallback *self, ArtRender *render,
619                                   art_u8 *dest, int y)
620{
621  ArtImageSourceGradRad *z = (ArtImageSourceGradRad *)self;
622  const ArtGradientRadial *gradient = &(z->gradient);
623  int pixstride = (render->n_chan + 1) * (render->depth >> 3);
624  int x;
625  int x0 = render->x0;
626  int width = render->x1 - x0;
627  int n_stops = gradient->n_stops;
628  ArtGradientStop *stops = gradient->stops;
629  art_u8 *bufp = render->image_buf;
630  double fx = gradient->fx;
631  double fy = gradient->fy;
632  double dx, dy;
633  double *affine = gradient->affine;
634  double aff0 = affine[0];
635  double aff1 = affine[1];
636  const double a = z->a;
637  const double arecip = 1.0 / a;
638  double b, db;
639  double c, dc, ddc;
640  double b_a, db_a;
641  double rad, drad, ddrad;
642
643  dx = x0 * aff0 + y * affine[2] + affine[4] - fx;
644  dy = x0 * aff1 + y * affine[3] + affine[5] - fy;
645  b = dx * fx + dy * fy;
646  db = aff0 * fx + aff1 * fy;
647  c = dx * dx + dy * dy;
648  dc = 2 * aff0 * dx + aff0 * aff0 + 2 * aff1 * dy + aff1 * aff1;
649  ddc = 2 * aff0 * aff0 + 2 * aff1 * aff1;
650
651  b_a = b * arecip;
652  db_a = db * arecip;
653
654  rad = b_a * b_a + c * arecip;
655  drad = 2 * b_a * db_a + db_a * db_a + dc * arecip;
656  ddrad = 2 * db_a * db_a + ddc * arecip;
657
658  for (x = 0; x < width; x++)
659    {
660      double z;
661
662      if (rad > 0)
663        z = b_a + sqrt (rad);
664      else
665        z = b_a;
666      art_render_gradient_setpix (render, bufp, n_stops, stops, z);
667      bufp += pixstride;
668      b_a += db_a;
669      rad += drad;
670      drad += ddrad;
671    }
672}
673
674static void
675art_render_gradient_radial_negotiate (ArtImageSource *self, ArtRender *render,
676                                      ArtImageSourceFlags *p_flags,
677                                      int *p_buf_depth, ArtAlphaType *p_alpha)
678{
679  self->super.render = art_render_gradient_radial_render;
680  *p_flags = 0;
681  *p_buf_depth = render->depth;
682  *p_alpha = ART_ALPHA_PREMUL;
683}
684
685/**
686 * art_render_gradient_radial: Add a radial gradient image source.
687 * @render: The render object.
688 * @gradient: The radial gradient.
689 *
690 * Adds the radial gradient @gradient as the image source for rendering
691 * in the render object @render.
692 **/
693void
694art_render_gradient_radial (ArtRender *render,
695                            const ArtGradientRadial *gradient,
696                            ArtFilterLevel level)
697{
698  ArtImageSourceGradRad *image_source = art_alloc (sizeof (ArtImageSourceGradRad) +
699                                                   sizeof (ArtGradientStop) * (gradient->n_stops - 1));
700  double fx = gradient->fx;
701  double fy = gradient->fy;
702
703  image_source->super.super.render = NULL;
704  image_source->super.super.done = art_render_gradient_radial_done;
705  image_source->super.negotiate = art_render_gradient_radial_negotiate;
706
707  /* copy the gradient into the structure */
708  image_source->gradient = *gradient;
709  image_source->gradient.stops = image_source->stops;
710  memcpy (image_source->gradient.stops, gradient->stops, sizeof (ArtGradientStop) * gradient->n_stops);
711
712  /* todo: sanitycheck fx, fy? */
713  image_source->a = 1 - fx * fx - fy * fy;
714
715  art_render_add_image_source (render, &image_source->super);
716}
Note: See TracBrowser for help on using the repository browser.