| /* | 
 |  * Copyright (C) 2009 The Android Open Source Project | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | #ifndef ANDROID_UI_PRIVATE_REGION_HELPER_H | 
 | #define ANDROID_UI_PRIVATE_REGION_HELPER_H | 
 |  | 
 | #include <stdint.h> | 
 | #include <sys/types.h> | 
 |  | 
 | namespace android { | 
 | // ---------------------------------------------------------------------------- | 
 |  | 
 | template<typename RECT> | 
 | class region_operator | 
 | { | 
 |     typedef typename RECT::value_type TYPE;     | 
 |     static const TYPE max_value = 0x7FFFFFF; | 
 |  | 
 | public: | 
 |     /*  | 
 |      * Common boolean operations: | 
 |      * value is computed as 0b101 op 0b110 | 
 |      *    other boolean operation are possible, simply compute | 
 |      *    their corresponding value with the above formulae and use | 
 |      *    it when instantiating a region_operator. | 
 |      */ | 
 |     static const uint32_t LHS = 0x5;  // 0b101 | 
 |     static const uint32_t RHS = 0x6;  // 0b110 | 
 |     enum { | 
 |         op_nand = LHS & ~RHS, | 
 |         op_and  = LHS &  RHS, | 
 |         op_or   = LHS |  RHS, | 
 |         op_xor  = LHS ^  RHS | 
 |     }; | 
 |  | 
 |     struct region { | 
 |         RECT const* rects; | 
 |         size_t count; | 
 |         TYPE dx; | 
 |         TYPE dy; | 
 |         inline region(const region& rhs)  | 
 |             : rects(rhs.rects), count(rhs.count), dx(rhs.dx), dy(rhs.dy) { } | 
 |         inline region(RECT const* r, size_t c)  | 
 |             : rects(r), count(c), dx(), dy() { } | 
 |         inline region(RECT const* r, size_t c, TYPE dx, TYPE dy)  | 
 |             : rects(r), count(c), dx(dx), dy(dy) { } | 
 |     }; | 
 |  | 
 |     class region_rasterizer { | 
 |         friend class region_operator; | 
 |         virtual void operator()(const RECT& rect) = 0; | 
 |     public: | 
 |         virtual ~region_rasterizer() { }; | 
 |     }; | 
 |      | 
 |     inline region_operator(int op, const region& lhs, const region& rhs)  | 
 |         : op_mask(op), spanner(lhs, rhs)  | 
 |     { | 
 |     } | 
 |  | 
 |     void operator()(region_rasterizer& rasterizer) { | 
 |         RECT current; | 
 |         do { | 
 |             SpannerInner spannerInner(spanner.lhs, spanner.rhs); | 
 |             int inside = spanner.next(current.top, current.bottom); | 
 |             spannerInner.prepare(inside); | 
 |             do { | 
 |                 TYPE left, right; | 
 |                 int inside = spannerInner.next(current.left, current.right); | 
 |                 if ((op_mask >> inside) & 1) { | 
 |                     if (current.left < current.right &&  | 
 |                             current.top < current.bottom) { | 
 |                         rasterizer(current); | 
 |                     } | 
 |                 } | 
 |             } while(!spannerInner.isDone()); | 
 |         } while(!spanner.isDone()); | 
 |     } | 
 |  | 
 | private:     | 
 |     uint32_t op_mask; | 
 |  | 
 |     class SpannerBase | 
 |     { | 
 |     public: | 
 |         enum { | 
 |             lhs_before_rhs   = 0, | 
 |             lhs_after_rhs    = 1, | 
 |             lhs_coincide_rhs = 2 | 
 |         }; | 
 |  | 
 |     protected: | 
 |         TYPE lhs_head; | 
 |         TYPE lhs_tail; | 
 |         TYPE rhs_head; | 
 |         TYPE rhs_tail; | 
 |  | 
 |         inline int next(TYPE& head, TYPE& tail, | 
 |                 bool& more_lhs, bool& more_rhs)  | 
 |         { | 
 |             int inside; | 
 |             more_lhs = false; | 
 |             more_rhs = false; | 
 |             if (lhs_head < rhs_head) { | 
 |                 inside = lhs_before_rhs; | 
 |                 head = lhs_head; | 
 |                 if (lhs_tail <= rhs_head) { | 
 |                     tail = lhs_tail; | 
 |                     more_lhs = true; | 
 |                 } else { | 
 |                     lhs_head = rhs_head; | 
 |                     tail = rhs_head; | 
 |                 } | 
 |             } else if (rhs_head < lhs_head) { | 
 |                 inside = lhs_after_rhs; | 
 |                 head = rhs_head; | 
 |                 if (rhs_tail <= lhs_head) { | 
 |                     tail = rhs_tail; | 
 |                     more_rhs = true; | 
 |                 } else { | 
 |                     rhs_head = lhs_head; | 
 |                     tail = lhs_head; | 
 |                 } | 
 |             } else { | 
 |                 inside = lhs_coincide_rhs; | 
 |                 head = lhs_head; | 
 |                 if (lhs_tail <= rhs_tail) { | 
 |                     tail = rhs_head = lhs_tail; | 
 |                     more_lhs = true; | 
 |                 } | 
 |                 if (rhs_tail <= lhs_tail) { | 
 |                     tail = lhs_head = rhs_tail; | 
 |                     more_rhs = true; | 
 |                 } | 
 |             } | 
 |             return inside; | 
 |         } | 
 |     }; | 
 |  | 
 |     class Spanner : protected SpannerBase  | 
 |     { | 
 |         friend class region_operator; | 
 |         region lhs; | 
 |         region rhs; | 
 |  | 
 |     public: | 
 |         inline Spanner(const region& lhs, const region& rhs) | 
 |             : lhs(lhs), rhs(rhs)  | 
 |         { | 
 |             SpannerBase::lhs_head = lhs.rects->top      + lhs.dy; | 
 |             SpannerBase::lhs_tail = lhs.rects->bottom   + lhs.dy; | 
 |             SpannerBase::rhs_head = rhs.rects->top      + rhs.dy; | 
 |             SpannerBase::rhs_tail = rhs.rects->bottom   + rhs.dy; | 
 |         } | 
 |  | 
 |         inline bool isDone() const { | 
 |             return !rhs.count && !lhs.count; | 
 |         } | 
 |  | 
 |         inline int next(TYPE& top, TYPE& bottom)  | 
 |         { | 
 |             bool more_lhs = false; | 
 |             bool more_rhs = false; | 
 |             int inside = SpannerBase::next(top, bottom, more_lhs, more_rhs); | 
 |             if (more_lhs) { | 
 |                 advance(lhs, SpannerBase::lhs_head, SpannerBase::lhs_tail); | 
 |             } | 
 |             if (more_rhs) { | 
 |                 advance(rhs, SpannerBase::rhs_head, SpannerBase::rhs_tail); | 
 |             } | 
 |             return inside; | 
 |         } | 
 |  | 
 |     private: | 
 |         static inline  | 
 |         void advance(region& reg, TYPE& aTop, TYPE& aBottom) { | 
 |             // got to next span | 
 |             size_t count = reg.count; | 
 |             RECT const * rects = reg.rects; | 
 |             RECT const * const end = rects + count; | 
 |             const int top = rects->top; | 
 |             while (rects != end && rects->top == top) { | 
 |                 rects++; | 
 |                 count--; | 
 |             } | 
 |             if (rects != end) { | 
 |                 aTop    = rects->top    + reg.dy; | 
 |                 aBottom = rects->bottom + reg.dy; | 
 |             } else { | 
 |                 aTop    = max_value; | 
 |                 aBottom = max_value; | 
 |             } | 
 |             reg.rects = rects; | 
 |             reg.count = count; | 
 |         } | 
 |     }; | 
 |  | 
 |     class SpannerInner : protected SpannerBase  | 
 |     { | 
 |         region lhs; | 
 |         region rhs; | 
 |          | 
 |     public: | 
 |         inline SpannerInner(const region& lhs, const region& rhs) | 
 |             : lhs(lhs), rhs(rhs)  | 
 |         { | 
 |         } | 
 |  | 
 |         inline void prepare(int inside) { | 
 |             if (inside == SpannerBase::lhs_before_rhs) { | 
 |                 SpannerBase::lhs_head = lhs.rects->left  + lhs.dx; | 
 |                 SpannerBase::lhs_tail = lhs.rects->right + lhs.dx; | 
 |                 SpannerBase::rhs_head = max_value; | 
 |                 SpannerBase::rhs_tail = max_value; | 
 |             } else if (inside == SpannerBase::lhs_after_rhs) { | 
 |                 SpannerBase::lhs_head = max_value; | 
 |                 SpannerBase::lhs_tail = max_value; | 
 |                 SpannerBase::rhs_head = rhs.rects->left  + rhs.dx; | 
 |                 SpannerBase::rhs_tail = rhs.rects->right + rhs.dx; | 
 |             } else { | 
 |                 SpannerBase::lhs_head = lhs.rects->left  + lhs.dx; | 
 |                 SpannerBase::lhs_tail = lhs.rects->right + lhs.dx; | 
 |                 SpannerBase::rhs_head = rhs.rects->left  + rhs.dx; | 
 |                 SpannerBase::rhs_tail = rhs.rects->right + rhs.dx; | 
 |             } | 
 |         } | 
 |  | 
 |         inline bool isDone() const { | 
 |             return SpannerBase::lhs_head == max_value &&  | 
 |                    SpannerBase::rhs_head == max_value; | 
 |         } | 
 |  | 
 |         inline int next(TYPE& left, TYPE& right)  | 
 |         { | 
 |             bool more_lhs = false; | 
 |             bool more_rhs = false; | 
 |             int inside = SpannerBase::next(left, right, more_lhs, more_rhs); | 
 |             if (more_lhs) { | 
 |                 advance(lhs, SpannerBase::lhs_head, SpannerBase::lhs_tail); | 
 |             } | 
 |             if (more_rhs) { | 
 |                 advance(rhs, SpannerBase::rhs_head, SpannerBase::rhs_tail); | 
 |             } | 
 |             return inside; | 
 |         } | 
 |  | 
 |     private: | 
 |         static inline  | 
 |         void advance(region& reg, TYPE& left, TYPE& right) { | 
 |             if (reg.rects && reg.count) { | 
 |                 const int cur_span_top = reg.rects->top; | 
 |                 reg.rects++; | 
 |                 reg.count--; | 
 |                 if (!reg.count || reg.rects->top != cur_span_top) { | 
 |                     left  = max_value; | 
 |                     right = max_value; | 
 |                 } else { | 
 |                     left  = reg.rects->left  + reg.dx; | 
 |                     right = reg.rects->right + reg.dx; | 
 |                 } | 
 |             } | 
 |         } | 
 |     }; | 
 |  | 
 |     Spanner spanner; | 
 | }; | 
 |  | 
 | // ---------------------------------------------------------------------------- | 
 | }; | 
 |  | 
 | #endif /* ANDROID_UI_PRIVATE_REGION_HELPER_H */ |