/*
* 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.powerbrush;
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.util.BitSet;
import java.util.Random;
import com.kreative.paint.util.Bitmap;
public class BrushSettings {
public static final BrushShape[] SHAPES = new BrushShape[] {
RoundBrushShape.instance,
SquareBrushShape.instance,
RoundRectBrushShape.instance,
DiamondBrushShape.instance,
YNXDiagonalBrushShape.instance,
YXDiagonalBrushShape.instance,
XBrushShape.instance,
VerticalBrushShape.instance,
HorizontalBrushShape.instance,
};
private BrushShape shape;
private float outerWidth;
private float outerHeight;
private float innerWidth;
private float innerHeight;
private int flowRate;
public BrushSettings() {
this.shape = RoundBrushShape.instance;
this.outerWidth = 16.0f;
this.outerHeight = 16.0f;
this.innerWidth = 16.0f;
this.innerHeight = 16.0f;
this.flowRate = 20;
setCache();
}
public BrushSettings(BrushShape shape, float outerWidth, float outerHeight, float innerWidth, float innerHeight, int flowRate) {
this.shape = shape;
this.outerWidth = outerWidth;
this.outerHeight = outerHeight;
this.innerWidth = innerWidth;
this.innerHeight = innerHeight;
this.flowRate = flowRate;
setCache();
}
private float m;
private int mx;
private Shape br;
private Shape[] brs;
private int bmw, bmh;
private BitSet bmi;
private Random bmr;
private void setCache() {
float minw = Math.min(innerWidth, outerWidth);
float minh = Math.min(innerHeight, outerHeight);
float maxw = Math.max(innerWidth, outerWidth);
float maxh = Math.max(innerHeight, outerHeight);
m = Math.max(maxw-minw, maxh-minh);
mx = (int)Math.ceil(m);
br = shape.makeBrush(0, 0, maxw, maxh);
brs = new Shape[mx+1];
for (int i = 0; i <= mx; i++) {
float d = (float)i/(float)mx;
float w = maxw + (minw-maxw)*d;
float h = maxh + (minh-maxh)*d;
brs[i] = shape.makeBrush(0, 0, w, h);
}
bmw = (int)Math.ceil(maxw/2) * 2;
bmh = (int)Math.ceil(maxh/2) * 2;
bmi = new BitSet();
Shape bms = shape.makeBrush(bmw/2, bmh/2, maxw, maxh);
for (int by = 0; by < bmh; by++) {
for (int bx = 0; bx < bmw; bx++) {
if (bms.intersects(bx, by, 1, 1)) {
bmi.set(by*bmw + bx);
}
}
}
bmr = new Random();
}
public BrushShape getBrushShape() {
return shape;
}
public float getOuterWidth() {
return outerWidth;
}
public float getOuterHeight() {
return outerHeight;
}
public float getInnerWidth() {
return innerWidth;
}
public float getInnerHeight() {
return innerHeight;
}
public int getFlowRate() {
return flowRate;
}
public void paint(Graphics2D g, float x, float y) {
if (br == null || brs == null) setCache();
boolean single = m < 1.0 || !(g.getComposite() instanceof AlphaComposite);
AffineTransform tx = AffineTransform.getTranslateInstance(x, y);
if (single) {
g.fill(tx.createTransformedShape(br));
} else {
AlphaComposite c = (AlphaComposite)g.getComposite();
g.setComposite(AlphaComposite.getInstance(c.getRule(), c.getAlpha()/mx));
for (int i = 0; i <= mx; i++) {
g.fill(tx.createTransformedShape(brs[i]));
}
g.setComposite(c);
}
}
public void paint(Graphics2D g, boolean includeStart, float sx, float sy, float dx, float dy) {
if (br == null || brs == null) setCache();
boolean single = m < 1.0 || !(g.getComposite() instanceof AlphaComposite);
AffineTransform tx = AffineTransform.getTranslateInstance(sx, sy);
if (includeStart) {
if (single) {
g.fill(tx.createTransformedShape(br));
} else {
AlphaComposite c = (AlphaComposite)g.getComposite();
g.setComposite(AlphaComposite.getInstance(c.getRule(), c.getAlpha()/mx));
for (int i = 0; i <= mx; i++) {
g.fill(tx.createTransformedShape(brs[i]));
}
g.setComposite(c);
}
}
int n = (int)Math.ceil(Math.max(Math.abs(dx-sx),Math.abs(dy-sy)));
for (int j = 1; j <= n; j++) {
float x = sx + ((dx-sx)*j)/n;
float y = sy + ((dy-sy)*j)/n;
tx.setToTranslation(x, y);
if (single) {
g.fill(tx.createTransformedShape(br));
} else {
AlphaComposite c = (AlphaComposite)g.getComposite();
g.setComposite(AlphaComposite.getInstance(c.getRule(), c.getAlpha()/mx));
for (int i = 0; i <= mx; i++) {
g.fill(tx.createTransformedShape(brs[i]));
}
g.setComposite(c);
}
}
}
public void spray(Graphics2D g, float x, float y) {
int[] rgb = new int[bmw*bmh];
for (int i = 0, k = flowRate; k >= 0; k--) {
i = bmr.nextInt(rgb.length); if (bmi.get(i)) rgb[i] = 0xFF000000;
i = bmr.nextInt(rgb.length); if (bmi.get(i)) rgb[i] = 0xFF000000;
i = bmr.nextInt(rgb.length); if (bmi.get(i)) rgb[i] = 0xFF000000;
i = bmr.nextInt(rgb.length); if (bmi.get(i)) rgb[i] = 0xFF000000;
}
new Bitmap(bmw, bmh, rgb).paint(g, (int)x-bmw/2, (int)y-bmh/2);
}
}