Click here to Skip to main content
15,896,557 members
Articles / Mobile Apps / Android

Android ImageView and Drawable with SVG Support

Rate me:
Please Sign up or sign in to vote.
4.75/5 (11 votes)
14 Dec 2010CPOL3 min read 155.5K   5.7K   33  
This article introduces custom classes SvgImageView and SvgDrawable which allow to use SVG image just like any other image.
//----------------------------------------------------------------------------
// Anti-Grain Geometry (AGG) - Version 2.5
// A high quality rendering engine for C++
// Copyright (C) 2002-2006 Maxim Shemanarev
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://antigrain.com
// 
// AGG is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// AGG is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with AGG; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
// MA 02110-1301, USA.
//----------------------------------------------------------------------------

#ifndef AGG_SCANLINE_BOOLEAN_ALGEBRA_INCLUDED
#define AGG_SCANLINE_BOOLEAN_ALGEBRA_INCLUDED

#include <stdlib.h>
#include <math.h>
#include "agg_basics.h"


namespace agg
{

    //-----------------------------------------------sbool_combine_spans_bin
    // Functor.
    // Combine two binary encoded spans, i.e., when we don't have any
    // anti-aliasing information, but only X and Length. The function
    // is compatible with any type of scanlines.
    //----------------
    template<class Scanline1, 
             class Scanline2, 
             class Scanline> 
    struct sbool_combine_spans_bin
    {
        void operator () (const typename Scanline1::const_iterator&, 
                          const typename Scanline2::const_iterator&, 
                          int x, unsigned len, 
                          Scanline& sl) const
        {
            sl.add_span(x, len, cover_full);
        }
    };



    //---------------------------------------------sbool_combine_spans_empty
    // Functor.
    // Combine two spans as empty ones. The functor does nothing
    // and is used to XOR binary spans.
    //----------------
    template<class Scanline1, 
             class Scanline2, 
             class Scanline> 
    struct sbool_combine_spans_empty
    {
        void operator () (const typename Scanline1::const_iterator&, 
                          const typename Scanline2::const_iterator&, 
                          int, unsigned, 
                          Scanline&) const
        {}
    };



    //--------------------------------------------------sbool_add_span_empty
    // Functor.
    // Add nothing. Used in conbine_shapes_sub
    //----------------
    template<class Scanline1, 
             class Scanline> 
    struct sbool_add_span_empty
    {
        void operator () (const typename Scanline1::const_iterator&, 
                          int, unsigned, 
                          Scanline&) const
        {}
    };


    //----------------------------------------------------sbool_add_span_bin
    // Functor.
    // Add a binary span
    //----------------
    template<class Scanline1, 
             class Scanline> 
    struct sbool_add_span_bin
    {
        void operator () (const typename Scanline1::const_iterator&, 
                          int x, unsigned len, 
                          Scanline& sl) const
        {
            sl.add_span(x, len, cover_full);
        }
    };

    


    //-----------------------------------------------------sbool_add_span_aa
    // Functor.
    // Add an anti-aliased span
    // anti-aliasing information, but only X and Length. The function
    // is compatible with any type of scanlines.
    //----------------
    template<class Scanline1, 
             class Scanline> 
    struct sbool_add_span_aa
    {
        void operator () (const typename Scanline1::const_iterator& span, 
                          int x, unsigned len, 
                          Scanline& sl) const
        {
            if(span->len < 0)
            {
                sl.add_span(x, len, *span->covers);
            }
            else
            if(span->len > 0)
            {
                const typename Scanline1::cover_type* covers = span->covers;
                if(span->x < x) covers += x - span->x;
                sl.add_cells(x, len, covers);
            }
        }
    };




    //----------------------------------------------sbool_intersect_spans_aa
    // Functor.
    // Intersect two spans preserving the anti-aliasing information.
    // The result is added to the "sl" scanline.
    //------------------
    template<class Scanline1, 
             class Scanline2, 
             class Scanline, 
             unsigned CoverShift = cover_shift> 
    struct sbool_intersect_spans_aa
    {
        enum cover_scale_e
        {
            cover_shift = CoverShift,
            cover_size  = 1 << cover_shift,
            cover_mask  = cover_size - 1,
            cover_full  = cover_mask
        };
        

        void operator () (const typename Scanline1::const_iterator& span1, 
                          const typename Scanline2::const_iterator& span2, 
                          int x, unsigned len, 
                          Scanline& sl) const
        {
            unsigned cover;
            const typename Scanline1::cover_type* covers1;
            const typename Scanline2::cover_type* covers2;

            // Calculate the operation code and choose the 
            // proper combination algorithm.
            // 0 = Both spans are of AA type
            // 1 = span1 is solid, span2 is AA
            // 2 = span1 is AA, span2 is solid
            // 3 = Both spans are of solid type
            //-----------------
            switch((span1->len < 0) | ((span2->len < 0) << 1))
            {
            case 0:      // Both are AA spans
                covers1 = span1->covers;
                covers2 = span2->covers;
                if(span1->x < x) covers1 += x - span1->x;
                if(span2->x < x) covers2 += x - span2->x;
                do
                {
                    cover = *covers1++ * *covers2++;
                    sl.add_cell(x++, 
                                (cover == cover_full * cover_full) ?
                                cover_full : 
                                (cover >> cover_shift));
                }
                while(--len);
                break;

            case 1:      // span1 is solid, span2 is AA
                covers2 = span2->covers;
                if(span2->x < x) covers2 += x - span2->x;
                if(*(span1->covers) == cover_full)
                {
                    sl.add_cells(x, len, covers2);
                }
                else
                {
                    do
                    {
                        cover = *(span1->covers) * *covers2++;
                        sl.add_cell(x++, 
                                    (cover == cover_full * cover_full) ?
                                    cover_full : 
                                    (cover >> cover_shift));
                    }
                    while(--len);
                }
                break;

            case 2:      // span1 is AA, span2 is solid
                covers1 = span1->covers;
                if(span1->x < x) covers1 += x - span1->x;
                if(*(span2->covers) == cover_full)
                {
                    sl.add_cells(x, len, covers1);
                }
                else
                {
                    do
                    {
                        cover = *covers1++ * *(span2->covers);
                        sl.add_cell(x++, 
                                    (cover == cover_full * cover_full) ?
                                    cover_full : 
                                    (cover >> cover_shift));
                    }
                    while(--len);
                }
                break;

            case 3:      // Both are solid spans
                cover = *(span1->covers) * *(span2->covers);
                sl.add_span(x, len, 
                            (cover == cover_full * cover_full) ?
                            cover_full : 
                            (cover >> cover_shift));
                break;
            }
        }
    };






    //--------------------------------------------------sbool_unite_spans_aa
    // Functor.
    // Unite two spans preserving the anti-aliasing information.
    // The result is added to the "sl" scanline.
    //------------------
    template<class Scanline1, 
             class Scanline2, 
             class Scanline, 
             unsigned CoverShift = cover_shift> 
    struct sbool_unite_spans_aa
    {
        enum cover_scale_e
        {
            cover_shift = CoverShift,
            cover_size  = 1 << cover_shift,
            cover_mask  = cover_size - 1,
            cover_full  = cover_mask
        };
        

        void operator () (const typename Scanline1::const_iterator& span1, 
                          const typename Scanline2::const_iterator& span2, 
                          int x, unsigned len, 
                          Scanline& sl) const
        {
            unsigned cover;
            const typename Scanline1::cover_type* covers1;
            const typename Scanline2::cover_type* covers2;

            // Calculate the operation code and choose the 
            // proper combination algorithm.
            // 0 = Both spans are of AA type
            // 1 = span1 is solid, span2 is AA
            // 2 = span1 is AA, span2 is solid
            // 3 = Both spans are of solid type
            //-----------------
            switch((span1->len < 0) | ((span2->len < 0) << 1))
            {
            case 0:      // Both are AA spans
                covers1 = span1->covers;
                covers2 = span2->covers;
                if(span1->x < x) covers1 += x - span1->x;
                if(span2->x < x) covers2 += x - span2->x;
                do
                {
                    cover = cover_mask * cover_mask - 
                                (cover_mask - *covers1++) * 
                                (cover_mask - *covers2++);
                    sl.add_cell(x++, 
                                (cover == cover_full * cover_full) ?
                                cover_full : 
                                (cover >> cover_shift));
                }
                while(--len);
                break;

            case 1:      // span1 is solid, span2 is AA
                covers2 = span2->covers;
                if(span2->x < x) covers2 += x - span2->x;
                if(*(span1->covers) == cover_full)
                {
                    sl.add_span(x, len, cover_full);
                }
                else
                {
                    do
                    {
                        cover = cover_mask * cover_mask - 
                                    (cover_mask - *(span1->covers)) * 
                                    (cover_mask - *covers2++);
                        sl.add_cell(x++, 
                                    (cover == cover_full * cover_full) ?
                                    cover_full : 
                                    (cover >> cover_shift));
                    }
                    while(--len);
                }
                break;

            case 2:      // span1 is AA, span2 is solid
                covers1 = span1->covers;
                if(span1->x < x) covers1 += x - span1->x;
                if(*(span2->covers) == cover_full)
                {
                    sl.add_span(x, len, cover_full);
                }
                else
                {
                    do
                    {
                        cover = cover_mask * cover_mask - 
                                    (cover_mask - *covers1++) * 
                                    (cover_mask - *(span2->covers));
                        sl.add_cell(x++, 
                                    (cover == cover_full * cover_full) ?
                                    cover_full : 
                                    (cover >> cover_shift));
                    }
                    while(--len);
                }
                break;

            case 3:      // Both are solid spans
                cover = cover_mask * cover_mask - 
                            (cover_mask - *(span1->covers)) * 
                            (cover_mask - *(span2->covers));
                sl.add_span(x, len, 
                            (cover == cover_full * cover_full) ?
                            cover_full : 
                            (cover >> cover_shift));
                break;
            }
        }
    };


    //---------------------------------------------sbool_xor_formula_linear
    template<unsigned CoverShift = cover_shift> 
    struct sbool_xor_formula_linear
    {
        enum cover_scale_e
        {
            cover_shift = CoverShift,
            cover_size  = 1 << cover_shift,
            cover_mask  = cover_size - 1
        };

        static AGG_INLINE unsigned calculate(unsigned a, unsigned b)
        {
            unsigned cover = a + b;
            if(cover > cover_mask) cover = cover_mask + cover_mask - cover;
            return cover;
        }
    };


    //---------------------------------------------sbool_xor_formula_saddle
    template<unsigned CoverShift = cover_shift> 
    struct sbool_xor_formula_saddle
    {
        enum cover_scale_e
        {
            cover_shift = CoverShift,
            cover_size  = 1 << cover_shift,
            cover_mask  = cover_size - 1
        };

        static AGG_INLINE unsigned calculate(unsigned a, unsigned b)
        {
            unsigned k = a * b;
            if(k == cover_mask * cover_mask) return 0;

            a = (cover_mask * cover_mask - (a << cover_shift) + k) >> cover_shift;
            b = (cover_mask * cover_mask - (b << cover_shift) + k) >> cover_shift;
            return cover_mask - ((a * b) >> cover_shift);
        }
    };


    //-------------------------------------------sbool_xor_formula_abs_diff
    struct sbool_xor_formula_abs_diff
    {
        static AGG_INLINE unsigned calculate(unsigned a, unsigned b)
        {
            return unsigned(abs(int(a) - int(b)));
        }
    };



    //----------------------------------------------------sbool_xor_spans_aa
    // Functor.
    // XOR two spans preserving the anti-aliasing information.
    // The result is added to the "sl" scanline.
    //------------------
    template<class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class XorFormula,
             unsigned CoverShift = cover_shift> 
    struct sbool_xor_spans_aa
    {
        enum cover_scale_e
        {
            cover_shift = CoverShift,
            cover_size  = 1 << cover_shift,
            cover_mask  = cover_size - 1,
            cover_full  = cover_mask
        };
        

        void operator () (const typename Scanline1::const_iterator& span1, 
                          const typename Scanline2::const_iterator& span2, 
                          int x, unsigned len, 
                          Scanline& sl) const
        {
            unsigned cover;
            const typename Scanline1::cover_type* covers1;
            const typename Scanline2::cover_type* covers2;

            // Calculate the operation code and choose the 
            // proper combination algorithm.
            // 0 = Both spans are of AA type
            // 1 = span1 is solid, span2 is AA
            // 2 = span1 is AA, span2 is solid
            // 3 = Both spans are of solid type
            //-----------------
            switch((span1->len < 0) | ((span2->len < 0) << 1))
            {
            case 0:      // Both are AA spans
                covers1 = span1->covers;
                covers2 = span2->covers;
                if(span1->x < x) covers1 += x - span1->x;
                if(span2->x < x) covers2 += x - span2->x;
                do
                {
                    cover = XorFormula::calculate(*covers1++, *covers2++);
                    if(cover) sl.add_cell(x, cover);
                    ++x;
                }
                while(--len);
                break;

            case 1:      // span1 is solid, span2 is AA
                covers2 = span2->covers;
                if(span2->x < x) covers2 += x - span2->x;
                do
                {
                    cover = XorFormula::calculate(*(span1->covers), *covers2++);
                    if(cover) sl.add_cell(x, cover);
                    ++x;
                }
                while(--len);
                break;

            case 2:      // span1 is AA, span2 is solid
                covers1 = span1->covers;
                if(span1->x < x) covers1 += x - span1->x;
                do
                {
                    cover = XorFormula::calculate(*covers1++, *(span2->covers));
                    if(cover) sl.add_cell(x, cover);
                    ++x;
                }
                while(--len);
                break;

            case 3:      // Both are solid spans
                cover = XorFormula::calculate(*(span1->covers), *(span2->covers));
                if(cover) sl.add_span(x, len, cover);
                break;

            }
        }
    };





    //-----------------------------------------------sbool_subtract_spans_aa
    // Functor.
    // Unite two spans preserving the anti-aliasing information.
    // The result is added to the "sl" scanline.
    //------------------
    template<class Scanline1, 
             class Scanline2, 
             class Scanline, 
             unsigned CoverShift = cover_shift> 
    struct sbool_subtract_spans_aa
    {
        enum cover_scale_e
        {
            cover_shift = CoverShift,
            cover_size  = 1 << cover_shift,
            cover_mask  = cover_size - 1,
            cover_full  = cover_mask
        };
        

        void operator () (const typename Scanline1::const_iterator& span1, 
                          const typename Scanline2::const_iterator& span2, 
                          int x, unsigned len, 
                          Scanline& sl) const
        {
            unsigned cover;
            const typename Scanline1::cover_type* covers1;
            const typename Scanline2::cover_type* covers2;

            // Calculate the operation code and choose the 
            // proper combination algorithm.
            // 0 = Both spans are of AA type
            // 1 = span1 is solid, span2 is AA
            // 2 = span1 is AA, span2 is solid
            // 3 = Both spans are of solid type
            //-----------------
            switch((span1->len < 0) | ((span2->len < 0) << 1))
            {
            case 0:      // Both are AA spans
                covers1 = span1->covers;
                covers2 = span2->covers;
                if(span1->x < x) covers1 += x - span1->x;
                if(span2->x < x) covers2 += x - span2->x;
                do
                {
                    cover = *covers1++ * (cover_mask - *covers2++);
                    if(cover)
                    {
                        sl.add_cell(x, 
                                    (cover == cover_full * cover_full) ?
                                    cover_full : 
                                    (cover >> cover_shift));
                    }
                    ++x;
                }
                while(--len);
                break;

            case 1:      // span1 is solid, span2 is AA
                covers2 = span2->covers;
                if(span2->x < x) covers2 += x - span2->x;
                do
                {
                    cover = *(span1->covers) * (cover_mask - *covers2++);
                    if(cover)
                    {
                        sl.add_cell(x, 
                                    (cover == cover_full * cover_full) ?
                                    cover_full : 
                                    (cover >> cover_shift));
                    }
                    ++x;
                }
                while(--len);
                break;

            case 2:      // span1 is AA, span2 is solid
                covers1 = span1->covers;
                if(span1->x < x) covers1 += x - span1->x;
                if(*(span2->covers) != cover_full)
                {
                    do
                    {
                        cover = *covers1++ * (cover_mask - *(span2->covers));
                        if(cover)
                        {
                            sl.add_cell(x, 
                                        (cover == cover_full * cover_full) ?
                                        cover_full : 
                                        (cover >> cover_shift));
                        }
                        ++x;
                    }
                    while(--len);
                }
                break;

            case 3:      // Both are solid spans
                cover = *(span1->covers) * (cover_mask - *(span2->covers));
                if(cover)
                {
                    sl.add_span(x, len, 
                                (cover == cover_full * cover_full) ?
                                cover_full : 
                                (cover >> cover_shift));
                }
                break;
            }
        }
    };






    //--------------------------------------------sbool_add_spans_and_render
    template<class Scanline1, 
             class Scanline, 
             class Renderer, 
             class AddSpanFunctor>
    void sbool_add_spans_and_render(const Scanline1& sl1, 
                                    Scanline& sl, 
                                    Renderer& ren, 
                                    AddSpanFunctor add_span)
    {
        sl.reset_spans();
        typename Scanline1::const_iterator span = sl1.begin();
        unsigned num_spans = sl1.num_spans();
        for(;;)
        {
            add_span(span, span->x, abs((int)span->len), sl);
            if(--num_spans == 0) break;
            ++span;
        }
        sl.finalize(sl1.y());
        ren.render(sl);
    }







    //---------------------------------------------sbool_intersect_scanlines
    // Intersect two scanlines, "sl1" and "sl2" and generate a new "sl" one.
    // The combine_spans functor can be of type sbool_combine_spans_bin or
    // sbool_intersect_spans_aa. First is a general functor to combine
    // two spans without Anti-Aliasing, the second preserves the AA
    // information, but works slower
    //
    template<class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class CombineSpansFunctor>
    void sbool_intersect_scanlines(const Scanline1& sl1, 
                                   const Scanline2& sl2, 
                                   Scanline& sl, 
                                   CombineSpansFunctor combine_spans)
    {
        sl.reset_spans();

        unsigned num1 = sl1.num_spans();
        if(num1 == 0) return;

        unsigned num2 = sl2.num_spans();
        if(num2 == 0) return;

        typename Scanline1::const_iterator span1 = sl1.begin();
        typename Scanline2::const_iterator span2 = sl2.begin();

        while(num1 && num2)
        {
            int xb1 = span1->x;
            int xb2 = span2->x;
            int xe1 = xb1 + abs((int)span1->len) - 1;
            int xe2 = xb2 + abs((int)span2->len) - 1;

            // Determine what spans we should advance in the next step
            // The span with the least ending X should be advanced
            // advance_both is just an optimization when we ending 
            // coordinates are the same and we can advance both
            //--------------
            bool advance_span1 = xe1 <  xe2;
            bool advance_both  = xe1 == xe2;

            // Find the intersection of the spans
            // and check if they intersect
            //--------------
            if(xb1 < xb2) xb1 = xb2;
            if(xe1 > xe2) xe1 = xe2;
            if(xb1 <= xe1)
            {
                combine_spans(span1, span2, xb1, xe1 - xb1 + 1, sl);
            }

            // Advance the spans
            //--------------
            if(advance_both)
            {
                --num1;
                --num2;
                if(num1) ++span1;
                if(num2) ++span2;
            }
            else
            {
                if(advance_span1)
                {
                    --num1;
                    if(num1) ++span1;
                }
                else
                {
                    --num2;
                    if(num2) ++span2;
                }
            }
        }
    }








    //------------------------------------------------sbool_intersect_shapes
    // Intersect the scanline shapes. Here the "Scanline Generator" 
    // abstraction is used. ScanlineGen1 and ScanlineGen2 are 
    // the generators, and can be of type rasterizer_scanline_aa<>.
    // There function requires three scanline containers that can be of
    // different types.
    // "sl1" and "sl2" are used to retrieve scanlines from the generators,
    // "sl" is ised as the resulting scanline to render it.
    // The external "sl1" and "sl2" are used only for the sake of
    // optimization and reusing of the scanline objects.
    // the function calls sbool_intersect_scanlines with CombineSpansFunctor 
    // as the last argument. See sbool_intersect_scanlines for details.
    //----------
    template<class ScanlineGen1, 
             class ScanlineGen2, 
             class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class Renderer,
             class CombineSpansFunctor>
    void sbool_intersect_shapes(ScanlineGen1& sg1, ScanlineGen2& sg2,
                                Scanline1& sl1, Scanline2& sl2,
                                Scanline& sl, Renderer& ren, 
                                CombineSpansFunctor combine_spans)
    {
        // Prepare the scanline generators.
        // If anyone of them doesn't contain 
        // any scanlines, then return.
        //-----------------
        if(!sg1.rewind_scanlines()) return;
        if(!sg2.rewind_scanlines()) return;

        // Get the bounding boxes
        //----------------
        rect_i r1(sg1.min_x(), sg1.min_y(), sg1.max_x(), sg1.max_y());
        rect_i r2(sg2.min_x(), sg2.min_y(), sg2.max_x(), sg2.max_y());

        // Calculate the intersection of the bounding 
        // boxes and return if they don't intersect.
        //-----------------
        rect_i ir = intersect_rectangles(r1, r2);
        if(!ir.is_valid()) return;

        // Reset the scanlines and get two first ones
        //-----------------
        sl.reset(ir.x1, ir.x2);
        sl1.reset(sg1.min_x(), sg1.max_x());
        sl2.reset(sg2.min_x(), sg2.max_x());
        if(!sg1.sweep_scanline(sl1)) return;
        if(!sg2.sweep_scanline(sl2)) return;

        ren.prepare();

        // The main loop
        // Here we synchronize the scanlines with 
        // the same Y coordinate, ignoring all other ones.
        // Only scanlines having the same Y-coordinate 
        // are to be combined.
        //-----------------
        for(;;)
        {
            while(sl1.y() < sl2.y())
            {
                if(!sg1.sweep_scanline(sl1)) return;
            }
            while(sl2.y() < sl1.y())
            {
                if(!sg2.sweep_scanline(sl2)) return;
            }

            if(sl1.y() == sl2.y())
            {
                // The Y coordinates are the same.
                // Combine the scanlines, render if they contain any spans,
                // and advance both generators to the next scanlines
                //----------------------
                sbool_intersect_scanlines(sl1, sl2, sl, combine_spans);
                if(sl.num_spans())
                {
                    sl.finalize(sl1.y());
                    ren.render(sl);
                }
                if(!sg1.sweep_scanline(sl1)) return;
                if(!sg2.sweep_scanline(sl2)) return;
            }
        }
    }







    //-------------------------------------------------sbool_unite_scanlines
    // Unite two scanlines, "sl1" and "sl2" and generate a new "sl" one.
    // The combine_spans functor can be of type sbool_combine_spans_bin or
    // sbool_intersect_spans_aa. First is a general functor to combine
    // two spans without Anti-Aliasing, the second preserves the AA
    // information, but works slower
    //
    template<class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class AddSpanFunctor1,
             class AddSpanFunctor2,
             class CombineSpansFunctor>
    void sbool_unite_scanlines(const Scanline1& sl1, 
                               const Scanline2& sl2, 
                               Scanline& sl, 
                               AddSpanFunctor1 add_span1,
                               AddSpanFunctor2 add_span2,
                               CombineSpansFunctor combine_spans)
    {
        sl.reset_spans();

        unsigned num1 = sl1.num_spans();
        unsigned num2 = sl2.num_spans();

        typename Scanline1::const_iterator span1;// = sl1.begin();
        typename Scanline2::const_iterator span2;// = sl2.begin();

        enum invalidation_e 
        { 
            invalid_b = 0xFFFFFFF, 
            invalid_e = invalid_b - 1 
        };

        // Initialize the spans as invalid
        //---------------
        int xb1 = invalid_b;
        int xb2 = invalid_b;
        int xe1 = invalid_e;
        int xe2 = invalid_e;

        // Initialize span1 if there are spans
        //---------------
        if(num1)
        {
            span1 = sl1.begin();
            xb1 = span1->x;
            xe1 = xb1 + abs((int)span1->len) - 1;
            --num1;
        }

        // Initialize span2 if there are spans
        //---------------
        if(num2)
        {
            span2 = sl2.begin();
            xb2 = span2->x;
            xe2 = xb2 + abs((int)span2->len) - 1;
            --num2;
        }


        for(;;)
        {
            // Retrieve a new span1 if it's invalid
            //----------------
            if(num1 && xb1 > xe1) 
            {
                --num1;
                ++span1;
                xb1 = span1->x;
                xe1 = xb1 + abs((int)span1->len) - 1;
            }

            // Retrieve a new span2 if it's invalid
            //----------------
            if(num2 && xb2 > xe2) 
            {
                --num2;
                ++span2;
                xb2 = span2->x;
                xe2 = xb2 + abs((int)span2->len) - 1;
            }

            if(xb1 > xe1 && xb2 > xe2) break;

            // Calculate the intersection
            //----------------
            int xb = xb1;
            int xe = xe1;
            if(xb < xb2) xb = xb2;
            if(xe > xe2) xe = xe2;
            int len = xe - xb + 1; // The length of the intersection
            if(len > 0)
            {
                // The spans intersect,
                // add the beginning of the span
                //----------------
                if(xb1 < xb2)
                {
                    add_span1(span1, xb1, xb2 - xb1, sl);
                    xb1 = xb2;
                }
                else
                if(xb2 < xb1)
                {
                    add_span2(span2, xb2, xb1 - xb2, sl);
                    xb2 = xb1;
                }

                // Add the combination part of the spans
                //----------------
                combine_spans(span1, span2, xb, len, sl);


                // Invalidate the fully processed span or both
                //----------------
                if(xe1 < xe2)
                {
                    // Invalidate span1 and eat
                    // the processed part of span2
                    //--------------
                    xb1 = invalid_b;    
                    xe1 = invalid_e;
                    xb2 += len;
                }
                else
                if(xe2 < xe1)
                {
                    // Invalidate span2 and eat
                    // the processed part of span1
                    //--------------
                    xb2 = invalid_b;  
                    xe2 = invalid_e;
                    xb1 += len;
                }
                else
                {
                    xb1 = invalid_b;  // Invalidate both
                    xb2 = invalid_b;
                    xe1 = invalid_e;
                    xe2 = invalid_e;
                }
            }
            else
            {
                // The spans do not intersect
                //--------------
                if(xb1 < xb2) 
                {
                    // Advance span1
                    //---------------
                    if(xb1 <= xe1)
                    {
                        add_span1(span1, xb1, xe1 - xb1 + 1, sl);
                    }
                    xb1 = invalid_b; // Invalidate
                    xe1 = invalid_e;
                }
                else
                {
                    // Advance span2
                    //---------------
                    if(xb2 <= xe2)
                    {
                        add_span2(span2, xb2, xe2 - xb2 + 1, sl);
                    }
                    xb2 = invalid_b; // Invalidate
                    xe2 = invalid_e;
                }
            }
        }
    }




    //----------------------------------------------------sbool_unite_shapes
    // Unite the scanline shapes. Here the "Scanline Generator" 
    // abstraction is used. ScanlineGen1 and ScanlineGen2 are 
    // the generators, and can be of type rasterizer_scanline_aa<>.
    // There function requires three scanline containers that can be 
    // of different type.
    // "sl1" and "sl2" are used to retrieve scanlines from the generators,
    // "sl" is ised as the resulting scanline to render it.
    // The external "sl1" and "sl2" are used only for the sake of
    // optimization and reusing of the scanline objects.
    // the function calls sbool_unite_scanlines with CombineSpansFunctor 
    // as the last argument. See sbool_unite_scanlines for details.
    //----------
    template<class ScanlineGen1, 
             class ScanlineGen2, 
             class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class Renderer,
             class AddSpanFunctor1,
             class AddSpanFunctor2,
             class CombineSpansFunctor>
    void sbool_unite_shapes(ScanlineGen1& sg1, ScanlineGen2& sg2,
                            Scanline1& sl1, Scanline2& sl2,
                            Scanline& sl, Renderer& ren, 
                            AddSpanFunctor1 add_span1,
                            AddSpanFunctor2 add_span2,
                            CombineSpansFunctor combine_spans)
    {
        // Prepare the scanline generators.
        // If anyone of them doesn't contain 
        // any scanlines, then return.
        //-----------------
        bool flag1 = sg1.rewind_scanlines();
        bool flag2 = sg2.rewind_scanlines();
        if(!flag1 && !flag2) return;

        // Get the bounding boxes
        //----------------
        rect_i r1(sg1.min_x(), sg1.min_y(), sg1.max_x(), sg1.max_y());
        rect_i r2(sg2.min_x(), sg2.min_y(), sg2.max_x(), sg2.max_y());

        // Calculate the union of the bounding boxes
        //-----------------
        rect_i ur(1,1,0,0);
             if(flag1 && flag2) ur = unite_rectangles(r1, r2);
        else if(flag1)          ur = r1;
        else if(flag2)          ur = r2;

        if(!ur.is_valid()) return;

        ren.prepare();

        // Reset the scanlines and get two first ones
        //-----------------
        sl.reset(ur.x1, ur.x2);
        if(flag1) 
        {
            sl1.reset(sg1.min_x(), sg1.max_x());
            flag1 = sg1.sweep_scanline(sl1);
        }

        if(flag2) 
        {
            sl2.reset(sg2.min_x(), sg2.max_x());
            flag2 = sg2.sweep_scanline(sl2);
        }

        // The main loop
        // Here we synchronize the scanlines with 
        // the same Y coordinate.
        //-----------------
        while(flag1 || flag2)
        {
            if(flag1 && flag2)
            {
                if(sl1.y() == sl2.y())
                {
                    // The Y coordinates are the same.
                    // Combine the scanlines, render if they contain any spans,
                    // and advance both generators to the next scanlines
                    //----------------------
                    sbool_unite_scanlines(sl1, sl2, sl, 
                                          add_span1, add_span2, combine_spans);
                    if(sl.num_spans())
                    {
                        sl.finalize(sl1.y());
                        ren.render(sl);
                    }
                    flag1 = sg1.sweep_scanline(sl1);
                    flag2 = sg2.sweep_scanline(sl2);
                }
                else
                {
                    if(sl1.y() < sl2.y())
                    {
                        sbool_add_spans_and_render(sl1, sl, ren, add_span1);
                        flag1 = sg1.sweep_scanline(sl1);
                    }
                    else
                    {
                        sbool_add_spans_and_render(sl2, sl, ren, add_span2);
                        flag2 = sg2.sweep_scanline(sl2);
                    }
                }
            }
            else
            {
                if(flag1)
                {
                    sbool_add_spans_and_render(sl1, sl, ren, add_span1);
                    flag1 = sg1.sweep_scanline(sl1);
                }
                if(flag2)
                {
                    sbool_add_spans_and_render(sl2, sl, ren, add_span2);
                    flag2 = sg2.sweep_scanline(sl2);
                }
            }
        }
    }








    //-------------------------------------------------sbool_subtract_shapes
    // Subtract the scanline shapes, "sg1-sg2". Here the "Scanline Generator" 
    // abstraction is used. ScanlineGen1 and ScanlineGen2 are 
    // the generators, and can be of type rasterizer_scanline_aa<>.
    // There function requires three scanline containers that can be of
    // different types.
    // "sl1" and "sl2" are used to retrieve scanlines from the generators,
    // "sl" is ised as the resulting scanline to render it.
    // The external "sl1" and "sl2" are used only for the sake of
    // optimization and reusing of the scanline objects.
    // the function calls sbool_intersect_scanlines with CombineSpansFunctor 
    // as the last argument. See combine_scanlines_sub for details.
    //----------
    template<class ScanlineGen1, 
             class ScanlineGen2, 
             class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class Renderer,
             class AddSpanFunctor1,
             class CombineSpansFunctor>
    void sbool_subtract_shapes(ScanlineGen1& sg1, ScanlineGen2& sg2,
                               Scanline1& sl1, Scanline2& sl2,
                               Scanline& sl, Renderer& ren, 
                               AddSpanFunctor1 add_span1,
                               CombineSpansFunctor combine_spans)
    {
        // Prepare the scanline generators.
        // Here "sg1" is master, "sg2" is slave.
        //-----------------
        if(!sg1.rewind_scanlines()) return;
        bool flag2 = sg2.rewind_scanlines();

        // Get the bounding box
        //----------------
        rect_i r1(sg1.min_x(), sg1.min_y(), sg1.max_x(), sg1.max_y());

        // Reset the scanlines and get two first ones
        //-----------------
        sl.reset(sg1.min_x(), sg1.max_x());
        sl1.reset(sg1.min_x(), sg1.max_x());
        sl2.reset(sg2.min_x(), sg2.max_x());
        if(!sg1.sweep_scanline(sl1)) return;

        if(flag2) flag2 = sg2.sweep_scanline(sl2);

        ren.prepare();

        // A fake span2 processor
        sbool_add_span_empty<Scanline2, Scanline> add_span2;

        // The main loop
        // Here we synchronize the scanlines with 
        // the same Y coordinate, ignoring all other ones.
        // Only scanlines having the same Y-coordinate 
        // are to be combined.
        //-----------------
        bool flag1 = true;
        do
        {
            // Synchronize "slave" with "master"
            //-----------------
            while(flag2 && sl2.y() < sl1.y())
            {
                flag2 = sg2.sweep_scanline(sl2);
            }


            if(flag2 && sl2.y() == sl1.y())
            {
                // The Y coordinates are the same.
                // Combine the scanlines and render if they contain any spans.
                //----------------------
                sbool_unite_scanlines(sl1, sl2, sl, add_span1, add_span2, combine_spans);
                if(sl.num_spans())
                {
                    sl.finalize(sl1.y());
                    ren.render(sl);
                }
            }
            else
            {
                sbool_add_spans_and_render(sl1, sl, ren, add_span1);
            }

            // Advance the "master"
            flag1 = sg1.sweep_scanline(sl1);
        }
        while(flag1);
    }







    //---------------------------------------------sbool_intersect_shapes_aa
    // Intersect two anti-aliased scanline shapes. 
    // Here the "Scanline Generator" abstraction is used. 
    // ScanlineGen1 and ScanlineGen2 are the generators, and can be of 
    // type rasterizer_scanline_aa<>. There function requires three 
    // scanline containers that can be of different types.
    // "sl1" and "sl2" are used to retrieve scanlines from the generators,
    // "sl" is ised as the resulting scanline to render it.
    // The external "sl1" and "sl2" are used only for the sake of
    // optimization and reusing of the scanline objects.
    //----------
    template<class ScanlineGen1, 
             class ScanlineGen2, 
             class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class Renderer>
    void sbool_intersect_shapes_aa(ScanlineGen1& sg1, ScanlineGen2& sg2,
                                   Scanline1& sl1, Scanline2& sl2,
                                   Scanline& sl, Renderer& ren)
    {
        sbool_intersect_spans_aa<Scanline1, Scanline2, Scanline> combine_functor;
        sbool_intersect_shapes(sg1, sg2, sl1, sl2, sl, ren, combine_functor);
    }





    //--------------------------------------------sbool_intersect_shapes_bin
    // Intersect two binary scanline shapes (without anti-aliasing). 
    // See intersect_shapes_aa for more comments
    //----------
    template<class ScanlineGen1, 
             class ScanlineGen2, 
             class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class Renderer>
    void sbool_intersect_shapes_bin(ScanlineGen1& sg1, ScanlineGen2& sg2,
                                    Scanline1& sl1, Scanline2& sl2,
                                    Scanline& sl, Renderer& ren)
    {
        sbool_combine_spans_bin<Scanline1, Scanline2, Scanline> combine_functor;
        sbool_intersect_shapes(sg1, sg2, sl1, sl2, sl, ren, combine_functor);
    }





    //-------------------------------------------------sbool_unite_shapes_aa
    // Unite two anti-aliased scanline shapes 
    // See intersect_shapes_aa for more comments
    //----------
    template<class ScanlineGen1, 
             class ScanlineGen2, 
             class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class Renderer>
    void sbool_unite_shapes_aa(ScanlineGen1& sg1, ScanlineGen2& sg2,
                               Scanline1& sl1, Scanline2& sl2,
                               Scanline& sl, Renderer& ren)
    {
        sbool_add_span_aa<Scanline1, Scanline> add_functor1;
        sbool_add_span_aa<Scanline2, Scanline> add_functor2;
        sbool_unite_spans_aa<Scanline1, Scanline2, Scanline> combine_functor;
        sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren, 
                           add_functor1, add_functor2, combine_functor);
    }





    //------------------------------------------------sbool_unite_shapes_bin
    // Unite two binary scanline shapes (without anti-aliasing). 
    // See intersect_shapes_aa for more comments
    //----------
    template<class ScanlineGen1, 
             class ScanlineGen2, 
             class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class Renderer>
    void sbool_unite_shapes_bin(ScanlineGen1& sg1, ScanlineGen2& sg2,
                                Scanline1& sl1, Scanline2& sl2,
                                Scanline& sl, Renderer& ren)
    {
        sbool_add_span_bin<Scanline1, Scanline> add_functor1;
        sbool_add_span_bin<Scanline2, Scanline> add_functor2;
        sbool_combine_spans_bin<Scanline1, Scanline2, Scanline> combine_functor;
        sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren, 
                           add_functor1, add_functor2, combine_functor);
    }









    //---------------------------------------------------sbool_xor_shapes_aa
    // Apply eXclusive OR to two anti-aliased scanline shapes. There's 
    // a modified "Linear" XOR used instead of classical "Saddle" one.
    // The reason is to have the result absolutely conststent with what
    // the scanline rasterizer produces.
    // See intersect_shapes_aa for more comments
    //----------
    template<class ScanlineGen1, 
             class ScanlineGen2, 
             class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class Renderer>
    void sbool_xor_shapes_aa(ScanlineGen1& sg1, ScanlineGen2& sg2,
                             Scanline1& sl1, Scanline2& sl2,
                             Scanline& sl, Renderer& ren)
    {
        sbool_add_span_aa<Scanline1, Scanline> add_functor1;
        sbool_add_span_aa<Scanline2, Scanline> add_functor2;
        sbool_xor_spans_aa<Scanline1, Scanline2, Scanline, 
                           sbool_xor_formula_linear<> > combine_functor;
        sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren, 
                           add_functor1, add_functor2, combine_functor);
    }



    //------------------------------------------sbool_xor_shapes_saddle_aa
    // Apply eXclusive OR to two anti-aliased scanline shapes. 
    // There's the classical "Saddle" used to calculate the 
    // Anti-Aliasing values, that is:
    // a XOR b : 1-((1-a+a*b)*(1-b+a*b))
    // See intersect_shapes_aa for more comments
    //----------
    template<class ScanlineGen1, 
             class ScanlineGen2, 
             class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class Renderer>
    void sbool_xor_shapes_saddle_aa(ScanlineGen1& sg1, ScanlineGen2& sg2,
                                    Scanline1& sl1, Scanline2& sl2,
                                    Scanline& sl, Renderer& ren)
    {
        sbool_add_span_aa<Scanline1, Scanline> add_functor1;
        sbool_add_span_aa<Scanline2, Scanline> add_functor2;
        sbool_xor_spans_aa<Scanline1, 
                           Scanline2, 
                           Scanline, 
                           sbool_xor_formula_saddle<> > combine_functor;
        sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren, 
                           add_functor1, add_functor2, combine_functor);
    }


    //--------------------------------------sbool_xor_shapes_abs_diff_aa
    // Apply eXclusive OR to two anti-aliased scanline shapes. 
    // There's the absolute difference used to calculate 
    // Anti-Aliasing values, that is:
    // a XOR b : abs(a-b)
    // See intersect_shapes_aa for more comments
    //----------
    template<class ScanlineGen1, 
             class ScanlineGen2, 
             class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class Renderer>
    void sbool_xor_shapes_abs_diff_aa(ScanlineGen1& sg1, ScanlineGen2& sg2,
                                      Scanline1& sl1, Scanline2& sl2,
                                      Scanline& sl, Renderer& ren)
    {
        sbool_add_span_aa<Scanline1, Scanline> add_functor1;
        sbool_add_span_aa<Scanline2, Scanline> add_functor2;
        sbool_xor_spans_aa<Scanline1, 
                           Scanline2, 
                           Scanline, 
                           sbool_xor_formula_abs_diff> combine_functor;
        sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren, 
                           add_functor1, add_functor2, combine_functor);
    }



    //--------------------------------------------------sbool_xor_shapes_bin
    // Apply eXclusive OR to two binary scanline shapes (without anti-aliasing). 
    // See intersect_shapes_aa for more comments
    //----------
    template<class ScanlineGen1, 
             class ScanlineGen2, 
             class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class Renderer>
    void sbool_xor_shapes_bin(ScanlineGen1& sg1, ScanlineGen2& sg2,
                              Scanline1& sl1, Scanline2& sl2,
                              Scanline& sl, Renderer& ren)
    {
        sbool_add_span_bin<Scanline1, Scanline> add_functor1;
        sbool_add_span_bin<Scanline2, Scanline> add_functor2;
        sbool_combine_spans_empty<Scanline1, Scanline2, Scanline> combine_functor;
        sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren, 
                           add_functor1, add_functor2, combine_functor);
    }






    //----------------------------------------------sbool_subtract_shapes_aa
    // Subtract shapes "sg1-sg2" with anti-aliasing
    // See intersect_shapes_aa for more comments
    //----------
    template<class ScanlineGen1, 
             class ScanlineGen2, 
             class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class Renderer>
    void sbool_subtract_shapes_aa(ScanlineGen1& sg1, ScanlineGen2& sg2,
                                  Scanline1& sl1, Scanline2& sl2,
                                  Scanline& sl, Renderer& ren)
    {
        sbool_add_span_aa<Scanline1, Scanline> add_functor;
        sbool_subtract_spans_aa<Scanline1, Scanline2, Scanline> combine_functor;
        sbool_subtract_shapes(sg1, sg2, sl1, sl2, sl, ren, 
                              add_functor, combine_functor);
    }





    //---------------------------------------------sbool_subtract_shapes_bin
    // Subtract binary shapes "sg1-sg2" without anti-aliasing
    // See intersect_shapes_aa for more comments
    //----------
    template<class ScanlineGen1, 
             class ScanlineGen2, 
             class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class Renderer>
    void sbool_subtract_shapes_bin(ScanlineGen1& sg1, ScanlineGen2& sg2,
                                   Scanline1& sl1, Scanline2& sl2,
                                   Scanline& sl, Renderer& ren)
    {
        sbool_add_span_bin<Scanline1, Scanline> add_functor;
        sbool_combine_spans_empty<Scanline1, Scanline2, Scanline> combine_functor;
        sbool_subtract_shapes(sg1, sg2, sl1, sl2, sl, ren, 
                              add_functor, combine_functor);
    }






    //------------------------------------------------------------sbool_op_e
    enum sbool_op_e
    {
        sbool_or,            //----sbool_or
        sbool_and,           //----sbool_and
        sbool_xor,           //----sbool_xor
        sbool_xor_saddle,    //----sbool_xor_saddle
        sbool_xor_abs_diff,  //----sbool_xor_abs_diff
        sbool_a_minus_b,     //----sbool_a_minus_b
        sbool_b_minus_a      //----sbool_b_minus_a
    };






    //----------------------------------------------sbool_combine_shapes_bin
    template<class ScanlineGen1, 
             class ScanlineGen2, 
             class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class Renderer>
    void sbool_combine_shapes_bin(sbool_op_e op,
                                  ScanlineGen1& sg1, ScanlineGen2& sg2,
                                  Scanline1& sl1, Scanline2& sl2,
                                  Scanline& sl, Renderer& ren)
    {
        switch(op)
        {
        case sbool_or          : sbool_unite_shapes_bin    (sg1, sg2, sl1, sl2, sl, ren); break;
        case sbool_and         : sbool_intersect_shapes_bin(sg1, sg2, sl1, sl2, sl, ren); break;
        case sbool_xor         :
        case sbool_xor_saddle  : 
        case sbool_xor_abs_diff: sbool_xor_shapes_bin      (sg1, sg2, sl1, sl2, sl, ren); break;
        case sbool_a_minus_b   : sbool_subtract_shapes_bin (sg1, sg2, sl1, sl2, sl, ren); break;
        case sbool_b_minus_a   : sbool_subtract_shapes_bin (sg2, sg1, sl2, sl1, sl, ren); break;
        }
    }




    //-----------------------------------------------sbool_combine_shapes_aa
    template<class ScanlineGen1, 
             class ScanlineGen2, 
             class Scanline1, 
             class Scanline2, 
             class Scanline, 
             class Renderer>
    void sbool_combine_shapes_aa(sbool_op_e op,
                                 ScanlineGen1& sg1, ScanlineGen2& sg2,
                                 Scanline1& sl1, Scanline2& sl2,
                                 Scanline& sl, Renderer& ren)
    {
        switch(op)
        {
        case sbool_or          : sbool_unite_shapes_aa       (sg1, sg2, sl1, sl2, sl, ren); break;
        case sbool_and         : sbool_intersect_shapes_aa   (sg1, sg2, sl1, sl2, sl, ren); break;
        case sbool_xor         : sbool_xor_shapes_aa         (sg1, sg2, sl1, sl2, sl, ren); break;
        case sbool_xor_saddle  : sbool_xor_shapes_saddle_aa  (sg1, sg2, sl1, sl2, sl, ren); break;
        case sbool_xor_abs_diff: sbool_xor_shapes_abs_diff_aa(sg1, sg2, sl1, sl2, sl, ren); break;
        case sbool_a_minus_b   : sbool_subtract_shapes_aa    (sg1, sg2, sl1, sl2, sl, ren); break;
        case sbool_b_minus_a   : sbool_subtract_shapes_aa    (sg2, sg1, sl2, sl1, sl, ren); break;
        }
    }

}


#endif

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Russian Federation Russian Federation
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions