diff --git a/trunk/src/com/jpexs/decompiler/flash/Layer.java b/trunk/src/com/jpexs/decompiler/flash/Layer.java new file mode 100644 index 000000000..75318d220 --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/Layer.java @@ -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 . + */ +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 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; +} diff --git a/trunk/src/com/jpexs/decompiler/flash/SWF.java b/trunk/src/com/jpexs/decompiler/flash/SWF.java index cf0121416..69fceb5bf 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWF.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWF.java @@ -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 layers, Color backgroundColor, HashMap characters, int frame, List allTags, List 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 allTags, List controlTags, RECT displayRect, int totalFrameCount) { + List ret = new ArrayList(); + 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 ret, int startFrame, int stopFrame, List allTags, List 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 characters = new HashMap(); + for (Tag t : allTags) { + if (t instanceof CharacterTag) { + CharacterTag ch = (CharacterTag) t; + characters.put(ch.getCharacterID(), ch); + } + } + + HashMap layers = new HashMap(); + + 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 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; + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/gui/DecompiledEditorPane.java b/trunk/src/com/jpexs/decompiler/flash/abc/gui/DecompiledEditorPane.java index 714263f46..18a0c043e 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/gui/DecompiledEditorPane.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/gui/DecompiledEditorPane.java @@ -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; diff --git a/trunk/src/com/jpexs/decompiler/flash/action/gui/ActionPanel.java b/trunk/src/com/jpexs/decompiler/flash/action/gui/ActionPanel.java index 4705aa561..24236726e 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/gui/ActionPanel.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/gui/ActionPanel.java @@ -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); diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java index 9f2e83234..09e625ad2 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java @@ -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 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 objs = new ArrayList(); 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) { diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/SWFPreviwPanel.java b/trunk/src/com/jpexs/decompiler/flash/gui/SWFPreviwPanel.java new file mode 100644 index 000000000..d5cc2f192 --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/gui/SWFPreviwPanel.java @@ -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 . + */ +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 frameImages = new ArrayList(); + 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); + } +} diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/SWFRoot.java b/trunk/src/com/jpexs/decompiler/flash/gui/SWFRoot.java new file mode 100644 index 000000000..d5bb5a54e --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/gui/SWFRoot.java @@ -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 . + */ +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; + } +} diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/TagTreeModel.java b/trunk/src/com/jpexs/decompiler/flash/gui/TagTreeModel.java index fdd80a85a..162b6ead2 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/TagTreeModel.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/TagTreeModel.java @@ -26,10 +26,10 @@ import javax.swing.tree.TreePath; public class TagTreeModel implements TreeModel { - private String root = ""; + private Object root; private List list = new ArrayList(); - public TagTreeModel(List list, String rootName) { + public TagTreeModel(List list, Object rootName) { this.root = rootName; this.list = list; } diff --git a/trunk/src/com/jpexs/decompiler/flash/helpers/Cache.java b/trunk/src/com/jpexs/decompiler/flash/helpers/Cache.java index 4219bd550..cd2876364 100644 --- a/trunk/src/com/jpexs/decompiler/flash/helpers/Cache.java +++ b/trunk/src/com/jpexs/decompiler/flash/helpers/Cache.java @@ -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 cacheFiles = new WeakHashMap(); + private Map cacheFiles; + + public Cache(boolean weak) { + if (weak) { + cacheFiles = new WeakHashMap(); + } else { + cacheFiles = new HashMap(); + } + } public boolean contains(Object key) { return cacheFiles.containsKey(key); diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java index 28ec5ea76..c51671fca 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java @@ -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 tags, RECT displayRect, HashMap characters) { + HashMap layers = new HashMap(); + 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 characters) { + return new Point(0, 0); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java index 9e44c5e58..e1a4525a4 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java @@ -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 tags, RECT displayRect, HashMap characters) { + HashMap layers = new HashMap(); + 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 characters) { + return new Point(0, 0); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java index 5447a7654..e1c8aec21 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java @@ -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 getPaths(List tags) { + return null; //FIXME + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java index 639b59a8e..1e59663d2 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java @@ -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; + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java index 9952770a8..0f706fd2d 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont3Tag.java @@ -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; + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java index d4c4ada32..ec56b8fe4 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFontTag.java @@ -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; + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineMorphShape2Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineMorphShape2Tag.java index 1c50f4fa5..50db734b3 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineMorphShape2Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineMorphShape2Tag.java @@ -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 getPaths(List tags) { + return null; //FIXME + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineMorphShapeTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineMorphShapeTag.java index b6b3eb9e3..5514c0128 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineMorphShapeTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineMorphShapeTag.java @@ -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 getPaths(List tags) { + return null; //FIXME + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineShape2Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineShape2Tag.java index 778f571af..90f351732 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineShape2Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineShape2Tag.java @@ -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 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 tags) { + public BufferedImage toImage(int frame, List tags, RECT displayRect, HashMap 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 getPaths(List tags) { + return SHAPERECORD.shapeToPaths(tags, 2, shapes.shapeRecords); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineShape3Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineShape3Tag.java index 6d8689197..5bedd0b7a 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineShape3Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineShape3Tag.java @@ -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 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 tags) { + public BufferedImage toImage(int frame, List tags, RECT displayRect, HashMap 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 getPaths(List tags) { + return SHAPERECORD.shapeToPaths(tags, 3, shapes.shapeRecords); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineShape4Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineShape4Tag.java index 0202b0463..72c058479 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineShape4Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineShape4Tag.java @@ -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 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 tags) { + public BufferedImage toImage(int frame, List tags, RECT displayRect, HashMap 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 getPaths(List tags) { + return SHAPERECORD.shapeToPaths(tags, 4, shapes.shapeRecords); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineShapeTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineShapeTag.java index b9c955990..db730160c 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineShapeTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineShapeTag.java @@ -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 tags) { + public BufferedImage toImage(int frame, List tags, RECT displayRect, HashMap characters) { return shapes.toImage(1, tags); } + + @Override + public Point getImagePos(HashMap characters) { + return new Point(shapeBounds.Xmin / 20, shapeBounds.Ymin / 20); + } + + @Override + public List getPaths(List tags) { + return SHAPERECORD.shapeToPaths(tags, 1, shapes.shapeRecords); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java index 98d056202..1f8a03290 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java @@ -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 depthMap = new HashMap(); for (Tag t : subTags) { - Set 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 need = new HashSet(); + 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 tags, RECT displayRect, HashMap 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 characters) { + RECT displayRect = getRect(characters); + return new Point(0, 0); //displayRect.Xmin,displayRect.Ymin); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java index d06b5b30d..34eac2ea8 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java @@ -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 tags, RECT displayRect, HashMap 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 characters) { + return new Point(0, 0); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java index 2ca1fe13c..e94788655 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java @@ -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 tags, RECT displayRect, HashMap 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 characters) { + return new Point(0, 0); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/base/BoundedTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/base/BoundedTag.java index 65ebf1510..415d8c8ab 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/base/BoundedTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/base/BoundedTag.java @@ -26,4 +26,5 @@ import java.util.HashMap; public interface BoundedTag { public RECT getRect(HashMap characters); + //public List getPaths(List tags); } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/base/ButtonTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/base/ButtonTag.java index d863340a9..c607f470c 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/base/ButtonTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/base/ButtonTag.java @@ -23,7 +23,7 @@ import java.util.List; * * @author JPEXS */ -public interface ButtonTag { +public interface ButtonTag extends DrawableTag { public List getRecords(); diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/base/DrawableTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/base/DrawableTag.java new file mode 100644 index 000000000..f54cc01db --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/tags/base/DrawableTag.java @@ -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 . + */ +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 tags, RECT displayRect, HashMap characters); + + public Point getImagePos(HashMap characters); +} diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/base/FontTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/base/FontTag.java index 1f5aa1902..d262330b4 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/base/FontTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/base/FontTag.java @@ -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(); } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/base/ShapeTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/base/ShapeTag.java index 06855602d..546ec3ea5 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/base/ShapeTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/base/ShapeTag.java @@ -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 tags); + public List getPaths(List tags); } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/CXFORM.java b/trunk/src/com/jpexs/decompiler/flash/types/CXFORM.java index e3d4e3597..ee1e44c76 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/CXFORM.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/CXFORM.java @@ -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); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/CXFORMWITHALPHA.java b/trunk/src/com/jpexs/decompiler/flash/types/CXFORMWITHALPHA.java index 3029cdc1c..30b9f36bd 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/CXFORMWITHALPHA.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/CXFORMWITHALPHA.java @@ -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); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/MATRIX.java b/trunk/src/com/jpexs/decompiler/flash/types/MATRIX.java index 783a296ed..47ca4c2bd 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/MATRIX.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/MATRIX.java @@ -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 + "]"; diff --git a/trunk/src/com/jpexs/decompiler/flash/types/RECT.java b/trunk/src/com/jpexs/decompiler/flash/types/RECT.java index e7777c430..d44c9f2ea 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/RECT.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/RECT.java @@ -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; } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/RGB.java b/trunk/src/com/jpexs/decompiler/flash/types/RGB.java index d4074cbbc..492d0e2fd 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/RGB.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/RGB.java @@ -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(); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/RGBA.java b/trunk/src/com/jpexs/decompiler/flash/types/RGBA.java index e22f6d2a6..17002513c 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/RGBA.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/RGBA.java @@ -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); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/SHAPE.java b/trunk/src/com/jpexs/decompiler/flash/types/SHAPE.java index 6505b647a..e6f0fdf7e 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/SHAPE.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/SHAPE.java @@ -54,7 +54,7 @@ public class SHAPE implements NeedsCharacters { } public BufferedImage toImage(int shapeNum, List tags) { - return SHAPERECORD.shapeToImage(tags, shapeNum, null, null, numFillBits, numLineBits, shapeRecords); + return SHAPERECORD.shapeToImage(tags, shapeNum, null, null,/* numFillBits, numLineBits, */ shapeRecords); } public RECT getBounds() { diff --git a/trunk/src/com/jpexs/decompiler/flash/types/SHAPEWITHSTYLE.java b/trunk/src/com/jpexs/decompiler/flash/types/SHAPEWITHSTYLE.java index a35cae70f..9817082ce 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/SHAPEWITHSTYLE.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/SHAPEWITHSTYLE.java @@ -57,7 +57,7 @@ public class SHAPEWITHSTYLE implements NeedsCharacters { } public BufferedImage toImage(int shapeNum, List tags) { - return SHAPERECORD.shapeToImage(tags, shapeNum, fillStyles, lineStyles, numFillBits, numLineBits, shapeRecords); + return SHAPERECORD.shapeToImage(tags, shapeNum, fillStyles, lineStyles, /*numFillBits, numLineBits,*/ shapeRecords); } public RECT getBounds() { diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java index 0061959c9..5ef751efa 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java @@ -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); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/BLURFILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/BLURFILTER.java index cb546b26a..5c3cae7a3 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/filters/BLURFILTER.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/BLURFILTER.java @@ -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); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/BlendComposite.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/BlendComposite.java new file mode 100644 index 000000000..a208e9cbb --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/BlendComposite.java @@ -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 . + */ +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; + } + + /** + *

Returns the blending mode of this composite.

+ * + * @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()); + } + } +} diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/COLORMATRIXFILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/COLORMATRIXFILTER.java index 0f4cc836f..6456ceca2 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/filters/COLORMATRIXFILTER.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/COLORMATRIXFILTER.java @@ -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); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/CONVOLUTIONFILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/CONVOLUTIONFILTER.java index de5cef6a0..4e036ba42 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/filters/CONVOLUTIONFILTER.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/CONVOLUTIONFILTER.java @@ -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); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java index eda42fb14..c4493b7eb 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java @@ -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); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/FILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/FILTER.java index d9d89aa1a..b38c61cd7 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/filters/FILTER.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/FILTER.java @@ -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; + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/Filtering.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/Filtering.java new file mode 100644 index 000000000..4ac7a0e75 --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/Filtering.java @@ -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 . + */ +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; + } +} diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java index 660ac7306..da655b5d1 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java @@ -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); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java index 61ee02500..94f19710a 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java @@ -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 colors = new ArrayList(); + List ratios = new ArrayList(); + 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); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java index 11ed9a9a4..557ec338a 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java @@ -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 colors = new ArrayList(); + List ratios = new ArrayList(); + 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); + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/CurvedEdgeRecord.java b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/CurvedEdgeRecord.java index e4a945a4f..9bf4b88dd 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/CurvedEdgeRecord.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/CurvedEdgeRecord.java @@ -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); } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java index bbe0ccc24..2f7dcb686 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java @@ -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 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 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 getPaths(RECT bounds, int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, int numFillBits, int numLineBits, List records) { + private static List getPaths(RECT bounds, int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, List records) { List paths = new ArrayList(); 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 paths2 = new ArrayList(); 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 records) { String ret = ""; RECT bounds = new RECT(); - List paths = getPaths(bounds, shapeNum, fillStyles, lineStylesList, numFillBits, numLineBits, records); + List paths = getPaths(bounds, shapeNum, fillStyles, lineStylesList, /*numFillBits, numLineBits,*/ records); ret = ""; for (Path p : paths) { ret += p.toSVG(shapeNum); } ret = " \n" + "\n" - + "" + ret + "" + ""; return ret; } + private static HashMap cache = new HashMap(); - public static BufferedImage shapeToImage(List tags, int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, int numFillBits, int numLineBits, List records) { + public static BufferedImage shapeToImage(List tags, int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, List records) { + return shapeToImage(tags, shapeNum, fillStyles, lineStylesList, records, Color.black); + } + + public static List shapeToPaths(List tags, int shapeNum, List records) { RECT rect = new RECT(); - List 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 paths = getPaths(rect, shapeNum, null, null, records); + List ret = new ArrayList(); 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 tags, int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, List records, Color defaultColor) { + String key = "shape_" + records.hashCode() + "_" + defaultColor.hashCode(); + if (cache.containsKey(key)) { + return cache.get(key); + } + RECT rect = new RECT(); + List 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(); } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StraightEdgeRecord.java b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StraightEdgeRecord.java index cecf43171..e9db41b07 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StraightEdgeRecord.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StraightEdgeRecord.java @@ -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); } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StyleChangeRecord.java b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StyleChangeRecord.java index 48d8f163b..b5f27260b 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StyleChangeRecord.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StyleChangeRecord.java @@ -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 ""; }