Anti-Grain Geometry Tutorial
agg_rasterizer_cells_aa.h
1 //----------------------------------------------------------------------------
2 // Anti-Grain Geometry - Version 2.4
3 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
4 //
5 // Permission to copy, use, modify, sell and distribute this software
6 // is granted provided this copyright notice appears in all copies.
7 // This software is provided "as is" without express or implied
8 // warranty, and with no claim as to its suitability for any purpose.
9 //
10 //----------------------------------------------------------------------------
11 //
12 // The author gratefully acknowleges the support of David Turner,
13 // Robert Wilhelm, and Werner Lemberg - the authors of the FreeType
14 // libray - in producing this work. See http://www.freetype.org for details.
15 //
16 //----------------------------------------------------------------------------
17 // Contact: mcseem@antigrain.com
18 // mcseemagg@yahoo.com
19 // http://www.antigrain.com
20 //----------------------------------------------------------------------------
21 //
22 // Adaptation for 32-bit screen coordinates has been sponsored by
23 // Liberty Technology Systems, Inc., visit http://lib-sys.com
24 //
25 // Liberty Technology Systems, Inc. is the provider of
26 // PostScript and PDF technology for software developers.
27 //
28 //----------------------------------------------------------------------------
29 #ifndef AGG_RASTERIZER_CELLS_AA_INCLUDED
30 #define AGG_RASTERIZER_CELLS_AA_INCLUDED
31 
32 #include <cstring>
33 #include <cstdlib>
34 #include <limits>
35 #include "agg_math.h"
36 #include "agg_array.h"
37 
38 
39 namespace agg
40 {
41 
42  //-----------------------------------------------------rasterizer_cells_aa
43  // An internal class that implements the main rasterization algorithm.
44  // Used in the rasterizer. Should not be used direcly.
45  template<class Cell> class rasterizer_cells_aa
46  {
47  enum cell_block_scale_e
48  {
49  cell_block_shift = 12,
50  cell_block_size = 1 << cell_block_shift,
51  cell_block_mask = cell_block_size - 1,
52  cell_block_pool = 256
53  };
54 
55  struct sorted_y
56  {
57  unsigned start;
58  unsigned num;
59  };
60 
61  public:
62  typedef Cell cell_type;
64 
66  rasterizer_cells_aa(unsigned cell_block_limit=1024);
67 
68  void reset();
69  void style(const cell_type& style_cell);
70  void line(int x1, int y1, int x2, int y2);
71 
72  int min_x() const { return m_min_x; }
73  int min_y() const { return m_min_y; }
74  int max_x() const { return m_max_x; }
75  int max_y() const { return m_max_y; }
76 
77  void sort_cells();
78 
79  unsigned total_cells() const
80  {
81  return m_num_cells;
82  }
83 
84  unsigned scanline_num_cells(unsigned y) const
85  {
86  return m_sorted_y[y - m_min_y].num;
87  }
88 
89  const cell_type* const* scanline_cells(unsigned y) const
90  {
91  return m_sorted_cells.data() + m_sorted_y[y - m_min_y].start;
92  }
93 
94  bool sorted() const { return m_sorted; }
95 
96  private:
97  rasterizer_cells_aa(const self_type&);
98  const self_type& operator = (const self_type&);
99 
100  void set_curr_cell(int x, int y);
101  void add_curr_cell();
102  void render_hline(int ey, int x1, int y1, int x2, int y2);
103  void allocate_block();
104 
105  private:
106  unsigned m_num_blocks;
107  unsigned m_max_blocks;
108  unsigned m_curr_block;
109  unsigned m_num_cells;
110  unsigned m_cell_block_limit;
111  cell_type** m_cells;
112  cell_type* m_curr_cell_ptr;
113  pod_vector<cell_type*> m_sorted_cells;
114  pod_vector<sorted_y> m_sorted_y;
115  cell_type m_curr_cell;
116  cell_type m_style_cell;
117  int m_min_x;
118  int m_min_y;
119  int m_max_x;
120  int m_max_y;
121  bool m_sorted;
122  };
123 
124 
125 
126 
127  //------------------------------------------------------------------------
128  template<class Cell>
130  {
131  if(m_num_blocks)
132  {
133  cell_type** ptr = m_cells + m_num_blocks - 1;
134  while(m_num_blocks--)
135  {
136  pod_allocator<cell_type>::deallocate(*ptr, cell_block_size);
137  ptr--;
138  }
139  pod_allocator<cell_type*>::deallocate(m_cells, m_max_blocks);
140  }
141  }
142 
143  //------------------------------------------------------------------------
144  template<class Cell>
145  rasterizer_cells_aa<Cell>::rasterizer_cells_aa(unsigned cell_block_limit) :
146  m_num_blocks(0),
147  m_max_blocks(0),
148  m_curr_block(0),
149  m_num_cells(0),
150  m_cell_block_limit(cell_block_limit),
151  m_cells(0),
152  m_curr_cell_ptr(0),
153  m_sorted_cells(),
154  m_sorted_y(),
155  m_min_x(std::numeric_limits<int>::max()),
156  m_min_y(std::numeric_limits<int>::max()),
157  m_max_x(std::numeric_limits<int>::min()),
158  m_max_y(std::numeric_limits<int>::min()),
159  m_sorted(false)
160  {
161  m_style_cell.initial();
162  m_curr_cell.initial();
163  }
164 
165  //------------------------------------------------------------------------
166  template<class Cell>
168  {
169  m_num_cells = 0;
170  m_curr_block = 0;
171  m_curr_cell.initial();
172  m_style_cell.initial();
173  m_sorted = false;
174  m_min_x = std::numeric_limits<int>::max();
175  m_min_y = std::numeric_limits<int>::max();
176  m_max_x = std::numeric_limits<int>::min();
177  m_max_y = std::numeric_limits<int>::min();
178  }
179 
180  //------------------------------------------------------------------------
181  template<class Cell>
183  {
184  if(m_curr_cell.area | m_curr_cell.cover)
185  {
186  if((m_num_cells & cell_block_mask) == 0)
187  {
188  if(m_num_blocks >= m_cell_block_limit) return;
189  allocate_block();
190  }
191  *m_curr_cell_ptr++ = m_curr_cell;
192  ++m_num_cells;
193  }
194  }
195 
196  //------------------------------------------------------------------------
197  template<class Cell>
198  AGG_INLINE void rasterizer_cells_aa<Cell>::set_curr_cell(int x, int y)
199  {
200  if(m_curr_cell.not_equal(x, y, m_style_cell))
201  {
202  add_curr_cell();
203  m_curr_cell.style(m_style_cell);
204  m_curr_cell.x = x;
205  m_curr_cell.y = y;
206  m_curr_cell.cover = 0;
207  m_curr_cell.area = 0;
208  }
209  }
210 
211  //------------------------------------------------------------------------
212  template<class Cell>
213  AGG_INLINE void rasterizer_cells_aa<Cell>::render_hline(int ey,
214  int x1, int y1,
215  int x2, int y2)
216  {
217  int ex1 = x1 >> poly_subpixel_shift;
218  int ex2 = x2 >> poly_subpixel_shift;
219  int fx1 = x1 & poly_subpixel_mask;
220  int fx2 = x2 & poly_subpixel_mask;
221 
222  int delta, p, first;
223  long long dx;
224  int incr, lift, mod, rem;
225 
226  //trivial case. Happens often
227  if(y1 == y2)
228  {
229  set_curr_cell(ex2, ey);
230  return;
231  }
232 
233  //everything is located in a single cell. That is easy!
234  if(ex1 == ex2)
235  {
236  delta = y2 - y1;
237  m_curr_cell.cover += delta;
238  m_curr_cell.area += (fx1 + fx2) * delta;
239  return;
240  }
241 
242  //ok, we'll have to render a run of adjacent cells on the same
243  //hline...
244  p = (poly_subpixel_scale - fx1) * (y2 - y1);
245  first = poly_subpixel_scale;
246  incr = 1;
247 
248  dx = (long long)x2 - (long long)x1;
249 
250  if(dx < 0)
251  {
252  p = fx1 * (y2 - y1);
253  first = 0;
254  incr = -1;
255  dx = -dx;
256  }
257 
258  delta = (int)(p / dx);
259  mod = (int)(p % dx);
260 
261  if(mod < 0)
262  {
263  delta--;
264  mod += dx;
265  }
266 
267  m_curr_cell.cover += delta;
268  m_curr_cell.area += (fx1 + first) * delta;
269 
270  ex1 += incr;
271  set_curr_cell(ex1, ey);
272  y1 += delta;
273 
274  if(ex1 != ex2)
275  {
276  p = poly_subpixel_scale * (y2 - y1 + delta);
277  lift = (int)(p / dx);
278  rem = (int)(p % dx);
279 
280  if (rem < 0)
281  {
282  lift--;
283  rem += dx;
284  }
285 
286  mod -= dx;
287 
288  while (ex1 != ex2)
289  {
290  delta = lift;
291  mod += rem;
292  if(mod >= 0)
293  {
294  mod -= dx;
295  delta++;
296  }
297 
298  m_curr_cell.cover += delta;
299  m_curr_cell.area += poly_subpixel_scale * delta;
300  y1 += delta;
301  ex1 += incr;
302  set_curr_cell(ex1, ey);
303  }
304  }
305  delta = y2 - y1;
306  m_curr_cell.cover += delta;
307  m_curr_cell.area += (fx2 + poly_subpixel_scale - first) * delta;
308  }
309 
310  //------------------------------------------------------------------------
311  template<class Cell>
312  AGG_INLINE void rasterizer_cells_aa<Cell>::style(const cell_type& style_cell)
313  {
314  m_style_cell.style(style_cell);
315  }
316 
317  //------------------------------------------------------------------------
318  template<class Cell>
319  void rasterizer_cells_aa<Cell>::line(int x1, int y1, int x2, int y2)
320  {
321  enum dx_limit_e { dx_limit = 16384 << poly_subpixel_shift };
322 
323  long long dx = (long long)x2 - (long long)x1;
324 
325  if(dx >= dx_limit || dx <= -dx_limit)
326  {
327  int cx = (int)(((long long)x1 + (long long)x2) >> 1);
328  int cy = (int)(((long long)y1 + (long long)y2) >> 1);
329  line(x1, y1, cx, cy);
330  line(cx, cy, x2, y2);
331  }
332 
333  long long dy = (long long)y2 - (long long)y1;
334  int ex1 = x1 >> poly_subpixel_shift;
335  int ex2 = x2 >> poly_subpixel_shift;
336  int ey1 = y1 >> poly_subpixel_shift;
337  int ey2 = y2 >> poly_subpixel_shift;
338  int fy1 = y1 & poly_subpixel_mask;
339  int fy2 = y2 & poly_subpixel_mask;
340 
341  int x_from, x_to;
342  int rem, mod, lift, delta, first, incr;
343  long long p;
344 
345  if(ex1 < m_min_x) m_min_x = ex1;
346  if(ex1 > m_max_x) m_max_x = ex1;
347  if(ey1 < m_min_y) m_min_y = ey1;
348  if(ey1 > m_max_y) m_max_y = ey1;
349  if(ex2 < m_min_x) m_min_x = ex2;
350  if(ex2 > m_max_x) m_max_x = ex2;
351  if(ey2 < m_min_y) m_min_y = ey2;
352  if(ey2 > m_max_y) m_max_y = ey2;
353 
354  set_curr_cell(ex1, ey1);
355 
356  //everything is on a single hline
357  if(ey1 == ey2)
358  {
359  render_hline(ey1, x1, fy1, x2, fy2);
360  return;
361  }
362 
363  //Vertical line - we have to calculate start and end cells,
364  //and then - the common values of the area and coverage for
365  //all cells of the line. We know exactly there's only one
366  //cell, so, we don't have to call render_hline().
367  incr = 1;
368  if(dx == 0)
369  {
370  int ex = x1 >> poly_subpixel_shift;
371  int two_fx = (x1 - (ex << poly_subpixel_shift)) << 1;
372  int area;
373 
374  first = poly_subpixel_scale;
375  if(dy < 0)
376  {
377  first = 0;
378  incr = -1;
379  }
380 
381  x_from = x1;
382 
383  //render_hline(ey1, x_from, fy1, x_from, first);
384  delta = first - fy1;
385  m_curr_cell.cover += delta;
386  m_curr_cell.area += two_fx * delta;
387 
388  ey1 += incr;
389  set_curr_cell(ex, ey1);
390 
391  delta = first + first - poly_subpixel_scale;
392  area = two_fx * delta;
393  while(ey1 != ey2)
394  {
395  //render_hline(ey1, x_from, poly_subpixel_scale - first, x_from, first);
396  m_curr_cell.cover = delta;
397  m_curr_cell.area = area;
398  ey1 += incr;
399  set_curr_cell(ex, ey1);
400  }
401  //render_hline(ey1, x_from, poly_subpixel_scale - first, x_from, fy2);
402  delta = fy2 - poly_subpixel_scale + first;
403  m_curr_cell.cover += delta;
404  m_curr_cell.area += two_fx * delta;
405  return;
406  }
407 
408  //ok, we have to render several hlines
409  p = (poly_subpixel_scale - fy1) * dx;
410  first = poly_subpixel_scale;
411 
412  if(dy < 0)
413  {
414  p = fy1 * dx;
415  first = 0;
416  incr = -1;
417  dy = -dy;
418  }
419 
420  delta = (int)(p / dy);
421  mod = (int)(p % dy);
422 
423  if(mod < 0)
424  {
425  delta--;
426  mod += dy;
427  }
428 
429  x_from = x1 + delta;
430  render_hline(ey1, x1, fy1, x_from, first);
431 
432  ey1 += incr;
433  set_curr_cell(x_from >> poly_subpixel_shift, ey1);
434 
435  if(ey1 != ey2)
436  {
437  p = poly_subpixel_scale * dx;
438  lift = (int)(p / dy);
439  rem = (int)(p % dy);
440 
441  if(rem < 0)
442  {
443  lift--;
444  rem += dy;
445  }
446  mod -= dy;
447 
448  while(ey1 != ey2)
449  {
450  delta = lift;
451  mod += rem;
452  if (mod >= 0)
453  {
454  mod -= dy;
455  delta++;
456  }
457 
458  x_to = x_from + delta;
459  render_hline(ey1, x_from, poly_subpixel_scale - first, x_to, first);
460  x_from = x_to;
461 
462  ey1 += incr;
463  set_curr_cell(x_from >> poly_subpixel_shift, ey1);
464  }
465  }
466  render_hline(ey1, x_from, poly_subpixel_scale - first, x2, fy2);
467  }
468 
469  //------------------------------------------------------------------------
470  template<class Cell>
472  {
473  if(m_curr_block >= m_num_blocks)
474  {
475  if(m_num_blocks >= m_max_blocks)
476  {
477  cell_type** new_cells =
479  cell_block_pool);
480 
481  if(m_cells)
482  {
483  std::memcpy(new_cells, m_cells, m_max_blocks * sizeof(cell_type*));
484  pod_allocator<cell_type*>::deallocate(m_cells, m_max_blocks);
485  }
486  m_cells = new_cells;
487  m_max_blocks += cell_block_pool;
488  }
489 
490  m_cells[m_num_blocks++] =
491  pod_allocator<cell_type>::allocate(cell_block_size);
492 
493  }
494  m_curr_cell_ptr = m_cells[m_curr_block++];
495  }
496 
497 
498 
499  //------------------------------------------------------------------------
500  template <class T> static AGG_INLINE void swap_cells(T* a, T* b)
501  {
502  T temp = *a;
503  *a = *b;
504  *b = temp;
505  }
506 
507 
508  //------------------------------------------------------------------------
509  enum
510  {
511  qsort_threshold = 9
512  };
513 
514 
515  //------------------------------------------------------------------------
516  template<class Cell>
517  void qsort_cells(Cell** start, unsigned num)
518  {
519  Cell** stack[80];
520  Cell*** top;
521  Cell** limit;
522  Cell** base;
523 
524  limit = start + num;
525  base = start;
526  top = stack;
527 
528  for (;;)
529  {
530  int len = int(limit - base);
531 
532  Cell** i;
533  Cell** j;
534  Cell** pivot;
535 
536  if(len > qsort_threshold)
537  {
538  // we use base + len/2 as the pivot
539  pivot = base + len / 2;
540  swap_cells(base, pivot);
541 
542  i = base + 1;
543  j = limit - 1;
544 
545  // now ensure that *i <= *base <= *j
546  if((*j)->x < (*i)->x)
547  {
548  swap_cells(i, j);
549  }
550 
551  if((*base)->x < (*i)->x)
552  {
553  swap_cells(base, i);
554  }
555 
556  if((*j)->x < (*base)->x)
557  {
558  swap_cells(base, j);
559  }
560 
561  for(;;)
562  {
563  int x = (*base)->x;
564  do i++; while( (*i)->x < x );
565  do j--; while( x < (*j)->x );
566 
567  if(i > j)
568  {
569  break;
570  }
571 
572  swap_cells(i, j);
573  }
574 
575  swap_cells(base, j);
576 
577  // now, push the largest sub-array
578  if(j - base > limit - i)
579  {
580  top[0] = base;
581  top[1] = j;
582  base = i;
583  }
584  else
585  {
586  top[0] = i;
587  top[1] = limit;
588  limit = j;
589  }
590  top += 2;
591  }
592  else
593  {
594  // the sub-array is small, perform insertion sort
595  j = base;
596  i = j + 1;
597 
598  for(; i < limit; j = i, i++)
599  {
600  for(; j[1]->x < (*j)->x; j--)
601  {
602  swap_cells(j + 1, j);
603  if (j == base)
604  {
605  break;
606  }
607  }
608  }
609 
610  if(top > stack)
611  {
612  top -= 2;
613  base = top[0];
614  limit = top[1];
615  }
616  else
617  {
618  break;
619  }
620  }
621  }
622  }
623 
624 
625  //------------------------------------------------------------------------
626  template<class Cell>
628  {
629  if(m_sorted) return; //Perform sort only the first time.
630 
631  add_curr_cell();
632  m_curr_cell.x = std::numeric_limits<int>::max();
633  m_curr_cell.y = std::numeric_limits<int>::max();
634  m_curr_cell.cover = 0;
635  m_curr_cell.area = 0;
636 
637  if(m_num_cells == 0) return;
638 
639 // DBG: Check to see if min/max works well.
640 //for(unsigned nc = 0; nc < m_num_cells; nc++)
641 //{
642 // cell_type* cell = m_cells[nc >> cell_block_shift] + (nc & cell_block_mask);
643 // if(cell->x < m_min_x ||
644 // cell->y < m_min_y ||
645 // cell->x > m_max_x ||
646 // cell->y > m_max_y)
647 // {
648 // cell = cell; // Breakpoint here
649 // }
650 //}
651  // Allocate the array of cell pointers
652  m_sorted_cells.allocate(m_num_cells, 16);
653 
654  // Allocate and zero the Y array
655  m_sorted_y.allocate(m_max_y - m_min_y + 1, 16);
656  m_sorted_y.zero();
657 
658  // Create the Y-histogram (count the numbers of cells for each Y)
659  cell_type** block_ptr = m_cells;
660  cell_type* cell_ptr;
661  unsigned nb = m_num_cells;
662  unsigned i;
663  while(nb)
664  {
665  cell_ptr = *block_ptr++;
666  i = (nb > cell_block_size) ? unsigned(cell_block_size) : nb;
667  nb -= i;
668  while(i--)
669  {
670  m_sorted_y[cell_ptr->y - m_min_y].start++;
671  ++cell_ptr;
672  }
673  }
674 
675  // Convert the Y-histogram into the array of starting indexes
676  unsigned start = 0;
677  for(i = 0; i < m_sorted_y.size(); i++)
678  {
679  unsigned v = m_sorted_y[i].start;
680  m_sorted_y[i].start = start;
681  start += v;
682  }
683 
684  // Fill the cell pointer array sorted by Y
685  block_ptr = m_cells;
686  nb = m_num_cells;
687  while(nb)
688  {
689  cell_ptr = *block_ptr++;
690  i = (nb > cell_block_size) ? unsigned(cell_block_size) : nb;
691  nb -= i;
692  while(i--)
693  {
694  sorted_y& curr_y = m_sorted_y[cell_ptr->y - m_min_y];
695  m_sorted_cells[curr_y.start + curr_y.num] = cell_ptr;
696  ++curr_y.num;
697  ++cell_ptr;
698  }
699  }
700 
701  // Finally arrange the X-arrays
702  for(i = 0; i < m_sorted_y.size(); i++)
703  {
704  const sorted_y& curr_y = m_sorted_y[i];
705  if(curr_y.num)
706  {
707  qsort_cells(m_sorted_cells.data() + curr_y.start, curr_y.num);
708  }
709  }
710  m_sorted = true;
711  }
712 
713 
714 
715  //------------------------------------------------------scanline_hit_test
717  {
718  public:
719  scanline_hit_test(int x) : m_x(x), m_hit(false) {}
720 
721  void reset_spans() {}
722  void finalize(int) {}
723  void add_cell(int x, int)
724  {
725  if(m_x == x) m_hit = true;
726  }
727  void add_span(int x, int len, int)
728  {
729  if(m_x >= x && m_x < x+len) m_hit = true;
730  }
731  unsigned num_spans() const { return 1; }
732  bool hit() const { return m_hit; }
733 
734  private:
735  int m_x;
736  bool m_hit;
737  };
738 
739 
740 }
741 
742 #endif
Definition: agg_arc.cpp:24