X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=libraries%2Flibart_lgpl-2.3.7%2Fart_render_gradient.c;fp=libraries%2Flibart_lgpl-2.3.7%2Fart_render_gradient.c;h=d84dad8655a22d2a43a33da9c5927fb8ea47bb6b;hb=a1fe8fd19bb4f558150e52c0624869667b739877;hp=0000000000000000000000000000000000000000;hpb=e8f4081448f258adfbfa962b75d8392a7e152849;p=rrdtool.git diff --git a/libraries/libart_lgpl-2.3.7/art_render_gradient.c b/libraries/libart_lgpl-2.3.7/art_render_gradient.c new file mode 100644 index 0000000..d84dad8 --- /dev/null +++ b/libraries/libart_lgpl-2.3.7/art_render_gradient.c @@ -0,0 +1,295 @@ +/* + * art_render_gradient.c: Gradient image source for modular rendering. + * + * Libart_LGPL - library of basic graphic primitives + * Copyright (C) 2000 Raph Levien + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Raph Levien + * Alexander Larsson + */ + +#include + +#include "art_misc.h" +#include "art_alphagamma.h" +#include "art_filterlevel.h" + +#include "art_render.h" +#include "art_render_gradient.h" + +typedef struct _ArtImageSourceGradLin ArtImageSourceGradLin; +typedef struct _ArtImageSourceGradRad ArtImageSourceGradRad; + +struct _ArtImageSourceGradLin { + ArtImageSource super; + const ArtGradientLinear *gradient; +}; + +struct _ArtImageSourceGradRad { + ArtImageSource super; + const ArtGradientRadial *gradient; + double a; +}; + +#define EPSILON 1e-6 + +/** + * art_render_gradient_setpix: Set a gradient pixel. + * @render: The render object. + * @dst: Pointer to destination (where to store pixel). + * @n_stops: Number of stops in @stops. + * @stops: The stops for the gradient. + * @offset: The offset. + * + * @n_stops must be > 0. + * + * Sets a gradient pixel, storing it at @dst. + **/ +static void +art_render_gradient_setpix (ArtRender *render, + art_u8 *dst, + int n_stops, ArtGradientStop *stops, + double offset) +{ + int ix; + int j; + double off0, off1; + int n_ch = render->n_chan + 1; + + for (ix = 0; ix < n_stops; ix++) + if (stops[ix].offset > offset) + break; + /* stops[ix - 1].offset < offset < stops[ix].offset */ + if (ix > 0 && ix < n_stops) + { + off0 = stops[ix - 1].offset; + off1 = stops[ix].offset; + if (fabs (off1 - off0) > EPSILON) + { + double interp; + + interp = (offset - off0) / (off1 - off0); + for (j = 0; j < n_ch; j++) + { + int z0, z1; + int z; + z0 = stops[ix - 1].color[j]; + z1 = stops[ix].color[j]; + z = floor (z0 + (z1 - z0) * interp + 0.5); + if (render->buf_depth == 8) + dst[j] = ART_PIX_8_FROM_MAX (z); + else /* (render->buf_depth == 16) */ + ((art_u16 *)dst)[j] = z; + } + return; + } + } + else if (ix == n_stops) + ix--; + + for (j = 0; j < n_ch; j++) + { + int z; + z = stops[ix].color[j]; + if (render->buf_depth == 8) + dst[j] = ART_PIX_8_FROM_MAX (z); + else /* (render->buf_depth == 16) */ + ((art_u16 *)dst)[j] = z; + } +} + +static void +art_render_gradient_linear_done (ArtRenderCallback *self, ArtRender *render) +{ + art_free (self); +} + +static void +art_render_gradient_linear_render (ArtRenderCallback *self, ArtRender *render, + art_u8 *dest, int y) +{ + ArtImageSourceGradLin *z = (ArtImageSourceGradLin *)self; + const ArtGradientLinear *gradient = z->gradient; + int pixstride = (render->n_chan + 1) * (render->depth >> 3); + int x; + int width = render->x1 - render->x0; + double offset, d_offset; + double actual_offset; + int n_stops = gradient->n_stops; + ArtGradientStop *stops = gradient->stops; + art_u8 *bufp = render->image_buf; + ArtGradientSpread spread = gradient->spread; + + offset = render->x0 * gradient->a + y * gradient->b + gradient->c; + d_offset = gradient->a; + + for (x = 0; x < width; x++) + { + if (spread == ART_GRADIENT_PAD) + actual_offset = offset; + else if (spread == ART_GRADIENT_REPEAT) + actual_offset = offset - floor (offset); + else /* (spread == ART_GRADIENT_REFLECT) */ + { + double tmp; + + tmp = offset - 2 * floor (0.5 * offset); + actual_offset = tmp > 1 ? 2 - tmp : tmp; + } + art_render_gradient_setpix (render, bufp, n_stops, stops, actual_offset); + offset += d_offset; + bufp += pixstride; + } +} + +static void +art_render_gradient_linear_negotiate (ArtImageSource *self, ArtRender *render, + ArtImageSourceFlags *p_flags, + int *p_buf_depth, ArtAlphaType *p_alpha) +{ + self->super.render = art_render_gradient_linear_render; + *p_flags = 0; + *p_buf_depth = render->depth; + *p_alpha = ART_ALPHA_PREMUL; +} + +/** + * art_render_gradient_linear: Add a linear gradient image source. + * @render: The render object. + * @gradient: The linear gradient. + * + * Adds the linear gradient @gradient as the image source for rendering + * in the render object @render. + **/ +void +art_render_gradient_linear (ArtRender *render, + const ArtGradientLinear *gradient, + ArtFilterLevel level) +{ + ArtImageSourceGradLin *image_source = art_new (ArtImageSourceGradLin, 1); + + image_source->super.super.render = NULL; + image_source->super.super.done = art_render_gradient_linear_done; + image_source->super.negotiate = art_render_gradient_linear_negotiate; + + image_source->gradient = gradient; + + art_render_add_image_source (render, &image_source->super); +} + +static void +art_render_gradient_radial_done (ArtRenderCallback *self, ArtRender *render) +{ + art_free (self); +} + +static void +art_render_gradient_radial_render (ArtRenderCallback *self, ArtRender *render, + art_u8 *dest, int y) +{ + ArtImageSourceGradRad *z = (ArtImageSourceGradRad *)self; + const ArtGradientRadial *gradient = z->gradient; + int pixstride = (render->n_chan + 1) * (render->depth >> 3); + int x; + int x0 = render->x0; + int width = render->x1 - x0; + int n_stops = gradient->n_stops; + ArtGradientStop *stops = gradient->stops; + art_u8 *bufp = render->image_buf; + double fx = gradient->fx; + double fy = gradient->fy; + double dx, dy; + double *affine = gradient->affine; + double aff0 = affine[0]; + double aff1 = affine[1]; + const double a = z->a; + const double arecip = 1.0 / a; + double b, db; + double c, dc, ddc; + double b_a, db_a; + double rad, drad, ddrad; + + dx = x0 * aff0 + y * affine[2] + affine[4] - fx; + dy = x0 * aff1 + y * affine[3] + affine[5] - fy; + b = dx * fx + dy * fy; + db = aff0 * fx + aff1 * fy; + c = dx * dx + dy * dy; + dc = 2 * aff0 * dx + aff0 * aff0 + 2 * aff1 * dy + aff1 * aff1; + ddc = 2 * aff0 * aff0 + 2 * aff1 * aff1; + + b_a = b * arecip; + db_a = db * arecip; + + rad = b_a * b_a + c * arecip; + drad = 2 * b_a * db_a + db_a * db_a + dc * arecip; + ddrad = 2 * db_a * db_a + ddc * arecip; + + for (x = 0; x < width; x++) + { + double z; + + if (rad > 0) + z = b_a + sqrt (rad); + else + z = b_a; + art_render_gradient_setpix (render, bufp, n_stops, stops, z); + bufp += pixstride; + b_a += db_a; + rad += drad; + drad += ddrad; + } +} + +static void +art_render_gradient_radial_negotiate (ArtImageSource *self, ArtRender *render, + ArtImageSourceFlags *p_flags, + int *p_buf_depth, ArtAlphaType *p_alpha) +{ + self->super.render = art_render_gradient_radial_render; + *p_flags = 0; + *p_buf_depth = render->depth; + *p_alpha = ART_ALPHA_PREMUL; +} + +/** + * art_render_gradient_radial: Add a radial gradient image source. + * @render: The render object. + * @gradient: The radial gradient. + * + * Adds the radial gradient @gradient as the image source for rendering + * in the render object @render. + **/ +void +art_render_gradient_radial (ArtRender *render, + const ArtGradientRadial *gradient, + ArtFilterLevel level) +{ + ArtImageSourceGradRad *image_source = art_new (ArtImageSourceGradRad, 1); + double fx = gradient->fx; + double fy = gradient->fy; + + image_source->super.super.render = NULL; + image_source->super.super.done = art_render_gradient_radial_done; + image_source->super.negotiate = art_render_gradient_radial_negotiate; + + image_source->gradient = gradient; + /* todo: sanitycheck fx, fy? */ + image_source->a = 1 - fx * fx - fy * fy; + + art_render_add_image_source (render, &image_source->super); +}