matterbridge/vendor/github.com/Benau/go_rlottie/vector_vrle.cpp

749 lines
20 KiB
C++
Raw Permalink Normal View History

/*
* 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