xref: /haiku/src/servers/app/drawing/Painter/agg_rasterizer_scanline_aa_subpix.h (revision 70b978fd9fc574e72a9f91f58beb36c521ec57c4)
1 /*
2  * Copyright 2008, Andrej Spielmann <andrej.spielmann@seh.ox.ac.uk>.
3  * All rights reserved. Distributed under the terms of the MIT License.
4  *
5  * Copyright 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
6  *
7  *
8  */
9 
10 #ifndef AGG_RASTERIZER_SCANLINE_AA_SUBPIX_INCLUDED
11 #define AGG_RASTERIZER_SCANLINE_AA_SUBPIX_INCLUDED
12 
13 #include "agg_rasterizer_cells_aa.h"
14 #include "agg_rasterizer_sl_clip.h"
15 #include "agg_gamma_functions.h"
16 
17 
18 namespace agg
19 {
20 	template<class Clip=rasterizer_sl_clip_int> class rasterizer_scanline_aa_subpix
21 	{
22 		enum status
23 		{
24 			status_initial,
25 			status_move_to,
26 			status_line_to,
27 			status_closed
28 		};
29 
30 	public:
31 		typedef Clip					  clip_type;
32 		typedef typename Clip::conv_type  conv_type;
33 		typedef typename Clip::coord_type coord_type;
34 
35 		enum aa_scale_e
36 		{
37 			aa_shift  = 8,
38 			aa_scale  = 1 << aa_shift,
39 			aa_mask	  = aa_scale - 1,
40 			aa_scale2 = aa_scale * 2,
41 			aa_mask2  = aa_scale2 - 1
42 		};
43 
44 		//--------------------------------------------------------------------
rasterizer_scanline_aa_subpix()45 		rasterizer_scanline_aa_subpix() :
46 			m_outline(),
47 			m_clipper(),
48 			m_filling_rule(fill_non_zero),
49 			m_auto_close(true),
50 			m_start_x(0),
51 			m_start_y(0),
52 			m_status(status_initial)
53 		{
54 			int i;
55 			for(i = 0; i < aa_scale; i++) m_gamma[i] = i;
56 		}
57 
58 		//--------------------------------------------------------------------
59 		template<class GammaF>
rasterizer_scanline_aa_subpix(const GammaF & gamma_function)60 		rasterizer_scanline_aa_subpix(const GammaF& gamma_function) :
61 			m_outline(),
62 			m_clipper(m_outline),
63 			m_filling_rule(fill_non_zero),
64 			m_auto_close(true),
65 			m_start_x(0),
66 			m_start_y(0),
67 			m_status(status_initial)
68 		{
69 			gamma(gamma_function);
70 		}
71 
72 		//--------------------------------------------------------------------
73 		void reset();
74 		void reset_clipping();
75 		void clip_box(double x1, double y1, double x2, double y2);
76 		void filling_rule(filling_rule_e filling_rule);
auto_close(bool flag)77 		void auto_close(bool flag) { m_auto_close = flag; }
78 
79 		//--------------------------------------------------------------------
gamma(const GammaF & gamma_function)80 		template<class GammaF> void gamma(const GammaF& gamma_function)
81 		{
82 			int i;
83 			for(i = 0; i < aa_scale; i++)
84 			{
85 				m_gamma[i] = uround(gamma_function(double(i) / aa_mask) * aa_mask);
86 			}
87 		}
88 
89 		//--------------------------------------------------------------------
apply_gamma(unsigned cover)90 		unsigned apply_gamma(unsigned cover) const
91 		{
92 			return m_gamma[cover];
93 		}
94 
95 		//--------------------------------------------------------------------
96 		void move_to(int x, int y);
97 		void line_to(int x, int y);
98 		void move_to_d(double x, double y);
99 		void line_to_d(double x, double y);
100 		void close_polygon();
101 		void add_vertex(double x, double y, unsigned cmd);
102 
103 		void edge(int x1, int y1, int x2, int y2);
104 		void edge_d(double x1, double y1, double x2, double y2);
105 
106 		//-------------------------------------------------------------------
107 		template<class VertexSource>
108 		void add_path(VertexSource& vs, unsigned path_id=0)
109 		{
110 			double x = 0;
111 			double y = 0;
112 
113 			unsigned cmd;
114 			vs.rewind(path_id);
115 			if(m_outline.sorted()) reset();
116 			while(!is_stop(cmd = vs.vertex(&x, &y)))
117 			{
118 				if (is_vertex(cmd)) {
119 					x *= 3;
120 				}
121 				add_vertex(x, y, cmd);
122 			}
123 		}
124 
125 		//--------------------------------------------------------------------
min_x()126 		int min_x() const { return m_outline.min_x() / 3; }
min_y()127 		int min_y() const { return m_outline.min_y(); }
max_x()128 		int max_x() const { return m_outline.max_x() / 3; }
max_y()129 		int max_y() const { return m_outline.max_y(); }
130 
131 		//--------------------------------------------------------------------
132 		void sort();
133 		bool rewind_scanlines();
134 		bool navigate_scanline(int y);
135 
136 		//--------------------------------------------------------------------
calculate_alpha(int area)137 		AGG_INLINE unsigned calculate_alpha(int area) const
138 		{
139 			int cover = area >> (poly_subpixel_shift*2 + 1 - aa_shift);
140 
141 			if(cover < 0) cover = -cover;
142 			if(m_filling_rule == fill_even_odd)
143 			{
144 				cover &= aa_mask2;
145 				if(cover > aa_scale)
146 				{
147 					cover = aa_scale2 - cover;
148 				}
149 			}
150 			if(cover > aa_mask) cover = aa_mask;
151 			return m_gamma[cover];
152 		}
153 
154 		//--------------------------------------------------------------------
sweep_scanline(Scanline & sl)155 		template<class Scanline> bool sweep_scanline(Scanline& sl)
156 		{
157 			for(;;)
158 			{
159 				if(m_scan_y > m_outline.max_y()) return false;
160 				sl.reset_spans();
161 				unsigned num_cells = m_outline.scanline_num_cells(m_scan_y);
162 				const cell_aa* const* cells = m_outline.scanline_cells(m_scan_y);
163 				int cover = 0;
164 				int cover2 = 0;
165 				int cover3 = 0;
166 
167 				while(num_cells)
168 				{
169 					const cell_aa* cur_cell = *cells;
170 					int x	 = cur_cell->x;
171 					int area1 = cur_cell->area;
172 					int area2;
173 					int area3;
174 					unsigned alpha1;
175 					unsigned alpha2;
176 					unsigned alpha3;
177 
178 					int last_cover = cover3;
179 					cover = cover3;
180 					cover += cur_cell->cover;
181 
182 					while(--num_cells)
183 					{
184 						cur_cell = *++cells;
185 						if(cur_cell->x != x) break;
186 						area1  += cur_cell->area;
187 						cover += cur_cell->cover;
188 					}
189 
190 					if (x % 3 == 0)
191 					{
192 						if (cur_cell->x == x + 1)
193 						{
194 							area2 = cur_cell->area;
195 							cover2 = cover + cur_cell->cover;
196 
197 							while (--num_cells)
198 							{
199 								cur_cell = *++cells;
200 								if (cur_cell->x != x+1) break;
201 								area2 += cur_cell->area;
202 								cover2 += cur_cell->cover;
203 							}
204 						}
205 						else
206 						{
207 							area2 = 0;
208 							cover2 = cover;
209 						}
210 
211 						if (cur_cell->x == x + 2)
212 						{
213 							area3 = cur_cell->area;
214 							cover3 = cover2 + cur_cell->cover;
215 
216 							while (--num_cells)
217 							{
218 								cur_cell = *++cells;
219 								if (cur_cell->x != x+2) break;
220 								area3 += cur_cell->area;
221 								cover3 += cur_cell->cover;
222 							}
223 						}
224 						else
225 						{
226 							area3 = 0;
227 							cover3 = cover2;
228 						}
229 					}
230 					else if (x % 3 == 1)
231 					{
232 						area2 = area1;
233 						area1 = 0;
234 						cover2 = cover;
235 						cover = last_cover;
236 						if (cur_cell->x == x+1)
237 						{
238 							area3 = cur_cell->area;
239 							cover3 = cover2 + cur_cell->cover;
240 
241 							while (--num_cells)
242 							{
243 								cur_cell = *++cells;
244 								if (cur_cell->x != x+1) break;
245 								area3 += cur_cell->area;
246 								cover3 += cur_cell->cover;
247 							}
248 						}
249 						else
250 						{
251 							area3 = 0;
252 							cover3 = cover2;
253 						}
254 					}
255 					else // if (x % 3 == 2)
256 					{
257 						area3 = area1;
258 						area2 = 0;
259 						area1 = 0;
260 						cover3 = cover;
261 						cover = last_cover;
262 						cover2 = last_cover;
263 					}
264 
265 					alpha1 = area1 ? calculate_alpha((cover
266 						<< (poly_subpixel_shift + 1)) - area1) : 0;
267 					alpha2 = area2 ? calculate_alpha((cover2
268 						<< (poly_subpixel_shift + 1)) - area2) : 0;
269 					alpha3 = area3 ? calculate_alpha((cover3
270 						<< (poly_subpixel_shift + 1)) - area3) : 0;
271 					if(alpha1 || alpha2 || alpha3)
272 					{
273 						x += 3 - (x % 3);
274 						if (area1 && !area2 && area3)
275 						{
276 							alpha2 = calculate_alpha(cover
277 								<< (poly_subpixel_shift + 1));
278 						}
279 						else if (num_cells && cur_cell->x >= x)
280 						{
281 							if (area1 && !area2)
282 							{
283 								alpha2 = calculate_alpha(cover
284 									<< (poly_subpixel_shift + 1));
285 								alpha3 = alpha2;
286 							}
287 							if (area2 && !area3)
288 							{
289 								alpha3 = calculate_alpha(cover2
290 									<< (poly_subpixel_shift + 1));
291 							}
292 						}
293 						if (!area1)
294 						{
295 							if (area2)
296 							{
297 								alpha1 = calculate_alpha(cover
298 									<< (poly_subpixel_shift + 1));
299 							}
300 							else if (area3)
301 							{
302 								alpha2 = calculate_alpha(cover
303 									<< (poly_subpixel_shift + 1));
304 								alpha1 = alpha2;
305 							}
306 						}
307 						sl.add_cell(x / 3 - 1, alpha1, alpha2, alpha3);
308 					}
309 
310 					if (num_cells && cur_cell->x - x >= 3)
311 					{
312 						alpha1 = calculate_alpha(cover3
313 							<< (poly_subpixel_shift + 1));
314 						sl.add_span(x / 3, cur_cell->x / 3 - x / 3, alpha1);
315 					}
316 				}
317 
318 				if(sl.num_spans()) break;
319 				++m_scan_y;
320 			}
321 
322 			sl.finalize(m_scan_y);
323 			++m_scan_y;
324 			return true;
325 		}
326 
327 		//--------------------------------------------------------------------
328 		bool hit_test(int tx, int ty);
329 
330 
331 	private:
332 		//--------------------------------------------------------------------
333 		// Disable copying
334 		rasterizer_scanline_aa_subpix(const rasterizer_scanline_aa_subpix<Clip>&);
335 		const rasterizer_scanline_aa_subpix<Clip>&
336 		operator = (const rasterizer_scanline_aa_subpix<Clip>&);
337 
338 	private:
339 		rasterizer_cells_aa<cell_aa> m_outline;
340 		clip_type	   m_clipper;
341 		int			   m_gamma[aa_scale];
342 		filling_rule_e m_filling_rule;
343 		bool		   m_auto_close;
344 		coord_type	   m_start_x;
345 		coord_type	   m_start_y;
346 		unsigned	   m_status;
347 		int			   m_scan_y;
348 	};
349 
350 
351 
352 
353 
354 
355 
356 
357 
358 
359 
360 
361 	//------------------------------------------------------------------------
362 	template<class Clip>
reset()363 	void rasterizer_scanline_aa_subpix<Clip>::reset()
364 	{
365 		m_outline.reset();
366 		m_status = status_initial;
367 	}
368 
369 	//------------------------------------------------------------------------
370 	template<class Clip>
filling_rule(filling_rule_e filling_rule)371 	void rasterizer_scanline_aa_subpix<Clip>::filling_rule(filling_rule_e filling_rule)
372 	{
373 		m_filling_rule = filling_rule;
374 	}
375 
376 	//------------------------------------------------------------------------
377 	template<class Clip>
clip_box(double x1,double y1,double x2,double y2)378 	void rasterizer_scanline_aa_subpix<Clip>::clip_box(double x1, double y1,
379 												double x2, double y2)
380 	{
381 		reset();
382 		m_clipper.clip_box(3 * conv_type::downscale(x1), conv_type::upscale(y1),
383 						   conv_type::upscale(3 * x2), conv_type::upscale(y2));
384 	}
385 
386 	//------------------------------------------------------------------------
387 	template<class Clip>
reset_clipping()388 	void rasterizer_scanline_aa_subpix<Clip>::reset_clipping()
389 	{
390 		reset();
391 		m_clipper.reset_clipping();
392 	}
393 
394 	//------------------------------------------------------------------------
395 	template<class Clip>
close_polygon()396 	void rasterizer_scanline_aa_subpix<Clip>::close_polygon()
397 	{
398 		if(m_status == status_line_to)
399 		{
400 			m_clipper.line_to(m_outline, m_start_x, m_start_y);
401 			m_status = status_closed;
402 		}
403 	}
404 
405 	//------------------------------------------------------------------------
406 	template<class Clip>
move_to(int x,int y)407 	void rasterizer_scanline_aa_subpix<Clip>::move_to(int x, int y)
408 	{
409 		if(m_outline.sorted()) reset();
410 		if(m_auto_close) close_polygon();
411 		m_clipper.move_to(m_start_x = conv_type::downscale(x),
412 						  m_start_y = conv_type::downscale(y));
413 		m_status = status_move_to;
414 	}
415 
416 	//------------------------------------------------------------------------
417 	template<class Clip>
line_to(int x,int y)418 	void rasterizer_scanline_aa_subpix<Clip>::line_to(int x, int y)
419 	{
420 		m_clipper.line_to(m_outline,
421 						  conv_type::downscale(x),
422 						  conv_type::downscale(y));
423 		m_status = status_line_to;
424 	}
425 
426 	//------------------------------------------------------------------------
427 	template<class Clip>
move_to_d(double x,double y)428 	void rasterizer_scanline_aa_subpix<Clip>::move_to_d(double x, double y)
429 	{
430 		if(m_outline.sorted()) reset();
431 		if(m_auto_close) close_polygon();
432 		m_clipper.move_to(m_start_x = conv_type::upscale(x),
433 						  m_start_y = conv_type::upscale(y));
434 		m_status = status_move_to;
435 	}
436 
437 	//------------------------------------------------------------------------
438 	template<class Clip>
line_to_d(double x,double y)439 	void rasterizer_scanline_aa_subpix<Clip>::line_to_d(double x, double y)
440 	{
441 		m_clipper.line_to(m_outline,
442 						  conv_type::upscale(x),
443 						  conv_type::upscale(y));
444 		m_status = status_line_to;
445 	}
446 
447 	//------------------------------------------------------------------------
448 	template<class Clip>
add_vertex(double x,double y,unsigned cmd)449 	void rasterizer_scanline_aa_subpix<Clip>::add_vertex(double x, double y, unsigned cmd)
450 	{
451 		if(is_move_to(cmd))
452 		{
453 			move_to_d(x, y);
454 		}
455 		else
456 		if(is_vertex(cmd))
457 		{
458 			line_to_d(x, y);
459 		}
460 		else
461 		if(is_close(cmd))
462 		{
463 			close_polygon();
464 		}
465 	}
466 
467 	//------------------------------------------------------------------------
468 	template<class Clip>
edge(int x1,int y1,int x2,int y2)469 	void rasterizer_scanline_aa_subpix<Clip>::edge(int x1, int y1, int x2, int y2)
470 	{
471 		if(m_outline.sorted()) reset();
472 		m_clipper.move_to(conv_type::downscale(x1), conv_type::downscale(y1));
473 		m_clipper.line_to(m_outline,
474 						  conv_type::downscale(x2),
475 						  conv_type::downscale(y2));
476 		m_status = status_move_to;
477 	}
478 
479 	//------------------------------------------------------------------------
480 	template<class Clip>
edge_d(double x1,double y1,double x2,double y2)481 	void rasterizer_scanline_aa_subpix<Clip>::edge_d(double x1, double y1,
482 											  double x2, double y2)
483 	{
484 		if(m_outline.sorted()) reset();
485 		m_clipper.move_to(conv_type::upscale(x1), conv_type::upscale(y1));
486 		m_clipper.line_to(m_outline,
487 						  conv_type::upscale(x2),
488 						  conv_type::upscale(y2));
489 		m_status = status_move_to;
490 	}
491 
492 	//------------------------------------------------------------------------
493 	template<class Clip>
sort()494 	void rasterizer_scanline_aa_subpix<Clip>::sort()
495 	{
496 		m_outline.sort_cells();
497 	}
498 
499 	//------------------------------------------------------------------------
500 	template<class Clip>
rewind_scanlines()501 	AGG_INLINE bool rasterizer_scanline_aa_subpix<Clip>::rewind_scanlines()
502 	{
503 		if(m_auto_close) close_polygon();
504 		m_outline.sort_cells();
505 		if(m_outline.total_cells() == 0)
506 		{
507 			return false;
508 		}
509 		m_scan_y = m_outline.min_y();
510 		return true;
511 	}
512 
513 
514 	//------------------------------------------------------------------------
515 	template<class Clip>
navigate_scanline(int y)516 	AGG_INLINE bool rasterizer_scanline_aa_subpix<Clip>::navigate_scanline(int y)
517 	{
518 		if(m_auto_close) close_polygon();
519 		m_outline.sort_cells();
520 		if(m_outline.total_cells() == 0 ||
521 		   y < m_outline.min_y() ||
522 		   y > m_outline.max_y())
523 		{
524 			return false;
525 		}
526 		m_scan_y = y;
527 		return true;
528 	}
529 
530 	//------------------------------------------------------------------------
531 	template<class Clip>
hit_test(int tx,int ty)532 	bool rasterizer_scanline_aa_subpix<Clip>::hit_test(int tx, int ty)
533 	{
534 		if(!navigate_scanline(ty)) return false;
535 		scanline_hit_test sl(tx);
536 		sweep_scanline(sl);
537 		return sl.hit();
538 	}
539 
540 
541 
542 }
543 
544 
545 
546 #endif
547 
548