import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import java.awt.image.*; import java.util.*; import javaforce.*; import com.jhlabs.image.*; public class PaintCanvas extends JComponent implements MouseListener, MouseMotionListener, KeyListener { public static MainPanel mainPanel; public int mx=0, my=0; //current mouse pos public int sx=0, sy=0; //starting draw pos public boolean drag = false; //a draggable selection/text box is visible public JFImage img[], bimg, fimg; //the "real" image / background / foreground images public JFImage limg; //alpha, color layers public JFImage cimg; //current color layer public boolean dirty = false, undoDirty = false; public ArrayList<Undo> undos = new ArrayList<Undo>(); public int undoPos = 0; public static int MAX_UNDO_SIZE = 7; public Border border_east, border_south, border_corner; public JPanel parentPanel; public JScrollPane scroll; public int button = -1; public boolean textCursor; public int selBoxIdx = 0; //dashed line public float scale = 100; public boolean disableScale = false; //for file operations public boolean show[]; public String name[]; private int colorLayer = 0; private int imageLayer = 0; private int imageLayers = 1; private void init() { addMouseListener(this); addMouseMotionListener(this); addKeyListener(this); setFocusable(true); } public PaintCanvas(JPanel parent, JScrollPane scroll) { init(); parentPanel = parent; this.scroll = scroll; img = new JFImage[1]; img[imageLayer] = new JFImage(); show = new boolean[1]; show[imageLayer] = true; name = new String[1]; name[imageLayer] = "Background"; bimg = new JFImage(); fimg = new JFImage(); limg = new JFImage(); cimg = img[imageLayer]; setName("image"); } public void setImageSize(int x,int y) { if (x <= 0) x = 1; if (y <= 0) y = 1; for(int a=0;a<imageLayers;a++) { img[a].setImageSize(x, y); } limg.setImageSize(x, y); bimg.setImageSize(x, y); fimg.setImageSize(x, y); setSize(x,y); setPreferredSize(new Dimension(x,y)); backClear(); foreClear(); } public void paint(Graphics g) { if (img == null) return; if (img[imageLayer].getImage() == null) return; // JFLog.log("PaintCanvas:paint():layers=" + imageLayers); // JFLog.log("\nscale=" + scale); Rectangle clip = g.getClipBounds(); // JFLog.log("clipA=" + clip); //align clip to max scaled pixel size int t; t = clip.x & 0x7; if (t > 0) { clip.x &= 0xfffffff8; clip.width += t; } t = clip.width & 0x7; if (t > 0) { clip.width &= 0xfffffff8; clip.width += 8; } t = clip.y & 0x7; if (t > 0) { clip.y &= 0xfffffff8; clip.height += t; } t = clip.height & 0x7; if (t > 0) { clip.height &= 0xfffffff8; clip.height += 8; } // JFLog.log("clipB=" + clip); int dx1 = clip.x; int dy1 = clip.y; int dx2 = dx1 + clip.width; int dy2 = dy1 + clip.height; int sx1 = clip.x; int sy1 = clip.y; int sx2 = sx1 + clip.width; int sy2 = sy1 + clip.height; float div = 100f / scale; sx1 *= div; sy1 *= div; sx2 *= div; sy2 *= div; // JFLog.log("src=" + sx1 + "," + sy1 + "," + sx2 + "," + sy2); // JFLog.log("dst=" + dx1 + "," + dy1 + "," + dx2 + "," + dy2); if (colorLayer == 0) { g.drawImage(bimg.getImage() , dx1, dy1, dx2, dy2 , sx1, sy1, sx2, sy2 , null); for(int a=0;a<imageLayers;a++) { // JFLog.log("layer=" + name[a]); if (a == imageLayer) { g.drawImage(cimg.getImage() , dx1, dy1, dx2, dy2 , sx1, sy1, sx2, sy2 , null); } else { if (show[a]) g.drawImage(img[a].getImage() , dx1, dy1, dx2, dy2 , sx1, sy1, sx2, sy2 , null); } } } else { g.drawImage(cimg.getImage() , dx1, dy1, dx2, dy2 , sx1, sy1, sx2, sy2 , null); } g.drawImage(fimg.getImage() , dx1, dy1, dx2, dy2 , sx1, sy1, sx2, sy2 , null); } public void setSize(int x, int y) { // System.out.println("PaintCanvas.setSize:" + x + "," + y + ":" + getName()); super.setSize(x,y); //calls setBounds() } public void setSize(Dimension d) { // System.out.println("PaintCanvas.setSize:" + d + ":" + getName()); super.setSize(d); //calls setBounds() } public void setBounds(int x,int y,int w,int h) { // System.out.println("PaintCanvas.setBounds:" + x + "," + y + "," + w + "," + h + ":" + getName()); super.setBounds(x,y,w,h); } public void createBorders() { border_east = new Border(this, Border.Types.east) { public void paint(Graphics g) { int x = getScaledWidth(); int y = getScaledHeight(); g.setColor(new Color(0x888888)); g.fillRect(0,0, 10,y); g.setColor(new Color(0x000000)); g.fillRect(0,y/2-5, 10,10); } }; border_south = new Border(this, Border.Types.south) { public void paint(Graphics g) { int x = getScaledWidth(); int y = getScaledHeight(); g.setColor(new Color(0x888888)); g.fillRect(0,0, x,10); g.setColor(new Color(0x000000)); g.fillRect(x/2-5,0, 10,10); } }; border_corner = new Border(this, Border.Types.corner) { public void paint(Graphics g) { g.setColor(new Color(0x000000)); g.fillRect(0,0, 10,10); } }; } public void resizeBorder() { // System.out.println("doLayout"); parentPanel.doLayout(); } public Dimension getPreferredSize() { Dimension ps = super.getPreferredSize(); // System.out.println("PaintCanvas.getPreferredSize()" + ps); return ps; } public Dimension getMinimumSize() { // System.out.println("PaintCanvas.getMinimumSize()" + getPreferredSize()); return getPreferredSize(); } public Dimension getMaximumSize() { // System.out.println("PaintCanvas.getMaximumSize()" + getPreferredSize()); return getPreferredSize(); } public void setScale(float newScale) { // System.out.println(" setScale:" + newScale); int x = (int)(img[imageLayer].getWidth() * newScale / 100f); int y = (int)(img[imageLayer].getHeight() * newScale / 100f); setPreferredSize(new Dimension(x,y)); scale = newScale; } public int getWidth() { // System.out.println("getWidth=" + super.getWidth()); if (disableScale) return getUnscaledWidth(); return getScaledWidth(); } public int getHeight() { // System.out.println("getHeight=" + super.getHeight()); if (disableScale) return getUnscaledHeight(); return getScaledHeight(); } public int getScaledWidth() { // System.out.println("getScaledWidth=" + super.getWidth()); return (int)(img[imageLayer].getWidth() * scale / 100f); } public int getScaledHeight() { // System.out.println("getScaledHeight=" + super.getHeight()); return (int)(img[imageLayer].getHeight() * scale / 100f); } public int getUnscaledWidth() { return img[imageLayer].getWidth(); } public int getUnscaledHeight() { return img[imageLayer].getHeight(); } public void setLineWidth(int w) { cimg.getGraphics2D().setStroke(new BasicStroke(w, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); } public void drawLine(int x1,int y1,int x2,int y2) { cimg.getGraphics().drawLine(x1,y1,x2,y2); } public void drawBox(int x1,int y1,int x2,int y2) { int tmp; if (x1 > x2) {tmp=x1; x1=x2; x2=tmp;} if (y1 > y2) {tmp=y1; y1=y2; y2=tmp;} cimg.getGraphics().drawRect(x1,y1,x2-x1,y2-y1); //NOTE : left=x1 right=x1+width (strange) } public void drawRoundBox(int x1,int y1,int x2,int y2,int ax,int ay) { int tmp; if (x1 > x2) {tmp=x1; x1=x2; x2=tmp;} if (y1 > y2) {tmp=y1; y1=y2; y2=tmp;} cimg.getGraphics().drawRoundRect(x1,y1,x2-x1,y2-y1,ax,ay); //NOTE : left=x1 right=x1+width (strange) } public void drawCircle(int x1,int y1,int x2,int y2) { int tmp; if (x1 > x2) {tmp=x1; x1=x2; x2=tmp;} if (y1 > y2) {tmp=y1; y1=y2; y2=tmp;} cimg.getGraphics().drawOval(x1,y1,x2-x1+1,y2-y1+1); } public void drawCurve(int cx[], int cy[]) { cimg.getGraphics2D().draw(new CubicCurve2D.Double(cx[0],cy[0], cx[2],cy[2], cx[3],cy[3], cx[1],cy[1])); } public void fillBox(int x1,int y1,int x2,int y2) { int tmp; if (x1 > x2) {tmp=x1; x1=x2; x2=tmp;} if (y1 > y2) {tmp=y1; y1=y2; y2=tmp;} cimg.getGraphics().fillRect(x1,y1,x2-x1+1,y2-y1+1); } public void fillRoundBox(int x1,int y1,int x2,int y2,int ax,int ay) { int tmp; if (x1 > x2) {tmp=x1; x1=x2; x2=tmp;} if (y1 > y2) {tmp=y1; y1=y2; y2=tmp;} cimg.getGraphics().fillRoundRect(x1,y1,x2-x1+1,y2-y1+1,ax,ay); } public void fillCircle(int x1,int y1,int x2,int y2) { int tmp; if (x1 > x2) {tmp=x1; x1=x2; x2=tmp;} if (y1 > y2) {tmp=y1; y1=y2; y2=tmp;} cimg.getGraphics().fillOval(x1,y1,x2-x1+1,y2-y1+1); } private class Point { public int x,y,dir; public Point(int x,int y,int dir) {this.x=x; this.y=y; this.dir=dir;} } private static final byte TODO = 0; private static final byte DONE = 1; private static final byte PAINT = 2; private int match_threshold; private int match_target; private boolean match(int px) { if (match_threshold > 0) { //check RGB values against threshold int c1, c2, diff; c1 = (px & 0xff0000) >> 16; c2 = (match_target & 0xff0000) >> 16; diff = Math.abs(c1-c2); c1 = (px & 0xff00) >> 8; c2 = (match_target & 0xff00) >> 8; diff += Math.abs(c1-c2); c1 = px & 0xff; c2 = match_target & 0xff; diff += Math.abs(c1-c2); diff /= 3; return (diff <= match_threshold); } else { //must be exact match return (px & JFImage.RGB_MASK) == match_target; } } public void fillFast(int x1,int y1,int clr, boolean hasAlpha, boolean edge, int threshold) { //threshold : 0=exact 255=everything this.match_threshold = threshold; match_target = cimg.getPixel(x1,y1) & JFImage.RGB_MASK; if (match_target == (clr & JFImage.RGB_MASK)) return; int w = cimg.getWidth(); int h = cimg.getHeight(); int px[] = cimg.getBuffer(); byte done[] = new byte[w * h]; //to keep track of what has been filled in already Vector<Point> pts = new Vector<Point>(); pts.add(new Point(x1,y1,1)); if (x1 > 0) pts.add(new Point(x1-1,y1,-1)); int x,y,dir,p; boolean top,bottom; while (pts.size() > 0) { Point pt = pts.remove(0); x = pt.x; y = pt.y; dir = pt.dir; top = false; bottom = false; while ((x >= 0) && (x < w)) { p = y * w + x; if (done[p] != TODO || (!match(px[p]))) { break; } if (edge) { if ( (x > 0 && (!match(px[p-1]))) || (x < w-1 && (!match(px[p+1]))) || (y > 0 && (!match(px[p-w]))) || (y < h-1 && (!match(px[p+w]))) ) { done[p] = PAINT; } else { done[p] = DONE; } } else { if (hasAlpha) { px[p] = clr; } else { px[p] &= JFImage.ALPHA_MASK; px[p] |= clr; } done[p] = DONE; //not needed } if (y > 0) { if (!top) { if (done[p-w] == TODO && (match(px[p-w]))) { top=true; pts.add(new Point(x,y-1,1)); if (x > 0) pts.add(new Point(x-1,y-1,-1)); } } else { if (done[p-w] == TODO && (!match(px[p-w]))) top=false; } } if (y < h-1) { if (!bottom) { if (done[p+w] == TODO && (match(px[p+w]))) { bottom=true; pts.add(new Point(x,y+1,1)); if (x > 0) pts.add(new Point(x-1,y+1,-1)); } } else { if (done[p+w] == TODO && (!match(px[p+w]))) bottom=false; } } x += dir; } } if (edge) { //do all edge painting after int wh = w * h; for(p=0;p<wh;p++) { if (done[p] == PAINT) { if (hasAlpha) { px[p] = clr; } else { px[p] &= JFImage.ALPHA_MASK; px[p] |= clr; } } } } } public void fillSlow(int x1,int y1, boolean edge, int threshold) { int w = getUnscaledWidth(); int h = getUnscaledHeight(); Graphics2D g = img[imageLayer].getGraphics2D(); match_target = img[imageLayer].getPixel(x1,y1) & JFImage.RGB_MASK; match_threshold = threshold; int px[] = cimg.getBuffer(); byte done[] = new byte[w * h]; //to keep track of what has been filled in already Vector<Point> pts = new Vector<Point>(); pts.add(new Point(x1,y1,1)); if (x1 > 0) pts.add(new Point(x1-1,y1,-1)); int x,y,dir,p; boolean top,bottom; while (pts.size() > 0) { Point pt = pts.remove(0); x = pt.x; y = pt.y; dir = pt.dir; top = false; bottom = false; while ((x >= 0) && (x < w)) { p = y * w + x; if ((!match(px[p])) || (done[p] != TODO)) { break; } if (edge) { if ( (x > 0 && (!match(px[p-1]))) || (x < w-1 && (!match(px[p+1]))) || (y > 0 && (!match(px[p-w]))) || (y < h-1 && (!match(px[p+w]))) ) { done[p] = PAINT; } else { done[p] = DONE; } } else { g.fillRect(x,y,1,1); //slow done[p] = DONE; } if (y > 0) { if (!top) { if ((match(px[p-w])) && (done[p-w] == TODO)) { top=true; pts.add(new Point(x,y-1,1)); if (x > 0) pts.add(new Point(x-1,y-1,-1)); } } else { if (!match(px[p-w])) top=false; } } if (y < h-1) { if (!bottom) { if ((match(px[p+w])) && (done[p+w] == TODO)) { bottom=true; pts.add(new Point(x,y+1,1)); if (x > 0) pts.add(new Point(x-1,y+1,-1)); } } else { if (!match(px[p+w])) bottom=false; } } x += dir; } } if (edge) { //do all edge painting after p = 0; for(y=0;y<h;y++) { for(x=0;x<w;x++) { if (done[p] == PAINT) { g.fillRect(x,y,1,1); //slow } p++; } } } } //changes clr1 -> clr2 public void subBoxFast(int x1, int y1, int x2, int y2, int clr1, int clr2, int threshold) { int clr = clr1 & JFImage.RGB_MASK; int w = getUnscaledWidth(); int h = getUnscaledHeight(); int px[] = img[imageLayer].getBuffer(); int x,y,p; //do clipping int tmp; if (x1 > x2) {tmp=x1; x1=x2; x2=tmp;} if (y1 > y2) {tmp=y1; y1=y2; y2=tmp;} if (x1 < 0) x1 = 0; if (y1 < 0) y1 = 0; if (x2 < 0) x2 = 0; if (y2 < 0) y2 = 0; if (x1 >= w) x1 = w-1; if (y1 >= h) y1 = h-1; if (x2 >= w) x2 = w-1; if (y2 >= h) y2 = h-1; match_threshold = threshold; match_target = clr; for(y=y1;y<=y2;y++) { p = y * w + x1; for(x=x1;x<=x2;x++,p++) { if (!match(px[p])) continue; px[p] &= JFImage.ALPHA_MASK; px[p] |= clr2; } } } public void subBoxSlow(int x1, int y1, int x2, int y2, int clr1, int threshold) { int clr = clr1 & JFImage.RGB_MASK; match_threshold = threshold; match_target = clr; int w = getUnscaledWidth(); int h = getUnscaledHeight(); int px[] = img[imageLayer].getPixels(); Graphics2D g = img[imageLayer].getGraphics2D(); int x,y,p; //do clipping int tmp; if (x1 > x2) {tmp=x1; x1=x2; x2=tmp;} if (y1 > y2) {tmp=y1; y1=y2; y2=tmp;} if (x1 < 0) x1 = 0; if (y1 < 0) y1 = 0; if (x2 < 0) x2 = 0; if (y2 < 0) y2 = 0; if (x1 >= w) x1 = w-1; if (y1 >= h) y1 = h-1; if (x2 >= w) x2 = w-1; if (y2 >= h) y2 = h-1; for(y=y1;y<=y2;y++) { p = y * w + x1; for(x=x1;x<=x2;x++,p++) { if (!match(px[p])) continue; g.fillRect(x,y,1,1); //slow } } } public void setFont(Font font) { cimg.setFont(font); } public void drawText(String txt[], int x, int y, int dy, float sx, float sy, float rotate) { Graphics2D g = cimg.getGraphics2D(); AffineTransform org = g.getTransform(); g.scale(sx, sy); g.translate(x,y); x = 0; y = dy; if (rotate != 0) { g.rotate(rotate); } for(int a=0;a<txt.length;a++) { g.drawString(txt[a], x, y); y += dy; } g.setTransform(org); } public static boolean swap; public void backClear() { //create a checkered board on the back image (only visible if there is any transparent parts) int x = getUnscaledWidth(); int y = getUnscaledHeight(); //System.out.println("backclear:"+x+","+y); bimg.fill(0,0,x,y,0xffffff); JFImage tmp = new JFImage(); tmp.setImageSize(16,16); Graphics g = tmp.getGraphics(); g.setColor(new Color(0x888888)); if (swap) { g.fillRect(0,0,8,8); g.fillRect(8,8,8,8); } else { g.fillRect(8,0,8,8); g.fillRect(0,8,8,8); } Graphics2D g2d = bimg.getGraphics2D(); g2d.setPaint(new TexturePaint(tmp.getBufferedImage(), new Rectangle2D.Double(0,0,16,16) )); g2d.fillRect(0,0,x,y); } public void foreClear() { fimg.fillAlpha(0,0,img[imageLayer].getWidth(),img[imageLayer].getHeight(),0x00); //transparent repaint(); } public void foreDrawLine(int x1,int y1,int x2,int y2) { fimg.getGraphics().drawLine(x1,y1,x2,y2); } public void foreDrawBox(int x1,int y1,int x2,int y2) { int tmp; if (x1 > x2) {tmp=x1; x1=x2; x2=tmp;} if (y1 > y2) {tmp=y1; y1=y2; y2=tmp;} fimg.getGraphics().drawRect(x1,y1,x2-x1,y2-y1); //NOTE : left=x1 right=x1+width (strange) } public void foreDrawRoundBox(int x1,int y1,int x2,int y2,int ax, int ay) { int tmp; if (x1 > x2) {tmp=x1; x1=x2; x2=tmp;} if (y1 > y2) {tmp=y1; y1=y2; y2=tmp;} fimg.getGraphics().drawRoundRect(x1,y1,x2-x1,y2-y1,ax,ay); //NOTE : left=x1 right=x1+width (strange) } public void foreDrawCircle(int x1,int y1,int x2,int y2) { int tmp; if (x1 > x2) {tmp=x1; x1=x2; x2=tmp;} if (y1 > y2) {tmp=y1; y1=y2; y2=tmp;} fimg.getGraphics().drawOval(x1,y1,x2-x1+1,y2-y1+1); } public void foreDrawSelBox(int x1,int y1,int x2,int y2, float rotate) { int tmp; Graphics2D fg = fimg.getGraphics2D(); if (x1 > x2) {tmp=x1; x1=x2; x2=tmp;} if (y1 > y2) {tmp=y1; y1=y2; y2=tmp;} fg.setColor(new Color(0x00)); AffineTransform org = fg.getTransform(); fg.translate(x1, y1); if (rotate != 0) { fg.rotate(rotate); } int width = x2-x1; int height = y2-y1; x2 -= x1; y2 -= y1; x1 = 0; y1 = 0; fg.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL,0, new float[]{9}, selBoxIdx++)); if (selBoxIdx == 9) selBoxIdx = 0; //NOTE : left=x1 right=x1+width (strange) int width2 = width / 2 - 2; int height2 = height / 2 - 2; fg.drawRect(x1, y1, width, height); fg.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); fg.fillRect(x1-5,y1-5,6,6); //NW box fg.fillRect(x1+width2,y1-5,6,6); //N box fg.fillRect(x1+width,y1-5,6,6); //NE box fg.fillRect(x1+width,y1+height2,6,6); //E box fg.fillRect(x1+width,y1+height,6,6); //SE box fg.fillRect(x1+width2,y1+height,6,6); //S box fg.fillRect(x1-5,y1+height,6,6); //SW box fg.fillRect(x1-5,y1+height2,6,6); //W box fg.drawImage(mainPanel.rotateImg.getImage(),x1+width+5,y1+height+5,null); //R box fg.setTransform(org); } public void forePutPixels(int px[], int x, int y, int w, int h, int iw, int ih, float rotate) { Graphics2D fg = fimg.getGraphics2D(); BufferedImage bi = new BufferedImage(iw,ih,BufferedImage.TYPE_INT_ARGB); bi.setRGB(0, 0, iw, ih, px, 0, iw); AffineTransform transform = new AffineTransform(); transform.translate(x, y); if (rotate != 0) { transform.rotate(rotate); } transform.scale((float)w / (float)iw, (float)h / (float)ih); fg.drawImage(bi, transform, null); } public void putPixels(int px[], int x, int y, int w, int h, int iw, int ih, float rotate) { Graphics2D fg = img[getImageLayer()].getGraphics2D(); BufferedImage bi = new BufferedImage(iw,ih,BufferedImage.TYPE_INT_ARGB); bi.setRGB(0, 0, iw, ih, px, 0, iw); AffineTransform transform = new AffineTransform(); transform.translate(x, y); if (rotate != 0) { transform.rotate(rotate); } transform.scale((float)w / (float)iw, (float)h / (float)ih); fg.drawImage(bi, transform, null); } public void forePutPixelsKeyClr(int px[], int x, int y, int w, int h, int offset, int keyclr) { fimg.putPixelsKeyClr(px, x, y, w, h, offset, keyclr); } public void forePutPixelsBlend(int px[], int x, int y, int w, int h, int offset) { fimg.putPixelsBlend(px, x, y, w, h, offset, true); } public void forePutPixelsBlendKeyClr(int px[], int x, int y, int w, int h, int offset, int keyclr) { fimg.putPixelsBlendKeyClr(px, x, y, w, h, offset, true, keyclr); } public void foreDrawCurve(int cx[], int cy[]) { fimg.getGraphics2D().draw(new CubicCurve2D.Double(cx[0],cy[0], cx[2],cy[2], cx[3],cy[3], cx[1],cy[1])); } public void foreFillBox(int x1,int y1,int x2,int y2) { int tmp; if (x1 > x2) {tmp=x1; x1=x2; x2=tmp;} if (y1 > y2) {tmp=y1; y1=y2; y2=tmp;} fimg.getGraphics().fillRect(x1,y1,x2-x1+1,y2-y1+1); } public void foreFillRoundBox(int x1,int y1,int x2,int y2,int ax,int ay) { int tmp; if (x1 > x2) {tmp=x1; x1=x2; x2=tmp;} if (y1 > y2) {tmp=y1; y1=y2; y2=tmp;} fimg.getGraphics().fillRoundRect(x1,y1,x2-x1+1,y2-y1+1,ax,ay); } public void foreFillCircle(int x1,int y1,int x2,int y2) { int tmp; if (x1 > x2) {tmp=x1; x1=x2; x2=tmp;} if (y1 > y2) {tmp=y1; y1=y2; y2=tmp;} fimg.getGraphics().fillOval(x1,y1,x2-x1+1,y2-y1+1); } public void foreSetFont(Font font) { fimg.setFont(font); } public void foreDrawText(String txt[], int x, int y, int dy, float sx, float sy, float rotate) { Graphics2D g = fimg.getGraphics2D(); AffineTransform org = g.getTransform(); g.scale(sx, sy); g.translate(x,y); x = 0; y = dy; if (rotate != 0) { g.rotate(rotate); } for(int a=0;a<txt.length;a++) { g.drawString(txt[a], x, y); y += dy; } g.setTransform(org); } public void rotateCW() { int w = getUnscaledWidth(); int h = getUnscaledHeight(); int px1[] = img[imageLayer].getPixels(); int p1; int px2[] = new int[w*h]; int p2; for(int y=0;y<h;y++) { p1 = y * w; p2 = h - 1 - y; for(int x=0;x<w;x++) { px2[p2] = px1[p1++]; p2 += h; } } setImageSize(h,w); img[imageLayer].putPixels(px2,0,0,h,w,0); } public void rotateCCW() { int w = getUnscaledWidth(); int h = getUnscaledHeight(); int px1[] = img[imageLayer].getPixels(); int p1; int px2[] = new int[w*h]; int p2; for(int y=0;y<h;y++) { p1 = y * w; p2 = h * (w-1) + y; for(int x=0;x<w;x++) { px2[p2] = px1[p1++]; p2 -= h; } } setImageSize(h,w); img[imageLayer].putPixels(px2,0,0,h,w,0); } public void flipVert() { int w = getUnscaledWidth(); int h = getUnscaledHeight(); int px1[] = img[imageLayer].getPixels(); int p1; int px2[] = new int[w*h]; int p2; for(int y=0;y<h;y++) { p1 = y * w; p2 = p1 + w - 1; for(int x=0;x<w;x++) { px2[p2--] = px1[p1++]; } } img[imageLayer].putPixels(px2,0,0,w,h,0); } public void flipHorz() { int w = getUnscaledWidth(); int h = getUnscaledHeight(); int px1[] = img[imageLayer].getPixels(); int p1 = 0; int px2[] = new int[w*h]; int p2 = w * (h-1); for(int y=0;y<h;y++) { System.arraycopy(px1, p1, px2, p2, w); p1 += w; p2 -= w; } img[imageLayer].putPixels(px2,0,0,w,h,0); } public void scaleImage(int ws, int hs) { int w = getUnscaledWidth(); int h = getUnscaledHeight(); if ((ws == 100) && (hs == 100)) return; int neww = w * ws / 100; int newh = h * hs / 100; if (neww < 1) neww = 1; if (newh < 1) newh = 1; JFImage tmp[] = new JFImage[imageLayers]; for(int a=0;a<imageLayers;a++) { tmp[a] = new JFImage(); tmp[a].setImageSize(neww, newh); tmp[a].getGraphics().drawImage(img[imageLayer].getImage(),0, 0, neww, newh, 0, 0, w, h, null); } setImageSize(neww, newh); for(int a=0;a<imageLayers;a++) { img[a].getGraphics().drawImage(tmp[a].getImage(), 0, 0, null); } } public void createUndo() { createUndo("undo"); } public void createUndo(String type) { // JFLog.log("create:" + type); int w = getUnscaledWidth(); int h = getUnscaledHeight(); try { Undo undo = new Undo(); undo.img.setImageSize(w, h); applyColorLayer(); int px[] = img[imageLayer].getPixels(); undo.img.putPixels(px, 0, 0, w, h, 0); undo.imageLayer = imageLayer; undoPos++; undos.add(undo); while(undos.size() > undoPos) { undos.remove(undos.size()-1); //remove redos } if (undos.size() > MAX_UNDO_SIZE) { undos.remove(0); undoPos--; } JFLog.log("undoSize=" + undos.size() + ",undoPos=" + undoPos); } catch (Exception e) { JFLog.log(e); } } public void clearUndo() { undoPos = 0; undos.clear(); } public void undo() { if (undoPos <= 0) return; // JFLog.log("undo"); try { if (undoDirty) { //createRedo createUndo("redo"); undoDirty = false; undoPos--; } undoPos--; Undo undo = undos.get(undoPos); int orgLayer = colorLayer; changeColorLayer(0); int w = undo.img.getWidth(); int h = undo.img.getHeight(); setImageSize(w, h); backClear(); foreClear(); int px[] = undo.img.getPixels(); img[undo.imageLayer].putPixels(px, 0, 0, w, h, 0); resizeBorder(); changeColorLayer(orgLayer); repaint(); JFLog.log("undoSize=" + undos.size() + ",undoPos=" + undoPos); } catch (Exception e) { JFLog.log(e); } } public void redo() { if (undos.isEmpty()) return; if (undoPos == undos.size()-1) return; if (undoDirty) { while(undos.size() > undoPos) { undos.remove(undos.size()-1); //remove redos } return; } // JFLog.log("redo"); try { undoPos++; Undo undo = undos.get(undoPos); int w = undo.img.getWidth(); int h = undo.img.getHeight(); setImageSize(w, h); backClear(); foreClear(); int px[] = undo.img.getPixels(); img[undo.imageLayer].putPixels(px, 0, 0, w, h, 0); resizeBorder(); repaint(); // JFLog.log("undoSize=" + undos.size() + ",undoPos=" + undoPos); } catch (Exception e) { JFLog.log(e); } } public void setDirty() { dirty = true; undoDirty = true; } public int getColorLayer() { return colorLayer; } public void changeColorLayer(int newLayer) { //merge current layer back to img applyColorLayer(); colorLayer = newLayer; int px[]; int w = getUnscaledWidth(); int h = getUnscaledHeight(); switch (colorLayer) { case 0: //ARGB cimg = img[imageLayer]; break; case 1: //A--- px = img[imageLayer].getAlphaLayer(); limg.putPixels(px, 0, 0, w, h, 0); cimg = limg; break; case 2: //-R-- px = img[imageLayer].getLayer(0x00ff0000); limg.putPixels(px, 0, 0, w, h, 0); cimg = limg; break; case 3: //--G- px = img[imageLayer].getLayer(0x0000ff00); limg.putPixels(px, 0, 0, w, h, 0); cimg = limg; break; case 4: //---B px = img[imageLayer].getLayer(0x000000ff); limg.putPixels(px, 0, 0, w, h, 0); cimg = limg; break; } } public void applyColorLayer() { int px[]; switch (colorLayer) { case 0: //ARGB : nothing to do break; case 1: //A--- px = limg.getBuffer(); img[imageLayer].putAlphaLayer(px); break; case 2: //-R-- px = limg.getBuffer(); img[imageLayer].putLayer(px, 0x00ff0000); break; case 3: //--G- px = limg.getBuffer(); img[imageLayer].putLayer(px, 0x0000ff00); break; case 4: //---B px = limg.getBuffer(); img[imageLayer].putLayer(px, 0x000000ff); break; } } public void blur(int x1,int y1,int x2,int y2,int radius) { int tmp; int width = img[imageLayer].getWidth(), height = img[imageLayer].getHeight(); if (x1 < 0) x1 = 0; if (x2 < 0) x2 = 0; if (y1 < 0) y1 = 0; if (y2 < 0) y2 = 0; if (x1 >= width) x1 = width-1; if (x2 >= width) x2 = width-1; if (y1 >= height) y1 = height-1; if (y2 >= height) y2 = height-1; if (x1 > x2) { tmp = x1; x1 = x2; x2 = tmp; } if (y1 > y2) { tmp = y1; y1 = y2; y2 = tmp; } width = x2-x1+1; height = y2-y1+1; Kernel kernel = GaussianFilter.makeKernel(radius); int dst[] = new int[width * height]; int inPixels[] = img[imageLayer].getPixels(x1,y1,width,height); int outPixels[] = dst; boolean alpha = false, premultiplyAlpha = false; if (radius > 0) { GaussianFilter.convolveAndTranspose(kernel, inPixels, outPixels, width, height, alpha, alpha && premultiplyAlpha, false, GaussianFilter.CLAMP_EDGES); GaussianFilter.convolveAndTranspose(kernel, outPixels, inPixels, height, width, alpha, false, alpha && premultiplyAlpha, GaussianFilter.CLAMP_EDGES); } img[imageLayer].putPixels(inPixels, x1, y1, width, height, 0); } public void pixelate(int x1,int y1,int x2,int y2,int pixelSize) { int tmp; int width = img[imageLayer].getWidth(), height = img[imageLayer].getHeight(); if (x1 < 0) x1 = 0; if (x2 < 0) x2 = 0; if (y1 < 0) y1 = 0; if (y2 < 0) y2 = 0; if (x1 >= width) x1 = width-1; if (x2 >= width) x2 = width-1; if (y1 >= height) y1 = height-1; if (y2 >= height) y2 = height-1; if (x1 > x2) { tmp = x1; x1 = x2; x2 = tmp; } if (y1 > y2) { tmp = y1; y1 = y2; y2 = tmp; } width = x2-x1+1; height = y2-y1+1; int px[] = img[imageLayer].getPixels(x1,y1,width,height); BlockFilter filter = new BlockFilter(pixelSize); BufferedImage src = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); src.setRGB(0, 0, width, height, px, 0, width); BufferedImage dst = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); filter.filter(src, dst); dst.getRGB(0, 0, width, height, px, 0, width); img[imageLayer].putPixels(px, x1, y1, width, height, 0); } public void chrome(int x1,int y1,int x2,int y2,float amount, float exposure) { int tmp; int width = img[imageLayer].getWidth(), height = img[imageLayer].getHeight(); if (x1 < 0) x1 = 0; if (x2 < 0) x2 = 0; if (y1 < 0) y1 = 0; if (y2 < 0) y2 = 0; if (x1 >= width) x1 = width-1; if (x2 >= width) x2 = width-1; if (y1 >= height) y1 = height-1; if (y2 >= height) y2 = height-1; if (x1 > x2) { tmp = x1; x1 = x2; x2 = tmp; } if (y1 > y2) { tmp = y1; y1 = y2; y2 = tmp; } width = x2-x1+1; height = y2-y1+1; int px[] = img[imageLayer].getPixels(x1,y1,width,height); ChromeFilter filter = new ChromeFilter(); filter.setAmount(amount); filter.setExposure(exposure); BufferedImage src = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); src.setRGB(0, 0, width, height, px, 0, width); BufferedImage dst = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); filter.filter(src, dst); dst.getRGB(0, 0, width, height, px, 0, width); img[imageLayer].putPixels(px, x1, y1, width, height, 0); } public int getImageLayers() { return imageLayers; } private String uniqueName() { int n = imageLayers; do { String str = "Layer " + n; boolean dup = false; for(int a=0;a<imageLayers;a++) { if (name[a].equals(str)) { dup = true; break; } } if (dup) { n++; continue; } else { return str; } } while (true); } public void addImageLayer() { img = Arrays.copyOf(img, imageLayers+1); img[imageLayers] = new JFImage(getUnscaledWidth(), getUnscaledHeight()); //fills with back color but 100% transparent img[imageLayers].fill(0, 0, getUnscaledWidth(), getUnscaledHeight(), mainPanel.backClr, true); show = Arrays.copyOf(show, imageLayers+1); show[imageLayers] = true; name = Arrays.copyOf(name, imageLayers+1); name[imageLayers] = uniqueName(); imageLayers++; } public void swapLayers(int i1, int i2) { if (i1 == i2) return; JFImage tmp = img[i2]; img[i2] = img[i1]; img[i1] = tmp; boolean tmp2 = show[i2]; show[i2] = show[i1]; show[i1] = tmp2; String tmp3 = name[i2]; name[i2] = name[i1]; name[i1] = tmp3; } public void removeImageLayer(int layer) { if (layer >= imageLayers) return; if (layer == 0 && imageLayers == 1) { //fills with back color (0% transparent) img[0].fill(0, 0, getUnscaledWidth(), getUnscaledHeight(), mainPanel.backClr); return; } img = (JFImage[])JF.copyOfExcluding(img, layer); show = JF.copyOfExcluding(show, layer); name = (String[])JF.copyOfExcluding(name, layer); imageLayers--; if (imageLayer >= imageLayers) imageLayer--; if (!show[imageLayer]) show[imageLayer] = true; changeColorLayer(colorLayer); } public void setImageLayer(int layer) { clearUndo(); int orgColorLayer = colorLayer; if (orgColorLayer != 0) { changeColorLayer(0); } imageLayer = layer; changeColorLayer(orgColorLayer); } public int getImageLayer() { return imageLayer; } public JFImage combineImageLayers() { if (img.length == 1) return img[0]; JFImage c = new JFImage(); c.setSize(img[0].getWidth(), img[0].getHeight()); Graphics g = c.getGraphics(); for(int a=0;a<img.length;a++) { g.drawImage(img[a].getImage(), 0, 0, null); } return c; } public void mouseClicked(MouseEvent e) { mainPanel.mouseClicked(e); } public void mouseEntered(MouseEvent e) { mainPanel.mouseEntered(e); } public void mouseExited(MouseEvent e) { mainPanel.mouseExited(e); } public void mousePressed(MouseEvent e) { mainPanel.mousePressed(e); } public void mouseReleased(MouseEvent e) { mainPanel.mouseReleased(e); } public void mouseDragged(MouseEvent e) { mx = (int)(e.getX() / (scale / 100f)); my = (int)(e.getY() / (scale / 100f)); mainPanel.mouseDragged(e); } public void mouseMoved(MouseEvent e) { mx = (int)(e.getX() / (scale / 100f)); my = (int)(e.getY() / (scale / 100f)); mainPanel.mouseMoved(e); } public void keyPressed(KeyEvent e) {} public void keyReleased(KeyEvent e) {} public void keyTyped(KeyEvent e) { mainPanel.keyTypedOnImage(e.getKeyChar()); } }