/* * Copyright © 2009-2011 Rebecca G. Bettencourt / Kreative Software * <p> * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * <a href="http://www.mozilla.org/MPL/">http://www.mozilla.org/MPL/</a> * <p> * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * <p> * Alternatively, the contents of this file may be used under the terms * of the GNU Lesser General Public License (the "LGPL License"), in which * case the provisions of LGPL License are applicable instead of those * above. If you wish to allow use of your version of this file only * under the terms of the LGPL License and not to allow others to use * your version of this file under the MPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the LGPL License. If you do not delete * the provisions above, a recipient may use your version of this file * under either the MPL or the LGPL License. * @since PowerPaint 1.0 * @author Rebecca G. Bettencourt, Kreative Software */ package com.kreative.paint.pict; import java.awt.*; import java.awt.geom.*; import java.awt.image.*; import java.io.*; import java.util.*; import java.util.List; public class Region implements Shape { public int rgnSize = 10; public Rect rgnBBox = new Rect(); public List<Short> rgnData = new Vector<Short>(); public static Region read(DataInputStream in) throws IOException { Region r = new Region(); r.rgnSize = in.readUnsignedShort(); r.rgnBBox = Rect.read(in); r.rgnData = new Vector<Short>(); for (int i = 10; i < r.rgnSize; i += 2) { r.rgnData.add(in.readShort()); } return r; } public Region() { this.rgnSize = 10; this.rgnBBox = new Rect(); this.rgnData = new Vector<Short>(); } public Region(int x, int y, int width, int height) { this.rgnSize = 10; this.rgnBBox = new Rect(x, y, width, height); this.rgnData = new Vector<Short>(); } public void write(DataOutputStream out) throws IOException { if (rgnData.isEmpty()) { out.writeShort(rgnSize = 10); rgnBBox.write(out); } else { int minx = Integer.MAX_VALUE, maxx = Integer.MIN_VALUE; int miny = Integer.MAX_VALUE, maxy = Integer.MIN_VALUE; Iterator<Short> i = rgnData.iterator(); while (i.hasNext()) { int row = i.next(); if (row == 0x7FFF) break; else { if (row < miny) miny = row; if (row > maxy) maxy = row; while (i.hasNext()) { int col = i.next(); if (col == 0x7FFF) break; else { if (col < minx) minx = col; if (col > maxx) maxx = col; } } } } if (minx < maxx && miny < maxy) { out.writeShort(rgnSize = 10+rgnData.size()*2); (rgnBBox = new Rect(minx, miny, maxx-minx, maxy-miny)).write(out); for (Short s : rgnData) { out.writeShort(s); } } else { out.writeShort(rgnSize = 10); (rgnBBox = new Rect(rgnBBox.left, rgnBBox.top, 0, 0)).write(out); rgnData.clear(); } } } public static Region fromAlpha(int x, int y, int width, int height, int[] rgb, int offset, int scanwidth) { byte[] tmp = new byte[(width+1)*(height+1)]; for (int ly = 0, py = offset, ty = 0; ly < height; ly++, py += scanwidth, ty += width+1) { for (int lx = 0, px = py, tx = ty; lx < width; lx++, px++, tx++) { if (rgb[px] < 0) { tmp[tx] ^= 1; tmp[tx+1] ^= 1; tmp[tx+width+1] ^= 1; tmp[tx+width+2] ^= 1; } } } return fromBits(x, y, width, height, tmp); } public static Region fromRGB(int x, int y, int width, int height, int[] rgb, int offset, int scanwidth) { byte[] tmp = new byte[(width+1)*(height+1)]; for (int ly = 0, py = offset, ty = 0; ly < height; ly++, py += scanwidth, ty += width+1) { for (int lx = 0, px = py, tx = ty; lx < width; lx++, px++, tx++) { if (rgb[px] < 0) { int r = (rgb[px] >>> 16) & 0xFF; int g = (rgb[px] >>> 8) & 0xFF; int b = rgb[px] & 0xFF; int k = (30*r + 59*g + 11*b) / 100; if (k < 0x80) { tmp[tx] ^= 1; tmp[tx+1] ^= 1; tmp[tx+width+1] ^= 1; tmp[tx+width+2] ^= 1; } } } } return fromBits(x, y, width, height, tmp); } public static Region fromShape(Shape s) { Rectangle r = s.getBounds(); byte[] tmp = new byte[(r.width+1)*(r.height+1)]; for (int ly = 0, sy = r.y, ty = 0; ly < r.height; ly++, sy++, ty += r.width+1) { for (int lx = 0, sx = r.x, tx = ty; lx < r.width; lx++, sx++, tx++) { if (s.contains(sx, sy)) { tmp[tx] ^= 1; tmp[tx+1] ^= 1; tmp[tx+r.width+1] ^= 1; tmp[tx+r.width+2] ^= 1; } } } return fromBits(r.x, r.y, r.width, r.height, tmp); } public static Region fromImage(int x, int y, Image i, ImageObserver obs) { int w = i.getWidth(obs); int h = i.getHeight(obs); if (w < 0 || h < 0) return null; BufferedImage bimg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Graphics2D bg = bimg.createGraphics(); if (!bg.drawImage(i, 0, 0, obs)) return null; bg.dispose(); int[] rgb = new int[w*h]; bimg.getRGB(0, 0, w, h, rgb, 0, w); return fromRGB(x, y, w, h, rgb, 0, w); } private static Region fromBits(int x, int y, int width, int height, byte[] tmp) { int minx = x+width; int miny = y+height; int maxx = x; int maxy = y; List<Short> data = new Vector<Short>(); for (int ly = 0, ty = 0; ly < height+1; ly++, ty += width+1) { boolean wroteRow = false; for (int lx = 0, tx = ty; lx < width+1; lx++, tx++) { if (tmp[tx] != 0) { if (!wroteRow) { data.add((short)(y+ly)); wroteRow = true; } data.add((short)(x+lx)); if (x+lx < minx) minx = x+lx; if (x+lx > maxx) maxx = x+lx; } } if (wroteRow) { data.add((short)0x7FFF); if (y+ly < miny) miny = y+ly; if (y+ly > maxy) maxy = y+ly; } } data.add((short)0x7FFF); if (maxx < minx || maxy < miny) { minx = maxx = x; miny = maxy = y; data.clear(); } Region r = new Region(); r.rgnSize = 10+data.size()*2; r.rgnBBox = new Rect(minx, miny, maxx-minx, maxy-miny); r.rgnData = data; return r; } public Area toArea() { if (rgnData.isEmpty()) { return new Area(new Rectangle(rgnBBox.left, rgnBBox.top, rgnBBox.right-rgnBBox.left, rgnBBox.bottom-rgnBBox.top)); } else { Area a = new Area(); Iterator<Short> i = rgnData.iterator(); while (i.hasNext()) { int row = i.next(); if (row == 0x7FFF) break; else { while (i.hasNext()) { int col = i.next(); if (col == 0x7FFF) break; else { a.exclusiveOr(new Area(new Rectangle(col, row, rgnBBox.right-col, rgnBBox.bottom-row))); } } } } return a; } } public BufferedImage toBufferedImage() { int w = rgnBBox.right-rgnBBox.left; int h = rgnBBox.bottom-rgnBBox.top; BufferedImage bimg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Graphics2D bg = bimg.createGraphics(); Shape s = AffineTransform.getTranslateInstance(-rgnBBox.left, -rgnBBox.top).createTransformedShape(toArea()); bg.setPaint(Color.black); bg.fill(s); bg.dispose(); return bimg; } public void toRGB(int x, int y, int width, int height, int[] rgb, int offset, int scanwidth) { BufferedImage bimg = toBufferedImage(); bimg.getRGB(x-rgnBBox.left, y-rgnBBox.top, width, height, rgb, offset, scanwidth); } public String toString() { return "Region[" + rgnSize + "," + rgnBBox.toString() + "]"; } public boolean contains(Point2D p) { return toArea().contains(p); } public boolean contains(Rectangle2D r) { return toArea().contains(r); } public boolean contains(double x, double y) { return toArea().contains(x,y); } public boolean contains(double x, double y, double w, double h) { return toArea().contains(x,y,w,h); } public Rectangle getBounds() { return toArea().getBounds(); } public Rectangle2D getBounds2D() { return toArea().getBounds2D(); } public PathIterator getPathIterator(AffineTransform at) { return toArea().getPathIterator(at); } public PathIterator getPathIterator(AffineTransform at, double flatness) { return toArea().getPathIterator(at, flatness); } public boolean intersects(Rectangle2D r) { return toArea().intersects(r); } public boolean intersects(double x, double y, double w, double h) { return toArea().intersects(x,y,w,h); } }