diff options
Diffstat (limited to 'draw9patch')
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); } |