Fixed #1982 Slow calculation of large shape outlines - now use only rectangles for large shapes

This commit is contained in:
Jindra Petřík
2023-03-03 18:24:58 +01:00
parent 8116df7a7b
commit f67bb6753f
14 changed files with 77 additions and 34 deletions

View File

@@ -116,7 +116,7 @@ public class DefineScalingGridTag extends Tag implements CharacterIdTag {
}
public RECT getRect() {
Shape s = getOutline(0, 0, 0, new RenderContext(), new Matrix(), new Matrix(), true, new ExportRectangle(0, 0, 1, 1)/*?*/, 1);
Shape s = getOutline(true, 0, 0, 0, new RenderContext(), new Matrix(), new Matrix(), true, new ExportRectangle(0, 0, 1, 1)/*?*/, 1);
if (s == null) {
return null;
}
@@ -124,7 +124,7 @@ public class DefineScalingGridTag extends Tag implements CharacterIdTag {
return new RECT(r.x, r.x + r.width, r.y, r.y + r.height);
}
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, Matrix prevTransform, boolean stroked, ExportRectangle viewRect, double unzoom) {
public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, Matrix prevTransform, boolean stroked, ExportRectangle viewRect, double unzoom) {
CharacterTag ct = swf.getCharacter(characterId);
if (ct == null) {
return null;
@@ -135,7 +135,7 @@ public class DefineScalingGridTag extends Tag implements CharacterIdTag {
double[] coords = new double[6];
DrawableTag dt = (DrawableTag) ct;
Shape path = dt.getOutline(frame, time, ratio, renderContext, transformation, stroked, viewRect, unzoom);
Shape path = dt.getOutline(fast, frame, time, ratio, renderContext, transformation, stroked, viewRect, unzoom);
PathIterator iterator = path.getPathIterator(new AffineTransform());
GeneralPath gp = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
ExportRectangle boundsRect = new ExportRectangle(dt.getRect());

View File

@@ -400,8 +400,8 @@ public class DefineSpriteTag extends DrawableTag implements Timelined {
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
return getTimeline().getOutline(frame, time, renderContext, transformation, stroked, viewRect, unzoom);
public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
return getTimeline().getOutline(fast, frame, time, renderContext, transformation, stroked, viewRect, unzoom);
}
@Override

View File

@@ -325,7 +325,7 @@ public class DefineVideoStreamTag extends DrawableTag implements BoundedTag, Tim
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
return transformation.toTransform().createTransformedShape(new Rectangle2D.Double(0, 0, width * SWF.unitDivisor, height * SWF.unitDivisor));
}

View File

@@ -88,8 +88,8 @@ public abstract class ButtonTag extends DrawableTag implements Timelined {
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
return getTimeline().getOutline(frame, time, renderContext, transformation, stroked, viewRect, unzoom);
public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
return getTimeline().getOutline(fast, frame, time, renderContext, transformation, stroked, viewRect, unzoom);
}
@Override

View File

@@ -44,8 +44,21 @@ public abstract class DrawableTag extends CharacterTag implements BoundedTag {
public abstract int getUsedParameters();
public abstract Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom);
/**
* Calculates drawable outline.
* @param fast When the structure is large, can approximate to rectangles instead of being slow.
* @param frame
* @param time
* @param ratio
* @param renderContext
* @param transformation
* @param stroked
* @param viewRect
* @param unzoom
* @return
*/
public abstract Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom);
public abstract void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, SerializableImage fullImage, boolean isClip, Matrix transformation, Matrix prevTransformation, Matrix absoluteTransformation, Matrix fullTransformation, ColorTransform colorTransform, double unzoom, boolean sameImage, ExportRectangle viewRect, boolean scaleStrokes, int drawMode, int blendMode, boolean canUseSmoothing);
public abstract void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException;

View File

@@ -427,7 +427,7 @@ public abstract class FontTag extends DrawableTag implements AloneTag {
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
RECT r = getRect();
return new Area(new Rectangle(r.Xmin, r.Ymin, r.getWidth(), r.getHeight()));
}

View File

@@ -249,8 +249,8 @@ public abstract class ImageTag extends DrawableTag {
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
return transformation.toTransform().createTransformedShape(getShape(1).getOutline(1, swf, stroked));
public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
return transformation.toTransform().createTransformedShape(getShape(1).getOutline(fast, 1, swf, stroked));
}
@Override

View File

@@ -349,8 +349,8 @@ public abstract class MorphShapeTag extends DrawableTag {
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
return transformation.toTransform().createTransformedShape(getShapeAtRatio(ratio).getOutline(getShapeNum() == 2 ? 4 : 1, swf, stroked));
public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
return transformation.toTransform().createTransformedShape(getShapeAtRatio(ratio).getOutline(fast, getShapeNum() == 2 ? 4 : 1, swf, stroked));
}
@Override

View File

@@ -183,8 +183,8 @@ public abstract class ShapeTag extends DrawableTag implements LazyObject {
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
return transformation.toTransform().createTransformedShape(getShapes().getOutline(getShapeNum(), swf, stroked));
public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
return transformation.toTransform().createTransformedShape(getShapes().getOutline(fast, getShapeNum(), swf, stroked));
}
@Override

View File

@@ -787,7 +787,7 @@ public abstract class TextTag extends DrawableTag {
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
public Shape getOutline(boolean fast, int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
RECT r = getBounds();
Shape shp = new Rectangle.Double(r.Xmin, r.Ymin, r.getWidth(), r.getHeight());
return transformation.toTransform().createTransformedShape(shp); //TODO: match character shapes (?)

View File

@@ -813,7 +813,7 @@ public class Timeline {
int dy = (int) (viewRect.yMin * unzoom);
Point cursorPositionInView = new Point((int) Math.round(renderContext.cursorPosition.x * unzoom) - dx, (int) Math.round(renderContext.cursorPosition.y * unzoom) - dy);
Shape buttonShape = drawable.getOutline(ButtonTag.FRAME_HITTEST, dtime, ratio, renderContext, absMat, true, viewRect, unzoom);
Shape buttonShape = ((ButtonTag) drawable).getOutline(true, ButtonTag.FRAME_HITTEST, dtime, ratio, renderContext, absMat, true, viewRect, unzoom);
if (buttonShape.contains(cursorPositionInView)) {
renderContext.mouseOverButton = (ButtonTag) drawable;
if (renderContext.mouseButton > 0) {
@@ -980,7 +980,7 @@ public class Timeline {
renderContext.stateUnderCursor.add(layer);
}
} else if (absMat.transform(new ExportRectangle(boundRect)).contains(cursorPositionInView)) {
Shape shape = drawable.getOutline(dframe, dtime, layer.ratio, renderContext, absMat, true, viewRect, unzoom);
Shape shape = drawable.getOutline(true, dframe, dtime, layer.ratio, renderContext, absMat, true, viewRect, unzoom);
if (shape.contains(cursorPositionInView)) {
renderContext.stateUnderCursor.add(layer);
}
@@ -991,7 +991,7 @@ public class Timeline {
Graphics2D g2 = (Graphics2D) renderContext.borderImage.getGraphics();
g2.setPaint(Color.red);
g2.setStroke(new BasicStroke(2));
Shape shape = drawable.getOutline(dframe, dtime, layer.ratio, renderContext, absMat.preConcatenate(Matrix.getScaleInstance(1 / SWF.unitDivisor)), true, viewRect, unzoom);
Shape shape = drawable.getOutline(true, dframe, dtime, layer.ratio, renderContext, absMat.preConcatenate(Matrix.getScaleInstance(1 / SWF.unitDivisor)), true, viewRect, unzoom);
g2.draw(shape);
}
if (!(sameImage && canUseSameImage)) {
@@ -1341,7 +1341,7 @@ public class Timeline {
}
}
public Shape getOutline(int frame, int time, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
public Shape getOutline(boolean fast, int frame, int time, RenderContext renderContext, Matrix transformation, boolean stroked, ExportRectangle viewRect, double unzoom) {
Frame fr = getFrame(frame);
Area area = new Area();
Stack<Clip> clips = new Stack<>();
@@ -1378,7 +1378,7 @@ public class Timeline {
dframe = ButtonTag.FRAME_UP;
if (renderContext.cursorPosition != null) {
ButtonTag buttonTag = (ButtonTag) character;
Shape buttonShape = buttonTag.getOutline(ButtonTag.FRAME_HITTEST, time, layer.ratio, renderContext, m, stroked, viewRect, unzoom);
Shape buttonShape = buttonTag.getOutline(fast, ButtonTag.FRAME_HITTEST, time, layer.ratio, renderContext, m, stroked, viewRect, unzoom);
int dx = (int) Math.round(viewRect.xMin * unzoom);
int dy = (int) Math.round(viewRect.yMin * unzoom);
Point cursorPositionInView = new Point((int) Math.round(renderContext.cursorPosition.x * unzoom) - dx, (int) Math.round(renderContext.cursorPosition.y * unzoom) - dy);
@@ -1392,7 +1392,7 @@ public class Timeline {
}
}
Shape cshape = ((DrawableTag) character).getOutline(dframe, time, layer.ratio, renderContext, m, stroked, viewRect, unzoom);
Shape cshape = ((DrawableTag) character).getOutline(fast, dframe, time, layer.ratio, renderContext, m, stroked, viewRect, unzoom);
Area addArea = new Area(cshape);
if (currentClip != null) {
Area a = new Area(new Rectangle(displayRect.Xmin, displayRect.Ymin, displayRect.getWidth(), displayRect.getHeight()));

View File

@@ -47,6 +47,7 @@ public class SHAPE implements NeedsCharacters, Serializable {
public List<SHAPERECORD> shapeRecords;
private Shape cachedOutline;
private Shape fastCachedOutline;
@Override
public void getNeededCharacters(Set<Integer> needed) {
@@ -86,27 +87,50 @@ public class SHAPE implements NeedsCharacters, Serializable {
public void clearCachedOutline() {
cachedOutline = null;
fastCachedOutline = null;
}
public Shape getOutline(int shapeNum, SWF swf, boolean stroked) {
/**
*
* @param fast When the shape is large, can approximate to rectangles instead of being slow.
* @param shapeNum Version of DefineShape, 2 for DefineShape2 etc.
* @param swf
* @param stroked
* @return
*/
public Shape getOutline(boolean fast, int shapeNum, SWF swf, boolean stroked) {
if (cachedOutline != null) {
return cachedOutline;
}
if (fast && fastCachedOutline != null) {
return fastCachedOutline;
}
List<GeneralPath> strokes = new ArrayList<>();
List<GeneralPath> paths = PathExporter.export(shapeNum, swf, this, strokes);
Area area = new Area();
boolean large = shapeRecords.size() > 500;
if (!large) {
fast = false;
}
Area area = new Area();
for (GeneralPath path : paths) {
area.add(new Area(path));
area.add(new Area(fast ? path.getBounds2D() : path));
}
if (stroked) {
for (GeneralPath path : strokes) {
area.add(new Area(path));
area.add(new Area(fast ? path.getBounds2D() : path));
}
}
}
cachedOutline = area;
if (fast) {
fastCachedOutline = area;
} else {
fastCachedOutline = null;
cachedOutline = area;
}
return area;
}