Coverage Report

Created: 2025-07-23 08:13

/src/cairo/subprojects/pixman-0.44.2/pixman/pixman-radial-gradient.c
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */
2
/*
3
 *
4
 * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
5
 * Copyright © 2000 SuSE, Inc.
6
 *             2005 Lars Knoll & Zack Rusin, Trolltech
7
 * Copyright © 2007 Red Hat, Inc.
8
 *
9
 *
10
 * Permission to use, copy, modify, distribute, and sell this software and its
11
 * documentation for any purpose is hereby granted without fee, provided that
12
 * the above copyright notice appear in all copies and that both that
13
 * copyright notice and this permission notice appear in supporting
14
 * documentation, and that the name of Keith Packard not be used in
15
 * advertising or publicity pertaining to distribution of the software without
16
 * specific, written prior permission.  Keith Packard makes no
17
 * representations about the suitability of this software for any purpose.  It
18
 * is provided "as is" without express or implied warranty.
19
 *
20
 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
21
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
22
 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
23
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
24
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
25
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
26
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
27
 * SOFTWARE.
28
 */
29
30
#ifdef HAVE_CONFIG_H
31
#include <pixman-config.h>
32
#endif
33
#include <stdlib.h>
34
#include <math.h>
35
#include "pixman-private.h"
36
37
static inline pixman_fixed_32_32_t
38
dot (pixman_fixed_48_16_t x1,
39
     pixman_fixed_48_16_t y1,
40
     pixman_fixed_48_16_t z1,
41
     pixman_fixed_48_16_t x2,
42
     pixman_fixed_48_16_t y2,
43
     pixman_fixed_48_16_t z2)
44
0
{
45
    /*
46
     * Exact computation, assuming that the input values can
47
     * be represented as pixman_fixed_16_16_t
48
     */
49
0
    return x1 * x2 + y1 * y2 + z1 * z2;
50
0
}
51
52
static inline double
53
fdot (double x1,
54
      double y1,
55
      double z1,
56
      double x2,
57
      double y2,
58
      double z2)
59
0
{
60
    /*
61
     * Error can be unbound in some special cases.
62
     * Using clever dot product algorithms (for example compensated
63
     * dot product) would improve this but make the code much less
64
     * obvious
65
     */
66
0
    return x1 * x2 + y1 * y2 + z1 * z2;
67
0
}
68
69
static void
70
radial_write_color (double                         a,
71
        double                         b,
72
        double                         c,
73
        double                         inva,
74
        double                         dr,
75
        double                         mindr,
76
        pixman_gradient_walker_t      *walker,
77
        pixman_repeat_t                repeat,
78
        int                            Bpp,
79
        pixman_gradient_walker_write_t write_pixel,
80
        uint32_t                      *buffer)
81
0
{
82
    /*
83
     * In this function error propagation can lead to bad results:
84
     *  - discr can have an unbound error (if b*b-a*c is very small),
85
     *    potentially making it the opposite sign of what it should have been
86
     *    (thus clearing a pixel that would have been colored or vice-versa)
87
     *    or propagating the error to sqrtdiscr;
88
     *    if discr has the wrong sign or b is very small, this can lead to bad
89
     *    results
90
     *
91
     *  - the algorithm used to compute the solutions of the quadratic
92
     *    equation is not numerically stable (but saves one division compared
93
     *    to the numerically stable one);
94
     *    this can be a problem if a*c is much smaller than b*b
95
     *
96
     *  - the above problems are worse if a is small (as inva becomes bigger)
97
     */
98
0
    double discr;
99
100
0
    if (a == 0)
101
0
    {
102
0
  double t;
103
104
0
  if (b == 0)
105
0
  {
106
0
      memset (buffer, 0, Bpp);
107
0
      return;
108
0
  }
109
110
0
  t = pixman_fixed_1 / 2 * c / b;
111
0
  if (repeat == PIXMAN_REPEAT_NONE)
112
0
  {
113
0
      if (0 <= t && t <= pixman_fixed_1)
114
0
      {
115
0
    write_pixel (walker, t, buffer);
116
0
    return;
117
0
      }
118
0
  }
119
0
  else
120
0
  {
121
0
      if (t * dr >= mindr)
122
0
      {
123
0
    write_pixel (walker, t, buffer);
124
0
    return;
125
0
      }
126
0
  }
127
128
0
  memset (buffer, 0, Bpp);
129
0
  return;
130
0
    }
131
132
0
    discr = fdot (b, a, 0, b, -c, 0);
133
0
    if (discr >= 0)
134
0
    {
135
0
  double sqrtdiscr, t0, t1;
136
137
0
  sqrtdiscr = sqrt (discr);
138
0
  t0 = (b + sqrtdiscr) * inva;
139
0
  t1 = (b - sqrtdiscr) * inva;
140
141
  /*
142
   * The root that must be used is the biggest one that belongs
143
   * to the valid range ([0,1] for PIXMAN_REPEAT_NONE, any
144
   * solution that results in a positive radius otherwise).
145
   *
146
   * If a > 0, t0 is the biggest solution, so if it is valid, it
147
   * is the correct result.
148
   *
149
   * If a < 0, only one of the solutions can be valid, so the
150
   * order in which they are tested is not important.
151
   */
152
0
  if (repeat == PIXMAN_REPEAT_NONE)
153
0
  {
154
0
      if (0 <= t0 && t0 <= pixman_fixed_1)
155
0
      {
156
0
    write_pixel (walker, t0, buffer);
157
0
    return;
158
0
      }
159
0
      else if (0 <= t1 && t1 <= pixman_fixed_1)
160
0
      {
161
0
    write_pixel (walker, t1, buffer);
162
0
    return;
163
0
           }
164
0
  }
165
0
  else
166
0
  {
167
0
      if (t0 * dr >= mindr)
168
0
      {
169
0
    write_pixel (walker, t0, buffer);
170
0
    return;
171
0
      }
172
0
      else if (t1 * dr >= mindr)
173
0
      {
174
0
    write_pixel (walker, t1, buffer);
175
0
    return;
176
0
      }
177
0
  }
178
0
    }
179
180
0
    memset (buffer, 0, Bpp);
181
0
    return;
182
0
}
183
184
static uint32_t *
185
radial_get_scanline (pixman_iter_t                 *iter,
186
         const uint32_t                *mask,
187
         int                            Bpp,
188
         pixman_gradient_walker_write_t write_pixel)
189
0
{
190
    /*
191
     * Implementation of radial gradients following the PDF specification.
192
     * See section 8.7.4.5.4 Type 3 (Radial) Shadings of the PDF Reference
193
     * Manual (PDF 32000-1:2008 at the time of this writing).
194
     *
195
     * In the radial gradient problem we are given two circles (c₁,r₁) and
196
     * (c₂,r₂) that define the gradient itself.
197
     *
198
     * Mathematically the gradient can be defined as the family of circles
199
     *
200
     *     ((1-t)·c₁ + t·(c₂), (1-t)·r₁ + t·r₂)
201
     *
202
     * excluding those circles whose radius would be < 0. When a point
203
     * belongs to more than one circle, the one with a bigger t is the only
204
     * one that contributes to its color. When a point does not belong
205
     * to any of the circles, it is transparent black, i.e. RGBA (0, 0, 0, 0).
206
     * Further limitations on the range of values for t are imposed when
207
     * the gradient is not repeated, namely t must belong to [0,1].
208
     *
209
     * The graphical result is the same as drawing the valid (radius > 0)
210
     * circles with increasing t in [-inf, +inf] (or in [0,1] if the gradient
211
     * is not repeated) using SOURCE operator composition.
212
     *
213
     * It looks like a cone pointing towards the viewer if the ending circle
214
     * is smaller than the starting one, a cone pointing inside the page if
215
     * the starting circle is the smaller one and like a cylinder if they
216
     * have the same radius.
217
     *
218
     * What we actually do is, given the point whose color we are interested
219
     * in, compute the t values for that point, solving for t in:
220
     *
221
     *     length((1-t)·c₁ + t·(c₂) - p) = (1-t)·r₁ + t·r₂
222
     *
223
     * Let's rewrite it in a simpler way, by defining some auxiliary
224
     * variables:
225
     *
226
     *     cd = c₂ - c₁
227
     *     pd = p - c₁
228
     *     dr = r₂ - r₁
229
     *     length(t·cd - pd) = r₁ + t·dr
230
     *
231
     * which actually means
232
     *
233
     *     hypot(t·cdx - pdx, t·cdy - pdy) = r₁ + t·dr
234
     *
235
     * or
236
     *
237
     *     ⎷((t·cdx - pdx)² + (t·cdy - pdy)²) = r₁ + t·dr.
238
     *
239
     * If we impose (as stated earlier) that r₁ + t·dr >= 0, it becomes:
240
     *
241
     *     (t·cdx - pdx)² + (t·cdy - pdy)² = (r₁ + t·dr)²
242
     *
243
     * where we can actually expand the squares and solve for t:
244
     *
245
     *     t²cdx² - 2t·cdx·pdx + pdx² + t²cdy² - 2t·cdy·pdy + pdy² =
246
     *       = r₁² + 2·r₁·t·dr + t²·dr²
247
     *
248
     *     (cdx² + cdy² - dr²)t² - 2(cdx·pdx + cdy·pdy + r₁·dr)t +
249
     *         (pdx² + pdy² - r₁²) = 0
250
     *
251
     *     A = cdx² + cdy² - dr²
252
     *     B = pdx·cdx + pdy·cdy + r₁·dr
253
     *     C = pdx² + pdy² - r₁²
254
     *     At² - 2Bt + C = 0
255
     *
256
     * The solutions (unless the equation degenerates because of A = 0) are:
257
     *
258
     *     t = (B ± ⎷(B² - A·C)) / A
259
     *
260
     * The solution we are going to prefer is the bigger one, unless the
261
     * radius associated to it is negative (or it falls outside the valid t
262
     * range).
263
     *
264
     * Additional observations (useful for optimizations):
265
     * A does not depend on p
266
     *
267
     * A < 0 <=> one of the two circles completely contains the other one
268
     *   <=> for every p, the radiuses associated with the two t solutions
269
     *       have opposite sign
270
     */
271
0
    pixman_image_t *image = iter->image;
272
0
    int x = iter->x;
273
0
    int y = iter->y;
274
0
    int width = iter->width;
275
0
    uint32_t *buffer = iter->buffer;
276
277
0
    gradient_t *gradient = (gradient_t *)image;
278
0
    radial_gradient_t *radial = (radial_gradient_t *)image;
279
0
    uint32_t *end = buffer + width * (Bpp / 4);
280
0
    pixman_gradient_walker_t walker;
281
0
    pixman_vector_t v, unit;
282
283
    /* reference point is the center of the pixel */
284
0
    v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2;
285
0
    v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2;
286
0
    v.vector[2] = pixman_fixed_1;
287
288
0
    _pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
289
290
0
    if (image->common.transform)
291
0
    {
292
0
  if (!pixman_transform_point_3d (image->common.transform, &v))
293
0
      return iter->buffer;
294
295
0
  unit.vector[0] = image->common.transform->matrix[0][0];
296
0
  unit.vector[1] = image->common.transform->matrix[1][0];
297
0
  unit.vector[2] = image->common.transform->matrix[2][0];
298
0
    }
299
0
    else
300
0
    {
301
0
  unit.vector[0] = pixman_fixed_1;
302
0
  unit.vector[1] = 0;
303
0
  unit.vector[2] = 0;
304
0
    }
305
306
0
    if (unit.vector[2] == 0 && v.vector[2] == pixman_fixed_1)
307
0
    {
308
  /*
309
   * Given:
310
   *
311
   * t = (B ± ⎷(B² - A·C)) / A
312
   *
313
   * where
314
   *
315
   * A = cdx² + cdy² - dr²
316
   * B = pdx·cdx + pdy·cdy + r₁·dr
317
   * C = pdx² + pdy² - r₁²
318
   * det = B² - A·C
319
   *
320
   * Since we have an affine transformation, we know that (pdx, pdy)
321
   * increase linearly with each pixel,
322
   *
323
   * pdx = pdx₀ + n·ux,
324
   * pdy = pdy₀ + n·uy,
325
   *
326
   * we can then express B, C and det through multiple differentiation.
327
   */
328
0
  pixman_fixed_32_32_t b, db, c, dc, ddc;
329
330
  /* warning: this computation may overflow */
331
0
  v.vector[0] -= radial->c1.x;
332
0
  v.vector[1] -= radial->c1.y;
333
334
  /*
335
   * B and C are computed and updated exactly.
336
   * If fdot was used instead of dot, in the worst case it would
337
   * lose 11 bits of precision in each of the multiplication and
338
   * summing up would zero out all the bit that were preserved,
339
   * thus making the result 0 instead of the correct one.
340
   * This would mean a worst case of unbound relative error or
341
   * about 2^10 absolute error
342
   */
343
0
  b = dot (v.vector[0], v.vector[1], radial->c1.radius,
344
0
     radial->delta.x, radial->delta.y, radial->delta.radius);
345
0
  db = dot (unit.vector[0], unit.vector[1], 0,
346
0
      radial->delta.x, radial->delta.y, 0);
347
348
0
  c = dot (v.vector[0], v.vector[1],
349
0
     -((pixman_fixed_48_16_t) radial->c1.radius),
350
0
     v.vector[0], v.vector[1], radial->c1.radius);
351
0
  dc = dot (2 * (pixman_fixed_48_16_t) v.vector[0] + unit.vector[0],
352
0
      2 * (pixman_fixed_48_16_t) v.vector[1] + unit.vector[1],
353
0
      0,
354
0
      unit.vector[0], unit.vector[1], 0);
355
0
  ddc = 2 * dot (unit.vector[0], unit.vector[1], 0,
356
0
           unit.vector[0], unit.vector[1], 0);
357
358
0
  while (buffer < end)
359
0
  {
360
0
      if (!mask || *mask++)
361
0
      {
362
0
    radial_write_color (radial->a, b, c,
363
0
            radial->inva,
364
0
            radial->delta.radius,
365
0
            radial->mindr,
366
0
            &walker,
367
0
            image->common.repeat,
368
0
            Bpp,
369
0
            write_pixel,
370
0
            buffer);
371
0
      }
372
373
0
      b += db;
374
0
      c += dc;
375
0
      dc += ddc;
376
0
      buffer += (Bpp / 4);
377
0
  }
378
0
    }
379
0
    else
380
0
    {
381
  /* projective */
382
  /* Warning:
383
   * error propagation guarantees are much looser than in the affine case
384
   */
385
0
  while (buffer < end)
386
0
  {
387
0
      if (!mask || *mask++)
388
0
      {
389
0
    if (v.vector[2] != 0)
390
0
    {
391
0
        double pdx, pdy, invv2, b, c;
392
393
0
        invv2 = 1. * pixman_fixed_1 / v.vector[2];
394
395
0
        pdx = v.vector[0] * invv2 - radial->c1.x;
396
        /*    / pixman_fixed_1 */
397
398
0
        pdy = v.vector[1] * invv2 - radial->c1.y;
399
        /*    / pixman_fixed_1 */
400
401
0
        b = fdot (pdx, pdy, radial->c1.radius,
402
0
            radial->delta.x, radial->delta.y,
403
0
            radial->delta.radius);
404
        /*  / pixman_fixed_1 / pixman_fixed_1 */
405
406
0
        c = fdot (pdx, pdy, -radial->c1.radius,
407
0
            pdx, pdy, radial->c1.radius);
408
        /*  / pixman_fixed_1 / pixman_fixed_1 */
409
410
0
        radial_write_color (radial->a, b, c,
411
0
          radial->inva,
412
0
          radial->delta.radius,
413
0
          radial->mindr,
414
0
          &walker,
415
0
          image->common.repeat,
416
0
          Bpp,
417
0
          write_pixel,
418
0
          buffer);
419
0
    }
420
0
    else
421
0
    {
422
0
        memset (buffer, 0, Bpp);
423
0
    }
424
0
      }
425
426
0
      buffer += (Bpp / 4);
427
428
0
      v.vector[0] += unit.vector[0];
429
0
      v.vector[1] += unit.vector[1];
430
0
      v.vector[2] += unit.vector[2];
431
0
  }
432
0
    }
433
434
0
    iter->y++;
435
0
    return iter->buffer;
436
0
}
437
438
static uint32_t *
439
radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
440
0
{
441
0
    return radial_get_scanline (iter, mask, 4,
442
0
        _pixman_gradient_walker_write_narrow);
443
0
}
444
445
static uint32_t *
446
radial_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
447
0
{
448
0
    return radial_get_scanline (iter, NULL, 16,
449
0
        _pixman_gradient_walker_write_wide);
450
0
}
451
452
void
453
_pixman_radial_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter)
454
0
{
455
0
    if (iter->iter_flags & ITER_NARROW)
456
0
  iter->get_scanline = radial_get_scanline_narrow;
457
0
    else
458
0
  iter->get_scanline = radial_get_scanline_wide;
459
0
}
460
461
PIXMAN_EXPORT pixman_image_t *
462
pixman_image_create_radial_gradient (const pixman_point_fixed_t *  inner,
463
             const pixman_point_fixed_t *  outer,
464
             pixman_fixed_t                inner_radius,
465
             pixman_fixed_t                outer_radius,
466
             const pixman_gradient_stop_t *stops,
467
             int                           n_stops)
468
0
{
469
0
    pixman_image_t *image;
470
0
    radial_gradient_t *radial;
471
472
0
    image = _pixman_image_allocate ();
473
474
0
    if (!image)
475
0
  return NULL;
476
477
0
    radial = &image->radial;
478
479
0
    if (!_pixman_init_gradient (&radial->common, stops, n_stops))
480
0
    {
481
0
  free (image);
482
0
  return NULL;
483
0
    }
484
485
0
    image->type = RADIAL;
486
487
0
    radial->c1.x = inner->x;
488
0
    radial->c1.y = inner->y;
489
0
    radial->c1.radius = inner_radius;
490
0
    radial->c2.x = outer->x;
491
0
    radial->c2.y = outer->y;
492
0
    radial->c2.radius = outer_radius;
493
494
    /* warning: this computations may overflow */
495
0
    radial->delta.x = radial->c2.x - radial->c1.x;
496
0
    radial->delta.y = radial->c2.y - radial->c1.y;
497
0
    radial->delta.radius = radial->c2.radius - radial->c1.radius;
498
499
    /* computed exactly, then cast to double -> every bit of the double
500
       representation is correct (53 bits) */
501
0
    radial->a = dot (radial->delta.x, radial->delta.y, -radial->delta.radius,
502
0
         radial->delta.x, radial->delta.y, radial->delta.radius);
503
0
    if (radial->a != 0)
504
0
  radial->inva = 1. * pixman_fixed_1 / radial->a;
505
506
0
    radial->mindr = -1. * pixman_fixed_1 * radial->c1.radius;
507
508
0
    return image;
509
0
}