/* JWildfire - an image and animation processor written in Java Copyright (C) 1995-2011 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. */ package org.jwildfire.create.tina.swing; import static org.jwildfire.base.mathlib.MathLib.EPSILON; import static org.jwildfire.base.mathlib.MathLib.fabs; import static org.jwildfire.base.mathlib.MathLib.iabs; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import javax.swing.JColorChooser; import org.jwildfire.create.tina.base.Layer; import org.jwildfire.create.tina.palette.RGBColor; import org.jwildfire.create.tina.palette.RGBPalette; import org.jwildfire.create.tina.swing.flamepanel.FlamePanel; import org.jwildfire.create.tina.swing.flamepanel.FlamePanelConfig; import com.l2fprod.common.beans.editor.FilePropertyEditor; import com.l2fprod.common.util.ResourceManager; public class GradientOverlay { private final FlamePanel parent; private static final int GRADIENT_OUTER_BORDER = 20; private static final int GRADIENT_HEIGHT = 56; private static final int GRADIENT_MARKER_SIZE = 24; private static final int GRADIENT_MARKER_DISTANCE = 15; private static final int GRADIENT_SIZE = RGBPalette.PALETTE_SIZE; private int[] markerPos = { 0, GRADIENT_SIZE - 1 }; private int[] markerXMin = new int[markerPos.length]; private int[] markerXMax = new int[markerPos.length]; private int dragStartX, dragStartY, dragMarkerIdx; private int markerHandleYMin; private int markerHandleYMax; private int markerColorSelYMin; private int markerColorSelYMax; private double gradientOff = 0; private double gradientZoom = 1.0; private int xMin, xMax, yMin, yMax; private int xPos[] = new int[GRADIENT_SIZE + 1]; public GradientOverlay(FlamePanel pParent) { parent = pParent; } private void calculateSizes(int pViewportWidth, int pViewportHeight) { yMin = pViewportHeight - GRADIENT_OUTER_BORDER - GRADIENT_HEIGHT + 1 - GRADIENT_MARKER_SIZE - GRADIENT_MARKER_DISTANCE; yMax = pViewportHeight - GRADIENT_OUTER_BORDER - 1 - (GRADIENT_MARKER_SIZE * 3) / 2 - GRADIENT_MARKER_DISTANCE; xMin = xPos[0] = GRADIENT_OUTER_BORDER + 1; xMax = xPos[GRADIENT_SIZE] = pViewportWidth - GRADIENT_OUTER_BORDER - 1; double xScl = (double) (pViewportWidth - 2 * GRADIENT_OUTER_BORDER - 1 + 1) * gradientZoom / (double) (GRADIENT_SIZE + 2); for (int i = 1; i < GRADIENT_SIZE; i++) { xPos[i] = GRADIENT_OUTER_BORDER + 1 + (int) ((i + gradientOff) * xScl + 0.5); } } public void paintGradient(Graphics2D g, RGBPalette pGradient, Rectangle pBounds) { if (pGradient != null) { int width = pBounds.width; int height = pBounds.height; calculateSizes(width, height); drawGradient(g, pGradient); drawMarkers(g, pGradient); } } private void drawMarkers(Graphics2D g, RGBPalette pGradient) { drawMarker(g, pGradient, 0); drawMarker(g, pGradient, 1); } private void drawMarker(Graphics2D g, RGBPalette pGradient, int pMarkerIdx) { g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setColor(FlamePanelConfig.XFORM_COLOR); int mPos = markerPos[pMarkerIdx]; int x = xPos[mPos] + (xPos[mPos + 1] - xPos[mPos]) / 2; if (x >= xMin && x <= xMax) { x += 1; int size2 = GRADIENT_MARKER_SIZE / 2; int yOff = yMax + 1 + size2; int xPoints[] = { x - size2 + 1, x, x + size2 - 1 }; int yPoints[] = { yOff + size2, yOff - size2, yOff + size2 }; g.fillPolygon(xPoints, yPoints, xPoints.length); g.fillOval(x - size2, yOff + size2, 2 * size2, 2 * size2); markerHandleYMin = yOff - size2; markerHandleYMax = yOff + size2; markerColorSelYMin = markerHandleYMax; markerColorSelYMax = yOff + 3 * size2; markerXMin[pMarkerIdx] = x - size2; markerXMax[pMarkerIdx] = x + size2; RGBColor color = pGradient.getColor(mPos); g.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue())); g.fillOval(x - size2 + 2, yOff + size2 + 2, 2 * size2 - 4, 2 * size2 - 4); } } private void drawGradient(Graphics2D g, RGBPalette pGradient) { g.setColor(FlamePanelConfig.XFORM_COLOR); g.drawRect(xMin - 1, yMin - 1, xMax - xMin + 1, yMax - yMin + 2); for (int i = 0; i < GRADIENT_SIZE; i++) { int cxMin = xPos[i], cxMax = xPos[i + 1] - 1; RGBColor color = pGradient.getColor(i); g.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue())); for (int cx = cxMin; cx <= cxMax && cx >= xMin && cx <= xMax; cx++) { g.drawLine(cx, yMin, cx, yMax); } } } public double getGradientZoom() { return gradientZoom; } public void setGradientOff(double gradientOff) { this.gradientOff = gradientOff; } public double getGradientOff() { return gradientOff; } public void setGradientZoom(double pGradientZoom) { if (pGradientZoom < 1.0) { pGradientZoom = 1.0; } if (fabs(pGradientZoom - 1.0) < EPSILON) { gradientOff = 0.0; } this.gradientZoom = pGradientZoom; } public boolean mouseDragged(double pDX, int pPosX, int pPosY, Layer pLayer) { boolean reRender = false; int iDX = (int) (pDX + 0.5); if (iDX < 1 && pDX > 0.0) { iDX = 1; } else if (iDX > -1 && pDX < 0.0) { iDX = -1; } // drag marker if (dragStartY > markerHandleYMin && dragStartY < markerHandleYMax) { if (dragMarkerIdx >= 0) { setMarker(pPosX, dragMarkerIdx); } } // drag gradient else if (dragStartY > yMin && dragStartY < yMax) { if (fabs(gradientZoom - 1.0) < EPSILON) { int modShift = pLayer.getPalette().getModShift() + iDX; pLayer.getPalette().shiftColors(modShift); reRender = true; } else { gradientOff += iDX; if (gradientOff > 0) { gradientOff = 0; } else { } } } return reRender; } private void setMarker(int pPosX, int pIndex) { for (int i = 0; i < GRADIENT_SIZE; i++) { int x = pPosX; if ((x >= xPos[i] && x <= xPos[i + 1]) || (i == 0 && x <= xPos[i + 1]) || (i == GRADIENT_SIZE - 1 && x >= xPos[i])) { int newPos = i; int leftLimit = 0; int rightLimit = GRADIENT_SIZE - 1; if (newPos < leftLimit) { newPos = leftLimit; } else if (newPos > rightLimit) { newPos = rightLimit; } markerPos[pIndex] = newPos; break; } } } private int getMarkerByPosition(int pPosX, boolean pExact) { int res = -1; int minDist = -1; for (int i = 0; i < markerPos.length; i++) { if (pExact) { if (pPosX >= markerXMin[i] && pPosX <= markerXMax[i]) { return i; } } else { int cx = markerXMin[i] + (markerXMax[i] - markerXMin[i]) / 2; int dist = iabs(cx - pPosX); if (minDist < 0 || dist < minDist) { minDist = dist; res = i; } } } return res; } public void mouseWheelAction(double dz) { double zoom = getGradientZoom() + dz * 5.0; setGradientZoom(zoom); } public void beginDrag(int pStartX, int pStartY) { dragStartX = pStartX; dragStartY = pStartY; dragMarkerIdx = getMarkerByPosition(dragStartX, false); } public boolean mouseClicked(int x, int y, RGBPalette pGradient) { if (y >= markerColorSelYMin && y <= markerColorSelYMax) { int marker = getMarkerByPosition(x, true); if (marker >= 0) { return gradientMarker_selectColor(marker, pGradient); } } else if (y >= markerHandleYMin && y <= markerHandleYMax) { int marker = getMarkerByPosition(x, false); setMarker(x, marker); return true; } return false; } public void fadeRange(RGBPalette pGradient) { pGradient.fadeRange(getFrom(), getTo()); } public void invertRange(RGBPalette pGradient) { pGradient.negativeColors(); } public void reverseRange(RGBPalette pGradient) { pGradient.reverseColors(); } public void sortRange(RGBPalette pGradient) { pGradient.sort(); } public void selectAll() { markerPos[0] = 0; markerPos[1] = GRADIENT_SIZE - 1; } public void gradientMarker_move(int marker, int pDeltaPos) { if (marker >= 0) { int leftLimit = 0; int rightLimit = GRADIENT_SIZE - 1; int newPos = markerPos[marker] + pDeltaPos; if (newPos < leftLimit) { newPos = leftLimit; } else if (newPos > rightLimit) { newPos = rightLimit; } markerPos[marker] = newPos; } } public boolean gradientMarker_selectColor(int marker, RGBPalette pGradient) { if (marker >= 0) { ResourceManager rm = ResourceManager.all(FilePropertyEditor.class); String title = rm.getString("ColorPropertyEditor.title"); RGBColor color = pGradient.getColor(markerPos[marker]); Color selectedColor = JColorChooser.showDialog(parent, title, new Color(color.getRed(), color.getGreen(), color.getBlue())); if (selectedColor != null) { pGradient.setColor(markerPos[marker], selectedColor.getRed(), selectedColor.getGreen(), selectedColor.getBlue()); return true; } } return false; } public int getFrom() { return markerPos[0] <= markerPos[1] ? markerPos[0] : markerPos[1]; } public int getTo() { return markerPos[0] <= markerPos[1] ? markerPos[1] : markerPos[0]; } private RGBColor[] currClipboard; public void copyRange(RGBPalette pGradient) { if (getTo() >= getFrom()) { currClipboard = new RGBColor[getTo() - getFrom() + 1]; for (int i = getFrom(); i <= getTo(); i++) { currClipboard[i - getFrom()] = pGradient.getRawColor(i); } } } public void pasteRange(RGBPalette pGradient) { if (currClipboard != null && currClipboard.length > 0) { for (int i = getFrom(); (i <= getTo()) && ((i - getFrom()) < currClipboard.length); i++) { RGBColor color = currClipboard[i - getFrom()]; pGradient.setColor(i, color.getRed(), color.getGreen(), color.getBlue()); } } } public void eraseRange(RGBPalette pGradient) { if (getTo() >= getFrom()) { for (int i = getFrom(); i <= getTo(); i++) { pGradient.setColor(i, 0, 0, 0); } } } public void monochrome(RGBPalette pGradient) { pGradient.monochrome(getFrom(), getTo()); } public void fadeAll(RGBPalette pGradient) { int startIdx = 0;//getFrom(); int endIdx = GRADIENT_SIZE - 1; // getTo(); for (int i = startIdx; i <= endIdx; i++) { RGBColor color = pGradient.getRawColor(i); if (color.getRed() > 0 || color.getGreen() > 0 || color.getBlue() > 0 || i == endIdx) { if (startIdx < i) { pGradient.fadeRange(startIdx, i); startIdx = i; } } } } }