diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java index b75f1c4d5..9db8f2b49 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java @@ -1,685 +1,687 @@ -/* - * Copyright (C) 2010-2015 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.decompiler.flash.exporters; - -import com.jpacker.JPacker; -import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; -import com.jpexs.decompiler.flash.EventListener; -import com.jpexs.decompiler.flash.RetryTask; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; -import com.jpexs.decompiler.flash.exporters.modes.FramesExportMode; -import com.jpexs.decompiler.flash.exporters.settings.ButtonExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.FramesExportSettings; -import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; -import com.jpexs.decompiler.flash.helpers.BMPFile; -import com.jpexs.decompiler.flash.helpers.ImageHelper; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.timeline.Frame; -import com.jpexs.decompiler.flash.timeline.Timeline; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.filters.BEVELFILTER; -import com.jpexs.decompiler.flash.types.filters.COLORMATRIXFILTER; -import com.jpexs.decompiler.flash.types.filters.CONVOLUTIONFILTER; -import com.jpexs.decompiler.flash.types.filters.DROPSHADOWFILTER; -import com.jpexs.decompiler.flash.types.filters.FILTER; -import com.jpexs.decompiler.flash.types.filters.GLOWFILTER; -import com.jpexs.decompiler.flash.types.filters.GRADIENTBEVELFILTER; -import com.jpexs.decompiler.flash.types.filters.GRADIENTGLOWFILTER; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.Path; -import com.jpexs.helpers.utf8.Utf8Helper; -import gnu.jpdf.PDFJob; -import java.awt.Color; -import java.awt.Graphics; -import java.awt.image.BufferedImage; -import java.awt.print.PageFormat; -import java.awt.print.Paper; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.Stack; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.imageio.stream.FileImageOutputStream; -import javax.imageio.stream.ImageOutputStream; -import net.kroo.elliot.GifSequenceWriter; -import net.weiner.kevin.AnimatedGifEncoder; -import org.monte.media.VideoFormatKeys; -import org.monte.media.avi.AVIWriter; - -/** - * - * @author JPEXS - */ -public class FrameExporter { - - private static final Logger logger = Logger.getLogger(FrameExporter.class.getName()); - - public List exportFrames(AbortRetryIgnoreHandler handler, String outdir, SWF swf, int containerId, List frames, ButtonExportSettings settings, EventListener evl) throws IOException { - FramesExportMode fem; - switch (settings.mode) { - case BMP: - fem = FramesExportMode.BMP; - break; - case PNG: - fem = FramesExportMode.PNG; - break; - case SVG: - fem = FramesExportMode.SVG; - break; - default: - throw new Error("Unsupported button export mode"); - } - - FramesExportSettings fes = new FramesExportSettings(fem, settings.zoom); - return exportFrames(handler, outdir, swf, containerId, frames, fes, evl); - } - - public List exportFrames(AbortRetryIgnoreHandler handler, String outdir, final SWF swf, int containerId, List frames, final FramesExportSettings settings, final EventListener evl) throws IOException { - final List ret = new ArrayList<>(); - if (swf.tags.isEmpty()) { - return ret; - } - Timeline tim0; - String path = ""; - if (containerId == 0) { - tim0 = swf.getTimeline(); - } else { - tim0 = ((Timelined) swf.getCharacter(containerId)).getTimeline(); - path = File.separator + Helper.makeFileName(swf.getCharacter(containerId).getExportFileName()); - } - - final Timeline tim = tim0; - - if (frames == null) { - int frameCnt = tim.getFrameCount(); - frames = new ArrayList<>(); - for (int i = 0; i < frameCnt; i++) { - frames.add(i); - } - } - - final File foutdir = new File(outdir + path); - Path.createDirectorySafe(foutdir); - - final List fframes = frames; - - Color backgroundColor = null; - if (settings.mode == FramesExportMode.AVI) { - for (Tag t : swf.tags) { - if (t instanceof SetBackgroundColorTag) { - SetBackgroundColorTag sb = (SetBackgroundColorTag) t; - backgroundColor = sb.backgroundColor.toColor(); - } - } - } - - if (settings.mode == FramesExportMode.SVG) { - for (int i = 0; i < frames.size(); i++) { - if (evl != null) { - evl.handleExportingEvent("frame", i + 1, frames.size(), tim.parentTag == null ? "" : tim.parentTag.getName()); - } - - final int fi = i; - final Color fbackgroundColor = backgroundColor; - new RetryTask(() -> { - int frame = fframes.get(fi); - File f = new File(foutdir + File.separator + frame + ".svg"); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(f))) { - ExportRectangle rect = new ExportRectangle(tim.displayRect); - rect.xMax *= settings.zoom; - rect.yMax *= settings.zoom; - rect.xMin *= settings.zoom; - rect.yMin *= settings.zoom; - SVGExporter exporter = new SVGExporter(rect); - if (fbackgroundColor != null) { - exporter.setBackGroundColor(fbackgroundColor); - } - SWF.frameToSvg(tim, frame, 0, null, 0, exporter, new ColorTransform(), 0, settings.zoom); - fos.write(Utf8Helper.getBytes(exporter.getSVG())); - } - ret.add(f); - }, handler).run(); - - if (evl != null) { - evl.handleExportedEvent("frame", i + 1, frames.size(), tim.parentTag == null ? "" : tim.parentTag.getName()); - } - } - - return ret; - } - - if (settings.mode == FramesExportMode.CANVAS) { - if (evl != null) { - evl.handleExportingEvent("canvas", 1, 1, tim.parentTag == null ? "" : tim.parentTag.getName()); - } - - final Timeline ftim = tim; - final Color fbackgroundColor = backgroundColor; - final SWF fswf = swf; - new RetryTask(() -> { - File fcanvas = new File(foutdir + File.separator + "canvas.js"); - Helper.saveStream(SWF.class.getClassLoader().getResourceAsStream("com/jpexs/helpers/resource/canvas.js"), fcanvas); - ret.add(fcanvas); - - File f = new File(foutdir + File.separator + "frames.js"); - File fmin = new File(foutdir + File.separator + "frames.min.js"); - int width = (int) (ftim.displayRect.getWidth() * settings.zoom / SWF.unitDivisor); - int height = (int) (ftim.displayRect.getHeight() * settings.zoom / SWF.unitDivisor); - try (final OutputStream fos = new BufferedOutputStream(new FileOutputStream(f))) { - fos.write(Utf8Helper.getBytes("\r\n")); - Set library = new HashSet<>(); - ftim.getNeededCharacters(fframes, library); - - SWF.writeLibrary(fswf, library, fos); - - String currentName = ftim.id == 0 ? "main" : SWF.getTypePrefix(fswf.getCharacter(ftim.id)) + ftim.id; - - fos.write(Utf8Helper.getBytes("function " + currentName + "(ctx,ctrans,frame,ratio,time){\r\n")); - fos.write(Utf8Helper.getBytes("\tctx.save();\r\n")); - fos.write(Utf8Helper.getBytes("\tctx.transform(1,0,0,1," + (-ftim.displayRect.Xmin * settings.zoom / SWF.unitDivisor) + "," + (-ftim.displayRect.Ymin * settings.zoom / SWF.unitDivisor) + ");\r\n")); - fos.write(Utf8Helper.getBytes(framesToHtmlCanvas(SWF.unitDivisor / settings.zoom, ftim, fframes, 0, null, 0, ftim.displayRect, new ColorTransform(), fbackgroundColor))); - fos.write(Utf8Helper.getBytes("\tctx.restore();\r\n")); - fos.write(Utf8Helper.getBytes("}\r\n\r\n")); - - fos.write(Utf8Helper.getBytes("var frame = -1;\r\n")); - fos.write(Utf8Helper.getBytes("var time = 0;\r\n")); - fos.write(Utf8Helper.getBytes("var frames = [];\r\n")); - for (int i : fframes) { - fos.write(Utf8Helper.getBytes("frames.push(" + i + ");\r\n")); - } - fos.write(Utf8Helper.getBytes("\r\n")); - RGB backgroundColor1 = new RGB(255, 255, 255); - for (Tag t : fswf.tags) { - if (t instanceof SetBackgroundColorTag) { - SetBackgroundColorTag sb = (SetBackgroundColorTag) t; - backgroundColor1 = sb.backgroundColor; - } - } - - fos.write(Utf8Helper.getBytes("var backgroundColor = \"" + backgroundColor1.toHexRGB() + "\";\r\n")); - fos.write(Utf8Helper.getBytes("var originalWidth = " + width + ";\r\n")); - fos.write(Utf8Helper.getBytes("var originalHeight= " + height + ";\r\n")); - fos.write(Utf8Helper.getBytes("function nextFrame(ctx,ctrans){\r\n")); - fos.write(Utf8Helper.getBytes("\tvar oldframe = frame;\r\n")); - fos.write(Utf8Helper.getBytes("\tframe = (frame+1)%frames.length;\r\n")); - fos.write(Utf8Helper.getBytes("\tif(frame==oldframe){time++;}else{time=0;};\r\n")); - fos.write(Utf8Helper.getBytes("\tdrawFrame();\r\n")); - fos.write(Utf8Helper.getBytes("}\r\n\r\n")); - - fos.write(Utf8Helper.getBytes("function drawFrame(){\r\n")); - fos.write(Utf8Helper.getBytes("\tctx.fillStyle = backgroundColor;\r\n")); - fos.write(Utf8Helper.getBytes("\tctx.fillRect(0,0,canvas.width,canvas.height);\r\n")); - fos.write(Utf8Helper.getBytes("\tctx.save();\r\n")); - fos.write(Utf8Helper.getBytes("\tctx.transform(canvas.width/originalWidth,0,0,canvas.height/originalHeight,0,0);\r\n")); - fos.write(Utf8Helper.getBytes("\t" + currentName + "(ctx,ctrans,frames[frame],0,time);\r\n")); - fos.write(Utf8Helper.getBytes("\tctx.restore();\r\n")); - fos.write(Utf8Helper.getBytes("}\r\n\r\n")); - if (ftim.swf.frameRate > 0) { - fos.write(Utf8Helper.getBytes("window.setInterval(function(){nextFrame(ctx,ctrans);}," + (int) (1000.0 / ftim.swf.frameRate) + ");\r\n")); - } - fos.write(Utf8Helper.getBytes("nextFrame(ctx,ctrans);\r\n")); - } - - boolean packed = false; - if (Configuration.packJavaScripts.get()) { - try { - JPacker.main(new String[]{"-q", "-b", "62", "-o", fmin.getAbsolutePath(), f.getAbsolutePath()}); - f.delete(); - packed = true; - } catch (Exception | Error e) { // Something wrong in the packer - logger.log(Level.WARNING, "JPacker: Cannot minimize script"); - f.renameTo(fmin); - } - } else { - f.renameTo(fmin); - } - - File fh = new File(foutdir + File.separator + "frames.html"); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(fh)); FileInputStream fis = new FileInputStream(fmin)) { - fos.write(Utf8Helper.getBytes(CanvasShapeExporter.getHtmlPrefix(width, height))); - fos.write(Utf8Helper.getBytes(CanvasShapeExporter.getJsPrefix())); - byte[] buf = new byte[1000]; - int cnt; - while ((cnt = fis.read(buf)) > 0) { - fos.write(buf, 0, cnt); - } - if (packed) { - fos.write(Utf8Helper.getBytes(";")); - } - fos.write(Utf8Helper.getBytes(CanvasShapeExporter.getJsSuffix())); - fos.write(Utf8Helper.getBytes(CanvasShapeExporter.getHtmlSuffix())); - } - - fmin.delete(); - - ret.add(f); - }, handler).run(); - - if (evl != null) { - evl.handleExportedEvent("canvas", 1, 1, tim.parentTag == null ? "" : tim.parentTag.getName()); - } - return ret; - } - - final Timeline ftim = tim; - final Color fbackgroundColor = backgroundColor; - final Iterator frameImages = new Iterator() { - - private int pos = 0; - - @Override - public boolean hasNext() { - return fframes.size() > pos; - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - @Override - public BufferedImage next() { - if (!hasNext()) { - return null; - } - - if (evl != null) { - evl.handleExportingEvent("frame", pos + 1, fframes.size(), tim.parentTag == null ? "" : tim.parentTag.getName()); - } - - BufferedImage result = SWF.frameToImageGet(ftim, fframes.get(pos++), 0, null, 0, ftim.displayRect, new Matrix(), new ColorTransform(), fbackgroundColor, false, settings.zoom).getBufferedImage(); - - if (evl != null) { - evl.handleExportedEvent("frame", pos, fframes.size(), tim.parentTag == null ? "" : tim.parentTag.getName()); - } - - return result; - } - }; - - switch (settings.mode) { - case GIF: - new RetryTask(() -> { - File f = new File(foutdir + File.separator + "frames.gif"); - makeGIF(frameImages, swf.frameRate, f, evl); - ret.add(f); - }, handler).run(); - break; - case BMP: - for (int i = 0; frameImages.hasNext(); i++) { - final int fi = i; - new RetryTask(() -> { - File f = new File(foutdir + File.separator + (fframes.get(fi) + 1) + ".bmp"); - BMPFile.saveBitmap(frameImages.next(), f); - ret.add(f); - }, handler).run(); - } - break; - case PNG: - for (int i = 0; frameImages.hasNext(); i++) { - final int fi = i; - new RetryTask(() -> { - File file = new File(foutdir + File.separator + (fframes.get(fi) + 1) + ".png"); - ImageHelper.write(frameImages.next(), "PNG", file); - ret.add(file); - }, handler).run(); - } - - //ShapeExporterBase.clearCache(); - break; - case PDF: - new RetryTask(() -> { - File f = new File(foutdir + File.separator + "frames.pdf"); - PDFJob job = new PDFJob(new BufferedOutputStream(new FileOutputStream(f))); - PageFormat pf = new PageFormat(); - pf.setOrientation(PageFormat.PORTRAIT); - Paper p = new Paper(); - BufferedImage img0 = frameImages.next(); - p.setSize(img0.getWidth() + 10, img0.getHeight() + 10); - pf.setPaper(p); - - for (int i = 0; frameImages.hasNext(); i++) { - BufferedImage img = frameImages.next(); - Graphics g = job.getGraphics(pf); - g.drawImage(img, 5, 5, img.getWidth(), img.getHeight(), null); - g.dispose(); - } - - job.end(); - ret.add(f); - }, handler).run(); - break; - case AVI: - new RetryTask(() -> { - File f = new File(foutdir + File.separator + "frames.avi"); - makeAVI(frameImages, swf.frameRate, f, evl); - ret.add(f); - }, handler).run(); - break; - } - - return ret; - } - - private static String jsArrColor(RGB rgb) { - return "[" + rgb.red + "," + rgb.green + "," + rgb.blue + "," + ((rgb instanceof RGBA) ? ((RGBA) rgb).getAlphaFloat() : 1) + "]"; - } - - public static void makeAVI(Iterator images, int frameRate, File file, EventListener evl) throws IOException { - if (!images.hasNext()) { - return; - } - - AVIWriter out = new AVIWriter(file); - BufferedImage img0 = images.next(); - out.addVideoTrack(VideoFormatKeys.ENCODING_AVI_PNG, 1, frameRate, img0.getWidth(), img0.getHeight(), 0, 0); - try { - out.write(0, img0, 1); - while (images.hasNext()) { - out.write(0, images.next(), 1); - } - } finally { - out.close(); - } - } - - public static void makeGIF(Iterator images, int frameRate, File file, EventListener evl) throws IOException { - if (!images.hasNext()) { - return; - } - - AnimatedGifEncoder encoder = new AnimatedGifEncoder(); - encoder.setRepeat(0); // repeat forever - encoder.start(file.getAbsolutePath()); - encoder.setDelay(1000 / frameRate); - while (images.hasNext()) { - encoder.addFrame(images.next()); - } - - encoder.finish(); - } - - public static void makeGIFOld(Iterator images, int frameRate, File file, EventListener evl) throws IOException { - if (!images.hasNext()) { - return; - } - - try (ImageOutputStream output = new FileImageOutputStream(file)) { - BufferedImage img0 = images.next(); - GifSequenceWriter writer = new GifSequenceWriter(output, img0.getType(), 1000 / frameRate, true); - writer.writeToSequence(img0); - - while (images.hasNext()) { - writer.writeToSequence(images.next()); - } - - writer.close(); - } - } - - public static String framesToHtmlCanvas(double unitDivisor, Timeline timeline, List frames, int time, DepthState stateUnderCursor, int mouseButton, RECT displayRect, ColorTransform colorTransform, Color backGroundColor) { - StringBuilder sb = new StringBuilder(); - if (frames == null) { - frames = new ArrayList<>(); - for (int i = 0; i < timeline.getFrameCount(); i++) { - frames.add(i); - } - } - - sb.append("\tvar clips = [];\r\n"); - sb.append("\tvar frame_cnt = ").append(timeline.getFrameCount()).append(";\r\n"); - sb.append("\tframe = frame % frame_cnt;\r\n"); - sb.append("\tswitch(frame){\r\n"); - int maxDepth = timeline.getMaxDepth(); - Stack clipDepths = new Stack<>(); - for (int frame : frames) { - sb.append("\t\tcase ").append(frame).append(":\r\n"); - Frame frameObj = timeline.getFrame(frame); - for (int i = 1; i <= maxDepth + 1; i++) { - while (!clipDepths.isEmpty() && clipDepths.peek() <= i) { - clipDepths.pop(); - sb.append("\t\t\tvar o = clips.pop();\r\n"); - sb.append("\t\t\tctx.globalCompositeOperation = \"destination-in\";\r\n"); - sb.append("\t\t\tctx.setTransform(1,0,0,1,0,0);\r\n"); - sb.append("\t\t\tctx.drawImage(o.clipCanvas,0,0);\r\n"); - sb.append("\t\t\tvar ms=o.ctx._matrix;\r\n"); - sb.append("\t\t\to.ctx.setTransform(1,0,0,1,0,0);\r\n"); - sb.append("\t\t\to.ctx.globalCompositeOperation = \"source-over\";\r\n"); - sb.append("\t\t\to.ctx.drawImage(canvas,0,0);\r\n"); - sb.append("\t\t\to.ctx.applyTransforms(ms);\r\n"); - sb.append("\t\t\tctx = o.ctx;\r\n"); - sb.append("\t\t\tcanvas = o.canvas;\r\n"); - } - if (!frameObj.layers.containsKey(i)) { - continue; - } - DepthState layer = frameObj.layers.get(i); - if (!timeline.swf.getCharacters().containsKey(layer.characterId)) { - continue; - } - if (!layer.isVisible) { - continue; - } - - CharacterTag character = timeline.swf.getCharacter(layer.characterId); - if (colorTransform == null) { - colorTransform = new ColorTransform(); - } - - Matrix placeMatrix = new Matrix(layer.matrix); - placeMatrix.scaleX /= unitDivisor; - placeMatrix.scaleY /= unitDivisor; - placeMatrix.rotateSkew0 /= unitDivisor; - placeMatrix.rotateSkew1 /= unitDivisor; - placeMatrix.translateX /= unitDivisor; - placeMatrix.translateY /= unitDivisor; - - int f = 0; - String fstr = "0"; - if (character instanceof DefineSpriteTag) { - DefineSpriteTag sp = (DefineSpriteTag) character; - Timeline tim = sp.getTimeline(); - if (tim.getFrameCount() > 0) { - f = layer.time % tim.getFrameCount(); - fstr = "(" + f + "+time)%" + tim.getFrameCount(); - } - } - - if (layer.clipDepth != -1) { - clipDepths.push(layer.clipDepth); - sb.append("\t\t\tclips.push({ctx:ctx,canvas:canvas});\r\n"); - sb.append("\t\t\tvar ccanvas = createCanvas(canvas.width,canvas.height);\r\n"); - sb.append("\t\t\tvar cctx = ccanvas.getContext(\"2d\");\r\n"); - sb.append("\t\t\tenhanceContext(cctx);\r\n"); - sb.append("\t\t\tcctx.applyTransforms(ctx._matrix);\r\n"); - sb.append("\t\t\tcanvas = ccanvas;\r\n"); - sb.append("\t\t\tctx = cctx;\r\n"); - } - - if (layer.filters != null && layer.filters.size() > 0) { - sb.append("\t\t\tvar oldctx = ctx;\r\n"); - sb.append("\t\t\tvar fcanvas = createCanvas(canvas.width,canvas.height);"); - sb.append("\t\t\tvar fctx = fcanvas.getContext(\"2d\");\r\n"); - sb.append("\t\t\tenhanceContext(fctx);\r\n"); - sb.append("\t\t\tfctx.applyTransforms(ctx._matrix);\r\n"); - sb.append("\t\t\tctx = fctx;\r\n"); - } - - ColorTransform ctrans = layer.colorTransForm; - String ctrans_str = "ctrans"; - if (ctrans == null) { - ctrans = new ColorTransform(); - } else { - ctrans_str = "ctrans.merge(new cxform(" - + ctrans.getRedAdd() + "," + ctrans.getGreenAdd() + "," + ctrans.getBlueAdd() + "," + ctrans.getAlphaAdd() + "," - + ctrans.getRedMulti() + "," + ctrans.getGreenMulti() + "," + ctrans.getBlueMulti() + "," + ctrans.getAlphaMulti() - + "))"; - } - sb.append("\t\t\tplace(\"").append(SWF.getTypePrefix(character)).append(layer.characterId).append("\",canvas,ctx,[").append(placeMatrix.scaleX).append(",") - .append(placeMatrix.rotateSkew0).append(",") - .append(placeMatrix.rotateSkew1).append(",") - .append(placeMatrix.scaleY).append(",") - .append(placeMatrix.translateX).append(",") - .append(placeMatrix.translateY).append("],").append(ctrans_str).append(",").append("").append(layer.blendMode < 1 ? 1 : layer.blendMode).append(",").append(fstr).append(",").append(layer.ratio < 0 ? 0 : layer.ratio).append(",time").append(");\r\n"); - - if (layer.filters != null && layer.filters.size() > 0) { - for (FILTER filter : layer.filters) { - if (filter instanceof COLORMATRIXFILTER) { - COLORMATRIXFILTER cmf = (COLORMATRIXFILTER) filter; - String mat = "["; - for (int k = 0; k < cmf.matrix.length; k++) { - if (k > 0) { - mat += ","; - } - mat += cmf.matrix[k]; - } - mat += "]"; - sb.append("\t\t\tfcanvas = Filters.colorMatrix(fcanvas,fcanvas.getContext(\"2d\"),").append(mat).append(");\r\n"); - } - - if (filter instanceof CONVOLUTIONFILTER) { - CONVOLUTIONFILTER cf = (CONVOLUTIONFILTER) filter; - int height = cf.matrix.length; - int width = cf.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] = cf.matrix[x][y] * cf.divisor + cf.bias; - } - } - String mat = "["; - for (int k = 0; k < matrix2.length; k++) { - if (k > 0) { - mat += ","; - } - mat += matrix2[k]; - } - mat += "]"; - sb.append("\t\t\tfcanvas = Filters.convolution(fcanvas,fcanvas.getContext(\"2d\"),").append(mat).append(",false);\r\n"); - } - - if (filter instanceof GLOWFILTER) { - GLOWFILTER gf = (GLOWFILTER) filter; - sb.append("\t\t\tfcanvas = Filters.glow(fcanvas,fcanvas.getContext(\"2d\"),").append(gf.blurX).append(",").append(gf.blurY).append(",").append(gf.strength).append(",").append(jsArrColor(gf.glowColor)).append(",").append(gf.innerGlow ? "true" : "false").append(",").append(gf.knockout ? "true" : "false").append(",").append(gf.passes).append(");\r\n"); - } - - if (filter instanceof DROPSHADOWFILTER) { - DROPSHADOWFILTER ds = (DROPSHADOWFILTER) filter; - sb.append("\t\t\tfcanvas = Filters.dropShadow(fcanvas,fcanvas.getContext(\"2d\"),").append(ds.blurX).append(",").append(ds.blurY).append(",").append((int) (ds.angle * 180 / Math.PI)).append(",").append(ds.distance).append(",").append(jsArrColor(ds.dropShadowColor)).append(",").append(ds.innerShadow ? "true" : "false").append(",").append(ds.passes).append(",").append(ds.strength).append(",").append(ds.knockout ? "true" : "false").append(");\r\n"); - } - if (filter instanceof BEVELFILTER) { - BEVELFILTER bv = (BEVELFILTER) filter; - String type = "Filters.INNER"; - if (bv.onTop && !bv.innerShadow) { - type = "Filters.FULL"; - } else if (!bv.innerShadow) { - type = "Filters.OUTER"; - } - sb.append("\t\t\tfcanvas = Filters.bevel(fcanvas,fcanvas.getContext(\"2d\"),").append(bv.blurX).append(",").append(bv.blurY).append(",").append(bv.strength).append(",").append(type).append(",").append(jsArrColor(bv.highlightColor)).append(",").append(jsArrColor(bv.shadowColor)).append(",").append((int) (bv.angle * 180 / Math.PI)).append(",").append(bv.distance).append(",").append(bv.knockout ? "true" : "false").append(",").append(bv.passes).append(");\r\n"); - } - - if (filter instanceof GRADIENTBEVELFILTER) { - GRADIENTBEVELFILTER gbf = (GRADIENTBEVELFILTER) filter; - String colArr = "["; - String ratArr = "["; - for (int k = 0; k < gbf.gradientColors.length; k++) { - if (k > 0) { - colArr += ","; - ratArr += ","; - } - colArr += jsArrColor(gbf.gradientColors[k]); - ratArr += gbf.gradientRatio[k] / 255f; - } - colArr += "]"; - ratArr += "]"; - String type = "Filters.INNER"; - if (gbf.onTop && !gbf.innerShadow) { - type = "Filters.FULL"; - } else if (!gbf.innerShadow) { - type = "Filters.OUTER"; - } - - sb.append("\t\t\tfcanvas = Filters.gradientBevel(fcanvas,fcanvas.getContext(\"2d\"),").append(colArr).append(",").append(ratArr).append(",").append(gbf.blurX).append(",").append(gbf.blurY).append(",").append(gbf.strength).append(",").append(type).append(",").append((int) (gbf.angle * 180 / Math.PI)).append(",").append(gbf.distance).append(",").append(gbf.knockout ? "true" : "false").append(",").append(gbf.passes).append(");\r\n"); - } - - if (filter instanceof GRADIENTGLOWFILTER) { - GRADIENTGLOWFILTER ggf = (GRADIENTGLOWFILTER) filter; - String colArr = "["; - String ratArr = "["; - for (int k = 0; k < ggf.gradientColors.length; k++) { - if (k > 0) { - colArr += ","; - ratArr += ","; - } - colArr += jsArrColor(ggf.gradientColors[k]); - ratArr += ggf.gradientRatio[k] / 255f; - } - colArr += "]"; - ratArr += "]"; - String type = "Filters.INNER"; - if (ggf.onTop && !ggf.innerShadow) { - type = "Filters.FULL"; - } else if (!ggf.innerShadow) { - type = "Filters.OUTER"; - } - - sb.append("\t\t\tfcanvas = Filters.gradientGlow(fcanvas,fcanvas.getContext(\"2d\"),").append(ggf.blurX).append(",").append(ggf.blurY).append(",").append((int) (ggf.angle * 180 / Math.PI)).append(",").append(ggf.distance).append(",").append(colArr).append(",").append(ratArr).append(",").append(type).append(",").append(ggf.passes).append(",").append(ggf.strength).append(",").append(ggf.knockout ? "true" : "false").append(");\r\n"); - } - } - sb.append("\t\t\tctx = oldctx;\r\n"); - sb.append("\t\t\tvar ms=ctx._matrix;\r\n"); - sb.append("\t\t\tctx.setTransform(1,0,0,1,0,0);\r\n"); - sb.append("\t\t\tctx.drawImage(fcanvas,0,0);\r\n"); - sb.append("\t\t\tctx.applyTransforms(ms);\r\n"); - } - - if (layer.clipDepth != -1) { - sb.append("\t\t\tclips[clips.length-1].clipCanvas = canvas;\r\n"); - sb.append("\t\t\tcanvas = createCanvas(canvas.width,canvas.height);\r\n"); - sb.append("\t\t\tvar nctx = canvas.getContext(\"2d\");\r\n"); - sb.append("\t\t\tenhanceContext(nctx);\r\n"); - sb.append("\t\t\tnctx.applyTransforms(ctx._matrix);\r\n"); - sb.append("\t\t\tctx = nctx;\r\n"); - } - } - sb.append("\t\t\tbreak;\r\n"); - } - sb.append("\t}\r\n"); - return sb.toString(); - } -} +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.exporters; + +import com.jpacker.JPacker; +import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; +import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.RetryTask; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.exporters.modes.FramesExportMode; +import com.jpexs.decompiler.flash.exporters.settings.ButtonExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.FramesExportSettings; +import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; +import com.jpexs.decompiler.flash.helpers.BMPFile; +import com.jpexs.decompiler.flash.helpers.ImageHelper; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.timeline.Timeline; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.filters.BEVELFILTER; +import com.jpexs.decompiler.flash.types.filters.COLORMATRIXFILTER; +import com.jpexs.decompiler.flash.types.filters.CONVOLUTIONFILTER; +import com.jpexs.decompiler.flash.types.filters.DROPSHADOWFILTER; +import com.jpexs.decompiler.flash.types.filters.FILTER; +import com.jpexs.decompiler.flash.types.filters.GLOWFILTER; +import com.jpexs.decompiler.flash.types.filters.GRADIENTBEVELFILTER; +import com.jpexs.decompiler.flash.types.filters.GRADIENTGLOWFILTER; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.Path; +import com.jpexs.helpers.utf8.Utf8Helper; +import gnu.jpdf.PDFJob; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.awt.print.PageFormat; +import java.awt.print.Paper; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.Stack; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.imageio.stream.FileImageOutputStream; +import javax.imageio.stream.ImageOutputStream; +import net.kroo.elliot.GifSequenceWriter; +import net.weiner.kevin.AnimatedGifEncoder; +import org.monte.media.VideoFormatKeys; +import org.monte.media.avi.AVIWriter; + +/** + * + * @author JPEXS + */ +public class FrameExporter { + + private static final Logger logger = Logger.getLogger(FrameExporter.class.getName()); + + public List exportFrames(AbortRetryIgnoreHandler handler, String outdir, SWF swf, int containerId, List frames, ButtonExportSettings settings, EventListener evl) throws IOException { + FramesExportMode fem; + switch (settings.mode) { + case BMP: + fem = FramesExportMode.BMP; + break; + case PNG: + fem = FramesExportMode.PNG; + break; + case SVG: + fem = FramesExportMode.SVG; + break; + default: + throw new Error("Unsupported button export mode"); + } + + FramesExportSettings fes = new FramesExportSettings(fem, settings.zoom); + return exportFrames(handler, outdir, swf, containerId, frames, fes, evl); + } + + public List exportFrames(AbortRetryIgnoreHandler handler, String outdir, final SWF swf, int containerId, List frames, final FramesExportSettings settings, final EventListener evl) throws IOException { + final List ret = new ArrayList<>(); + if (swf.tags.isEmpty()) { + return ret; + } + Timeline tim0; + String path = ""; + if (containerId == 0) { + tim0 = swf.getTimeline(); + } else { + tim0 = ((Timelined) swf.getCharacter(containerId)).getTimeline(); + path = File.separator + Helper.makeFileName(swf.getCharacter(containerId).getExportFileName()); + } + + final Timeline tim = tim0; + + if (frames == null) { + int frameCnt = tim.getFrameCount(); + frames = new ArrayList<>(); + for (int i = 0; i < frameCnt; i++) { + frames.add(i); + } + } + + final File foutdir = new File(outdir + path); + Path.createDirectorySafe(foutdir); + + final List fframes = frames; + + Color backgroundColor = null; + if (settings.mode == FramesExportMode.AVI) { + for (Tag t : swf.tags) { + if (t instanceof SetBackgroundColorTag) { + SetBackgroundColorTag sb = (SetBackgroundColorTag) t; + backgroundColor = sb.backgroundColor.toColor(); + } + } + } + + if (settings.mode == FramesExportMode.SVG) { + for (int i = 0; i < frames.size(); i++) { + if (evl != null) { + evl.handleExportingEvent("frame", i + 1, frames.size(), tim.parentTag == null ? "" : tim.parentTag.getName()); + } + + final int fi = i; + final Color fbackgroundColor = backgroundColor; + new RetryTask(() -> { + int frame = fframes.get(fi); + File f = new File(foutdir + File.separator + frame + ".svg"); + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(f))) { + ExportRectangle rect = new ExportRectangle(tim.displayRect); + rect.xMax *= settings.zoom; + rect.yMax *= settings.zoom; + rect.xMin *= settings.zoom; + rect.yMin *= settings.zoom; + SVGExporter exporter = new SVGExporter(rect); + if (fbackgroundColor != null) { + exporter.setBackGroundColor(fbackgroundColor); + } + SWF.frameToSvg(tim, frame, 0, null, 0, exporter, new ColorTransform(), 0, settings.zoom); + fos.write(Utf8Helper.getBytes(exporter.getSVG())); + } + ret.add(f); + }, handler).run(); + + if (evl != null) { + evl.handleExportedEvent("frame", i + 1, frames.size(), tim.parentTag == null ? "" : tim.parentTag.getName()); + } + } + + return ret; + } + + if (settings.mode == FramesExportMode.CANVAS) { + if (evl != null) { + evl.handleExportingEvent("canvas", 1, 1, tim.parentTag == null ? "" : tim.parentTag.getName()); + } + + final Timeline ftim = tim; + final Color fbackgroundColor = backgroundColor; + final SWF fswf = swf; + new RetryTask(() -> { + File fcanvas = new File(foutdir + File.separator + "canvas.js"); + Helper.saveStream(SWF.class.getClassLoader().getResourceAsStream("com/jpexs/helpers/resource/canvas.js"), fcanvas); + ret.add(fcanvas); + + File f = new File(foutdir + File.separator + "frames.js"); + File fmin = new File(foutdir + File.separator + "frames.min.js"); + int width = (int) (ftim.displayRect.getWidth() * settings.zoom / SWF.unitDivisor); + int height = (int) (ftim.displayRect.getHeight() * settings.zoom / SWF.unitDivisor); + try (final OutputStream fos = new BufferedOutputStream(new FileOutputStream(f))) { + fos.write(Utf8Helper.getBytes("\r\n")); + Set library = new HashSet<>(); + ftim.getNeededCharacters(fframes, library); + + SWF.writeLibrary(fswf, library, fos); + + String currentName = ftim.id == 0 ? "main" : SWF.getTypePrefix(fswf.getCharacter(ftim.id)) + ftim.id; + + fos.write(Utf8Helper.getBytes("function " + currentName + "(ctx,ctrans,frame,ratio,time){\r\n")); + fos.write(Utf8Helper.getBytes("\tctx.save();\r\n")); + fos.write(Utf8Helper.getBytes("\tctx.transform(1,0,0,1," + (-ftim.displayRect.Xmin * settings.zoom / SWF.unitDivisor) + "," + (-ftim.displayRect.Ymin * settings.zoom / SWF.unitDivisor) + ");\r\n")); + fos.write(Utf8Helper.getBytes(framesToHtmlCanvas(SWF.unitDivisor / settings.zoom, ftim, fframes, 0, null, 0, ftim.displayRect, new ColorTransform(), fbackgroundColor))); + fos.write(Utf8Helper.getBytes("\tctx.restore();\r\n")); + fos.write(Utf8Helper.getBytes("}\r\n\r\n")); + + fos.write(Utf8Helper.getBytes("var frame = -1;\r\n")); + fos.write(Utf8Helper.getBytes("var time = 0;\r\n")); + fos.write(Utf8Helper.getBytes("var frames = [];\r\n")); + for (int i : fframes) { + fos.write(Utf8Helper.getBytes("frames.push(" + i + ");\r\n")); + } + fos.write(Utf8Helper.getBytes("\r\n")); + RGB backgroundColor1 = new RGB(255, 255, 255); + for (Tag t : fswf.tags) { + if (t instanceof SetBackgroundColorTag) { + SetBackgroundColorTag sb = (SetBackgroundColorTag) t; + backgroundColor1 = sb.backgroundColor; + } + } + + fos.write(Utf8Helper.getBytes("var backgroundColor = \"" + backgroundColor1.toHexRGB() + "\";\r\n")); + fos.write(Utf8Helper.getBytes("var originalWidth = " + width + ";\r\n")); + fos.write(Utf8Helper.getBytes("var originalHeight= " + height + ";\r\n")); + fos.write(Utf8Helper.getBytes("function nextFrame(ctx,ctrans){\r\n")); + fos.write(Utf8Helper.getBytes("\tvar oldframe = frame;\r\n")); + fos.write(Utf8Helper.getBytes("\tframe = (frame+1)%frames.length;\r\n")); + fos.write(Utf8Helper.getBytes("\tif(frame==oldframe){time++;}else{time=0;};\r\n")); + fos.write(Utf8Helper.getBytes("\tdrawFrame();\r\n")); + fos.write(Utf8Helper.getBytes("}\r\n\r\n")); + + fos.write(Utf8Helper.getBytes("function drawFrame(){\r\n")); + fos.write(Utf8Helper.getBytes("\tctx.fillStyle = backgroundColor;\r\n")); + fos.write(Utf8Helper.getBytes("\tctx.fillRect(0,0,canvas.width,canvas.height);\r\n")); + fos.write(Utf8Helper.getBytes("\tctx.save();\r\n")); + fos.write(Utf8Helper.getBytes("\tctx.transform(canvas.width/originalWidth,0,0,canvas.height/originalHeight,0,0);\r\n")); + fos.write(Utf8Helper.getBytes("\t" + currentName + "(ctx,ctrans,frames[frame],0,time);\r\n")); + fos.write(Utf8Helper.getBytes("\tctx.restore();\r\n")); + fos.write(Utf8Helper.getBytes("}\r\n\r\n")); + if (ftim.swf.frameRate > 0) { + fos.write(Utf8Helper.getBytes("window.setInterval(function(){nextFrame(ctx,ctrans);}," + (int) (1000.0 / ftim.swf.frameRate) + ");\r\n")); + } + fos.write(Utf8Helper.getBytes("nextFrame(ctx,ctrans);\r\n")); + } + + boolean packed = false; + if (Configuration.packJavaScripts.get()) { + try { + JPacker.main(new String[]{"-q", "-b", "62", "-o", fmin.getAbsolutePath(), f.getAbsolutePath()}); + f.delete(); + packed = true; + } catch (Exception | Error e) { // Something wrong in the packer + logger.log(Level.WARNING, "JPacker: Cannot minimize script"); + f.renameTo(fmin); + } + } else { + f.renameTo(fmin); + } + + File fh = new File(foutdir + File.separator + "frames.html"); + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(fh)); FileInputStream fis = new FileInputStream(fmin)) { + fos.write(Utf8Helper.getBytes(CanvasShapeExporter.getHtmlPrefix(width, height))); + fos.write(Utf8Helper.getBytes(CanvasShapeExporter.getJsPrefix())); + byte[] buf = new byte[1000]; + int cnt; + while ((cnt = fis.read(buf)) > 0) { + fos.write(buf, 0, cnt); + } + if (packed) { + fos.write(Utf8Helper.getBytes(";")); + } + fos.write(Utf8Helper.getBytes(CanvasShapeExporter.getJsSuffix())); + fos.write(Utf8Helper.getBytes(CanvasShapeExporter.getHtmlSuffix())); + } + + fmin.delete(); + + ret.add(f); + }, handler).run(); + + if (evl != null) { + evl.handleExportedEvent("canvas", 1, 1, tim.parentTag == null ? "" : tim.parentTag.getName()); + } + return ret; + } + + final Timeline ftim = tim; + final Color fbackgroundColor = backgroundColor; + final Iterator frameImages = new Iterator() { + + private int pos = 0; + + @Override + public boolean hasNext() { + return fframes.size() > pos; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + @Override + public BufferedImage next() { + if (!hasNext()) { + return null; + } + + if (evl != null) { + evl.handleExportingEvent("frame", pos + 1, fframes.size(), tim.parentTag == null ? "" : tim.parentTag.getName()); + } + + BufferedImage result = SWF.frameToImageGet(ftim, fframes.get(pos++), 0, null, 0, ftim.displayRect, new Matrix(), new ColorTransform(), fbackgroundColor, false, settings.zoom).getBufferedImage(); + + if (evl != null) { + evl.handleExportedEvent("frame", pos, fframes.size(), tim.parentTag == null ? "" : tim.parentTag.getName()); + } + + return result; + } + }; + + switch (settings.mode) { + case GIF: + new RetryTask(() -> { + File f = new File(foutdir + File.separator + "frames.gif"); + makeGIF(frameImages, swf.frameRate, f, evl); + ret.add(f); + }, handler).run(); + break; + case BMP: + for (int i = 0; frameImages.hasNext(); i++) { + final int fi = i; + new RetryTask(() -> { + File f = new File(foutdir + File.separator + (fframes.get(fi) + 1) + ".bmp"); + BMPFile.saveBitmap(frameImages.next(), f); + ret.add(f); + }, handler).run(); + } + break; + case PNG: + for (int i = 0; frameImages.hasNext(); i++) { + final int fi = i; + new RetryTask(() -> { + File file = new File(foutdir + File.separator + (fframes.get(fi) + 1) + ".png"); + ImageHelper.write(frameImages.next(), "PNG", file); + ret.add(file); + }, handler).run(); + } + + //ShapeExporterBase.clearCache(); + break; + case PDF: + if (frameImages.hasNext()) { + new RetryTask(() -> { + File f = new File(foutdir + File.separator + "frames.pdf"); + PDFJob job = new PDFJob(new BufferedOutputStream(new FileOutputStream(f))); + PageFormat pf = new PageFormat(); + pf.setOrientation(PageFormat.PORTRAIT); + Paper p = new Paper(); + BufferedImage img0 = frameImages.next(); + p.setSize(img0.getWidth() + 10, img0.getHeight() + 10); + pf.setPaper(p); + + for (int i = 0; frameImages.hasNext(); i++) { + BufferedImage img = frameImages.next(); + Graphics g = job.getGraphics(pf); + g.drawImage(img, 5, 5, img.getWidth(), img.getHeight(), null); + g.dispose(); + } + + job.end(); + ret.add(f); + }, handler).run(); + } + break; + case AVI: + new RetryTask(() -> { + File f = new File(foutdir + File.separator + "frames.avi"); + makeAVI(frameImages, swf.frameRate, f, evl); + ret.add(f); + }, handler).run(); + break; + } + + return ret; + } + + private static String jsArrColor(RGB rgb) { + return "[" + rgb.red + "," + rgb.green + "," + rgb.blue + "," + ((rgb instanceof RGBA) ? ((RGBA) rgb).getAlphaFloat() : 1) + "]"; + } + + public static void makeAVI(Iterator images, int frameRate, File file, EventListener evl) throws IOException { + if (!images.hasNext()) { + return; + } + + AVIWriter out = new AVIWriter(file); + BufferedImage img0 = images.next(); + out.addVideoTrack(VideoFormatKeys.ENCODING_AVI_PNG, 1, frameRate, img0.getWidth(), img0.getHeight(), 0, 0); + try { + out.write(0, img0, 1); + while (images.hasNext()) { + out.write(0, images.next(), 1); + } + } finally { + out.close(); + } + } + + public static void makeGIF(Iterator images, int frameRate, File file, EventListener evl) throws IOException { + if (!images.hasNext()) { + return; + } + + AnimatedGifEncoder encoder = new AnimatedGifEncoder(); + encoder.setRepeat(0); // repeat forever + encoder.start(file.getAbsolutePath()); + encoder.setDelay(1000 / frameRate); + while (images.hasNext()) { + encoder.addFrame(images.next()); + } + + encoder.finish(); + } + + public static void makeGIFOld(Iterator images, int frameRate, File file, EventListener evl) throws IOException { + if (!images.hasNext()) { + return; + } + + try (ImageOutputStream output = new FileImageOutputStream(file)) { + BufferedImage img0 = images.next(); + GifSequenceWriter writer = new GifSequenceWriter(output, img0.getType(), 1000 / frameRate, true); + writer.writeToSequence(img0); + + while (images.hasNext()) { + writer.writeToSequence(images.next()); + } + + writer.close(); + } + } + + public static String framesToHtmlCanvas(double unitDivisor, Timeline timeline, List frames, int time, DepthState stateUnderCursor, int mouseButton, RECT displayRect, ColorTransform colorTransform, Color backGroundColor) { + StringBuilder sb = new StringBuilder(); + if (frames == null) { + frames = new ArrayList<>(); + for (int i = 0; i < timeline.getFrameCount(); i++) { + frames.add(i); + } + } + + sb.append("\tvar clips = [];\r\n"); + sb.append("\tvar frame_cnt = ").append(timeline.getFrameCount()).append(";\r\n"); + sb.append("\tframe = frame % frame_cnt;\r\n"); + sb.append("\tswitch(frame){\r\n"); + int maxDepth = timeline.getMaxDepth(); + Stack clipDepths = new Stack<>(); + for (int frame : frames) { + sb.append("\t\tcase ").append(frame).append(":\r\n"); + Frame frameObj = timeline.getFrame(frame); + for (int i = 1; i <= maxDepth + 1; i++) { + while (!clipDepths.isEmpty() && clipDepths.peek() <= i) { + clipDepths.pop(); + sb.append("\t\t\tvar o = clips.pop();\r\n"); + sb.append("\t\t\tctx.globalCompositeOperation = \"destination-in\";\r\n"); + sb.append("\t\t\tctx.setTransform(1,0,0,1,0,0);\r\n"); + sb.append("\t\t\tctx.drawImage(o.clipCanvas,0,0);\r\n"); + sb.append("\t\t\tvar ms=o.ctx._matrix;\r\n"); + sb.append("\t\t\to.ctx.setTransform(1,0,0,1,0,0);\r\n"); + sb.append("\t\t\to.ctx.globalCompositeOperation = \"source-over\";\r\n"); + sb.append("\t\t\to.ctx.drawImage(canvas,0,0);\r\n"); + sb.append("\t\t\to.ctx.applyTransforms(ms);\r\n"); + sb.append("\t\t\tctx = o.ctx;\r\n"); + sb.append("\t\t\tcanvas = o.canvas;\r\n"); + } + if (!frameObj.layers.containsKey(i)) { + continue; + } + DepthState layer = frameObj.layers.get(i); + if (!timeline.swf.getCharacters().containsKey(layer.characterId)) { + continue; + } + if (!layer.isVisible) { + continue; + } + + CharacterTag character = timeline.swf.getCharacter(layer.characterId); + if (colorTransform == null) { + colorTransform = new ColorTransform(); + } + + Matrix placeMatrix = new Matrix(layer.matrix); + placeMatrix.scaleX /= unitDivisor; + placeMatrix.scaleY /= unitDivisor; + placeMatrix.rotateSkew0 /= unitDivisor; + placeMatrix.rotateSkew1 /= unitDivisor; + placeMatrix.translateX /= unitDivisor; + placeMatrix.translateY /= unitDivisor; + + int f = 0; + String fstr = "0"; + if (character instanceof DefineSpriteTag) { + DefineSpriteTag sp = (DefineSpriteTag) character; + Timeline tim = sp.getTimeline(); + if (tim.getFrameCount() > 0) { + f = layer.time % tim.getFrameCount(); + fstr = "(" + f + "+time)%" + tim.getFrameCount(); + } + } + + if (layer.clipDepth != -1) { + clipDepths.push(layer.clipDepth); + sb.append("\t\t\tclips.push({ctx:ctx,canvas:canvas});\r\n"); + sb.append("\t\t\tvar ccanvas = createCanvas(canvas.width,canvas.height);\r\n"); + sb.append("\t\t\tvar cctx = ccanvas.getContext(\"2d\");\r\n"); + sb.append("\t\t\tenhanceContext(cctx);\r\n"); + sb.append("\t\t\tcctx.applyTransforms(ctx._matrix);\r\n"); + sb.append("\t\t\tcanvas = ccanvas;\r\n"); + sb.append("\t\t\tctx = cctx;\r\n"); + } + + if (layer.filters != null && layer.filters.size() > 0) { + sb.append("\t\t\tvar oldctx = ctx;\r\n"); + sb.append("\t\t\tvar fcanvas = createCanvas(canvas.width,canvas.height);"); + sb.append("\t\t\tvar fctx = fcanvas.getContext(\"2d\");\r\n"); + sb.append("\t\t\tenhanceContext(fctx);\r\n"); + sb.append("\t\t\tfctx.applyTransforms(ctx._matrix);\r\n"); + sb.append("\t\t\tctx = fctx;\r\n"); + } + + ColorTransform ctrans = layer.colorTransForm; + String ctrans_str = "ctrans"; + if (ctrans == null) { + ctrans = new ColorTransform(); + } else { + ctrans_str = "ctrans.merge(new cxform(" + + ctrans.getRedAdd() + "," + ctrans.getGreenAdd() + "," + ctrans.getBlueAdd() + "," + ctrans.getAlphaAdd() + "," + + ctrans.getRedMulti() + "," + ctrans.getGreenMulti() + "," + ctrans.getBlueMulti() + "," + ctrans.getAlphaMulti() + + "))"; + } + sb.append("\t\t\tplace(\"").append(SWF.getTypePrefix(character)).append(layer.characterId).append("\",canvas,ctx,[").append(placeMatrix.scaleX).append(",") + .append(placeMatrix.rotateSkew0).append(",") + .append(placeMatrix.rotateSkew1).append(",") + .append(placeMatrix.scaleY).append(",") + .append(placeMatrix.translateX).append(",") + .append(placeMatrix.translateY).append("],").append(ctrans_str).append(",").append("").append(layer.blendMode < 1 ? 1 : layer.blendMode).append(",").append(fstr).append(",").append(layer.ratio < 0 ? 0 : layer.ratio).append(",time").append(");\r\n"); + + if (layer.filters != null && layer.filters.size() > 0) { + for (FILTER filter : layer.filters) { + if (filter instanceof COLORMATRIXFILTER) { + COLORMATRIXFILTER cmf = (COLORMATRIXFILTER) filter; + String mat = "["; + for (int k = 0; k < cmf.matrix.length; k++) { + if (k > 0) { + mat += ","; + } + mat += cmf.matrix[k]; + } + mat += "]"; + sb.append("\t\t\tfcanvas = Filters.colorMatrix(fcanvas,fcanvas.getContext(\"2d\"),").append(mat).append(");\r\n"); + } + + if (filter instanceof CONVOLUTIONFILTER) { + CONVOLUTIONFILTER cf = (CONVOLUTIONFILTER) filter; + int height = cf.matrix.length; + int width = cf.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] = cf.matrix[x][y] * cf.divisor + cf.bias; + } + } + String mat = "["; + for (int k = 0; k < matrix2.length; k++) { + if (k > 0) { + mat += ","; + } + mat += matrix2[k]; + } + mat += "]"; + sb.append("\t\t\tfcanvas = Filters.convolution(fcanvas,fcanvas.getContext(\"2d\"),").append(mat).append(",false);\r\n"); + } + + if (filter instanceof GLOWFILTER) { + GLOWFILTER gf = (GLOWFILTER) filter; + sb.append("\t\t\tfcanvas = Filters.glow(fcanvas,fcanvas.getContext(\"2d\"),").append(gf.blurX).append(",").append(gf.blurY).append(",").append(gf.strength).append(",").append(jsArrColor(gf.glowColor)).append(",").append(gf.innerGlow ? "true" : "false").append(",").append(gf.knockout ? "true" : "false").append(",").append(gf.passes).append(");\r\n"); + } + + if (filter instanceof DROPSHADOWFILTER) { + DROPSHADOWFILTER ds = (DROPSHADOWFILTER) filter; + sb.append("\t\t\tfcanvas = Filters.dropShadow(fcanvas,fcanvas.getContext(\"2d\"),").append(ds.blurX).append(",").append(ds.blurY).append(",").append((int) (ds.angle * 180 / Math.PI)).append(",").append(ds.distance).append(",").append(jsArrColor(ds.dropShadowColor)).append(",").append(ds.innerShadow ? "true" : "false").append(",").append(ds.passes).append(",").append(ds.strength).append(",").append(ds.knockout ? "true" : "false").append(");\r\n"); + } + if (filter instanceof BEVELFILTER) { + BEVELFILTER bv = (BEVELFILTER) filter; + String type = "Filters.INNER"; + if (bv.onTop && !bv.innerShadow) { + type = "Filters.FULL"; + } else if (!bv.innerShadow) { + type = "Filters.OUTER"; + } + sb.append("\t\t\tfcanvas = Filters.bevel(fcanvas,fcanvas.getContext(\"2d\"),").append(bv.blurX).append(",").append(bv.blurY).append(",").append(bv.strength).append(",").append(type).append(",").append(jsArrColor(bv.highlightColor)).append(",").append(jsArrColor(bv.shadowColor)).append(",").append((int) (bv.angle * 180 / Math.PI)).append(",").append(bv.distance).append(",").append(bv.knockout ? "true" : "false").append(",").append(bv.passes).append(");\r\n"); + } + + if (filter instanceof GRADIENTBEVELFILTER) { + GRADIENTBEVELFILTER gbf = (GRADIENTBEVELFILTER) filter; + String colArr = "["; + String ratArr = "["; + for (int k = 0; k < gbf.gradientColors.length; k++) { + if (k > 0) { + colArr += ","; + ratArr += ","; + } + colArr += jsArrColor(gbf.gradientColors[k]); + ratArr += gbf.gradientRatio[k] / 255f; + } + colArr += "]"; + ratArr += "]"; + String type = "Filters.INNER"; + if (gbf.onTop && !gbf.innerShadow) { + type = "Filters.FULL"; + } else if (!gbf.innerShadow) { + type = "Filters.OUTER"; + } + + sb.append("\t\t\tfcanvas = Filters.gradientBevel(fcanvas,fcanvas.getContext(\"2d\"),").append(colArr).append(",").append(ratArr).append(",").append(gbf.blurX).append(",").append(gbf.blurY).append(",").append(gbf.strength).append(",").append(type).append(",").append((int) (gbf.angle * 180 / Math.PI)).append(",").append(gbf.distance).append(",").append(gbf.knockout ? "true" : "false").append(",").append(gbf.passes).append(");\r\n"); + } + + if (filter instanceof GRADIENTGLOWFILTER) { + GRADIENTGLOWFILTER ggf = (GRADIENTGLOWFILTER) filter; + String colArr = "["; + String ratArr = "["; + for (int k = 0; k < ggf.gradientColors.length; k++) { + if (k > 0) { + colArr += ","; + ratArr += ","; + } + colArr += jsArrColor(ggf.gradientColors[k]); + ratArr += ggf.gradientRatio[k] / 255f; + } + colArr += "]"; + ratArr += "]"; + String type = "Filters.INNER"; + if (ggf.onTop && !ggf.innerShadow) { + type = "Filters.FULL"; + } else if (!ggf.innerShadow) { + type = "Filters.OUTER"; + } + + sb.append("\t\t\tfcanvas = Filters.gradientGlow(fcanvas,fcanvas.getContext(\"2d\"),").append(ggf.blurX).append(",").append(ggf.blurY).append(",").append((int) (ggf.angle * 180 / Math.PI)).append(",").append(ggf.distance).append(",").append(colArr).append(",").append(ratArr).append(",").append(type).append(",").append(ggf.passes).append(",").append(ggf.strength).append(",").append(ggf.knockout ? "true" : "false").append(");\r\n"); + } + } + sb.append("\t\t\tctx = oldctx;\r\n"); + sb.append("\t\t\tvar ms=ctx._matrix;\r\n"); + sb.append("\t\t\tctx.setTransform(1,0,0,1,0,0);\r\n"); + sb.append("\t\t\tctx.drawImage(fcanvas,0,0);\r\n"); + sb.append("\t\t\tctx.applyTransforms(ms);\r\n"); + } + + if (layer.clipDepth != -1) { + sb.append("\t\t\tclips[clips.length-1].clipCanvas = canvas;\r\n"); + sb.append("\t\t\tcanvas = createCanvas(canvas.width,canvas.height);\r\n"); + sb.append("\t\t\tvar nctx = canvas.getContext(\"2d\");\r\n"); + sb.append("\t\t\tenhanceContext(nctx);\r\n"); + sb.append("\t\t\tnctx.applyTransforms(ctx._matrix);\r\n"); + sb.append("\t\t\tctx = nctx;\r\n"); + } + } + sb.append("\t\t\tbreak;\r\n"); + } + sb.append("\t}\r\n"); + return sb.toString(); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/BMPFile.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/BMPFile.java index c786ba505..a89d00ecf 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/BMPFile.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/BMPFile.java @@ -1,246 +1,247 @@ -/* - * Copyright (C) 2010-2015 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.decompiler.flash.helpers; - -import java.awt.Component; -import java.awt.Image; -import java.awt.image.PixelGrabber; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -/** - * Adapted from - * http://www.javaworld.com/article/2077561/learn-java/java-tip-60--saving-bitmap-files-in-java.html - */ -public class BMPFile extends Component { - - //--- Private constants - private final static int BITMAPFILEHEADER_SIZE = 14; - - private final static int BITMAPINFOHEADER_SIZE = 40; - - //--- Private variable declaration - //--- Bitmap file header - private final byte[] bitmapFileHeader = new byte[14]; - - private final byte[] bfType = {'B', 'M'}; - - private int bfSize = 0; - - private final int bfReserved1 = 0; - - private final int bfReserved2 = 0; - - private final int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; - - //--- Bitmap info header - private final byte[] bitmapInfoHeader = new byte[40]; - - private final int biSize = BITMAPINFOHEADER_SIZE; - - private int biWidth = 0; - - private int biHeight = 0; - - private final int biPlanes = 1; - - private final int biBitCount = 24; - - private final int biCompression = 0; - - private int biSizeImage = 0x030000; - - private final int biXPelsPerMeter = 0x0; - - private final int biYPelsPerMeter = 0x0; - - private final int biClrUsed = 0; - - private final int biClrImportant = 0; - - //--- Bitmap raw data - private int[] bitmap; - - //--- File section - private OutputStream fo; - - //--- Private constructor - private BMPFile() { - } - - public static void saveBitmap(Image image, File file) throws IOException { - BMPFile b = new BMPFile(); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(file))) { - b.fo = fos; - b.save(image, image.getWidth(null), image.getHeight(null)); - } - } - /* - * The saveMethod is the main method of the process. This method - * will call the convertImage method to convert the memory image to - * a byte array; method writeBitmapFileHeader creates and writes - * the bitmap file header; writeBitmapInfoHeader creates the - * information header; and writeBitmap writes the image. - * - */ - - private void save(Image parImage, int parWidth, int parHeight) throws IOException { - convertImage(parImage, parWidth, parHeight); - writeBitmapFileHeader(); - writeBitmapInfoHeader(); - writeBitmap(); - - } - /* - * convertImage converts the memory image to the bitmap format (BRG). - * It also computes some information for the bitmap info header. - * - */ - - private boolean convertImage(Image parImage, int parWidth, int parHeight) { - int pad; - bitmap = new int[parWidth * parHeight]; - PixelGrabber pg = new PixelGrabber(parImage, 0, 0, parWidth, parHeight, - bitmap, 0, parWidth); - try { - pg.grabPixels(); - } catch (InterruptedException e) { - return (false); - } - pad = (4 - ((parWidth * 3) % 4)) * parHeight; - biSizeImage = ((parWidth * parHeight) * 3) + pad; - bfSize = biSizeImage + BITMAPFILEHEADER_SIZE - + BITMAPINFOHEADER_SIZE; - biWidth = parWidth; - biHeight = parHeight; - return (true); - } - /* - * writeBitmap converts the image returned from the pixel grabber to - * the format required. Remember: scan lines are inverted in - * a bitmap file! - * - * Each scan line must be padded to an even 4-byte boundary. - */ - - private void writeBitmap() throws IOException { - int size; - int value; - int j; - int i; - int rowCount; - int rowIndex; - int lastRowIndex; - int pad; - int padCount; - byte[] rgb = new byte[3]; - size = (biWidth * biHeight); - pad = (biWidth * 3) % 4; - rowCount = 1; - padCount = 0; - rowIndex = size - biWidth; - lastRowIndex = rowIndex; - for (j = 0; j < size; j++) { - value = bitmap[rowIndex]; - rgb[0] = (byte) (value & 0xFF); - rgb[1] = (byte) ((value >> 8) & 0xFF); - rgb[2] = (byte) ((value >> 16) & 0xFF); - fo.write(rgb); - if (rowCount == biWidth) { - padCount += pad; - for (i = 1; i <= pad; i++) { - fo.write(0x00); - } - rowCount = 1; - rowIndex = lastRowIndex - biWidth; - lastRowIndex = rowIndex; - } else { - rowCount++; - } - rowIndex++; - } - //--- Update the size of the file - bfSize += padCount - pad; - biSizeImage += padCount - pad; - } - /* - * writeBitmapFileHeader writes the bitmap file header to the file. - * - */ - - private void writeBitmapFileHeader() throws IOException { - - fo.write(bfType); - fo.write(intToDWord(bfSize)); - fo.write(intToWord(bfReserved1)); - fo.write(intToWord(bfReserved2)); - fo.write(intToDWord(bfOffBits)); - } - /* - * - * writeBitmapInfoHeader writes the bitmap information header - * to the file. - * - */ - - private void writeBitmapInfoHeader() throws IOException { - - fo.write(intToDWord(biSize)); - fo.write(intToDWord(biWidth)); - fo.write(intToDWord(biHeight)); - fo.write(intToWord(biPlanes)); - fo.write(intToWord(biBitCount)); - fo.write(intToDWord(biCompression)); - fo.write(intToDWord(biSizeImage)); - fo.write(intToDWord(biXPelsPerMeter)); - fo.write(intToDWord(biYPelsPerMeter)); - fo.write(intToDWord(biClrUsed)); - fo.write(intToDWord(biClrImportant)); - - } - /* - * - * intToWord converts an int to a word, where the return - * value is stored in a 2-byte array. - * - */ - - private byte[] intToWord(int parValue) { - byte[] retValue = new byte[2]; - retValue[0] = (byte) (parValue & 0x00FF); - retValue[1] = (byte) ((parValue >> 8) & 0x00FF); - return (retValue); - } - /* - * - * intToDWord converts an int to a double word, where the return - * value is stored in a 4-byte array. - * - */ - - private byte[] intToDWord(int parValue) { - byte[] retValue = new byte[4]; - retValue[0] = (byte) (parValue & 0x00FF); - retValue[1] = (byte) ((parValue >> 8) & 0x000000FF); - retValue[2] = (byte) ((parValue >> 16) & 0x000000FF); - retValue[3] = (byte) ((parValue >> 24) & 0x000000FF); - return (retValue); - } -} +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.helpers; + +import java.awt.Component; +import java.awt.Image; +import java.awt.image.PixelGrabber; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * Adapted from + * http://www.javaworld.com/article/2077561/learn-java/java-tip-60--saving-bitmap-files-in-java.html + */ +public class BMPFile extends Component { + + //--- Private constants + private final static int BITMAPFILEHEADER_SIZE = 14; + + private final static int BITMAPINFOHEADER_SIZE = 40; + + //--- Private variable declaration + //--- Bitmap file header + private final byte[] bitmapFileHeader = new byte[14]; + + private final byte[] bfType = {'B', 'M'}; + + private int bfSize = 0; + + private final int bfReserved1 = 0; + + private final int bfReserved2 = 0; + + private final int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; + + //--- Bitmap info header + private final byte[] bitmapInfoHeader = new byte[40]; + + private final int biSize = BITMAPINFOHEADER_SIZE; + + private int biWidth = 0; + + private int biHeight = 0; + + private final int biPlanes = 1; + + private final int biBitCount = 24; + + private final int biCompression = 0; + + private int biSizeImage = 0x030000; + + private final int biXPelsPerMeter = 0x0; + + private final int biYPelsPerMeter = 0x0; + + private final int biClrUsed = 0; + + private final int biClrImportant = 0; + + //--- Bitmap raw data + private int[] bitmap; + + //--- File section + private OutputStream fo; + + //--- Private constructor + private BMPFile() { + } + + public static void saveBitmap(Image image, File file) throws IOException { + BMPFile b = new BMPFile(); + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(file))) { + b.fo = fos; + b.save(image, image.getWidth(null), image.getHeight(null)); + } + } + /* + * The saveMethod is the main method of the process. This method + * will call the convertImage method to convert the memory image to + * a byte array; method writeBitmapFileHeader creates and writes + * the bitmap file header; writeBitmapInfoHeader creates the + * information header; and writeBitmap writes the image. + * + */ + + private void save(Image parImage, int parWidth, int parHeight) throws IOException { + convertImage(parImage, parWidth, parHeight); + writeBitmapFileHeader(); + writeBitmapInfoHeader(); + writeBitmap(); + + } + /* + * convertImage converts the memory image to the bitmap format (BRG). + * It also computes some information for the bitmap info header. + * + */ + + private boolean convertImage(Image parImage, int parWidth, int parHeight) { + int pad; + bitmap = new int[parWidth * parHeight]; + PixelGrabber pg = new PixelGrabber(parImage, 0, 0, parWidth, parHeight, + bitmap, 0, parWidth); + try { + pg.grabPixels(); + } catch (InterruptedException e) { + return (false); + } + + pad = ((4 - ((parWidth * 3) % 4)) % 4) * parHeight; + biSizeImage = ((parWidth * parHeight) * 3) + pad; + bfSize = biSizeImage + BITMAPFILEHEADER_SIZE + + BITMAPINFOHEADER_SIZE; + biWidth = parWidth; + biHeight = parHeight; + return (true); + } + /* + * writeBitmap converts the image returned from the pixel grabber to + * the format required. Remember: scan lines are inverted in + * a bitmap file! + * + * Each scan line must be padded to an even 4-byte boundary. + */ + + private void writeBitmap() throws IOException { + int size; + int value; + int j; + int i; + int rowCount; + int rowIndex; + int lastRowIndex; + int pad; + int padCount; + byte[] rgb = new byte[3]; + size = biWidth * biHeight; + pad = ((4 - ((biWidth * 3) % 4)) % 4); + rowCount = 1; + padCount = 0; + rowIndex = size - biWidth; + lastRowIndex = rowIndex; + for (j = 0; j < size; j++) { + value = bitmap[rowIndex]; + rgb[0] = (byte) (value & 0xFF); + rgb[1] = (byte) ((value >> 8) & 0xFF); + rgb[2] = (byte) ((value >> 16) & 0xFF); + fo.write(rgb); + if (rowCount == biWidth) { + padCount += pad; + for (i = 1; i <= pad; i++) { + fo.write(0x00); + } + rowCount = 1; + rowIndex = lastRowIndex - biWidth; + lastRowIndex = rowIndex; + } else { + rowCount++; + } + rowIndex++; + } + //--- Update the size of the file + bfSize += padCount - pad; + biSizeImage += padCount - pad; + } + /* + * writeBitmapFileHeader writes the bitmap file header to the file. + * + */ + + private void writeBitmapFileHeader() throws IOException { + + fo.write(bfType); + fo.write(intToDWord(bfSize)); + fo.write(intToWord(bfReserved1)); + fo.write(intToWord(bfReserved2)); + fo.write(intToDWord(bfOffBits)); + } + /* + * + * writeBitmapInfoHeader writes the bitmap information header + * to the file. + * + */ + + private void writeBitmapInfoHeader() throws IOException { + + fo.write(intToDWord(biSize)); + fo.write(intToDWord(biWidth)); + fo.write(intToDWord(biHeight)); + fo.write(intToWord(biPlanes)); + fo.write(intToWord(biBitCount)); + fo.write(intToDWord(biCompression)); + fo.write(intToDWord(biSizeImage)); + fo.write(intToDWord(biXPelsPerMeter)); + fo.write(intToDWord(biYPelsPerMeter)); + fo.write(intToDWord(biClrUsed)); + fo.write(intToDWord(biClrImportant)); + + } + /* + * + * intToWord converts an int to a word, where the return + * value is stored in a 2-byte array. + * + */ + + private byte[] intToWord(int parValue) { + byte[] retValue = new byte[2]; + retValue[0] = (byte) (parValue & 0x00FF); + retValue[1] = (byte) ((parValue >> 8) & 0x00FF); + return (retValue); + } + /* + * + * intToDWord converts an int to a double word, where the return + * value is stored in a 4-byte array. + * + */ + + private byte[] intToDWord(int parValue) { + byte[] retValue = new byte[4]; + retValue[0] = (byte) (parValue & 0x00FF); + retValue[1] = (byte) ((parValue >> 8) & 0x000000FF); + retValue[2] = (byte) ((parValue >> 16) & 0x000000FF); + retValue[3] = (byte) ((parValue >> 24) & 0x000000FF); + return (retValue); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java index 6ce8def44..365b794e7 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java @@ -429,7 +429,7 @@ public abstract class TextTag extends CharacterTag implements DrawableTag { double rat = textHeight / 1024.0 / (font == null ? 1 : font.getDivider()); - Color textColor2 = new Color(textColor); + Color textColor2 = new Color(textColor, true); for (GLYPHENTRY entry : rec.glyphEntries) { Matrix mat = transformation.clone(); mat = mat.concatenate(new Matrix(textMatrix)); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ColorTransform.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ColorTransform.java index 20d7b6f78..a138eb9da 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ColorTransform.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ColorTransform.java @@ -1,22 +1,24 @@ /* * Copyright (C) 2010-2015 JPEXS, All rights reserved. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. - * + * * This library 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 * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.types; import com.jpexs.decompiler.flash.types.filters.Filtering; import com.jpexs.helpers.SerializableImage; +import java.awt.Color; import java.awt.image.RescaleOp; /** @@ -52,6 +54,13 @@ public class ColorTransform implements Cloneable { return new RGBA(Filtering.colorEffect(color.toInt(), getRedAdd(), getGreenAdd(), getBlueAdd(), getAlphaAdd(), getRedMulti(), getGreenMulti(), getBlueMulti(), getAlphaMulti())); } + public Color apply(Color color) { + if (color == null) { + return null; + } + return new Color(Filtering.colorEffect(color.getRGB(), getRedAdd(), getGreenAdd(), getBlueAdd(), getAlphaAdd(), getRedMulti(), getGreenMulti(), getBlueMulti(), getAlphaMulti())); + } + public GRADRECORD[] apply(GRADRECORD[] gradRecords) { GRADRECORD[] ret = new GRADRECORD[gradRecords.length]; for (int i = 0; i < gradRecords.length; i++) { diff --git a/src/com/jpexs/decompiler/flash/gui/GuiAbortRetryIgnoreHandler.java b/src/com/jpexs/decompiler/flash/gui/GuiAbortRetryIgnoreHandler.java index b1f1342e5..04f050471 100644 --- a/src/com/jpexs/decompiler/flash/gui/GuiAbortRetryIgnoreHandler.java +++ b/src/com/jpexs/decompiler/flash/gui/GuiAbortRetryIgnoreHandler.java @@ -41,7 +41,19 @@ public class GuiAbortRetryIgnoreHandler implements AbortRetryIgnoreHandler { return AbortRetryIgnoreHandler.IGNORE; } - int result = View.showOptionDialog(null, AppStrings.translate("error.occured").replace("%error%", thrown.getLocalizedMessage()), AppStrings.translate("error"), JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE, null, options, ""); + String msg = null; + if (thrown != null) { + msg = thrown.getLocalizedMessage(); + if (msg == null) { + msg = thrown.toString(); + } + } + + if (msg == null) { + msg = ""; + } + + int result = View.showOptionDialog(null, AppStrings.translate("error.occured").replace("%error%", msg), AppStrings.translate("error"), JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE, null, options, ""); if (result == AbortRetryIgnoreHandler.IGNORE_ALL) { ignoreAll = true; result = AbortRetryIgnoreHandler.IGNORE; diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index c456c58d3..e62f663fb 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -1222,42 +1222,42 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se EventListener evl = swf.getExportEventListener(); if (export.isOptionEnabled(ImageExportMode.class)) { - new ImageExporter().exportImages(handler, selFile + File.separator + "images", swf.tags, + new ImageExporter().exportImages(handler, Path.combine(selFile, "images"), swf.tags, new ImageExportSettings(export.getValue(ImageExportMode.class)), evl); } if (export.isOptionEnabled(ShapeExportMode.class)) { - new ShapeExporter().exportShapes(handler, selFile + File.separator + "shapes", swf.tags, + new ShapeExporter().exportShapes(handler, Path.combine(selFile, "shapes"), swf.tags, new ShapeExportSettings(export.getValue(ShapeExportMode.class), export.getZoom()), evl); } if (export.isOptionEnabled(MorphShapeExportMode.class)) { - new MorphShapeExporter().exportMorphShapes(handler, selFile + File.separator + "morphshapes", swf.tags, + new MorphShapeExporter().exportMorphShapes(handler, Path.combine(selFile, "morphshapes"), swf.tags, new MorphShapeExportSettings(export.getValue(MorphShapeExportMode.class), export.getZoom()), evl); } if (export.isOptionEnabled(TextExportMode.class)) { - new TextExporter().exportTexts(handler, selFile + File.separator + TextExportSettings.EXPORT_FOLDER_NAME, swf.tags, + new TextExporter().exportTexts(handler, Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME), swf.tags, new TextExportSettings(export.getValue(TextExportMode.class), Configuration.textExportSingleFile.get(), export.getZoom()), evl); } if (export.isOptionEnabled(MovieExportMode.class)) { - new MovieExporter().exportMovies(handler, selFile + File.separator + "movies", swf.tags, + new MovieExporter().exportMovies(handler, Path.combine(selFile, "movies"), swf.tags, new MovieExportSettings(export.getValue(MovieExportMode.class)), evl); } if (export.isOptionEnabled(SoundExportMode.class)) { - new SoundExporter().exportSounds(handler, selFile + File.separator + "sounds", swf.tags, + new SoundExporter().exportSounds(handler, Path.combine(selFile, "sounds"), swf.tags, new SoundExportSettings(export.getValue(SoundExportMode.class)), evl); } if (export.isOptionEnabled(BinaryDataExportMode.class)) { - new BinaryDataExporter().exportBinaryData(handler, selFile + File.separator + "binaryData", swf.tags, + new BinaryDataExporter().exportBinaryData(handler, Path.combine(selFile, "binaryData"), swf.tags, new BinaryDataExportSettings(export.getValue(BinaryDataExportMode.class)), evl); } if (export.isOptionEnabled(FontExportMode.class)) { - new FontExporter().exportFonts(handler, selFile + File.separator + "fonts", swf.tags, + new FontExporter().exportFonts(handler, Path.combine(selFile, "fonts"), swf.tags, new FontExportSettings(export.getValue(FontExportMode.class)), evl); } @@ -1272,7 +1272,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se frameExporter.exportFrames(handler, selFile + File.separator + "frames", swf, 0, null, fes, evl); for (CharacterTag c : swf.getCharacters().values()) { if (c instanceof DefineSpriteTag) { - frameExporter.exportFrames(handler, selFile + File.separator + "sprites", swf, c.getCharacterId(), null, fes, evl); + frameExporter.exportFrames(handler, Path.combine(selFile, "sprites"), swf, c.getCharacterId(), null, fes, evl); } } } @@ -1283,7 +1283,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (c instanceof ButtonTag) { List frameNums = new ArrayList<>(); frameNums.add(0); // todo: export all frames - frameExporter.exportFrames(handler, selFile + File.separator + "buttons", swf, c.getCharacterId(), frameNums, bes, evl); + frameExporter.exportFrames(handler, Path.combine(selFile, "buttons"), swf, c.getCharacterId(), frameNums, bes, evl); } } } @@ -1301,6 +1301,113 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } } + public void exportAllDebug(SWF swf, AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException { + EventListener evl = swf.getExportEventListener(); + + if (export.isOptionEnabled(ImageExportMode.class)) { + for (ImageExportMode exportMode : ImageExportMode.values()) { + new ImageExporter().exportImages(handler, Path.combine(selFile, "images", exportMode.name()), swf.tags, + new ImageExportSettings(exportMode), evl); + } + } + + if (export.isOptionEnabled(ShapeExportMode.class)) { + for (ShapeExportMode exportMode : ShapeExportMode.values()) { + new ShapeExporter().exportShapes(handler, Path.combine(selFile, "shapes", exportMode.name()), swf.tags, + new ShapeExportSettings(exportMode, export.getZoom()), evl); + } + } + + if (export.isOptionEnabled(MorphShapeExportMode.class)) { + for (MorphShapeExportMode exportMode : MorphShapeExportMode.values()) { + new MorphShapeExporter().exportMorphShapes(handler, Path.combine(selFile, "morphshapes", exportMode.name()), swf.tags, + new MorphShapeExportSettings(exportMode, export.getZoom()), evl); + } + } + + if (export.isOptionEnabled(TextExportMode.class)) { + for (TextExportMode exportMode : TextExportMode.values()) { + new TextExporter().exportTexts(handler, Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, + new TextExportSettings(exportMode, Configuration.textExportSingleFile.get(), export.getZoom()), evl); + } + } + + if (export.isOptionEnabled(MovieExportMode.class)) { + for (MovieExportMode exportMode : MovieExportMode.values()) { + new MovieExporter().exportMovies(handler, Path.combine(selFile, "movies", exportMode.name()), swf.tags, + new MovieExportSettings(exportMode), evl); + } + } + + if (export.isOptionEnabled(SoundExportMode.class)) { + for (SoundExportMode exportMode : SoundExportMode.values()) { + new SoundExporter().exportSounds(handler, Path.combine(selFile, "sounds", exportMode.name()), swf.tags, + new SoundExportSettings(exportMode), evl); + } + } + + if (export.isOptionEnabled(BinaryDataExportMode.class)) { + for (BinaryDataExportMode exportMode : BinaryDataExportMode.values()) { + new BinaryDataExporter().exportBinaryData(handler, Path.combine(selFile, "binaryData", exportMode.name()), swf.tags, + new BinaryDataExportSettings(exportMode), evl); + } + } + + if (export.isOptionEnabled(FontExportMode.class)) { + for (FontExportMode exportMode : FontExportMode.values()) { + new FontExporter().exportFonts(handler, Path.combine(selFile, "fonts", exportMode.name()), swf.tags, + new FontExportSettings(exportMode), evl); + } + } + + if (export.isOptionEnabled(SymbolClassExportMode.class)) { + for (SymbolClassExportMode exportMode : SymbolClassExportMode.values()) { + new SymbolClassExporter().exportNames(selFile, swf.tags, evl); + } + } + + FrameExporter frameExporter = new FrameExporter(); + + if (export.isOptionEnabled(FramesExportMode.class)) { + for (FramesExportMode exportMode : FramesExportMode.values()) { + FramesExportSettings fes = new FramesExportSettings(exportMode, export.getZoom()); + frameExporter.exportFrames(handler, Path.combine(selFile, "frames", exportMode.name()), swf, 0, null, fes, evl); + for (CharacterTag c : swf.getCharacters().values()) { + if (c instanceof DefineSpriteTag) { + frameExporter.exportFrames(handler, Path.combine(selFile, "sprites", exportMode.name()), swf, c.getCharacterId(), null, fes, evl); + } + } + } + } + + if (export.isOptionEnabled(ButtonExportMode.class)) { + for (ButtonExportMode exportMode : ButtonExportMode.values()) { + ButtonExportSettings bes = new ButtonExportSettings(exportMode, export.getZoom()); + for (CharacterTag c : swf.getCharacters().values()) { + if (c instanceof ButtonTag) { + List frameNums = new ArrayList<>(); + frameNums.add(0); // todo: export all frames + frameExporter.exportFrames(handler, Path.combine(selFile, "buttons", exportMode.name()), swf, c.getCharacterId(), frameNums, bes, evl); + } + } + } + } + + if (export.isOptionEnabled(ScriptExportMode.class)) { + boolean parallel = Configuration.parallelSpeedUp.get(); + for (ScriptExportMode exportMode : ScriptExportMode.values()) { + String scriptsFolder = Path.combine(selFile, ScriptExportSettings.EXPORT_FOLDER_NAME, exportMode.name()); + Path.createDirectorySafe(new File(scriptsFolder)); + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(exportMode, !parallel && Configuration.scriptExportSingleFile.get()); + String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); + try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { + scriptExportSettings.singleFileWriter = writer; + swf.exportActionScript(handler, scriptsFolder, scriptExportSettings, parallel, evl); + } + } + } + } + public List getSwfs() { return swfs; } diff --git a/src/com/jpexs/decompiler/flash/gui/View.java b/src/com/jpexs/decompiler/flash/gui/View.java index 2d4733726..fb52f21f6 100644 --- a/src/com/jpexs/decompiler/flash/gui/View.java +++ b/src/com/jpexs/decompiler/flash/gui/View.java @@ -1,654 +1,652 @@ -/* - * Copyright (C) 2010-2015 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.configuration.Configuration; -import com.jpexs.decompiler.flash.configuration.ConfigurationItem; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Desktop; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Graphics; -import java.awt.GraphicsDevice; -import java.awt.GraphicsEnvironment; -import java.awt.Image; -import java.awt.Rectangle; -import java.awt.TexturePaint; -import java.awt.Window; -import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; -import java.awt.event.WindowEvent; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.imageio.ImageIO; -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.ActionMap; -import javax.swing.Icon; -import javax.swing.ImageIcon; -import javax.swing.InputMap; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JEditorPane; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JRootPane; -import javax.swing.JTable; -import javax.swing.JTree; -import javax.swing.KeyStroke; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; -import javax.swing.plaf.FontUIResource; -import javax.swing.plaf.basic.BasicColorChooserUI; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableColumnModel; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableColumn; -import javax.swing.table.TableModel; -import javax.swing.text.JTextComponent; -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreePath; -import org.pushingpixels.flamingo.api.common.icon.ImageWrapperResizableIcon; -import org.pushingpixels.substance.api.ColorSchemeAssociationKind; -import org.pushingpixels.substance.api.ComponentState; -import org.pushingpixels.substance.api.DecorationAreaType; -import org.pushingpixels.substance.api.SubstanceColorScheme; -import org.pushingpixels.substance.api.SubstanceConstants; -import org.pushingpixels.substance.api.SubstanceLookAndFeel; -import org.pushingpixels.substance.api.fonts.FontPolicy; -import org.pushingpixels.substance.api.fonts.FontSet; -import org.pushingpixels.substance.api.skin.SubstanceOfficeBlue2007LookAndFeel; -import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities; - -/** - * Contains methods for GUI - * - * @author JPEXS - */ -public class View { - - public static Color getDefaultBackgroundColor() { - return SubstanceLookAndFeel.getCurrentSkin().getColorScheme(DecorationAreaType.GENERAL, ColorSchemeAssociationKind.FILL, ComponentState.ENABLED).getBackgroundFillColor(); - } - - private static Color swfBackgroundColor = null; - - public static void setSwfBackgroundColor(Color swfBackgroundColor) { - View.swfBackgroundColor = swfBackgroundColor; - } - - public static Color getSwfBackgroundColor() { - if (swfBackgroundColor == null) { - return getDefaultBackgroundColor(); - } - return swfBackgroundColor; - } - - private static final BufferedImage transparentTexture; - - public static final TexturePaint transparentPaint; - - private static final Color transparentColor1 = new Color(0x99, 0x99, 0x99); - - private static final Color transparentColor2 = new Color(0x66, 0x66, 0x66); - - static { - transparentTexture = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); - Graphics g = transparentTexture.getGraphics(); - g.setColor(transparentColor1); - g.fillRect(0, 0, 16, 16); - g.setColor(transparentColor2); - g.fillRect(0, 0, 8, 8); - g.fillRect(8, 8, 8, 8); - transparentPaint = new TexturePaint(View.transparentTexture, new Rectangle(0, 0, transparentTexture.getWidth(), transparentTexture.getHeight())); - } - - /** - * Sets windows Look and Feel - */ - public static void setLookAndFeel() { - - //Save default font for Chinese characters - final Font defaultFont = (new JLabel()).getFont(); - try { - - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - - } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) { - } - - execInEventDispatch(() -> { - try { - UIManager.setLookAndFeel(new SubstanceOfficeBlue2007LookAndFeel()); - SubstanceLookAndFeel.setSkin(Configuration.guiSkin.get()); - UIManager.put(SubstanceLookAndFeel.COLORIZATION_FACTOR, 0.999);//This works for not changing labels color and not changing Dialogs title - UIManager.put("Tree.expandedIcon", getIcon("expand16")); - UIManager.put("Tree.collapsedIcon", getIcon("collapse16")); - UIManager.put("ColorChooserUI", BasicColorChooserUI.class.getName()); - UIManager.put("ColorChooser.swatchesRecentSwatchSize", new Dimension(20, 20)); - UIManager.put("ColorChooser.swatchesSwatchSize", new Dimension(20, 20)); - UIManager.put("RibbonApplicationMenuPopupPanelUI", MyRibbonApplicationMenuPopupPanelUI.class.getName()); - UIManager.put("RibbonApplicationMenuButtonUI", MyRibbonApplicationMenuButtonUI.class.getName()); - UIManager.put("ProgressBarUI", MyProgressBarUI.class.getName()); - UIManager.put("TextField.background", Color.white); - UIManager.put("FormattedTextField.background", Color.white); - UIManager.put("CommandButtonUI", MyCommandButtonUI.class.getName()); - - FontPolicy pol = SubstanceLookAndFeel.getFontPolicy(); - final FontSet fs = pol.getFontSet("Substance", null); - - //Restore default font for chinese characters - SubstanceLookAndFeel.setFontPolicy(new FontPolicy() { - - private final FontSet fontSet = new FontSet() { - - private FontUIResource controlFont; - - private FontUIResource menuFont; - - private FontUIResource titleFont; - - private FontUIResource windowTitleFont; - - private FontUIResource smallFont; - - private FontUIResource messageFont; - - @Override - public FontUIResource getControlFont() { - if (controlFont == null) { - FontUIResource f = fs.getControlFont(); - controlFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return controlFont; - } - - @Override - public FontUIResource getMenuFont() { - if (menuFont == null) { - FontUIResource f = fs.getMenuFont(); - menuFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return menuFont; - } - - @Override - public FontUIResource getTitleFont() { - if (titleFont == null) { - FontUIResource f = fs.getTitleFont(); - titleFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return titleFont; - } - - @Override - public FontUIResource getWindowTitleFont() { - if (windowTitleFont == null) { - FontUIResource f = fs.getWindowTitleFont(); - windowTitleFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return windowTitleFont; - } - - @Override - public FontUIResource getSmallFont() { - if (smallFont == null) { - FontUIResource f = fs.getSmallFont(); - smallFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return smallFont; - } - - @Override - public FontUIResource getMessageFont() { - if (messageFont == null) { - FontUIResource f = fs.getMessageFont(); - messageFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return messageFont; - } - }; - - @Override - public FontSet getFontSet(String string, UIDefaults uid) { - return fontSet; - } - }); - } catch (UnsupportedLookAndFeelException ex) { - Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); - } - }); - - UIManager.put(SubstanceLookAndFeel.TABBED_PANE_CONTENT_BORDER_KIND, SubstanceConstants.TabContentPaneBorderKind.SINGLE_FULL); - - JFrame.setDefaultLookAndFeelDecorated(true); - JDialog.setDefaultLookAndFeelDecorated(true); - } - - /** - * Loads image from resources - * - * @param name Name of the image - * @return loaded Image - */ - public static BufferedImage loadImage(String name) { - URL imageURL = View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + name + ".png"); - try { - return ImageIO.read(imageURL); - } catch (IOException ex) { - return null; - } - } - - /** - * Sets icon of specified frame to ASDec icon - * - * @param f Frame to set icon in - */ - public static void setWindowIcon(Window f) { - List images = new ArrayList<>(); - MyResizableIcon[] icons = MyRibbonApplicationMenuButtonUI.getIcons(); - MyResizableIcon icon = icons[1]; - int sizes[] = new int[]{16, 32, 48, 256}; - for (int size : sizes) { - icon.setIconSize(size, size); - BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR); - icon.paintIcon(f, bi.getGraphics(), 0, 0); - images.add(bi); - } - f.setIconImages(images); - } - - /** - * Centers specified frame on the screen - * - * @param f Frame to center on the screen - */ - public static void centerScreen(Window f) { - centerScreen(f, 0); // todo, set screen to the currently active screen instead of the first screen in a multi screen setup, (maybe by using the screen where the main window is now classic or ribbon?) - } - - public static void centerScreen(Window f, int screen) { - - GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); - GraphicsDevice[] allDevices = env.getScreenDevices(); - int topLeftX, topLeftY, screenX, screenY, windowPosX, windowPosY; - - if (screen < allDevices.length && screen > -1) { - topLeftX = allDevices[screen].getDefaultConfiguration().getBounds().x; - topLeftY = allDevices[screen].getDefaultConfiguration().getBounds().y; - - screenX = allDevices[screen].getDefaultConfiguration().getBounds().width; - screenY = allDevices[screen].getDefaultConfiguration().getBounds().height; - } else { - topLeftX = allDevices[0].getDefaultConfiguration().getBounds().x; - topLeftY = allDevices[0].getDefaultConfiguration().getBounds().y; - - screenX = allDevices[0].getDefaultConfiguration().getBounds().width; - screenY = allDevices[0].getDefaultConfiguration().getBounds().height; - } - - windowPosX = ((screenX - f.getWidth()) / 2) + topLeftX; - windowPosY = ((screenY - f.getHeight()) / 2) + topLeftY; - - f.setLocation(windowPosX, windowPosY); - } - - public static ImageIcon getIcon(String name) { - return new ImageIcon(View.class.getClassLoader().getResource("com/jpexs/decompiler/flash/gui/graphics/" + name + ".png")); - } - - private static final KeyStroke escapeStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); - - private static final String dispatchWindowClosingActionMapKey = "com.jpexs.dispatch:WINDOW_CLOSING"; - - public static void installEscapeCloseOperation(final JDialog dialog) { - Action dispatchClosing = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent event) { - dialog.dispatchEvent(new WindowEvent( - dialog, WindowEvent.WINDOW_CLOSING)); - } - }; - JRootPane root = dialog.getRootPane(); - root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( - escapeStroke, dispatchWindowClosingActionMapKey); - root.getActionMap().put(dispatchWindowClosingActionMapKey, dispatchClosing); - } - - public static ImageWrapperResizableIcon getResizableIcon(String resource) { - return ImageWrapperResizableIcon.getIcon(View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png"), new Dimension(256, 256)); - } - - public static MyResizableIcon getMyResizableIcon(String resource) { - try { - return new MyResizableIcon(ImageIO.read(View.class.getResourceAsStream("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png"))); - } catch (IOException ex) { - Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); - return null; - } - } - - public static void execInEventDispatch(Runnable r) { - if (SwingUtilities.isEventDispatchThread()) { - r.run(); - } else { - try { - SwingUtilities.invokeAndWait(r); - } catch (InterruptedException ex) { - } catch (InvocationTargetException ex) { - Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - - public static void execInEventDispatchLater(Runnable r) { - if (SwingUtilities.isEventDispatchThread()) { - r.run(); - } else { - SwingUtilities.invokeLater(r); - } - } - - public static int showOptionDialog(final Component parentComponent, final Object message, final String title, final int optionType, final int messageType, final Icon icon, final Object[] options, final Object initialValue) { - final int[] ret = new int[1]; - execInEventDispatch(() -> { - ret[0] = JOptionPane.showOptionDialog(parentComponent, message, title, optionType, messageType, icon, options, initialValue); - }); - return ret[0]; - } - - public static int showConfirmDialog(Component parentComponent, Object message, String title, int optionType) { - return showConfirmDialog(parentComponent, message, title, optionType, JOptionPane.PLAIN_MESSAGE); - } - - public static int showConfirmDialog(final Component parentComponent, final Object message, final String title, final int optionType, final int messageTyp) { - final int ret[] = new int[1]; - execInEventDispatch(() -> { - ret[0] = JOptionPane.showConfirmDialog(parentComponent, message, title, optionType, messageTyp); - }); - return ret[0]; - } - - public static int showConfirmDialog(Component parentComponent, String message, String title, int optionType, ConfigurationItem showAgainConfig, int defaultOption) { - return showConfirmDialog(parentComponent, message, title, optionType, JOptionPane.PLAIN_MESSAGE, showAgainConfig, defaultOption); - } - - public static int showConfirmDialog(final Component parentComponent, String message, final String title, final int optionType, final int messageTyp, ConfigurationItem showAgainConfig, int defaultOption) { - - JLabel warLabel = new JLabel("" + message.replace("\r\n", "
") + ""); - final JPanel warPanel = new JPanel(new BorderLayout()); - warPanel.add(warLabel, BorderLayout.CENTER); - JCheckBox donotShowAgainCheckBox = new JCheckBox(AppStrings.translate("message.confirm.donotshowagain")); - donotShowAgainCheckBox.setSelected(!showAgainConfig.get()); - warPanel.add(donotShowAgainCheckBox, BorderLayout.SOUTH); - - if (donotShowAgainCheckBox.isSelected()) { - return defaultOption; - } - - final int ret[] = new int[1]; - execInEventDispatch(() -> { - ret[0] = JOptionPane.showConfirmDialog(parentComponent, warPanel, title, optionType, messageTyp); - }); - showAgainConfig.set(!donotShowAgainCheckBox.isSelected()); - return ret[0]; - } - - public static void showMessageDialog(final Component parentComponent, final String message, final String title, final int messageType) { - showMessageDialog(parentComponent, message, title, messageType, null); - } - - public static void showMessageDialog(final Component parentComponent, final String message, final String title, final int messageType, ConfigurationItem showAgainConfig) { - - execInEventDispatch(() -> { - Object msg = message; - JCheckBox donotShowAgainCheckBox = new JCheckBox(AppStrings.translate("message.confirm.donotshowagain")); - if (showAgainConfig != null) { - JLabel warLabel = new JLabel("" + message.replace("\r\n", "
") + ""); - final JPanel warPanel = new JPanel(new BorderLayout()); - warPanel.add(warLabel, BorderLayout.CENTER); - donotShowAgainCheckBox.setSelected(!showAgainConfig.get()); - warPanel.add(donotShowAgainCheckBox, BorderLayout.SOUTH); - msg = warPanel; - if (donotShowAgainCheckBox.isSelected()) { - return; - } - } - final Object fmsg = msg; - - JOptionPane.showMessageDialog(parentComponent, fmsg, title, messageType); - if (showAgainConfig != null) { - showAgainConfig.set(!donotShowAgainCheckBox.isSelected()); - } - }); - } - - public static void showMessageDialog(final Component parentComponent, final Object message) { - execInEventDispatch(() -> { - JOptionPane.showMessageDialog(parentComponent, message); - }); - } - - public static String showInputDialog(final Object message, final Object initialSelection) { - final String[] ret = new String[1]; - execInEventDispatch(() -> { - ret[0] = JOptionPane.showInputDialog(message, initialSelection); - }); - return ret[0]; - } - - public static SubstanceColorScheme getColorScheme() { - return SubstanceColorSchemeUtilities.getActiveColorScheme(new JButton(), ComponentState.ENABLED); - } - - public static void refreshTree(JTree tree, TreeModel model) { - List> expandedNodes = getExpandedNodes(tree); - tree.setModel(model); - expandTreeNodes(tree, expandedNodes); - } - - public static List> getExpandedNodes(JTree tree) { - List> expandedNodes = new ArrayList<>(); - int rowCount = tree.getRowCount(); - for (int i = 0; i < rowCount; i++) { - try { - TreePath path = tree.getPathForRow(i); - if (tree.isExpanded(path)) { - List pathAsStringList = new ArrayList<>(); - for (Object pathCompnent : path.getPath()) { - pathAsStringList.add(pathCompnent.toString()); - } - expandedNodes.add(pathAsStringList); - } - } catch (IndexOutOfBoundsException | NullPointerException ex) { - // TreeNode was removed, ignore - } - } - return expandedNodes; - } - - public static void expandTreeNodes(JTree tree, List> pathsToExpand) { - for (List pathAsStringList : pathsToExpand) { - expandTreeNode(tree, pathAsStringList); - } - } - - private static TreePath expandTreeNode(JTree tree, List pathAsStringList) { - TreePath tp = getTreePathByPathStrings(tree, pathAsStringList); - tree.expandPath(tp); - return tp; - } - - public static TreePath getTreePathByPathStrings(JTree tree, List pathAsStringList) { - TreeModel model = tree.getModel(); - if (model == null) { - return null; - } - - Object node = model.getRoot(); - - if (pathAsStringList.isEmpty()) { - return null; - } - if (!pathAsStringList.get(0).equals(node.toString())) { - return null; - } - - List path = new ArrayList<>(); - path.add(node); - - for (int i = 1; i < pathAsStringList.size(); i++) { - String name = pathAsStringList.get(i); - int childCount = model.getChildCount(node); - for (int j = 0; j < childCount; j++) { - Object child = model.getChild(node, j); - if (child.toString().equals(name)) { - node = child; - path.add(node); - break; - } - } - } - - TreePath tp = new TreePath(path.toArray(new Object[path.size()])); - return tp; - } - - public static void expandTreeNodes(JTree tree, TreePath parent, boolean expand) { - expandTreeNodesRecursive(tree, parent, expand); - } - - private static void expandTreeNodesRecursive(JTree tree, TreePath parent, boolean expand) { - TreeModel model = tree.getModel(); - - Object node = parent.getLastPathComponent(); - int childCount = model.getChildCount(node); - for (int j = 0; j < childCount; j++) { - Object child = model.getChild(node, j); - TreePath path = parent.pathByAddingChild(child); - expandTreeNodesRecursive(tree, path, expand); - } - - if (expand) { - tree.expandPath(parent); - } else { - tree.collapsePath(parent); - } - } - - public static void addEditorAction(JEditorPane editor, AbstractAction a, String key, String name, String keyStroke) { - KeyStroke ks = KeyStroke.getKeyStroke(keyStroke); - a.putValue(Action.ACCELERATOR_KEY, ks); - a.putValue(Action.NAME, name); - - String actionName = key; - ActionMap amap = editor.getActionMap(); - InputMap imap = editor.getInputMap(JTextComponent.WHEN_FOCUSED); - imap.put(ks, actionName); - amap.put(actionName, a); - - JPopupMenu pmenu = editor.getComponentPopupMenu(); - JMenuItem findUsagesMenu = new JMenuItem(a); - pmenu.add(findUsagesMenu); - } - - public static boolean navigateUrl(String url) { - if (Desktop.isDesktopSupported()) { - Desktop desktop = Desktop.getDesktop(); - if (desktop.isSupported(Desktop.Action.BROWSE)) { - try { - URI uri = new URI(url); - desktop.browse(uri); - return true; - } catch (URISyntaxException | IOException ex) { - Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - - return false; - } - - public static JTable autoResizeColWidth(final JTable table, final TableModel model) { - View.execInEventDispatch(() -> { - table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); - table.setModel(model); - - int margin = 5; - - for (int i = 0; i < table.getColumnCount(); i++) { - int vColIndex = i; - DefaultTableColumnModel colModel = (DefaultTableColumnModel) table.getColumnModel(); - TableColumn col = colModel.getColumn(vColIndex); - int width; - - // Get width of column header - TableCellRenderer renderer = col.getHeaderRenderer(); - - if (renderer == null) { - renderer = table.getTableHeader().getDefaultRenderer(); - } - - Component comp = renderer.getTableCellRendererComponent(table, col.getHeaderValue(), false, false, 0, 0); - - width = comp.getPreferredSize().width; - - // Get maximum width of column data - for (int r = 0; r < table.getRowCount(); r++) { - renderer = table.getCellRenderer(r, vColIndex); - comp = renderer.getTableCellRendererComponent(table, table.getValueAt(r, vColIndex), false, false, - r, vColIndex); - width = Math.max(width, comp.getPreferredSize().width); - } - - // Add margin - width += 2 * margin; - - // Set the width - col.setPreferredWidth(width); - } - - ((DefaultTableCellRenderer) table.getTableHeader().getDefaultRenderer()).setHorizontalAlignment( - SwingConstants.LEFT); - - // table.setAutoCreateRowSorter(true); - table.getTableHeader().setReorderingAllowed(false); - }); - - return table; - } -} +/* + * Copyright (C) 2010-2015 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.configuration.Configuration; +import com.jpexs.decompiler.flash.configuration.ConfigurationItem; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.TexturePaint; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.imageio.ImageIO; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ActionMap; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.InputMap; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JRootPane; +import javax.swing.JTable; +import javax.swing.JTree; +import javax.swing.KeyStroke; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; +import javax.swing.plaf.FontUIResource; +import javax.swing.plaf.basic.BasicColorChooserUI; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableColumnModel; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableModel; +import javax.swing.text.JTextComponent; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; +import org.pushingpixels.flamingo.api.common.icon.ImageWrapperResizableIcon; +import org.pushingpixels.substance.api.ColorSchemeAssociationKind; +import org.pushingpixels.substance.api.ComponentState; +import org.pushingpixels.substance.api.DecorationAreaType; +import org.pushingpixels.substance.api.SubstanceColorScheme; +import org.pushingpixels.substance.api.SubstanceConstants; +import org.pushingpixels.substance.api.SubstanceLookAndFeel; +import org.pushingpixels.substance.api.fonts.FontPolicy; +import org.pushingpixels.substance.api.fonts.FontSet; +import org.pushingpixels.substance.api.skin.SubstanceOfficeBlue2007LookAndFeel; +import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities; + +/** + * Contains methods for GUI + * + * @author JPEXS + */ +public class View { + + public static Color getDefaultBackgroundColor() { + return SubstanceLookAndFeel.getCurrentSkin().getColorScheme(DecorationAreaType.GENERAL, ColorSchemeAssociationKind.FILL, ComponentState.ENABLED).getBackgroundFillColor(); + } + + private static Color swfBackgroundColor = null; + + public static void setSwfBackgroundColor(Color swfBackgroundColor) { + View.swfBackgroundColor = swfBackgroundColor; + } + + public static Color getSwfBackgroundColor() { + if (swfBackgroundColor == null) { + return getDefaultBackgroundColor(); + } + return swfBackgroundColor; + } + + private static final BufferedImage transparentTexture; + + public static final TexturePaint transparentPaint; + + private static final Color transparentColor1 = new Color(0x99, 0x99, 0x99); + + private static final Color transparentColor2 = new Color(0x66, 0x66, 0x66); + + static { + transparentTexture = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); + Graphics g = transparentTexture.getGraphics(); + g.setColor(transparentColor1); + g.fillRect(0, 0, 16, 16); + g.setColor(transparentColor2); + g.fillRect(0, 0, 8, 8); + g.fillRect(8, 8, 8, 8); + transparentPaint = new TexturePaint(View.transparentTexture, new Rectangle(0, 0, transparentTexture.getWidth(), transparentTexture.getHeight())); + } + + /** + * Sets windows Look and Feel + */ + public static void setLookAndFeel() { + + //Save default font for Chinese characters + final Font defaultFont = (new JLabel()).getFont(); + try { + + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + + } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) { + } + + try { + UIManager.setLookAndFeel(new SubstanceOfficeBlue2007LookAndFeel()); + SubstanceLookAndFeel.setSkin(Configuration.guiSkin.get()); + UIManager.put(SubstanceLookAndFeel.COLORIZATION_FACTOR, 0.999);//This works for not changing labels color and not changing Dialogs title + UIManager.put("Tree.expandedIcon", getIcon("expand16")); + UIManager.put("Tree.collapsedIcon", getIcon("collapse16")); + UIManager.put("ColorChooserUI", BasicColorChooserUI.class.getName()); + UIManager.put("ColorChooser.swatchesRecentSwatchSize", new Dimension(20, 20)); + UIManager.put("ColorChooser.swatchesSwatchSize", new Dimension(20, 20)); + UIManager.put("RibbonApplicationMenuPopupPanelUI", MyRibbonApplicationMenuPopupPanelUI.class.getName()); + UIManager.put("RibbonApplicationMenuButtonUI", MyRibbonApplicationMenuButtonUI.class.getName()); + UIManager.put("ProgressBarUI", MyProgressBarUI.class.getName()); + UIManager.put("TextField.background", Color.white); + UIManager.put("FormattedTextField.background", Color.white); + UIManager.put("CommandButtonUI", MyCommandButtonUI.class.getName()); + + FontPolicy pol = SubstanceLookAndFeel.getFontPolicy(); + final FontSet fs = pol.getFontSet("Substance", null); + + //Restore default font for chinese characters + SubstanceLookAndFeel.setFontPolicy(new FontPolicy() { + + private final FontSet fontSet = new FontSet() { + + private FontUIResource controlFont; + + private FontUIResource menuFont; + + private FontUIResource titleFont; + + private FontUIResource windowTitleFont; + + private FontUIResource smallFont; + + private FontUIResource messageFont; + + @Override + public FontUIResource getControlFont() { + if (controlFont == null) { + FontUIResource f = fs.getControlFont(); + controlFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return controlFont; + } + + @Override + public FontUIResource getMenuFont() { + if (menuFont == null) { + FontUIResource f = fs.getMenuFont(); + menuFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return menuFont; + } + + @Override + public FontUIResource getTitleFont() { + if (titleFont == null) { + FontUIResource f = fs.getTitleFont(); + titleFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return titleFont; + } + + @Override + public FontUIResource getWindowTitleFont() { + if (windowTitleFont == null) { + FontUIResource f = fs.getWindowTitleFont(); + windowTitleFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return windowTitleFont; + } + + @Override + public FontUIResource getSmallFont() { + if (smallFont == null) { + FontUIResource f = fs.getSmallFont(); + smallFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return smallFont; + } + + @Override + public FontUIResource getMessageFont() { + if (messageFont == null) { + FontUIResource f = fs.getMessageFont(); + messageFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return messageFont; + } + }; + + @Override + public FontSet getFontSet(String string, UIDefaults uid) { + return fontSet; + } + }); + } catch (UnsupportedLookAndFeelException ex) { + Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); + } + + UIManager.put(SubstanceLookAndFeel.TABBED_PANE_CONTENT_BORDER_KIND, SubstanceConstants.TabContentPaneBorderKind.SINGLE_FULL); + + JFrame.setDefaultLookAndFeelDecorated(true); + JDialog.setDefaultLookAndFeelDecorated(true); + } + + /** + * Loads image from resources + * + * @param name Name of the image + * @return loaded Image + */ + public static BufferedImage loadImage(String name) { + URL imageURL = View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + name + ".png"); + try { + return ImageIO.read(imageURL); + } catch (IOException ex) { + return null; + } + } + + /** + * Sets icon of specified frame to ASDec icon + * + * @param f Frame to set icon in + */ + public static void setWindowIcon(Window f) { + List images = new ArrayList<>(); + MyResizableIcon[] icons = MyRibbonApplicationMenuButtonUI.getIcons(); + MyResizableIcon icon = icons[1]; + int sizes[] = new int[]{16, 32, 48, 256}; + for (int size : sizes) { + icon.setIconSize(size, size); + BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR); + icon.paintIcon(f, bi.getGraphics(), 0, 0); + images.add(bi); + } + f.setIconImages(images); + } + + /** + * Centers specified frame on the screen + * + * @param f Frame to center on the screen + */ + public static void centerScreen(Window f) { + centerScreen(f, 0); // todo, set screen to the currently active screen instead of the first screen in a multi screen setup, (maybe by using the screen where the main window is now classic or ribbon?) + } + + public static void centerScreen(Window f, int screen) { + + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice[] allDevices = env.getScreenDevices(); + int topLeftX, topLeftY, screenX, screenY, windowPosX, windowPosY; + + if (screen < allDevices.length && screen > -1) { + topLeftX = allDevices[screen].getDefaultConfiguration().getBounds().x; + topLeftY = allDevices[screen].getDefaultConfiguration().getBounds().y; + + screenX = allDevices[screen].getDefaultConfiguration().getBounds().width; + screenY = allDevices[screen].getDefaultConfiguration().getBounds().height; + } else { + topLeftX = allDevices[0].getDefaultConfiguration().getBounds().x; + topLeftY = allDevices[0].getDefaultConfiguration().getBounds().y; + + screenX = allDevices[0].getDefaultConfiguration().getBounds().width; + screenY = allDevices[0].getDefaultConfiguration().getBounds().height; + } + + windowPosX = ((screenX - f.getWidth()) / 2) + topLeftX; + windowPosY = ((screenY - f.getHeight()) / 2) + topLeftY; + + f.setLocation(windowPosX, windowPosY); + } + + public static ImageIcon getIcon(String name) { + return new ImageIcon(View.class.getClassLoader().getResource("com/jpexs/decompiler/flash/gui/graphics/" + name + ".png")); + } + + private static final KeyStroke escapeStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + + private static final String dispatchWindowClosingActionMapKey = "com.jpexs.dispatch:WINDOW_CLOSING"; + + public static void installEscapeCloseOperation(final JDialog dialog) { + Action dispatchClosing = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent event) { + dialog.dispatchEvent(new WindowEvent( + dialog, WindowEvent.WINDOW_CLOSING)); + } + }; + JRootPane root = dialog.getRootPane(); + root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( + escapeStroke, dispatchWindowClosingActionMapKey); + root.getActionMap().put(dispatchWindowClosingActionMapKey, dispatchClosing); + } + + public static ImageWrapperResizableIcon getResizableIcon(String resource) { + return ImageWrapperResizableIcon.getIcon(View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png"), new Dimension(256, 256)); + } + + public static MyResizableIcon getMyResizableIcon(String resource) { + try { + return new MyResizableIcon(ImageIO.read(View.class.getResourceAsStream("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png"))); + } catch (IOException ex) { + Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); + return null; + } + } + + public static void execInEventDispatch(Runnable r) { + if (SwingUtilities.isEventDispatchThread()) { + r.run(); + } else { + try { + SwingUtilities.invokeAndWait(r); + } catch (InterruptedException ex) { + } catch (InvocationTargetException ex) { + Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + public static void execInEventDispatchLater(Runnable r) { + if (SwingUtilities.isEventDispatchThread()) { + r.run(); + } else { + SwingUtilities.invokeLater(r); + } + } + + public static int showOptionDialog(final Component parentComponent, final Object message, final String title, final int optionType, final int messageType, final Icon icon, final Object[] options, final Object initialValue) { + final int[] ret = new int[1]; + execInEventDispatch(() -> { + ret[0] = JOptionPane.showOptionDialog(parentComponent, message, title, optionType, messageType, icon, options, initialValue); + }); + return ret[0]; + } + + public static int showConfirmDialog(Component parentComponent, Object message, String title, int optionType) { + return showConfirmDialog(parentComponent, message, title, optionType, JOptionPane.PLAIN_MESSAGE); + } + + public static int showConfirmDialog(final Component parentComponent, final Object message, final String title, final int optionType, final int messageTyp) { + final int ret[] = new int[1]; + execInEventDispatch(() -> { + ret[0] = JOptionPane.showConfirmDialog(parentComponent, message, title, optionType, messageTyp); + }); + return ret[0]; + } + + public static int showConfirmDialog(Component parentComponent, String message, String title, int optionType, ConfigurationItem showAgainConfig, int defaultOption) { + return showConfirmDialog(parentComponent, message, title, optionType, JOptionPane.PLAIN_MESSAGE, showAgainConfig, defaultOption); + } + + public static int showConfirmDialog(final Component parentComponent, String message, final String title, final int optionType, final int messageTyp, ConfigurationItem showAgainConfig, int defaultOption) { + + JLabel warLabel = new JLabel("" + message.replace("\r\n", "
") + ""); + final JPanel warPanel = new JPanel(new BorderLayout()); + warPanel.add(warLabel, BorderLayout.CENTER); + JCheckBox donotShowAgainCheckBox = new JCheckBox(AppStrings.translate("message.confirm.donotshowagain")); + donotShowAgainCheckBox.setSelected(!showAgainConfig.get()); + warPanel.add(donotShowAgainCheckBox, BorderLayout.SOUTH); + + if (donotShowAgainCheckBox.isSelected()) { + return defaultOption; + } + + final int ret[] = new int[1]; + execInEventDispatch(() -> { + ret[0] = JOptionPane.showConfirmDialog(parentComponent, warPanel, title, optionType, messageTyp); + }); + showAgainConfig.set(!donotShowAgainCheckBox.isSelected()); + return ret[0]; + } + + public static void showMessageDialog(final Component parentComponent, final String message, final String title, final int messageType) { + showMessageDialog(parentComponent, message, title, messageType, null); + } + + public static void showMessageDialog(final Component parentComponent, final String message, final String title, final int messageType, ConfigurationItem showAgainConfig) { + + execInEventDispatch(() -> { + Object msg = message; + JCheckBox donotShowAgainCheckBox = new JCheckBox(AppStrings.translate("message.confirm.donotshowagain")); + if (showAgainConfig != null) { + JLabel warLabel = new JLabel("" + message.replace("\r\n", "
") + ""); + final JPanel warPanel = new JPanel(new BorderLayout()); + warPanel.add(warLabel, BorderLayout.CENTER); + donotShowAgainCheckBox.setSelected(!showAgainConfig.get()); + warPanel.add(donotShowAgainCheckBox, BorderLayout.SOUTH); + msg = warPanel; + if (donotShowAgainCheckBox.isSelected()) { + return; + } + } + final Object fmsg = msg; + + JOptionPane.showMessageDialog(parentComponent, fmsg, title, messageType); + if (showAgainConfig != null) { + showAgainConfig.set(!donotShowAgainCheckBox.isSelected()); + } + }); + } + + public static void showMessageDialog(final Component parentComponent, final Object message) { + execInEventDispatch(() -> { + JOptionPane.showMessageDialog(parentComponent, message); + }); + } + + public static String showInputDialog(final Object message, final Object initialSelection) { + final String[] ret = new String[1]; + execInEventDispatch(() -> { + ret[0] = JOptionPane.showInputDialog(message, initialSelection); + }); + return ret[0]; + } + + public static SubstanceColorScheme getColorScheme() { + return SubstanceColorSchemeUtilities.getActiveColorScheme(new JButton(), ComponentState.ENABLED); + } + + public static void refreshTree(JTree tree, TreeModel model) { + List> expandedNodes = getExpandedNodes(tree); + tree.setModel(model); + expandTreeNodes(tree, expandedNodes); + } + + public static List> getExpandedNodes(JTree tree) { + List> expandedNodes = new ArrayList<>(); + int rowCount = tree.getRowCount(); + for (int i = 0; i < rowCount; i++) { + try { + TreePath path = tree.getPathForRow(i); + if (tree.isExpanded(path)) { + List pathAsStringList = new ArrayList<>(); + for (Object pathCompnent : path.getPath()) { + pathAsStringList.add(pathCompnent.toString()); + } + expandedNodes.add(pathAsStringList); + } + } catch (IndexOutOfBoundsException | NullPointerException ex) { + // TreeNode was removed, ignore + } + } + return expandedNodes; + } + + public static void expandTreeNodes(JTree tree, List> pathsToExpand) { + for (List pathAsStringList : pathsToExpand) { + expandTreeNode(tree, pathAsStringList); + } + } + + private static TreePath expandTreeNode(JTree tree, List pathAsStringList) { + TreePath tp = getTreePathByPathStrings(tree, pathAsStringList); + tree.expandPath(tp); + return tp; + } + + public static TreePath getTreePathByPathStrings(JTree tree, List pathAsStringList) { + TreeModel model = tree.getModel(); + if (model == null) { + return null; + } + + Object node = model.getRoot(); + + if (pathAsStringList.isEmpty()) { + return null; + } + if (!pathAsStringList.get(0).equals(node.toString())) { + return null; + } + + List path = new ArrayList<>(); + path.add(node); + + for (int i = 1; i < pathAsStringList.size(); i++) { + String name = pathAsStringList.get(i); + int childCount = model.getChildCount(node); + for (int j = 0; j < childCount; j++) { + Object child = model.getChild(node, j); + if (child.toString().equals(name)) { + node = child; + path.add(node); + break; + } + } + } + + TreePath tp = new TreePath(path.toArray(new Object[path.size()])); + return tp; + } + + public static void expandTreeNodes(JTree tree, TreePath parent, boolean expand) { + expandTreeNodesRecursive(tree, parent, expand); + } + + private static void expandTreeNodesRecursive(JTree tree, TreePath parent, boolean expand) { + TreeModel model = tree.getModel(); + + Object node = parent.getLastPathComponent(); + int childCount = model.getChildCount(node); + for (int j = 0; j < childCount; j++) { + Object child = model.getChild(node, j); + TreePath path = parent.pathByAddingChild(child); + expandTreeNodesRecursive(tree, path, expand); + } + + if (expand) { + tree.expandPath(parent); + } else { + tree.collapsePath(parent); + } + } + + public static void addEditorAction(JEditorPane editor, AbstractAction a, String key, String name, String keyStroke) { + KeyStroke ks = KeyStroke.getKeyStroke(keyStroke); + a.putValue(Action.ACCELERATOR_KEY, ks); + a.putValue(Action.NAME, name); + + String actionName = key; + ActionMap amap = editor.getActionMap(); + InputMap imap = editor.getInputMap(JTextComponent.WHEN_FOCUSED); + imap.put(ks, actionName); + amap.put(actionName, a); + + JPopupMenu pmenu = editor.getComponentPopupMenu(); + JMenuItem findUsagesMenu = new JMenuItem(a); + pmenu.add(findUsagesMenu); + } + + public static boolean navigateUrl(String url) { + if (Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + if (desktop.isSupported(Desktop.Action.BROWSE)) { + try { + URI uri = new URI(url); + desktop.browse(uri); + return true; + } catch (URISyntaxException | IOException ex) { + Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + return false; + } + + public static JTable autoResizeColWidth(final JTable table, final TableModel model) { + View.execInEventDispatch(() -> { + table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + table.setModel(model); + + int margin = 5; + + for (int i = 0; i < table.getColumnCount(); i++) { + int vColIndex = i; + DefaultTableColumnModel colModel = (DefaultTableColumnModel) table.getColumnModel(); + TableColumn col = colModel.getColumn(vColIndex); + int width; + + // Get width of column header + TableCellRenderer renderer = col.getHeaderRenderer(); + + if (renderer == null) { + renderer = table.getTableHeader().getDefaultRenderer(); + } + + Component comp = renderer.getTableCellRendererComponent(table, col.getHeaderValue(), false, false, 0, 0); + + width = comp.getPreferredSize().width; + + // Get maximum width of column data + for (int r = 0; r < table.getRowCount(); r++) { + renderer = table.getCellRenderer(r, vColIndex); + comp = renderer.getTableCellRendererComponent(table, table.getValueAt(r, vColIndex), false, false, + r, vColIndex); + width = Math.max(width, comp.getPreferredSize().width); + } + + // Add margin + width += 2 * margin; + + // Set the width + col.setPreferredWidth(width); + } + + ((DefaultTableCellRenderer) table.getTableHeader().getDefaultRenderer()).setHorizontalAlignment( + SwingConstants.LEFT); + + // table.setAutoCreateRowSorter(true); + table.getTableHeader().setReorderingAllowed(false); + }); + + return table; + } +}