aboutsummaryrefslogtreecommitdiffstats
path: root/draw9patch
diff options
context:
space:
mode:
Diffstat (limited to 'draw9patch')
-rw-r--r--draw9patch/src/main/java/com/android/draw9patch/ui/ImageEditorPanel.java8
-rw-r--r--draw9patch/src/main/java/com/android/draw9patch/ui/ImageViewer.java675
-rw-r--r--draw9patch/src/main/java/com/android/draw9patch/ui/PatchInfo.java8
3 files changed, 544 insertions, 147 deletions
diff --git a/draw9patch/src/main/java/com/android/draw9patch/ui/ImageEditorPanel.java b/draw9patch/src/main/java/com/android/draw9patch/ui/ImageEditorPanel.java
index 7eb0b69..3b4cdcb 100644
--- a/draw9patch/src/main/java/com/android/draw9patch/ui/ImageEditorPanel.java
+++ b/draw9patch/src/main/java/com/android/draw9patch/ui/ImageEditorPanel.java
@@ -46,7 +46,7 @@ import javax.swing.JSplitPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
-class ImageEditorPanel extends JPanel {
+public class ImageEditorPanel extends JPanel {
private static final String EXTENSION_9PATCH = ".9.png";
private String name;
@@ -60,11 +60,13 @@ class ImageEditorPanel extends JPanel {
private TexturePaint texture;
- ImageEditorPanel(MainFrame mainFrame, BufferedImage image, String name) {
+ public ImageEditorPanel(MainFrame mainFrame, BufferedImage image, String name) {
this.image = image;
this.name = name;
- setTransferHandler(new ImageTransferHandler(mainFrame));
+ if (mainFrame != null) {
+ setTransferHandler(new ImageTransferHandler(mainFrame));
+ }
setOpaque(false);
setLayout(new BorderLayout());
diff --git a/draw9patch/src/main/java/com/android/draw9patch/ui/ImageViewer.java b/draw9patch/src/main/java/com/android/draw9patch/ui/ImageViewer.java
index eb179c0..fca896f 100644
--- a/draw9patch/src/main/java/com/android/draw9patch/ui/ImageViewer.java
+++ b/draw9patch/src/main/java/com/android/draw9patch/ui/ImageViewer.java
@@ -39,6 +39,8 @@ import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
@@ -68,6 +70,7 @@ public class ImageViewer extends JComponent {
private final Color HELP_COLOR = new Color(0xffffe1);
private final Color PATCH_COLOR = new Color(1.0f, 0.37f, 0.99f, 0.5f);
private final Color PATCH_ONEWAY_COLOR = new Color(0.37f, 1.0f, 0.37f, 0.5f);
+ private final Color HIGHLIGHT_REGION_COLOR = new Color(0.5f, 0.5f, 0.5f, 0.5f);
private static final float STRIPES_WIDTH = 4.0f;
private static final double STRIPES_SPACING = 6.0;
@@ -97,7 +100,6 @@ public class ImageViewer extends JComponent {
private int lastPositionX;
private int lastPositionY;
- private int currentButton;
private boolean showCursor;
private JLabel helpLabel;
@@ -115,9 +117,47 @@ public class ImageViewer extends JComponent {
private int lineToY;
private boolean showDrawingLine;
+ private final List<Rectangle> hoverHighlightRegions = new ArrayList<Rectangle>();
+ private String toolTipText;
+
+ /**
+ * Indicates whether we are currently in edit mode.
+ * All fields with the prefix 'edit' are valid only when in edit mode.
+ */
+ private boolean isEditMode;
+
+ /** Region being edited. */
+ private UpdateRegion editRegion;
+
+ /**
+ * The start and end points corresponding to the region being edited.
+ * During an edit sequence, the start point is constant and the end varies based on the
+ * mouse location.
+ */
+ private final Pair<Integer> editSegment = new Pair<Integer>(0, 0);
+
+ /** Regions to highlight based on the current edit. */
+ private final List<Rectangle> editHighlightRegions = new ArrayList<Rectangle>();
+
+ /** The actual patch location in the image being edited. */
+ private Rectangle editPatchRegion = new Rectangle();
+
private BufferedImage image;
private PatchInfo patchInfo;
+ /** The types of edit actions that can be performed on the image. */
+ private enum DrawMode {
+ PATCH, // drawing a patch or a padding
+ LAYOUT_BOUND, // drawing layout bounds
+ ERASE, // erasing whatever has been drawn
+ }
+
+ /**
+ * Current drawing mode. The mode is changed by using either the Shift or Ctrl keys while
+ * drawing.
+ */
+ private DrawMode currentMode = DrawMode.PATCH;
+
ImageViewer(Container container, TexturePaint texture, BufferedImage image,
StatusBar statusBar) {
this.container = container;
@@ -167,37 +207,73 @@ public class ImageViewer extends JComponent {
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent event) {
- // Store the button here instead of retrieving it again in MouseDragged
+ // Update the drawing mode looking at the current state of modifier (shift/ctrl)
+ // keys. This is done here instead of retrieving it again in MouseDragged
// below, because on linux, calling MouseEvent.getButton() for the drag
// event returns 0, which appears to be technically correct (no button
// changed state).
- currentButton = event.isShiftDown() ? MouseEvent.BUTTON3 : event.getButton();
- currentButton = event.isControlDown() ? MouseEvent.BUTTON2 : currentButton;
- startDrawingLine(event.getX(), event.getY());
+ updateDrawMode(event);
+
+ int x = imageXCoordinate(event.getX());
+ int y = imageYCoordinate(event.getY());
+
+ startDrawingLine(x, y);
+
+ if (currentMode == DrawMode.PATCH) {
+ startEditingRegion(x, y);
+ } else {
+ hoverHighlightRegions.clear();
+ setCursor(Cursor.getDefaultCursor());
+ repaint();
+ }
}
@Override
public void mouseReleased(MouseEvent event) {
+ int x = imageXCoordinate(event.getX());
+ int y = imageYCoordinate(event.getY());
+
endDrawingLine();
+ endEditingRegion(x, y);
+
+ resetDrawMode();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent event) {
- if (!checkLockedRegion(event.getX(), event.getY())) {
- // use the stored button, see note above
+ int x = imageXCoordinate(event.getX());
+ int y = imageYCoordinate(event.getY());
- moveLine(event.getX(), event.getY());
+ if (!checkLockedRegion(x, y)) {
+ // use the stored button, see note above
+ moveLine(x, y);
}
+
+ updateEditRegion(x, y);
}
@Override
public void mouseMoved(MouseEvent event) {
- checkLockedRegion(event.getX(), event.getY());
- updateHoverRegion(event.getX(), event.getY());
+ int x = imageXCoordinate(event.getX());
+ int y = imageYCoordinate(event.getY());
+
+ checkLockedRegion(x, y);
+
+ updateHoverRegion(x, y);
repaint();
}
});
+
+ addComponentListener(new ComponentAdapter() {
+ @Override
+ public void componentResized(ComponentEvent e) {
+ hoverHighlightRegions.clear();
+ updateSize();
+ repaint();
+ }
+ });
+
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
public void eventDispatched(AWTEvent event) {
enableEraseMode((KeyEvent) event);
@@ -220,14 +296,26 @@ public class ImageViewer extends JComponent {
});
}
- private List<Rectangle> highlightRegions = new ArrayList<Rectangle>();
+ private void updateDrawMode(MouseEvent event) {
+ if (event.isShiftDown()) {
+ currentMode = DrawMode.ERASE;
+ } else if (event.isControlDown()) {
+ currentMode = DrawMode.LAYOUT_BOUND;
+ } else {
+ currentMode = DrawMode.PATCH;
+ }
+ }
+
+ private void resetDrawMode() {
+ currentMode = DrawMode.PATCH;
+ }
private enum UpdateRegion {
LEFT_PATCH,
TOP_PATCH,
RIGHT_PADDING,
BOTTOM_PADDING,
- };
+ }
private static class UpdateRegionInfo {
public final UpdateRegion region;
@@ -240,31 +328,37 @@ public class ImageViewer extends JComponent {
}
private UpdateRegionInfo findVerticalPatch(int x, int y) {
+ List<Pair<Integer>> markers;
+ UpdateRegion region;
+
// Given the mouse x location, we need to determine if we need to map this edit to
// the patch info at the left, or the padding info at the right. We make this decision
// based on whichever is closer, so if the mouse x is in the left half of the image,
// we are editing the left patch, else the right padding.
if (x < image.getWidth() / 2) {
- return getVerticalPatchLeft(y);
+ markers = patchInfo.verticalPatchMarkers;
+ region = UpdateRegion.LEFT_PATCH;
} else {
- return getVerticalPaddingRight(y);
+ markers = patchInfo.verticalPaddingMarkers;
+ region = UpdateRegion.RIGHT_PADDING;
}
+
+ return getContainingPatch(markers, y, region);
}
private UpdateRegionInfo findHorizontalPatch(int x, int y) {
+ List<Pair<Integer>> markers;
+ UpdateRegion region;
+
if (y < image.getHeight() / 2) {
- return getHorizontalPatchTop(x);
+ markers = patchInfo.horizontalPatchMarkers;
+ region = UpdateRegion.TOP_PATCH;
} else {
- return getHorizontalPaddingBottom(x);
+ markers = patchInfo.horizontalPaddingMarkers;
+ region = UpdateRegion.BOTTOM_PADDING;
}
- }
- private UpdateRegionInfo getVerticalPatchLeft(int y) {
- return getContainingPatch(patchInfo.verticalPatchMarkers, y, UpdateRegion.LEFT_PATCH);
- }
-
- private UpdateRegionInfo getHorizontalPatchTop(int x) {
- return getContainingPatch(patchInfo.horizontalPatchMarkers, x, UpdateRegion.TOP_PATCH);
+ return getContainingPatch(markers, x, region);
}
private UpdateRegionInfo getContainingPatch(List<Pair<Integer>> patches, int a,
@@ -282,92 +376,337 @@ public class ImageViewer extends JComponent {
return new UpdateRegionInfo(region, null);
}
- private UpdateRegionInfo getVerticalPaddingRight(int y) {
- int top = patchInfo.verticalPadding.first + 1; // add 1 to offset for the 1 additional 9patch info pixel at top
- int bottom = image.getHeight() - patchInfo.verticalPadding.second - 1; // similarly, remove 1 pixel from the bottom
- return getContainingPadding(top, bottom, y, UpdateRegion.RIGHT_PADDING);
+ private void updateHoverRegion(int x, int y) {
+ // find regions to highlight based on the horizontal and vertical patches that
+ // cover this (x, y)
+ UpdateRegionInfo vertical = findVerticalPatch(x, y);
+ UpdateRegionInfo horizontal = findHorizontalPatch(x, y);
+ computeHoverHighlightRegions(vertical, horizontal);
+ computeHoverRegionTooltip(vertical, horizontal);
+
+ // change cursor if (x,y) is at the edge of either of the regions
+ UpdateRegionInfo updateRegion = pickUpdateRegion(x, y, vertical, horizontal);
+ setCursorForRegion(x, y, updateRegion);
}
- private UpdateRegionInfo getHorizontalPaddingBottom(int x) {
- int left = patchInfo.horizontalPadding.first + 1; // add 1 to offset for the 1 additional pixel on the left
- int right = image.getWidth() - patchInfo.horizontalPadding.second - 1; // similarly, remove 1 pixel from the right
- return getContainingPadding(left, right, x, UpdateRegion.BOTTOM_PADDING);
+ private void startEditingRegion(int x, int y) {
+ hoverHighlightRegions.clear();
+ isEditMode = false;
+ editRegion = null;
+
+ UpdateRegionInfo vertical = findVerticalPatch(x, y);
+ UpdateRegionInfo horizontal = findHorizontalPatch(x, y);
+ UpdateRegionInfo updateRegion = pickUpdateRegion(x, y, vertical, horizontal);
+ setCursorForRegion(x, y, updateRegion);
+
+ if (updateRegion != null) { // edit an existing patch
+ editRegion = updateRegion.region;
+ isEditMode = true;
+
+ Edge e = null;
+ switch (this.editRegion) {
+ case LEFT_PATCH:
+ case RIGHT_PADDING:
+ e = getClosestEdge(y, updateRegion.segment);
+ break;
+ case TOP_PATCH:
+ case BOTTOM_PADDING:
+ e = getClosestEdge(x, updateRegion.segment);
+ break;
+ default:
+ assert false : this.editRegion;
+ }
+
+ int first = updateRegion.segment.first;
+ int second = updateRegion.segment.second;
+
+ // The edge being edited should always be the end point in editSegment.
+ boolean start = e == Edge.START;
+ editSegment.first = start ? second : first;
+ editSegment.second = start ? first : second;
+
+ // clear the current patch data
+ flushEditPatchData(0);
+ } else if ((editRegion = findNewPatchRegion(x, y)) != null) { // create a new patch
+ isEditMode = true;
+
+ boolean verticalPatch = editRegion == UpdateRegion.LEFT_PATCH
+ || editRegion == UpdateRegion.RIGHT_PADDING;
+
+ x = clamp(x, 1, image.getWidth() - 1);
+ y = clamp(y, 1, image.getHeight() - 1);
+
+ editSegment.first = editSegment.second = verticalPatch ? y : x;
+ }
+
+ if (isEditMode) {
+ computeEditHighlightRegions();
+ }
+
+ repaint();
}
- private UpdateRegionInfo getContainingPadding(int start, int end, int x, UpdateRegion region) {
- Pair<Integer> p = null;
- if (x >= start && x <= end) {
- p = new Pair<Integer>(start, end);
+ private void endEditingRegion(int x, int y) {
+ if (!isEditMode) {
+ return;
+ }
+
+ x = clamp(x, 1, image.getWidth() - 1);
+ y = clamp(y, 1, image.getHeight() - 1);
+
+ switch (editRegion) {
+ case LEFT_PATCH:
+ case RIGHT_PADDING:
+ editSegment.second = y;
+ break;
+ case TOP_PATCH:
+ case BOTTOM_PADDING:
+ editSegment.second = x;
+ break;
+ default:
+ assert false : editRegion;
}
- return new UpdateRegionInfo(region, p);
+ flushEditPatchData(PatchInfo.BLACK_TICK);
+
+ hoverHighlightRegions.clear();
+ setCursor(Cursor.getDefaultCursor());
+ patchesChanged();
+ repaint();
+
+
+ isEditMode = false;
+ editRegion = null;
}
- private void updateHoverRegion(int x, int y) {
- x = imageXCoordinate(x);
- y = imageYCoordinate(y);
-
- UpdateRegionInfo verticalUpdateRegion = findVerticalPatch(x, y);
- UpdateRegionInfo horizontalUpdateRegion = findHorizontalPatch(x, y);
-
- // find regions to highlight
- computeHighlightRegions(verticalUpdateRegion, horizontalUpdateRegion);
-
- // change cursor if necessary
- Cursor c = getCursor(x, y, verticalUpdateRegion, horizontalUpdateRegion);
- setCursor(c);
- }
-
- private void computeHighlightRegions(UpdateRegionInfo verticalUpdateRegion,
- UpdateRegionInfo horizontalUpdateRegion) {
- highlightRegions.clear();
- if (verticalUpdateRegion != null && verticalUpdateRegion.segment != null) {
- Rectangle r = displayCoordinates(new Rectangle(0,
- verticalUpdateRegion.segment.first,
- image.getWidth(),
- verticalUpdateRegion.segment.second - verticalUpdateRegion.segment.first));
- // highlight the region within the image
- highlightRegions.add(r);
-
- // add a 1 pixel line at the top and bottom that extends outside the image
- highlightRegions.add(new Rectangle(0, r.y, getWidth(), 1));
- highlightRegions.add(new Rectangle(0, r.y + r.height, getWidth(), 1));
- }
- if (horizontalUpdateRegion != null && horizontalUpdateRegion.segment != null) {
- Rectangle r = displayCoordinates(new Rectangle(horizontalUpdateRegion.segment.first,
- 0,
- horizontalUpdateRegion.segment.second - horizontalUpdateRegion.segment.first,
- image.getHeight()));
- // highlight the region within the image
- highlightRegions.add(r);
-
- // add a 1 pixel line at the top and bottom that extends outside the image
- highlightRegions.add(new Rectangle(r.x, 0, 1, getHeight()));
- highlightRegions.add(new Rectangle(r.x + r.width, 0, 1, getHeight()));
- }
- }
-
- private Cursor getCursor(int x, int y, UpdateRegionInfo verticalUpdateRegion,
- UpdateRegionInfo horizontalUpdateRegion) {
- Cursor c = null;
-
- if (verticalUpdateRegion != null && verticalUpdateRegion.segment != null) {
- Edge e = getClosestEdge(y, verticalUpdateRegion.segment);
- if (e == Edge.START) {
- c = Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR);
- } else if (e == Edge.END) {
- c = Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR);
+ private void updateEditRegion(int x, int y) {
+ if (!isEditMode) {
+ return;
+ }
+
+ x = clamp(x, 1, image.getWidth() - 1);
+ y = clamp(y, 1, image.getHeight() - 1);
+
+ switch (editRegion) {
+ case LEFT_PATCH:
+ case RIGHT_PADDING:
+ editSegment.second = y;
+ break;
+ case TOP_PATCH:
+ case BOTTOM_PADDING:
+ editSegment.second = x;
+ break;
+ }
+
+ computeEditHighlightRegions();
+ repaint();
+ }
+
+ private int clamp(int i, int min, int max) {
+ if (i < min) {
+ return min;
+ }
+
+ if (i > max) {
+ return max;
+ }
+
+ return i;
+ }
+
+ /** Returns the type of patch that should be created given the initial mouse location. */
+ private UpdateRegion findNewPatchRegion(int x, int y) {
+ boolean verticalPatch = y >= 0 && y <= image.getHeight();
+ boolean horizontalPatch = x >= 0 && x <= image.getWidth();
+
+ // Heuristic: If the pointer is within the vertical bounds of the image,
+ // then we create a patch on the left or right depending on which side of the image
+ // the pointer is on
+ if (verticalPatch) {
+ if (x < 0) {
+ return UpdateRegion.LEFT_PATCH;
+ } else if (x > image.getWidth()) {
+ return UpdateRegion.RIGHT_PADDING;
+ }
+ }
+
+ // Similarly, if it is within the horizontal bounds of the image,
+ // then create a patch at the top or bottom depending on its location relative to the image
+ if (horizontalPatch) {
+ if (y < 0) {
+ return UpdateRegion.TOP_PATCH;
+ } else if (y > image.getHeight()) {
+ return UpdateRegion.BOTTOM_PADDING;
+ }
+ }
+
+ return null;
+ }
+
+ private void computeHoverHighlightRegions(UpdateRegionInfo vertical,
+ UpdateRegionInfo horizontal) {
+ hoverHighlightRegions.clear();
+ if (vertical != null && vertical.segment != null) {
+ hoverHighlightRegions.addAll(
+ getHorizontalHighlightRegions(0,
+ vertical.segment.first,
+ image.getWidth(),
+ vertical.segment.second - vertical.segment.first));
+ }
+ if (horizontal != null && horizontal.segment != null) {
+ hoverHighlightRegions.addAll(
+ getVerticalHighlightRegions(horizontal.segment.first,
+ 0,
+ horizontal.segment.second - horizontal.segment.first,
+ image.getHeight()));
+ }
+ }
+
+ private void computeHoverRegionTooltip(UpdateRegionInfo vertical, UpdateRegionInfo horizontal) {
+ StringBuilder sb = new StringBuilder(50);
+
+ if (vertical != null && vertical.segment != null) {
+ if (vertical.region == UpdateRegion.LEFT_PATCH) {
+ sb.append("Vertical Patch: ");
+ } else {
+ sb.append("Vertical Padding: ");
+ }
+ sb.append(String.format("%d - %d px",
+ vertical.segment.first, vertical.segment.second));
+ }
+
+ if (horizontal != null && horizontal.segment != null) {
+ if (sb.length() > 0) {
+ sb.append(", ");
+ }
+ if (horizontal.region == UpdateRegion.TOP_PATCH) {
+ sb.append("Horizontal Patch: ");
+ } else {
+ sb.append("Horizontal Padding: ");
}
+ sb.append(String.format("%d - %d px",
+ horizontal.segment.first, horizontal.segment.second));
}
- if (c == null && horizontalUpdateRegion != null && horizontalUpdateRegion.segment != null) {
- Edge e = getClosestEdge(x, horizontalUpdateRegion.segment);
- if (e == Edge.START) {
- c = Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR);
- } else if (e == Edge.END) {
- c = Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR);
+
+ toolTipText = sb.length() > 0 ? sb.toString() : null;
+ }
+
+ private void computeEditHighlightRegions() {
+ editHighlightRegions.clear();
+
+ int f = editSegment.first;
+ int s = editSegment.second;
+ int min = Math.min(f, s);
+ int diff = Math.abs(f - s);
+
+ int imageWidth = image.getWidth();
+ int imageHeight = image.getHeight();
+
+ switch (editRegion) {
+ case LEFT_PATCH:
+ editPatchRegion = displayCoordinates(new Rectangle(0, min, 1, diff));
+ editHighlightRegions.addAll(
+ getHorizontalHighlightRegions(0, min, imageWidth, diff));
+ break;
+ case RIGHT_PADDING:
+ editPatchRegion = displayCoordinates(new Rectangle(imageWidth - 1, min, 1, diff));
+ editHighlightRegions.addAll(
+ getHorizontalHighlightRegions(0, min, imageWidth, diff));
+ break;
+ case TOP_PATCH:
+ editPatchRegion = displayCoordinates(new Rectangle(min, 0, diff, 1));
+ editHighlightRegions.addAll(
+ getVerticalHighlightRegions(min, 0, diff, imageHeight));
+ break;
+ case BOTTOM_PADDING:
+ editPatchRegion = displayCoordinates(new Rectangle(min, imageHeight - 1, diff, 1));
+ editHighlightRegions.addAll(
+ getVerticalHighlightRegions(min, 0, diff, imageHeight));
+ default:
+ assert false : editRegion;
+ }
+ }
+
+ private List<Rectangle> getHorizontalHighlightRegions(int x, int y, int w, int h) {
+ List<Rectangle> l = new ArrayList<Rectangle>(3);
+
+ // highlight the region within the image
+ Rectangle r = displayCoordinates(new Rectangle(x, y, w, h));
+ l.add(r);
+
+ // add a 1 pixel line at the top and bottom that extends outside the image
+ l.add(new Rectangle(0, r.y, getWidth(), 1));
+ l.add(new Rectangle(0, r.y + r.height, getWidth(), 1));
+ return l;
+ }
+
+ private List<Rectangle> getVerticalHighlightRegions(int x, int y, int w, int h) {
+ List<Rectangle> l = new ArrayList<Rectangle>(3);
+
+
+ // highlight the region within the image
+ Rectangle r = displayCoordinates(new Rectangle(x, y, w, h));
+ l.add(r);
+
+ // add a 1 pixel line at the top and bottom that extends outside the image
+ l.add(new Rectangle(r.x, 0, 1, getHeight()));
+ l.add(new Rectangle(r.x + r.width, 0, 1, getHeight()));
+
+ return l;
+ }
+
+ private void setCursorForRegion(int x, int y, UpdateRegionInfo region) {
+ if (region != null) {
+ Cursor c = getCursor(x, y, region);
+ setCursor(c);
+ } else {
+ setCursor(Cursor.getDefaultCursor());
+ }
+ }
+
+ private Cursor getCursor(int x, int y, UpdateRegionInfo editRegion) {
+ Edge e;
+ int cursor = Cursor.DEFAULT_CURSOR;
+ switch (editRegion.region) {
+ case LEFT_PATCH:
+ case RIGHT_PADDING:
+ e = getClosestEdge(y, editRegion.segment);
+ cursor = (e == Edge.START) ? Cursor.N_RESIZE_CURSOR : Cursor.S_RESIZE_CURSOR;
+ break;
+ case TOP_PATCH:
+ case BOTTOM_PADDING:
+ e = getClosestEdge(x, editRegion.segment);
+ cursor = (e == Edge.START) ? Cursor.W_RESIZE_CURSOR : Cursor.E_RESIZE_CURSOR;
+ break;
+ default:
+ assert false : this.editRegion;
+ }
+
+ return Cursor.getPredefinedCursor(cursor);
+ }
+
+ /**
+ * Returns whether the horizontal or the vertical region should be updated based on the
+ * mouse pointer's location relative to the edges of either region. If no edge is close to
+ * the mouse pointer, then it returns null.
+ */
+ private UpdateRegionInfo pickUpdateRegion(int x, int y, UpdateRegionInfo vertical,
+ UpdateRegionInfo horizontal) {
+ if (vertical != null && vertical.segment != null) {
+ Edge e = getClosestEdge(y, vertical.segment);
+ if (e != Edge.NONE) {
+ return vertical;
}
}
- return c == null ? Cursor.getDefaultCursor() : c;
+
+ if (horizontal != null && horizontal.segment != null) {
+ Edge e = getClosestEdge(x, horizontal.segment);
+ if (e != Edge.NONE) {
+ return horizontal;
+ }
+ }
+
+ return null;
}
private enum Edge {
@@ -378,9 +717,9 @@ public class ImageViewer extends JComponent {
private static final int EDGE_DELTA = 1;
private Edge getClosestEdge(int x, Pair<Integer> range) {
- if (x - range.first <= EDGE_DELTA) {
+ if (Math.abs(x - range.first) <= EDGE_DELTA) {
return Edge.START;
- } else if (range.second - x <= EDGE_DELTA) {
+ } else if (Math.abs(range.second - x) <= EDGE_DELTA) {
return Edge.END;
} else {
return Edge.NONE;
@@ -432,12 +771,6 @@ public class ImageViewer extends JComponent {
}
private void startDrawingLine(int x, int y) {
- int left = (getWidth() - size.width) / 2;
- int top = helpPanel.getHeight() + (getHeight() - size.height) / 2;
-
- x = (x - left) / zoom;
- y = (y - top) / zoom;
-
int width = image.getWidth();
int height = image.getHeight();
if (((x == 0 || x == width - 1) && (y > 0 && y < height - 1))
@@ -461,12 +794,6 @@ public class ImageViewer extends JComponent {
return;
}
- int left = (getWidth() - size.width) / 2;
- int top = helpPanel.getHeight() + (getHeight() - size.height) / 2;
-
- x = (x - left) / zoom;
- y = (y - top) / zoom;
-
int width = image.getWidth();
int height = image.getHeight();
@@ -495,48 +822,95 @@ public class ImageViewer extends JComponent {
}
int color;
- switch (currentButton) {
- case MouseEvent.BUTTON1:
+ switch (currentMode) {
+ case PATCH:
color = PatchInfo.BLACK_TICK;
break;
- case MouseEvent.BUTTON2:
+ case LAYOUT_BOUND:
color = PatchInfo.RED_TICK;
break;
- case MouseEvent.BUTTON3:
+ case ERASE:
color = 0;
break;
default:
return;
}
- int x = lineFromX;
- int y = lineFromY;
+ setPatchData(color, lineFromX, lineFromY, lineToX, lineToY, true);
+
+ patchesChanged();
+ repaint();
+ }
+
+ /**
+ * Set the color of pixels on the line from (x1, y1) to (x2, y2) to given color.
+ * @param inclusive indicates whether the range is inclusive. If true, the last pixel (x2, y2)
+ * will be set to the given color as well.
+ */
+ private void setPatchData(int color, int x1, int y1, int x2, int y2, boolean inclusive) {
+ int x = x1;
+ int y = y1;
int dx = 0;
int dy = 0;
- if (lineToX != lineFromX)
- dx = lineToX > lineFromX ? 1 : -1;
- else if (lineToY != lineFromY)
- dy = lineToY > lineFromY ? 1 : -1;
+ if (x2 != x1) {
+ dx = x2 > x1 ? 1 : -1;
+ } else if (y2 != y1) {
+ dy = y2 > y1 ? 1 : -1;
+ }
+
+ while (x != x2 || y != y2) {
+ image.setRGB(x, y, color);
+ x += dx;
+ y += dy;
+ }
- do {
+ if (inclusive) {
image.setRGB(x, y, color);
+ }
+ }
- if (x == lineToX && y == lineToY)
+ /** Flushes current edit data to the image. */
+ private void flushEditPatchData(int color) {
+ int x1, y1, x2, y2;
+ x1 = x2 = y1 = y2 = 0;
+ int min = Math.min(editSegment.first, editSegment.second);
+ int max = Math.max(editSegment.first, editSegment.second);
+ switch (editRegion) {
+ case LEFT_PATCH:
+ x1 = x2 = 0;
+ y1 = min;
+ y2 = max;
break;
+ case RIGHT_PADDING:
+ x1 = x2 = image.getWidth() - 1;
+ y1 = min;
+ y2 = max;
+ break;
+ case TOP_PATCH:
+ x1 = min;
+ x2 = max;
+ y1 = y2 = 0;
+ break;
+ case BOTTOM_PADDING:
+ x1 = min;
+ x2 = max;
+ y1 = y2 = image.getHeight() - 1;
+ break;
+ default:
+ assert false : editRegion;
+ }
- x += dx;
- y += dy;
- } while (true);
+ setPatchData(color, x1, y1, x2, y2, false);
+ }
+ private void patchesChanged() {
updatePatchInfo();
notifyPatchesUpdated();
if (showBadPatches) {
corruptedPatches = CorruptPatch.findBadPatches(image, patchInfo);
}
-
- repaint();
}
private boolean checkLockedRegion(int x, int y) {
@@ -545,12 +919,6 @@ public class ImageViewer extends JComponent {
lastPositionX = x;
lastPositionY = y;
- int left = (getWidth() - size.width) / 2;
- int top = helpPanel.getHeight() + (getHeight() - size.height) / 2;
-
- x = (x - left) / zoom;
- y = (y - top) / zoom;
-
int width = image.getWidth();
int height = image.getHeight();
@@ -672,11 +1040,27 @@ public class ImageViewer extends JComponent {
}
g2 = (Graphics2D) g.create();
- Color c = new Color(0.5f, 0.5f, 0.5f, 0.5f);
- g2.setColor(c);
- for (Rectangle r: highlightRegions) {
+ g2.setColor(HIGHLIGHT_REGION_COLOR);
+ for (Rectangle r: hoverHighlightRegions) {
g2.fillRect(r.x, r.y, r.width, r.height);
}
+
+ if (!hoverHighlightRegions.isEmpty()) {
+ setToolTipText(toolTipText);
+ } else {
+ setToolTipText(null);
+ }
+
+ if (isEditMode && editRegion != null) {
+ g2.setColor(HIGHLIGHT_REGION_COLOR);
+ for (Rectangle r: editHighlightRegions) {
+ g2.fillRect(r.x, r.y, r.width, r.height);
+ }
+ g2.setColor(Color.BLACK);
+ g2.fillRect(editPatchRegion.x, editPatchRegion.y,
+ editPatchRegion.width, editPatchRegion.height);
+ }
+
g2.dispose();
}
@@ -717,21 +1101,24 @@ public class ImageViewer extends JComponent {
}
void setZoom(int value) {
+ zoom = value;
+ updateSize();
+ if (!size.equals(getSize())) {
+ setSize(size);
+ container.validate();
+ repaint();
+ }
+ }
+
+ private void updateSize() {
int width = image.getWidth();
int height = image.getHeight();
- zoom = value;
if (size.height == 0 || (getHeight() - size.height) == 0) {
size.setSize(width * zoom, height * zoom + helpPanel.getHeight());
} else {
size.setSize(width * zoom, height * zoom);
}
-
- if (!size.equals(getSize())) {
- setSize(size);
- container.validate();
- repaint();
- }
}
void setPatchesVisible(boolean visible) {
diff --git a/draw9patch/src/main/java/com/android/draw9patch/ui/PatchInfo.java b/draw9patch/src/main/java/com/android/draw9patch/ui/PatchInfo.java
index 9efcdda..0cca67b 100644
--- a/draw9patch/src/main/java/com/android/draw9patch/ui/PatchInfo.java
+++ b/draw9patch/src/main/java/com/android/draw9patch/ui/PatchInfo.java
@@ -45,9 +45,15 @@ public class PatchInfo {
/** Bounds of horizontal patch markers. */
public final List<Pair<Integer>> horizontalPatchMarkers;
+ /** Bounds of horizontal padding markers. */
+ public final List<Pair<Integer>> horizontalPaddingMarkers;
+
/** Bounds of vertical patch markers. */
public final List<Pair<Integer>> verticalPatchMarkers;
+ /** Bounds of vertical padding markers. */
+ public final List<Pair<Integer>> verticalPaddingMarkers;
+
public final boolean verticalStartWithPatch;
public final boolean horizontalStartWithPatch;
@@ -98,9 +104,11 @@ public class PatchInfo {
column = GraphicsUtilities.getPixels(image, width - 1, 0, 1, height, column);
top = PatchInfo.getPatches(row);
+ horizontalPaddingMarkers = top.patches;
horizontalPadding = getPadding(top.fixed);
left = PatchInfo.getPatches(column);
+ verticalPaddingMarkers = left.patches;
verticalPadding = getPadding(left.fixed);
}