feat: add advanced multi sample anti-aliased shape renderer

Multi sample anti-alias renderer with configurable grid.
It can be turned on with icon under render window,
and with checkbox for export.
This commit is contained in:
Jindra Petřík
2026-04-03 08:11:19 +02:00
parent 0938f5cbad
commit 74b4e957a6
34 changed files with 2176 additions and 182 deletions

View File

@@ -5035,8 +5035,8 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
int originalHeight = (int) Math.ceil(rect.getHeight() * zoom / SWF.unitDivisor);
SerializableImage image = new SerializableImage(
originalWidth * aaScale,
originalHeight * aaScale,
originalWidth,
originalHeight,
SerializableImage.TYPE_INT_ARGB_PRE
);
if (backGroundColor == null) {
@@ -5049,25 +5049,17 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
}
Matrix m = transformation.clone();
m.translate(-rect.Xmin * zoom * aaScale, -rect.Ymin * zoom * aaScale);
m.scale(zoom * aaScale);
m.translate(-rect.Xmin * zoom, -rect.Ymin * zoom);
m.scale(zoom);
RenderContext renderContext = new RenderContext();
renderContext.cursorPosition = cursorPosition;
renderContext.mouseButton = mouseButton;
ExportRectangle viewRect = new ExportRectangle(rect);
viewRect.xMin *= aaScale;
viewRect.yMin *= aaScale;
viewRect.xMax *= aaScale;
viewRect.yMax *= aaScale;
timeline.toImage(frame, time, renderContext, image, image, false, m, new Matrix(), m, colorTransform, zoom * aaScale, true, viewRect, viewRect, m, true, Timeline.DRAW_MODE_ALL, 0, canUseSmoothing, new ArrayList<>(), aaScale);
if (aaScale > 1) {
image = new SerializableImage(ImageResizer.resizeImage(image.getBufferedImage(), originalWidth, originalHeight, RenderingHints.VALUE_INTERPOLATION_BICUBIC, true));
}
timeline.toImage(frame, time, renderContext, image, image, false, m, new Matrix(), m, colorTransform, zoom, true, viewRect, viewRect, m, true, Timeline.DRAW_MODE_ALL, 0, canUseSmoothing, new ArrayList<>(), aaScale);
return image;
}
@@ -6476,6 +6468,6 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
@Override
public RECT getRectWithFilters() {
return getRect();
return getRectWithStrokes();
}
}

View File

@@ -844,18 +844,22 @@ public final class Configuration {
@ConfigurationDefaultBoolean(false)
@ConfigurationCategory("display")
@ConfigurationRemoved
public static ConfigurationItem<Boolean> reduceAntialiasConflationByScalingForDisplay = null;
@ConfigurationDefaultInt(4)
@ConfigurationCategory("display")
@ConfigurationRemoved
public static ConfigurationItem<Integer> reduceAntialiasConflationByScalingValueForDisplay = null;
@ConfigurationDefaultBoolean(false)
@ConfigurationCategory("export")
@ConfigurationRemoved
public static ConfigurationItem<Boolean> reduceAntialiasConflationByScalingForExport = null;
@ConfigurationDefaultInt(10)
@ConfigurationDefaultInt(4)
@ConfigurationCategory("export")
@ConfigurationRemoved
public static ConfigurationItem<Integer> reduceAntialiasConflationByScalingValueForExport = null;
@ConfigurationDefaultBoolean(true)
@@ -1213,6 +1217,22 @@ public final class Configuration {
@ConfigurationCategory("export")
public static ConfigurationItem<Boolean> exportFlaAs3DisableScriptLayer = null;
@ConfigurationDefaultBoolean(false)
@ConfigurationCategory("display")
public static ConfigurationItem<Boolean> useMsaaForDisplay = null;
@ConfigurationDefaultBoolean(false)
@ConfigurationCategory("export")
public static ConfigurationItem<Boolean> useMsaaForExport = null;
@ConfigurationDefaultInt(4)
@ConfigurationCategory("display")
public static ConfigurationItem<Integer> msaaGridForDisplay = null;
@ConfigurationDefaultInt(4)
@ConfigurationCategory("export")
public static ConfigurationItem<Integer> msaaGridForExport = null;
private static Map<String, String> configurationDescriptions = new LinkedHashMap<>();
private static Map<String, String> configurationTitles = new LinkedHashMap<>();
@@ -1865,20 +1885,5 @@ public final class Configuration {
}
return null;
}
public static int calculateRealAaScale(int imageWidth, int imageHeight, double zoom, int initialAaScale) {
final int MAX_IMAGE_DIMENSION = 10000;
int aaScale = initialAaScale;
while (
aaScale > 1
&& (((long) imageWidth * zoom * aaScale / SWF.unitDivisor) > MAX_IMAGE_DIMENSION
|| ((long) imageHeight * zoom * aaScale / SWF.unitDivisor) > MAX_IMAGE_DIMENSION)
) {
aaScale--;
}
return aaScale;
}
}
}

View File

@@ -268,8 +268,7 @@ public class FrameExporter {
int fframe = subFrameMode ? fframes.get(0) : fframes.get(pos++);
RECT displayRect = tim.getDisplayRectWithFilters();
int realAaScale = Configuration.calculateRealAaScale(displayRect.getWidth(), displayRect.getHeight(), settings.zoom, settings.aaScale);
BufferedImage result = SWF.frameToImageGet(tim, fframe, subFrameMode ? pos++ : 0, null, 0, displayRect, new Matrix(), null, backgroundColor == null && !usesTransparency ? Color.white : backgroundColor, settings.zoom, true, realAaScale).getBufferedImage();
BufferedImage result = SWF.frameToImageGet(tim, fframe, subFrameMode ? pos++ : 0, null, 0, displayRect, new Matrix(), null, backgroundColor == null && !usesTransparency ? Color.white : backgroundColor, settings.zoom, true, settings.aaScale).getBufferedImage();
if (CancellableWorker.isInterrupted()) {
return null;
}

View File

@@ -171,13 +171,11 @@ public class MorphShapeExporter {
st = mst.getStartShapeTag();
rect = st.getRect();
int realAaScale = Configuration.calculateRealAaScale(rect.getWidth(), rect.getHeight(), settings.zoom, settings.aaScale);
int originalWidth = (int) Math.ceil(rect.getWidth() * settings.zoom / SWF.unitDivisor);
int originalHeight = (int) Math.ceil(rect.getHeight() * settings.zoom / SWF.unitDivisor);
int newWidth = originalWidth * realAaScale;
int newHeight = originalHeight * realAaScale;
int newWidth = originalWidth;
int newHeight = originalHeight;
SerializableImage img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB_PRE);
img.fillTransparent();
if (settings.mode == MorphShapeExportMode.BMP_START_END) {
@@ -188,16 +186,11 @@ public class MorphShapeExporter {
g.fillRect(0, 0, img.getWidth(), img.getHeight());
}
}
m = Matrix.getScaleInstance(settings.zoom * realAaScale);
m = Matrix.getScaleInstance(settings.zoom);
m.translate(-rect.Xmin, -rect.Ymin);
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, m, m, m, new CXFORMWITHALPHA(), unzoom * realAaScale, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, realAaScale);
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, m, m, m, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, settings.aaScale);
BufferedImage bim = img.getBufferedImage();
if (realAaScale > 1) {
bim = ImageResizer.resizeImage(bim, originalWidth, originalHeight, RenderingHints.VALUE_INTERPOLATION_BICUBIC, true);
}
if (settings.mode == MorphShapeExportMode.PNG_START_END) {
ImageHelper.write(bim, ImageFormat.PNG, fileStart);
} else if (settings.mode == MorphShapeExportMode.WEBP_START_END) {
@@ -210,13 +203,12 @@ public class MorphShapeExporter {
st = mst.getEndShapeTag();
rect = st.getRect();
realAaScale = Configuration.calculateRealAaScale(rect.getWidth(), rect.getHeight(), settings.zoom, settings.aaScale);
originalWidth = (int) Math.ceil(rect.getWidth() * settings.zoom / SWF.unitDivisor);
originalHeight = (int) Math.ceil(rect.getHeight() * settings.zoom / SWF.unitDivisor);
newWidth = originalWidth * realAaScale;
newHeight = originalHeight * realAaScale;
newWidth = originalWidth;
newHeight = originalHeight;
img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB_PRE);
img.fillTransparent();
@@ -228,16 +220,12 @@ public class MorphShapeExporter {
g.fillRect(0, 0, img.getWidth(), img.getHeight());
}
}
m = Matrix.getScaleInstance(settings.zoom * realAaScale);
m = Matrix.getScaleInstance(settings.zoom);
m.translate(-rect.Xmin, -rect.Ymin);
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, m, m, m, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, settings.aaScale);
bim = img.getBufferedImage();
if (realAaScale > 1) {
bim = ImageResizer.resizeImage(bim, originalWidth, originalHeight, RenderingHints.VALUE_INTERPOLATION_BICUBIC, true);
}
if (settings.mode == MorphShapeExportMode.PNG_START_END) {
ImageHelper.write(bim, ImageFormat.PNG, fileEnd);
} else if (settings.mode == MorphShapeExportMode.WEBP_START_END) {
@@ -452,11 +440,8 @@ public class MorphShapeExporter {
private final Color backgroundColor;
private final MorphShapeExportSettings settings;
private final RECT rect;
private final int realAaScale;
private final int originalWidth;
private final int originalHeight;
private final int newWidth;
private final int newHeight;
public MyFrameIterator(
MorphShapeTag mst,
@@ -474,11 +459,8 @@ public class MorphShapeExporter {
this.settings = settings;
rect = mst.getRect();
realAaScale = Configuration.calculateRealAaScale(rect.getWidth(), rect.getHeight(), settings.zoom, settings.aaScale);
originalWidth = (int) Math.ceil(rect.getWidth() * settings.zoom / SWF.unitDivisor);
originalHeight = (int) Math.ceil(rect.getHeight() * settings.zoom / SWF.unitDivisor);
newWidth = originalWidth * realAaScale;
newHeight = originalHeight * realAaScale;
}
public void reset() {
@@ -514,7 +496,7 @@ public class MorphShapeExporter {
ShapeTag st = mst.getShapeTagAtRatio(ratio);
SerializableImage img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB_PRE);
SerializableImage img = new SerializableImage(originalWidth, originalHeight, SerializableImage.TYPE_INT_ARGB_PRE);
img.fillTransparent();
if (!usesTransparency) {
Graphics2D g = (Graphics2D) img.getGraphics();
@@ -525,16 +507,12 @@ public class MorphShapeExporter {
}
g.fillRect(0, 0, img.getWidth(), img.getHeight());
}
Matrix m = Matrix.getScaleInstance(settings.zoom * realAaScale);
Matrix m = Matrix.getScaleInstance(settings.zoom);
m.translate(-rect.Xmin, -rect.Ymin);
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, m, m, m, new CXFORMWITHALPHA(), settings.zoom * realAaScale, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, realAaScale);
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, m, m, m, new CXFORMWITHALPHA(), settings.zoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, settings.aaScale);
BufferedImage result = img.getBufferedImage();
if (realAaScale > 1) {
result = ImageResizer.resizeImage(result, originalWidth, originalHeight, RenderingHints.VALUE_INTERPOLATION_BICUBIC, true);
}
if (CancellableWorker.isInterrupted()) {
return null;
}

View File

@@ -132,11 +132,10 @@ public class ShapeExporter {
case PNG:
case BMP:
case WEBP:
int realAaScale = Configuration.calculateRealAaScale(rect.getWidth(), rect.getHeight(), settings.zoom, aaScale);
int originalWidth = (int) Math.ceil(rect.getWidth() * settings.zoom / SWF.unitDivisor);
int originalHeight = (int) Math.ceil(rect.getHeight() * settings.zoom / SWF.unitDivisor);
int newWidth = originalWidth * realAaScale;
int newHeight = originalHeight * realAaScale;
int newWidth = originalWidth;
int newHeight = originalHeight;
SerializableImage img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB_PRE);
img.fillTransparent();
if (settings.mode == ShapeExportMode.BMP) {
@@ -147,15 +146,13 @@ public class ShapeExporter {
g.fillRect(0, 0, img.getWidth(), img.getHeight());
}
}
Matrix m2 = Matrix.getScaleInstance(settings.zoom * realAaScale);
Matrix m2 = Matrix.getScaleInstance(settings.zoom);
m2.translate(-rect.Xmin, -rect.Ymin);
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m2, m2, m2, m2, new CXFORMWITHALPHA(), unzoom * realAaScale, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, realAaScale);
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m2, m2, m2, m2, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, aaScale);
BufferedImage bim = img.getBufferedImage();
if (realAaScale > 1) {
bim = ImageResizer.resizeImage(bim, originalWidth, originalHeight, RenderingHints.VALUE_INTERPOLATION_BICUBIC, true);
}
if (settings.mode == ShapeExportMode.PNG) {
ImageHelper.write(bim, ImageFormat.PNG, file);

View File

@@ -17,10 +17,10 @@
package com.jpexs.decompiler.flash.exporters.shape;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.ImageTagBufferedImage;
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.shape.aa.AntialiasedBitmapExporter;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.types.ColorTransform;
@@ -66,49 +66,49 @@ public class BitmapExporter extends ShapeExporterBase {
private static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform();
private SerializableImage image;
protected SerializableImage image;
private Graphics2D graphics;
protected Graphics2D graphics;
private final Color defaultColor;
protected final Color defaultColor;
private final SWF swf;
protected final SWF swf;
private final GeneralPath path;
protected final GeneralPath path;
private Paint fillPaint;
protected Paint fillPaint;
private boolean fillRepeat;
protected boolean fillRepeat;
private boolean fillSmooth;
protected boolean fillSmooth;
private AffineTransform fillTransform;
protected AffineTransform fillTransform;
private Paint linePaint;
protected Paint linePaint;
private AffineTransform lineTransform;
protected AffineTransform lineTransform;
private Color lineColor;
protected Color lineColor;
private Stroke lineStroke;
protected Stroke lineStroke;
private Stroke defaultStroke;
protected Stroke defaultStroke;
private Matrix strokeTransformation;
protected Matrix strokeTransformation;
private double thicknessScale;
private double thicknessScaleX;
private double thicknessScaleY;
protected double thicknessScale;
protected double thicknessScaleX;
protected double thicknessScaleY;
private double unzoom;
protected double unzoom;
private int aaScale;
protected int aaScale;
private static boolean linearGradientColorWarningShown = false;
protected static boolean linearGradientColorWarningShown = false;
private boolean scaleStrokes;
private class TransformedStroke implements Stroke {
protected boolean scaleStrokes;
protected class TransformedStroke implements Stroke {
/**
* To make this serializable without problems.
@@ -176,41 +176,49 @@ public class BitmapExporter extends ShapeExporterBase {
* @param aaScale Antialias conflation reducing scale coefficient
*/
public static void export(int windingRule, int shapeNum, SWF swf, SHAPE shape, Color defaultColor, SerializableImage image, double unzoom, Matrix transformation, Matrix strokeTransformation, ColorTransform colorTransform, boolean scaleStrokes, boolean canUseSmoothing, int aaScale) {
BitmapExporter exporter = new BitmapExporter(windingRule, shapeNum, swf, shape, defaultColor, colorTransform);
exporter.setCanUseSmoothing(canUseSmoothing);
exporter.exportTo(shapeNum, image, unzoom, transformation, strokeTransformation, scaleStrokes, aaScale);
if (aaScale > 1) {
AntialiasedBitmapExporter.export(windingRule, shapeNum, swf, shape, defaultColor, image, unzoom, transformation, strokeTransformation, colorTransform, scaleStrokes, canUseSmoothing, aaScale);
} else {
BitmapExporter exporter = new BitmapExporter(windingRule, shapeNum, swf, shape, defaultColor, colorTransform);
exporter.setCanUseSmoothing(canUseSmoothing);
exporter.exportTo(shapeNum, image, unzoom, transformation, strokeTransformation, scaleStrokes, aaScale);
}
}
private BitmapExporter(int windingRule, int shapeNum, SWF swf, SHAPE shape, Color defaultColor, ColorTransform colorTransform) {
protected BitmapExporter(int windingRule, int shapeNum, SWF swf, SHAPE shape, Color defaultColor, ColorTransform colorTransform) {
super(windingRule, shapeNum, swf, shape, colorTransform);
this.swf = swf;
this.defaultColor = defaultColor;
path = new GeneralPath(windingRule == ShapeTag.WIND_NONZERO ? GeneralPath.WIND_NON_ZERO : GeneralPath.WIND_EVEN_ODD);
}
private void exportTo(int shapeNum, SerializableImage image, double unzoom, Matrix transformation, Matrix strokeTransformation, boolean scaleStrokes, int aaScale) {
protected void exportTo(int shapeNum, SerializableImage image, double unzoom, Matrix transformation, Matrix strokeTransformation, boolean scaleStrokes, int aaScale) {
this.image = image;
this.scaleStrokes = scaleStrokes;
ExportRectangle bounds = new ExportRectangle(shape.getBounds(shapeNum));
this.strokeTransformation = strokeTransformation;
calculateThicknessScale(bounds, transformation);
calculateThicknessScale();
graphics = (Graphics2D) image.getGraphics();
AffineTransform at = transformation.toTransform();
at.preConcatenate(AffineTransform.getScaleInstance(1 / SWF.unitDivisor, 1 / SWF.unitDivisor));
graphics.setTransform(at);
setTransform(graphics, transformation);
defaultStroke = graphics.getStroke();
this.unzoom = unzoom;
this.aaScale = aaScale;
super.export();
}
protected void setTransform(Graphics2D g, Matrix transformation) {
AffineTransform at = transformation.toTransform();
at.preConcatenate(AffineTransform.getScaleInstance(1 / SWF.unitDivisor, 1 / SWF.unitDivisor));
graphics.setTransform(at);
}
private void calculateThicknessScale(ExportRectangle bounds, Matrix transformation) {
protected void calculateThicknessScale() {
com.jpexs.decompiler.flash.exporters.commonshape.Point p00 = strokeTransformation.transform(0, 0);
com.jpexs.decompiler.flash.exporters.commonshape.Point p11 = strokeTransformation.transform(1, 1);
thicknessScale = p00.distanceTo(p11) / Math.sqrt(2);
thicknessScaleX = Math.abs(p11.x - p00.x);
thicknessScaleY = Math.abs(p11.y - p00.y);
thicknessScaleY = Math.abs(p11.y - p00.y);
}
/**
@@ -417,19 +425,9 @@ public class BitmapExporter extends ShapeExporterBase {
//always display minimum stroke of 1 pixel, no matter how zoomed it is
if (thickness * unzoom / aaScale < 1 * SWF.unitDivisor) {
thickness = 1 * SWF.unitDivisor / (unzoom / aaScale);
}
/*
if (Configuration.fixAntialiasConflation.get()) {
thickness += 1 * SWF.unitDivisor / unzoom;
}
*/
}
if (joinStyle == BasicStroke.JOIN_MITER) {
//lineStroke = new BasicStroke((float) thickness, capStyle, joinStyle, miterLimit);
/*if (Configuration.allowMiterClipLinestyle.get()) {
lineStroke = new MiterClipBasicStroke((BasicStroke) lineStroke);
}*/
lineStroke = new ExtendedBasicStroke((float) thickness, capStyle, ExtendedBasicStroke.JOIN_MITER_CLIP, miterLimit);
} else {
lineStroke = new BasicStroke((float) thickness, capStyle, joinStyle);

View File

@@ -0,0 +1,426 @@
package com.jpexs.decompiler.flash.exporters.shape.aa;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.LINESTYLE2;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.RGB;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.graphics.ExtendedBasicStroke;
import com.jpexs.helpers.SerializableImage;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.MultipleGradientPaint;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.TexturePaint;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.util.ArrayList;
import java.util.List;
/**
* Antialiased Shape Bitmap Exporter
*
* @author JPEXS
*/
public class AntialiasedBitmapExporter extends BitmapExporter {
private final List<List<Vec2>> contours = new ArrayList<>();
private AntialiasTools.SceneRasterizerMSAA rz;
private Vec2 lastPos = null;
private Matrix vecTrans;
private Matrix vecTransLines;
private ExportRectangle viewRect;
private boolean inLines = false;
private boolean closeLine = false;
private double curveFlattness = 1.0;
/**
* Exports a shape to a bitmap.
*
* @param windingRule Winding rule
* @param shapeNum Shape number
* @param swf SWF
* @param shape Shape
* @param defaultColor Default color
* @param image Image
* @param unzoom Unzoom
* @param transformation Transformation
* @param strokeTransformation Stroke transformation
* @param colorTransform Color transform
* @param scaleStrokes Scale strokes
* @param canUseSmoothing Can use smoothing
* @param aaScale Antialias conflation reducing scale coefficient
*/
public static void export(int windingRule, int shapeNum, SWF swf, SHAPE shape, Color defaultColor, SerializableImage image, double unzoom, Matrix transformation, Matrix strokeTransformation, ColorTransform colorTransform, boolean scaleStrokes, boolean canUseSmoothing, int aaScale) {
AntialiasedBitmapExporter exporter = new AntialiasedBitmapExporter(windingRule, shapeNum, swf, shape, defaultColor, colorTransform, (int) Math.round(20 / unzoom));
exporter.setCanUseSmoothing(canUseSmoothing);
exporter.exportTo(shapeNum, image, unzoom, transformation, strokeTransformation, scaleStrokes, aaScale);
}
protected AntialiasedBitmapExporter(int windingRule, int shapeNum, SWF swf, SHAPE shape, Color defaultColor, ColorTransform colorTransform, int thicknessDivisor) {
super(windingRule, shapeNum, swf, shape, defaultColor, colorTransform);
}
@Override
protected void exportTo(int shapeNum, SerializableImage image, double unzoom, Matrix transformation, Matrix strokeTransformation, boolean scaleStrokes, int aaScale) {
this.strokeTransformation = strokeTransformation;
calculateThicknessScale();
//double s = Math.max(transformation.scaleX,transformation.scaleY);
RECT bounds = new RECT(shape.getBounds(shapeNum));
int maxStrokeWidth = shape.getMaxStrokeWidth(shapeNum);
bounds.Xmin -= maxStrokeWidth;
bounds.Ymin -= maxStrokeWidth;
bounds.Xmax += maxStrokeWidth;
bounds.Ymax += maxStrokeWidth;
vecTrans = transformation.preConcatenate(Matrix.getScaleInstance(1 / SWF.unitDivisor));
viewRect = vecTrans.transform(new ExportRectangle(bounds));
/*viewRect.xMin -= 1;
viewRect.yMin -= 1;
viewRect.yMax += 1;
viewRect.xMax += 1;*/
viewRect.xMin = Math.floor(viewRect.xMin - unzoom - 0.5);
viewRect.yMin = Math.floor(viewRect.yMin - unzoom - 0.5);
viewRect.xMax = Math.ceil(viewRect.xMax + unzoom);
viewRect.yMax = Math.ceil(viewRect.yMax + unzoom);
int width = (int) viewRect.getWidth();
int height = (int) viewRect.getHeight();
vecTrans = vecTrans.preConcatenate(Matrix.getTranslateInstance(-viewRect.xMin, -viewRect.yMin));
vecTransLines = vecTrans.preConcatenate(Matrix.getTranslateInstance(-0.5, -0.5));
Graphics2D g = (Graphics2D) image.getGraphics();
//g.setTransform(t);
if (width <= 0 || height <= 0) {
return;
}
curveFlattness = 1.0 / aaScale;
BufferedImage image2 = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
Graphics2D g2 = (Graphics2D) image2.getGraphics();
g2.setComposite(AlphaComposite.Src);
g2.setColor(new Color(0, 0, 0, 0));
g2.fillRect(0, 0, width, height);
try {
rz = new AntialiasTools.SceneRasterizerMSAA(width, height, aaScale);
} catch (OutOfMemoryError er) {
System.gc();
return;
}
rz.clear(0x00000000);
Shape clip = g.getClip();
if (clip != null) {
AffineTransform t = new AffineTransform(g.getTransform());
t.preConcatenate(AffineTransform.getTranslateInstance(-viewRect.xMin, -viewRect.yMin));
clip = t.createTransformedShape(clip);
List<List<Vec2>> clipContours = AntialiasTools.shapeToContours(clip, curveFlattness);
rz.setClipContours(clipContours, Path2D.WIND_EVEN_ODD);
}
this.image = image;
this.scaleStrokes = scaleStrokes;
graphics = (Graphics2D) image.getGraphics();
setTransform(graphics, transformation);
defaultStroke = graphics.getStroke();
this.unzoom = unzoom;
this.aaScale = 1; //aaScale;
super.export();
//super.exportTo(shapeNum, image, unzoom, transformation, strokeTransformation, scaleStrokes, aaScale);
//rz.resolveToReplace(image2);
rz.resolveTo(image2);
g.setComposite(AlphaComposite.SrcOver);
g.setTransform(new AffineTransform());
g.drawImage(image2, (int) viewRect.xMin, (int) viewRect.yMin, null);
/*
g.setColor(new Color(0,0,0,128));
g.setStroke(new BasicStroke(1));
g.drawRect((int) viewRect.xMin, (int) viewRect.yMin, (int) viewRect.getWidth(), (int) viewRect.getHeight());
*/
}
@Override
protected void setTransform(Graphics2D g, Matrix transformation) {
AffineTransform at = transformation.toTransform();
at.preConcatenate(AffineTransform.getScaleInstance(1 / SWF.unitDivisor, 1 / SWF.unitDivisor));
at.preConcatenate(AffineTransform.getTranslateInstance(-viewRect.xMin, -viewRect.yMin));
graphics.setTransform(at);
}
/**
* Returns the image.
*
* @return Image
*/
public SerializableImage getImage() {
return image;
}
@Override
public void beginShape() {
}
@Override
public void endShape() {
}
@Override
public void beginFills() {
}
@Override
public void endFills() {
}
@Override
public void beginLines() {
inLines = true;
}
@Override
public void endLines(boolean close) {
closeLine = close;
/*if (close) {
//path.closePath();
}*/
finalizePath();
inLines = false;
}
@Override
public void moveTo(double x, double y) {
Point2D src = new Point2D.Double(x, y);
Point2D dst = new Point2D.Double();
dst = (inLines ? vecTransLines : vecTrans).transform(src);
contours.add(new ArrayList<>());
lastPos = new Vec2(dst.getX(), dst.getY());
contours.get(contours.size() - 1).add(lastPos);
}
@Override
public void lineTo(double x, double y) {
Point2D src = new Point2D.Double(x, y);
Point2D dst = new Point2D.Double();
dst = (inLines ? vecTransLines : vecTrans).transform(src);
lastPos = new Vec2(dst.getX(), dst.getY());
contours.get(contours.size() - 1).add(lastPos);
}
@Override
public void curveTo(double controlX, double controlY, double anchorX, double anchorY) {
Point2D src = new Point2D.Double(controlX, controlY);
Point2D dst = new Point2D.Double();
dst = (inLines ? vecTransLines : vecTrans).transform(src);
Vec2 controlVec2 = new Vec2(dst.getX(), dst.getY());
src = new Point2D.Double(anchorX, anchorY);
dst = (inLines ? vecTransLines : vecTrans).transform(src);
Vec2 anchorVec2 = new Vec2(dst.getX(), dst.getY());
List<Vec2> contour = contours.get(contours.size() - 1);
AntialiasTools.append(contour, AntialiasTools.flattenQuadraticBezier(lastPos, controlVec2, anchorVec2, curveFlattness), true);
lastPos = anchorVec2;
}
private void drawImage(BufferedImage image, int dx, int dy, int dx2, int dy2, int sx, int sy, int sx2, int sy2, ImageObserver obs, boolean smooth) {
List<List<Vec2>> cs = new ArrayList<>();
List<Vec2> c = new ArrayList<>();
Point2D dst = new Point2D.Double();
fillTransform.transform(new Point2D.Double(dx, dy), dst);
c.add(new Vec2(dst.getX(), dst.getY()));
fillTransform.transform(new Point2D.Double(dx2, dy), dst);
c.add(new Vec2(dst.getX(), dst.getY()));
fillTransform.transform(new Point2D.Double(dx2, dy2), dst);
c.add(new Vec2(dst.getX(), dst.getY()));
fillTransform.transform(new Point2D.Double(dx, dy2), dst);
c.add(new Vec2(dst.getX(), dst.getY()));
for (int i = 0; i < c.size(); i++) {
double x = Math.round(c.get(i).x);
double y = Math.round(c.get(i).y);
c.set(i, new Vec2(x, y));
}
cs.add(c);
AffineTransform t = new AffineTransform();
if (dx2 != dx && dy2 != dy) {
double scaleX = (sx2 - sx) / (double) (dx2 - dx);
double scaleY = (sy2 - sy) / (double) (dy2 - dy);
t = AffineTransform.getScaleInstance(scaleX, scaleY);
t.preConcatenate(fillTransform);
}
/*int w = sx2 - sx;
int h = sy2 - sy;
int one = (int) (1 * unzoom);
BufferedImage imgTrans = new BufferedImage(w + 2, h + 2, BufferedImage.TYPE_INT_ARGB_PRE);
Graphics2D g = (Graphics2D) imgTrans.getGraphics();
g.setComposite(AlphaComposite.Src);
g.setColor(new Color(0,255,0,255));
g.fillRect(0, 0, w + 2, h + 2);
g.drawImage(image, 1, 1, 1 + w, 1 + h, sx,sy,sx2,sy2, null);
*/
//t.concatenate(AffineTransform.getTranslateInstance(-1, -1));
//t.preConcatenate(AffineTransform.getTranslateInstance(-1 * unzoom, -1 * unzoom));
//new Rectangle2D.Double(sx, sy, sx2 - sx, sy2 - sy)
//rz.fillContoursWithPaint(cs, windingRule, new TexturePaint(imgTrans, new Rectangle2D.Double(0,0,w+2,h+2)), t, smooth);
//new TexturePaint(image, new Rectangle2D.Double(sx, sy, sx2 - sx, sy2 - sy))
rz.fillContoursWithPaint(cs, windingRule, Color.black, t, smooth, image);
}
private void drawImage(BufferedImage image, int x, int y, ImageObserver obs, boolean smooth) {
drawImage(image, x, y, x + image.getWidth(), y + image.getHeight(), 0, 0, image.getWidth(), image.getHeight(), obs, smooth);
}
/**
* Finalizes the path.
*/
@Override
protected void finalizePath() {
if (fillPaint != null) {
if (fillPaint instanceof MultipleGradientPaint) {
fillTransform.preConcatenate(graphics.getTransform());
rz.fillContoursWithPaint(contours, Path2D.WIND_EVEN_ODD, fillPaint, fillTransform);
} else if (fillPaint instanceof TexturePaint) {
//rz.setClipContours(contours, Path2D.WIND_EVEN_ODD);
fillTransform.preConcatenate(graphics.getTransform());
if (fillRepeat) {
rz.fillContoursWithPaint(contours, Path2D.WIND_EVEN_ODD, fillPaint, fillTransform, fillSmooth, null);
} else {
drawImage(((TexturePaint) fillPaint).getImage(), 0, 0, null, fillSmooth);
}
} else {
rz.fillContoursWithPaint(contours, Path2D.WIND_EVEN_ODD, fillPaint);
}
}
if ((linePaint != null && lineStroke != null) || lineColor != null) {
Stroke stroke = lineStroke == null ? defaultStroke : lineStroke;
Shape shape = AntialiasTools.contoursToShape(contours, Path2D.WIND_EVEN_ODD, closeLine);
Shape strokedShape = stroke.createStrokedShape(shape);
List<List<Vec2>> strokeContours = AntialiasTools.shapeToContours(strokedShape, curveFlattness);
if (linePaint != null && lineStroke != null) {
if (linePaint instanceof MultipleGradientPaint) {
lineTransform.preConcatenate(graphics.getTransform());
rz.fillContoursWithPaint(strokeContours, Path2D.WIND_NON_ZERO, linePaint, lineTransform);
} else if (linePaint instanceof TexturePaint) {
lineTransform.preConcatenate(graphics.getTransform());
rz.fillContoursWithPaint(strokeContours, Path2D.WIND_NON_ZERO, linePaint, lineTransform);
} else {
rz.fillContoursWithPaint(strokeContours, Path2D.WIND_NON_ZERO, linePaint);
}
} else if (lineColor != null) {
rz.fillContoursWithPaint(strokeContours, Path2D.WIND_NON_ZERO, lineColor);
}
}
contours.clear();
lineStroke = null;
lineColor = null;
fillPaint = null;
}
@Override
public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, float miterLimit, boolean noClose) {
finalizePath();
linePaint = null;
lineTransform = null;
if (thickness == 0) {
lineColor = null;
return;
}
lineColor = color == null ? null : color.toColor();
int capStyle = BasicStroke.CAP_ROUND;
switch (startCaps) {
case LINESTYLE2.NO_CAP:
capStyle = BasicStroke.CAP_BUTT;
break;
case LINESTYLE2.ROUND_CAP:
capStyle = BasicStroke.CAP_ROUND;
break;
case LINESTYLE2.SQUARE_CAP:
capStyle = BasicStroke.CAP_SQUARE;
break;
}
int joinStyle = BasicStroke.JOIN_ROUND;
switch (joints) {
case LINESTYLE2.BEVEL_JOIN:
joinStyle = BasicStroke.JOIN_BEVEL;
break;
case LINESTYLE2.MITER_JOIN:
joinStyle = BasicStroke.JOIN_MITER;
break;
case LINESTYLE2.ROUND_JOIN:
joinStyle = BasicStroke.JOIN_ROUND;
break;
}
if (scaleStrokes) {
switch (scaleMode) {
case "VERTICAL":
thickness *= thicknessScaleY;
break;
case "HORIZONTAL":
thickness *= thicknessScaleX;
break;
case "NORMAL":
thickness *= thicknessScale;
break;
case "NONE":
break;
}
}
thickness *= unzoom / SWF.unitDivisor;
//always display minimum stroke of 1 pixel, no matter how zoomed it is
if (thickness < 1) {
thickness = 1;
}
if (joinStyle == BasicStroke.JOIN_MITER) {
lineStroke = new ExtendedBasicStroke((float) thickness, capStyle, ExtendedBasicStroke.JOIN_MITER_CLIP, miterLimit);
} else {
lineStroke = new BasicStroke((float) thickness, capStyle, joinStyle);
}
}
}

View File

@@ -0,0 +1,48 @@
package com.jpexs.decompiler.flash.exporters.shape.aa;
/**
*
* @author JPEXS
*/
public class Vec2 {
public final double x;
public final double y;
public Vec2(double x, double y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "[" + x + ", " + y + "]";
}
@Override
public int hashCode() {
int hash = 5;
hash = 71 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32));
hash = 71 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32));
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Vec2 other = (Vec2) obj;
if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(other.x)) {
return false;
}
return Double.doubleToLongBits(this.y) == Double.doubleToLongBits(other.y);
}
}

View File

@@ -145,7 +145,7 @@ public abstract class DrawableTag extends CharacterTag implements BoundedTag {
@Override
public RECT getRectWithFilters() {
RECT r = new RECT(getRect());
RECT r = new RECT(getRectWithStrokes());
Dimension filterDimension = getFilterDimensions();
r.Xmin -= filterDimension.width;
r.Xmax += filterDimension.width;

View File

@@ -219,6 +219,9 @@ public abstract class ShapeTag extends DrawableTag implements LazyObject {
r.Ymin -= maxWidth;
r.Xmax += maxWidth;
r.Ymax += maxWidth;
r.Xmin -= SWF.unitDivisor / 2;
r.Ymin -= SWF.unitDivisor / 2;
return r;
}

View File

@@ -112,7 +112,7 @@ public class SHAPE implements NeedsCharacters, Serializable {
public RECT getEdgeBounds() {
return SHAPERECORD.getBounds(shapeRecords, null, 1, true);
}
/**
* Clears cached outline.
*/
@@ -206,4 +206,8 @@ public class SHAPE implements NeedsCharacters, Serializable {
ret.shapeRecords.add(new EndShapeRecord());
return ret;
}
public int getMaxStrokeWidth(int shapeNum) {
return SHAPERECORD.getMaxStrokeWidth(shapeRecords, null, shapeNum);
}
}

View File

@@ -118,7 +118,7 @@ public class SHAPEWITHSTYLE extends SHAPE implements NeedsCharacters, Serializab
@Override
public RECT getBounds(int shapeNum) {
return SHAPERECORD.getBounds(shapeRecords, lineStyles, shapeNum, false);
}
}
/**
* Updates morph shape tag.
@@ -291,4 +291,9 @@ public class SHAPEWITHSTYLE extends SHAPE implements NeedsCharacters, Serializab
}
return result;
}
@Override
public int getMaxStrokeWidth(int shapeNum) {
return SHAPERECORD.getMaxStrokeWidth(shapeRecords, lineStyles, shapeNum);
}
}

View File

@@ -186,6 +186,13 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali
}
}
}
if (min_x == Integer.MAX_VALUE) {
min_x = 0;
}
if (min_y == Integer.MAX_VALUE) {
min_y = 0;
}
return new RECT(min_x, max_x, min_y, max_y);
}
@@ -537,4 +544,33 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali
}
public abstract boolean isTooLarge();
public static int getMaxStrokeWidth(List<SHAPERECORD> records, LINESTYLEARRAY baseLineStyles, int shapeNum) {
LINESTYLEARRAY lineStyles = baseLineStyles;
int ret = 0;
for (SHAPERECORD rec : records) {
if (rec instanceof StyleChangeRecord) {
StyleChangeRecord scr = (StyleChangeRecord) rec;
if (scr.stateNewStyles) {
lineStyles = scr.lineStyles;
}
if (scr.stateLineStyle) {
if (scr.lineStyle > 0) {
int width;
if (shapeNum <= 3) {
width = lineStyles.lineStyles[scr.lineStyle - 1].width;
} else {
width = lineStyles.lineStyles2[scr.lineStyle - 1].width;
}
if (width > ret) {
ret = width;
}
}
}
}
}
return ret;
}
}

View File

@@ -2332,7 +2332,7 @@ public class CommandLineArgumentParser {
}
}
int aaScale = Configuration.reduceAntialiasConflationByScalingForExport.get() ? Configuration.reduceAntialiasConflationByScalingValueForExport.get() : 1;
int aaScale = Configuration.useMsaaForExport.get() ? Configuration.msaaGridForExport.get() : 1;
// Here the exportFormats array should contain only validitems
commandLineMode = true;
@@ -3014,7 +3014,7 @@ public class CommandLineArgumentParser {
File outFile = new File(args.pop());
printHeader();
int aaScale = Configuration.reduceAntialiasConflationByScalingForExport.get() ? Configuration.reduceAntialiasConflationByScalingValueForExport.get() : 1;
int aaScale = Configuration.useMsaaForExport.get() ? Configuration.msaaGridForExport.get() : 1;
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {

View File

@@ -174,6 +174,8 @@ public class ExportDialog extends AppDialog {
private JCheckBox transparentFrameBackgroundCheckBox;
private JCheckBox antialiasCheckBox;
private JTextField durationTextField = new JTextField(4);
private JTextField numberOfFramesTextField = new JTextField(4);
@@ -276,6 +278,8 @@ public class ExportDialog extends AppDialog {
}
}
Configuration.useMsaaForExport.set(antialiasCheckBox.isSelected());
StringBuilder cfg = new StringBuilder();
for (int i = 0; i < optionNames.length; i++) {
Object val = ((ComboValue) combos[i].getSelectedItem()).value;
@@ -347,6 +351,70 @@ public class ExportDialog extends AppDialog {
numberOfFramesTextField.setVisible(mode.hasFrames());
}
boolean aaVisible = false;
if (isOptionEnabled(FrameExportMode.class)) {
FrameExportMode mode = getValue(FrameExportMode.class);
switch (mode) {
case CANVAS:
case PDF:
case SWF:
case SVG:
break;
default:
aaVisible = true;
}
}
if (isOptionEnabled(SpriteExportMode.class)) {
SpriteExportMode mode = getValue(SpriteExportMode.class);
switch (mode) {
case CANVAS:
case PDF:
case SWF:
case SVG:
break;
default:
aaVisible = true;
}
}
if (isOptionEnabled(ButtonExportMode.class)) {
ButtonExportMode mode = getValue(ButtonExportMode.class);
switch (mode) {
case SWF:
case SVG:
case SVG_COMBINED:
break;
default:
aaVisible = true;
}
}
if (isOptionEnabled(ShapeExportMode.class)) {
ShapeExportMode mode = getValue(ShapeExportMode.class);
switch (mode) {
case CANVAS:
case SWF:
case SVG:
break;
default:
aaVisible = true;
}
}
if (isOptionEnabled(MorphShapeExportMode.class)) {
MorphShapeExportMode mode = getValue(MorphShapeExportMode.class);
switch (mode) {
case CANVAS:
case SWF:
case SVG:
case SVG_FRAMES:
case SVG_START_END:
break;
default:
aaVisible = true;
}
}
antialiasCheckBox.setVisible(aaVisible);
transparentFrameBackgroundCheckBox.setVisible(isOptionEnabled(FrameExportMode.class));
boolean hasZoom = false;
@@ -521,14 +589,13 @@ public class ExportDialog extends AppDialog {
label.setLabelFor(combos[i]);
}
gbc.gridy++;
gbc.gridx = 0;
gbc.gridwidth = 5;
gbc.fill = GridBagConstraints.BOTH;
comboPanel.add(new JPanel(), gbc);
gbc.insets = new Insets(2, 2, 2, 2);
embedCheckBox = new JCheckBox(translate("embed"));
@@ -571,6 +638,24 @@ public class ExportDialog extends AppDialog {
}
}
antialiasCheckBox = new JCheckBox(translate("antialias"));
antialiasCheckBox.setToolTipText(translate("antialias.hint"));
antialiasCheckBox.setVisible(false);
if (Configuration.useMsaaForExport.get()) {
antialiasCheckBox.setSelected(true);
}
if (visibleOptionClasses.contains(FrameExportMode.class)
|| visibleOptionClasses.contains(ButtonExportMode.class)
|| visibleOptionClasses.contains(ShapeExportMode.class)
|| visibleOptionClasses.contains(MorphShapeExportMode.class)
|| visibleOptionClasses.contains(SpriteExportMode.class)) {
gbc.gridy++;
antialiasCheckBox.setVisible(true);
comboPanel.add(antialiasCheckBox, gbc);
}
durationTextField.setVisible(false);
numberOfFramesTextField.setVisible(false);
if (visibleOptionClasses.contains(MorphShapeExportMode.class)) {

View File

@@ -308,7 +308,7 @@ public class FolderPreviewPanel extends JPanel {
treeItem = ((SceneFrame) treeItem).getFrame();
}
int aaScale = Configuration.reduceAntialiasConflationByScalingForDisplay.get() ? Configuration.reduceAntialiasConflationByScalingValueForDisplay.get() : 1;
int aaScale = Configuration.useMsaaForDisplay.get() ? Configuration.msaaGridForDisplay.get() : 1;
if (treeItem instanceof Frame) {
Frame fn = (Frame) treeItem;
@@ -333,8 +333,6 @@ public class FolderPreviewPanel extends JPanel {
String key = "frame_" + fn.frame + "_" + timeline.id + "_" + zoom;
imgSrc = swf.getFromCache(key);
if (imgSrc == null) {
aaScale = Configuration.calculateRealAaScale(rect.getWidth(), rect.getHeight(), zoom, aaScale);
imgSrc = SWF.frameToImageGet(timeline, fn.frame, 0, null, 0, rect, new Matrix(), null, swf.getBackgroundColor() == null ? null : swf.getBackgroundColor().backgroundColor.toColor(), zoom, !Configuration.disableBitmapSmoothing.get(), aaScale);
swf.putToCache(key, imgSrc);
}
@@ -385,23 +383,14 @@ public class FolderPreviewPanel extends JPanel {
return null;
}
aaScale = Configuration.calculateRealAaScale(width, height, 1, aaScale);
if (imgSrc == null) {
m = m.preConcatenate(Matrix.getScaleInstance(scale * aaScale));
m = m.preConcatenate(Matrix.getScaleInstance(scale));
DrawableTag drawable = (DrawableTag) treeItem;
ExportRectangle viewRectangle = new ExportRectangle(0, 0, ow, oh);
SerializableImage imageLarger = new SerializableImage(width * aaScale, height * aaScale, SerializableImage.TYPE_INT_ARGB);
SerializableImage imageLarger = new SerializableImage(width, height, SerializableImage.TYPE_INT_ARGB);
imageLarger.fillTransparent();
drawable.toImage(0, 0, 0, new RenderContext(), imageLarger, imageLarger, false, m, new Matrix(), m, m, null, scale * aaScale, false, viewRectangle, viewRectangle, true, Timeline.DRAW_MODE_ALL, 0, !Configuration.disableBitmapSmoothing.get(), aaScale);
if (aaScale > 1) {
SerializableImage imageNormalSize = new SerializableImage(width, height, SerializableImage.TYPE_INT_ARGB);
imageNormalSize.fillTransparent();
imageNormalSize.getGraphics().drawImage(imageLarger.getBufferedImage().getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null);
return imageNormalSize;
}
drawable.toImage(0, 0, 0, new RenderContext(), imageLarger, imageLarger, false, m, new Matrix(), m, m, null, scale, false, viewRectangle, viewRectangle, true, Timeline.DRAW_MODE_ALL, 0, !Configuration.disableBitmapSmoothing.get(), aaScale);
return imageLarger;
}

View File

@@ -5081,7 +5081,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
Matrix m = Matrix.getTranslateInstance(-rect.Xmin * zoomDouble, -rect.Ymin * zoomDouble);
m.scale(zoomDouble);
int aaScale = Configuration.reduceAntialiasConflationByScalingForDisplay.get() ? Configuration.reduceAntialiasConflationByScalingValueForDisplay.get() : 1;
int aaScale = Configuration.useMsaaForDisplay.get() ? Configuration.msaaGridForDisplay.get() : 1;
textTag.toImage(0, 0, 0, new RenderContext(), image, image, false, m, m, m, m, new ConstantColorColorTransform(0xFFC0C0C0), zoomDouble, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, false, aaScale);
@@ -5217,12 +5217,10 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
) {
Timeline timeline = drawable.getTimeline();
SerializableImage img;
int aaScale = Configuration.reduceAntialiasConflationByScalingForDisplay.get() ? Configuration.reduceAntialiasConflationByScalingValueForDisplay.get() : 1;
int aaScale = Configuration.useMsaaForDisplay.get() ? Configuration.msaaGridForDisplay.get() : 1;
aaScale = Configuration.calculateRealAaScale((int) viewRect.getWidth(), (int) viewRect.getHeight(), zoom, aaScale);
int width = aaScale * (int) (viewRect.getWidth() * zoom);
int height = aaScale * (int) (viewRect.getHeight() * zoom);
int width = (int) (viewRect.getWidth() * zoom);
int height = (int) (viewRect.getHeight() * zoom);
if (width == 0) {
width = 1;
}
@@ -5231,18 +5229,14 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
}
Rectangle realRectAA = new Rectangle(realRect);
realRectAA.x *= aaScale;
realRectAA.y *= aaScale;
realRectAA.width *= aaScale;
realRectAA.height *= aaScale;
SerializableImage image = new SerializableImage((int) Math.ceil(width / SWF.unitDivisor),
(int) Math.ceil(height / SWF.unitDivisor), SerializableImage.TYPE_INT_ARGB);
image.fillTransparent();
Matrix m = new Matrix();
m.translate(-viewRect.xMin * zoom * aaScale, -viewRect.yMin * zoom * aaScale);
m.scale(zoom * aaScale);
m.translate(-viewRect.xMin * zoom, -viewRect.yMin * zoom);
m.scale(zoom);
Matrix fullM = m.clone();
@@ -5279,7 +5273,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
if (Configuration.showGrid.get() && (drawable instanceof SWF) && !Configuration.gridOverObjects.get()) {
Graphics2D g = (Graphics2D) image.getBufferedImage().getGraphics();
drawGridSwf(g, realRectAA, zoom * aaScale);
drawGridSwf(g, realRectAA, zoom);
}
parentMatrix = new Matrix();
@@ -5291,7 +5285,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
ignoreDepths.add(parentDepthState.depth);
if (Configuration.halfTransparentParentLayersEasy.get()) {
parentTimelined.getTimeline().toImage(parentFrames.get(i), 0, new RenderContext(), image, image, false,
parentMatrix.preConcatenate(m), new Matrix(), parentMatrix.preConcatenate(m), null, zoom * aaScale, true, viewRect, viewRect, parentMatrix.preConcatenate(m), true, Timeline.DRAW_MODE_ALL, 0, !Configuration.disableBitmapSmoothing.get(),
parentMatrix.preConcatenate(m), new Matrix(), parentMatrix.preConcatenate(m), null, zoom, true, viewRect, viewRect, parentMatrix.preConcatenate(m), true, Timeline.DRAW_MODE_ALL, 0, !Configuration.disableBitmapSmoothing.get(),
ignoreDepths, aaScale);
}
parentMatrix = parentMatrix.concatenate(new Matrix(parentDepthState.matrix));
@@ -5304,12 +5298,9 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
g.fillRect(realRectAA.x, realRectAA.y, realRectAA.width, realRectAA.height);
}
timeline.toImage(frame, time, renderContext, image, image, false, parentMatrix.preConcatenate(m), new Matrix(), parentMatrix.preConcatenate(m), null, zoom * aaScale, true, viewRect, viewRect, parentMatrix.preConcatenate(m), true, Timeline.DRAW_MODE_ALL, 0, !Configuration.disableBitmapSmoothing.get(), ignoreDepths, aaScale);
if (Configuration.reduceAntialiasConflationByScalingForDisplay.get()) {
image = new SerializableImage(ImageResizer.resizeImage(image.getBufferedImage(), image.getWidth() / aaScale, image.getHeight() / aaScale, RenderingHints.VALUE_INTERPOLATION_BICUBIC, true));
}
timeline.toImage(frame, time, renderContext, image, image, false, parentMatrix.preConcatenate(m), new Matrix(), parentMatrix.preConcatenate(m), null, zoom, true, viewRect, viewRect, parentMatrix.preConcatenate(m), true, Timeline.DRAW_MODE_ALL, 0, !Configuration.disableBitmapSmoothing.get(), ignoreDepths, aaScale);
Graphics2D gg = (Graphics2D) image.getGraphics();
gg.setStroke(new BasicStroke(3));
gg.setPaint(Color.green);
@@ -5885,12 +5876,14 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
first = false;
CharacterTag c = ds.getCharacter();
ret.append(tagNameResolver.getTagName(c));
if (ds.depth > -1) {
ret.append(" ");
ret.append(AppStrings.translate("imagePanel.depth"));
ret.append(" ");
ret.append(ds.depth);
if (c != null) {
ret.append(tagNameResolver.getTagName(c));
if (ds.depth > -1) {
ret.append(" ");
ret.append(AppStrings.translate("imagePanel.depth"));
ret.append(" ");
ret.append(ds.depth);
}
}
}

View File

@@ -2275,7 +2275,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
selFile2 = selFile;
}
int aaScale = Configuration.reduceAntialiasConflationByScalingForExport.get() ? Configuration.reduceAntialiasConflationByScalingValueForExport.get() : 1;
int aaScale = Configuration.useMsaaForExport.get() ? Configuration.msaaGridForExport.get() : 1;
EventListener evl = swf.getExportEventListener();
@@ -2395,7 +2395,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
return;
}
int aaScale = Configuration.reduceAntialiasConflationByScalingForExport.get() ? Configuration.reduceAntialiasConflationByScalingValueForExport.get() : 1;
int aaScale = Configuration.useMsaaForExport.get() ? Configuration.msaaGridForExport.get() : 1;
EventListener evl = swf.getExportEventListener();
@@ -2491,7 +2491,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
public void exportAllDebug(SWF swf, AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException, InterruptedException {
EventListener evl = swf.getExportEventListener();
int aaScale = Configuration.reduceAntialiasConflationByScalingForExport.get() ? Configuration.reduceAntialiasConflationByScalingValueForExport.get() : 1;
int aaScale = Configuration.useMsaaForExport.get() ? Configuration.msaaGridForExport.get() : 1;
if (export.isOptionEnabled(ImageExportMode.class)) {
for (ImageExportMode exportMode : ImageExportMode.values()) {
@@ -5667,7 +5667,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
try {
AbortRetryIgnoreHandler errorHandler = new GuiAbortRetryIgnoreHandler();
int aaScale = Configuration.reduceAntialiasConflationByScalingForExport.get() ? Configuration.reduceAntialiasConflationByScalingValueForExport.get() : 1;
int aaScale = Configuration.useMsaaForExport.get() ? Configuration.msaaGridForExport.get() : 1;
FrameExporter frameExporter = new FrameExporter();
FrameExportSettings fes = new FrameExportSettings(mode, dialog.getZoom(), dialog.isTransparentFrameBackgroundEnabled(), aaScale);

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

View File

@@ -679,3 +679,15 @@ config.description.svgExportGaussianBlur = Use gaussian blur instead of box blur
#after 25.1.3
config.name.exportFlaAs3DisableScriptLayer = FLA export - AS3 - disable script layer
config.description.exportFlaAs3DisableScriptLayer = In ActionScript 3 FLA export, the scripts in frames will stay inside class they belong, instead of putting them separately to Script layer. True = in class, False = in Script layer.
config.name.useMsaaForDisplay = Use Multi sample Anti-aliasing (Display)
config.description.useMsaaForDisplay = Use Multi sample Anti-aliasing 4x4 for display purposes.
config.name.useMsaaForExport = Use Multi sample Anti-aliasing (Export)
config.description.useMsaaForExport = Use Multi sample Anti-aliasing 4x4 for export purposes.
config.name.msaaGridForDisplay = Multi sample Anti-aliasing Grid (Display)
config.description.msaaGridForDisplay = Multi sample Anti-aliasing grid size NxN for display purposes.
config.name.msaaGridForExport = Multi sample Anti-aliasing Grid (Export)
config.description.msaaGridForExport = Multi sample Anti-aliasing grid size NxN for display purposes.

View File

@@ -679,3 +679,15 @@ config.description.svgExportGaussianBlur = Pou\u017e\u00edt gaussovsk\u00fd blur
#after 25.1.3
config.name.exportFlaAs3DisableScriptLayer = Export FLA \u2013 AS3 - zak\u00e1zat vrstvu skript\u016f
config.description.exportFlaAs3DisableScriptLayer = P\u0159i exportu ActionScript 3 FLA z\u016fstanou skripty ve sn\u00edmc\u00edch uvnit\u0159 t\u0159\u00eddy, do kter\u00e9 pat\u0159\u00ed, m\u00edsto aby byly odd\u011blen\u011b um\u00edst\u011bny do vrstvy Script. True = ve t\u0159\u00edd\u011b, False = ve vrstv\u011b Script.
config.name.useMsaaForDisplay = Pou\u017e\u00edt v\u00edcen\u00e1sobn\u00e9 vyhlazov\u00e1n\u00ed (Zobrazen\u00ed)
config.description.useMsaaForDisplay = Pou\u017e\u00edt v\u00edcen\u00e1sobn\u00e9 vyhlazov\u00e1n\u00ed 4x4 pro \u00fa\u010dely zobrazen\u00ed.
config.name.useMsaaForExport = Pou\u017e\u00edt v\u00edcen\u00e1sobn\u00e9 vyhlazov\u00e1n\u00ed (Export)
config.description.useMsaaForExport = Pou\u017e\u00edt v\u00edcen\u00e1sobn\u00e9 vyhlazov\u00e1n\u00ed 4x4 pro \u00fa\u010dely exportu.
config.name.msaaGridForDisplay = M\u0159\u00ed\u017eka v\u00edcen\u00e1sobn\u00e9ho vyhlazov\u00e1n\u00ed (Zobrazen\u00ed)
config.description.msaaGridForDisplay = Velikost m\u0159\u00ed\u017eky v\u00edcen\u00e1sobn\u00e9ho vyhlazov\u00e1n\u00ed NxN pro \u00fa\u010dely zobrazen\u00ed.
config.name.msaaGridForExport = M\u0159\u00ed\u017eka v\u00edcen\u00e1sobn\u00e9ho vyhlazov\u00e1n\u00ed (Export)
config.description.msaaGridForExport = Velikost m\u0159\u00ed\u017eky v\u00edcen\u00e1sobn\u00e9ho vyhlazov\u00e1n\u00ed NxN pro \u00fa\u010dely zobrazen\u00ed.

View File

@@ -608,3 +608,15 @@ config.description.svgExportGaussianBlur = Beim SVG-Export den gau\u00dfschen We
#after 25.1.3
config.name.exportFlaAs3DisableScriptLayer = FLA-Export \u2013 AS3 - Skriptebene deaktivieren
config.description.exportFlaAs3DisableScriptLayer = Beim Export von ActionScript 3 FLA bleiben die Skripte in Frames innerhalb der Klasse, zu der sie geh\u00f6ren, anstatt separat in die Skriptebene verschoben zu werden. True = in der Klasse, False = in der Skriptebene.
config.name.useMsaaForDisplay = Multisample-Anti-Aliasing verwenden (Anzeige)
config.description.useMsaaForDisplay = Multisample-Anti-Aliasing 4x4 f\u00fcr Anzeigezwecke verwenden.
config.name.useMsaaForExport = Multisample-Anti-Aliasing verwenden (Export)
config.description.useMsaaForExport = Multisample-Anti-Aliasing 4x4 f\u00fcr Exportzwecke verwenden.
config.name.msaaGridForDisplay = Multisample-Anti-Aliasing-Raster (Anzeige)
config.description.msaaGridForDisplay = Rastergr\u00f6\u00dfe NxN f\u00fcr Multisample-Anti-Aliasing f\u00fcr Anzeigezwecke.
config.name.msaaGridForExport = Multisample-Anti-Aliasing-Raster (Export)
config.description.msaaGridForExport = Rastergr\u00f6\u00dfe NxN pro Multisample-Anti-Aliasing pro Anzeigezwecke.

View File

@@ -679,3 +679,15 @@ config.description.svgExportGaussianBlur = Pou\u017ei\u0165 gaussovsk\u00fd blur
#after 25.1.3
config.name.exportFlaAs3DisableScriptLayer = Export FLA \u2013 AS3 - zak\u00e1za\u0165 vrstvu skriptov
config.description.exportFlaAs3DisableScriptLayer = Pri exporte ActionScript 3 FLA zostan\u00fa skripty v sn\u00edmkach vn\u00fatri triedy, do ktorej patria, namiesto toho, aby boli oddelene umiestnen\u00e9 do vrstvy Script. True = v triede, False = vo vrstve Script.
config.name.useMsaaForDisplay = Pou\u017ei\u0165 viacn\u00e1sobn\u00e9 vyhladzovanie (Zobrazenie)
config.description.useMsaaForDisplay = Pou\u017ei\u0165 viacn\u00e1sobn\u00e9 vyhladzovanie 4x4 na \u00fa\u010dely zobrazenia.
config.name.useMsaaForExport = Pou\u017ei\u0165 viacn\u00e1sobn\u00e9 vyhladzovanie (Export)
config.description.useMsaaForExport = Pou\u017ei\u0165 viacn\u00e1sobn\u00e9 vyhladzovanie 4x4 na \u00fa\u010dely exportu.
config.name.msaaGridForDisplay = Mrie\u017eka viacn\u00e1sobn\u00e9ho vyhladzovania (Zobrazenie)
config.description.msaaGridForDisplay = Ve\u013ekos\u0165 mrie\u017eky viacn\u00e1sobn\u00e9ho vyhladzovania NxN na \u00fa\u010dely zobrazenia.
config.name.msaaGridForExport = Mrie\u017eka viacn\u00e1sobn\u00e9ho vyhladzovania (Export)
config.description.msaaGridForExport = Ve\u013ekos\u0165 mrie\u017eky viacn\u00e1sobn\u00e9ho vyhladzovania NxN na \u00fa\u010dely zobrazenia.

View File

@@ -124,3 +124,6 @@ morphshapes.apng = APNG
#after 25.1.3
buttons.svg_combined = SVG combined
antialias = Use advanced MSAA renderer (slow)
antialias.hint = Use advanced renderer with multi sample anti-aliasing. More precise, but slower than standard renderer. Use for seamless shape rendering.

View File

@@ -122,3 +122,7 @@ arrow = \ud83e\udc06
#after 25.1.3
buttons.svg_combined = SVG kombinovan\u00e9
antialias = Pou\u017e\u00edt pokro\u010dil\u00fd MSAA renderer (pomal\u00e9)
antialias.hint = Pou\u017e\u00edt pokro\u010dil\u00fd renderer s v\u00edcen\u00e1sobn\u00fdm vyhlazov\u00e1n\u00edm. P\u0159esn\u011bj\u0161\u00ed, ale pomalej\u0161\u00ed ne\u017e standardn\u00ed renderer. Vhodn\u00e9 pro vykreslov\u00e1n\u00ed tvar\u016f bez \u0161v\u016f.

View File

@@ -119,3 +119,6 @@ morphshapes.apng = APNG
#after 25.1.3
buttons.svg_combined = SVG kombiniertes
antialias = Erweiterter MSAA-Renderer verwenden (langsam)
antialias.hint = Erweiterter Renderer mit Multisample-Anti-Aliasing verwenden. Pr\u00e4ziser, aber langsamer als der Standard-Renderer. Geeignet f\u00fcr nahtloses Rendern ohne sichtbare \u00dcberg\u00e4nge zwischen Formen.

View File

@@ -124,3 +124,6 @@ morphshapes.apng = APNG
#after 25.1.3
buttons.svg_combined = SVG kombinovan\u00e9
antialias = Pou\u017ei\u0165 pokro\u010dil\u00fd MSAA renderer (pomal\u00e9)
antialias.hint = Pou\u017ei\u0165 pokro\u010dil\u00fd renderer s viacn\u00e1sobn\u00fdm vyhladzovan\u00edm. Presnej\u0161\u00ed, ale pomal\u0161\u00ed ne\u017e \u0161tandardn\u00fd renderer. Vhodn\u00e9 pre vykres\u013eovanie tvarov bez \u0161vov.

View File

@@ -1149,4 +1149,6 @@ contextmenu.exportXaml = Export XAML
menu.file.import.bulkImport = Bulk import...
menu.file.import.createTagFromFile = Create tag from file...
contextmenu.convertTextType = Convert text type
contextmenu.convertTextType = Convert text type
button.antialias.hint = Use advanced renderer with multi sample anti-aliasing (slow)

View File

@@ -1149,4 +1149,6 @@ contextmenu.exportXaml = Exportovat XAML
menu.file.import.bulkImport = Hromadn\u00fd import...
menu.file.import.createTagFromFile = Vytvo\u0159it tag ze souboru...
contextmenu.convertTextType = Konvertovat typ textu
contextmenu.convertTextType = Konvertovat typ textu
button.antialias.hint = Pou\u017e\u00edt pokro\u010dil\u00fd renderer s v\u00edcen\u00e1sobn\u00fdm vyhlazov\u00e1n\u00edm (pomal\u00e9)

View File

@@ -1010,4 +1010,6 @@ contextmenu.exportXaml = Exportiere als XAML
menu.file.import.bulkImport = Massenimport...
menu.file.import.createTagFromFile = Tag aus Datei erstellen...
contextmenu.convertTextType = Texttyp konvertieren
contextmenu.convertTextType = Texttyp konvertieren
button.antialias.hint = Erweiterter Renderer mit Multisample-Anti-Aliasing verwenden (langsam)

View File

@@ -1149,4 +1149,6 @@ contextmenu.exportXaml = Exportova\u0165 XAML
menu.file.import.bulkImport = Hromadn\u00fd import...
menu.file.import.createTagFromFile = Vytvori\u0165 tag zo s\u00faboru...
contextmenu.convertTextType = Konvertova\u0165 typ textu
contextmenu.convertTextType = Konvertova\u0165 typ textu
button.antialias.hint = Pou\u017ei\u0165 pokro\u010dil\u00fd renderer s viacn\u00e1sobn\u00fdm vyhladzovan\u00edm (pomal\u00e9)

View File

@@ -17,6 +17,7 @@
package com.jpexs.decompiler.flash.gui.player;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.configuration.ConfigurationItemChangeListener;
import com.jpexs.decompiler.flash.gui.AppStrings;
import com.jpexs.decompiler.flash.gui.MainPanel;
import com.jpexs.decompiler.flash.gui.View;
@@ -125,7 +126,9 @@ public class PlayerControls extends JPanel implements MediaDisplayListener {
private final int zeroCharacterWidth;
private JButton selectColorButton;
private final JButton selectColorButton;
private JToggleButton msaaButton;
static {
Font font = new JLabel().getFont();
@@ -152,6 +155,19 @@ public class PlayerControls extends JPanel implements MediaDisplayListener {
selectColorButton = new JButton(View.getIcon("color16"));
selectColorButton.addActionListener(this::selectBkColorButtonActionPerformed);
selectColorButton.setToolTipText(AppStrings.translate("button.selectbkcolor.hint"));
msaaButton = new JToggleButton(View.getIcon("antialias16"));
msaaButton.addActionListener(this::msaaButtonActionPerformed);
msaaButton.setToolTipText(AppStrings.translate("button.antialias.hint"));
msaaButton.setSelected(Configuration.useMsaaForDisplay.get());
Configuration.useMsaaForDisplay.addListener(new ConfigurationItemChangeListener<Boolean>() {
@Override
public void configurationItemChanged(Boolean newValue) {
msaaButton.setSelected(newValue);
}
});
zoomPanel = new ZoomPanel(display);
zoomPanel.setVisible(false);
@@ -162,6 +178,7 @@ public class PlayerControls extends JPanel implements MediaDisplayListener {
snapshotButton.setVisible(false);
graphicButtonsPanel.add(zoomPanel);
graphicButtonsPanel.add(msaaButton);
graphicButtonsPanel.add(selectColorButton);
graphicButtonsPanel.add(snapshotButton);
@@ -499,6 +516,10 @@ public class PlayerControls extends JPanel implements MediaDisplayListener {
putImageToClipBoard(display.printScreen());
}
private void msaaButtonActionPerformed(ActionEvent evt) {
Configuration.useMsaaForDisplay.set(msaaButton.isSelected());
}
private void showButtonActionPerformed(ActionEvent evt) {
display.setDisplayed(showButton.isSelected());
if (!showButton.isSelected()) {