1 /*
2  * art_render_gradient.c: SVP mask 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  */
24 
25 #include "art_render_svp.h"
26 #include "art_svp_render_aa.h"
27 
28 typedef struct _ArtMaskSourceSVP ArtMaskSourceSVP;
29 
30 struct _ArtMaskSourceSVP {
31   ArtMaskSource super;
32   ArtRender *render;
33   const ArtSVP *svp;
34   art_u8 *dest_ptr;
35 };
36 
37 static void
art_render_svp_done(ArtRenderCallback * self,ArtRender * render)38 art_render_svp_done (ArtRenderCallback *self, ArtRender *render)
39 {
40   art_free (self);
41 }
42 
43 static int
art_render_svp_can_drive(ArtMaskSource * self,ArtRender * render)44 art_render_svp_can_drive (ArtMaskSource *self, ArtRender *render)
45 {
46   return 10;
47 }
48 
49 /* The basic art_render_svp_callback function is repeated four times,
50    for all combinations of non-unit opacity and generating spans. In
51    general, I'd consider this bad style, but in this case I plead
52    a measurable performance improvement. */
53 
54 static void
art_render_svp_callback(void * callback_data,int y,int start,ArtSVPRenderAAStep * steps,int n_steps)55 art_render_svp_callback (void *callback_data, int y,
56 			 int start, ArtSVPRenderAAStep *steps, int n_steps)
57 {
58   ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data;
59   ArtRender *render = z->render;
60   int n_run = 0;
61   int i;
62   int running_sum = start;
63   int x0 = render->x0;
64   int x1 = render->x1;
65   int run_x0, run_x1;
66   ArtRenderMaskRun *run = render->run;
67 
68   if (n_steps > 0)
69     {
70       run_x1 = steps[0].x;
71       if (run_x1 > x0 && running_sum > 0x80ff)
72 	{
73 	  run[0].x = x0;
74 	  run[0].alpha = running_sum;
75 	  n_run++;
76 	}
77 
78       for (i = 0; i < n_steps - 1; i++)
79 	{
80           running_sum += steps[i].delta;
81           run_x0 = run_x1;
82           run_x1 = steps[i + 1].x;
83 	  if (run_x1 > run_x0)
84 	    {
85 	      run[n_run].x = run_x0;
86 	      run[n_run].alpha = running_sum;
87 	      n_run++;
88 	    }
89 	}
90       if (x1 > run_x1)
91 	{
92 	  running_sum += steps[n_steps - 1].delta;
93 	  run[n_run].x = run_x1;
94 	  run[n_run].alpha = running_sum;
95 	  n_run++;
96 	}
97       if (running_sum > 0x80ff)
98 	{
99 	  run[n_run].x = x1;
100 	  run[n_run].alpha = 0x8000;
101 	  n_run++;
102 	}
103     }
104   else if ((running_sum >> 16) > 0)
105     {
106       run[0].x = x0;
107       run[0].alpha = running_sum;
108       run[1].x = x1;
109       run[1].alpha = running_sum;
110       n_run = 2;
111     }
112 
113   render->n_run = n_run;
114 
115   art_render_invoke_callbacks (render, z->dest_ptr, y);
116 
117   z->dest_ptr += render->rowstride;
118 }
119 
120 static void
art_render_svp_callback_span(void * callback_data,int y,int start,ArtSVPRenderAAStep * steps,int n_steps)121 art_render_svp_callback_span (void *callback_data, int y,
122 			      int start, ArtSVPRenderAAStep *steps, int n_steps)
123 {
124   ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data;
125   ArtRender *render = z->render;
126   int n_run = 0;
127   int n_span = 0;
128   int i;
129   int running_sum = start;
130   int x0 = render->x0;
131   int x1 = render->x1;
132   int run_x0, run_x1;
133   ArtRenderMaskRun *run = render->run;
134   int *span_x = render->span_x;
135 
136   if (n_steps > 0)
137     {
138       run_x1 = steps[0].x;
139       if (run_x1 > x0 && running_sum > 0x80ff)
140 	{
141 	  run[0].x = x0;
142 	  run[0].alpha = running_sum;
143 	  n_run++;
144 	  span_x[0] = x0;
145 	  n_span++;
146 	}
147 
148       for (i = 0; i < n_steps - 1; i++)
149 	{
150           running_sum += steps[i].delta;
151           run_x0 = run_x1;
152           run_x1 = steps[i + 1].x;
153 	  if (run_x1 > run_x0)
154 	    {
155 	      run[n_run].x = run_x0;
156 	      run[n_run].alpha = running_sum;
157 	      n_run++;
158 	      if ((n_span & 1) != (running_sum > 0x80ff))
159 		span_x[n_span++] = run_x0;
160 	    }
161 	}
162       if (x1 > run_x1)
163 	{
164 	  running_sum += steps[n_steps - 1].delta;
165 	  run[n_run].x = run_x1;
166 	  run[n_run].alpha = running_sum;
167 	  n_run++;
168 	  if ((n_span & 1) != (running_sum > 0x80ff))
169 	    span_x[n_span++] = run_x1;
170 	}
171       if (running_sum > 0x80ff)
172 	{
173 	  run[n_run].x = x1;
174 	  run[n_run].alpha = 0x8000;
175 	  n_run++;
176 	  span_x[n_span++] = x1;
177 	}
178     }
179   else if ((running_sum >> 16) > 0)
180     {
181       run[0].x = x0;
182       run[0].alpha = running_sum;
183       run[1].x = x1;
184       run[1].alpha = running_sum;
185       n_run = 2;
186       span_x[0] = x0;
187       span_x[1] = x1;
188       n_span = 2;
189     }
190 
191   render->n_run = n_run;
192   render->n_span = n_span;
193 
194   art_render_invoke_callbacks (render, z->dest_ptr, y);
195 
196   z->dest_ptr += render->rowstride;
197 }
198 
199 static void
art_render_svp_callback_opacity(void * callback_data,int y,int start,ArtSVPRenderAAStep * steps,int n_steps)200 art_render_svp_callback_opacity (void *callback_data, int y,
201 				 int start, ArtSVPRenderAAStep *steps, int n_steps)
202 {
203   ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data;
204   ArtRender *render = z->render;
205   int n_run = 0;
206   int i;
207   art_u32 running_sum;
208   int x0 = render->x0;
209   int x1 = render->x1;
210   int run_x0, run_x1;
211   ArtRenderMaskRun *run = render->run;
212   art_u32 opacity = render->opacity;
213   art_u32 alpha;
214 
215   running_sum = start - 0x7f80;
216 
217   if (n_steps > 0)
218     {
219       run_x1 = steps[0].x;
220       alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8;
221       if (run_x1 > x0 && alpha > 0x80ff)
222 	{
223 	  run[0].x = x0;
224 	  run[0].alpha = alpha;
225 	  n_run++;
226 	}
227 
228       for (i = 0; i < n_steps - 1; i++)
229 	{
230           running_sum += steps[i].delta;
231           run_x0 = run_x1;
232           run_x1 = steps[i + 1].x;
233 	  if (run_x1 > run_x0)
234 	    {
235 	      run[n_run].x = run_x0;
236 	      alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8;
237 	      run[n_run].alpha = alpha;
238 	      n_run++;
239 	    }
240 	}
241       if (x1 > run_x1)
242 	{
243 	  running_sum += steps[n_steps - 1].delta;
244 	  run[n_run].x = run_x1;
245 	  alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8;
246 	  run[n_run].alpha = alpha;
247 	  n_run++;
248 	}
249       if (alpha > 0x80ff)
250 	{
251 	  run[n_run].x = x1;
252 	  run[n_run].alpha = 0x8000;
253 	  n_run++;
254 	}
255     }
256   else if ((running_sum >> 16) > 0)
257     {
258       run[0].x = x0;
259       run[0].alpha = running_sum;
260       run[1].x = x1;
261       run[1].alpha = running_sum;
262       n_run = 2;
263     }
264 
265   render->n_run = n_run;
266 
267   art_render_invoke_callbacks (render, z->dest_ptr, y);
268 
269   z->dest_ptr += render->rowstride;
270 }
271 
272 static void
art_render_svp_callback_opacity_span(void * callback_data,int y,int start,ArtSVPRenderAAStep * steps,int n_steps)273 art_render_svp_callback_opacity_span (void *callback_data, int y,
274 				      int start, ArtSVPRenderAAStep *steps, int n_steps)
275 {
276   ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data;
277   ArtRender *render = z->render;
278   int n_run = 0;
279   int n_span = 0;
280   int i;
281   art_u32 running_sum;
282   int x0 = render->x0;
283   int x1 = render->x1;
284   int run_x0, run_x1;
285   ArtRenderMaskRun *run = render->run;
286   int *span_x = render->span_x;
287   art_u32 opacity = render->opacity;
288   art_u32 alpha;
289 
290   running_sum = start - 0x7f80;
291 
292   if (n_steps > 0)
293     {
294       run_x1 = steps[0].x;
295       alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8;
296       if (run_x1 > x0 && alpha > 0x80ff)
297 	{
298 	  run[0].x = x0;
299 	  run[0].alpha = alpha;
300 	  n_run++;
301 	  span_x[0] = x0;
302 	  n_span++;
303 	}
304 
305       for (i = 0; i < n_steps - 1; i++)
306 	{
307           running_sum += steps[i].delta;
308           run_x0 = run_x1;
309           run_x1 = steps[i + 1].x;
310 	  if (run_x1 > run_x0)
311 	    {
312 	      run[n_run].x = run_x0;
313 	      alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8;
314 	      run[n_run].alpha = alpha;
315 	      n_run++;
316 	      if ((n_span & 1) != (alpha > 0x80ff))
317 		span_x[n_span++] = run_x0;
318 	    }
319 	}
320       if (x1 > run_x1)
321 	{
322 	  running_sum += steps[n_steps - 1].delta;
323 	  run[n_run].x = run_x1;
324 	  alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8;
325 	  run[n_run].alpha = alpha;
326 	  n_run++;
327 	  if ((n_span & 1) != (alpha > 0x80ff))
328 	    span_x[n_span++] = run_x1;
329 	}
330       if (alpha > 0x80ff)
331 	{
332 	  run[n_run].x = x1;
333 	  run[n_run].alpha = 0x8000;
334 	  n_run++;
335 	  span_x[n_span++] = x1;
336 	}
337     }
338   else if ((running_sum >> 16) > 0)
339     {
340       run[0].x = x0;
341       run[0].alpha = running_sum;
342       run[1].x = x1;
343       run[1].alpha = running_sum;
344       n_run = 2;
345       span_x[0] = x0;
346       span_x[1] = x1;
347       n_span = 2;
348     }
349 
350   render->n_run = n_run;
351   render->n_span = n_span;
352 
353   art_render_invoke_callbacks (render, z->dest_ptr, y);
354 
355   z->dest_ptr += render->rowstride;
356 }
357 
358 static void
art_render_svp_invoke_driver(ArtMaskSource * self,ArtRender * render)359 art_render_svp_invoke_driver (ArtMaskSource *self, ArtRender *render)
360 {
361   ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)self;
362   void (*callback) (void *callback_data,
363 		    int y,
364 		    int start,
365 		    ArtSVPRenderAAStep *steps, int n_steps);
366 
367   z->dest_ptr = render->pixels;
368   if (render->opacity == 0x10000)
369     {
370       if (render->need_span)
371 	callback = art_render_svp_callback_span;
372       else
373 	callback = art_render_svp_callback;
374     }
375   else
376     {
377       if (render->need_span)
378 	callback = art_render_svp_callback_opacity_span;
379       else
380 	callback = art_render_svp_callback_opacity;
381     }
382 
383   art_svp_render_aa (z->svp,
384 		     render->x0, render->y0,
385 		     render->x1, render->y1, callback,
386 		     self);
387   art_render_svp_done (&self->super, render);
388 }
389 
390 static void
art_render_svp_prepare(ArtMaskSource * self,ArtRender * render,art_boolean first)391 art_render_svp_prepare (ArtMaskSource *self, ArtRender *render,
392 			art_boolean first)
393 {
394   /* todo */
395   art_die ("art_render_svp non-driver mode not yet implemented.\n");
396 }
397 
398 /**
399  * art_render_svp: Use an SVP as a render mask source.
400  * @render: Render object.
401  * @svp: SVP.
402  *
403  * Adds @svp to the render object as a mask. Note: @svp must remain
404  * allocated until art_render_invoke() is called on @render.
405  **/
406 void
art_render_svp(ArtRender * render,const ArtSVP * svp)407 art_render_svp (ArtRender *render, const ArtSVP *svp)
408 {
409   ArtMaskSourceSVP *mask_source;
410   mask_source = art_new (ArtMaskSourceSVP, 1);
411 
412   mask_source->super.super.render = NULL;
413   mask_source->super.super.done = art_render_svp_done;
414   mask_source->super.can_drive = art_render_svp_can_drive;
415   mask_source->super.invoke_driver = art_render_svp_invoke_driver;
416   mask_source->super.prepare = art_render_svp_prepare;
417   mask_source->render = render;
418   mask_source->svp = svp;
419 
420   art_render_add_mask_source (render, &mask_source->super);
421 }
422