diff --git a/src/com/jpexs/decompiler/flash/gui/DisplayPoint.java b/src/com/jpexs/decompiler/flash/gui/DisplayPoint.java new file mode 100644 index 000000000..fb928ff08 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/DisplayPoint.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui; + +/** + * + * @author JPEXS + */ +public class DisplayPoint { + public int x; + public int y; + public boolean onPath; + + public DisplayPoint(DisplayPoint src) { + this(src.x, src.y, src.onPath); + } + + public DisplayPoint(int x, int y) { + this(x, y, true); + } + public DisplayPoint(int x, int y, boolean onPath) { + this.x = x; + this.y = y; + this.onPath = onPath; + } + + @Override + public String toString() { + return "["+x+","+y+"]"; + } + +} diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index 19c189b14..89e146cca 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -231,6 +231,8 @@ public final class ImagePanel extends JPanel implements MediaDisplay { private static Cursor selectCursor; private static Cursor shearXCursor; private static Cursor shearYCursor; + private static Cursor movePointCursor; + private static Cursor defaultCursor; private Point2D offsetPoint = new Point2D.Double(0, 0); @@ -253,9 +255,14 @@ public final class ImagePanel extends JPanel implements MediaDisplay { private DepthState depthStateUnderCursor = null; private List placeObjectSelectedListeners = new ArrayList<>(); - + private Point[] hilightedEdge = null; + + private List hilightedPoints = null; + private List pointsUnderCursor = new ArrayList<>(); + private List pointsUnderCursorValues = new ArrayList<>(); + private int hilightEdgeColorStep = 10; private int hilightEdgeColor = 0; @@ -268,21 +275,44 @@ public final class ImagePanel extends JPanel implements MediaDisplay { private SerializableImage imgPlay = null; private List boundsChangeListeners = new ArrayList<>(); + + private List pointUpdateListeners = new ArrayList<>(); + + public void addPointUpdateListener(PointUpdateListener listener) { + pointUpdateListeners.add(listener); + } + public void removePointUpdateListener(PointUpdateListener listener) { + pointUpdateListeners.remove(listener); + } + + private void firePointsUpdated(List points) { + for(PointUpdateListener listener:pointUpdateListeners) { + listener.pointsUpdated(points); + } + } + + private void firePointAdded(int position, DisplayPoint point) { + for(PointUpdateListener listener:pointUpdateListeners) { + listener.pointAdded(position, point); + } + } + + private void firePointRemoved(int position) { + for(PointUpdateListener listener:pointUpdateListeners) { + listener.pointRemoved(position); + } + } + + public void setHilightedPoints(List hilightedPoints) { + this.hilightedPoints = hilightedPoints; + redraw(); + } public void setHilightedEdge(Point[] hilightedEdge) { this.hilightedEdge = hilightedEdge; hilightEdgeColor = 255; - /*if (hilightedEdge == null) { - System.out.println("set no hilight edge"); - } else { - String s = ""; - for(Point p:hilightedEdge) { - s+="["+p.x+","+p.y+"]"; - } - System.out.println("set hilight edge " + s); - }*/ redraw(); - } + } public void addBoundsChangeListener(BoundsChangeListener listener) { boundsChangeListeners.add(listener); @@ -371,7 +401,9 @@ public final class ImagePanel extends JPanel implements MediaDisplay { selectCursor = loadCursor("select", 0, 0); shearXCursor = loadCursor("shear_x", 9, 5); shearYCursor = loadCursor("shear_y", 5, 9); - + movePointCursor = loadCursor("move_point", 0, 0); + defaultCursor = loadCursor("default", 0, 0); + } catch (IOException ex) { Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex); } @@ -481,6 +513,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { this.freeTransformDepth = depth; } hilightedEdge = null; + hilightedPoints = null; registrationPoint = null; calculateFreeTransform(); hideMouseSelection(); @@ -637,7 +670,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { g2.drawImage(img.getBufferedImage(), x, y, x + img.getWidth(), y + img.getHeight(), 0, 0, img.getWidth(), img.getHeight(), null); - if (hilightedEdge != null) { + if (hilightedEdge != null || hilightedPoints != null) { hilightEdgeColor += hilightEdgeColorStep; if (hilightEdgeColor < 100 || hilightEdgeColor > 255) { hilightEdgeColorStep = -hilightEdgeColorStep; @@ -645,43 +678,59 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } RECT timRect = timelined.getRect(); double zoomDouble = zoom.fit ? getZoomToFit() : zoom.value; - AffineTransform t = new AffineTransform(); - t.translate(offsetPoint.getX() - (int) (timRect.Xmin * zoomDouble / SWF.unitDivisor) - , offsetPoint.getY() - (int) (timRect.Ymin * zoomDouble / SWF.unitDivisor)); - t.scale(1/SWF.unitDivisor, 1/SWF.unitDivisor); + AffineTransform t = new AffineTransform(); + t.translate(offsetPoint.getX() - (int) (timRect.Xmin * zoomDouble / SWF.unitDivisor), + offsetPoint.getY() - (int) (timRect.Ymin * zoomDouble / SWF.unitDivisor)); + t.scale(1 / SWF.unitDivisor, 1 / SWF.unitDivisor); t.scale(zoomDouble, zoomDouble); AffineTransform oldTransform = g2.getTransform(); g2.setTransform(t); - g2.setStroke(new BasicStroke((float) (SWF.unitDivisor * 6 / zoomDouble))); - g2.setPaint(new Color(hilightEdgeColor, hilightEdgeColor, hilightEdgeColor)); - Point[] edge = hilightedEdge; - GeneralPath path = new GeneralPath(); - if (edge.length == 2) { - path.moveTo(edge[0].x, edge[0].y); - path.lineTo(edge[1].x, edge[1].y); + + if (hilightedEdge != null) { + g2.setStroke(new BasicStroke((float) (SWF.unitDivisor * 6 / zoomDouble))); + g2.setPaint(new Color(hilightEdgeColor, hilightEdgeColor, hilightEdgeColor)); + Point[] edge = hilightedEdge; + GeneralPath path = new GeneralPath(); + if (edge.length == 2) { + path.moveTo(edge[0].x, edge[0].y); + path.lineTo(edge[1].x, edge[1].y); + } + if (edge.length == 3) { + path.moveTo(edge[0].x, edge[0].y); + path.quadTo(edge[1].x, edge[1].y, edge[2].x, edge[2].y); + } + if (edge.length == 1) { + double crossSize = (SWF.unitDivisor * 10 / zoomDouble); + path.moveTo(edge[0].x - crossSize, edge[0].y); + path.lineTo(edge[0].x + crossSize, edge[0].y); + path.moveTo(edge[0].x, edge[0].y - crossSize); + path.lineTo(edge[0].x, edge[0].y + crossSize); + } + g2.draw(path); + + double pointSize = SWF.unitDivisor * 4 / zoomDouble; + + g2.setPaint(Color.red); + g2.fill(new Ellipse2D.Double(edge[edge.length - 1].x - pointSize, edge[edge.length - 1].y - pointSize, pointSize * 2, pointSize * 2)); + g2.setPaint(Color.green); + g2.fill(new Ellipse2D.Double(edge[0].x - pointSize, edge[0].y - pointSize, pointSize * 2, pointSize * 2)); } - if (edge.length == 3) { - path.moveTo(edge[0].x, edge[0].y); - path.quadTo(edge[1].x, edge[1].y, edge[2].x, edge[2].y); - } - if (edge.length == 1) { - double crossSize = (SWF.unitDivisor * 10 / zoomDouble); - path.moveTo(edge[0].x - crossSize, edge[0].y); - path.lineTo(edge[0].x + crossSize, edge[0].y); - path.moveTo(edge[0].x, edge[0].y - crossSize); - path.lineTo(edge[0].x, edge[0].y + crossSize); - } - g2.draw(path); - double pointSize = SWF.unitDivisor * 4 / zoomDouble; - - g2.setPaint(Color.red); - g2.fill(new Ellipse2D.Double(edge[edge.length - 1].x - pointSize, edge[edge.length - 1].y - pointSize, pointSize * 2, pointSize * 2)); - g2.setPaint(Color.green); - g2.fill(new Ellipse2D.Double(edge[0].x - pointSize, edge[0].y - pointSize, pointSize * 2, pointSize * 2)); + List points = hilightedPoints; + if (points != null) { + g2.setStroke(new BasicStroke((float) (SWF.unitDivisor * 1 / zoomDouble))); + double pointSize = SWF.unitDivisor * 4 / zoomDouble; + for (DisplayPoint p:points) { + Rectangle2D pointRect = new Rectangle2D.Double(p.x - pointSize, p.y - pointSize, pointSize * 2, pointSize * 2); + g2.setPaint(Color.white); + g2.fill(pointRect); + g2.setPaint(Color.black); + g2.draw(pointRect); + } + } g2.setTransform(oldTransform); } - if (!(timelined instanceof SWF) && freeTransformDepth > -1) { + if (!(timelined instanceof SWF) && (freeTransformDepth > -1 || hilightedPoints != null)) { int axisX = 0; int axisY = 0; @@ -800,6 +849,14 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } mouseMoved(e); //to correctly calculate mode, because moseMoved event is not called during dragging setDragStart(e.getPoint()); + + List newPointsUnderCursorValues = new ArrayList<>(); + for (int i:pointsUnderCursor) { + newPointsUnderCursorValues.add(new DisplayPoint(hilightedPoints.get(i))); + } + + pointsUnderCursorValues = newPointsUnderCursorValues; + if (!autoPlayed) { Configuration.autoPlayPreviews.set(true); @@ -858,6 +915,26 @@ public final class ImagePanel extends JPanel implements MediaDisplay { @Override public void mouseDragged(MouseEvent e) { + List points = hilightedPoints; + if (dragStart != null && points != null) { + if (!pointsUnderCursor.isEmpty()) { + + for (int i = 0; i < pointsUnderCursor.size(); i++) { + int pointIndex = pointsUnderCursor.get(i); + DisplayPoint pointStart = pointsUnderCursorValues.get(i); + Point2D dragEnd = e.getPoint(); + Point2D startTransformPoint = toTransformPoint(dragStart); + Point2D endTransformPoint = toTransformPoint(dragEnd); + Point2D delta = new Point2D.Double(endTransformPoint.getX() - startTransformPoint.getX(), endTransformPoint.getY() - startTransformPoint.getY()); + //System.out.println("delta = "+delta); + DisplayPoint newPoint = new DisplayPoint((int)Math.round(pointStart.x + delta.getX()), (int)Math.round(pointStart.y + delta.getY()), pointStart.onPath); + points.set(pointIndex, newPoint); + } + firePointsUpdated(points); + repaint(); + return; + } + } if (dragStart != null && allowMove && mode == Cursor.DEFAULT_CURSOR) { Point2D dragEnd = e.getPoint(); Point2D delta = new Point2D.Double(dragEnd.getX() - dragStart.getX(), dragEnd.getY() - dragStart.getY()); @@ -1347,6 +1424,32 @@ public final class ImagePanel extends JPanel implements MediaDisplay { @Override public void mouseMoved(MouseEvent e) { + List points = hilightedPoints; + if (points != null) { + Cursor cursor = defaultCursor; + int maxDistance = 5; + List newPointsUnderCursor = new ArrayList<>(); + for (int i = 0; i < points.size(); i++) { + DisplayPoint p = points.get(i); + Point2D ip = toImagePoint(new Point2D.Double(p.x,p.y)); + int ex = e.getX(); + int ey = e.getY(); + if (ex > ip.getX() - maxDistance && ex < ip.getX() + maxDistance) { + if (ey > ip.getY() - maxDistance && ey < ip.getY() + maxDistance) { + cursor = movePointCursor; + newPointsUnderCursor.add(i); + } + } + } + if (dragStart == null) { + pointsUnderCursor = newPointsUnderCursor; + } + + if (getCursor() != cursor) { + setCursor(cursor); + } + return; + } if (freeTransformDepth > -1) { if (bounds == null) { return; @@ -1983,6 +2086,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { this.mutable = mutable; depthStateUnderCursor = null; hilightedEdge = null; + hilightedPoints = null; this.showObjectsUnderCursor = showObjectsUnderCursor; this.registrationPointPosition = RegistrationPointPosition.CENTER; centerImage(); @@ -2019,6 +2123,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { stillFrame = true; zoomAvailable = false; hilightedEdge = null; + hilightedPoints = null; iconPanel.setImg(image); drawReady = true; @@ -2690,7 +2795,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } else { debugLabel.setText(ret.toString()); } - if (freeTransformDepth == -1) { + if (freeTransformDepth == -1 && hilightedPoints == null) { Cursor newCursor; if (iconPanel.isAltDown()) { if (depthStateUnderCursor == null) { diff --git a/src/com/jpexs/decompiler/flash/gui/PointUpdateListener.java b/src/com/jpexs/decompiler/flash/gui/PointUpdateListener.java new file mode 100644 index 000000000..a46a094a6 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/PointUpdateListener.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2022 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui; + +import java.util.List; + +/** + * + * @author JPEXS + */ +public interface PointUpdateListener { + public void pointsUpdated(List points); + + public void pointAdded(int position, DisplayPoint point); + + public void pointRemoved(int position); +} diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index 35b51e4e9..a76f9e7fd 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -64,6 +64,7 @@ import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import com.jpexs.helpers.Helper; import com.jpexs.helpers.SerializableImage; import java.awt.BorderLayout; import java.awt.CardLayout; @@ -220,6 +221,8 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel private JButton displayEditSaveButton; private JButton displayEditCancelButton; + + private JButton displayEditEditPointsButton; private JPanel parametersPanel; @@ -241,9 +244,12 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel private HexView unknownHexView; - private final int PLACE_EDIT_TRANSFORM = 1; - private final int PLACE_EDIT_RAW = 2; - private int placeEditMode = PLACE_EDIT_RAW; + private final int EDIT_TRANSFORM = 1; + private final int EDIT_RAW = 2; + private final int EDIT_POINTS = 3; + private int displayEditMode = EDIT_RAW; + + private List oldShapeRecords; //used only for flash player private TreeItem currentItem; @@ -731,6 +737,58 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel } }); } + + displayEditImagePanel.addPointUpdateListener(new PointUpdateListener() { + @Override + public void pointsUpdated(List points) { + ShapeTag shape = (ShapeTag) displayEditTag; + int pointsPos = 0; + int x = 0; + int y = 0; + for(int i = 0; i < shape.shapes.shapeRecords.size(); i++) { + SHAPERECORD rec = shape.shapes.shapeRecords.get(i); + if (rec instanceof StyleChangeRecord) { + StyleChangeRecord scr = (StyleChangeRecord) rec; + if (scr.stateMoveTo) { + scr.moveDeltaX = points.get(pointsPos).x; + scr.moveDeltaY = points.get(pointsPos).y; + pointsPos++; + } + } + if (rec instanceof StraightEdgeRecord) { + StraightEdgeRecord ser = (StraightEdgeRecord) rec; + ser.generalLineFlag = true; + ser.deltaX = points.get(pointsPos).x - x; + ser.deltaY = points.get(pointsPos).y - y; + pointsPos += 1; + ser.simplify(); + } + if (rec instanceof CurvedEdgeRecord) { + CurvedEdgeRecord cer = (CurvedEdgeRecord) rec; + cer.controlDeltaX = points.get(pointsPos).x - x; + cer.controlDeltaY = points.get(pointsPos).y - y; + cer.anchorDeltaX = points.get(pointsPos + 1).x - points.get(pointsPos).x; + cer.anchorDeltaY = points.get(pointsPos + 1).y - points.get(pointsPos).y; + pointsPos += 2; + } + x = rec.changeX(x); + y = rec.changeY(y); + } + shape.getSwf().clearShapeCache(); + displayEditImagePanel.repaint(); + } + + @Override + public void pointAdded(int position, DisplayPoint point) { + } + + @Override + public void pointRemoved(int position) { + + } + + }); + displayEditTransformPanel = new TransformPanel(displayEditImagePanel); //imagePanel.setLoop(Configuration.loopMedia.get()); previewCnt.add(displayEditTransformSplitPane = new JPersistentSplitPane( @@ -837,6 +895,10 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel displayEditCancelButton.setMargin(new Insets(3, 3, 3, 10)); displayEditCancelButton.addActionListener(this::cancelDisplayEditTagButtonActionPerformed); + displayEditEditPointsButton = new JButton(mainPanel.translate("button.edit.points"), View.getIcon("edit16")); + displayEditEditPointsButton.setMargin(new Insets(3, 3, 3, 10)); + displayEditEditPointsButton.addActionListener(this::editPointsDisplayEditTagButtonActionPerformed); + replaceShapeButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("replaceshape16")); replaceShapeButton.setMargin(new Insets(3, 3, 3, 10)); replaceShapeButton.addActionListener(new ActionListener() { @@ -874,6 +936,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel placeTagButtonsPanel.add(displayEditEditButton); placeTagButtonsPanel.add(displayEditSaveButton); placeTagButtonsPanel.add(displayEditCancelButton); + placeTagButtonsPanel.add(displayEditEditPointsButton); placeTagButtonsPanel.add(replaceShapeButton); placeTagButtonsPanel.add(replaceShapeUpdateBoundsButton); return placeTagButtonsPanel; @@ -1225,6 +1288,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel replaceImageButton.setVisible(showImage); replaceImageAlphaButton.setVisible(showAlpha); replaceShapeButton.setVisible(showShape); + displayEditEditPointsButton.setVisible(showShape); replaceShapeUpdateBoundsButton.setVisible(showShape); replaceSoundButton.setVisible(showSound); replaceMovieButton.setVisible(showMovie); @@ -1460,7 +1524,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel } private void saveDisplayEditTag(boolean refreshTree) { - if (placeEditMode == PLACE_EDIT_TRANSFORM) { + if (displayEditMode == EDIT_TRANSFORM) { Matrix matrix = displayEditImagePanel.getNewMatrix(); if (displayEditTag instanceof PlaceObjectTypeTag) { PlaceObjectTypeTag placeTag = (PlaceObjectTypeTag) displayEditTag; @@ -1550,9 +1614,14 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel displayEditTransformScrollPane.setVisible(false); displayEditGenericPanel.setVisible(true); } + if (displayEditMode == EDIT_POINTS) { + displayEditImagePanel.setHilightedPoints(null); + displayEditTag.setModified(true); + oldShapeRecords = null; + } Tag hilightTag = null; SWF swf = null; - if (placeEditMode == PLACE_EDIT_RAW) { + if (displayEditMode == EDIT_RAW) { if (displayEditGenericPanel.save()) { Tag tag = displayEditGenericPanel.getTag(); swf = tag.getSwf(); @@ -1565,6 +1634,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel if (displayEditTag instanceof ShapeTag) { replaceShapeButton.setVisible(true); replaceShapeUpdateBoundsButton.setVisible(true); + displayEditEditPointsButton.setVisible(true); } displayEditTransformButton.setVisible(true); @@ -1581,7 +1651,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel displayEditCancelButton.setVisible(false); } - if (placeEditMode == PLACE_EDIT_RAW && refreshTree && swf != null) { + if (displayEditMode == EDIT_RAW && refreshTree && swf != null) { mainPanel.refreshTree(swf); } mainPanel.clearEditingStatus(); @@ -1589,8 +1659,8 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel if (hilightTag != null) { mainPanel.setTagTreeSelectedNode(mainPanel.getCurrentTree(), hilightTag); } - if (placeEditMode == PLACE_EDIT_TRANSFORM) { - placeEditMode = PLACE_EDIT_RAW; + if (displayEditMode == EDIT_TRANSFORM) { + displayEditMode = EDIT_RAW; } } @@ -1599,7 +1669,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel } private void editDisplayEditTagButtonActionPerformed(ActionEvent evt) { - placeEditMode = PLACE_EDIT_RAW; + displayEditMode = EDIT_RAW; displayEditGenericPanel.setEditMode(true, displayEditTag); displayEditEditButton.setVisible(false); displayEditTransformButton.setVisible(false); @@ -1607,6 +1677,55 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel displayEditCancelButton.setVisible(true); replaceShapeButton.setVisible(false); replaceShapeUpdateBoundsButton.setVisible(false); + displayEditEditPointsButton.setVisible(false); + mainPanel.setEditingStatus(); + } + + private void editPointsDisplayEditTagButtonActionPerformed(ActionEvent evt) { + displayEditMode = EDIT_POINTS; + displayEditGenericPanel.setVisible(false); + displayEditEditButton.setVisible(false); + displayEditTransformButton.setVisible(false); + displayEditSaveButton.setVisible(true); + displayEditCancelButton.setVisible(true); + replaceShapeButton.setVisible(false); + replaceShapeUpdateBoundsButton.setVisible(false); + displayEditEditPointsButton.setVisible(false); + + + ShapeTag shape = (ShapeTag) displayEditTag; + + oldShapeRecords = Helper.deepCopy(shape.shapes.shapeRecords); + int x = 0; + int y = 0; + + List points = new ArrayList<>(); + for (SHAPERECORD rec:shape.shapes.shapeRecords) { + if (rec instanceof StraightEdgeRecord) { + StraightEdgeRecord ser = (StraightEdgeRecord) rec; + DisplayPoint point = new DisplayPoint(x + ser.deltaX, y + ser.deltaY); + points.add(point); + } + if (rec instanceof CurvedEdgeRecord) { + CurvedEdgeRecord cer = (CurvedEdgeRecord) rec; + DisplayPoint controlPoint = new DisplayPoint(x + cer.controlDeltaX, y + cer.controlDeltaY, false); + DisplayPoint anchorPoint = new DisplayPoint(x + cer.controlDeltaX + cer.anchorDeltaX, y + cer.controlDeltaY + cer.anchorDeltaY); + points.add(controlPoint); + points.add(anchorPoint); + } + if (rec instanceof StyleChangeRecord) { + StyleChangeRecord scr = (StyleChangeRecord) rec; + if (scr.stateMoveTo) { + DisplayPoint point = new DisplayPoint(scr.moveDeltaX, scr.moveDeltaY); + points.add(point); + } + } + + x = rec.changeX(x); + y = rec.changeY(y); + } + displayEditImagePanel.setHilightedPoints(points); + mainPanel.setEditingStatus(); } @@ -1615,7 +1734,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel if (item == null) { return; } - placeEditMode = PLACE_EDIT_TRANSFORM; + displayEditMode = EDIT_TRANSFORM; displayEditGenericPanel.setVisible(false); displayEditImagePanel.selectDepth(-1); @@ -1628,6 +1747,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel replaceShapeButton.setVisible(false); replaceShapeUpdateBoundsButton.setVisible(false); + displayEditEditPointsButton.setVisible(false); if (Configuration.editorMode.get()) { displayEditSaveButton.setEnabled(false); @@ -1869,7 +1989,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel } private void cancelDisplayEditTagButtonActionPerformed(ActionEvent evt) { - if (placeEditMode == PLACE_EDIT_TRANSFORM) { + if (displayEditMode == EDIT_TRANSFORM) { if (displayEditTag instanceof PlaceObjectTypeTag) { PlaceObjectTypeTag place = (PlaceObjectTypeTag) displayEditTag; displayEditImagePanel.selectDepth(place.getDepth()); @@ -1879,9 +1999,18 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel displayEditTransformScrollPane.setVisible(false); displayEditGenericPanel.setVisible(true); } + + if (displayEditMode == EDIT_POINTS) { + displayEditImagePanel.setHilightedPoints(null); + ShapeTag shape = (ShapeTag) displayEditTag; + shape.shapes.shapeRecords = oldShapeRecords; + shape.getSwf().clearShapeCache(); + displayEditImagePanel.repaint(); + displayEditGenericPanel.setVisible(true); + } if (Configuration.editorMode.get()) { - if (placeEditMode == PLACE_EDIT_RAW) { + if (displayEditMode == EDIT_RAW) { displayEditGenericPanel.setEditMode(true, null); } displayEditEditButton.setVisible(false); @@ -1890,7 +2019,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel displayEditCancelButton.setVisible(true); displayEditCancelButton.setEnabled(false); } else { - if (placeEditMode == PLACE_EDIT_RAW) { + if (displayEditMode == EDIT_RAW) { displayEditGenericPanel.setEditMode(false, null); } displayEditEditButton.setVisible(true); @@ -1900,14 +2029,15 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel if (displayEditTag instanceof ShapeTag) { replaceShapeButton.setVisible(true); - replaceShapeUpdateBoundsButton.setVisible(true); + replaceShapeUpdateBoundsButton.setVisible(true); + displayEditEditPointsButton.setVisible(true); } mainPanel.clearEditingStatus(); displayEditTransformButton.setVisible(true); - if (placeEditMode == PLACE_EDIT_TRANSFORM) { - placeEditMode = PLACE_EDIT_RAW; + if (displayEditMode == EDIT_TRANSFORM) { + displayEditMode = EDIT_RAW; } } diff --git a/src/com/jpexs/decompiler/flash/gui/graphics/cursors/default.png b/src/com/jpexs/decompiler/flash/gui/graphics/cursors/default.png new file mode 100644 index 000000000..a1adae238 Binary files /dev/null and b/src/com/jpexs/decompiler/flash/gui/graphics/cursors/default.png differ diff --git a/src/com/jpexs/decompiler/flash/gui/graphics/cursors/move_point.png b/src/com/jpexs/decompiler/flash/gui/graphics/cursors/move_point.png new file mode 100644 index 000000000..df67227a9 Binary files /dev/null and b/src/com/jpexs/decompiler/flash/gui/graphics/cursors/move_point.png differ diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index c92268376..54b9da5d2 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -1107,4 +1107,7 @@ message.info.importSounds2 = During importing sounds, you need to select a FOLDE If there exist "sounds" folder inside, it is selected instead.\r\n \ The best way to get the structure right is to export sounds in current SWF file first. -import.sound.result = %count% sounds imported. \ No newline at end of file +import.sound.result = %count% sounds imported. + +#after 18.2.1 +button.edit.points = Edit points \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties index 7c6c35b58..b6f44da28 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties @@ -1092,4 +1092,7 @@ message.info.importSounds2 = B\u011bhem importu zvuk\u016f mus\u00edte vybrat SL Pokud je uvnit\u0159 slo\u017eka "sounds", pak je vybr\u00e1na m\u00edsto n\u00ed.\r\n \ Nejlep\u0161\u00ed zp\u016fsob jak m\u00edt tuto strukturu spr\u00e1vn\u011b je nejprve exportovat zvuky v aktu\u00e1ln\u00edm SWF souboru. -import.sound.result = %count% zvuk\u016f importov\u00e1no. \ No newline at end of file +import.sound.result = %count% zvuk\u016f importov\u00e1no. + +#after 18.2.1 +button.edit.points = Editovat body \ No newline at end of file