vector: refactor vrle class
authorSubhransu Mohanty <sub.mohanty@samsung.com>
Tue, 18 Aug 2020 01:53:57 +0000 (10:53 +0900)
committerJongmin Lee <jm105.lee@samsung.com>
Sun, 23 Aug 2020 21:11:23 +0000 (06:11 +0900)
src/vector/vrle.cpp
src/vector/vrle.h

index 328b6c8..c7a520f 100644 (file)
 #include <algorithm>
 #include <array>
 #include <cstdlib>
+#include <cstring>
 #include <vector>
 #include "vdebug.h"
 #include "vglobal.h"
 
 V_BEGIN_NAMESPACE
 
-enum class Operation { Add, Xor };
-
-struct VRleHelper {
-    size_t      alloc{0};
-    size_t      size{0};
-    VRle::Span *spans{nullptr};
-};
-static void rleIntersectWithRle(VRleHelper *, int, int, VRleHelper *,
-                                VRleHelper *);
-static void rleIntersectWithRect(const VRect &, VRleHelper *, VRleHelper *);
-static void rleOpGeneric(VRleHelper *, VRleHelper *, VRleHelper *,
-                         Operation op);
-static void rleSubstractWithRle(VRleHelper *, VRleHelper *, VRleHelper *);
+using Result = std::array<VRle::Span, 255>;
+using rle_view = VRle::View;
+static size_t _opGeneric(rle_view &a, rle_view &b, Result &result,
+                         VRle::Data::Op op);
+static size_t _opIntersect(const VRect &, rle_view &, Result &);
+static size_t _opIntersect(rle_view &, rle_view &, Result &);
 
 static inline uchar divBy255(int x)
 {
     return (x + (x >> 8) + 0x80) >> 8;
 }
 
-inline static void copyArrayToVector(const VRle::Span *span, size_t count,
-                                     std::vector<VRle::Span> &v)
+inline static void copy(const VRle::Span *span, size_t count,
+                        std::vector<VRle::Span> &v)
 {
     // make sure enough memory available
     if (v.capacity() < v.size() + count) v.reserve(v.size() + count);
     std::copy(span, span + count, back_inserter(v));
 }
 
-void VRle::VRleData::addSpan(const VRle::Span *span, size_t count)
+void VRle::Data::addSpan(const VRle::Span *span, size_t count)
 {
-    copyArrayToVector(span, count, mSpans);
+    copy(span, count, mSpans);
     mBboxDirty = true;
 }
 
-VRect VRle::VRleData::bbox() const
+VRect VRle::Data::bbox() const
 {
     updateBbox();
     return mBbox;
 }
 
-void VRle::VRleData::setBbox(const VRect &bbox) const
+void VRle::Data::setBbox(const VRect &bbox) const
 {
     mBboxDirty = false;
     mBbox = bbox;
 }
 
-void VRle::VRleData::reset()
+void VRle::Data::reset()
 {
     mSpans.clear();
     mBbox = VRect();
@@ -85,12 +79,12 @@ void VRle::VRleData::reset()
     mBboxDirty = false;
 }
 
-void VRle::VRleData::clone(const VRle::VRleData &o)
+void VRle::Data::clone(const VRle::Data &o)
 {
     *this = o;
 }
 
-void VRle::VRleData::translate(const VPoint &p)
+void VRle::Data::translate(const VPoint &p)
 {
     // take care of last offset if applied
     mOffset = p - mOffset;
@@ -104,7 +98,7 @@ void VRle::VRleData::translate(const VPoint &p)
     mBbox.translate(mOffset.x(), mOffset.y());
 }
 
-void VRle::VRleData::addRect(const VRect &rect)
+void VRle::Data::addRect(const VRect &rect)
 {
     int x = rect.left();
     int y = rect.top();
@@ -124,7 +118,7 @@ void VRle::VRleData::addRect(const VRect &rect)
     mBbox = rect;
 }
 
-void VRle::VRleData::updateBbox() const
+void VRle::Data::updateBbox() const
 {
     if (!mBboxDirty) return;
 
@@ -147,15 +141,15 @@ void VRle::VRleData::updateBbox() const
     }
 }
 
-void VRle::VRleData::operator*=(uchar alpha)
+void VRle::Data::operator*=(uchar alpha)
 {
     for (auto &i : mSpans) {
         i.coverage = divBy255(i.coverage * alpha);
     }
 }
 
-void VRle::VRleData::opIntersect(const VRect &r, VRle::VRleSpanCb cb,
-                                 void *userData) const
+void VRle::Data::opIntersect(const VRect &r, VRle::VRleSpanCb cb,
+                             void *userData) const
 {
     if (empty()) return;
 
@@ -164,202 +158,162 @@ void VRle::VRleData::opIntersect(const VRect &r, VRle::VRleSpanCb cb,
         return;
     }
 
-    VRect                       clip = r;
-    VRleHelper                  tresult, tmp_obj;
-    std::array<VRle::Span, 256> array;
-
-    // setup the tresult object
-    tresult.size = array.size();
-    tresult.alloc = array.size();
-    tresult.spans = array.data();
-
-    // setup tmp object
-    tmp_obj.size = mSpans.size();
-    tmp_obj.spans = const_cast<VRle::Span *>(mSpans.data());
-
+    auto   obj = view();
+    Result result;
     // run till all the spans are processed
-    while (tmp_obj.size) {
-        rleIntersectWithRect(clip, &tmp_obj, &tresult);
-        if (tresult.size) {
-            cb(tresult.size, tresult.spans, userData);
-        }
-        tresult.size = 0;
+    while (obj.size()) {
+        auto count = _opIntersect(r, obj, result);
+        if (count) cb(count, result.data(), userData);
     }
 }
 
 // res = a - b;
-void VRle::VRleData::opSubstract(const VRle::VRleData &a,
-                                 const VRle::VRleData &b)
+void VRle::Data::opSubstract(const VRle::Data &aObj, const VRle::Data &bObj)
 {
     // if two rle are disjoint
-    if (!a.bbox().intersects(b.bbox())) {
-        mSpans = a.mSpans;
+    if (!aObj.bbox().intersects(bObj.bbox())) {
+        mSpans = aObj.mSpans;
     } else {
-        VRle::Span *      aPtr = const_cast<VRle::Span *>(a.mSpans.data());
-        const VRle::Span *aEnd = a.mSpans.data() + a.mSpans.size();
-        VRle::Span *      bPtr = const_cast<VRle::Span *>(b.mSpans.data());
-        const VRle::Span *bEnd = b.mSpans.data() + b.mSpans.size();
-
-        // 1. forward till both y intersect
-        while ((aPtr != aEnd) && (aPtr->y < bPtr->y)) aPtr++;
-        size_t sizeA = size_t(aPtr - a.mSpans.data());
-        if (sizeA) copyArrayToVector(a.mSpans.data(), sizeA, mSpans);
+        auto a = aObj.view();
+        auto b = bObj.view();
 
-        // 2. forward b till it intersect with a.
-        while ((bPtr != bEnd) && (bPtr->y < aPtr->y)) bPtr++;
-        size_t sizeB = size_t(bPtr - b.mSpans.data());
+        auto aPtr = a.data();
+        auto aEnd = a.data() + a.size();
+        auto bPtr = b.data();
+        auto bEnd = b.data() + b.size();
 
-        // 2. calculate the intersect region
-        VRleHelper                  tresult, aObj, bObj;
-        std::array<VRle::Span, 256> array;
+        // 1. forward a till it intersects with b
+        while ((aPtr != aEnd) && (aPtr->y < bPtr->y)) aPtr++;
+        auto count = aPtr - a.data();
+        if (count) copy(a.data(), count, mSpans);
 
-        // setup the tresult object
-        tresult.size = array.size();
-        tresult.alloc = array.size();
-        tresult.spans = array.data();
+        // 2. forward b till it intersects with a
+        if (aPtr != aEnd)
+            while ((bPtr != bEnd) && (bPtr->y < aPtr->y)) bPtr++;
 
-        // setup a object
-        aObj.size = a.mSpans.size() - sizeA;
-        aObj.spans = aPtr;
+        // update a and b object
+        a = {aPtr, size_t(aEnd - aPtr)};
+        b = {bPtr, size_t(bEnd - bPtr)};
 
-        // setup b object
-        bObj.size = b.mSpans.size() - sizeB;
-        bObj.spans = bPtr;
+        // 3. calculate the intersect region
+        Result result;
 
         // run till all the spans are processed
-        while (aObj.size && bObj.size) {
-            rleSubstractWithRle(&aObj, &bObj, &tresult);
-            if (tresult.size) {
-                copyArrayToVector(tresult.spans, tresult.size, mSpans);
-            }
-            tresult.size = 0;
+        while (a.size() && b.size()) {
+            auto count = _opGeneric(a, b, result, Op::Substract);
+            if (count) copy(result.data(), count, mSpans);
         }
-        // 3. copy the rest of a
-        if (aObj.size) copyArrayToVector(aObj.spans, aObj.size, mSpans);
+
+        // 4. copy the rest of a
+        if (a.size()) copy(a.data(), a.size(), mSpans);
     }
 
     mBboxDirty = true;
 }
 
-void VRle::VRleData::opGeneric(const VRle::VRleData &a, const VRle::VRleData &b,
-                               OpCode code)
+void VRle::Data::opGeneric(const VRle::Data &aObj, const VRle::Data &bObj,
+                           Op op)
 {
     // This routine assumes, obj1(span_y) < obj2(span_y).
 
+    auto a = aObj.view();
+    auto b = bObj.view();
+
     // reserve some space for the result vector.
-    mSpans.reserve(a.mSpans.size() + b.mSpans.size());
+    mSpans.reserve(a.size() + b.size());
 
     // if two rle are disjoint
-    if (!a.bbox().intersects(b.bbox())) {
-        if (a.mSpans[0].y < b.mSpans[0].y) {
-            copyArrayToVector(a.mSpans.data(), a.mSpans.size(), mSpans);
-            copyArrayToVector(b.mSpans.data(), b.mSpans.size(), mSpans);
+    if (!aObj.bbox().intersects(aObj.bbox())) {
+        if (a.data()[0].y < b.data()[0].y) {
+            copy(a.data(), a.size(), mSpans);
+            copy(b.data(), b.size(), mSpans);
         } else {
-            copyArrayToVector(b.mSpans.data(), b.mSpans.size(), mSpans);
-            copyArrayToVector(a.mSpans.data(), a.mSpans.size(), mSpans);
+            copy(b.data(), b.size(), mSpans);
+            copy(a.data(), a.size(), mSpans);
         }
     } else {
-        VRle::Span *      aPtr = const_cast<VRle::Span *>(a.mSpans.data());
-        const VRle::Span *aEnd = a.mSpans.data() + a.mSpans.size();
-        VRle::Span *      bPtr = const_cast<VRle::Span *>(b.mSpans.data());
-        const VRle::Span *bEnd = b.mSpans.data() + b.mSpans.size();
+        auto aPtr = a.data();
+        auto aEnd = a.data() + a.size();
+        auto bPtr = b.data();
+        auto bEnd = b.data() + b.size();
 
         // 1. forward a till it intersects with b
         while ((aPtr != aEnd) && (aPtr->y < bPtr->y)) aPtr++;
-        size_t sizeA = size_t(aPtr - a.mSpans.data());
-        if (sizeA) copyArrayToVector(a.mSpans.data(), sizeA, mSpans);
+
+        auto count = aPtr - a.data();
+        if (count) copy(a.data(), count, mSpans);
 
         // 2. forward b till it intersects with a
-        while ((bPtr != bEnd) && (bPtr->y < aPtr->y)) bPtr++;
-        size_t sizeB = size_t(bPtr - b.mSpans.data());
-        if (sizeB) copyArrayToVector(b.mSpans.data(), sizeB, mSpans);
+        if (aPtr != aEnd)
+            while ((bPtr != bEnd) && (bPtr->y < aPtr->y)) bPtr++;
+
+        count = bPtr - b.data();
+        if (count) copy(b.data(), count, mSpans);
+
+        // update a and b object
+        a = {aPtr, size_t(aEnd - aPtr)};
+        b = {bPtr, size_t(bEnd - bPtr)};
 
         // 3. calculate the intersect region
-        VRleHelper                  tresult, aObj, bObj;
-        std::array<VRle::Span, 256> array;
-
-        // setup the tresult object
-        tresult.size = array.size();
-        tresult.alloc = array.size();
-        tresult.spans = array.data();
-
-        // setup a object
-        aObj.size = a.mSpans.size() - sizeA;
-        aObj.spans = aPtr;
-
-        // setup b object
-        bObj.size = b.mSpans.size() - sizeB;
-        bObj.spans = bPtr;
-
-        Operation op = Operation::Add;
-        switch (code) {
-        case OpCode::Add:
-            op = Operation::Add;
-            break;
-        case OpCode::Xor:
-            op = Operation::Xor;
-            break;
-        }
+        Result result;
+
         // run till all the spans are processed
-        while (aObj.size && bObj.size) {
-            rleOpGeneric(&aObj, &bObj, &tresult, op);
-            if (tresult.size) {
-                copyArrayToVector(tresult.spans, tresult.size, mSpans);
-            }
-            tresult.size = 0;
+        while (a.size() && b.size()) {
+            auto count = _opGeneric(a, b, result, op);
+            if (count) copy(result.data(), count, mSpans);
         }
         // 3. copy the rest
-        if (bObj.size) copyArrayToVector(bObj.spans, bObj.size, mSpans);
-        if (aObj.size) copyArrayToVector(aObj.spans, aObj.size, mSpans);
+        if (b.size()) copy(b.data(), b.size(), mSpans);
+        if (a.size()) copy(a.data(), a.size(), mSpans);
     }
 
     mBboxDirty = true;
 }
 
-static void rle_cb(size_t count, const VRle::Span *spans, void *userData)
-{
-    auto vector = static_cast<std::vector<VRle::Span> *>(userData);
-    copyArrayToVector(spans, count, *vector);
-}
-
-void opIntersectHelper(const VRle::VRleData &obj1, const VRle::VRleData &obj2,
-                       VRle::VRleSpanCb cb, void *userData)
+static inline V_ALWAYS_INLINE void _opIntersectPrepare(VRle::View &a,
+                                                       VRle::View &b)
 {
-    VRleHelper                  result, source, clip;
-    std::array<VRle::Span, 256> array;
+    auto aPtr = a.data();
+    auto aEnd = a.data() + a.size();
+    auto bPtr = b.data();
+    auto bEnd = b.data() + b.size();
 
-    // setup the tresult object
-    result.size = array.size();
-    result.alloc = array.size();
-    result.spans = array.data();
+    // 1. advance a till it intersects with b
+    while ((aPtr != aEnd) && (aPtr->y < bPtr->y)) aPtr++;
 
-    // setup tmp object
-    source.size = obj1.mSpans.size();
-    source.spans = const_cast<VRle::Span *>(obj1.mSpans.data());
-
-    // setup tmp clip object
-    clip.size = obj2.mSpans.size();
-    clip.spans = const_cast<VRle::Span *>(obj2.mSpans.data());
+    // 2. advance b till it intersects with a
+    if (aPtr != aEnd)
+        while ((bPtr != bEnd) && (bPtr->y < aPtr->y)) bPtr++;
 
-    // run till all the spans are processed
-    while (source.size) {
-        rleIntersectWithRle(&clip, 0, 0, &source, &result);
-        if (result.size) {
-            cb(result.size, result.spans, userData);
-        }
-        result.size = 0;
-    }
+    // update a and b object
+    a = {aPtr, size_t(aEnd - aPtr)};
+    b = {bPtr, size_t(bEnd - bPtr)};
 }
 
-void VRle::VRleData::opIntersect(const VRle::VRleData &obj1,
-                                 const VRle::VRleData &obj2)
+void VRle::Data::opIntersect(VRle::View a, VRle::View b)
 {
-    opIntersectHelper(obj1, obj2, rle_cb, &mSpans);
+    _opIntersectPrepare(a, b);
+    Result result;
+    while (a.size()) {
+        auto count = _opIntersect(a, b, result);
+        if (count) copy(result.data(), count, mSpans);
+    }
+
     updateBbox();
 }
 
-#define VMIN(a, b) ((a) < (b) ? (a) : (b))
-#define VMAX(a, b) ((a) > (b) ? (a) : (b))
+static void _opIntersect(rle_view a, rle_view b, VRle::VRleSpanCb cb,
+                         void *userData)
+{
+    if (!cb) return;
+
+    _opIntersectPrepare(a, b);
+    Result result;
+    while (a.size()) {
+        auto count = _opIntersect(a, b, result);
+        if (count) cb(count, result.data(), userData);
+    }
+}
 
 /*
  * This function will clip a rle list with another rle object
@@ -371,35 +325,34 @@ void VRle::VRleData::opIntersect(const VRle::VRleData &obj1,
  *       that are yet to be processed as well as the tpm_clip object
  *       with the unprocessed clip spans.
  */
-static void rleIntersectWithRle(VRleHelper *tmp_clip, int clip_offset_x,
-                                int clip_offset_y, VRleHelper *tmp_obj,
-                                VRleHelper *result)
-{
-    VRle::Span *out = result->spans;
-    size_t      available = result->alloc;
-    VRle::Span *spans = tmp_obj->spans;
-    VRle::Span *end = tmp_obj->spans + tmp_obj->size;
-    VRle::Span *clipSpans = tmp_clip->spans;
-    VRle::Span *clipEnd = tmp_clip->spans + tmp_clip->size;
-    int         sx1, sx2, cx1, cx2, x, len;
+
+static size_t _opIntersect(rle_view &obj, rle_view &clip, Result &result)
+{
+    auto out = result.data();
+    auto available = result.max_size();
+    auto spans = obj.data();
+    auto end = obj.data() + obj.size();
+    auto clipSpans = clip.data();
+    auto clipEnd = clip.data() + clip.size();
+    int  sx1, sx2, cx1, cx2, x, len;
 
     while (available && spans < end) {
         if (clipSpans >= clipEnd) {
             spans = end;
             break;
         }
-        if ((clipSpans->y + clip_offset_y) > spans->y) {
+        if (clipSpans->y > spans->y) {
             ++spans;
             continue;
         }
-        if (spans->y != (clipSpans->y + clip_offset_y)) {
+        if (spans->y != clipSpans->y) {
             ++clipSpans;
             continue;
         }
         // assert(spans->y == (clipSpans->y + clip_offset_y));
         sx1 = spans->x;
         sx2 = sx1 + spans->len;
-        cx1 = (clipSpans->x + clip_offset_x);
+        cx1 = clipSpans->x;
         cx2 = cx1 + clipSpans->len;
 
         if (cx1 < sx1 && cx2 < sx1) {
@@ -426,16 +379,13 @@ static void rleIntersectWithRle(VRleHelper *tmp_clip, int clip_offset_x,
         }
     }
 
-    // update the span list that yet to be processed
-    tmp_obj->spans = spans;
-    tmp_obj->size = end - spans;
+    // update the obj view yet to be processed
+    obj = {spans, size_t(end - spans)};
 
-    // update the clip list that yet to be processed
-    tmp_clip->spans = clipSpans;
-    tmp_clip->size = clipEnd - clipSpans;
+    // update the clip view yet to be processed
+    clip = {clipSpans, size_t(clipEnd - clipSpans)};
 
-    // update the result
-    result->size = result->alloc - available;
+    return result.max_size() - available;
 }
 
 /*
@@ -447,55 +397,51 @@ static void rleIntersectWithRle(VRleHelper *tmp_clip, int clip_offset_x,
  *       it will stop and update the tmp_obj with the span list
  *       that are yet to be processed
  */
-static void rleIntersectWithRect(const VRect &clip, VRleHelper *tmp_obj,
-                                 VRleHelper *result)
+static size_t _opIntersect(const VRect &clip, rle_view &obj, Result &result)
 {
-    VRle::Span *out = result->spans;
-    size_t      available = result->alloc;
-    VRle::Span *spans = tmp_obj->spans;
-    VRle::Span *end = tmp_obj->spans + tmp_obj->size;
-    short       minx, miny, maxx, maxy;
-
-    minx = clip.left();
-    miny = clip.top();
-    maxx = clip.right() - 1;
-    maxy = clip.bottom() - 1;
-
-    while (available && spans < end) {
-        if (spans->y > maxy) {
-            spans = end;  // update spans so that we can breakout
+    auto out = result.data();
+    auto available = result.max_size();
+    auto ptr = obj.data();
+    auto end = obj.data() + obj.size();
+
+    const auto minx = clip.left();
+    const auto miny = clip.top();
+    const auto maxx = clip.right() - 1;
+    const auto maxy = clip.bottom() - 1;
+
+    while (available && ptr < end) {
+        const auto &span = *ptr;
+        if (span.y > maxy) {
+            ptr = end;  // update spans so that we can breakout
             break;
         }
-        if (spans->y < miny || spans->x > maxx ||
-            spans->x + spans->len <= minx) {
-            ++spans;
+        if (span.y < miny || span.x > maxx || span.x + span.len <= minx) {
+            ++ptr;
             continue;
         }
-        if (spans->x < minx) {
-            out->len = VMIN(spans->len - (minx - spans->x), maxx - minx + 1);
+        if (span.x < minx) {
+            out->len = std::min(span.len - (minx - span.x), maxx - minx + 1);
             out->x = minx;
         } else {
-            out->x = spans->x;
-            out->len = VMIN(spans->len, (maxx - spans->x + 1));
+            out->x = span.x;
+            out->len = std::min(span.len, ushort(maxx - span.x + 1));
         }
         if (out->len != 0) {
-            out->y = spans->y;
-            out->coverage = spans->coverage;
+            out->y = span.y;
+            out->coverage = span.coverage;
             ++out;
             --available;
         }
-        ++spans;
+        ++ptr;
     }
 
     // update the span list that yet to be processed
-    tmp_obj->spans = spans;
-    tmp_obj->size = end - spans;
+    obj = {ptr, size_t(end - ptr)};
 
-    // update the result
-    result->size = result->alloc - available;
+    return result.max_size() - available;
 }
 
-void blitXor(VRle::Span *spans, int count, uchar *buffer, int offsetX)
+static void blitXor(VRle::Span *spans, int count, uchar *buffer, int offsetX)
 {
     while (count--) {
         int    x = spans->x + offsetX;
@@ -511,8 +457,8 @@ void blitXor(VRle::Span *spans, int count, uchar *buffer, int offsetX)
     }
 }
 
-void blitDestinationOut(VRle::Span *spans, int count, uchar *buffer,
-                        int offsetX)
+static void blitDestinationOut(VRle::Span *spans, int count, uchar *buffer,
+                               int offsetX)
 {
     while (count--) {
         int    x = spans->x + offsetX;
@@ -526,7 +472,8 @@ void blitDestinationOut(VRle::Span *spans, int count, uchar *buffer,
     }
 }
 
-void blitSrcOver(VRle::Span *spans, int count, uchar *buffer, int offsetX)
+static void blitSrcOver(VRle::Span *spans, int count, uchar *buffer,
+                        int offsetX)
 {
     while (count--) {
         int    x = spans->x + offsetX;
@@ -540,7 +487,7 @@ void blitSrcOver(VRle::Span *spans, int count, uchar *buffer, int offsetX)
     }
 }
 
-void blit(VRle::Span *spans, int count, uchar *buffer, int offsetX)
+void blitSrc(VRle::Span *spans, int count, uchar *buffer, int offsetX)
 {
     while (count--) {
         int    x = spans->x + offsetX;
@@ -560,7 +507,7 @@ size_t bufferToRle(uchar *buffer, int size, int offsetX, int y, VRle::Span *out)
     uchar  value = buffer[0];
     int    curIndex = 0;
 
-    size = offsetX < 0 ? size + offsetX : size;
+    // size = offsetX < 0 ? size + offsetX : size;
     for (int i = 0; i < size; i++) {
         uchar curValue = buffer[0];
         if (value != curValue) {
@@ -587,128 +534,118 @@ size_t bufferToRle(uchar *buffer, int size, int offsetX, int y, VRle::Span *out)
     return count;
 }
 
-static void rleOpGeneric(VRleHelper *a, VRleHelper *b, VRleHelper *result,
-                         Operation op)
+struct SpanMerger {
+    explicit SpanMerger(VRle::Data::Op op)
+    {
+        switch (op) {
+        case VRle::Data::Op::Add:
+            _blitter = &blitSrcOver;
+            break;
+        case VRle::Data::Op::Xor:
+            _blitter = &blitXor;
+            break;
+        case VRle::Data::Op::Substract:
+            _blitter = &blitDestinationOut;
+            break;
+        }
+    }
+    using blitter = void (*)(VRle::Span *, int, uchar *, int);
+    blitter                     _blitter;
+    std::array<VRle::Span, 256> _result;
+    std::array<uchar, 1024>     _buffer;
+    VRle::Span *                _aStart{nullptr};
+    VRle::Span *                _bStart{nullptr};
+
+    void revert(VRle::Span *&aPtr, VRle::Span *&bPtr)
+    {
+        aPtr = _aStart;
+        bPtr = _bStart;
+    }
+    VRle::Span *data() { return _result.data(); }
+    size_t merge(VRle::Span *&aPtr, const VRle::Span *aEnd, VRle::Span *&bPtr,
+                 const VRle::Span *bEnd);
+};
+
+size_t SpanMerger::merge(VRle::Span *&aPtr, const VRle::Span *aEnd,
+                         VRle::Span *&bPtr, const VRle::Span *bEnd)
 {
-    std::array<VRle::Span, 256> temp;
-    VRle::Span *                out = result->spans;
-    size_t                      available = result->alloc;
-    VRle::Span *                aPtr = a->spans;
-    VRle::Span *                aEnd = a->spans + a->size;
-    VRle::Span *                bPtr = b->spans;
-    VRle::Span *                bEnd = b->spans + b->size;
+    assert(aPtr->y == bPtr->y);
 
-    while (available && aPtr < aEnd && bPtr < bEnd) {
-        if (aPtr->y < bPtr->y) {
-            *out++ = *aPtr++;
-            available--;
-        } else if (bPtr->y < aPtr->y) {
-            *out++ = *bPtr++;
-            available--;
-        } else {  // same y
-            VRle::Span *aStart = aPtr;
-            VRle::Span *bStart = bPtr;
-
-            int y = aPtr->y;
-
-            while (aPtr < aEnd && aPtr->y == y) aPtr++;
-            while (bPtr < bEnd && bPtr->y == y) bPtr++;
-
-            int aLength = (aPtr - 1)->x + (aPtr - 1)->len;
-            int bLength = (bPtr - 1)->x + (bPtr - 1)->len;
-            int offset = std::min(aStart->x, bStart->x);
-
-            std::array<uchar, 1024> array = {{0}};
-            blit(aStart, (aPtr - aStart), array.data(), -offset);
-            if (op == Operation::Add)
-                blitSrcOver(bStart, (bPtr - bStart), array.data(), -offset);
-            else if (op == Operation::Xor)
-                blitXor(bStart, (bPtr - bStart), array.data(), -offset);
-            VRle::Span *tResult = temp.data();
-            size_t size = bufferToRle(array.data(), std::max(aLength, bLength),
-                                      offset, y, tResult);
-            if (available >= size) {
-                while (size--) {
-                    *out++ = *tResult++;
-                    available--;
-                }
-            } else {
-                aPtr = aStart;
-                bPtr = bStart;
-                break;
-            }
-        }
+    _aStart = aPtr;
+    _bStart = bPtr;
+    int lb = std::min(aPtr->x, bPtr->x);
+    int y = aPtr->y;
+
+    while (aPtr < aEnd && aPtr->y == y) aPtr++;
+    while (bPtr < bEnd && bPtr->y == y) bPtr++;
+
+    int ub = std::max((aPtr - 1)->x + (aPtr - 1)->len,
+                      (bPtr - 1)->x + (bPtr - 1)->len);
+    int length = (lb < 0) ? ub + lb : ub - lb;
+
+    if (length <= 0 || size_t(length) >= _buffer.max_size()) {
+        // can't handle merge . skip
+        return 0;
     }
-    // update the span list that yet to be processed
-    a->spans = aPtr;
-    a->size = aEnd - aPtr;
 
-    // update the clip list that yet to be processed
-    b->spans = bPtr;
-    b->size = bEnd - bPtr;
+    // clear buffer
+    memset(_buffer.data(), 0, length);
 
-    // update the result
-    result->size = result->alloc - available;
+    // blit a to buffer
+    blitSrc(_aStart, aPtr - _aStart, _buffer.data(), -lb);
+
+    // blit b to buffer
+    _blitter(_bStart, bPtr - _bStart, _buffer.data(), -lb);
+
+    // convert buffer to span
+    return bufferToRle(_buffer.data(), length, lb, y, _result.data());
 }
 
-static void rleSubstractWithRle(VRleHelper *a, VRleHelper *b,
-                                VRleHelper *result)
+static size_t _opGeneric(rle_view &a, rle_view &b, Result &result,
+                         VRle::Data::Op op)
 {
-    std::array<VRle::Span, 256> temp;
-    VRle::Span *                out = result->spans;
-    size_t                      available = result->alloc;
-    VRle::Span *                aPtr = a->spans;
-    VRle::Span *                aEnd = a->spans + a->size;
-    VRle::Span *                bPtr = b->spans;
-    VRle::Span *                bEnd = b->spans + b->size;
+    SpanMerger merger{op};
+
+    auto   out = result.data();
+    size_t available = result.max_size();
+    auto   aPtr = a.data();
+    auto   aEnd = a.data() + a.size();
+    auto   bPtr = b.data();
+    auto   bEnd = b.data() + b.size();
+
+    // only logic change for substract operation.
+    const bool keep = op != (VRle::Data::Op::Substract);
 
     while (available && aPtr < aEnd && bPtr < bEnd) {
         if (aPtr->y < bPtr->y) {
             *out++ = *aPtr++;
             available--;
         } else if (bPtr->y < aPtr->y) {
+            if (keep) {
+                *out++ = *bPtr;
+                available--;
+            }
             bPtr++;
         } else {  // same y
-            VRle::Span *aStart = aPtr;
-            VRle::Span *bStart = bPtr;
-
-            int y = aPtr->y;
-
-            while (aPtr < aEnd && aPtr->y == y) aPtr++;
-            while (bPtr < bEnd && bPtr->y == y) bPtr++;
-
-            int aLength = (aPtr - 1)->x + (aPtr - 1)->len;
-            int bLength = (bPtr - 1)->x + (bPtr - 1)->len;
-            int offset = std::min(aStart->x, bStart->x);
-
-            std::array<uchar, 1024> array = {{0}};
-            blit(aStart, (aPtr - aStart), array.data(), -offset);
-            blitDestinationOut(bStart, (bPtr - bStart), array.data(), -offset);
-            VRle::Span *tResult = temp.data();
-            size_t size = bufferToRle(array.data(), std::max(aLength, bLength),
-                                      offset, y, tResult);
-            if (available >= size) {
-                while (size--) {
-                    *out++ = *tResult++;
-                    available--;
+            auto count = merger.merge(aPtr, aEnd, bPtr, bEnd);
+            if (available >= count) {
+                if (count) {
+                    memcpy(out, merger.data(), count * sizeof(VRle::Span));
+                    out += count;
+                    available -= count;
                 }
             } else {
-                aPtr = aStart;
-                bPtr = bStart;
+                // not enough space try next time.
+                merger.revert(aPtr, bPtr);
                 break;
             }
         }
     }
     // update the span list that yet to be processed
-    a->spans = aPtr;
-    a->size = size_t(aEnd - aPtr);
+    a = {aPtr, size_t(aEnd - aPtr)};
+    b = {bPtr, size_t(bEnd - bPtr)};
 
-    // update the clip list that yet to be processed
-    b->spans = bPtr;
-    b->size = size_t(bEnd - bPtr);
-
-    // update the result
-    result->size = result->alloc - available;
+    return result.max_size() - available;
 }
 
 /*
@@ -719,7 +656,48 @@ static void rleSubstractWithRle(VRleHelper *a, VRleHelper *b,
  * this function is thread safe as it uses thread_local variable
  * which is unique per thread.
  */
-static vthread_local VRle::VRleData Scratch_Object;
+static vthread_local VRle::Data Scratch_Object;
+
+VRle VRle::opGeneric(const VRle &o, Data::Op op) const
+{
+    if (empty()) return o;
+    if (o.empty()) return *this;
+
+    Scratch_Object.reset();
+    Scratch_Object.opGeneric(d.read(), o.d.read(), op);
+
+    VRle result;
+    result.d.write() = Scratch_Object;
+
+    return result;
+}
+
+VRle VRle::operator-(const VRle &o) const
+{
+    if (empty()) return {};
+    if (o.empty()) return *this;
+
+    Scratch_Object.reset();
+    Scratch_Object.opSubstract(d.read(), o.d.read());
+
+    VRle result;
+    result.d.write() = Scratch_Object;
+
+    return result;
+}
+
+VRle VRle::operator&(const VRle &o) const
+{
+    if (empty() || o.empty()) return {};
+
+    Scratch_Object.reset();
+    Scratch_Object.opIntersect(d.read().view(), o.d.read().view());
+
+    VRle result;
+    result.d.write() = Scratch_Object;
+
+    return result;
+}
 
 void VRle::operator&=(const VRle &o)
 {
@@ -729,7 +707,7 @@ void VRle::operator&=(const VRle &o)
         return;
     }
     Scratch_Object.reset();
-    Scratch_Object.opIntersect(d.read(), o.d.read());
+    Scratch_Object.opIntersect(d.read().view(), o.d.read().view());
     d.write() = Scratch_Object;
 }
 
@@ -754,9 +732,16 @@ VRle operator&(const VRect &rect, const VRle &o)
     Scratch_Object.addRect(rect);
 
     VRle result;
-    result.d.write().opIntersect(Scratch_Object, o.d.read());
+    result.d.write().opIntersect(Scratch_Object.view(), o.d.read().view());
 
     return result;
 }
 
+void VRle::intersect(const VRle &clip, VRleSpanCb cb, void *userData) const
+{
+    if (empty() || clip.empty()) return;
+
+    _opIntersect(d.read().view(), clip.d.read().view(), cb, userData);
+}
+
 V_END_NAMESPACE
index d25ef8d..761e5d1 100644 (file)
@@ -41,15 +41,18 @@ public:
     };
     using VRleSpanCb = void (*)(size_t count, const VRle::Span *spans,
                                 void *userData);
-    bool  empty() const;
-    VRect boundingRect() const;
-    void  setBoundingRect(const VRect &bbox);
-    void  addSpan(const VRle::Span *span, size_t count);
+    bool  empty() const { return d->empty(); }
+    VRect boundingRect() const { return d->bbox(); }
+    void  setBoundingRect(const VRect &bbox) { d->setBbox(bbox); }
+    void  addSpan(const VRle::Span *span, size_t count)
+    {
+        d.write().addSpan(span, count);
+    }
 
-    void reset();
-    void translate(const VPoint &p);
+    void reset() { d.write().reset(); }
+    void translate(const VPoint &p) { d.write().translate(p); }
 
-    void operator*=(uchar alpha);
+    void operator*=(uchar alpha) { d.write() *= alpha; }
 
     void intersect(const VRect &r, VRleSpanCb cb, void *userData) const;
     void intersect(const VRle &rle, VRleSpanCb cb, void *userData) const;
@@ -57,19 +60,30 @@ public:
     void operator&=(const VRle &o);
     VRle operator&(const VRle &o) const;
     VRle operator-(const VRle &o) const;
-    VRle operator+(const VRle &o) const;
-    VRle operator^(const VRle &o) const;
+    VRle operator+(const VRle &o) const { return opGeneric(o, Data::Op::Add); }
+    VRle operator^(const VRle &o) const { return opGeneric(o, Data::Op::Xor); }
 
     friend VRle operator-(const VRect &rect, const VRle &o);
     friend VRle operator&(const VRect &rect, const VRle &o);
 
     bool   unique() const { return d.unique(); }
     size_t refCount() const { return d.refCount(); }
-    void   clone(const VRle &o);
+    void   clone(const VRle &o) { d.write().clone(o.d.read()); }
 
 public:
-    struct VRleData {
-        enum class OpCode { Add, Xor };
+    struct View {
+        Span * _data;
+        size_t _size;
+        View(const Span *data, size_t sz) : _data((Span *)data), _size(sz) {}
+        Span * data() { return _data; }
+        size_t size() { return _size; }
+    };
+    struct Data {
+        enum class Op { Add, Xor, Substract };
+        VRle::View view() const
+        {
+            return VRle::View(mSpans.data(), mSpans.size());
+        }
         bool  empty() const { return mSpans.empty(); }
         void  addSpan(const VRle::Span *span, size_t count);
         void  updateBbox() const;
@@ -78,13 +92,12 @@ public:
         void  reset();
         void  translate(const VPoint &p);
         void  operator*=(uchar alpha);
+        void  opGeneric(const VRle::Data &, const VRle::Data &, Op code);
+        void  opSubstract(const VRle::Data &, const VRle::Data &);
+        void  opIntersect(VRle::View a, VRle::View b);
         void  opIntersect(const VRect &, VRle::VRleSpanCb, void *) const;
-        void  opGeneric(const VRle::VRleData &, const VRle::VRleData &,
-                        OpCode code);
-        void  opSubstract(const VRle::VRleData &, const VRle::VRleData &);
-        void  opIntersect(const VRle::VRleData &, const VRle::VRleData &);
         void  addRect(const VRect &rect);
-        void  clone(const VRle::VRleData &);
+        void  clone(const VRle::Data &);
 
         std::vector<VRle::Span> mSpans;
         VPoint                  mOffset;
@@ -93,107 +106,16 @@ public:
     };
 
 private:
-    friend void opIntersectHelper(const VRle::VRleData &obj1,
-                                  const VRle::VRleData &obj2,
-                                  VRle::VRleSpanCb cb, void *userData);
+    VRle opGeneric(const VRle &o, Data::Op opcode) const;
 
-    vcow_ptr<VRleData> d;
+    vcow_ptr<Data> d;
 };
 
-inline bool VRle::empty() const
-{
-    return d->empty();
-}
-
-inline void VRle::addSpan(const VRle::Span *span, size_t count)
-{
-    d.write().addSpan(span, count);
-}
-
-inline VRect VRle::boundingRect() const
-{
-    return d->bbox();
-}
-
-inline void VRle::setBoundingRect(const VRect &bbox)
-{
-    d->setBbox(bbox);
-}
-
-inline void VRle::operator*=(uchar alpha)
-{
-    d.write() *= alpha;
-}
-
-inline VRle VRle::operator&(const VRle &o) const
-{
-    if (empty() || o.empty()) return VRle();
-
-    VRle result;
-    result.d.write().opIntersect(d.read(), o.d.read());
-
-    return result;
-}
-
-inline VRle VRle::operator+(const VRle &o) const
-{
-    if (empty()) return o;
-    if (o.empty()) return *this;
-
-    VRle result;
-    result.d.write().opGeneric(d.read(), o.d.read(), VRleData::OpCode::Add);
-
-    return result;
-}
-
-inline VRle VRle::operator^(const VRle &o) const
-{
-    if (empty()) return o;
-    if (o.empty()) return *this;
-
-    VRle result;
-    result.d.write().opGeneric(d.read(), o.d.read(), VRleData::OpCode::Xor);
-
-    return result;
-}
-
-inline VRle VRle::operator-(const VRle &o) const
-{
-    if (empty()) return VRle();
-    if (o.empty()) return *this;
-
-    VRle result;
-    result.d.write().opSubstract(d.read(), o.d.read());
-
-    return result;
-}
-
-inline void VRle::reset()
-{
-    d.write().reset();
-}
-
-inline void VRle::clone(const VRle &o)
-{
-    d.write().clone(o.d.read());
-}
-
-inline void VRle::translate(const VPoint &p)
-{
-    d.write().translate(p);
-}
-
 inline void VRle::intersect(const VRect &r, VRleSpanCb cb, void *userData) const
 {
     d->opIntersect(r, cb, userData);
 }
 
-inline void VRle::intersect(const VRle &r, VRleSpanCb cb, void *userData) const
-{
-    if (empty() || r.empty()) return;
-    opIntersectHelper(d.read(), r.d.read(), cb, userData);
-}
-
 V_END_NAMESPACE
 
 #endif  // VRLE_H