An LGPL/GPL-licensed artwork library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

art_render_gradient.c 19KB

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