blob: c75e2032a68f4470ee799ebe981e40c0246b5bfc [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* @author Denis M. Kishenko
* @version $Revision$
*/
package org.apache.harmony.awt.gl;
import java.awt.Rectangle;
public class MultiRectAreaOp {
/**
* Rectangle buffer capacity
*/
public static final int RECT_CAPACITY = 16;
/**
* If number of rectangle in MultiRectArea object less than MAX_SIMPLE simple algorithm applies
*/
private static final int MAX_SIMPLE = 8;
/**
* Create buffer
*/
public static int[] createBuf(int capacity) {
if (capacity == 0) {
capacity = RECT_CAPACITY;
}
int[] buf = new int[capacity];
buf[0] = 1;
return buf;
}
/**
* Checks buffer size and reallocate if necessary
*/
public static int[] checkBufSize(int[] buf, int capacity) {
if (buf[0] + capacity >= buf.length) {
int length = buf[0] + (capacity > RECT_CAPACITY ? capacity : RECT_CAPACITY);
int[] tmp = new int[length];
System.arraycopy(buf, 0, tmp, 0, buf[0]);
buf = tmp;
}
buf[0] += capacity;
return buf;
}
/**
* Region class provides basic functionlity for MultiRectArea objects to make logical operations
*/
static class Region {
int[] region;
int[] active;
int[] bottom;
int index;
public Region(int[] region) {
this.region = region;
active = new int[RECT_CAPACITY];
bottom = new int[RECT_CAPACITY];
active[0] = 1;
bottom[0] = 1;
index = 1;
}
void addActive(int index) {
int length = active[0];
active = checkBufSize(active, 4);
int i = 1;
while(i < length) {
if (region[index] < active[i]) {
// Insert
System.arraycopy(active, i, active, i + 4, length - i);
length = i;
break;
}
i += 4;
}
System.arraycopy(region, index, active, length, 4);
}
void findActive(int top, int bottom) {
while(index < region[0]) {
if (region[index + 1] > bottom) { // y1 > bottom
return;
}
if (region[index + 3] >= top) { // y2 >= top
addActive(index);
}
index += 4;
}
}
void deleteActive(int bottom) {
int length = active[0];
for(int i = 1; i < length;) {
if (active[i + 3] == bottom) {
length -= 4;
if (i < length) {
System.arraycopy(active, i + 4, active, i, length - i);
}
} else {
i += 4;
}
}
active[0] = length;
}
void deleteActive() {
int length = active[0];
for(int i = length - 4; i > 0; i -= 4) {
if (active[i + 1] > active[i + 3]) {
length -= 4;
if (i < length) {
System.arraycopy(active, i + 4, active, i, length - i);
}
}
}
active[0] = length;
}
void createLevel(int[] level) {
int levelCount = 1;
int topIndex = 1;
int i = 1;
while(i < region[0]) {
int top = region[i + 1];
int bottom = region[i + 3] + 1;
int j = topIndex;
addTop: {
while(j < levelCount) {
if (level[j] == top) {
break addTop;
}
if (level[j] > top) {
System.arraycopy(level, j, level, j + 1, levelCount - j);
break;
}
j++;
}
level[j] = top;
levelCount++;
topIndex = j;
}
addBottom: {
while(j < levelCount) {
if (level[j] == bottom) {
break addBottom;
}
if (level[j] > bottom) {
System.arraycopy(level, j, level, j + 1, levelCount - j);
break;
}
j++;
};
level[j] = bottom;
levelCount++;
}
i += 4;
}
level[0] = levelCount;
}
static void sortOrdered(int[] src1, int[] src2, int[] dst) {
int length1 = src1[0];
int length2 = src2[0];
int count = 1;
int i1 = 1;
int i2 = 1;
int v1 = src1[1];
int v2 = src2[1];
while(true) {
LEFT: {
while(i1 < length1) {
v1 = src1[i1];
if (v1 >= v2) {
break LEFT;
}
dst[count++] = v1;
i1++;
}
while(i2 < length2) {
dst[count++] = src2[i2++];
}
dst[0] = count;
return;
}
RIGHT: {
while(i2 < length2) {
v2 = src2[i2];
if (v2 >= v1) {
break RIGHT;
}
dst[count++] = v2;
i2++;
}
while(i1 < length1) {
dst[count++] = src1[i1++];
}
dst[0] = count;
return;
}
if (v1 == v2) {
dst[count++] = v1;
i1++;
i2++;
if (i1 < length1) {
v1 = src1[i1];
}
if (i2 < length2 - 1) {
v2 = src2[i2];
}
}
}
// UNREACHABLE
}
}
/**
* Intersection class provides intersection of two MultiRectAre aobjects
*/
static class Intersection {
static void intersectRegions(int[] reg1, int[] reg2, MultiRectArea.RectCash dst, int height1, int height2) {
Region d1 = new Region(reg1);
Region d2 = new Region(reg2);
int[] level = new int[height1 + height2];
int[] level1 = new int[height1];
int[] level2 = new int[height2];
d1.createLevel(level1);
d2.createLevel(level2);
Region.sortOrdered(level1, level2, level);
int top;
int bottom = level[1] - 1;
for(int i = 2; i < level[0]; i++) {
top = bottom + 1;
bottom = level[i] - 1;
d1.findActive(top, bottom);
d2.findActive(top, bottom);
int i1 = 1;
int i2 = 1;
while(i1 < d1.active[0] && i2 < d2.active[0]) {
int x11 = d1.active[i1];
int x12 = d1.active[i1 + 2];
int x21 = d2.active[i2];
int x22 = d2.active[i2 + 2];
if (x11 <= x21) {
if (x12 >= x21) {
if (x12 <= x22) {
dst.addRectCashed(x21, top, x12, bottom);
i1 += 4;
} else {
dst.addRectCashed(x21, top, x22, bottom);
i2 += 4;
}
} else {
i1 += 4;
}
} else {
if (x22 >= x11) {
if (x22 <= x12) {
dst.addRectCashed(x11, top, x22, bottom);
i2 += 4;
} else {
dst.addRectCashed(x11, top, x12, bottom);
i1 += 4;
}
} else {
i2 += 4;
}
}
}
d1.deleteActive(bottom);
d2.deleteActive(bottom);
}
}
static int[] simpleIntersect(MultiRectArea src1, MultiRectArea src2) {
int[] rect1 = src1.rect;
int[] rect2 = src2.rect;
int[] rect = createBuf(0);
int k = 1;
for(int i = 1; i < rect1[0];) {
int x11 = rect1[i++];
int y11 = rect1[i++];
int x12 = rect1[i++];
int y12 = rect1[i++];
for(int j = 1; j < rect2[0];) {
int x21 = rect2[j++];
int y21 = rect2[j++];
int x22 = rect2[j++];
int y22 = rect2[j++];
if (x11 <= x22 && x12 >= x21 &&
y11 <= y22 && y12 >= y21)
{
rect = checkBufSize(rect, 4);
rect[k++] = x11 > x21 ? x11 : x21;
rect[k++] = y11 > y21 ? y11 : y21;
rect[k++] = x12 > x22 ? x22 : x12;
rect[k++] = y12 > y22 ? y22 : y12;
}
}
}
rect[0] = k;
return rect;
}
public static MultiRectArea getResult(MultiRectArea src1, MultiRectArea src2) {
if (src1 == null || src2 == null || src1.isEmpty() || src2.isEmpty()) {
return new MultiRectArea();
}
MultiRectArea.RectCash dst = new MultiRectArea.RectCash();
if (!src1.sorted || !src2.sorted ||
src1.getRectCount() <= MAX_SIMPLE || src2.getRectCount() <= MAX_SIMPLE)
{
dst.setRect(simpleIntersect(src1, src2), false);
} else {
Rectangle bounds1 = src1.getBounds();
Rectangle bounds2 = src2.getBounds();
Rectangle bounds3 = bounds1.intersection(bounds2);
if (bounds3.width > 0 && bounds3.height > 0) {
intersectRegions(src1.rect, src2.rect, dst, bounds1.height + 2, bounds2.height + 2);
}
}
return dst;
}
}
/**
* Union class provides union of two MultiRectAre aobjects
*/
static class Union {
int rx1, rx2;
int top, bottom;
MultiRectArea.RectCash dst;
boolean next(Region d, int index) {
int x1 = d.active[index];
int x2 = d.active[index + 2];
boolean res = false;
if (x2 < rx1 - 1) {
res = true;
dst.addRectCashed(x1, top, x2, bottom);
} else
if (x1 > rx2 + 1) {
res = false;
dst.addRectCashed(rx1, top, rx2, bottom);
rx1 = x1;
rx2 = x2;
} else {
res = x2 <= rx2;
rx1 = Math.min(x1, rx1);
rx2 = Math.max(x2, rx2);
}
// Top
if (d.active[index + 1] < top) {
dst.addRectCashed(x1, d.active[index + 1], x2, top - 1);
}
// Bottom
if (d.active[index + 3] > bottom) {
d.active[index + 1] = bottom + 1;
}
return res;
}
void check(Region d, int index, boolean t) {
int x1 = d.active[index];
int x2 = d.active[index + 2];
// Top
if (d.active[index + 1] < top) {
dst.addRectCashed(x1, d.active[index + 1], x2, top - 1);
}
if (t) {
dst.addRectCashed(x1, top, x2, bottom);
}
// Bottom
if (d.active[index + 3] > bottom) {
d.active[index + 1] = bottom + 1;
}
}
void unionRegions(int[] reg1, int[] reg2, int height1, int height2) {
Region d1 = new Region(reg1);
Region d2 = new Region(reg2);
int[] level = new int[height1 + height2];
int[] level1 = new int[height1];
int[] level2 = new int[height2];
d1.createLevel(level1);
d2.createLevel(level2);
Region.sortOrdered(level1, level2, level);
bottom = level[1] - 1;
for(int i = 2; i < level[0]; i++) {
top = bottom + 1;
bottom = level[i] - 1;
d1.findActive(top, bottom);
d2.findActive(top, bottom);
int i1 = 1;
int i2 = 1;
boolean res1, res2;
if (d1.active[0] > 1) {
check(d1, 1, false);
rx1 = d1.active[1];
rx2 = d1.active[3];
i1 += 4;
res1 = false;
res2 = true;
} else
if (d2.active[0] > 1) {
check(d2, 1, false);
rx1 = d2.active[1];
rx2 = d2.active[3];
i2 += 4;
res1 = true;
res2 = false;
} else {
continue;
}
outer:
while(true) {
while (res1) {
if (i1 >= d1.active[0]) {
dst.addRectCashed(rx1, top, rx2, bottom);
while(i2 < d2.active[0]) {
check(d2, i2, true);
i2 += 4;
}
break outer;
}
res1 = next(d1, i1);
i1 += 4;
}
while (res2) {
if (i2 >= d2.active[0]) {
dst.addRectCashed(rx1, top, rx2, bottom);
while(i1 < d1.active[0]) {
check(d1, i1, true);
i1 += 4;
}
break outer;
}
res2 = next(d2, i2);
i2 += 4;
}
res1 = true;
res2 = true;
} // while
d1.deleteActive(bottom);
d2.deleteActive(bottom);
}
}
static void simpleUnion(MultiRectArea src1, MultiRectArea src2, MultiRectArea dst) {
if (src1.getRectCount() < src2.getRectCount()) {
simpleUnion(src2, src1, dst);
} else {
Subtraction.simpleSubtract(src1, src2, dst);
int pos = dst.rect[0];
int size = src2.rect[0] - 1;
dst.rect = checkBufSize(dst.rect, size);
System.arraycopy(src2.rect,1, dst.rect, pos, size);
dst.resort();
}
}
MultiRectArea getResult(MultiRectArea src1, MultiRectArea src2) {
if (src1 == null || src1.isEmpty()) {
return new MultiRectArea(src2);
}
if (src2 == null || src2.isEmpty()) {
return new MultiRectArea(src1);
}
dst = new MultiRectArea.RectCash();
if (!src1.sorted || !src2.sorted ||
src1.getRectCount() <= MAX_SIMPLE || src2.getRectCount() <= MAX_SIMPLE)
{
simpleUnion(src1, src2, dst);
} else {
Rectangle bounds1 = src1.getBounds();
Rectangle bounds2 = src2.getBounds();
Rectangle bounds3 = bounds1.intersection(bounds2);
if (bounds3.width < 0 || bounds3.height < 0) {
if (bounds1.y + bounds1.height < bounds2.y) {
dst.setRect(addVerRegion(src1.rect, src2.rect), false);
} else
if (bounds2.y + bounds2.height < bounds1.y) {
dst.setRect(addVerRegion(src2.rect, src1.rect), false);
} else
if (bounds1.x < bounds2.x) {
dst.setRect(addHorRegion(src1.rect, src2.rect), false);
} else {
dst.setRect(addHorRegion(src2.rect, src1.rect), false);
}
} else {
unionRegions(src1.rect, src2.rect, bounds1.height + 2, bounds2.height + 2);
}
}
return dst;
}
int[] addVerRegion(int[] top, int[] bottom) {
int length = top[0] + bottom[0] - 1;
int[] dst = new int[length];
dst[0] = length;
System.arraycopy(top, 1, dst, 1, top[0] - 1);
System.arraycopy(bottom, 1, dst, top[0], bottom[0] - 1);
return dst;
}
int[] addHorRegion(int[] left, int[] right) {
int count1 = left[0];
int count2 = right[0];
int[] dst = new int[count1 + count2 + 1];
int count = 1;
int index1 = 1;
int index2 = 1;
int top1 = left[2];
int top2 = right[2];
int pos1, pos2;
while(true) {
if (index1 >= count1) {
System.arraycopy(right, index2, dst, count, count2 - index2);
count += count2 - index2;
break;
}
if (index2 >= count2) {
System.arraycopy(left, index1, dst, count, count1 - index1);
count += count1 - index1;
break;
}
if (top1 < top2) {
pos1 = index1;
do {
index1 += 4;
} while (index1 < count1 && (top1 = left[index1 + 1]) < top2);
System.arraycopy(left, pos1, dst, count, index1 - pos1);
count += index1 - pos1;
continue;
}
if (top1 > top2) {
pos2 = index2;
do {
index2 += 4;
} while (index2 < count2 && (top2 = right[index2 + 1]) < top1);
System.arraycopy(right, pos2, dst, count, index2 - pos2);
count += index2 - pos2;
continue;
}
int top = top1;
pos1 = index1;
pos2 = index2;
do {
index1 += 4;
} while(index1 < count1 && (top1 = left[index1 + 1]) == top);
do {
index2 += 4;
} while(index2 < count2 && (top2 = right[index2 + 1]) == top);
System.arraycopy(left, pos1, dst, count, index1 - pos1);
count += index1 - pos1;
System.arraycopy(right, pos2, dst, count, index2 - pos2);
count += index2 - pos2;
}
dst[0] = count;
return dst;
}
}
/**
* Subtraction class provides subtraction of two MultiRectAre aobjects
*/
static class Subtraction {
static void subtractRegions(int[] reg1, int[] reg2, MultiRectArea.RectCash dst, int height1, int height2) {
Region d1 = new Region(reg1);
Region d2 = new Region(reg2);
int[] level = new int[height1 + height2];
int[] level1 = new int[height1];
int[] level2 = new int[height2];
d1.createLevel(level1);
d2.createLevel(level2);
Region.sortOrdered(level1, level2, level);
int top;
int bottom = level[1] - 1;
for(int i = 2; i < level[0]; i++) {
top = bottom + 1;
bottom = level[i] - 1;
d1.findActive(top, bottom);
if (d1.active[0] == 1) {
d2.deleteActive(bottom);
continue;
}
d2.findActive(top, bottom);
int i1 = 1;
int i2 = 1;
int rx1 = 0;
int rx2 = 0;
boolean next = true;
while(true) {
if (next) {
next = false;
if (i1 >= d1.active[0]) {
break;
}
// Bottom
d1.active[i1 + 1] = bottom + 1;
rx1 = d1.active[i1];
rx2 = d1.active[i1 + 2];
i1 += 4;
}
if (i2 >= d2.active[0]) {
dst.addRectCashed(rx1, top, rx2, bottom);
for(int j = i1; j < d1.active[0]; j += 4) {
dst.addRectCashed(d1.active[j], top, d1.active[j + 2], bottom);
d1.active[j + 1] = bottom + 1;
}
break;
}
int x1 = d2.active[i2];
int x2 = d2.active[i2 + 2];
if (rx1 < x1) {
if (rx2 >= x1) {
if (rx2 <= x2) {
// [-----------]
// [-------------]
dst.addRectCashed(rx1, top, x1 - 1, bottom);
next = true;
} else {
// [-----------------]
// [------]
dst.addRectCashed(rx1, top, x1 - 1, bottom);
rx1 = x2 + 1;
i2 += 4;
}
} else {
// [-----]
// [----]
dst.addRectCashed(rx1, top, rx2, bottom);
next = true;
}
} else {
if (rx1 <= x2) {
if (rx2 <= x2) {
// [------]
// [-----------]
next = true;
} else {
// [------------]
// [---------]
rx1 = x2 + 1;
i2 += 4;
}
} else {
// [----]
// [-----]
i2 += 4;
}
}
}
d1.deleteActive();
d2.deleteActive(bottom);
}
}
static void subtractRect(int x11, int y11, int x12, int y12, int[] rect, int index, MultiRectArea dst) {
for(int i = index; i < rect[0]; i += 4) {
int x21 = rect[i + 0];
int y21 = rect[i + 1];
int x22 = rect[i + 2];
int y22 = rect[i + 3];
if (x11 <= x22 && x12 >= x21 && y11 <= y22 && y12 >= y21) {
int top, bottom;
if (y11 < y21) {
subtractRect(x11, y11, x12, y21 - 1, rect, i + 4, dst);
top = y21;
} else {
top = y11;
}
if (y12 > y22) {
subtractRect(x11, y22 + 1, x12, y12, rect, i + 4, dst);
bottom = y22;
} else {
bottom = y12;
}
if (x11 < x21) {
subtractRect(x11, top, x21 - 1, bottom, rect, i + 4, dst);
}
if (x12 > x22) {
subtractRect(x22 + 1, top, x12, bottom, rect, i + 4, dst);
}
return;
}
}
dst.addRect(x11, y11, x12, y12);
}
static void simpleSubtract(MultiRectArea src1, MultiRectArea src2, MultiRectArea dst) {
for(int i = 1; i < src1.rect[0]; i += 4) {
subtractRect(
src1.rect[i + 0],
src1.rect[i + 1],
src1.rect[i + 2],
src1.rect[i + 3],
src2.rect,
1,
dst);
}
dst.resort();
}
public static MultiRectArea getResult(MultiRectArea src1, MultiRectArea src2) {
if (src1 == null || src1.isEmpty()) {
return new MultiRectArea();
}
if (src2 == null || src2.isEmpty()) {
return new MultiRectArea(src1);
}
MultiRectArea.RectCash dst = new MultiRectArea.RectCash();
if (!src1.sorted || !src2.sorted ||
src1.getRectCount() <= MAX_SIMPLE || src2.getRectCount() <= MAX_SIMPLE)
{
simpleSubtract(src1, src2, dst);
} else {
Rectangle bounds1 = src1.getBounds();
Rectangle bounds2 = src2.getBounds();
Rectangle bounds3 = bounds1.intersection(bounds2);
if (bounds3.width > 0 && bounds3.height > 0) {
subtractRegions(src1.rect, src2.rect, dst, bounds1.height + 2, bounds2.height + 2);
} else {
dst.setRect(src1.rect, true);
}
}
return dst;
}
}
}