displaying frames without flash player (partially)

This commit is contained in:
Jindra Petk
2013-06-02 14:45:40 +02:00
parent 06fe82c466
commit f0f7a1cb77
52 changed files with 2110 additions and 107 deletions

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash;
import com.jpexs.decompiler.flash.types.CXFORM;
import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.RGBA;
import com.jpexs.decompiler.flash.types.filters.FILTER;
import java.util.List;
/**
*
* @author JPEXS
*/
public class Layer {
public MATRIX matrix;
public String instanceName = null;
public CXFORM colorTransForm = null;
public CXFORMWITHALPHA colorTransFormAlpha = null;
public boolean cacheAsBitmap = false;
public int blendMode = 0;
public List<FILTER> filters = null;
public boolean isVisible = true;
public RGBA backGroundColor = null;
public int characterId = -1;
public int ratio = -1;
public int duration = 0;
public boolean visible = true;
}

View File

@@ -50,6 +50,7 @@ import com.jpexs.decompiler.flash.graph.GraphSourceItemContainer;
import com.jpexs.decompiler.flash.graph.GraphTargetItem;
import com.jpexs.decompiler.flash.gui.FrameNode;
import com.jpexs.decompiler.flash.gui.TagNode;
import com.jpexs.decompiler.flash.helpers.Cache;
import com.jpexs.decompiler.flash.helpers.Helper;
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
@@ -61,6 +62,8 @@ import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag;
import com.jpexs.decompiler.flash.tags.DoInitActionTag;
import com.jpexs.decompiler.flash.tags.ExportAssetsTag;
import com.jpexs.decompiler.flash.tags.JPEGTablesTag;
import com.jpexs.decompiler.flash.tags.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag;
import com.jpexs.decompiler.flash.tags.ShowFrameTag;
import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag;
import com.jpexs.decompiler.flash.tags.SoundStreamHeadTypeTag;
@@ -68,14 +71,31 @@ import com.jpexs.decompiler.flash.tags.SymbolClassTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.VideoFrameTag;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.flash.tags.base.BoundedTag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.Container;
import com.jpexs.decompiler.flash.tags.base.DrawableTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.base.RemoveTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.types.CXFORM;
import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.filters.BlendComposite;
import com.jpexs.decompiler.flash.types.filters.FILTER;
import com.jpexs.decompiler.flash.types.filters.Filtering;
import com.jpexs.decompiler.flash.types.sound.AdpcmDecoder;
import com.jpexs.decompiler.flash.xfl.XFLConverter;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
@@ -1329,4 +1349,320 @@ public class SWF {
public void exportXfl(String outfile, String swfName) {
XFLConverter.convertSWF(this, swfName, outfile, false);
}
public static float twipToPixel(int twip) {
return ((float) twip) / 20.0f;
}
private static class CachedImage implements Serializable {
private int data[];
private int width;
private int height;
private int type;
public CachedImage(BufferedImage img) {
width = img.getWidth();
height = img.getHeight();
type = img.getType();
data = Filtering.getRGB(img, 0, 0, width, height);
}
public BufferedImage getImage() {
BufferedImage ret = new BufferedImage(width, height, type);
Filtering.setRGB(ret, 0, 0, width, height, data);
return ret;
}
}
public static AffineTransform matrixToTransform(MATRIX mat) {
return new AffineTransform(mat.getScaleXFloat(), mat.getRotateSkew0Float(),
mat.getRotateSkew1Float(), mat.getScaleYFloat(),
mat.translateX, mat.translateY);
}
private static Cache cache = new Cache(false);
public static RECT fixRect(RECT rect) {
RECT ret = new RECT();
ret.Xmin = rect.Xmin;
ret.Xmax = rect.Xmax;
ret.Ymin = rect.Ymin;
ret.Ymax = rect.Ymax;
if (ret.Xmin < 0) {
ret.Xmax += (-ret.Xmin);
ret.Xmin = 0;
}
if (ret.Ymin < 0) {
ret.Ymax += (-ret.Ymin);
ret.Ymin = 0;
}
return ret;
}
public static BufferedImage frameToImage(int containerId, int maxDepth, HashMap<Integer, Layer> layers, Color backgroundColor, HashMap<Integer, CharacterTag> characters, int frame, List<Tag> allTags, List<Tag> controlTags, RECT displayRect) {
displayRect = fixRect(displayRect);
String key = "frame_" + frame + "_" + containerId;
if (cache.contains(key)) {
return ((CachedImage) cache.get(key)).getImage();
}
float unzoom = 20;
BufferedImage ret = new BufferedImage((int) (displayRect.Xmax / unzoom), (int) (displayRect.Ymax / unzoom), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) ret.getGraphics();
g.setPaint(backgroundColor);
g.fill(new Rectangle(ret.getWidth(), ret.getHeight()));
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (int i = 1; i <= maxDepth; i++) {
if (!layers.containsKey(i)) {
continue;
}
Layer layer = layers.get(i);
if (!characters.containsKey(layer.characterId)) {
continue;
}
if (!layer.visible) {
continue;
}
CharacterTag character = characters.get(layer.characterId);
MATRIX mat = new MATRIX(layer.matrix);
if (mat == null) {
mat = new MATRIX();
}
mat.translateX /= 20;
mat.translateY /= 20;
AffineTransform trans = matrixToTransform(mat);
//trans.translate(transX, transY);
g.setTransform(trans);
if (character instanceof DrawableTag) {
DrawableTag drawable = (DrawableTag) character;
BufferedImage img = drawable.toImage(1/*layer.duration*/, allTags, displayRect, characters);
if (layer.filters != null) {
for (FILTER filter : layer.filters) {
img = filter.apply(img);
}
}
if (layer.colorTransForm != null) {
img = layer.colorTransForm.apply(img);
}
if (layer.colorTransFormAlpha != null) {
img = layer.colorTransFormAlpha.apply(img);
}
Point imgPos = drawable.getImagePos(characters);
if (imgPos.x < 0) {
imgPos.x = 0;
}
if (imgPos.y < 0) {
imgPos.y = 0;
}
switch (layer.blendMode) {
case 0:
case 1:
g.setComposite(AlphaComposite.SrcOver);
break;
case 2: //TODO:Layer
g.setComposite(AlphaComposite.SrcOver);
break;
case 3:
g.setComposite(BlendComposite.Multiply);
break;
case 4:
g.setComposite(BlendComposite.Screen);
break;
case 5:
g.setComposite(BlendComposite.Lighten);
break;
case 6:
g.setComposite(BlendComposite.Darken);
break;
case 7:
g.setComposite(BlendComposite.Difference);
break;
case 8:
g.setComposite(BlendComposite.Add);
break;
case 9:
g.setComposite(BlendComposite.Subtract);
break;
case 10:
g.setComposite(BlendComposite.Invert);
break;
case 11:
g.setComposite(BlendComposite.Alpha);
break;
case 12:
g.setComposite(BlendComposite.Erase);
break;
case 13:
g.setComposite(BlendComposite.Overlay);
break;
case 14:
g.setComposite(BlendComposite.HardLight);
break;
default: //Not implemented
g.setComposite(AlphaComposite.SrcOver);
break;
}
g.drawImage(img, imgPos.x, imgPos.y, null);
} else if (character instanceof BoundedTag) {
BoundedTag b = (BoundedTag) character;
g.setPaint(new Color(255, 255, 255, 128));
g.setComposite(BlendComposite.Invert);
RECT r = b.getRect(characters);
g.drawString(character.toString(), (r.Xmin) / 20 + 3, (r.Ymin) / 20 + 15);
g.draw(new Rectangle(r.Xmin / 20, r.Ymin / 20, r.getWidth() / 20, r.getHeight() / 20));
g.drawLine(r.Xmin / 20, r.Ymin / 20, r.Xmax / 20, r.Ymax / 20);
g.drawLine(r.Xmax / 20, r.Ymin / 20, r.Xmin / 20, r.Ymax / 20);
g.setComposite(AlphaComposite.Dst);
}
}
g.setTransform(AffineTransform.getScaleInstance(1, 1));
/*g.setPaint(Color.yellow);
g.draw(new Rectangle(ret.getWidth()-1,ret.getHeight()-1));*/
cache.put(key, new CachedImage(ret));
return ret;
}
public static BufferedImage frameToImage(int containerId, int frame, List<Tag> allTags, List<Tag> controlTags, RECT displayRect, int totalFrameCount) {
List<BufferedImage> ret = new ArrayList<BufferedImage>();
framesToImage(containerId, ret, frame, frame, allTags, controlTags, displayRect, totalFrameCount);
if (ret.isEmpty()) {
return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
}
return ret.get(0);
}
public static void framesToImage(int containerId, List<BufferedImage> ret, int startFrame, int stopFrame, List<Tag> allTags, List<Tag> controlTags, RECT displayRect, int totalFrameCount) {
for (int i = startFrame; i <= stopFrame; i++) {
String key = "frame_" + i + "_" + containerId;
if (cache.contains(key)) {
ret.add(((CachedImage) cache.get(key)).getImage());
startFrame++;
} else {
break;
}
}
if (startFrame > stopFrame) {
return;
}
if (totalFrameCount == 0) {
return;
}
while (startFrame > totalFrameCount) {
startFrame -= totalFrameCount;
}
while (stopFrame > totalFrameCount) {
stopFrame -= totalFrameCount;
}
HashMap<Integer, CharacterTag> characters = new HashMap<Integer, CharacterTag>();
for (Tag t : allTags) {
if (t instanceof CharacterTag) {
CharacterTag ch = (CharacterTag) t;
characters.put(ch.getCharacterID(), ch);
}
}
HashMap<Integer, Layer> layers = new HashMap<Integer, Layer>();
int maxDepth = 0;
int f = 1;
Color backgroundColor = new Color(0, 0, 0, 0);
for (Tag t : controlTags) {
if (t instanceof SetBackgroundColorTag) {
SetBackgroundColorTag c = (SetBackgroundColorTag) t;
backgroundColor = new Color(c.backgroundColor.red, c.backgroundColor.green, c.backgroundColor.blue);
}
if (t instanceof PlaceObjectTypeTag) {
PlaceObjectTypeTag po = (PlaceObjectTypeTag) t;
int depth = po.getDepth();
if (depth > maxDepth) {
maxDepth = depth;
}
if (!layers.containsKey(depth)) {
layers.put(depth, new Layer());
}
Layer layer = layers.get(depth);
int characterId = po.getCharacterId();
if (characterId != -1) {
layer.characterId = characterId;
}
layer.visible = po.isVisible();
if (po.flagMove()) {
MATRIX matrix2 = po.getMatrix();
if (matrix2 != null) {
layer.matrix = matrix2;
}
String instanceName = po.getInstanceName();
if (instanceName != null) {
layer.instanceName = instanceName;
}
CXFORM colorTransForm = po.getColorTransform();
if (colorTransForm != null) {
layer.colorTransForm = colorTransForm;
}
CXFORMWITHALPHA colorTransFormAlpha = po.getColorTransformWithAlpha();
if (colorTransFormAlpha != null) {
layer.colorTransFormAlpha = colorTransFormAlpha;
}
if (po.cacheAsBitmap()) {
layer.cacheAsBitmap = true;
}
int blendMode = po.getBlendMode();
if (blendMode != 0) {
layer.blendMode = blendMode;
}
List<FILTER> filters = po.getFilters();
if (filters != null) {
layer.filters = filters;
}
int ratio = po.getRatio();
if (ratio != -1) {
layer.ratio = ratio;
}
} else {
layer.matrix = po.getMatrix();
layer.instanceName = po.getInstanceName();
layer.colorTransForm = po.getColorTransform();
layer.colorTransFormAlpha = po.getColorTransformWithAlpha();
layer.cacheAsBitmap = po.cacheAsBitmap();
layer.blendMode = po.getBlendMode();
layer.filters = po.getFilters();
layer.ratio = po.getRatio();
}
}
if (t instanceof RemoveTag) {
RemoveTag rt = (RemoveTag) t;
layers.remove(rt.getDepth());
}
for (Layer l : layers.values()) {
l.duration++;
}
if (t instanceof ShowFrameTag) {
if (f > stopFrame) {
break;
}
if ((f >= startFrame) && (f <= stopFrame)) {
ret.add(frameToImage(containerId, maxDepth, layers, backgroundColor, characters, f, allTags, controlTags, displayRect));
}
f++;
}
}
return;
}
}

View File

@@ -46,7 +46,7 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL
private ABCPanel abcPanel;
private int classIndex = -1;
private boolean isStatic = false;
private Cache cache = new Cache();
private Cache cache = new Cache(true);
public ScriptPack getScriptLeaf() {
return script;

View File

@@ -107,7 +107,7 @@ public class ActionPanel extends JPanel implements ActionListener {
private String searchFor;
private boolean searchIgnoreCase;
private boolean searchRegexp;
private Cache cache = new Cache();
private Cache cache = new Cache(true);
private CachedScript getCached(ASMSource pack) {
return (CachedScript) cache.get(pack);

View File

@@ -74,9 +74,9 @@ import com.jpexs.decompiler.flash.tags.base.AloneTag;
import com.jpexs.decompiler.flash.tags.base.BoundedTag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.Container;
import com.jpexs.decompiler.flash.tags.base.DrawableTag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.tags.text.ParseException;
import com.jpexs.decompiler.flash.types.GLYPHENTRY;
@@ -162,9 +162,11 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi
public FlashPlayerPanel flashPanel;
public JPanel displayPanel;
public ImagePanel imagePanel;
public ImagePanel shapeImagePanel;
public ImagePanel previewImagePanel;
public SWFPreviwPanel swfPreviewPanel;
final static String CARDFLASHPANEL = "Flash card";
final static String CARDSHAPEPANEL = "Shape card";
final static String CARDSWFPREVIEWPANEL = "SWF card";
final static String CARDDRAWPREVIEWPANEL = "Draw card";
final static String CARDIMAGEPANEL = "Image card";
final static String CARDEMPTYPANEL = "Empty card";
final static String CARDACTIONSCRIPTPANEL = "ActionScript card";
@@ -189,6 +191,7 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi
private JSplitPane previewSplitPane;
private JButton imageReplaceButton;
private JPanel imageButtonsPanel;
private JCheckBoxMenuItem miInternalViewer;
public void setPercent(int percent) {
progressBar.setValue(percent);
@@ -251,6 +254,12 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi
setTitle(Main.applicationVerName + (Main.DISPLAY_FILENAME ? " - " + Main.getFileTitle() : ""));
JMenuBar menuBar = new JMenuBar();
try {
flashPanel = new FlashPlayerPanel(this);
} catch (FlashUnsupportedException fue) {
}
JMenu menuFile = new JMenu("File");
JMenuItem miOpen = new JMenuItem("Open...");
miOpen.setIcon(View.getIcon("open16"));
@@ -364,6 +373,13 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi
miSearchScript.setIcon(View.getIcon("search16"));
menuTools.add(miSearchScript);
miInternalViewer = new JCheckBoxMenuItem("Use own Flash viewer");
miInternalViewer.setSelected((Boolean) Configuration.getConfig("internalFlashViewer", (Boolean) (flashPanel == null)));
miInternalViewer.setActionCommand("INTERNALVIEWERSWITCH");
miInternalViewer.addActionListener(this);
menuTools.add(miInternalViewer);
menuTools.add(miProxy);
//menuTools.add(menuDeobfuscation);
@@ -431,7 +447,7 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi
}
tagTree = new JTree(new TagTreeModel(createTagList(objs, null), (new File(Main.file)).getName()));
tagTree = new JTree(new TagTreeModel(createTagList(objs, null), new SWFRoot((new File(Main.file)).getName())));
tagTree.addTreeSelectionListener(this);
final JPopupMenu spritePopupMenu = new JPopupMenu();
JMenuItem removeMenuItem = new JMenuItem("Remove");
@@ -577,10 +593,7 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi
Component leftComponent = null;
try {
flashPanel = new FlashPlayerPanel(this);
} catch (FlashUnsupportedException fue) {
}
displayPanel = new JPanel(new CardLayout());
if (flashPanel != null) {
@@ -633,9 +646,21 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi
displayPanel.add(imagesCard, CARDIMAGEPANEL);
JPanel shapesCard = new JPanel(new BorderLayout());
shapeImagePanel = new ImagePanel();
shapesCard.add(shapeImagePanel, BorderLayout.CENTER);
displayPanel.add(shapesCard, CARDSHAPEPANEL);
JPanel previewPanel = new JPanel(new BorderLayout());
previewImagePanel = new ImagePanel();
previewPanel.add(previewImagePanel, BorderLayout.CENTER);
JLabel prevIntLabel = new JLabel("SWF preview (Internal viewer)");
prevIntLabel.setHorizontalAlignment(SwingConstants.CENTER);
prevIntLabel.setBorder(new BevelBorder(BevelBorder.RAISED));
previewPanel.add(prevIntLabel, BorderLayout.NORTH);
shapesCard.add(previewPanel, BorderLayout.CENTER);
displayPanel.add(shapesCard, CARDDRAWPREVIEWPANEL);
swfPreviewPanel = new SWFPreviwPanel();
displayPanel.add(swfPreviewPanel, CARDSWFPREVIEWPANEL);
displayPanel.add(new JPanel(), CARDEMPTYPANEL);
if (actionPanel != null) {
@@ -1146,6 +1171,9 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi
@Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("INTERNALVIEWERSWITCH")) {
Configuration.setConfig("internalFlashViewer", (Boolean) miInternalViewer.isSelected());
}
if (e.getActionCommand().equals("SEARCHAS")) {
if (searchDialog == null) {
searchDialog = new SearchDialog();
@@ -1810,7 +1838,12 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi
} else {
showDetail(DETAILCARDEMPTYPANEL);
}
if (tagObj instanceof DefineVideoStreamTag) {
swfPreviewPanel.stop();
if ((tagObj instanceof SWFRoot) && miInternalViewer.isSelected()) {
showCard(CARDSWFPREVIEWPANEL);
swfPreviewPanel.load(swf);
swfPreviewPanel.play();
} else if (tagObj instanceof DefineVideoStreamTag) {
showCard(CARDEMPTYPANEL);
} else if ((tagObj instanceof DefineSoundTag) || (tagObj instanceof SoundStreamHeadTag) || (tagObj instanceof SoundStreamHead2Tag)) {
showCard(CARDEMPTYPANEL);
@@ -1823,10 +1856,22 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi
imageButtonsPanel.setVisible(((ImageTag) tagObj).importSupported());
showCard(CARDIMAGEPANEL);
imagePanel.setImage(((ImageTag) tagObj).getImage(swf.tags));
} else if (tagObj instanceof ShapeTag) {
showCard(CARDSHAPEPANEL);
shapeImagePanel.setImage(((ShapeTag) tagObj).toImage(swf.tags));
} else if ((tagObj instanceof FrameNode && ((FrameNode) tagObj).isDisplayed()) || (((tagObj instanceof CharacterTag) || (tagObj instanceof FontTag)) && (tagObj instanceof Tag))) {
} else if ((tagObj instanceof DrawableTag) && (!(tagObj instanceof TextTag)) && (miInternalViewer.isSelected())) {
showCard(CARDDRAWPREVIEWPANEL);
previewImagePanel.setImage(((DrawableTag) tagObj).toImage(1, swf.tags, swf.displayRect, characters));
} else if (tagObj instanceof FrameNode && ((FrameNode) tagObj).isDisplayed() && (miInternalViewer.isSelected())) {
showCard(CARDDRAWPREVIEWPANEL);
FrameNode fn = (FrameNode) tagObj;
List<Tag> controlTags = swf.tags;
int containerId = 0;
RECT rect = swf.displayRect;
if (fn.getParent() instanceof DefineSpriteTag) {
controlTags = ((DefineSpriteTag) fn.getParent()).subTags;
containerId = ((DefineSpriteTag) fn.getParent()).spriteId;
rect = ((DefineSpriteTag) fn.getParent()).getRect(characters);
}
previewImagePanel.setImage(SWF.frameToImage(containerId, ((FrameNode) tagObj).getFrame(), swf.tags, controlTags, rect, swf.frameCount));
} else if (((tagObj instanceof FrameNode) && ((FrameNode) tagObj).isDisplayed()) || ((tagObj instanceof CharacterTag) || (tagObj instanceof FontTag)) && (tagObj instanceof Tag)) {
try {
if (tempFile != null) {
@@ -2025,7 +2070,7 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi
public void refreshTree() {
List<Object> objs = new ArrayList<Object>();
objs.addAll(swf.tags);
tagTree.setModel(new TagTreeModel(createTagList(objs, null), (new File(Main.file)).getName()));
tagTree.setModel(new TagTreeModel(createTagList(objs, null), new SWFRoot((new File(Main.file)).getName())));
}
public void setEditText(boolean edit) {

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.gui;
import com.jpexs.decompiler.flash.SWF;
import java.awt.BorderLayout;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.BevelBorder;
/**
*
* @author JPEXS
*/
public class SWFPreviwPanel extends JPanel {
ImagePanel pan;
Timer timer;
int frame = 0;
List<BufferedImage> frameImages = new ArrayList<BufferedImage>();
JLabel buffering = new JLabel("Buffering...");
public SWFPreviwPanel() {
pan = new ImagePanel();
setLayout(new BorderLayout());
add(pan, BorderLayout.CENTER);
add(buffering, BorderLayout.SOUTH);
buffering.setHorizontalAlignment(JLabel.CENTER);
buffering.setVisible(false);
JLabel prevLabel = new JLabel("SWF preview (Internal viewer)");
prevLabel.setHorizontalAlignment(SwingConstants.CENTER);
prevLabel.setBorder(new BevelBorder(BevelBorder.RAISED));
add(prevLabel, BorderLayout.NORTH);
}
private SWF swf;
public void load(final SWF swf) {
this.swf = swf;
new Thread() {
@Override
public void run() {
buffering.setVisible(true);
SWF.framesToImage(0, frameImages, 1, swf.frameCount, swf.tags, swf.tags, swf.displayRect, swf.frameCount);
buffering.setVisible(false);
}
}.start();
}
public void stop() {
if (timer != null) {
timer.cancel();
timer = null;
}
}
public void play() {
if (swf == null) {
return;
}
stop();
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
int newframe = (frame == swf.frameCount ? 1 : frame + 1);
if (frameImages.size() >= newframe) {
pan.setImage(frameImages.get(newframe - 1));
frame = newframe;
}
}
}, 0, 1000 / swf.frameRate);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.gui;
/**
*
* @author JPEXS
*/
public class SWFRoot {
private String name;
public SWFRoot(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}

View File

@@ -26,10 +26,10 @@ import javax.swing.tree.TreePath;
public class TagTreeModel implements TreeModel {
private String root = "";
private Object root;
private List<TagNode> list = new ArrayList<TagNode>();
public TagTreeModel(List<TagNode> list, String rootName) {
public TagTreeModel(List<TagNode> list, Object rootName) {
this.root = rootName;
this.list = list;
}

View File

@@ -22,6 +22,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
@@ -33,7 +34,15 @@ import java.util.logging.Logger;
*/
public class Cache {
private Map<Object, File> cacheFiles = new WeakHashMap<Object, File>();
private Map<Object, File> cacheFiles;
public Cache(boolean weak) {
if (weak) {
cacheFiles = new WeakHashMap<Object, File>();
} else {
cacheFiles = new HashMap<Object, File>();
}
}
public boolean contains(Object key) {
return cacheFiles.containsKey(key);

View File

@@ -16,7 +16,9 @@
*/
package com.jpexs.decompiler.flash.tags;
import com.jpexs.decompiler.flash.Layer;
import com.jpexs.decompiler.flash.Main;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.abc.CopyOutputStream;
@@ -27,6 +29,9 @@ import com.jpexs.decompiler.flash.tags.base.Container;
import com.jpexs.decompiler.flash.types.BUTTONCONDACTION;
import com.jpexs.decompiler.flash.types.BUTTONRECORD;
import com.jpexs.decompiler.flash.types.RECT;
import java.awt.Color;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -212,4 +217,30 @@ public class DefineButton2Tag extends CharacterTag implements Container, Bounded
public boolean trackAsMenu() {
return trackAsMenu;
}
@Override
public BufferedImage toImage(int frame, List<Tag> tags, RECT displayRect, HashMap<Integer, CharacterTag> characters) {
HashMap<Integer, Layer> layers = new HashMap<Integer, Layer>();
int maxDepth = 0;
for (BUTTONRECORD r : this.characters) {
if (r.buttonStateUp) {
Layer layer = new Layer();
layer.colorTransFormAlpha = r.colorTransform;
layer.blendMode = r.blendMode;
layer.filters = r.filterList;
layer.matrix = r.placeMatrix;
layer.characterId = r.characterId;
if (r.placeDepth > maxDepth) {
maxDepth = r.placeDepth;
}
layers.put(r.placeDepth, layer);
}
}
return SWF.frameToImage(buttonId, maxDepth, layers, new Color(0, 0, 0, 0), characters, 1, tags, tags, displayRect);
}
@Override
public Point getImagePos(HashMap<Integer, CharacterTag> characters) {
return new Point(0, 0);
}
}

View File

@@ -18,8 +18,10 @@ package com.jpexs.decompiler.flash.tags;
import com.jpexs.decompiler.flash.Configuration;
import com.jpexs.decompiler.flash.DisassemblyListener;
import com.jpexs.decompiler.flash.Layer;
import com.jpexs.decompiler.flash.Main;
import com.jpexs.decompiler.flash.ReReadableInputStream;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.abc.CopyOutputStream;
@@ -30,6 +32,9 @@ import com.jpexs.decompiler.flash.tags.base.ButtonTag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.types.BUTTONRECORD;
import com.jpexs.decompiler.flash.types.RECT;
import java.awt.Color;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -227,4 +232,30 @@ public class DefineButtonTag extends CharacterTag implements ASMSource, BoundedT
public boolean trackAsMenu() {
return false;
}
@Override
public BufferedImage toImage(int frame, List<Tag> tags, RECT displayRect, HashMap<Integer, CharacterTag> characters) {
HashMap<Integer, Layer> layers = new HashMap<Integer, Layer>();
int maxDepth = 0;
for (BUTTONRECORD r : this.characters) {
if (r.buttonStateUp) {
Layer layer = new Layer();
layer.colorTransFormAlpha = r.colorTransform;
layer.blendMode = r.blendMode;
layer.filters = r.filterList;
layer.matrix = r.placeMatrix;
layer.characterId = r.characterId;
if (r.placeDepth > maxDepth) {
maxDepth = r.placeDepth;
}
layers.put(r.placeDepth, layer);
}
}
return SWF.frameToImage(buttonId, maxDepth, layers, new Color(0, 0, 0, 0), characters, 1, tags, tags, displayRect);
}
@Override
public Point getImagePos(HashMap<Integer, CharacterTag> characters) {
return new Point(0, 0);
}
}

View File

@@ -26,6 +26,7 @@ import com.jpexs.decompiler.flash.tags.text.ParsedSymbol;
import com.jpexs.decompiler.flash.tags.text.TextLexer;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.RGBA;
import java.awt.geom.GeneralPath;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -512,4 +513,9 @@ public class DefineEditTextTag extends CharacterTag implements BoundedTag, TextT
}
return needed;
}
//@Override
public List<GeneralPath> getPaths(List<Tag> tags) {
return null; //FIXME
}
}

View File

@@ -270,4 +270,33 @@ public class DefineFont2Tag extends CharacterTag implements FontTag {
public boolean isItalic() {
return fontFlagsItalic;
}
@Override
public int getDivider() {
return 1;
}
@Override
public int getAscent() {
if (fontFlagsHasLayout) {
return fontAscent;
}
return -1;
}
@Override
public int getDescent() {
if (fontFlagsHasLayout) {
return fontDescent;
}
return -1;
}
@Override
public int getLeading() {
if (fontFlagsHasLayout) {
return fontLeading;
}
return -1;
}
}

View File

@@ -270,4 +270,33 @@ public class DefineFont3Tag extends CharacterTag implements FontTag {
public boolean isItalic() {
return fontFlagsItalic;
}
@Override
public int getDivider() {
return 20;
}
@Override
public int getAscent() {
if (fontFlagsHasLayout) {
return fontAscent;
}
return -1;
}
@Override
public int getDescent() {
if (fontFlagsHasLayout) {
return fontDescent;
}
return -1;
}
@Override
public int getLeading() {
if (fontFlagsHasLayout) {
return fontLeading;
}
return -1;
}
}

View File

@@ -194,4 +194,24 @@ public class DefineFontTag extends CharacterTag implements FontTag {
}
return false;
}
@Override
public int getAscent() {
return -1;
}
@Override
public int getDescent() {
return -1;
}
@Override
public int getLeading() {
return -1;
}
@Override
public int getDivider() {
return 1;
}
}

View File

@@ -25,12 +25,14 @@ import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPE;
import java.awt.geom.GeneralPath;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
@@ -169,4 +171,9 @@ public class DefineMorphShape2Tag extends CharacterTag implements BoundedTag, Mo
public int getShapeNum() {
return 2;
}
//@Override
public List<GeneralPath> getPaths(List<Tag> tags) {
return null; //FIXME
}
}

View File

@@ -25,12 +25,14 @@ import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPE;
import java.awt.geom.GeneralPath;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
@@ -156,4 +158,9 @@ public class DefineMorphShapeTag extends CharacterTag implements BoundedTag, Mor
public int getShapeNum() {
return 1;
}
// @Override
public List<GeneralPath> getPaths(List<Tag> tags) {
return null; //FIXME
}
}

View File

@@ -22,6 +22,9 @@ import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
import java.awt.Point;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -35,6 +38,11 @@ public class DefineShape2Tag extends CharacterTag implements BoundedTag, ShapeTa
public RECT shapeBounds;
public SHAPEWITHSTYLE shapes;
@Override
public Point getImagePos(HashMap<Integer, CharacterTag> characters) {
return new Point(shapeBounds.Xmin / 20, shapeBounds.Ymin / 20);
}
@Override
public int getShapeNum() {
return 2;
@@ -56,7 +64,7 @@ public class DefineShape2Tag extends CharacterTag implements BoundedTag, ShapeTa
}
@Override
public BufferedImage toImage(List<Tag> tags) {
public BufferedImage toImage(int frame, List<Tag> tags, RECT displayRect, HashMap<Integer, CharacterTag> characters) {
return shapes.toImage(2, tags);
}
@@ -77,4 +85,9 @@ public class DefineShape2Tag extends CharacterTag implements BoundedTag, ShapeTa
shapeBounds = sis.readRECT();
shapes = sis.readSHAPEWITHSTYLE(2);
}
@Override
public List<GeneralPath> getPaths(List<Tag> tags) {
return SHAPERECORD.shapeToPaths(tags, 2, shapes.shapeRecords);
}
}

View File

@@ -22,6 +22,9 @@ import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
import java.awt.Point;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -35,6 +38,11 @@ public class DefineShape3Tag extends CharacterTag implements BoundedTag, ShapeTa
public RECT shapeBounds;
public SHAPEWITHSTYLE shapes;
@Override
public Point getImagePos(HashMap<Integer, CharacterTag> characters) {
return new Point(shapeBounds.Xmin / 20, shapeBounds.Ymin / 20);
}
@Override
public int getShapeNum() {
return 3;
@@ -61,7 +69,7 @@ public class DefineShape3Tag extends CharacterTag implements BoundedTag, ShapeTa
}
@Override
public BufferedImage toImage(List<Tag> tags) {
public BufferedImage toImage(int frame, List<Tag> tags, RECT displayRect, HashMap<Integer, CharacterTag> characters) {
return shapes.toImage(3, tags);
}
@@ -77,4 +85,9 @@ public class DefineShape3Tag extends CharacterTag implements BoundedTag, ShapeTa
shapeBounds = sis.readRECT();
shapes = sis.readSHAPEWITHSTYLE(3);
}
@Override
public List<GeneralPath> getPaths(List<Tag> tags) {
return SHAPERECORD.shapeToPaths(tags, 3, shapes.shapeRecords);
}
}

View File

@@ -22,6 +22,9 @@ import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
import java.awt.Point;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -39,6 +42,11 @@ public class DefineShape4Tag extends CharacterTag implements BoundedTag, ShapeTa
public boolean usesScalingStrokes;
public SHAPEWITHSTYLE shapes;
@Override
public Point getImagePos(HashMap<Integer, CharacterTag> characters) {
return new Point(shapeBounds.Xmin / 20, shapeBounds.Ymin / 20);
}
@Override
public int getShapeNum() {
return 4;
@@ -60,7 +68,7 @@ public class DefineShape4Tag extends CharacterTag implements BoundedTag, ShapeTa
}
@Override
public BufferedImage toImage(List<Tag> tags) {
public BufferedImage toImage(int frame, List<Tag> tags, RECT displayRect, HashMap<Integer, CharacterTag> characters) {
return shapes.toImage(4, tags);
}
@@ -86,4 +94,9 @@ public class DefineShape4Tag extends CharacterTag implements BoundedTag, ShapeTa
usesScalingStrokes = sis.readUB(1) == 1;
shapes = sis.readSHAPEWITHSTYLE(4);
}
@Override
public List<GeneralPath> getPaths(List<Tag> tags) {
return SHAPERECORD.shapeToPaths(tags, 4, shapes.shapeRecords);
}
}

View File

@@ -22,6 +22,9 @@ import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
import java.awt.Point;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -74,7 +77,17 @@ public class DefineShapeTag extends CharacterTag implements BoundedTag, ShapeTag
}
@Override
public BufferedImage toImage(List<Tag> tags) {
public BufferedImage toImage(int frame, List<Tag> tags, RECT displayRect, HashMap<Integer, CharacterTag> characters) {
return shapes.toImage(1, tags);
}
@Override
public Point getImagePos(HashMap<Integer, CharacterTag> characters) {
return new Point(shapeBounds.Xmin / 20, shapeBounds.Ymin / 20);
}
@Override
public List<GeneralPath> getPaths(List<Tag> tags) {
return SHAPERECORD.shapeToPaths(tags, 1, shapes.shapeRecords);
}
}

View File

@@ -17,15 +17,19 @@
package com.jpexs.decompiler.flash.tags;
import com.jpexs.decompiler.flash.Main;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.abc.CopyOutputStream;
import com.jpexs.decompiler.flash.tags.base.BoundedTag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.Container;
import com.jpexs.decompiler.flash.tags.base.DrawableTag;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.RECT;
import java.awt.Point;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -39,7 +43,7 @@ import java.util.Set;
/**
* Defines a sprite character
*/
public class DefineSpriteTag extends CharacterTag implements Container, BoundedTag {
public class DefineSpriteTag extends CharacterTag implements Container, BoundedTag, DrawableTag {
/**
* Character ID of sprite
@@ -83,30 +87,43 @@ public class DefineSpriteTag extends CharacterTag implements Container, BoundedT
RECT ret = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE);
HashMap<Integer, Integer> depthMap = new HashMap<Integer, Integer>();
for (Tag t : subTags) {
Set<Integer> needed = t.getNeededCharacters();
MATRIX m = null;
int characterId = -1;
if (t instanceof PlaceObjectTypeTag) {
PlaceObjectTypeTag pot = (PlaceObjectTypeTag) t;
m = pot.getMatrix();
int charId = pot.getCharacterId();
if (charId > -1) {
depthMap.put(pot.getDepth(), charId);
characterId = (charId);
} else {
needed.add(depthMap.get(pot.getDepth()));
characterId = (depthMap.get(pot.getDepth()));
}
}
if (needed.isEmpty()) {
if (characterId == -1) {
continue;
}
RECT r = getCharacterBounds(characters, needed);
HashSet<Integer> need = new HashSet<Integer>();
need.add(characterId);
RECT r = getCharacterBounds(characters, need);
if (m != null) {
Point topleft = m.apply(new Point(r.Xmin, r.Ymin));
Point bottomright = m.apply(new Point(r.Xmax, r.Ymax));
r.Xmin = Math.min(topleft.x, bottomright.x);
r.Ymin = Math.min(topleft.y, bottomright.y);
r.Xmax = Math.max(topleft.x, bottomright.x);
r.Ymax = Math.max(topleft.y, bottomright.y);
AffineTransform trans = SWF.matrixToTransform(m);
Point topleft = new Point();
trans.transform(new Point(r.Xmin, r.Ymin), topleft);
Point topright = new Point();
trans.transform(new Point(r.Xmax, r.Ymin), topright);
Point bottomright = new Point();
trans.transform(new Point(r.Xmax, r.Ymax), bottomright);
Point bottomleft = new Point();
trans.transform(new Point(r.Xmin, r.Ymax), bottomleft);
r.Xmin = Math.min(Math.min(Math.min(topleft.x, topright.x), bottomleft.x), bottomright.x);
r.Ymin = Math.min(Math.min(Math.min(topleft.y, topright.y), bottomleft.y), bottomright.y);
r.Xmax = Math.max(Math.max(Math.max(topleft.x, topright.x), bottomleft.x), bottomright.x);
r.Ymax = Math.max(Math.max(Math.max(topleft.y, topright.y), bottomleft.y), bottomright.y);
}
ret.Xmin = Math.min(r.Xmin, ret.Xmin);
ret.Ymin = Math.min(r.Ymin, ret.Ymin);
@@ -200,4 +217,23 @@ public class DefineSpriteTag extends CharacterTag implements Container, BoundedT
}
return ret;
}
@Override
public BufferedImage toImage(int frame, List<Tag> tags, RECT displayRect, HashMap<Integer, CharacterTag> characters) {
/*
rect.Xmax=displayRect.Xmin+rect.getWidth();
rect.Ymax=displayRect.Ymin+rect.getWidth();
rect.Xmin=displayRect.Xmin;
rect.Ymin=displayRect.Ymin;
RECT rect=getRect(characters);
SWF.fixRect(rect);*/
RECT rect = getRect(characters);
return SWF.frameToImage(spriteId, frame, tags, subTags, rect, frameCount);
}
@Override
public Point getImagePos(HashMap<Integer, CharacterTag> characters) {
RECT displayRect = getRect(characters);
return new Point(0, 0); //displayRect.Xmin,displayRect.Ymin);
}
}

View File

@@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.helpers.Helper;
import com.jpexs.decompiler.flash.tags.base.BoundedTag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.DrawableTag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.tags.text.ParseException;
@@ -30,7 +31,15 @@ import com.jpexs.decompiler.flash.types.GLYPHENTRY;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.RGBA;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.decompiler.flash.types.TEXTRECORD;
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -52,7 +61,7 @@ import java.util.regex.Pattern;
*
* @author JPEXS
*/
public class DefineText2Tag extends CharacterTag implements BoundedTag, TextTag {
public class DefineText2Tag extends CharacterTag implements BoundedTag, TextTag, DrawableTag {
public int characterID;
public RECT textBounds;
@@ -427,4 +436,54 @@ public class DefineText2Tag extends CharacterTag implements BoundedTag, TextTag
}
return ret;
}
@Override
public BufferedImage toImage(int frame, List<Tag> tags, RECT displayRect, HashMap<Integer, CharacterTag> characters) {
RECT bound = getBounds();
BufferedImage ret = new BufferedImage(bound.Xmax / 20, bound.Ymax / 20, BufferedImage.TYPE_INT_ARGB);
Color textColor = new Color(0, 0, 0);
FontTag font = null;
int textHeight = 12;
int x = bound.Xmin;
int y = 0;
Graphics2D g = (Graphics2D) ret.getGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
SHAPE glyphs[] = new SHAPE[0];
for (TEXTRECORD rec : textRecords) {
if (rec.styleFlagsHasColor) {
textColor = rec.textColorA.toColor();
}
if (rec.styleFlagsHasFont) {
font = (FontTag) characters.get(rec.fontId);
glyphs = font.getGlyphShapeTable();
textHeight = rec.textHeight;
}
if (rec.styleFlagsHasXOffset) {
x = rec.xOffset * 1000 / textHeight;
}
if (rec.styleFlagsHasYOffset) {
y = rec.yOffset * 1000 / textHeight;
}
for (GLYPHENTRY entry : rec.glyphEntries) {
RECT rect = SHAPERECORD.getBounds(glyphs[entry.glyphIndex].shapeRecords);
rect.Xmax /= font.getDivider();
rect.Xmin /= font.getDivider();
rect.Ymax /= font.getDivider();
rect.Ymin /= font.getDivider();
BufferedImage img = SHAPERECORD.shapeToImage(tags, 4, null, null, glyphs[entry.glyphIndex].shapeRecords, textColor);
g.setTransform(AffineTransform.getScaleInstance(textHeight / 1000f / 20, textHeight / 1000f / 20));
g.drawImage(img, x, y + rect.Ymin, null);
x += entry.glyphAdvance * 1000 / textHeight;
}
}
return ret;
}
@Override
public Point getImagePos(HashMap<Integer, CharacterTag> characters) {
return new Point(0, 0);
}
}

View File

@@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.helpers.Helper;
import com.jpexs.decompiler.flash.tags.base.BoundedTag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.DrawableTag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.tags.text.ParseException;
@@ -30,7 +31,15 @@ import com.jpexs.decompiler.flash.types.GLYPHENTRY;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.RGB;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.decompiler.flash.types.TEXTRECORD;
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -52,7 +61,7 @@ import java.util.regex.Pattern;
*
* @author JPEXS
*/
public class DefineTextTag extends CharacterTag implements BoundedTag, TextTag {
public class DefineTextTag extends CharacterTag implements BoundedTag, TextTag, DrawableTag {
public int characterID;
public RECT textBounds;
@@ -437,4 +446,56 @@ public class DefineTextTag extends CharacterTag implements BoundedTag, TextTag {
}
return ret;
}
@Override
public BufferedImage toImage(int frame, List<Tag> tags, RECT displayRect, HashMap<Integer, CharacterTag> characters) {
RECT bound = getBounds();
BufferedImage ret = new BufferedImage(bound.Xmax / 20, bound.Ymax / 20, BufferedImage.TYPE_INT_ARGB);
Color textColor = new Color(0, 0, 0);
FontTag font = null;
int textHeight = 12;
int x = bound.Xmin;
int y = 0;
Graphics2D g = (Graphics2D) ret.getGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
SHAPE glyphs[] = new SHAPE[0];
for (TEXTRECORD rec : textRecords) {
if (rec.styleFlagsHasColor) {
textColor = rec.textColor.toColor();
}
if (rec.styleFlagsHasFont) {
font = (FontTag) characters.get(rec.fontId);
glyphs = font.getGlyphShapeTable();
textHeight = rec.textHeight;
}
if (rec.styleFlagsHasXOffset) {
x = rec.xOffset * 1000 / textHeight;
}
if (rec.styleFlagsHasYOffset) {
y = rec.yOffset * 1000 / textHeight;
}
for (GLYPHENTRY entry : rec.glyphEntries) {
RECT rect = SHAPERECORD.getBounds(glyphs[entry.glyphIndex].shapeRecords);
rect.Xmax /= font.getDivider();
rect.Xmin /= font.getDivider();
rect.Ymax /= font.getDivider();
rect.Ymin /= font.getDivider();
BufferedImage img = SHAPERECORD.shapeToImage(tags, 1, null, null, glyphs[entry.glyphIndex].shapeRecords, textColor);
g.setTransform(AffineTransform.getScaleInstance(textHeight / 1000f / 20, textHeight / 1000f / 20));
g.drawImage(img, x, y + rect.Ymin, null);
x += entry.glyphAdvance * 1000 / textHeight;
}
}
return ret;
}
@Override
public Point getImagePos(HashMap<Integer, CharacterTag> characters) {
return new Point(0, 0);
}
}

View File

@@ -26,4 +26,5 @@ import java.util.HashMap;
public interface BoundedTag {
public RECT getRect(HashMap<Integer, CharacterTag> characters);
//public List<GeneralPath> getPaths(List<Tag> tags);
}

View File

@@ -23,7 +23,7 @@ import java.util.List;
*
* @author JPEXS
*/
public interface ButtonTag {
public interface ButtonTag extends DrawableTag {
public List<BUTTONRECORD> getRecords();

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.tags.base;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.types.RECT;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.List;
/**
*
* @author JPEXS
*/
public interface DrawableTag {
public BufferedImage toImage(int frame, List<Tag> tags, RECT displayRect, HashMap<Integer, CharacterTag> characters);
public Point getImagePos(HashMap<Integer, CharacterTag> characters);
}

View File

@@ -45,4 +45,12 @@ public interface FontTag extends AloneTag {
public boolean isBold();
public boolean isItalic();
public int getDivider();
public int getAscent();
public int getDescent();
public int getLeading();
}

View File

@@ -18,14 +18,14 @@ package com.jpexs.decompiler.flash.tags.base;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
import java.awt.image.BufferedImage;
import java.awt.geom.GeneralPath;
import java.util.List;
/**
*
* @author JPEXS
*/
public interface ShapeTag {
public interface ShapeTag extends DrawableTag {
public SHAPEWITHSTYLE getShapes();
@@ -33,5 +33,5 @@ public interface ShapeTag {
public int getShapeNum();
public BufferedImage toImage(List<Tag> tags);
public List<GeneralPath> getPaths(List<Tag> tags);
}

View File

@@ -16,6 +16,9 @@
*/
package com.jpexs.decompiler.flash.types;
import com.jpexs.decompiler.flash.types.filters.Filtering;
import java.awt.image.BufferedImage;
/**
* Defines a transform that can be applied to the color space of a graphic
* object.
@@ -57,4 +60,8 @@ public class CXFORM {
* Blue addition value
*/
public int blueAddTerm;
public BufferedImage apply(BufferedImage src) {
return Filtering.colorEffect(src, hasAddTerms ? redAddTerm : 0, hasAddTerms ? greenAddTerm : 0, hasAddTerms ? blueAddTerm : 0, 0, hasMultTerms ? redMultTerm : 255, hasMultTerms ? greenMultTerm : 255, hasMultTerms ? blueMultTerm : 255, 1);
}
}

View File

@@ -16,6 +16,9 @@
*/
package com.jpexs.decompiler.flash.types;
import com.jpexs.decompiler.flash.types.filters.Filtering;
import java.awt.image.BufferedImage;
/**
* Defines a transform that can be applied to the color space of a graphic
* object.
@@ -65,4 +68,8 @@ public class CXFORMWITHALPHA {
*/
public int alphaAddTerm;
public int nbits;
public BufferedImage apply(BufferedImage src) {
return Filtering.colorEffect(src, hasAddTerms ? redAddTerm : 0, hasAddTerms ? greenAddTerm : 0, hasAddTerms ? blueAddTerm : 0, hasAddTerms ? alphaAddTerm : 0, hasMultTerms ? redMultTerm : 255, hasMultTerms ? greenMultTerm : 255, hasMultTerms ? blueMultTerm : 255, hasMultTerms ? alphaMultTerm : 255);
}
}

View File

@@ -63,6 +63,23 @@ public class MATRIX implements Serializable {
public int bitsRotate;
public int bitsScale;
public MATRIX() {
}
public MATRIX(MATRIX m) {
if (m == null) {
return;
}
hasScale = m.hasScale;
hasRotate = m.hasRotate;
scaleX = m.scaleX;
scaleY = m.scaleY;
rotateSkew0 = m.rotateSkew0;
rotateSkew1 = m.rotateSkew1;
translateX = m.translateX;
translateY = m.translateY;
}
@Override
public String toString() {
return "[MATRIX scale:" + getScaleXFloat() + "," + getScaleYFloat() + ", rotate:" + getRotateSkew0Float() + "," + getRotateSkew1Float() + ", translate:" + translateX + "," + translateY + "]";

View File

@@ -64,10 +64,10 @@ public class RECT {
}
public int getWidth() {
return Xmax - Xmin;
return (Xmax - Xmin) < 0 ? 0 : Xmax - Xmin;
}
public int getHeight() {
return Ymax - Ymin;
return (Ymax - Ymin) < 0 ? 0 : Ymax - Ymin;
}
}

View File

@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.types;
import java.awt.Color;
/**
* Represents 24-bit red, green, blue value
*
@@ -60,4 +62,14 @@ public class RGB {
}
return "#" + rh + gh + bh;
}
public Color toColor() {
return new Color(red, green, blue);
}
public RGB(Color color) {
this.red = color.getRed();
this.green = color.getGreen();
this.blue = color.getBlue();
}
}

View File

@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.types;
import java.awt.Color;
/**
* Represents 32-bit red, green, blue and alpha value
*
@@ -87,6 +89,17 @@ public class RGBA {
this.alpha = alpha;
}
public RGBA(Color color) {
this.red = color.getRed();
this.green = color.getGreen();
this.blue = color.getBlue();
this.alpha = color.getAlpha();
}
public RGBA() {
}
public Color toColor() {
return new Color(red, green, blue, alpha);
}
}

View File

@@ -54,7 +54,7 @@ public class SHAPE implements NeedsCharacters {
}
public BufferedImage toImage(int shapeNum, List<Tag> tags) {
return SHAPERECORD.shapeToImage(tags, shapeNum, null, null, numFillBits, numLineBits, shapeRecords);
return SHAPERECORD.shapeToImage(tags, shapeNum, null, null,/* numFillBits, numLineBits, */ shapeRecords);
}
public RECT getBounds() {

View File

@@ -57,7 +57,7 @@ public class SHAPEWITHSTYLE implements NeedsCharacters {
}
public BufferedImage toImage(int shapeNum, List<Tag> tags) {
return SHAPERECORD.shapeToImage(tags, shapeNum, fillStyles, lineStyles, numFillBits, numLineBits, shapeRecords);
return SHAPERECORD.shapeToImage(tags, shapeNum, fillStyles, lineStyles, /*numFillBits, numLineBits,*/ shapeRecords);
}
public RECT getBounds() {

View File

@@ -17,6 +17,7 @@
package com.jpexs.decompiler.flash.types.filters;
import com.jpexs.decompiler.flash.types.RGBA;
import java.awt.image.BufferedImage;
/**
* The Bevel filter creates a smooth bevel on display list objects.
@@ -80,4 +81,15 @@ public class BEVELFILTER extends FILTER {
public BEVELFILTER() {
super(3);
}
@Override
public BufferedImage apply(BufferedImage src) {
int type = Filtering.INNER;
if (onTop && !innerShadow) {
type = Filtering.FULL;
} else if (!innerShadow) {
type = Filtering.OUTER;
}
return Filtering.bevel(src, (int) blurX, (int) blurY, strength, type, highlightColor.toColor(), shadowColor.toColor(), (int) (angle * 180 / Math.PI), (float) distance, knockout, passes);
}
}

View File

@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.types.filters;
import java.awt.image.BufferedImage;
/**
* Blur filter based on a sub-pixel precise median filter
*
@@ -39,4 +41,9 @@ public class BLURFILTER extends FILTER {
public BLURFILTER() {
super(1);
}
@Override
public BufferedImage apply(BufferedImage src) {
return Filtering.blur(src, (int) blurX, (int) blurY, passes);
}
}

View File

@@ -0,0 +1,336 @@
/*
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.types.filters;
import java.awt.Composite;
import java.awt.CompositeContext;
import java.awt.RenderingHints;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DirectColorModel;
import java.awt.image.Raster;
import java.awt.image.RasterFormatException;
import java.awt.image.WritableRaster;
/**
*
* @author JPEXS
*/
public final class BlendComposite implements Composite {
public enum BlendingMode {
LAYER, //TODO!
DARKEN,
MULTIPLY,
LIGHTEN,
SCREEN,
OVERLAY,
HARD_LIGHT,
ADD,
SUBTRACT,
DIFFERENCE,
INVERT,
ALPHA,
ERASE
}
public static final BlendComposite Alpha = new BlendComposite(BlendingMode.ALPHA);
public static final BlendComposite Erase = new BlendComposite(BlendingMode.ERASE);
public static final BlendComposite Invert = new BlendComposite(BlendingMode.INVERT);
public static final BlendComposite Multiply = new BlendComposite(BlendingMode.MULTIPLY);
public static final BlendComposite Screen = new BlendComposite(BlendingMode.SCREEN);
public static final BlendComposite Darken = new BlendComposite(BlendingMode.DARKEN);
public static final BlendComposite Lighten = new BlendComposite(BlendingMode.LIGHTEN);
public static final BlendComposite Overlay = new BlendComposite(BlendingMode.OVERLAY);
public static final BlendComposite HardLight = new BlendComposite(BlendingMode.HARD_LIGHT);
public static final BlendComposite Difference = new BlendComposite(BlendingMode.DIFFERENCE);
public static final BlendComposite Add = new BlendComposite(BlendingMode.ADD);
public static final BlendComposite Subtract = new BlendComposite(BlendingMode.SUBTRACT);
private final float alpha;
private final BlendingMode mode;
private BlendComposite(BlendingMode mode) {
this(mode, 1.0f);
}
private BlendComposite(BlendingMode mode, float alpha) {
this.mode = mode;
if (alpha < 0.0f || alpha > 1.0f) {
throw new IllegalArgumentException(
"alpha must be comprised between 0.0f and 1.0f");
}
this.alpha = alpha;
}
public float getAlpha() {
return alpha;
}
/**
* <p>Returns the blending mode of this composite.</p>
*
* @return the blending mode used by this object
*/
public BlendingMode getMode() {
return mode;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Float.floatToIntBits(alpha) * 31 + mode.ordinal();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof BlendComposite)) {
return false;
}
BlendComposite bc = (BlendComposite) obj;
return mode == bc.mode && alpha == bc.alpha;
}
private static boolean checkComponentsOrder(ColorModel cm) {
if (cm instanceof DirectColorModel
&& cm.getTransferType() == DataBuffer.TYPE_INT) {
DirectColorModel directCM = (DirectColorModel) cm;
return directCM.getRedMask() == 0x00FF0000
&& directCM.getGreenMask() == 0x0000FF00
&& directCM.getBlueMask() == 0x000000FF
&& (directCM.getNumComponents() != 4
|| directCM.getAlphaMask() == 0xFF000000);
}
return false;
}
@Override
public CompositeContext createContext(ColorModel srcColorModel,
ColorModel dstColorModel,
RenderingHints hints) {
if (!checkComponentsOrder(srcColorModel)
|| !checkComponentsOrder(dstColorModel)) {
throw new RasterFormatException("Incompatible color models");
}
return new BlendingContext(this);
}
private static final class BlendingContext implements CompositeContext {
private final Blender blender;
private final BlendComposite composite;
private BlendingContext(BlendComposite composite) {
this.composite = composite;
this.blender = Blender.getBlenderFor(composite);
}
@Override
public void dispose() {
}
@Override
public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
int width = Math.min(src.getWidth(), dstIn.getWidth());
int height = Math.min(src.getHeight(), dstIn.getHeight());
float alpha = composite.getAlpha();
int[] result = new int[4];
int[] srcPixel = new int[4];
int[] dstPixel = new int[4];
int[] srcPixels = new int[width];
int[] dstPixels = new int[width];
for (int y = 0; y < height; y++) {
src.getDataElements(0, y, width, 1, srcPixels);
dstIn.getDataElements(0, y, width, 1, dstPixels);
for (int x = 0; x < width; x++) {
int pixel = srcPixels[x];
srcPixel[0] = (pixel >> 16) & 0xFF;
srcPixel[1] = (pixel >> 8) & 0xFF;
srcPixel[2] = (pixel) & 0xFF;
srcPixel[3] = (pixel >> 24) & 0xFF;
pixel = dstPixels[x];
dstPixel[0] = (pixel >> 16) & 0xFF;
dstPixel[1] = (pixel >> 8) & 0xFF;
dstPixel[2] = (pixel) & 0xFF;
dstPixel[3] = (pixel >> 24) & 0xFF;
blender.blend(srcPixel, dstPixel, result);
dstPixels[x] = ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24
| ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16
| ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) << 8
| (int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF;
}
dstOut.setDataElements(0, y, width, 1, dstPixels);
}
}
}
private static abstract class Blender {
public abstract void blend(int[] src, int[] dst, int[] result);
public static Blender getBlenderFor(BlendComposite composite) {
switch (composite.getMode()) {
case ADD:
return new Blender() {
@Override
public void blend(int[] src, int[] dst, int[] result) {
result[0] = Math.min(255, src[0] + dst[0]);
result[1] = Math.min(255, src[1] + dst[1]);
result[2] = Math.min(255, src[2] + dst[2]);
result[3] = Math.min(255, src[3] + dst[3]);
}
};
case INVERT:
return new Blender() {
@Override
public void blend(int[] src, int[] dst, int[] result) {
result[0] = 255 - dst[0];
result[1] = 255 - dst[1];
result[2] = 255 - dst[2];
result[3] = src[3];
}
};
case ALPHA:
return new Blender() {
@Override
public void blend(int[] src, int[] dst, int[] result) {
result[0] = src[0];
result[1] = src[1];
result[2] = src[2];
result[3] = dst[3]; //?
}
};
case ERASE:
return new Blender() {
@Override
public void blend(int[] src, int[] dst, int[] result) {
result[0] = src[0];
result[1] = src[1];
result[2] = src[2];
result[3] = 255 - dst[3]; //?
}
};
case DARKEN:
return new Blender() {
@Override
public void blend(int[] src, int[] dst, int[] result) {
result[0] = Math.min(src[0], dst[0]);
result[1] = Math.min(src[1], dst[1]);
result[2] = Math.min(src[2], dst[2]);
result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
}
};
case DIFFERENCE:
return new Blender() {
@Override
public void blend(int[] src, int[] dst, int[] result) {
result[0] = Math.abs(dst[0] - src[0]);
result[1] = Math.abs(dst[1] - src[1]);
result[2] = Math.abs(dst[2] - src[2]);
result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
}
};
case HARD_LIGHT:
return new Blender() {
@Override
public void blend(int[] src, int[] dst, int[] result) {
result[0] = src[0] < 128 ? dst[0] * src[0] >> 7
: 255 - ((255 - src[0]) * (255 - dst[0]) >> 7);
result[1] = src[1] < 128 ? dst[1] * src[1] >> 7
: 255 - ((255 - src[1]) * (255 - dst[1]) >> 7);
result[2] = src[2] < 128 ? dst[2] * src[2] >> 7
: 255 - ((255 - src[2]) * (255 - dst[2]) >> 7);
result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
}
};
case LIGHTEN:
return new Blender() {
@Override
public void blend(int[] src, int[] dst, int[] result) {
result[0] = Math.max(src[0], dst[0]);
result[1] = Math.max(src[1], dst[1]);
result[2] = Math.max(src[2], dst[2]);
result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
}
};
case MULTIPLY:
return new Blender() {
@Override
public void blend(int[] src, int[] dst, int[] result) {
result[0] = (src[0] * dst[0]) >> 8;
result[1] = (src[1] * dst[1]) >> 8;
result[2] = (src[2] * dst[2]) >> 8;
result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
}
};
case OVERLAY:
return new Blender() {
@Override
public void blend(int[] src, int[] dst, int[] result) {
result[0] = dst[0] < 128 ? dst[0] * src[0] >> 7
: 255 - ((255 - dst[0]) * (255 - src[0]) >> 7);
result[1] = dst[1] < 128 ? dst[1] * src[1] >> 7
: 255 - ((255 - dst[1]) * (255 - src[1]) >> 7);
result[2] = dst[2] < 128 ? dst[2] * src[2] >> 7
: 255 - ((255 - dst[2]) * (255 - src[2]) >> 7);
result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
}
};
case SCREEN:
return new Blender() {
@Override
public void blend(int[] src, int[] dst, int[] result) {
result[0] = 255 - ((255 - src[0]) * (255 - dst[0]) >> 8);
result[1] = 255 - ((255 - src[1]) * (255 - dst[1]) >> 8);
result[2] = 255 - ((255 - src[2]) * (255 - dst[2]) >> 8);
result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
}
};
case SUBTRACT:
return new Blender() {
@Override
public void blend(int[] src, int[] dst, int[] result) {
result[0] = Math.max(0, src[0] + dst[0] - 256);
result[1] = Math.max(0, src[1] + dst[1] - 256);
result[2] = Math.max(0, src[2] + dst[2] - 256);
result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
}
};
}
throw new IllegalArgumentException("Blender not implemented for "
+ composite.getMode().name());
}
}
}

View File

@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.types.filters;
import java.awt.image.BufferedImage;
/**
* Applies a color transformation on the pixels of a display list object
*
@@ -34,4 +36,16 @@ public class COLORMATRIXFILTER extends FILTER {
public COLORMATRIXFILTER() {
super(6);
}
@Override
public BufferedImage apply(BufferedImage src) {
float matrix2[][] = new float[4][4];
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
matrix2[y][x] = matrix[y * 5 + x];
}
}
//matrix2[4][4] = 1;
return Filtering.colorMatrix(src, matrix2);
}
}

View File

@@ -17,6 +17,7 @@
package com.jpexs.decompiler.flash.types.filters;
import com.jpexs.decompiler.flash.types.RGBA;
import java.awt.image.BufferedImage;
/**
* Two-dimensional discrete convolution filter.
@@ -64,4 +65,17 @@ public class CONVOLUTIONFILTER extends FILTER {
public CONVOLUTIONFILTER() {
super(5);
}
@Override
public BufferedImage apply(BufferedImage src) {
int height = matrix.length;
int width = matrix[0].length;
float matrix2[] = new float[width * height];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
matrix2[y * width + x] = matrix[x][y] * divisor + bias;
}
}
return Filtering.convolution(src, matrix2, width, height);
}
}

View File

@@ -17,6 +17,7 @@
package com.jpexs.decompiler.flash.types.filters;
import com.jpexs.decompiler.flash.types.RGBA;
import java.awt.image.BufferedImage;
/**
* Drop shadow filter based on the same median filter as the blur filter
@@ -72,4 +73,9 @@ public class DROPSHADOWFILTER extends FILTER {
public DROPSHADOWFILTER() {
super(0);
}
@Override
public BufferedImage apply(BufferedImage src) {
return Filtering.dropShadow(src, (int) blurX, (int) blurY, (int) (angle * 180 / Math.PI), distance, dropShadowColor.toColor(), innerShadow, passes, strength, knockout);
}
}

View File

@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.types.filters;
import java.awt.image.BufferedImage;
/**
* Bitmap filter
*
@@ -36,4 +38,8 @@ public class FILTER {
public FILTER(int id) {
this.id = id;
}
public BufferedImage apply(BufferedImage src) {
return src;
}
}

View File

@@ -0,0 +1,490 @@
/*
* Copyright (C) 2010-2013 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 <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.types.filters;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.LinearGradientPaint;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BandCombineOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
/**
*
* @author JPEXS
*/
public class Filtering {
public static final int INNER = 1;
public static final int OUTER = 2;
public static final int FULL = 3;
private static void boxBlurHorizontal(int[] pixels, int mask[], int w, int h, int radius) {
int index = 0;
int[] newColors = new int[w];
for (int y = 0; y < h; y++) {
int hits = 0;
long r = 0;
long g = 0;
long b = 0;
long a = 0;
for (int x = -radius; x < w; x++) {
int oldPixel = x - radius - 1;
if (oldPixel >= 0) {
int color = pixels[index + oldPixel];
if ((mask == null) || (((mask[index + oldPixel] >> 24) & 0xff) > 0)) {
if (color != 0) {
a -= (color >> 24) & 0xff;
r -= ((color >> 16) & 0xff);
g -= ((color >> 8) & 0xff);
b -= ((color) & 0xff);
}
hits--;
}
}
int newPixel = x + radius;
if (newPixel < w) {
int color = pixels[index + newPixel];
if ((mask == null) || (((mask[index + newPixel] >> 24) & 0xff) > 0)) {
if (color != 0) {
a += (color >> 24) & 0xff;
r += ((color >> 16) & 0xff);
g += ((color >> 8) & 0xff);
b += ((color) & 0xff);
}
hits++;
}
}
if (x >= 0) {
if ((mask == null) || (((mask[index + x] >> 24) & 0xff) > 0)) {
if (hits == 0) {
newColors[x] = 0;
} else {
newColors[x] = new Color((int) (r / hits) & 0xff, (int) (g / hits) & 0xff, (int) (b / hits) & 0xff, (int) (a / hits)).getRGB();
}
} else {
newColors[x] = 0;
}
}
}
for (int x = 0; x < w; x++) {
pixels[index + x] = newColors[x];
}
index += w;
}
}
private static void boxBlurVertical(int[] pixels, int mask[], int w, int h, int radius) {
int[] newColors = new int[h];
int oldPixelOffset = -(radius + 1) * w;
int newPixelOffset = (radius) * w;
for (int x = 0; x < w; x++) {
int hits = 0;
long r = 0;
long g = 0;
long b = 0;
long a = 0;
int index = -radius * w + x;
for (int y = -radius; y < h; y++) {
int oldPixel = y - radius - 1;
if (oldPixel >= 0) {
int color = pixels[index + oldPixelOffset];
if ((mask == null) || (((mask[index + oldPixelOffset] >> 24) & 0xff) > 0)) {
if (color != 0) {
a -= (color >> 24) & 0xff;
r -= ((color >> 16) & 0xff);
g -= ((color >> 8) & 0xff);
b -= ((color) & 0xff);
}
hits--;
}
}
int newPixel = y + radius;
if (newPixel < h) {
if ((mask == null) || (((mask[index + newPixelOffset] >> 24) & 0xff) > 0)) {
int color = pixels[index + newPixelOffset];
if (color != 0) {
a += (color >> 24) & 0xff;
r += ((color >> 16) & 0xff);
g += ((color >> 8) & 0xff);
b += ((color) & 0xff);
}
hits++;
}
}
if (y >= 0) {
if ((mask == null) || (((mask[y * w + x] >> 24) & 0xff) > 0)) {
if (hits == 0) {
newColors[y] = 0;
} else {
newColors[y] = new Color((int) (r / hits) & 0xff, (int) (g / hits) & 0xff, (int) (b / hits) & 0xff, (int) (a / hits) & 0xff).getRGB();
}
} else {
newColors[y] = 0;
}
}
index += w;
}
for (int y = 0; y < h; y++) {
pixels[y * w + x] = newColors[y];
}
}
}
private static void premultiply(int p[]) {
int length = p.length;
int offset = 0;
length += offset;
for (int i = offset; i < length; i++) {
int rgb = p[i];
int a = rgb >> 24 & 0xff;
int r = rgb >> 16 & 0xff;
int g = rgb >> 8 & 0xff;
int b = rgb & 0xff;
float f = (float) a * 0.003921569F;
r = (int) ((float) r * f);
g = (int) ((float) g * f);
b = (int) ((float) b * f);
p[i] = a << 24 | r << 16 | g << 8 | b;
}
}
private static void unpremultiply(int p[]) {
int length = p.length;
int offset = 0;
length += offset;
for (int i = offset; i < length; i++) {
int rgb = p[i];
int a = rgb >> 24 & 0xff;
int r = rgb >> 16 & 0xff;
int g = rgb >> 8 & 0xff;
int b = rgb & 0xff;
if (a == 0 || a == 255) {
continue;
}
float f = 255F / (float) a;
r = (int) ((float) r * f);
g = (int) ((float) g * f);
b = (int) ((float) b * f);
if (r > 255) {
r = 255;
}
if (g > 255) {
g = 255;
}
if (b > 255) {
b = 255;
}
p[i] = a << 24 | r << 16 | g << 8 | b;
}
}
public static BufferedImage blur(BufferedImage src, int hRadius, int vRadius, int iterations) {
return blur(src, hRadius, vRadius, iterations, null);
}
private static BufferedImage blur(BufferedImage src, int hRadius, int vRadius, int iterations, int[] mask) {
int width = src.getWidth();
int height = src.getHeight();
BufferedImage dst = new BufferedImage(width, height, src.getType());
int[] inPixels = getRGB(src, 0, 0, width, height);
premultiply(inPixels);
for (int i = 0; i < iterations; i++) {
boxBlurHorizontal(inPixels, mask, width, height, hRadius / 2);
boxBlurVertical(inPixels, mask, width, height, vRadius / 2);
}
unpremultiply(inPixels);
setRGB(dst, 0, 0, width, height, inPixels);
return dst;
}
public static BufferedImage bevel(BufferedImage src, int blurX, int blurY, float strength, int type, Color highlightColor, Color shadowColor, float angle, float distance, boolean knockout, int iterations) {
return gradientBevel(src, new Color[]{
highlightColor,
new Color(highlightColor.getRed(), highlightColor.getGreen(), highlightColor.getBlue(), 0),
new Color(shadowColor.getRed(), shadowColor.getGreen(), shadowColor.getBlue(), 0),
shadowColor
}, new float[]{0, 127f / 255f, 128f / 255f, 1}, blurX, blurY, strength, type, angle, distance, knockout, iterations);
}
public static BufferedImage gradientBevel(BufferedImage src, Color[] colors, float ratios[], int blurX, int blurY, float strength, int type, float angle, float distance, boolean knockout, int iterations) {
int width = src.getWidth();
int height = src.getHeight();
BufferedImage retImg = new BufferedImage(width, height, src.getType());
if (type == FULL) {
BufferedImage partIn = gradientBevel(src, colors, ratios, blurX, blurY, strength, INNER, angle, distance, true, iterations);
BufferedImage partOut = gradientBevel(src, colors, ratios, blurX, blurY, strength, OUTER, angle, distance, true, iterations);
Graphics2D g = (Graphics2D) retImg.getGraphics();
g.drawImage(partIn, 0, 0, null);
g.setComposite(AlphaComposite.SrcOver);
g.drawImage(partOut, 0, 0, null);
} else {
boolean inner = type == INNER;
int srcPixels[] = getRGB(src, 0, 0, width, height);
BufferedImage gradient = new BufferedImage(512, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D gg = (Graphics2D) gradient.getGraphics();
Point pnt1 = new Point(0, 0);
Point pnt2 = new Point(512, 0);
gg.setPaint(new LinearGradientPaint(inner ? pnt1 : pnt2, inner ? pnt2 : pnt1, ratios, colors));
gg.fill(new Rectangle(512, 1));
int gradientPixels[] = getRGB(gradient, 0, 0, gradient.getWidth(), gradient.getHeight());
BufferedImage hilightIm = dropShadow(src, blurX, blurY, angle, distance, Color.black, inner, iterations, strength, true);//new DropShadowFilter(blurX, blurY, strength, inner ? highlightColor : shadowColor, angle, distance, inner, true, iterations).filter(src);
BufferedImage shadowIm = dropShadow(src, blurX, blurY, angle + 180, distance, Color.black, inner, iterations, strength, true); //new DropShadowFilter(blurX, blurY, strength, inner ? shadowColor : highlightColor, angle + 180, distance, inner, true, iterations).filter(src);
int hilight[] = getRGB(hilightIm, 0, 0, width, height);
int shadow[] = getRGB(shadowIm, 0, 0, width, height);
for (int i = 0; i < srcPixels.length; i++) {
int ha = (hilight[i] >> 24) & 0xff;
int sa = (shadow[i] >> 24) & 0xff;
hilight[i] = gradientPixels[255 - ha];
shadow[i] = gradientPixels[256 + sa];
}
for (int i = 0; i < srcPixels.length; i++) {
int ah = (hilight[i] >> 24) & 0xff;
int as = (shadow[i] >> 24) & 0xff;
int ao = (srcPixels[i] >> 24) & 0xff;
if ((ao == 0) || ((ah > 0) && (as > 0))) {
hilight[i] = (hilight[i] & 0x00ffffff) + ((255 - as) << 24);
shadow[i] = (shadow[i] & 0x00ffffff) + ((255 - ah) << 24);
}
}
setRGB(shadowIm, 0, 0, width, height, shadow);
setRGB(hilightIm, 0, 0, width, height, hilight);
shadow = getRGB(shadowIm, 0, 0, width, height);
hilight = getRGB(hilightIm, 0, 0, width, height);
int ret[] = new int[width * height];
for (int i = 0; i < ret.length; i++) {
int ah = (hilight[i] >> 24) & 0xff;
int as = (shadow[i] >> 24) & 0xff;
if (as >= ah) {
ret[i] = (shadow[i] & 0x00ffffff) + ((as - ah) << 24);
} else {
ret[i] = (hilight[i] & 0x00ffffff) + ((ah - as) << 24);
}
}
setRGB(retImg, 0, 0, width, height, ret);
}
if (!knockout) {
Graphics2D g = (Graphics2D) retImg.getGraphics();
g.setComposite(AlphaComposite.DstOver);
g.drawImage(src, 0, 0, null);
}
return retImg;
}
public static BufferedImage glow(BufferedImage src, int blurX, int blurY, float strength, Color color, boolean inner, boolean knockout, int iterations) {
return dropShadow(src, blurX, blurY, 45, 0, color, inner, iterations, strength, knockout);
}
public static BufferedImage dropShadow(BufferedImage src, int blurX, int blurY, float angle, double distance, Color color, boolean inner, int iterations, float strength, boolean knockout) {
return gradientGlow(src, blurX, blurY, angle, distance, new Color[]{new Color(color.getRed(), color.getGreen(), color.getBlue(), 0), color}, new float[]{0, 1}, inner ? INNER : OUTER, iterations, strength, knockout);
}
public static BufferedImage gradientGlow(BufferedImage src, int blurX, int blurY, float angle, double distance, Color colors[], float ratios[], int type, int iterations, float strength, boolean knockout) {
int width = src.getWidth();
int height = src.getHeight();
BufferedImage retImg = new BufferedImage(width, height, src.getType());
if (type == FULL) {
BufferedImage partIn = gradientGlow(src, blurX, blurY, angle, distance, colors, ratios, INNER, iterations, strength, true);
BufferedImage partOut = gradientGlow(src, blurX, blurY, angle, distance, colors, ratios, OUTER, iterations, strength, true);
Graphics2D g = (Graphics2D) retImg.getGraphics();
g.drawImage(partIn, 0, 0, null);
g.setComposite(AlphaComposite.SrcOver);
g.drawImage(partOut, 0, 0, null);
} else {
boolean inner = type == INNER;
BufferedImage gradient = new BufferedImage(256, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D gg = (Graphics2D) gradient.getGraphics();
gg.setPaint(new LinearGradientPaint(new Point(0, 0), new Point(256, 0), ratios, colors));
gg.fill(new Rectangle(256, 1));
int gradientPixels[] = getRGB(gradient, 0, 0, gradient.getWidth(), gradient.getHeight());
double angleRad = angle / 180 * Math.PI;
double moveX = (distance * Math.cos(angleRad));
double moveY = (distance * Math.sin(angleRad));
int srcPixels[] = getRGB(src, 0, 0, width, height);
int revPixels[] = new int[srcPixels.length];
for (int i = 0; i < srcPixels.length; i++) {
int alpha = (srcPixels[i] >> 24) & 0xff;
alpha = 255 - alpha;
revPixels[i] = (srcPixels[i] & 0x00ffffff) + (alpha << 24);
}
int shadow[] = new int[srcPixels.length];
for (int i = 0; i < srcPixels.length; i++) {
int alpha = (srcPixels[i] >> 24) & 0xff;
if (inner) {
alpha = 255 - alpha;
}
shadow[i] = 0 + (Math.round(alpha * strength) << 24);
}
Color colorFirst = Color.black;
Color colorAlpha = new Color(0, 0, 0, 0);
shadow = moveRGB(width, height, shadow, moveX, moveY, inner ? colorFirst : colorAlpha);
setRGB(retImg, 0, 0, width, height, shadow);
retImg = blur(retImg, blurX, blurY, iterations, inner ? srcPixels : revPixels);//new BoxBlurFilter(blurX, blurY, iterations, inner ? srcPixels : revPixels).filter(ret);
shadow = getRGB(retImg, 0, 0, width, height);
for (int i = 0; i < shadow.length; i++) {
int a = (shadow[i] >> 24) & 0xff;
shadow[i] = gradientPixels[a];
}
for (int i = 0; i < shadow.length; i++) {
int srcA = (srcPixels[i] >> 24) & 0xff;
if (!inner) {
srcA = 255 - srcA;
}
int shadA = (shadow[i] >> 24) & 0xff;
shadow[i] = (shadow[i] & 0x00ffffff) + (Math.min(srcA, shadA) << 24);
}
setRGB(retImg, 0, 0, width, height, shadow);
}
if (!knockout) {
Graphics2D g = (Graphics2D) retImg.getGraphics();
//g.setComposite(inner ? AlphaComposite.DstOver : AlphaComposite.SrcOver);
g.setComposite(AlphaComposite.DstOver);
g.drawImage(src, 0, 0, null);
}
return retImg;
}
public static int[] getRGB(BufferedImage image, int x, int y, int width, int height) {
int type = image.getType();
if (type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB) {
return (int[]) image.getRaster().getDataElements(x, y, width, height, null);
}
return image.getRGB(x, y, width, height, null, 0, width);
}
public static void setRGB(BufferedImage image, int x, int y, int width, int height, int[] pixels) {
int type = image.getType();
if (type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB) {
image.getRaster().setDataElements(x, y, width, height, pixels);
} else {
image.setRGB(x, y, width, height, pixels, 0, width);
}
}
private static int[] moveRGB(int width, int height, int rgb[], double deltaX, double deltaY, Color fill) {
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
setRGB(img, 0, 0, width, height, rgb);
BufferedImage retImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) retImg.getGraphics();
g.setPaint(fill);
g.fillRect(0, 0, width, height);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setTransform(AffineTransform.getTranslateInstance(deltaX, deltaY));
g.setComposite(AlphaComposite.Src);
g.drawImage(img, 0, 0, null);
return getRGB(retImg, 0, 0, width, height);
}
public static BufferedImage convolution(BufferedImage src, float matrix[], int w, int h) {
BufferedImage dst = new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
BufferedImageOp op = new ConvolveOp(new Kernel(w, h, matrix), ConvolveOp.EDGE_ZERO_FILL, new RenderingHints(null));
op.filter(src, dst);
return dst;
}
public static BufferedImage colorMatrix(BufferedImage src, float[][] matrix) {
BandCombineOp changeColors = new BandCombineOp(matrix, new RenderingHints(null));
Raster sourceRaster = src.getRaster();
WritableRaster displayRaster = sourceRaster.createCompatibleWritableRaster();
changeColors.filter(sourceRaster, displayRaster);
return new BufferedImage(src.getColorModel(), displayRaster, true, null);
}
private static int cut(double val) {
int i = (int) val;
if (i < 0) {
i = 0;
}
if (i > 255) {
i = 255;
}
return i;
}
public static BufferedImage colorEffect(BufferedImage src,
int redAddTerm, int greenAddTerm, int blueAddTerm, int alphaAddTerm,
int redMultTerm, int greenMultTerm, int blueMultTerm, int alphaMultTerm) {
BufferedImage dst = new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
int rgb[] = getRGB(src, 0, 0, src.getWidth(), src.getHeight());
for (int i = 0; i < rgb.length; i++) {
int a = (rgb[i] >> 24) & 0xff;
int r = (rgb[i] >> 16) & 0xff;
int g = (rgb[i] >> 8) & 0xff;
int b = (rgb[i]) & 0xff;
r = Math.max(0, Math.min(((r * redMultTerm) / 256) + redAddTerm, 255));
g = Math.max(0, Math.min(((g * greenMultTerm) / 256) + greenAddTerm, 255));
b = Math.max(0, Math.min(((b * blueMultTerm) / 256) + blueAddTerm, 255));
a = Math.max(0, Math.min(((a * alphaMultTerm) / 256) + alphaAddTerm, 255));
rgb[i] = (a << 24) | (r << 16) | (g << 8) | (b);
}
setRGB(dst, 0, 0, src.getWidth(), src.getHeight(), rgb);
return dst;
}
}

View File

@@ -17,6 +17,7 @@
package com.jpexs.decompiler.flash.types.filters;
import com.jpexs.decompiler.flash.types.RGBA;
import java.awt.image.BufferedImage;
/**
* Glow filter
@@ -64,4 +65,9 @@ public class GLOWFILTER extends FILTER {
public GLOWFILTER() {
super(2);
}
@Override
public BufferedImage apply(BufferedImage src) {
return Filtering.glow(src, (int) blurX, (int) blurY, strength, glowColor.toColor(), innerGlow, knockout, passes);
}
}

View File

@@ -17,6 +17,10 @@
package com.jpexs.decompiler.flash.types.filters;
import com.jpexs.decompiler.flash.types.RGBA;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
/**
* Bevel filter with gradient instead of single color
@@ -77,4 +81,29 @@ public class GRADIENTBEVELFILTER extends FILTER {
public GRADIENTBEVELFILTER() {
super(7);
}
@Override
public BufferedImage apply(BufferedImage src) {
List<Color> colors = new ArrayList<Color>();
List<Float> ratios = new ArrayList<Float>();
for (int i = 0; i < gradientColors.length; i++) {
if ((i > 0) && (gradientRatio[i - 1] == gradientRatio[i])) {
continue;
}
colors.add(gradientColors[i].toColor());
ratios.add(gradientRatio[i] / 255f);
}
int type = Filtering.INNER;
if (onTop && !innerShadow) {
type = Filtering.FULL;
} else if (!innerShadow) {
type = Filtering.OUTER;
}
float ratiosAr[] = new float[ratios.size()];
for (int i = 0; i < ratios.size(); i++) {
ratiosAr[i] = ratios.get(i);
}
return Filtering.gradientBevel(src, colors.toArray(new Color[colors.size()]), ratiosAr, (int) blurX, (int) blurY, strength, type, (int) (angle * 180 / Math.PI), (float) distance, knockout, passes);
}
}

View File

@@ -17,6 +17,10 @@
package com.jpexs.decompiler.flash.types.filters;
import com.jpexs.decompiler.flash.types.RGBA;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
/**
* Glow filter with gradient instead of single color
@@ -80,4 +84,29 @@ public class GRADIENTGLOWFILTER extends FILTER {
public GRADIENTGLOWFILTER() {
super(4);
}
@Override
public BufferedImage apply(BufferedImage src) {
List<Color> colors = new ArrayList<Color>();
List<Float> ratios = new ArrayList<Float>();
for (int i = 0; i < gradientColors.length; i++) {
if ((i > 0) && (gradientRatio[i - 1] == gradientRatio[i])) {
continue;
}
colors.add(gradientColors[i].toColor());
ratios.add(gradientRatio[i] / 255f);
}
int type = Filtering.INNER;
if (onTop && !innerShadow) {
type = Filtering.FULL;
} else if (!innerShadow) {
type = Filtering.OUTER;
}
float ratiosAr[] = new float[ratios.size()];
for (int i = 0; i < ratios.size(); i++) {
ratiosAr[i] = ratios.get(i);
}
return Filtering.gradientGlow(src, (int) blurX, (int) blurY, (int) (angle * 180 / Math.PI), distance, colors.toArray(new Color[colors.size()]), ratiosAr, type, passes, strength, knockout);
}
}

View File

@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.types.shaperecords;
import com.jpexs.decompiler.flash.SWF;
/**
*
* @author JPEXS
@@ -37,7 +39,7 @@ public class CurvedEdgeRecord extends SHAPERECORD {
@Override
public String toSWG(int oldX, int oldY) {
return "Q " + twipToPixel(oldX + controlDeltaX) + " " + twipToPixel(oldY + controlDeltaY) + " " + twipToPixel(oldX + controlDeltaX + anchorDeltaX) + " " + twipToPixel(oldY + controlDeltaY + anchorDeltaY);
return "Q " + SWF.twipToPixel(oldX + controlDeltaX) + " " + SWF.twipToPixel(oldY + controlDeltaY) + " " + SWF.twipToPixel(oldX + controlDeltaX + anchorDeltaX) + " " + SWF.twipToPixel(oldY + controlDeltaY + anchorDeltaY);
}

View File

@@ -16,6 +16,7 @@
*/
package com.jpexs.decompiler.flash.types.shaperecords;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.base.NeedsCharacters;
@@ -25,7 +26,6 @@ import com.jpexs.decompiler.flash.types.GRADIENT;
import com.jpexs.decompiler.flash.types.LINESTYLE;
import com.jpexs.decompiler.flash.types.LINESTYLE2;
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.RGB;
import com.jpexs.decompiler.flash.types.RGBA;
@@ -37,11 +37,13 @@ import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.TexturePaint;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -54,9 +56,7 @@ import java.util.logging.Logger;
*/
public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
public static float twipToPixel(int twip) {
return ((float) twip) / 20.0f;
}
private static final float DESCALE = 20; //20
@Override
public Set<Integer> getNeededCharacters() {
@@ -75,7 +75,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
public void draw(int startX, int startY, Graphics2D g, int shapeNum) {
AffineTransform oldAf = g.getTransform();
AffineTransform trans20 = AffineTransform.getScaleInstance(1 / 20.0, 1 / 20.0);
AffineTransform trans20 = AffineTransform.getScaleInstance(1 / DESCALE, 1 / DESCALE);
//g.setTransform(trans20);
boolean ok = false;
if (shapeNum == 4) {
@@ -108,9 +108,9 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
break;
}
if (joinStyle == BasicStroke.JOIN_MITER) {
g.setStroke(new BasicStroke(lineStyle2.width / 20, capStyle, joinStyle, lineStyle2.miterLimitFactor));
g.setStroke(new BasicStroke(lineStyle2.width / DESCALE, capStyle, joinStyle, lineStyle2.miterLimitFactor));
} else {
g.setStroke(new BasicStroke(lineStyle2.width / 20, capStyle, joinStyle));
g.setStroke(new BasicStroke(lineStyle2.width / DESCALE, capStyle, joinStyle));
}
ok = true;
}
@@ -118,7 +118,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
if (lineStyle == null) {
ok = false;
} else {
g.setStroke(new BasicStroke(lineStyle.width / 20, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g.setStroke(new BasicStroke(lineStyle.width / DESCALE, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
ok = true;
}
}
@@ -129,29 +129,11 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
g.setClip(null);
}
private AffineTransform matrixToTransform(MATRIX mat) {
return new AffineTransform(mat.getScaleXFloat(), mat.getRotateSkew0Float(),
mat.getRotateSkew1Float(), mat.getScaleYFloat(),
mat.translateX, mat.translateY);
//mat.translateX,mat.translateY);
/*AffineTransform move=AffineTransform.getTranslateInstance(mat.translateX, mat.translateY);
AffineTransform rotate =AffineTransform.getRotateInstance(mat.getRotateSkew0Float(), mat.getRotateSkew1Float());
AffineTransform scale=AffineTransform.getScaleInstance(mat.getScaleXFloat(), mat.getScaleYFloat());
AffineTransform af=scale;
AffineTransform scale20=AffineTransform.getScaleInstance(1/20.0, 1/20.0);
//af.concatenate(move);
//af.concatenate(scale20);
af.concatenate(rotate);
af.concatenate(scale);
return af;*/
}
public void fill(List<Tag> tags, int startX, int startY, Graphics2D g, int shapeNum) {
AffineTransform oldAf = g.getTransform();
AffineTransform trans20 = AffineTransform.getScaleInstance(1 / 20.0, 1 / 20.0);
AffineTransform trans20 = AffineTransform.getScaleInstance(1 / DESCALE, 1 / DESCALE);
g.setTransform(trans20);
int maxRepeat = 200;
int maxRepeat = 10; //TODO:better handle gradient repeating
boolean ok = false;
switch (fillStyle0.fillStyleType) {
case FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP:
@@ -170,9 +152,9 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
}
if (image != null) {
g.setClip(toGeneralPath(startX, startY));
AffineTransform btrans = matrixToTransform(fillStyle0.bitmapMatrix);
btrans.preConcatenate(AffineTransform.getScaleInstance(1 / 20.0, 1 / 20.0));
btrans.preConcatenate(AffineTransform.getTranslateInstance(startX / 20, startY / 20));
AffineTransform btrans = SWF.matrixToTransform(fillStyle0.bitmapMatrix);
btrans.preConcatenate(AffineTransform.getScaleInstance(1 / DESCALE, 1 / DESCALE));
btrans.preConcatenate(AffineTransform.getTranslateInstance(startX / DESCALE, startY / DESCALE));
g.setTransform(btrans);
BufferedImage img = image.getImage(tags);
g.setPaint(new TexturePaint(img, new Rectangle(img.getWidth(), img.getHeight())));
@@ -202,9 +184,9 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
GeneralPath focPath = toGeneralPath(startX, startY);
g.fill(focPath);
g.setClip(focPath);
AffineTransform focTrans = matrixToTransform(fillStyle0.gradientMatrix);
focTrans.preConcatenate(AffineTransform.getScaleInstance(1 / 20.0, 1 / 20.0));
focTrans.preConcatenate(AffineTransform.getTranslateInstance(startX / 20, startY / 20));
AffineTransform focTrans = SWF.matrixToTransform(fillStyle0.gradientMatrix);
focTrans.preConcatenate(AffineTransform.getScaleInstance(1 / DESCALE, 1 / DESCALE));
focTrans.preConcatenate(AffineTransform.getTranslateInstance(startX / DESCALE, startY / DESCALE));
g.setTransform(focTrans);
CycleMethod cm = CycleMethod.NO_CYCLE;
if (fillStyle0.focalGradient.spreadMode == GRADIENT.SPREAD_PAD_MODE) {
@@ -242,9 +224,9 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
GeneralPath path = toGeneralPath(startX, startY);
g.fill(path);
g.setClip(path);
AffineTransform trans = matrixToTransform(fillStyle0.gradientMatrix);
trans.preConcatenate(AffineTransform.getScaleInstance(1 / 20.0, 1 / 20.0));
trans.preConcatenate(AffineTransform.getTranslateInstance(startX / 20, startY / 20));
AffineTransform trans = SWF.matrixToTransform(fillStyle0.gradientMatrix);
trans.preConcatenate(AffineTransform.getScaleInstance(1 / DESCALE, 1 / DESCALE));
trans.preConcatenate(AffineTransform.getTranslateInstance(startX / DESCALE, startY / DESCALE));
g.setTransform(trans);
CycleMethod cmRad = CycleMethod.NO_CYCLE;
@@ -275,9 +257,9 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
GeneralPath pathLin = toGeneralPath(startX, startY);
g.fill(pathLin);
g.setClip(pathLin);
AffineTransform transLin = matrixToTransform(fillStyle0.gradientMatrix);
transLin.preConcatenate(AffineTransform.getScaleInstance(1 / 20.0, 1 / 20.0));
transLin.preConcatenate(AffineTransform.getTranslateInstance(startX / 20, startY / 20));
AffineTransform transLin = SWF.matrixToTransform(fillStyle0.gradientMatrix);
transLin.preConcatenate(AffineTransform.getScaleInstance(1 / DESCALE, 1 / DESCALE));
transLin.preConcatenate(AffineTransform.getTranslateInstance(startX / DESCALE, startY / DESCALE));
g.setTransform(transLin);
CycleMethod cmLin = CycleMethod.NO_CYCLE;
@@ -367,7 +349,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
params += " stroke=\"" + ((shapeNum >= 3) ? lineStyle.colorA.toHexRGB() : lineStyle.color.toHexRGB()) + "\"";
}
if (useLineStyle2 && lineStyle2 != null) {
params += " stroke-width=\"" + twipToPixel(lineStyle2.width) + "\"" + (lineStyle2.color != null ? " stroke=\"" + lineStyle2.color.toHexRGB() + "\"" : "");
params += " stroke-width=\"" + SWF.twipToPixel(lineStyle2.width) + "\"" + (lineStyle2.color != null ? " stroke=\"" + lineStyle2.color.toHexRGB() + "\"" : "");
}
String points = "";
int x = 0;
@@ -422,7 +404,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
return new RECT(min_x, max_x, min_y, max_y);
}
private static List<Path> getPaths(RECT bounds, int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, int numFillBits, int numLineBits, List<SHAPERECORD> records) {
private static List<Path> getPaths(RECT bounds, int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, List<SHAPERECORD> records) {
List<Path> paths = new ArrayList<Path>();
Path path = new Path();
int x = 0;
@@ -441,18 +423,18 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
if (scr.stateNewStyles) {
fillStyles = scr.fillStyles;
lineStylesList = scr.lineStyles;
numFillBits = scr.numFillBits;
numLineBits = scr.numLineBits;
//numFillBits = scr.numFillBits;
//numLineBits = scr.numLineBits;
}
if (scr.stateFillStyle0) {
if (scr.fillStyle0 == 0) {
if ((scr.fillStyle0 == 0) || (fillStyles == null)) {
path.fillStyle0 = null;
} else {
path.fillStyle0 = fillStyles.fillStyles[scr.fillStyle0 - 1];
}
}
if (scr.stateFillStyle1) {
if (scr.fillStyle1 == 0) {
if ((scr.fillStyle1 == 0) || (fillStyles == null)) {
path.fillStyle1 = null;
} else {
path.fillStyle1 = fillStyles.fillStyles[scr.fillStyle1 - 1];
@@ -500,6 +482,9 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
paths.add(path);
List<Path> paths2 = new ArrayList<Path>();
for (Path p : paths) {
if ((p.fillStyle0 == null) && (p.fillStyle1 == null)) {
paths2.add(p);
}
if (p.fillStyle0 != null) {
paths2.add(p);
}
@@ -570,32 +555,63 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
public static String shapeToSVG(int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, int numFillBits, int numLineBits, List<SHAPERECORD> records) {
String ret = "";
RECT bounds = new RECT();
List<Path> paths = getPaths(bounds, shapeNum, fillStyles, lineStylesList, numFillBits, numLineBits, records);
List<Path> paths = getPaths(bounds, shapeNum, fillStyles, lineStylesList, /*numFillBits, numLineBits,*/ records);
ret = "";
for (Path p : paths) {
ret += p.toSVG(shapeNum);
}
ret = "<?xml version='1.0' encoding='UTF-8' ?> \n"
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"
+ "<svg width=\"" + (int) Math.ceil(twipToPixel(bounds.Xmax)) + "\"\n"
+ " height=\"" + (int) Math.ceil(twipToPixel(bounds.Ymax)) + "\"\n"
+ " viewBox=\"0 0 " + (int) Math.ceil(twipToPixel(bounds.Xmax)) + " " + (int) Math.ceil(twipToPixel(bounds.Ymax) + 50) + "\"\n"
+ "<svg width=\"" + (int) Math.ceil(SWF.twipToPixel(bounds.Xmax)) + "\"\n"
+ " height=\"" + (int) Math.ceil(SWF.twipToPixel(bounds.Ymax)) + "\"\n"
+ " viewBox=\"0 0 " + (int) Math.ceil(SWF.twipToPixel(bounds.Xmax)) + " " + (int) Math.ceil(SWF.twipToPixel(bounds.Ymax) + 50) + "\"\n"
+ " xmlns=\"http://www.w3.org/2000/svg\"\n"
+ " xmlns:xlink=\"http://www.w3.org/1999/xlink\">" + ret + ""
+ "</svg>";
return ret;
}
private static HashMap<String, BufferedImage> cache = new HashMap<String, BufferedImage>();
public static BufferedImage shapeToImage(List<Tag> tags, int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, int numFillBits, int numLineBits, List<SHAPERECORD> records) {
public static BufferedImage shapeToImage(List<Tag> tags, int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, List<SHAPERECORD> records) {
return shapeToImage(tags, shapeNum, fillStyles, lineStylesList, records, Color.black);
}
public static List<GeneralPath> shapeToPaths(List<Tag> tags, int shapeNum, List<SHAPERECORD> records) {
RECT rect = new RECT();
List<Path> paths = getPaths(rect, shapeNum, fillStyles, lineStylesList, numFillBits, numLineBits, records);
BufferedImage ret = new BufferedImage(rect.getWidth() / 20 + 2, rect.getHeight() / 20 + 2, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g = (Graphics2D) ret.getGraphics();
List<Path> paths = getPaths(rect, shapeNum, null, null, records);
List<GeneralPath> ret = new ArrayList<GeneralPath>();
for (Path p : paths) {
p.drawTo(tags, -rect.Xmin, -rect.Ymin, g, shapeNum);
ret.add(p.toGeneralPath(-rect.Xmin, -rect.Ymin));
}
return ret;
}
public static BufferedImage shapeToImage(List<Tag> tags, int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, List<SHAPERECORD> records, Color defaultColor) {
String key = "shape_" + records.hashCode() + "_" + defaultColor.hashCode();
if (cache.containsKey(key)) {
return cache.get(key);
}
RECT rect = new RECT();
List<Path> paths = getPaths(rect, shapeNum, fillStyles, lineStylesList, /*numFillBits, numLineBits,*/ records);
BufferedImage ret = new BufferedImage(
//(int)((rect.Xmax-rect.Xmin)/DESCALE),(int)((rect.Ymax-rect.Ymin)/DESCALE)
(int) (rect.getWidth() / DESCALE + 2), (int) (rect.getHeight() / DESCALE + 2), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) ret.getGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (Path p : paths) {
if (p.fillStyle0 == null) {
p.fillStyle0 = new FILLSTYLE();
p.fillStyle0.fillStyleType = FILLSTYLE.SOLID;
p.fillStyle0.color = new RGB(defaultColor);
p.fillStyle0.colorA = new RGBA(defaultColor);
}
p.drawTo(tags, -rect.Xmin, -rect.Ymin/*-rect.Xmin, -rect.Ymin*/, g, shapeNum);
}
cache.put(key, ret);
return ret;
}
public abstract boolean isMove();
}

View File

@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.types.shaperecords;
import com.jpexs.decompiler.flash.SWF;
/**
*
* @author JPEXS
@@ -38,11 +40,11 @@ public class StraightEdgeRecord extends SHAPERECORD {
@Override
public String toSWG(int oldX, int oldY) {
if (generalLineFlag) {
return "L " + twipToPixel(oldX + deltaX) + " " + twipToPixel(oldY + deltaY);
return "L " + SWF.twipToPixel(oldX + deltaX) + " " + SWF.twipToPixel(oldY + deltaY);
} else if (vertLineFlag) {
return "V " + twipToPixel(oldY + deltaY);
return "V " + SWF.twipToPixel(oldY + deltaY);
} else {
return "H " + twipToPixel(oldX + deltaX);
return "H " + SWF.twipToPixel(oldX + deltaX);
}
}

View File

@@ -16,6 +16,7 @@
*/
package com.jpexs.decompiler.flash.types.shaperecords;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
import java.util.Set;
@@ -62,7 +63,7 @@ public class StyleChangeRecord extends SHAPERECORD {
@Override
public String toSWG(int oldX, int oldY) {
if (stateMoveTo) {
return "M " + twipToPixel(moveDeltaX) + " " + twipToPixel(moveDeltaY);
return "M " + SWF.twipToPixel(moveDeltaX) + " " + SWF.twipToPixel(moveDeltaY);
}
return "";
}