forked from lug/matterbridge
Convert .tgs with go libraries (and cgo) (telegram) (#1569)
This commit adds support for go/cgo tgs conversion when building with the -tags `cgo` The default binaries are still "pure" go and uses the old way of converting. * Move lottie_convert.py conversion code to its own file * Add optional libtgsconverter * Update vendor * Apply suggestions from code review * Update bridge/helper/libtgsconverter.go Co-authored-by: Wim <wim@42.be>
This commit is contained in:
748
vendor/github.com/Benau/go_rlottie/vector_vrle.cpp
generated
vendored
Normal file
748
vendor/github.com/Benau/go_rlottie/vector_vrle.cpp
generated
vendored
Normal file
@@ -0,0 +1,748 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "vector_vrle.h"
|
||||
#include "vector_vrect.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include "vector_vdebug.h"
|
||||
#include "vector_vglobal.h"
|
||||
|
||||
V_BEGIN_NAMESPACE
|
||||
|
||||
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 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::Data::addSpan(const VRle::Span *span, size_t count)
|
||||
{
|
||||
copy(span, count, mSpans);
|
||||
mBboxDirty = true;
|
||||
}
|
||||
|
||||
VRect VRle::Data::bbox() const
|
||||
{
|
||||
updateBbox();
|
||||
return mBbox;
|
||||
}
|
||||
|
||||
void VRle::Data::setBbox(const VRect &bbox) const
|
||||
{
|
||||
mBboxDirty = false;
|
||||
mBbox = bbox;
|
||||
}
|
||||
|
||||
void VRle::Data::reset()
|
||||
{
|
||||
mSpans.clear();
|
||||
mBbox = VRect();
|
||||
mOffset = VPoint();
|
||||
mBboxDirty = false;
|
||||
}
|
||||
|
||||
void VRle::Data::clone(const VRle::Data &o)
|
||||
{
|
||||
*this = o;
|
||||
}
|
||||
|
||||
void VRle::Data::translate(const VPoint &p)
|
||||
{
|
||||
// take care of last offset if applied
|
||||
mOffset = p - mOffset;
|
||||
int x = mOffset.x();
|
||||
int y = mOffset.y();
|
||||
for (auto &i : mSpans) {
|
||||
i.x = i.x + x;
|
||||
i.y = i.y + y;
|
||||
}
|
||||
updateBbox();
|
||||
mBbox.translate(mOffset.x(), mOffset.y());
|
||||
}
|
||||
|
||||
void VRle::Data::addRect(const VRect &rect)
|
||||
{
|
||||
int x = rect.left();
|
||||
int y = rect.top();
|
||||
int width = rect.width();
|
||||
int height = rect.height();
|
||||
|
||||
mSpans.reserve(size_t(height));
|
||||
|
||||
VRle::Span span;
|
||||
for (int i = 0; i < height; i++) {
|
||||
span.x = x;
|
||||
span.y = y + i;
|
||||
span.len = width;
|
||||
span.coverage = 255;
|
||||
mSpans.push_back(span);
|
||||
}
|
||||
mBbox = rect;
|
||||
}
|
||||
|
||||
void VRle::Data::updateBbox() const
|
||||
{
|
||||
if (!mBboxDirty) return;
|
||||
|
||||
mBboxDirty = false;
|
||||
|
||||
int l = std::numeric_limits<int>::max();
|
||||
const VRle::Span *span = mSpans.data();
|
||||
|
||||
mBbox = VRect();
|
||||
size_t sz = mSpans.size();
|
||||
if (sz) {
|
||||
int t = span[0].y;
|
||||
int b = span[sz - 1].y;
|
||||
int r = 0;
|
||||
for (size_t i = 0; i < sz; i++) {
|
||||
if (span[i].x < l) l = span[i].x;
|
||||
if (span[i].x + span[i].len > r) r = span[i].x + span[i].len;
|
||||
}
|
||||
mBbox = VRect(l, t, r - l, b - t + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void VRle::Data::operator*=(uchar alpha)
|
||||
{
|
||||
for (auto &i : mSpans) {
|
||||
i.coverage = divBy255(i.coverage * alpha);
|
||||
}
|
||||
}
|
||||
|
||||
void VRle::Data::opIntersect(const VRect &r, VRle::VRleSpanCb cb,
|
||||
void *userData) const
|
||||
{
|
||||
if (empty()) return;
|
||||
|
||||
if (r.contains(bbox())) {
|
||||
cb(mSpans.size(), mSpans.data(), userData);
|
||||
return;
|
||||
}
|
||||
|
||||
auto obj = view();
|
||||
Result result;
|
||||
// run till all the spans are processed
|
||||
while (obj.size()) {
|
||||
auto count = _opIntersect(r, obj, result);
|
||||
if (count) cb(count, result.data(), userData);
|
||||
}
|
||||
}
|
||||
|
||||
// res = a - b;
|
||||
void VRle::Data::opSubstract(const VRle::Data &aObj, const VRle::Data &bObj)
|
||||
{
|
||||
// if two rle are disjoint
|
||||
if (!aObj.bbox().intersects(bObj.bbox())) {
|
||||
mSpans = aObj.mSpans;
|
||||
} else {
|
||||
auto a = aObj.view();
|
||||
auto b = bObj.view();
|
||||
|
||||
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++;
|
||||
auto count = aPtr - a.data();
|
||||
if (count) copy(a.data(), count, mSpans);
|
||||
|
||||
// 2. forward b till it intersects with a
|
||||
if (aPtr != aEnd)
|
||||
while ((bPtr != bEnd) && (bPtr->y < aPtr->y)) bPtr++;
|
||||
|
||||
// update a and b object
|
||||
a = {aPtr, size_t(aEnd - aPtr)};
|
||||
b = {bPtr, size_t(bEnd - bPtr)};
|
||||
|
||||
// 3. calculate the intersect region
|
||||
Result result;
|
||||
|
||||
// run till all the spans are processed
|
||||
while (a.size() && b.size()) {
|
||||
auto count = _opGeneric(a, b, result, Op::Substract);
|
||||
if (count) copy(result.data(), count, mSpans);
|
||||
}
|
||||
|
||||
// 4. copy the rest of a
|
||||
if (a.size()) copy(a.data(), a.size(), mSpans);
|
||||
}
|
||||
|
||||
mBboxDirty = true;
|
||||
}
|
||||
|
||||
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.size() + b.size());
|
||||
|
||||
// if two rle are disjoint
|
||||
if (!aObj.bbox().intersects(bObj.bbox())) {
|
||||
if (a.data()[0].y < b.data()[0].y) {
|
||||
copy(a.data(), a.size(), mSpans);
|
||||
copy(b.data(), b.size(), mSpans);
|
||||
} else {
|
||||
copy(b.data(), b.size(), mSpans);
|
||||
copy(a.data(), a.size(), mSpans);
|
||||
}
|
||||
} else {
|
||||
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++;
|
||||
|
||||
auto count = aPtr - a.data();
|
||||
if (count) copy(a.data(), count, mSpans);
|
||||
|
||||
// 2. forward b till it intersects with a
|
||||
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
|
||||
Result result;
|
||||
|
||||
// run till all the spans are processed
|
||||
while (a.size() && b.size()) {
|
||||
auto count = _opGeneric(a, b, result, op);
|
||||
if (count) copy(result.data(), count, mSpans);
|
||||
}
|
||||
// 3. copy the rest
|
||||
if (b.size()) copy(b.data(), b.size(), mSpans);
|
||||
if (a.size()) copy(a.data(), a.size(), mSpans);
|
||||
}
|
||||
|
||||
mBboxDirty = true;
|
||||
}
|
||||
|
||||
static inline V_ALWAYS_INLINE void _opIntersectPrepare(VRle::View &a,
|
||||
VRle::View &b)
|
||||
{
|
||||
auto aPtr = a.data();
|
||||
auto aEnd = a.data() + a.size();
|
||||
auto bPtr = b.data();
|
||||
auto bEnd = b.data() + b.size();
|
||||
|
||||
// 1. advance a till it intersects with b
|
||||
while ((aPtr != aEnd) && (aPtr->y < bPtr->y)) aPtr++;
|
||||
|
||||
// 2. advance b till it intersects with a
|
||||
if (aPtr != aEnd)
|
||||
while ((bPtr != bEnd) && (bPtr->y < aPtr->y)) bPtr++;
|
||||
|
||||
// update a and b object
|
||||
a = {aPtr, size_t(aEnd - aPtr)};
|
||||
b = {bPtr, size_t(bEnd - bPtr)};
|
||||
}
|
||||
|
||||
void VRle::Data::opIntersect(VRle::View a, VRle::View b)
|
||||
{
|
||||
_opIntersectPrepare(a, b);
|
||||
Result result;
|
||||
while (a.size()) {
|
||||
auto count = _opIntersect(a, b, result);
|
||||
if (count) copy(result.data(), count, mSpans);
|
||||
}
|
||||
|
||||
updateBbox();
|
||||
}
|
||||
|
||||
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
|
||||
* tmp_clip : The rle list that will be use to clip the rle
|
||||
* tmp_obj : holds the list of spans that has to be clipped
|
||||
* result : will hold the result after the processing
|
||||
* NOTE: if the algorithm runs out of the result buffer list
|
||||
* it will stop and update the tmp_obj with the span list
|
||||
* that are yet to be processed as well as the tpm_clip object
|
||||
* with the unprocessed clip spans.
|
||||
*/
|
||||
|
||||
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 > spans->y) {
|
||||
++spans;
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
cx2 = cx1 + clipSpans->len;
|
||||
|
||||
if (cx1 < sx1 && cx2 < sx1) {
|
||||
++clipSpans;
|
||||
continue;
|
||||
} else if (sx1 < cx1 && sx2 < cx1) {
|
||||
++spans;
|
||||
continue;
|
||||
}
|
||||
x = std::max(sx1, cx1);
|
||||
len = std::min(sx2, cx2) - x;
|
||||
if (len) {
|
||||
out->x = std::max(sx1, cx1);
|
||||
out->len = (std::min(sx2, cx2) - out->x);
|
||||
out->y = spans->y;
|
||||
out->coverage = divBy255(spans->coverage * clipSpans->coverage);
|
||||
++out;
|
||||
--available;
|
||||
}
|
||||
if (sx2 < cx2) {
|
||||
++spans;
|
||||
} else {
|
||||
++clipSpans;
|
||||
}
|
||||
}
|
||||
|
||||
// update the obj view yet to be processed
|
||||
obj = {spans, size_t(end - spans)};
|
||||
|
||||
// update the clip view yet to be processed
|
||||
clip = {clipSpans, size_t(clipEnd - clipSpans)};
|
||||
|
||||
return result.max_size() - available;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function will clip a rle list with a given rect
|
||||
* clip : The clip rect that will be use to clip the rle
|
||||
* tmp_obj : holds the list of spans that has to be clipped
|
||||
* result : will hold the result after the processing
|
||||
* NOTE: if the algorithm runs out of the result buffer list
|
||||
* it will stop and update the tmp_obj with the span list
|
||||
* that are yet to be processed
|
||||
*/
|
||||
static size_t _opIntersect(const VRect &clip, rle_view &obj, Result &result)
|
||||
{
|
||||
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 (span.y < miny || span.x > maxx || span.x + span.len <= minx) {
|
||||
++ptr;
|
||||
continue;
|
||||
}
|
||||
if (span.x < minx) {
|
||||
out->len = std::min(span.len - (minx - span.x), maxx - minx + 1);
|
||||
out->x = minx;
|
||||
} else {
|
||||
out->x = span.x;
|
||||
out->len = std::min(span.len, ushort(maxx - span.x + 1));
|
||||
}
|
||||
if (out->len != 0) {
|
||||
out->y = span.y;
|
||||
out->coverage = span.coverage;
|
||||
++out;
|
||||
--available;
|
||||
}
|
||||
++ptr;
|
||||
}
|
||||
|
||||
// update the span list that yet to be processed
|
||||
obj = {ptr, size_t(end - ptr)};
|
||||
|
||||
return result.max_size() - available;
|
||||
}
|
||||
|
||||
static void blitXor(VRle::Span *spans, int count, uchar *buffer, int offsetX)
|
||||
{
|
||||
while (count--) {
|
||||
int x = spans->x + offsetX;
|
||||
int l = spans->len;
|
||||
uchar *ptr = buffer + x;
|
||||
while (l--) {
|
||||
int da = *ptr;
|
||||
*ptr = divBy255((255 - spans->coverage) * (da) +
|
||||
spans->coverage * (255 - da));
|
||||
ptr++;
|
||||
}
|
||||
spans++;
|
||||
}
|
||||
}
|
||||
|
||||
static void blitDestinationOut(VRle::Span *spans, int count, uchar *buffer,
|
||||
int offsetX)
|
||||
{
|
||||
while (count--) {
|
||||
int x = spans->x + offsetX;
|
||||
int l = spans->len;
|
||||
uchar *ptr = buffer + x;
|
||||
while (l--) {
|
||||
*ptr = divBy255((255 - spans->coverage) * (*ptr));
|
||||
ptr++;
|
||||
}
|
||||
spans++;
|
||||
}
|
||||
}
|
||||
|
||||
static void blitSrcOver(VRle::Span *spans, int count, uchar *buffer,
|
||||
int offsetX)
|
||||
{
|
||||
while (count--) {
|
||||
int x = spans->x + offsetX;
|
||||
int l = spans->len;
|
||||
uchar *ptr = buffer + x;
|
||||
while (l--) {
|
||||
*ptr = spans->coverage + divBy255((255 - spans->coverage) * (*ptr));
|
||||
ptr++;
|
||||
}
|
||||
spans++;
|
||||
}
|
||||
}
|
||||
|
||||
void blitSrc(VRle::Span *spans, int count, uchar *buffer, int offsetX)
|
||||
{
|
||||
while (count--) {
|
||||
int x = spans->x + offsetX;
|
||||
int l = spans->len;
|
||||
uchar *ptr = buffer + x;
|
||||
while (l--) {
|
||||
*ptr = std::max(spans->coverage, *ptr);
|
||||
ptr++;
|
||||
}
|
||||
spans++;
|
||||
}
|
||||
}
|
||||
|
||||
size_t bufferToRle(uchar *buffer, int size, int offsetX, int y, VRle::Span *out)
|
||||
{
|
||||
size_t count = 0;
|
||||
uchar value = buffer[0];
|
||||
int curIndex = 0;
|
||||
|
||||
// size = offsetX < 0 ? size + offsetX : size;
|
||||
for (int i = 0; i < size; i++) {
|
||||
uchar curValue = buffer[0];
|
||||
if (value != curValue) {
|
||||
if (value) {
|
||||
out->y = y;
|
||||
out->x = offsetX + curIndex;
|
||||
out->len = i - curIndex;
|
||||
out->coverage = value;
|
||||
out++;
|
||||
count++;
|
||||
}
|
||||
curIndex = i;
|
||||
value = curValue;
|
||||
}
|
||||
buffer++;
|
||||
}
|
||||
if (value) {
|
||||
out->y = y;
|
||||
out->x = offsetX + curIndex;
|
||||
out->len = size - curIndex;
|
||||
out->coverage = value;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
assert(aPtr->y == bPtr->y);
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
memset(_buffer.data(), 0, length);
|
||||
|
||||
// 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 size_t _opGeneric(rle_view &a, rle_view &b, Result &result,
|
||||
VRle::Data::Op op)
|
||||
{
|
||||
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
|
||||
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 {
|
||||
// not enough space try next time.
|
||||
merger.revert(aPtr, bPtr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// update the span list that yet to be processed
|
||||
a = {aPtr, size_t(aEnd - aPtr)};
|
||||
b = {bPtr, size_t(bEnd - bPtr)};
|
||||
|
||||
return result.max_size() - available;
|
||||
}
|
||||
|
||||
/*
|
||||
* this api makes use of thread_local temporary
|
||||
* buffer to avoid creating intermediate temporary rle buffer
|
||||
* the scratch buffer object will grow its size on demand
|
||||
* so that future call won't need any more memory allocation.
|
||||
* this function is thread safe as it uses thread_local variable
|
||||
* which is unique per thread.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
if (empty()) return;
|
||||
if (o.empty()) {
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
Scratch_Object.reset();
|
||||
Scratch_Object.opIntersect(d.read().view(), o.d.read().view());
|
||||
d.write() = Scratch_Object;
|
||||
}
|
||||
|
||||
VRle operator-(const VRect &rect, const VRle &o)
|
||||
{
|
||||
if (rect.empty()) return {};
|
||||
|
||||
Scratch_Object.reset();
|
||||
Scratch_Object.addRect(rect);
|
||||
|
||||
VRle result;
|
||||
result.d.write().opSubstract(Scratch_Object, o.d.read());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
VRle operator&(const VRect &rect, const VRle &o)
|
||||
{
|
||||
if (rect.empty() || o.empty()) return {};
|
||||
|
||||
Scratch_Object.reset();
|
||||
Scratch_Object.addRect(rect);
|
||||
|
||||
VRle result;
|
||||
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
|
||||
Reference in New Issue
Block a user