2ca79399b9
since the transparent region hint really depends on the content of the window containing the SurfaceView (it's calculated by the view hierarchy based on overlapping views), it makes sense to latch it only when the content of the window (the app) changes. This should help fixing drawing artifacts when changing the layout of a window containing a SurfaceView. Bug: 8511430 Change-Id: Ic3aa668495293cb4d82a2cd7dcf3b6a337287678
825 lines
24 KiB
C++
825 lines
24 KiB
C++
/*
|
|
* Copyright (C) 2007 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.
|
|
*/
|
|
|
|
#define LOG_TAG "Region"
|
|
|
|
#include <limits.h>
|
|
|
|
#include <utils/Log.h>
|
|
#include <utils/String8.h>
|
|
#include <utils/CallStack.h>
|
|
|
|
#include <ui/Rect.h>
|
|
#include <ui/Region.h>
|
|
#include <ui/Point.h>
|
|
|
|
#include <private/ui/RegionHelper.h>
|
|
|
|
// ----------------------------------------------------------------------------
|
|
#define VALIDATE_REGIONS (false)
|
|
#define VALIDATE_WITH_CORECG (false)
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if VALIDATE_WITH_CORECG
|
|
#include <core/SkRegion.h>
|
|
#endif
|
|
|
|
namespace android {
|
|
// ----------------------------------------------------------------------------
|
|
|
|
enum {
|
|
op_nand = region_operator<Rect>::op_nand,
|
|
op_and = region_operator<Rect>::op_and,
|
|
op_or = region_operator<Rect>::op_or,
|
|
op_xor = region_operator<Rect>::op_xor
|
|
};
|
|
|
|
enum {
|
|
direction_LTR,
|
|
direction_RTL
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
Region::Region() {
|
|
mStorage.add(Rect(0,0));
|
|
}
|
|
|
|
Region::Region(const Region& rhs)
|
|
: mStorage(rhs.mStorage)
|
|
{
|
|
#if VALIDATE_REGIONS
|
|
validate(rhs, "rhs copy-ctor");
|
|
#endif
|
|
}
|
|
|
|
Region::Region(const Rect& rhs) {
|
|
mStorage.add(rhs);
|
|
}
|
|
|
|
Region::~Region()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Copy rects from the src vector into the dst vector, resolving vertical T-Junctions along the way
|
|
*
|
|
* First pass through, divideSpanRTL will be set because the 'previous span' (indexing into the dst
|
|
* vector) will be reversed. Each rectangle in the original list, starting from the bottom, will be
|
|
* compared with the span directly below, and subdivided as needed to resolve T-junctions.
|
|
*
|
|
* The resulting temporary vector will be a completely reversed copy of the original, without any
|
|
* bottom-up T-junctions.
|
|
*
|
|
* Second pass through, divideSpanRTL will be false since the previous span will index into the
|
|
* final, correctly ordered region buffer. Each rectangle will be compared with the span directly
|
|
* above it, and subdivided to resolve any remaining T-junctions.
|
|
*/
|
|
static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end,
|
|
Vector<Rect>& dst, int spanDirection) {
|
|
dst.clear();
|
|
|
|
const Rect* current = end - 1;
|
|
int lastTop = current->top;
|
|
|
|
// add first span immediately
|
|
do {
|
|
dst.add(*current);
|
|
current--;
|
|
} while (current->top == lastTop && current >= begin);
|
|
|
|
unsigned int beginLastSpan = -1;
|
|
unsigned int endLastSpan = -1;
|
|
int top = -1;
|
|
int bottom = -1;
|
|
|
|
// for all other spans, split if a t-junction exists in the span directly above
|
|
while (current >= begin) {
|
|
if (current->top != (current + 1)->top) {
|
|
// new span
|
|
if ((spanDirection == direction_RTL && current->bottom != (current + 1)->top) ||
|
|
(spanDirection == direction_LTR && current->top != (current + 1)->bottom)) {
|
|
// previous span not directly adjacent, don't check for T junctions
|
|
beginLastSpan = INT_MAX;
|
|
} else {
|
|
beginLastSpan = endLastSpan + 1;
|
|
}
|
|
endLastSpan = dst.size() - 1;
|
|
|
|
top = current->top;
|
|
bottom = current->bottom;
|
|
}
|
|
int left = current->left;
|
|
int right = current->right;
|
|
|
|
for (unsigned int prevIndex = beginLastSpan; prevIndex <= endLastSpan; prevIndex++) {
|
|
const Rect* prev = &dst[prevIndex];
|
|
if (spanDirection == direction_RTL) {
|
|
// iterating over previous span RTL, quit if it's too far left
|
|
if (prev->right <= left) break;
|
|
|
|
if (prev->right > left && prev->right < right) {
|
|
dst.add(Rect(prev->right, top, right, bottom));
|
|
right = prev->right;
|
|
}
|
|
|
|
if (prev->left > left && prev->left < right) {
|
|
dst.add(Rect(prev->left, top, right, bottom));
|
|
right = prev->left;
|
|
}
|
|
|
|
// if an entry in the previous span is too far right, nothing further left in the
|
|
// current span will need it
|
|
if (prev->left >= right) {
|
|
beginLastSpan = prevIndex;
|
|
}
|
|
} else {
|
|
// iterating over previous span LTR, quit if it's too far right
|
|
if (prev->left >= right) break;
|
|
|
|
if (prev->left > left && prev->left < right) {
|
|
dst.add(Rect(left, top, prev->left, bottom));
|
|
left = prev->left;
|
|
}
|
|
|
|
if (prev->right > left && prev->right < right) {
|
|
dst.add(Rect(left, top, prev->right, bottom));
|
|
left = prev->right;
|
|
}
|
|
// if an entry in the previous span is too far left, nothing further right in the
|
|
// current span will need it
|
|
if (prev->right <= left) {
|
|
beginLastSpan = prevIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (left < right) {
|
|
dst.add(Rect(left, top, right, bottom));
|
|
}
|
|
|
|
current--;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a new region with the same data as the argument, but divides rectangles as necessary to
|
|
* remove T-Junctions
|
|
*
|
|
* Note: the output will not necessarily be a very efficient representation of the region, since it
|
|
* may be that a triangle-based approach would generate significantly simpler geometry
|
|
*/
|
|
Region Region::createTJunctionFreeRegion(const Region& r) {
|
|
if (r.isEmpty()) return r;
|
|
if (r.isRect()) return r;
|
|
|
|
Vector<Rect> reversed;
|
|
reverseRectsResolvingJunctions(r.begin(), r.end(), reversed, direction_RTL);
|
|
|
|
Region outputRegion;
|
|
reverseRectsResolvingJunctions(reversed.begin(), reversed.end(),
|
|
outputRegion.mStorage, direction_LTR);
|
|
outputRegion.mStorage.add(r.getBounds()); // to make region valid, mStorage must end with bounds
|
|
|
|
#if VALIDATE_REGIONS
|
|
validate(outputRegion, "T-Junction free region");
|
|
#endif
|
|
|
|
return outputRegion;
|
|
}
|
|
|
|
Region& Region::operator = (const Region& rhs)
|
|
{
|
|
#if VALIDATE_REGIONS
|
|
validate(*this, "this->operator=");
|
|
validate(rhs, "rhs.operator=");
|
|
#endif
|
|
mStorage = rhs.mStorage;
|
|
return *this;
|
|
}
|
|
|
|
Region& Region::makeBoundsSelf()
|
|
{
|
|
if (mStorage.size() >= 2) {
|
|
const Rect bounds(getBounds());
|
|
mStorage.clear();
|
|
mStorage.add(bounds);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void Region::clear()
|
|
{
|
|
mStorage.clear();
|
|
mStorage.add(Rect(0,0));
|
|
}
|
|
|
|
void Region::set(const Rect& r)
|
|
{
|
|
mStorage.clear();
|
|
mStorage.add(r);
|
|
}
|
|
|
|
void Region::set(uint32_t w, uint32_t h)
|
|
{
|
|
mStorage.clear();
|
|
mStorage.add(Rect(w,h));
|
|
}
|
|
|
|
bool Region::isTriviallyEqual(const Region& region) const {
|
|
return begin() == region.begin();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void Region::addRectUnchecked(int l, int t, int r, int b)
|
|
{
|
|
Rect rect(l,t,r,b);
|
|
size_t where = mStorage.size() - 1;
|
|
mStorage.insertAt(rect, where, 1);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
Region& Region::orSelf(const Rect& r) {
|
|
return operationSelf(r, op_or);
|
|
}
|
|
Region& Region::xorSelf(const Rect& r) {
|
|
return operationSelf(r, op_xor);
|
|
}
|
|
Region& Region::andSelf(const Rect& r) {
|
|
return operationSelf(r, op_and);
|
|
}
|
|
Region& Region::subtractSelf(const Rect& r) {
|
|
return operationSelf(r, op_nand);
|
|
}
|
|
Region& Region::operationSelf(const Rect& r, int op) {
|
|
Region lhs(*this);
|
|
boolean_operation(op, *this, lhs, r);
|
|
return *this;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
Region& Region::orSelf(const Region& rhs) {
|
|
return operationSelf(rhs, op_or);
|
|
}
|
|
Region& Region::xorSelf(const Region& rhs) {
|
|
return operationSelf(rhs, op_xor);
|
|
}
|
|
Region& Region::andSelf(const Region& rhs) {
|
|
return operationSelf(rhs, op_and);
|
|
}
|
|
Region& Region::subtractSelf(const Region& rhs) {
|
|
return operationSelf(rhs, op_nand);
|
|
}
|
|
Region& Region::operationSelf(const Region& rhs, int op) {
|
|
Region lhs(*this);
|
|
boolean_operation(op, *this, lhs, rhs);
|
|
return *this;
|
|
}
|
|
|
|
Region& Region::translateSelf(int x, int y) {
|
|
if (x|y) translate(*this, x, y);
|
|
return *this;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
const Region Region::merge(const Rect& rhs) const {
|
|
return operation(rhs, op_or);
|
|
}
|
|
const Region Region::mergeExclusive(const Rect& rhs) const {
|
|
return operation(rhs, op_xor);
|
|
}
|
|
const Region Region::intersect(const Rect& rhs) const {
|
|
return operation(rhs, op_and);
|
|
}
|
|
const Region Region::subtract(const Rect& rhs) const {
|
|
return operation(rhs, op_nand);
|
|
}
|
|
const Region Region::operation(const Rect& rhs, int op) const {
|
|
Region result;
|
|
boolean_operation(op, result, *this, rhs);
|
|
return result;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
const Region Region::merge(const Region& rhs) const {
|
|
return operation(rhs, op_or);
|
|
}
|
|
const Region Region::mergeExclusive(const Region& rhs) const {
|
|
return operation(rhs, op_xor);
|
|
}
|
|
const Region Region::intersect(const Region& rhs) const {
|
|
return operation(rhs, op_and);
|
|
}
|
|
const Region Region::subtract(const Region& rhs) const {
|
|
return operation(rhs, op_nand);
|
|
}
|
|
const Region Region::operation(const Region& rhs, int op) const {
|
|
Region result;
|
|
boolean_operation(op, result, *this, rhs);
|
|
return result;
|
|
}
|
|
|
|
const Region Region::translate(int x, int y) const {
|
|
Region result;
|
|
translate(result, *this, x, y);
|
|
return result;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
Region& Region::orSelf(const Region& rhs, int dx, int dy) {
|
|
return operationSelf(rhs, dx, dy, op_or);
|
|
}
|
|
Region& Region::xorSelf(const Region& rhs, int dx, int dy) {
|
|
return operationSelf(rhs, dx, dy, op_xor);
|
|
}
|
|
Region& Region::andSelf(const Region& rhs, int dx, int dy) {
|
|
return operationSelf(rhs, dx, dy, op_and);
|
|
}
|
|
Region& Region::subtractSelf(const Region& rhs, int dx, int dy) {
|
|
return operationSelf(rhs, dx, dy, op_nand);
|
|
}
|
|
Region& Region::operationSelf(const Region& rhs, int dx, int dy, int op) {
|
|
Region lhs(*this);
|
|
boolean_operation(op, *this, lhs, rhs, dx, dy);
|
|
return *this;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
const Region Region::merge(const Region& rhs, int dx, int dy) const {
|
|
return operation(rhs, dx, dy, op_or);
|
|
}
|
|
const Region Region::mergeExclusive(const Region& rhs, int dx, int dy) const {
|
|
return operation(rhs, dx, dy, op_xor);
|
|
}
|
|
const Region Region::intersect(const Region& rhs, int dx, int dy) const {
|
|
return operation(rhs, dx, dy, op_and);
|
|
}
|
|
const Region Region::subtract(const Region& rhs, int dx, int dy) const {
|
|
return operation(rhs, dx, dy, op_nand);
|
|
}
|
|
const Region Region::operation(const Region& rhs, int dx, int dy, int op) const {
|
|
Region result;
|
|
boolean_operation(op, result, *this, rhs, dx, dy);
|
|
return result;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// This is our region rasterizer, which merges rects and spans together
|
|
// to obtain an optimal region.
|
|
class Region::rasterizer : public region_operator<Rect>::region_rasterizer
|
|
{
|
|
Rect bounds;
|
|
Vector<Rect>& storage;
|
|
Rect* head;
|
|
Rect* tail;
|
|
Vector<Rect> span;
|
|
Rect* cur;
|
|
public:
|
|
rasterizer(Region& reg)
|
|
: bounds(INT_MAX, 0, INT_MIN, 0), storage(reg.mStorage), head(), tail(), cur() {
|
|
storage.clear();
|
|
}
|
|
|
|
~rasterizer() {
|
|
if (span.size()) {
|
|
flushSpan();
|
|
}
|
|
if (storage.size()) {
|
|
bounds.top = storage.itemAt(0).top;
|
|
bounds.bottom = storage.top().bottom;
|
|
if (storage.size() == 1) {
|
|
storage.clear();
|
|
}
|
|
} else {
|
|
bounds.left = 0;
|
|
bounds.right = 0;
|
|
}
|
|
storage.add(bounds);
|
|
}
|
|
|
|
virtual void operator()(const Rect& rect) {
|
|
//ALOGD(">>> %3d, %3d, %3d, %3d",
|
|
// rect.left, rect.top, rect.right, rect.bottom);
|
|
if (span.size()) {
|
|
if (cur->top != rect.top) {
|
|
flushSpan();
|
|
} else if (cur->right == rect.left) {
|
|
cur->right = rect.right;
|
|
return;
|
|
}
|
|
}
|
|
span.add(rect);
|
|
cur = span.editArray() + (span.size() - 1);
|
|
}
|
|
private:
|
|
template<typename T>
|
|
static inline T min(T rhs, T lhs) { return rhs < lhs ? rhs : lhs; }
|
|
template<typename T>
|
|
static inline T max(T rhs, T lhs) { return rhs > lhs ? rhs : lhs; }
|
|
void flushSpan() {
|
|
bool merge = false;
|
|
if (tail-head == ssize_t(span.size())) {
|
|
Rect const* p = span.editArray();
|
|
Rect const* q = head;
|
|
if (p->top == q->bottom) {
|
|
merge = true;
|
|
while (q != tail) {
|
|
if ((p->left != q->left) || (p->right != q->right)) {
|
|
merge = false;
|
|
break;
|
|
}
|
|
p++, q++;
|
|
}
|
|
}
|
|
}
|
|
if (merge) {
|
|
const int bottom = span[0].bottom;
|
|
Rect* r = head;
|
|
while (r != tail) {
|
|
r->bottom = bottom;
|
|
r++;
|
|
}
|
|
} else {
|
|
bounds.left = min(span.itemAt(0).left, bounds.left);
|
|
bounds.right = max(span.top().right, bounds.right);
|
|
storage.appendVector(span);
|
|
tail = storage.editArray() + storage.size();
|
|
head = tail - span.size();
|
|
}
|
|
span.clear();
|
|
}
|
|
};
|
|
|
|
bool Region::validate(const Region& reg, const char* name, bool silent)
|
|
{
|
|
bool result = true;
|
|
const_iterator cur = reg.begin();
|
|
const_iterator const tail = reg.end();
|
|
const_iterator prev = cur;
|
|
Rect b(*prev);
|
|
while (cur != tail) {
|
|
if (cur->isValid() == false) {
|
|
ALOGE_IF(!silent, "%s: region contains an invalid Rect", name);
|
|
result = false;
|
|
}
|
|
if (cur->right > region_operator<Rect>::max_value) {
|
|
ALOGE_IF(!silent, "%s: rect->right > max_value", name);
|
|
result = false;
|
|
}
|
|
if (cur->bottom > region_operator<Rect>::max_value) {
|
|
ALOGE_IF(!silent, "%s: rect->right > max_value", name);
|
|
result = false;
|
|
}
|
|
if (prev != cur) {
|
|
b.left = b.left < cur->left ? b.left : cur->left;
|
|
b.top = b.top < cur->top ? b.top : cur->top;
|
|
b.right = b.right > cur->right ? b.right : cur->right;
|
|
b.bottom = b.bottom > cur->bottom ? b.bottom : cur->bottom;
|
|
if ((*prev < *cur) == false) {
|
|
ALOGE_IF(!silent, "%s: region's Rects not sorted", name);
|
|
result = false;
|
|
}
|
|
if (cur->top == prev->top) {
|
|
if (cur->bottom != prev->bottom) {
|
|
ALOGE_IF(!silent, "%s: invalid span %p", name, cur);
|
|
result = false;
|
|
} else if (cur->left < prev->right) {
|
|
ALOGE_IF(!silent,
|
|
"%s: spans overlap horizontally prev=%p, cur=%p",
|
|
name, prev, cur);
|
|
result = false;
|
|
}
|
|
} else if (cur->top < prev->bottom) {
|
|
ALOGE_IF(!silent,
|
|
"%s: spans overlap vertically prev=%p, cur=%p",
|
|
name, prev, cur);
|
|
result = false;
|
|
}
|
|
prev = cur;
|
|
}
|
|
cur++;
|
|
}
|
|
if (b != reg.getBounds()) {
|
|
result = false;
|
|
ALOGE_IF(!silent,
|
|
"%s: invalid bounds [%d,%d,%d,%d] vs. [%d,%d,%d,%d]", name,
|
|
b.left, b.top, b.right, b.bottom,
|
|
reg.getBounds().left, reg.getBounds().top,
|
|
reg.getBounds().right, reg.getBounds().bottom);
|
|
}
|
|
if (reg.mStorage.size() == 2) {
|
|
result = false;
|
|
ALOGE_IF(!silent, "%s: mStorage size is 2, which is never valid", name);
|
|
}
|
|
if (result == false && !silent) {
|
|
reg.dump(name);
|
|
CallStack stack(LOG_TAG);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void Region::boolean_operation(int op, Region& dst,
|
|
const Region& lhs,
|
|
const Region& rhs, int dx, int dy)
|
|
{
|
|
#if VALIDATE_REGIONS
|
|
validate(lhs, "boolean_operation (before): lhs");
|
|
validate(rhs, "boolean_operation (before): rhs");
|
|
validate(dst, "boolean_operation (before): dst");
|
|
#endif
|
|
|
|
size_t lhs_count;
|
|
Rect const * const lhs_rects = lhs.getArray(&lhs_count);
|
|
|
|
size_t rhs_count;
|
|
Rect const * const rhs_rects = rhs.getArray(&rhs_count);
|
|
|
|
region_operator<Rect>::region lhs_region(lhs_rects, lhs_count);
|
|
region_operator<Rect>::region rhs_region(rhs_rects, rhs_count, dx, dy);
|
|
region_operator<Rect> operation(op, lhs_region, rhs_region);
|
|
{ // scope for rasterizer (dtor has side effects)
|
|
rasterizer r(dst);
|
|
operation(r);
|
|
}
|
|
|
|
#if VALIDATE_REGIONS
|
|
validate(lhs, "boolean_operation: lhs");
|
|
validate(rhs, "boolean_operation: rhs");
|
|
validate(dst, "boolean_operation: dst");
|
|
#endif
|
|
|
|
#if VALIDATE_WITH_CORECG
|
|
SkRegion sk_lhs;
|
|
SkRegion sk_rhs;
|
|
SkRegion sk_dst;
|
|
|
|
for (size_t i=0 ; i<lhs_count ; i++)
|
|
sk_lhs.op(
|
|
lhs_rects[i].left + dx,
|
|
lhs_rects[i].top + dy,
|
|
lhs_rects[i].right + dx,
|
|
lhs_rects[i].bottom + dy,
|
|
SkRegion::kUnion_Op);
|
|
|
|
for (size_t i=0 ; i<rhs_count ; i++)
|
|
sk_rhs.op(
|
|
rhs_rects[i].left + dx,
|
|
rhs_rects[i].top + dy,
|
|
rhs_rects[i].right + dx,
|
|
rhs_rects[i].bottom + dy,
|
|
SkRegion::kUnion_Op);
|
|
|
|
const char* name = "---";
|
|
SkRegion::Op sk_op;
|
|
switch (op) {
|
|
case op_or: sk_op = SkRegion::kUnion_Op; name="OR"; break;
|
|
case op_xor: sk_op = SkRegion::kUnion_XOR; name="XOR"; break;
|
|
case op_and: sk_op = SkRegion::kIntersect_Op; name="AND"; break;
|
|
case op_nand: sk_op = SkRegion::kDifference_Op; name="NAND"; break;
|
|
}
|
|
sk_dst.op(sk_lhs, sk_rhs, sk_op);
|
|
|
|
if (sk_dst.isEmpty() && dst.isEmpty())
|
|
return;
|
|
|
|
bool same = true;
|
|
Region::const_iterator head = dst.begin();
|
|
Region::const_iterator const tail = dst.end();
|
|
SkRegion::Iterator it(sk_dst);
|
|
while (!it.done()) {
|
|
if (head != tail) {
|
|
if (
|
|
head->left != it.rect().fLeft ||
|
|
head->top != it.rect().fTop ||
|
|
head->right != it.rect().fRight ||
|
|
head->bottom != it.rect().fBottom
|
|
) {
|
|
same = false;
|
|
break;
|
|
}
|
|
} else {
|
|
same = false;
|
|
break;
|
|
}
|
|
head++;
|
|
it.next();
|
|
}
|
|
|
|
if (head != tail) {
|
|
same = false;
|
|
}
|
|
|
|
if(!same) {
|
|
ALOGD("---\nregion boolean %s failed", name);
|
|
lhs.dump("lhs");
|
|
rhs.dump("rhs");
|
|
dst.dump("dst");
|
|
ALOGD("should be");
|
|
SkRegion::Iterator it(sk_dst);
|
|
while (!it.done()) {
|
|
ALOGD(" [%3d, %3d, %3d, %3d]",
|
|
it.rect().fLeft,
|
|
it.rect().fTop,
|
|
it.rect().fRight,
|
|
it.rect().fBottom);
|
|
it.next();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Region::boolean_operation(int op, Region& dst,
|
|
const Region& lhs,
|
|
const Rect& rhs, int dx, int dy)
|
|
{
|
|
if (!rhs.isValid()) {
|
|
ALOGE("Region::boolean_operation(op=%d) invalid Rect={%d,%d,%d,%d}",
|
|
op, rhs.left, rhs.top, rhs.right, rhs.bottom);
|
|
return;
|
|
}
|
|
|
|
#if VALIDATE_WITH_CORECG || VALIDATE_REGIONS
|
|
boolean_operation(op, dst, lhs, Region(rhs), dx, dy);
|
|
#else
|
|
size_t lhs_count;
|
|
Rect const * const lhs_rects = lhs.getArray(&lhs_count);
|
|
|
|
region_operator<Rect>::region lhs_region(lhs_rects, lhs_count);
|
|
region_operator<Rect>::region rhs_region(&rhs, 1, dx, dy);
|
|
region_operator<Rect> operation(op, lhs_region, rhs_region);
|
|
{ // scope for rasterizer (dtor has side effects)
|
|
rasterizer r(dst);
|
|
operation(r);
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
void Region::boolean_operation(int op, Region& dst,
|
|
const Region& lhs, const Region& rhs)
|
|
{
|
|
boolean_operation(op, dst, lhs, rhs, 0, 0);
|
|
}
|
|
|
|
void Region::boolean_operation(int op, Region& dst,
|
|
const Region& lhs, const Rect& rhs)
|
|
{
|
|
boolean_operation(op, dst, lhs, rhs, 0, 0);
|
|
}
|
|
|
|
void Region::translate(Region& reg, int dx, int dy)
|
|
{
|
|
if ((dx || dy) && !reg.isEmpty()) {
|
|
#if VALIDATE_REGIONS
|
|
validate(reg, "translate (before)");
|
|
#endif
|
|
size_t count = reg.mStorage.size();
|
|
Rect* rects = reg.mStorage.editArray();
|
|
while (count) {
|
|
rects->translate(dx, dy);
|
|
rects++;
|
|
count--;
|
|
}
|
|
#if VALIDATE_REGIONS
|
|
validate(reg, "translate (after)");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void Region::translate(Region& dst, const Region& reg, int dx, int dy)
|
|
{
|
|
dst = reg;
|
|
translate(dst, dx, dy);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
size_t Region::getSize() const {
|
|
return mStorage.size() * sizeof(Rect);
|
|
}
|
|
|
|
status_t Region::flatten(void* buffer) const {
|
|
#if VALIDATE_REGIONS
|
|
validate(*this, "Region::flatten");
|
|
#endif
|
|
Rect* rects = reinterpret_cast<Rect*>(buffer);
|
|
memcpy(rects, mStorage.array(), mStorage.size() * sizeof(Rect));
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t Region::unflatten(void const* buffer, size_t size) {
|
|
Region result;
|
|
if (size >= sizeof(Rect)) {
|
|
Rect const* rects = reinterpret_cast<Rect const*>(buffer);
|
|
size_t count = size / sizeof(Rect);
|
|
if (count > 0) {
|
|
result.mStorage.clear();
|
|
ssize_t err = result.mStorage.insertAt(0, count);
|
|
if (err < 0) {
|
|
return status_t(err);
|
|
}
|
|
memcpy(result.mStorage.editArray(), rects, count*sizeof(Rect));
|
|
}
|
|
}
|
|
#if VALIDATE_REGIONS
|
|
validate(result, "Region::unflatten");
|
|
#endif
|
|
|
|
if (!result.validate(result, "Region::unflatten", true)) {
|
|
ALOGE("Region::unflatten() failed, invalid region");
|
|
return BAD_VALUE;
|
|
}
|
|
mStorage = result.mStorage;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
Region::const_iterator Region::begin() const {
|
|
return mStorage.array();
|
|
}
|
|
|
|
Region::const_iterator Region::end() const {
|
|
size_t numRects = isRect() ? 1 : mStorage.size() - 1;
|
|
return mStorage.array() + numRects;
|
|
}
|
|
|
|
Rect const* Region::getArray(size_t* count) const {
|
|
const_iterator const b(begin());
|
|
const_iterator const e(end());
|
|
if (count) *count = e-b;
|
|
return b;
|
|
}
|
|
|
|
SharedBuffer const* Region::getSharedBuffer(size_t* count) const {
|
|
// We can get to the SharedBuffer of a Vector<Rect> because Rect has
|
|
// a trivial destructor.
|
|
SharedBuffer const* sb = SharedBuffer::bufferFromData(mStorage.array());
|
|
if (count) {
|
|
size_t numRects = isRect() ? 1 : mStorage.size() - 1;
|
|
count[0] = numRects;
|
|
}
|
|
sb->acquire();
|
|
return sb;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void Region::dump(String8& out, const char* what, uint32_t flags) const
|
|
{
|
|
(void)flags;
|
|
const_iterator head = begin();
|
|
const_iterator const tail = end();
|
|
|
|
size_t SIZE = 256;
|
|
char buffer[SIZE];
|
|
|
|
snprintf(buffer, SIZE, " Region %s (this=%p, count=%d)\n",
|
|
what, this, tail-head);
|
|
out.append(buffer);
|
|
while (head != tail) {
|
|
snprintf(buffer, SIZE, " [%3d, %3d, %3d, %3d]\n",
|
|
head->left, head->top, head->right, head->bottom);
|
|
out.append(buffer);
|
|
head++;
|
|
}
|
|
}
|
|
|
|
void Region::dump(const char* what, uint32_t flags) const
|
|
{
|
|
(void)flags;
|
|
const_iterator head = begin();
|
|
const_iterator const tail = end();
|
|
ALOGD(" Region %s (this=%p, count=%d)\n", what, this, tail-head);
|
|
while (head != tail) {
|
|
ALOGD(" [%3d, %3d, %3d, %3d]\n",
|
|
head->left, head->top, head->right, head->bottom);
|
|
head++;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
}; // namespace android
|