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