/* * art_render_gradient.c: SVP mask 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 */ #include "config.h" #include "art_render_svp.h" #include "art_svp_render_aa.h" typedef struct _ArtMaskSourceSVP ArtMaskSourceSVP; struct _ArtMaskSourceSVP { ArtMaskSource super; ArtRender *render; const ArtSVP *svp; art_u8 *dest_ptr; }; static void art_render_svp_done (ArtRenderCallback *self, ArtRender *render) { art_free (self); } static int art_render_svp_can_drive (ArtMaskSource *self, ArtRender *render) { return 10; } /* The basic art_render_svp_callback function is repeated four times, for all combinations of non-unit opacity and generating spans. In general, I'd consider this bad style, but in this case I plead a measurable performance improvement. */ static void art_render_svp_callback (void *callback_data, int y, int start, ArtSVPRenderAAStep *steps, int n_steps) { ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data; ArtRender *render = z->render; int n_run = 0; int i; int running_sum = start; int x0 = render->x0; int x1 = render->x1; int run_x0, run_x1; ArtRenderMaskRun *run = render->run; if (n_steps > 0) { run_x1 = steps[0].x; if (run_x1 > x0 && running_sum > 0x80ff) { run[0].x = x0; run[0].alpha = running_sum; n_run++; } for (i = 0; i < n_steps - 1; i++) { running_sum += steps[i].delta; run_x0 = run_x1; run_x1 = steps[i + 1].x; if (run_x1 > run_x0) { run[n_run].x = run_x0; run[n_run].alpha = running_sum; n_run++; } } if (x1 > run_x1) { running_sum += steps[n_steps - 1].delta; run[n_run].x = run_x1; run[n_run].alpha = running_sum; n_run++; } if (running_sum > 0x80ff) { run[n_run].x = x1; run[n_run].alpha = 0x8000; n_run++; } } else if ((running_sum >> 16) > 0) { run[0].x = x0; run[0].alpha = running_sum; run[1].x = x1; run[1].alpha = running_sum; n_run = 2; } render->n_run = n_run; art_render_invoke_callbacks (render, z->dest_ptr, y); z->dest_ptr += render->rowstride; } static void art_render_svp_callback_span (void *callback_data, int y, int start, ArtSVPRenderAAStep *steps, int n_steps) { ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data; ArtRender *render = z->render; int n_run = 0; int n_span = 0; int i; int running_sum = start; int x0 = render->x0; int x1 = render->x1; int run_x0, run_x1; ArtRenderMaskRun *run = render->run; int *span_x = render->span_x; if (n_steps > 0) { run_x1 = steps[0].x; if (run_x1 > x0 && running_sum > 0x80ff) { run[0].x = x0; run[0].alpha = running_sum; n_run++; span_x[0] = x0; n_span++; } for (i = 0; i < n_steps - 1; i++) { running_sum += steps[i].delta; run_x0 = run_x1; run_x1 = steps[i + 1].x; if (run_x1 > run_x0) { run[n_run].x = run_x0; run[n_run].alpha = running_sum; n_run++; if ((n_span & 1) != (running_sum > 0x80ff)) span_x[n_span++] = run_x0; } } if (x1 > run_x1) { running_sum += steps[n_steps - 1].delta; run[n_run].x = run_x1; run[n_run].alpha = running_sum; n_run++; if ((n_span & 1) != (running_sum > 0x80ff)) span_x[n_span++] = run_x1; } if (running_sum > 0x80ff) { run[n_run].x = x1; run[n_run].alpha = 0x8000; n_run++; span_x[n_span++] = x1; } } else if ((running_sum >> 16) > 0) { run[0].x = x0; run[0].alpha = running_sum; run[1].x = x1; run[1].alpha = running_sum; n_run = 2; span_x[0] = x0; span_x[1] = x1; n_span = 2; } render->n_run = n_run; render->n_span = n_span; art_render_invoke_callbacks (render, z->dest_ptr, y); z->dest_ptr += render->rowstride; } static void art_render_svp_callback_opacity (void *callback_data, int y, int start, ArtSVPRenderAAStep *steps, int n_steps) { ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data; ArtRender *render = z->render; int n_run = 0; int i; art_u32 running_sum; int x0 = render->x0; int x1 = render->x1; int run_x0, run_x1; ArtRenderMaskRun *run = render->run; art_u32 opacity = render->opacity; art_u32 alpha; running_sum = start - 0x7f80; if (n_steps > 0) { run_x1 = steps[0].x; alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8; if (run_x1 > x0 && alpha > 0x80ff) { run[0].x = x0; run[0].alpha = alpha; n_run++; } for (i = 0; i < n_steps - 1; i++) { running_sum += steps[i].delta; run_x0 = run_x1; run_x1 = steps[i + 1].x; if (run_x1 > run_x0) { run[n_run].x = run_x0; alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8; run[n_run].alpha = alpha; n_run++; } } if (x1 > run_x1) { running_sum += steps[n_steps - 1].delta; run[n_run].x = run_x1; alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8; run[n_run].alpha = alpha; n_run++; } if (alpha > 0x80ff) { run[n_run].x = x1; run[n_run].alpha = 0x8000; n_run++; } } else if ((running_sum >> 16) > 0) { run[0].x = x0; run[0].alpha = running_sum; run[1].x = x1; run[1].alpha = running_sum; n_run = 2; } render->n_run = n_run; art_render_invoke_callbacks (render, z->dest_ptr, y); z->dest_ptr += render->rowstride; } static void art_render_svp_callback_opacity_span (void *callback_data, int y, int start, ArtSVPRenderAAStep *steps, int n_steps) { ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data; ArtRender *render = z->render; int n_run = 0; int n_span = 0; int i; art_u32 running_sum; int x0 = render->x0; int x1 = render->x1; int run_x0, run_x1; ArtRenderMaskRun *run = render->run; int *span_x = render->span_x; art_u32 opacity = render->opacity; art_u32 alpha; running_sum = start - 0x7f80; if (n_steps > 0) { run_x1 = steps[0].x; alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8; if (run_x1 > x0 && alpha > 0x80ff) { run[0].x = x0; run[0].alpha = alpha; n_run++; span_x[0] = x0; n_span++; } for (i = 0; i < n_steps - 1; i++) { running_sum += steps[i].delta; run_x0 = run_x1; run_x1 = steps[i + 1].x; if (run_x1 > run_x0) { run[n_run].x = run_x0; alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8; run[n_run].alpha = alpha; n_run++; if ((n_span & 1) != (alpha > 0x80ff)) span_x[n_span++] = run_x0; } } if (x1 > run_x1) { running_sum += steps[n_steps - 1].delta; run[n_run].x = run_x1; alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8; run[n_run].alpha = alpha; n_run++; if ((n_span & 1) != (alpha > 0x80ff)) span_x[n_span++] = run_x1; } if (alpha > 0x80ff) { run[n_run].x = x1; run[n_run].alpha = 0x8000; n_run++; span_x[n_span++] = x1; } } else if ((running_sum >> 16) > 0) { run[0].x = x0; run[0].alpha = running_sum; run[1].x = x1; run[1].alpha = running_sum; n_run = 2; span_x[0] = x0; span_x[1] = x1; n_span = 2; } render->n_run = n_run; render->n_span = n_span; art_render_invoke_callbacks (render, z->dest_ptr, y); z->dest_ptr += render->rowstride; } static void art_render_svp_invoke_driver (ArtMaskSource *self, ArtRender *render) { ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)self; void (*callback) (void *callback_data, int y, int start, ArtSVPRenderAAStep *steps, int n_steps); z->dest_ptr = render->pixels; if (render->opacity == 0x10000) { if (render->need_span) callback = art_render_svp_callback_span; else callback = art_render_svp_callback; } else { if (render->need_span) callback = art_render_svp_callback_opacity_span; else callback = art_render_svp_callback_opacity; } art_svp_render_aa (z->svp, render->x0, render->y0, render->x1, render->y1, callback, self); art_render_svp_done (&self->super, render); } static void art_render_svp_prepare (ArtMaskSource *self, ArtRender *render, art_boolean first) { /* todo */ art_die ("art_render_svp non-driver mode not yet implemented.\n"); } /** * art_render_svp: Use an SVP as a render mask source. * @render: Render object. * @svp: SVP. * * Adds @svp to the render object as a mask. Note: @svp must remain * allocated until art_render_invoke() is called on @render. **/ void art_render_svp (ArtRender *render, const ArtSVP *svp) { ArtMaskSourceSVP *mask_source; mask_source = art_new (ArtMaskSourceSVP, 1); mask_source->super.super.render = NULL; mask_source->super.super.done = art_render_svp_done; mask_source->super.can_drive = art_render_svp_can_drive; mask_source->super.invoke_driver = art_render_svp_invoke_driver; mask_source->super.prepare = art_render_svp_prepare; mask_source->render = render; mask_source->svp = svp; art_render_add_mask_source (render, &mask_source->super); }