/* JWildfire - an image and animation processor written in Java Copyright (C) 1995-2015 Andreas Maschke This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this software; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ // Nearly the same as ImagePanel from sunflow, but with access to image-buffer package org.jwildfire.create.tina.render.filter; import java.awt.Dimension; import java.awt.Graphics; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.JPanel; import javax.swing.event.MouseInputAdapter; import org.sunflow.core.Display; import org.sunflow.image.Color; @SuppressWarnings("serial") public class SunFlowImagePanel extends JPanel implements Display { private static final int[] BORDERS = { Color.RED.toRGB(), Color.GREEN.toRGB(), Color.BLUE.toRGB(), Color.YELLOW.toRGB(), Color.CYAN.toRGB(), Color.MAGENTA.toRGB() }; private BufferedImage image; private float xo, yo; private float w, h; private long repaintCounter; private class ScrollZoomListener extends MouseInputAdapter { int mx; int my; boolean dragging; boolean zooming; @Override public void mousePressed(MouseEvent e) { mx = e.getX(); my = e.getY(); switch (e.getButton()) { case MouseEvent.BUTTON1: dragging = true; zooming = false; break; case MouseEvent.BUTTON2: { dragging = zooming = false; // if CTRL is pressed if ((e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) == InputEvent.CTRL_DOWN_MASK) fit(); else reset(); break; } case MouseEvent.BUTTON3: zooming = true; dragging = false; break; default: return; } repaint(); } @Override public void mouseDragged(MouseEvent e) { int mx2 = e.getX(); int my2 = e.getY(); if (dragging) drag(mx2 - mx, my2 - my); if (zooming) zoom(mx2 - mx, my2 - my); mx = mx2; my = my2; } @Override public void mouseReleased(MouseEvent e) { // same behaviour mouseDragged(e); } @Override public void mouseWheelMoved(MouseWheelEvent e) { zoom(-20 * e.getWheelRotation(), 0); } } public SunFlowImagePanel() { setPreferredSize(new Dimension(640, 480)); image = null; xo = yo = 0; w = h = 0; ScrollZoomListener listener = new ScrollZoomListener(); addMouseListener(listener); addMouseMotionListener(listener); addMouseWheelListener(listener); } public void save(String filename) { // Bitmap.save(image, filename); try { ImageIO.write(image, "png", new File(filename)); } catch (IOException e) { e.printStackTrace(); } } private synchronized void drag(int dx, int dy) { xo += dx; yo += dy; repaint(); } private synchronized void zoom(int dx, int dy) { int a = Math.max(dx, dy); int b = Math.min(dx, dy); if (Math.abs(b) > Math.abs(a)) a = b; if (a == 0) return; // window center float cx = getWidth() * 0.5f; float cy = getHeight() * 0.5f; // origin of the image in window space float x = xo + (getWidth() - w) * 0.5f; float y = yo + (getHeight() - h) * 0.5f; // coordinates of the pixel we are over float sx = cx - x; float sy = cy - y; // scale if (w + a > 100) { h = (w + a) * h / w; sx = (w + a) * sx / w; sy = (w + a) * sy / w; w = (w + a); } // restore center pixel float x2 = cx - sx; float y2 = cy - sy; xo = (x2 - (getWidth() - w) * 0.5f); yo = (y2 - (getHeight() - h) * 0.5f); repaint(); } public synchronized void reset() { xo = yo = 0; if (image != null) { w = image.getWidth(); h = image.getHeight(); } repaint(); } public synchronized void fit() { xo = yo = 0; if (image != null) { float wx = Math.max(getWidth() - 10, 100); float hx = wx * image.getHeight() / image.getWidth(); float hy = Math.max(getHeight() - 10, 100); float wy = hy * image.getWidth() / image.getHeight(); if (hx > hy) { w = wy; h = hy; } else { w = wx; h = hx; } repaint(); } } public synchronized void imageBegin(int w, int h, int bucketSize) { if (image != null && w == image.getWidth() && h == image.getHeight()) { // dull image if it has same resolution (75%) for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { int rgba = image.getRGB(x, y); image.setRGB(x, y, ((rgba & 0xFEFEFEFE) >>> 1) + ((rgba & 0xFCFCFCFC) >>> 2)); } } } else { // allocate new framebuffer image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); // center this.w = w; this.h = h; xo = yo = 0; } repaintCounter = System.nanoTime(); repaint(); } public synchronized void imagePrepare(int x, int y, int w, int h, int id) { int border = BORDERS[id % BORDERS.length] | 0xFF000000; for (int by = 0; by < h; by++) { for (int bx = 0; bx < w; bx++) { if (bx == 0 || bx == w - 1) { if (5 * by < h || 5 * (h - by - 1) < h) image.setRGB(x + bx, y + by, border); } else if (by == 0 || by == h - 1) { if (5 * bx < w || 5 * (w - bx - 1) < w) image.setRGB(x + bx, y + by, border); } } } repaint(); } public synchronized void imageUpdate(int x, int y, int w, int h, Color[] data, float[] alpha) { for (int j = 0, index = 0; j < h; j++) for (int i = 0; i < w; i++, index++) image.setRGB(x + i, y + j, data[index].copy().mul(1.0f / alpha[index]).toNonLinear().toRGBA(alpha[index])); repaint(); } public synchronized void imageFill(int x, int y, int w, int h, Color c, float alpha) { int rgba = c.copy().mul(1.0f / alpha).toNonLinear().toRGBA(alpha); for (int j = 0, index = 0; j < h; j++) for (int i = 0; i < w; i++, index++) image.setRGB(x + i, y + j, rgba); fastRepaint(); } public void imageEnd() { repaint(); } private void fastRepaint() { long t = System.nanoTime(); if (repaintCounter + 125000000 < t) { repaintCounter = t; repaint(); } } @Override public synchronized void paintComponent(Graphics g) { super.paintComponent(g); if (image == null) return; int x = Math.round(xo + (getWidth() - w) * 0.5f); int y = Math.round(yo + (getHeight() - h) * 0.5f); int iw = Math.round(w); int ih = Math.round(h); int x0 = x - 1; int y0 = y - 1; int x1 = x + iw + 1; int y1 = y + ih + 1; g.setColor(java.awt.Color.WHITE); g.drawLine(x0, y0, x1, y0); g.drawLine(x1, y0, x1, y1); g.drawLine(x1, y1, x0, y1); g.drawLine(x0, y1, x0, y0); g.drawImage(image, x, y, iw, ih, java.awt.Color.BLACK, this); } public BufferedImage getImage() { return image; } }