From 61fd0d1b83ca0f8240bdd716d3d2a4417dc45600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=F8=EDk?= Date: Mon, 23 Sep 2013 06:55:03 +0200 Subject: [PATCH] GFx fonts: Adding characters GFx fonts: Displaying text fields Fonts display in internal viewer --- .../jpexs/decompiler/flash/gui/MainFrame.java | 90 ++++-- .../decompiler/flash/tags/DefineFont2Tag.java | 4 + .../flash/tags/GFxDefineCompactedFont.java | 263 ++++++++++++++++-- .../flash/tags/GFxFontTextureInfo.java | 5 +- .../decompiler/flash/tags/base/FontTag.java | 24 +- .../decompiler/flash/types/LANGCODE.java | 7 + .../flash/types/gfx/ContourType.java | 60 +++- .../decompiler/flash/types/gfx/EdgeType.java | 200 ++++++++++++- .../decompiler/flash/types/gfx/FONTINFO.java | 3 +- .../decompiler/flash/types/gfx/FontType.java | 52 +++- .../flash/types/gfx/GFxOutputStream.java | 195 +++++++++++++ .../decompiler/flash/types/gfx/GLYPHIDX.java | 3 +- .../flash/types/gfx/GlyphInfoType.java | 3 +- .../decompiler/flash/types/gfx/GlyphType.java | 47 +++- .../flash/types/gfx/KerningPairType.java | 3 +- .../decompiler/flash/types/gfx/TEXGLYPH.java | 3 +- .../types/shaperecords/CurvedEdgeRecord.java | 9 + .../types/shaperecords/EndShapeRecord.java | 4 + .../flash/types/shaperecords/SHAPERECORD.java | 83 +++++- .../shaperecords/StraightEdgeRecord.java | 9 + .../types/shaperecords/StyleChangeRecord.java | 6 + 21 files changed, 979 insertions(+), 94 deletions(-) create mode 100644 trunk/src/com/jpexs/decompiler/flash/types/gfx/GFxOutputStream.java diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java index d77214fec..f420b1f34 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java @@ -292,6 +292,10 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel private JComboBox fontSelection; private JCommandButton saveCommandButton; private PlayerControls flashControls; + private ImagePanel internelViewerPanel; + private JPanel viewerCards; + public static final String FLASH_VIEWER_CARD = "FLASHVIEWER"; + public static final String INTERNAL_VIEWER_CARD = "INTERNALVIEWER"; private Map sourceFontsMap = new HashMap<>(); private AbortRetryIgnoreHandler errorHandler = new AbortRetryIgnoreHandler() { @Override @@ -1273,7 +1277,19 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel paramsLabel.setHorizontalAlignment(SwingConstants.CENTER); //paramsLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); pan.add(prevLabel, BorderLayout.NORTH); - pan.add(leftComponent, BorderLayout.CENTER); + + viewerCards = new JPanel(); + viewerCards.setLayout(new CardLayout()); + //viewerCards.add(leftComponent,FLASH_VIEWER_CARD); + + internelViewerPanel = new ImagePanel(); + JPanel ivPanel = new JPanel(new BorderLayout()); + ivPanel.add(new HeaderLabel(translate("swfpreview.internal")), BorderLayout.NORTH); + ivPanel.add(internelViewerPanel, BorderLayout.CENTER); + viewerCards.add(ivPanel, INTERNAL_VIEWER_CARD); + + ((CardLayout) viewerCards.getLayout()).show(viewerCards, FLASH_VIEWER_CARD); + if (flashPanel != null) { JPanel bottomPanel = new JPanel(new BorderLayout()); JPanel buttonsPanel = new JPanel(new FlowLayout()); @@ -1285,7 +1301,9 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel bottomPanel.add(buttonsPanel, BorderLayout.EAST); pan.add(bottomPanel, BorderLayout.SOUTH); } - previewSplitPane.setLeftComponent(pan); + pan.add(leftComponent, BorderLayout.CENTER); + viewerCards.add(pan, FLASH_VIEWER_CARD); + previewSplitPane.setLeftComponent(viewerCards); parametersPanel = new JPanel(new BorderLayout()); parametersPanel.add(paramsLabel, BorderLayout.NORTH); @@ -2941,6 +2959,13 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel } } + private static Tag classicTag(Tag t) { + if (t instanceof GFxDefineCompactedFont) { + return ((GFxDefineCompactedFont) t).toClassicFont(); + } + return t; + } + public void reload(boolean forceReload) { Object tagObj = tagTree.getLastSelectedPathComponent(); if (tagObj == null) { @@ -3047,9 +3072,13 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel imageButtonsPanel.setVisible(((ImageTag) tagObj).importSupported()); showCard(CARDIMAGEPANEL); imagePanel.setImage(((ImageTag) tagObj).getImage(swf.tags)); - } else if ((tagObj instanceof DrawableTag) && (!(tagObj instanceof TextTag)) && (miInternalViewer.isSelected())) { + } else if ((tagObj instanceof DrawableTag) && (!(tagObj instanceof TextTag)) && (!(tagObj instanceof FontTag)) && (miInternalViewer.isSelected())) { showCard(CARDDRAWPREVIEWPANEL); previewImagePanel.setDrawable((DrawableTag) tagObj, swf, characters, 50/*FIXME*/); + } else if ((tagObj instanceof FontTag) && (miInternalViewer.isSelected() || (tagObj instanceof GFxDefineCompactedFont))) { + showCard(CARDFLASHPANEL); + previewImagePanel.setDrawable((DrawableTag) tagObj, swf, characters, 50/*FIXME*/); + showFontTag((FontTag) tagObj); } else if (tagObj instanceof FrameNode && ((FrameNode) tagObj).isDisplayed() && (miInternalViewer.isSelected())) { showCard(CARDDRAWPREVIEWPANEL); FrameNode fn = (FrameNode) tagObj; @@ -3065,6 +3094,7 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel } previewImagePanel.setImage(SWF.frameToImage(containerId, ((FrameNode) tagObj).getFrame() - 1, swf.tags, controlTags, rect, totalFrameCount, new Stack())); } else if (((tagObj instanceof FrameNode) && ((FrameNode) tagObj).isDisplayed()) || ((tagObj instanceof CharacterTag) || (tagObj instanceof FontTag)) && (tagObj instanceof Tag)) { + ((CardLayout) viewerCards.getLayout()).show(viewerCards, FLASH_VIEWER_CARD); try { if (tempFile != null) { @@ -3158,14 +3188,14 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel Set needed = t.getDeepNeededCharacters(characters, new ArrayList()); for (int n : needed) { if (!doneCharacters.contains(n)) { - sos2.writeTag(characters.get(n)); + sos2.writeTag(classicTag(characters.get(n))); doneCharacters.add(n); } } if (t instanceof CharacterTag) { doneCharacters.add(((CharacterTag) t).getCharacterId()); } - sos2.writeTag(t); + sos2.writeTag(classicTag(t)); if (parent != null) { if (t instanceof PlaceObjectTypeTag) { @@ -3201,11 +3231,11 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel } else { Set needed = ((Tag) tagObj).getDeepNeededCharacters(characters, new ArrayList()); for (int n : needed) { - sos2.writeTag(characters.get(n)); + sos2.writeTag(classicTag(characters.get(n))); } } - sos2.writeTag(((Tag) tagObj)); + sos2.writeTag(classicTag((Tag) tagObj)); int chtId = 0; if (tagObj instanceof CharacterTag) { @@ -3441,24 +3471,7 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel showDetailWithPreview(CARDTEXTPANEL); textValue.setText(((TextTag) tagObj).getFormattedText(swf.tags)); } else if (tagObj instanceof FontTag) { - parametersPanel.setVisible(true); - previewSplitPane.setDividerLocation(previewSplitPane.getWidth() / 2); - FontTag ft = (FontTag) tagObj; - fontNameLabel.setText(ft.getFontName(swf.tags)); - fontIsBoldLabel.setText(ft.isBold() ? translate("yes") : translate("no")); - fontIsItalicLabel.setText(ft.isItalic() ? translate("yes") : translate("no")); - fontDescentLabel.setText(ft.getDescent() == -1 ? translate("value.unknown") : "" + ft.getDescent()); - fontAscentLabel.setText(ft.getAscent() == -1 ? translate("value.unknown") : "" + ft.getAscent()); - fontLeadingLabel.setText(ft.getLeading() == -1 ? translate("value.unknown") : "" + ft.getLeading()); - String chars = ft.getCharacters(swf.tags); - fontCharactersTextArea.setText(chars); - if (sourceFontsMap.containsKey(ft.getFontId())) { - fontSelection.setSelectedItem(sourceFontsMap.get(ft.getFontId())); - } else { - fontSelection.setSelectedItem(FontTag.findInstalledFontName(ft.getFontName(swf.tags))); - } - fontChangeList.componentResized(null); - showDetailWithPreview(CARDFONTPANEL); + showFontTag((FontTag) tagObj); } else { parametersPanel.setVisible(false); } @@ -3468,6 +3481,33 @@ public class MainFrame extends AppRibbonFrame implements ActionListener, TreeSel } } + private void showFontTag(FontTag ft) { + if (miInternalViewer.isSelected() || ft instanceof GFxDefineCompactedFont) { + ((CardLayout) viewerCards.getLayout()).show(viewerCards, INTERNAL_VIEWER_CARD); + internelViewerPanel.setDrawable(ft, swf, characters, 1); + } else { + ((CardLayout) viewerCards.getLayout()).show(viewerCards, FLASH_VIEWER_CARD); + } + + parametersPanel.setVisible(true); + previewSplitPane.setDividerLocation(previewSplitPane.getWidth() / 2); + fontNameLabel.setText(ft.getFontName(swf.tags)); + fontIsBoldLabel.setText(ft.isBold() ? translate("yes") : translate("no")); + fontIsItalicLabel.setText(ft.isItalic() ? translate("yes") : translate("no")); + fontDescentLabel.setText(ft.getDescent() == -1 ? translate("value.unknown") : "" + ft.getDescent()); + fontAscentLabel.setText(ft.getAscent() == -1 ? translate("value.unknown") : "" + ft.getAscent()); + fontLeadingLabel.setText(ft.getLeading() == -1 ? translate("value.unknown") : "" + ft.getLeading()); + String chars = ft.getCharacters(swf.tags); + fontCharactersTextArea.setText(chars); + if (sourceFontsMap.containsKey(ft.getFontId())) { + fontSelection.setSelectedItem(sourceFontsMap.get(ft.getFontId())); + } else { + fontSelection.setSelectedItem(FontTag.findInstalledFontName(ft.getFontName(swf.tags))); + } + fontChangeList.componentResized(null); + showDetailWithPreview(CARDFONTPANEL); + } + public void refreshTree() { List objs = new ArrayList<>(); objs.addAll(swf.tags); diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java index e8f1d0af8..80b8d0834 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java @@ -161,6 +161,10 @@ public class DefineFont2Tag extends FontTag { return baos.toByteArray(); } + public DefineFont2Tag(SWF swf) throws IOException { + super(swf, ID, "DefineFont2", new byte[0], 0); + } + /** * Constructor * diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/GFxDefineCompactedFont.java b/trunk/src/com/jpexs/decompiler/flash/tags/GFxDefineCompactedFont.java index 858ed4c09..34fb33144 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/GFxDefineCompactedFont.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/GFxDefineCompactedFont.java @@ -18,29 +18,47 @@ package com.jpexs.decompiler.flash.tags; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; 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.types.KERNINGRECORD; +import com.jpexs.decompiler.flash.types.LANGCODE; import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.types.SHAPE; import com.jpexs.decompiler.flash.types.gfx.FontType; import com.jpexs.decompiler.flash.types.gfx.GFxInputStream; +import com.jpexs.decompiler.flash.types.gfx.GFxOutputStream; +import com.jpexs.decompiler.flash.types.gfx.GlyphInfoType; +import com.jpexs.decompiler.flash.types.gfx.GlyphType; +import com.jpexs.decompiler.flash.types.gfx.KerningPairType; +import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import com.jpexs.helpers.Helper; import java.awt.Color; +import java.awt.Font; import java.awt.Point; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Stack; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JPanel; /** * * * @author JPEXS */ -public class GFxDefineCompactedFont extends CharacterTag implements DrawableTag { +public class GFxDefineCompactedFont extends FontTag implements DrawableTag { public static final int ID = 1005; public int fontId; @@ -54,15 +72,17 @@ public class GFxDefineCompactedFont extends CharacterTag implements DrawableTag */ @Override public byte[] getData(int version) { - return super.getData(version); - /*ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - SWFOutputStream sos = new SWFOutputStream(os, version); - try { - //sos.write - } catch (IOException e) { - } - return baos.toByteArray();*/ + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + SWFOutputStream sos = new SWFOutputStream(os, version); + try { + sos.writeUI16(fontId); + for (FontType ft : fonts) { + ft.write(new GFxOutputStream(sos)); + } + } catch (IOException e) { + } + return baos.toByteArray(); } /** @@ -92,6 +112,9 @@ public class GFxDefineCompactedFont extends CharacterTag implements DrawableTag for (FontType f : fonts) { shapes.addAll(f.getGlyphShapes()); } + for (int i = 0; i < shapes.size(); i++) { + shapes.set(i, SHAPERECORD.resizeSHAPE(shapes.get(i), 20)); + } return SHAPERECORD.shapeListToImage(shapes, 500, 500, Color.black); } @@ -105,6 +128,7 @@ public class GFxDefineCompactedFont extends CharacterTag implements DrawableTag return 1; } + @Override public String getFontName(List tags) { String ret = ""; for (int i = 0; i < fonts.size(); i++) { @@ -116,18 +140,217 @@ public class GFxDefineCompactedFont extends CharacterTag implements DrawableTag return ret; } - @Override - public String getName(List tags) { - String nameAppend = ""; - String fontName = getFontName(tags); - if (fontName != null) { - nameAppend = ": " + fontName; - } - return name + " (" + getCharacterId() + nameAppend + ")"; - } - @Override public int getCharacterId() { return fontId; } + + @Override + public int getFontId() { + return fontId; + } + + @Override + public List getGlyphShapeTable() { + return fonts.get(0).getGlyphShapes(); + } + + @Override + public void addCharacter(List tags, char character, String fontName) { + int fontStyle = getFontStyle(); + FontType font = fonts.get(0); + SHAPE shp = SHAPERECORD.systemFontCharacterToSHAPE(fontName, fontStyle, 20 * font.nominalSize, character); + + + int code = (int) character; + int pos = -1; + for (int i = 0; i < font.glyphInfo.size(); i++) { + if (font.glyphInfo.get(i).glyphCode > code) { + pos = i; + break; + } + } + if (pos == -1) { + pos = font.glyphInfo.size(); + } + + FontTag.shiftGlyphIndices(fontId, pos, tags); + + Font fnt = new Font(fontName, fontStyle, 20 * font.nominalSize); + int advance = (int) Math.round(fnt.createGlyphVector((new JPanel()).getFontMetrics(fnt).getFontRenderContext(), "" + character).getGlyphMetrics(0).getAdvanceX()); + font.glyphInfo.add(pos, new GlyphInfoType(code, advance, 0)); + font.glyphs.add(pos, new GlyphType(shp.shapeRecords)); + } + + @Override + public char glyphToChar(List tags, int glyphIndex) { + return (char) fonts.get(0).glyphInfo.get(glyphIndex).glyphCode; + } + + @Override + public int charToGlyph(List tags, char c) { + FontType ft = fonts.get(0); + for (int i = 0; i < ft.glyphInfo.size(); i++) { + if (ft.glyphInfo.get(i).glyphCode == c) { + return i; + } + } + return -1; + } + + @Override + public int getGlyphAdvance(int glyphIndex) { + return fonts.get(0).glyphInfo.get(glyphIndex).advanceX; + } + + @Override + public int getGlyphKerningAdjustment(List tags, int glyphIndex, int nextGlyphIndex) { + int char1 = glyphToChar(tags, glyphIndex); + int char2 = glyphToChar(tags, nextGlyphIndex); + for (KerningPairType kp : fonts.get(0).kerning) { + if (kp.char1 == char1 && kp.char2 == char2) { + return kp.advance; + } + } + return 0; + } + + @Override + public int getGlyphWidth(int glyphIndex) { + return getGlyphShapeTable().get(glyphIndex).getBounds().getWidth(); + } + + @Override + public boolean isSmall() { + return false; + } + + @Override + public boolean isBold() { + return (fonts.get(0).flags & FontType.FF_Bold) == FontType.FF_Bold; + } + + @Override + public boolean isItalic() { + return (fonts.get(0).flags & FontType.FF_Italic) == FontType.FF_Italic; + } + + @Override + public int getDivider() { + return 1; + } + + @Override + public int getAscent() { + return fonts.get(0).ascent; + } + + @Override + public int getDescent() { + return fonts.get(0).descent; + } + + @Override + public int getLeading() { + return fonts.get(0).leading; + } + + @Override + public String getCharacters(List tags) { + FontType ft = fonts.get(0); + String ret = ""; + for (GlyphInfoType gi : ft.glyphInfo) { + ret += (char) gi.glyphCode; + } + return ret; + } + + private int resizemultiplier() { + FontType ft = fonts.get(0); + return (int) Math.round(1024.0 / ft.nominalSize); + } + + private int resize(int val) { + int ret = val * resizemultiplier(); + return ret; + } + + private SHAPE resize(SHAPE shp) { + SHAPE ret = new SHAPE(); + ret.numFillBits = 1; + ret.numLineBits = 0; + List recs = new ArrayList<>(); + for (SHAPERECORD r : shp.shapeRecords) { + SHAPERECORD c = (SHAPERECORD) Helper.deepCopy(r); + if (c instanceof StyleChangeRecord) { + StyleChangeRecord scr = (StyleChangeRecord) c; + scr.moveDeltaX = resize(scr.moveDeltaX); + scr.moveDeltaY = resize(scr.moveDeltaY); + scr.calculateBits(); + } + if (c instanceof CurvedEdgeRecord) { + CurvedEdgeRecord cer = (CurvedEdgeRecord) c; + cer.controlDeltaX = resize(cer.controlDeltaX); + cer.controlDeltaY = resize(cer.controlDeltaY); + cer.anchorDeltaX = resize(cer.anchorDeltaX); + cer.anchorDeltaY = resize(cer.anchorDeltaY); + cer.calculateBits(); + } + if (c instanceof StraightEdgeRecord) { + StraightEdgeRecord ser = (StraightEdgeRecord) c; + ser.deltaX = resize(ser.deltaX); + ser.deltaY = resize(ser.deltaY); + ser.calculateBits(); + } + recs.add(c); + } + ret.shapeRecords = recs; + return ret; + } + + public FontTag toClassicFont() { + try { + DefineFont2Tag ret = new DefineFont2Tag(swf); + ret.fontId = fontId; + ret.fontFlagsBold = isBold(); + ret.fontFlagsItalic = isItalic(); + ret.fontFlagsWideOffsets = true; + ret.fontFlagsWideCodes = true; + ret.fontFlagsHasLayout = true; + FontType ft = fonts.get(0); + ret.fontAscent = resize(ft.ascent); + ret.fontDescent = resize(ft.descent); + ret.fontLeading = resize(ft.leading); + ret.fontAdvanceTable = new ArrayList<>(); + ret.fontBoundsTable = new ArrayList<>(); + ret.codeTable = new ArrayList<>(); + ret.glyphShapeTable = new ArrayList<>(); + ret.numGlyphs = ft.glyphInfo.size(); + SHAPE shpA = SHAPERECORD.systemFontCharacterToSHAPE("Times New Roman", 0, getDivider() * 1024, 'A'); + for (GlyphInfoType gi : ft.glyphInfo) { + ret.fontAdvanceTable.add(resize(gi.advanceX)); + ret.codeTable.add(gi.glyphCode); + } + + for (GlyphType gt : ft.glyphs) { + SHAPE shpX = resize(gt.toSHAPE()); + ret.glyphShapeTable.add(shpX); // + ret.fontBoundsTable.add(new RECT(resize(gt.boundingBox[0]), resize(gt.boundingBox[1]), resize(gt.boundingBox[2]), resize(gt.boundingBox[3]))); + } + ret.fontName = ft.fontName; + ret.languageCode = new LANGCODE(1); + ret.fontKerningTable = new KERNINGRECORD[0];/*new KERNINGRECORD[ft.kerning.size()]; + for(int i=0;i tags, RECT displayRect, HashMap characters, Stack visited) { + return SHAPERECORD.shapeListToImage(getGlyphShapeTable(), 500, 500, Color.black); + } + + @Override + public Point getImagePos(int frame, HashMap characters, Stack visited) { + return new Point(0, 0); + } + + @Override + public int getNumFrames() { + return 1; + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/LANGCODE.java b/trunk/src/com/jpexs/decompiler/flash/types/LANGCODE.java index 4f351d5c8..9e167c854 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/LANGCODE.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/LANGCODE.java @@ -23,4 +23,11 @@ package com.jpexs.decompiler.flash.types; public class LANGCODE { public int languageCode; + + public LANGCODE() { + } + + public LANGCODE(int languageCode) { + this.languageCode = languageCode; + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/gfx/ContourType.java b/trunk/src/com/jpexs/decompiler/flash/types/gfx/ContourType.java index 7c4630ab6..1e97e4176 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/gfx/ContourType.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/gfx/ContourType.java @@ -16,8 +16,9 @@ */ package com.jpexs.decompiler.flash.types.gfx; -import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; import java.io.IOException; import java.util.ArrayList; @@ -35,6 +36,39 @@ public class ContourType { public boolean isReference; public long reference; + public ContourType(List records) { + int i = 0; + int divider = 20; + for (; i < records.size(); i++) { + if (records.get(i) instanceof StyleChangeRecord) { + StyleChangeRecord scr = (StyleChangeRecord) records.get(i); + if (scr.stateMoveTo) { + moveToX = scr.moveDeltaX / divider; + moveToY = scr.moveDeltaY / divider; + break; + } + } + } + List edgesList = new ArrayList<>(); + for (; i < records.size(); i++) { + SHAPERECORD rec = records.get(i); + if (rec instanceof StraightEdgeRecord) { + StraightEdgeRecord ser = (StraightEdgeRecord) rec; + if (ser.generalLineFlag) { + edgesList.add(new EdgeType(ser.deltaX / divider, ser.deltaY / divider)); + } else if (ser.vertLineFlag) { + edgesList.add(new EdgeType(true, ser.deltaY / divider)); + } else { + edgesList.add(new EdgeType(false, ser.deltaX / divider)); + } + } else if (rec instanceof CurvedEdgeRecord) { + CurvedEdgeRecord cer = (CurvedEdgeRecord) rec; + edgesList.add(new EdgeType(cer.controlDeltaX / divider, cer.controlDeltaY / divider, cer.anchorDeltaX / divider, cer.anchorDeltaY / divider)); + } + } + edges = edgesList.toArray(new EdgeType[edgesList.size()]); + } + public ContourType(GFxInputStream sis) throws IOException { moveToX = sis.readSI15(); moveToY = sis.readSI15(); @@ -59,19 +93,39 @@ public class ContourType { } public List toSHAPERECORDS() { - int multiplier = 20; + int multiplier = 1; List recs = new ArrayList<>(); StyleChangeRecord src = new StyleChangeRecord(); src.stateMoveTo = true; src.moveDeltaX = moveToX * multiplier; src.moveDeltaY = moveToY * multiplier; + src.calculateBits(); recs.add(src); for (EdgeType e : edges) { recs.add(e.toSHAPERECORD()); } + int x = src.moveDeltaX; + int y = src.moveDeltaY; + for (SHAPERECORD rec : recs) { + x = rec.changeX(x); + y = rec.changeY(y); + } + StraightEdgeRecord closeSer = new StraightEdgeRecord(); + closeSer.generalLineFlag = true; + closeSer.deltaX = (src.moveDeltaX - x); + closeSer.deltaY = (src.moveDeltaY - y); + closeSer.calculateBits(); + recs.add(closeSer); + return recs; } - public void write(SWFOutputStream sos) throws IOException { + public void write(GFxOutputStream sos) throws IOException { + sos.writeSI15(moveToX); + sos.writeSI15(moveToY); + sos.writeUI30(edges.length << 1); + for (int i = 0; i < edges.length; i++) { + edges[i].write(sos); + } } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/gfx/EdgeType.java b/trunk/src/com/jpexs/decompiler/flash/types/gfx/EdgeType.java index d254a6169..b327e5d86 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/gfx/EdgeType.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/gfx/EdgeType.java @@ -16,11 +16,13 @@ */ package com.jpexs.decompiler.flash.types.gfx; -import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; /** * @@ -52,6 +54,32 @@ public class EdgeType { public int data[]; public byte raw[]; + public EdgeType(boolean vertical, int v) { + data = new int[]{vertical ? Edge_VLine : Edge_HLine, v}; + calcRaw(); + } + + public EdgeType(int x, int y) { + data = new int[]{Edge_Line, x, y}; + calcRaw(); + } + + public EdgeType(int cx, int cy, int ax, int ay) { + data = new int[]{Edge_Quad, cx, cy, ax, ay}; + calcRaw(); + } + + private void calcRaw() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GFxOutputStream sos = new GFxOutputStream(baos); + try { + write(sos); + } catch (IOException ex) { + Logger.getLogger(EdgeType.class.getName()).log(Level.SEVERE, null, ex); + } + raw = baos.toByteArray(); + } + @Override public String toString() { String ret = "[Edge " + (raw[0] & 0xf) + " data:"; @@ -114,7 +142,7 @@ public class EdgeType { } public SHAPERECORD toSHAPERECORD() { - int multiplier = 20; + int multiplier = 1; StraightEdgeRecord ser; CurvedEdgeRecord cer; switch (data[0]) { @@ -122,18 +150,21 @@ public class EdgeType { ser = new StraightEdgeRecord(); ser.generalLineFlag = false; ser.deltaX = data[1] * multiplier; + ser.calculateBits(); return ser; case Edge_VLine: ser = new StraightEdgeRecord(); ser.generalLineFlag = false; ser.vertLineFlag = true; ser.deltaY = data[1] * multiplier; + ser.calculateBits(); return ser; case Edge_Line: ser = new StraightEdgeRecord(); ser.generalLineFlag = true; ser.deltaX = data[1] * multiplier; ser.deltaY = data[2] * multiplier; + ser.calculateBits(); return ser; case Edge_Quad: cer = new CurvedEdgeRecord(); @@ -141,6 +172,7 @@ public class EdgeType { cer.controlDeltaY = data[2] * multiplier; cer.anchorDeltaX = data[3] * multiplier; cer.anchorDeltaY = data[4] * multiplier; + cer.calculateBits(); return cer; } return null; @@ -262,6 +294,168 @@ public class EdgeType { return data; } - public void write(SWFOutputStream sos) throws IOException { + public void write(GFxOutputStream sos) throws IOException { + int x; + int y; + int m1 = 1; + int m2 = 3; + int m3 = 7; + int m4 = 0xF; + int m5 = 0x1F; + int m6 = 0x3F; + int m7 = 0x7F; + switch (data[0]) { + case Edge_HLine: + x = data[1]; + if (x >= GFxOutputStream.MinSInt12 && x <= GFxOutputStream.MaxSInt12) { + sos.writeUI8((x << 4) | Edge_H12); + sos.writeUI8(x >> 4); + return; + } + sos.writeUI8((x << 4) | Edge_H20); + sos.writeUI8(x >> 4); + sos.writeUI8(x >> 12); + break; + case Edge_VLine: + y = data[1]; + if (y >= GFxOutputStream.MinSInt12 && y <= GFxOutputStream.MaxSInt12) { + sos.writeUI8((y << 4) | Edge_V12); + sos.writeUI8(y >> 4); + return; + } + sos.writeUI8((y << 4) | Edge_V20); + sos.writeUI8(y >> 4); + sos.writeUI8(y >> 12); + return; + case Edge_Line: + x = data[1]; + y = data[2]; + if (x >= GFxOutputStream.MinSInt6 && x <= GFxOutputStream.MaxSInt6 && y >= GFxOutputStream.MinSInt6 && y <= GFxOutputStream.MaxSInt6) { + sos.writeUI8((x << 4) | Edge_L6); + sos.writeUI8(((x >> 4) & m2) | (y << 2)); + return; + } + if (x >= GFxOutputStream.MinSInt10 && x <= GFxOutputStream.MaxSInt10 && y >= GFxOutputStream.MinSInt10 && y <= GFxOutputStream.MaxSInt10) { + sos.writeUI8((x << 4) | Edge_L10); + sos.writeUI8(((x >> 4) & m6) | (y << 6)); + sos.writeUI8(y >> 2); + return; + } + if (x >= GFxOutputStream.MinSInt14 && x <= GFxOutputStream.MaxSInt14 && y >= GFxOutputStream.MinSInt14 && y <= GFxOutputStream.MaxSInt14) { + sos.writeUI8((x << 4) | Edge_L14); + sos.writeUI8(x >> 4); + sos.writeUI8(((x >> 12) & m2) | (y << 2)); + sos.writeUI8(y >> 6); + return; + } + sos.writeUI8((x << 4) | Edge_L18); + sos.writeUI8(x >> 4); + sos.writeUI8(((x >> 12) & m6) | (y << 6)); + sos.writeUI8(y >> 2); + sos.writeUI8(y >> 10); + return; + case Edge_Quad: + int cx = data[1]; + int cy = data[2]; + int ax = data[3]; + int ay = data[4]; + int minV = cx; + int maxV = cx; + if (cy < minV) { + minV = cy; + } + if (cy > maxV) { + maxV = cy; + } + if (ax < minV) { + minV = ax; + } + if (ax > maxV) { + maxV = ax; + } + if (ay < minV) { + minV = ay; + } + if (ay > maxV) { + maxV = ay; + } + + + + if (minV >= GFxOutputStream.MinSInt5 && maxV <= GFxOutputStream.MaxSInt5) { + sos.writeUI8(((cx << 4) | Edge_C5)); + sos.writeUI8((((cx >> 4) & m1) | ((cy << 1) & m6) | (ax << 6))); + sos.writeUI8((((ax >> 2) & m3) | (ay << 3))); + return; + } + if (minV >= GFxOutputStream.MinSInt7 && maxV <= GFxOutputStream.MaxSInt7) { + sos.writeUI8(((cx << 4) | Edge_C7)); + sos.writeUI8((((cx >> 4) & m3) | (cy << 3))); + sos.writeUI8((((cy >> 5) & m2) | (ax << 2))); + sos.writeUI8((((ax >> 6) & m1) | (ay << 1))); + return; + } + if (minV >= GFxOutputStream.MinSInt9 && maxV <= GFxOutputStream.MaxSInt9) { + sos.writeUI8(((cx << 4) | Edge_C9)); + sos.writeUI8((((cx >> 4) & m5) | (cy << 5))); + sos.writeUI8((((cy >> 3) & m6) | (ax << 6))); + sos.writeUI8((((ax >> 2) & m7) | (ay << 7))); + sos.writeUI8(((ay >> 1))); + return; + } + if (minV >= GFxOutputStream.MinSInt11 && maxV <= GFxOutputStream.MaxSInt11) { + sos.writeUI8(((cx << 4) | Edge_C11)); + sos.writeUI8((((cx >> 4) & m7) | (cy << 7))); + sos.writeUI8(((cy >> 1))); + sos.writeUI8((((cy >> 9) & m2) | (ax << 2))); + sos.writeUI8((((ax >> 6) & m5) | (ay << 5))); + sos.writeUI8(((ay >> 3))); + return; + } + if (minV >= GFxOutputStream.MinSInt13 && maxV <= GFxOutputStream.MaxSInt13) { + sos.writeUI8(((cx << 4) | Edge_C13)); + sos.writeUI8(((cx >> 4))); + sos.writeUI8((((cx >> 12) & m1) | (cy << 1))); + sos.writeUI8((((cy >> 7) & m6) | (ax << 6))); + sos.writeUI8(((ax >> 2))); + sos.writeUI8((((ax >> 10) & m3) | (ay << 3))); + sos.writeUI8(((ay >> 5))); + return; + } + if (minV >= GFxOutputStream.MinSInt15 && maxV <= GFxOutputStream.MaxSInt15) { + sos.writeUI8(((cx << 4) | Edge_C15)); + sos.writeUI8(((cx >> 4))); + sos.writeUI8((((cx >> 12) & m3) | (cy << 3))); + sos.writeUI8(((cy >> 5))); + sos.writeUI8((((cy >> 13) & m2) | (ax << 2))); + sos.writeUI8(((ax >> 6))); + sos.writeUI8((((ax >> 14) & m1) | (ay << 1))); + sos.writeUI8(((ay >> 7))); + return; + } + if (minV >= GFxOutputStream.MinSInt17 && maxV <= GFxOutputStream.MaxSInt17) { + sos.writeUI8(((cx << 4) | Edge_C17)); + sos.writeUI8(((cx >> 4))); + sos.writeUI8((((cx >> 12) & m5) | (cy << 5))); + sos.writeUI8(((cy >> 3))); + sos.writeUI8((((cy >> 11) & m6) | (ax << 6))); + sos.writeUI8(((ax >> 2))); + sos.writeUI8((((ax >> 10) & m7) | (ay << 7))); + sos.writeUI8(((ay >> 1))); + sos.writeUI8(((ay >> 9))); + return; + } + sos.writeUI8(((cx << 4) | Edge_C19)); + sos.writeUI8(((cx >> 4))); + sos.writeUI8((((cx >> 12) & m7) | (cy << 7))); + sos.writeUI8(((cy >> 1))); + sos.writeUI8(((cy >> 9))); + sos.writeUI8((((cy >> 17) & m2) | (ax << 2))); + sos.writeUI8(((ax >> 6))); + sos.writeUI8((((ax >> 14) & m5) | (ay << 5))); + sos.writeUI8(((ay >> 3))); + sos.writeUI8(((ay >> 11))); + return; + } } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/gfx/FONTINFO.java b/trunk/src/com/jpexs/decompiler/flash/types/gfx/FONTINFO.java index 584bb5ac4..bfd77620e 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/gfx/FONTINFO.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/gfx/FONTINFO.java @@ -16,7 +16,6 @@ */ package com.jpexs.decompiler.flash.types.gfx; -import com.jpexs.decompiler.flash.SWFOutputStream; import java.io.IOException; /** @@ -42,7 +41,7 @@ public class FONTINFO { } } - public void write(SWFOutputStream sos) throws IOException { + public void write(GFxOutputStream sos) throws IOException { sos.writeUI16(fontId); sos.writeUI16(glyphIndices.length); for (int i = 0; i < glyphIndices.length; i++) { diff --git a/trunk/src/com/jpexs/decompiler/flash/types/gfx/FontType.java b/trunk/src/com/jpexs/decompiler/flash/types/gfx/FontType.java index 8091271e2..20f29d4de 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/gfx/FontType.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/gfx/FontType.java @@ -16,7 +16,6 @@ */ package com.jpexs.decompiler.flash.types.gfx; -import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.types.SHAPE; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -37,14 +36,13 @@ public class FontType { public int ascent; public int descent; public int leading; - public GlyphType[] glyphs; - public GlyphInfoType[] glyphInfo; - public KerningPairType[] kerning; + public List glyphs; + public List glyphInfo; + public List kerning; public FontType(GFxInputStream sis) throws IOException { sis = new GFxInputStream(sis); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - //fontId = sis.readUI16(); int val; while ((val = sis.readUI8()) > 0) { baos.write(val); @@ -59,21 +57,21 @@ public class FontType { long glyphBytesLen = sis.readUI32(); byte glyphBytes[] = new byte[(int) glyphBytesLen]; sis.read(glyphBytes); - glyphInfo = new GlyphInfoType[(int) numGlyphs]; + glyphInfo = new ArrayList<>(); for (int i = 0; i < numGlyphs; i++) { - glyphInfo[i] = new GlyphInfoType(sis); + glyphInfo.add(new GlyphInfoType(sis)); } long kerningTableSize = sis.readUI30(); - kerning = new KerningPairType[((int) kerningTableSize)]; + kerning = new ArrayList<>(); for (int i = 0; i < kerningTableSize; i++) { - kerning[i] = new KerningPairType(sis); + kerning.add(new KerningPairType(sis)); } - glyphs = new GlyphType[glyphInfo.length]; - for (int i = 0; i < glyphInfo.length; i++) { - sis.setPos(glyphInfo[i].globalOffset); - glyphs[i] = new GlyphType(sis); + glyphs = new ArrayList<>(); + for (int i = 0; i < glyphInfo.size(); i++) { + sis.setPos(glyphInfo.get(i).globalOffset); + glyphs.add(new GlyphType(sis)); } } @@ -85,6 +83,32 @@ public class FontType { return ret; } - public void write(SWFOutputStream sos) throws IOException { + public void write(GFxOutputStream sos) throws IOException { + sos = new GFxOutputStream(sos); + sos.write(fontName.getBytes()); + sos.writeUI8(0); + sos.writeUI16(flags); + sos.writeUI16(nominalSize); + sos.writeUI16(ascent); + sos.writeUI16(descent); + sos.writeUI16(leading); + sos.writeUI32(glyphInfo.size()); //numGlyphs + long headerLen = sos.getPos() + 4; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GFxOutputStream sos2 = new GFxOutputStream(baos); + for (int i = 0; i < glyphs.size(); i++) { + glyphInfo.get(i).globalOffset = headerLen + sos2.getPos(); + glyphs.get(i).write(sos2); + } + byte[] glyphBytes = baos.toByteArray(); + sos.writeUI32(glyphBytes.length); + sos.write(glyphBytes); + for (int i = 0; i < glyphInfo.size(); i++) { + glyphInfo.get(i).write(sos); + } + sos.writeUI30(kerning.size()); + for (KerningPairType kp : kerning) { + kp.write(sos); + } } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/gfx/GFxOutputStream.java b/trunk/src/com/jpexs/decompiler/flash/types/gfx/GFxOutputStream.java new file mode 100644 index 000000000..a751424da --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/types/gfx/GFxOutputStream.java @@ -0,0 +1,195 @@ +/* + * 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.gfx; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * + * @author JPEXS + */ +public class GFxOutputStream extends OutputStream { + + public static final int MaxUInt6 = (1 << 6) - 1; + public static final int MaxUInt7 = (1 << 7) - 1; + public static final int MaxUInt14 = (1 << 14) - 1; + public static final int MaxUInt22 = (1 << 22) - 1; + public static final int MaxUInt30 = (1 << 30) - 1; + public static final int MinSInt5 = -(1 << 4); + public static final int MaxSInt5 = (1 << 4) - 1; + public static final int MinSInt6 = -(1 << 5); + public static final int MaxSInt6 = (1 << 5) - 1; + public static final int MinSInt7 = -(1 << 6); + public static final int MaxSInt7 = (1 << 6) - 1; + public static final int MinSInt8 = -(1 << 7); + public static final int MaxSInt8 = (1 << 7) - 1; + public static final int MinSInt9 = -(1 << 8); + public static final int MaxSInt9 = (1 << 8) - 1; + public static final int MinSInt10 = -(1 << 9); + public static final int MaxSInt10 = (1 << 9) - 1; + public static final int MinSInt11 = -(1 << 10); + public static final int MaxSInt11 = (1 << 10) - 1; + public static final int MinSInt12 = -(1 << 11); + public static final int MaxSInt12 = (1 << 11) - 1; + public static final int MinSInt13 = -(1 << 12); + public static final int MaxSInt13 = (1 << 12) - 1; + public static final int MinSInt14 = -(1 << 13); + public static final int MaxSInt14 = (1 << 13) - 1; + public static final int MinSInt15 = -(1 << 14); + public static final int MaxSInt15 = (1 << 14) - 1; + public static final int MinSInt17 = -(1 << 16); + public static final int MaxSInt17 = (1 << 16) - 1; + public static final int MinSInt18 = -(1 << 17); + public static final int MaxSInt18 = (1 << 17) - 1; + public static final int MinSInt19 = -(1 << 18); + public static final int MaxSInt19 = (1 << 18) - 1; + public static final int MinSInt20 = -(1 << 19); + public static final int MaxSInt20 = (1 << 19) - 1; + public static final int MinSInt22 = -(1 << 21); + public static final int MaxSInt22 = (1 << 21) - 1; + private OutputStream os; + private long pos = 0; + + public GFxOutputStream(OutputStream os) { + this.os = os; + } + + public long getPos() { + return pos; + } + + @Override + public void write(int b) throws IOException { + os.write(b); + pos++; + } + + /** + * Writes SI32 (Signed 32bit integer) value to the stream + * + * @param value SI32 value + * @throws IOException + */ + public void writeSI32(long value) throws IOException { + writeUI32(value); + } + + /** + * Writes SI16 (Signed 16bit integer) value to the stream + * + * @param value SI16 value + * @throws IOException + */ + public void writeSI16(int value) throws IOException { + writeUI16(value); + } + + /** + * Writes SI8 (Signed 8bit integer) value to the stream + * + * @param value SI8 value + * @throws IOException + */ + public void writeSI8(int value) throws IOException { + writeUI8(value); + } + + /** + * Writes UI32 (Unsigned 32bit integer) value to the stream + * + * @param value UI32 value + * @throws IOException + */ + public void writeUI32(long value) throws IOException { + write((int) (value & 0xff)); + write((int) ((value >> 8) & 0xff)); + write((int) ((value >> 16) & 0xff)); + write((int) ((value >> 24) & 0xff)); + } + + /** + * Writes UI16 (Unsigned 16bit integer) value to the stream + * + * @param value UI16 value + * @throws IOException + */ + public void writeUI16(int value) throws IOException { + write((int) (value & 0xff)); + write((int) ((value >> 8) & 0xff)); + } + + /** + * Writes UI8 (Unsigned 8bit integer) value to the stream + * + * @param val UI8 value to write + * @throws IOException + */ + public void writeUI8(int val) throws IOException { + write(val & 0xff); + } + + /** + * Writes FLOAT (single precision floating point value) value to the stream + * + * @param value FLOAT value + * @throws IOException + */ + public void writeFLOAT(float value) throws IOException { + writeUI32(Float.floatToIntBits(value)); + } + + public void writeSI15(int v) throws IOException { + if (v >= MinSInt7 && v <= MaxSInt7) { + writeUI8(v << 1); + return; + } + writeUI8((v << 1) | 1); + writeUI8(v >> 7); + } + + public void writeUI15(int v) throws IOException { + if (v <= MaxUInt7) { + writeUI8(v << 1); + return; + } + writeUI8((v << 1) | 1); + writeUI8(v >> 7); + } + + public void writeUI30(int v) throws IOException { + if (v <= MaxUInt6) { + writeUI8(v << 2); + return; + } + if (v <= MaxUInt14) { + writeUI8((v << 2) | 1); + writeUI8(v >> 6); + return; + } + if (v <= MaxUInt22) { + writeUI8((v << 2) | 2); + writeUI8(v >> 6); + writeUI8(v >> 14); + return; + } + writeUI8((v << 2) | 3); + writeUI8(v >> 6); + writeUI8(v >> 14); + writeUI8(v >> 22); + } +} diff --git a/trunk/src/com/jpexs/decompiler/flash/types/gfx/GLYPHIDX.java b/trunk/src/com/jpexs/decompiler/flash/types/gfx/GLYPHIDX.java index 4be9681c0..30a28bd6d 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/gfx/GLYPHIDX.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/gfx/GLYPHIDX.java @@ -16,7 +16,6 @@ */ package com.jpexs.decompiler.flash.types.gfx; -import com.jpexs.decompiler.flash.SWFOutputStream; import java.io.IOException; /** @@ -38,7 +37,7 @@ public class GLYPHIDX { this.indexInTexture = sis.readUI16(); } - public void write(SWFOutputStream sos) throws IOException { + public void write(GFxOutputStream sos) throws IOException { sos.writeUI16(indexInFont); sos.writeUI16(indexInTexture); } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/gfx/GlyphInfoType.java b/trunk/src/com/jpexs/decompiler/flash/types/gfx/GlyphInfoType.java index 99d9b82a3..54cfbe2ce 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/gfx/GlyphInfoType.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/gfx/GlyphInfoType.java @@ -16,7 +16,6 @@ */ package com.jpexs.decompiler.flash.types.gfx; -import com.jpexs.decompiler.flash.SWFOutputStream; import java.io.IOException; /** @@ -41,7 +40,7 @@ public class GlyphInfoType { this.globalOffset = sis.readUI32(); } - public void write(SWFOutputStream sos) throws IOException { + public void write(GFxOutputStream sos) throws IOException { sos.writeUI16(glyphCode); sos.writeSI16(advanceX); sos.writeUI32(globalOffset); diff --git a/trunk/src/com/jpexs/decompiler/flash/types/gfx/GlyphType.java b/trunk/src/com/jpexs/decompiler/flash/types/gfx/GlyphType.java index 681995bde..f5225d63f 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/gfx/GlyphType.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/gfx/GlyphType.java @@ -16,8 +16,9 @@ */ package com.jpexs.decompiler.flash.types.gfx; -import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; import java.io.IOException; @@ -33,6 +34,32 @@ public class GlyphType { public int[] boundingBox; public ContourType[] contours; + public GlyphType(List records) { + RECT bounds = SHAPERECORD.getBounds(records); + boundingBox = new int[4]; + boundingBox[0] = bounds.Xmin; + boundingBox[1] = bounds.Ymin; + boundingBox[2] = bounds.Xmax; + boundingBox[3] = bounds.Ymax; + List cont = new ArrayList<>(); + List contoursList = new ArrayList<>(); + for (int i = 0; i < records.size(); i++) { + SHAPERECORD r = records.get(i); + if ((r instanceof StyleChangeRecord) && ((StyleChangeRecord) r).stateMoveTo) { + if (!cont.isEmpty()) { + contoursList.add(new ContourType(cont)); + cont.clear(); + } + } + cont.add(r); + } + if (!cont.isEmpty()) { + contoursList.add(new ContourType(cont)); + cont.clear(); + } + contours = contoursList.toArray(new ContourType[contoursList.size()]); + } + public GlyphType(GFxInputStream sis) throws IOException { boundingBox = new int[4]; for (int i = 0; i < 4; i++) { @@ -48,19 +75,29 @@ public class GlyphType { public SHAPE toSHAPE() { SHAPE shp = new SHAPE(); shp.numFillBits = 1; - shp.numLineBits = 1; + shp.numLineBits = 0; List recs = new ArrayList<>(); StyleChangeRecord scr = new StyleChangeRecord(); - scr.fillStyle0 = 1; - scr.stateFillStyle0 = true; + scr.fillStyle0 = 0; + scr.fillStyle1 = 1; + scr.stateFillStyle1 = true; + scr.stateFillStyle0 = false; recs.add(scr); for (ContourType cnt : contours) { recs.addAll(cnt.toSHAPERECORDS()); } + recs.add(new EndShapeRecord()); shp.shapeRecords = recs; return shp; } - public void write(SWFOutputStream sos) throws IOException { + public void write(GFxOutputStream sos) throws IOException { + for (int b : boundingBox) { + sos.writeSI15(b); + } + sos.writeUI15(contours.length); + for (ContourType cnt : contours) { + cnt.write(sos); + } } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/gfx/KerningPairType.java b/trunk/src/com/jpexs/decompiler/flash/types/gfx/KerningPairType.java index 9936e6ec0..990381276 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/gfx/KerningPairType.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/gfx/KerningPairType.java @@ -16,7 +16,6 @@ */ package com.jpexs.decompiler.flash.types.gfx; -import com.jpexs.decompiler.flash.SWFOutputStream; import java.io.IOException; /** @@ -41,7 +40,7 @@ public class KerningPairType { this.advance = sis.readSI16(); } - public void write(SWFOutputStream sos) throws IOException { + public void write(GFxOutputStream sos) throws IOException { sos.writeUI16(char1); sos.writeUI16(char2); sos.writeSI16(advance); diff --git a/trunk/src/com/jpexs/decompiler/flash/types/gfx/TEXGLYPH.java b/trunk/src/com/jpexs/decompiler/flash/types/gfx/TEXGLYPH.java index 7fddac33b..541b86650 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/gfx/TEXGLYPH.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/gfx/TEXGLYPH.java @@ -16,7 +16,6 @@ */ package com.jpexs.decompiler.flash.types.gfx; -import com.jpexs.decompiler.flash.SWFOutputStream; import java.io.IOException; /** @@ -50,7 +49,7 @@ public class TEXGLYPH { this.uvOriginY = sis.readFLOAT(); } - public void write(SWFOutputStream sos) throws IOException { + public void write(GFxOutputStream sos) throws IOException { sos.writeFLOAT(uvBoundsLeft); sos.writeFLOAT(uvBoundsTop); sos.writeFLOAT(uvBoundsRight); 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 9bf4b88dd..ce8724ee0 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/CurvedEdgeRecord.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/CurvedEdgeRecord.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.types.shaperecords; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFOutputStream; /** * @@ -68,4 +69,12 @@ public class CurvedEdgeRecord extends SHAPERECORD { public boolean isMove() { return true; } + + @Override + public void calculateBits() { + numBits = SWFOutputStream.getNeededBitsS(controlDeltaX, controlDeltaY, anchorDeltaX, anchorDeltaY) - 2; + if (numBits < 0) { + numBits = 0; + } + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/EndShapeRecord.java b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/EndShapeRecord.java index 400fcfe8f..79f5e02ba 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/EndShapeRecord.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/EndShapeRecord.java @@ -53,4 +53,8 @@ public class EndShapeRecord extends SHAPERECORD { public boolean isMove() { return false; } + + @Override + public void calculateBits() { + } } 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 119695a16..ca8b42669 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java @@ -32,6 +32,7 @@ import com.jpexs.decompiler.flash.types.RGB; import com.jpexs.decompiler.flash.types.RGBA; import com.jpexs.decompiler.flash.types.SHAPE; import com.jpexs.helpers.Cache; +import com.jpexs.helpers.Helper; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; @@ -73,10 +74,12 @@ import javax.swing.JPanel; * * @author JPEXS */ -public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { +public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Serializable { private static final float DESCALE = 20; + public abstract void calculateBits(); + @Override public Set getNeededCharacters() { HashSet ret = new HashSet<>(); @@ -847,10 +850,33 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { for (SHAPE s : shapes) { images.add(s.toImage(1, new ArrayList(), color)); } + int maxw = 0; + int maxh = 0; + for (BufferedImage im : images) { + if (im.getWidth() > maxw) { + maxw = im.getWidth(); + } + if (im.getHeight() > maxh) { + maxh = im.getHeight(); + } + } int cols = (int) Math.ceil(Math.sqrt(images.size())); int pos = 0; int w2 = prevWidth / cols; int h2 = prevHeight / cols; + + + int mh = maxh * w2 / maxw; + int mw; + if (mh > h2) { + mw = maxw * h2 / maxh; + mh = h2; + } else { + mw = w2; + } + + float ratio = (float) mw / (float) maxw; + loopy: for (int y = 0; y < cols; y++) { for (int x = 0; x < cols; x++) { @@ -861,16 +887,18 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { int h1 = images.get(pos).getHeight(); - int h = h1 * w2 / w1; - int w; - if (h > h2) { - w = w1 * h2 / h1; - h = h2; - } else { - w = w2; - } - int px = x * w2 + w2 / 2 - w / 2; - int py = y * h2 + h2 / 2 - h / 2; + /*int h = h1 * mw / w1; + int w; + if (h > mh) { + w = w1 * mh / h1; + h = mh; + } else { + w = mw; + }*/ + int w = Math.round(ratio * w1); + int h = Math.round(ratio * h1); + int px = x * mw + mw / 2 - w / 2; + int py = y * mh + mh - h; g.drawImage(images.get(pos), px, py, px + w, py + h, 0, 0, w1, h1, null); pos++; } @@ -1139,4 +1167,37 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters { image = ImageIO.read(in); } } + + public static SHAPE resizeSHAPE(SHAPE shp, int multiplier) { + SHAPE ret = new SHAPE(); + ret.numFillBits = shp.numFillBits; + ret.numLineBits = shp.numLineBits; + List recs = new ArrayList<>(); + for (SHAPERECORD r : shp.shapeRecords) { + SHAPERECORD c = (SHAPERECORD) Helper.deepCopy(r); + if (c instanceof StyleChangeRecord) { + StyleChangeRecord scr = (StyleChangeRecord) c; + scr.moveDeltaX = (multiplier * scr.moveDeltaX); + scr.moveDeltaY = (multiplier * scr.moveDeltaY); + scr.calculateBits(); + } + if (c instanceof CurvedEdgeRecord) { + CurvedEdgeRecord cer = (CurvedEdgeRecord) c; + cer.controlDeltaX = (multiplier * cer.controlDeltaX); + cer.controlDeltaY = (multiplier * cer.controlDeltaY); + cer.anchorDeltaX = (multiplier * cer.anchorDeltaX); + cer.anchorDeltaY = (multiplier * cer.anchorDeltaY); + cer.calculateBits(); + } + if (c instanceof StraightEdgeRecord) { + StraightEdgeRecord ser = (StraightEdgeRecord) c; + ser.deltaX = (multiplier * ser.deltaX); + ser.deltaY = (multiplier * ser.deltaY); + ser.calculateBits(); + } + recs.add(c); + } + ret.shapeRecords = recs; + return ret; + } } 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 e9db41b07..f0281ce49 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StraightEdgeRecord.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StraightEdgeRecord.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.types.shaperecords; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFOutputStream; /** * @@ -80,4 +81,12 @@ public class StraightEdgeRecord extends SHAPERECORD { public boolean isMove() { return true; } + + @Override + public void calculateBits() { + numBits = SWFOutputStream.getNeededBitsS(deltaX, deltaY) - 2; + if (numBits < 0) { + numBits = 0; + } + } } 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 1bae85c11..132acb5a1 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StyleChangeRecord.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StyleChangeRecord.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.types.shaperecords; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; import java.util.Set; @@ -105,4 +106,9 @@ public class StyleChangeRecord extends SHAPERECORD implements Cloneable { } return null; } + + @Override + public void calculateBits() { + moveBits = SWFOutputStream.getNeededBitsS(moveDeltaX, moveDeltaY); + } }