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
18namespace 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		//--------------------------------------------------------------------
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>
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);
77		void auto_close(bool flag) { m_auto_close = flag; }
78
79		//--------------------------------------------------------------------
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		//--------------------------------------------------------------------
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		//--------------------------------------------------------------------
126		int min_x() const { return m_outline.min_x() / 3; }
127		int min_y() const { return m_outline.min_y(); }
128		int max_x() const { return m_outline.max_x() / 3; }
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		//--------------------------------------------------------------------
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		//--------------------------------------------------------------------
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>
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>
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>
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>
388	void rasterizer_scanline_aa_subpix<Clip>::reset_clipping()
389	{
390		reset();
391		m_clipper.reset_clipping();
392	}
393
394	//------------------------------------------------------------------------
395	template<class Clip>
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>
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>
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>
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>
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>
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>
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>
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>
494	void rasterizer_scanline_aa_subpix<Clip>::sort()
495	{
496		m_outline.sort_cells();
497	}
498
499	//------------------------------------------------------------------------
500	template<class Clip>
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>
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>
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