// SSI - Simple Scanline Image // A partial image consisting of exactly one horizontal line per row. // All pixels are of the same color. // The important values are y1 and data persistable sclass SSI extends AbstractSSI is ByteIO, StringIO { replace X with short. replace Y with short. replace toX with toShort_enforce. replace toY with toShort_enforce. replace newXArray with newShortArrayOrNull. // core value (mandatory): first row occupied settable Y y1; // core value (mandatory): x coordinates per row // x_left is inclusive, x_right is exclusive // all x coordinates are absolute in image // // Order in array: // x_left at y1, x_right at y1, // x_left at y1+1, x_right at y1+1, ,,, settable X[] data; *(int y1, int y2) { init(y1, y2); } void init(int y1, int y2) { this.y1 = toY(y1); data = newXArray((y2-y1)*2); } // all coordinates passed in are absolute void set(int y, int xLeft, int xRight) { data[(y-y1)*2] = toX(xLeft); data[(y-y1)*2+1] = toX(xRight); } // all coordinates passed in and out are absolute int getLeft (int y) { ret data[(y-y1)*2]; } int getRight(int y) { ret data[(y-y1)*2+1]; } // bounds can be calculated quickly from core values & are then cached public simplyCached Rect bounds() { int x1 = Int.MAX_VALUE, x2 = 0; for (int i = 0; i < l(data); i += 2) { x1 = min(x1, data[i]); x2 = max(x2, data[i+1]); } ret rectFromPoints(x1, y1, x2, y2()); } // same with number of pixels int numberOfPixels_cache = -1; public int numberOfPixels() { if (numberOfPixels_cache < 0) { int n = 0; for (int i = 0; i < l(data); i += 2) n += max(0, data[i+1]-data[i]); numberOfPixels_cache = n; } ret numberOfPixels_cache; } public int getHeight() { ret l(data)/2; } int y2() { ret y1+height(); } public bool contains(Pt p) { ret contains(p.x, p.y); } public bool contains(int x, int y) { if (y < y1) false; int i = (y-y1)*2; if (i >= l(data)) false; ret x >= data[i] && x < data[i+1]; } selfType color(Color color) { super.color(color); this; } public selfType hi15color(short hi15color) { super.hi15color(hi15color); this; } public void drawOn(Graphics2D g) { g.setColor(color()); int h = height(); for y to h: { int x1 = data[y*2], x2 = data[y*2+1]; if (x1 < x2) g.drawLine(x1, y1+y, x2-1, y1+y); } } // doesn't set a color // TODO public void drawOutline(Graphics2D g) { int h = height(); IntRange last = intRange(0, 0); for y to h: { IntRange r = intRange(data[y*2], data[y*2+1]); // anything to draw in this line? if (nempty(r)) { if (empty(last)) // draw full line if just starting g.drawLine(r.start, y1+y, r.end-1, y1+y); else { // We make a line from the left boundary that if possible goes only to last.start but is at least 1 pixel wide int xleft = max(r.start, last.start-1); // Same from the right boundary int xright = min(r.end-1, last.end); g.drawLine(r.start, y1+y, xleft, y1+y); g.drawLine(xright, y1+y, r.end-1, y1+y); } } last = r; } } SSI topPart(int nLines) { if (nLines <= 0) null; SSI ssi = new SSI(y1, toShort_enforce(y1+nLines)).hi15color(hi15color); arrayCopy(data, ssi.data, 0, l(ssi.data)); ret ssi; } // an SSI is coherent if all scanlines are non-empty // and the SSI is one region bool coherent(bool withDiagonals default false) { int h = height(); for y to h: { int i = y*2; int x1 = data[i], x2 = data[i+1]; if (x1 >= x2) false; if (y > 0) { int lastx1 = data[i-2], lastx2 = data[i-1]; if (withDiagonals) { lastx1--; lastx2++; } if (!intRangesOverlap(x1, x2, lastx1, lastx2)) false; } } true; } public void readWrite(ByteHead head) { head.exchangeShort(-> hi15color, color -> hi15color = color); head.exchangeShort(-> y1, y1 -> this.y1 = y1); head.exchangeShort(-> toShort_enforce(y2()), y2 -> init(y1, y2)); for i over data: { reMutable i; head.exchangeShort(-> data[i], x -> data[i] = x); } } SSI toSSI() { this; } public long sizeInInts() { // "s", color, y1, height, data ret 4+height()*2; } SSI reduceToInts(int ints) { ret topPart((ints-4)/2); } public void readWrite(StringHead head) { head.exchangeLine(l0 toLine, l1 fromLine); } S toLine() { ret spaceCombine("s", colorToString(), y1, height(), toList(data)); } void fromLine(S line) { Iterator it = splitAtSpaceIterator(line); assertEquals("s", nextFromIterator(it)); int y1 = parseInt(nextFromIterator(it)); int h = parseInt(nextFromIterator(it)); init(y1, y1+h); for i over data: data[i] = toX(parseInt(nextFromIterator(it))); } }