/* * Copyright (C) 2010-2016 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.tags; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.exporters.commonshape.Point; import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.DrawableTag; import com.jpexs.decompiler.flash.tags.base.RenderContext; import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.decompiler.flash.types.annotations.SWFVersion; import com.jpexs.helpers.ByteArrayRange; import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.PathIterator; import java.io.IOException; /** * * @author JPEXS */ @SWFVersion(from = 8) public class DefineScalingGridTag extends Tag implements CharacterIdTag { public static final int ID = 78; public static final String NAME = "DefineScalingGrid"; @SWFType(BasicType.UI16) public int characterId; public RECT splitter; /** * Constructor * * @param swf */ public DefineScalingGridTag(SWF swf) { super(swf, ID, NAME, null); splitter = new RECT(); } public DefineScalingGridTag(SWFInputStream sis, ByteArrayRange data) throws IOException { super(sis.getSwf(), ID, NAME, data); readData(sis, data, 0, false, false, false); } @Override public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { characterId = sis.readUI16("characterId"); splitter = sis.readRECT("splitter"); } /** * Gets data bytes * * @param sos SWF output stream * @throws java.io.IOException */ @Override public void getData(SWFOutputStream sos) throws IOException { sos.writeUI16(characterId); sos.writeRECT(splitter); } @Override public int getCharacterId() { return characterId; } @Override public void setCharacterId(int characterId) { this.characterId = characterId; } private static double roundPixels(double v) { return v; //Math.rint(v / SWF.unitDivisor) * SWF.unitDivisor; } private static double roundPixels20(double v) { return Math.rint(v / SWF.unitDivisor) * SWF.unitDivisor; } private static Matrix rectToRectMatrix(ExportRectangle fromRect, ExportRectangle toRect) { Matrix toOrigin = Matrix.getTranslateInstance(roundPixels(-fromRect.xMin), roundPixels(-fromRect.yMin)); Matrix scale = new Matrix(); scale.scaleX = roundPixels(toRect.getWidth()) / roundPixels(fromRect.getWidth()); scale.scaleY = roundPixels(toRect.getHeight()) / roundPixels(fromRect.getHeight()); Matrix toDest = Matrix.getTranslateInstance(roundPixels(toRect.xMin), roundPixels(toRect.yMin)); return toOrigin.preConcatenate(scale).preConcatenate(toDest); } public RECT getRect() { Shape s = getOutline(0, 0, 0, new RenderContext(), new Matrix(), new Matrix(), true); if (s == null) { return null; } Rectangle r = s.getBounds(); return new RECT(r.x, r.x + r.width, r.y, r.y + r.height); } public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, Matrix prevTransform, boolean stroked) { CharacterTag ct = swf.getCharacter(characterId); if (ct == null) { return null; } if (!(ct instanceof DrawableTag)) { return null; } double[] coords = new double[6]; DrawableTag dt = (DrawableTag) ct; Shape path = dt.getOutline(frame, time, ratio, renderContext, transformation, stroked); PathIterator iterator = path.getPathIterator(new AffineTransform()); GeneralPath gp = new GeneralPath(GeneralPath.WIND_EVEN_ODD); ExportRectangle boundsRect = new ExportRectangle(dt.getRect()); ExportRectangle scalingGrid = new ExportRectangle(splitter); ExportRectangle[] sourceRect = new ExportRectangle[9]; ExportRectangle[] targetRect = new ExportRectangle[9]; Matrix[] transforms = new Matrix[9]; getSlices(transformation.transform(boundsRect), boundsRect, scalingGrid, sourceRect, targetRect, transforms); while (!iterator.isDone()) { int type = iterator.currentSegment(coords); for (int i = 0; i < 6; i += 2) { double x = coords[i]; double y = coords[i + 1]; for (int s = 0; s < 9; s++) { Point p = new Point(x, y); if (sourceRect[s].contains(p)) { p = transforms[s].transform(p); coords[i] = p.x; coords[i + 1] = p.y; break; } } } switch (type) { case PathIterator.SEG_MOVETO: gp.moveTo(coords[0], coords[1]); break; case PathIterator.SEG_LINETO: gp.lineTo(coords[0], coords[1]); break; case PathIterator.SEG_QUADTO: gp.quadTo(coords[0], coords[1], coords[2], coords[3]); break; case PathIterator.SEG_CUBICTO: gp.curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]); break; case PathIterator.SEG_CLOSE: gp.closePath(); break; } iterator.next(); } return gp; } public static void getSlices(ExportRectangle targetBounds, ExportRectangle boundsRect, ExportRectangle scalingGrid, ExportRectangle[] sourceRect, ExportRectangle[] targetRect, Matrix[] transforms) { double[] src_x = new double[]{boundsRect.xMin, scalingGrid.xMin, scalingGrid.xMax, boundsRect.xMax}; double[] dst_x = new double[]{targetBounds.xMin, targetBounds.xMin + scalingGrid.xMin, targetBounds.xMax - (boundsRect.xMax - scalingGrid.xMax), targetBounds.xMax}; double[] src_y = new double[]{boundsRect.yMin, scalingGrid.yMin, scalingGrid.yMax, boundsRect.yMax}; double[] dst_y = new double[]{targetBounds.yMin, targetBounds.yMin + scalingGrid.yMin, targetBounds.yMax - (boundsRect.yMax - scalingGrid.yMax), targetBounds.yMax}; int pos = 0; for (int sy = 0; sy < 3; sy++) { for (int sx = 0; sx < 3; sx++) { sourceRect[pos] = new ExportRectangle(src_x[sx], src_y[sy], src_x[sx + 1], src_y[sy + 1]); targetRect[pos] = new ExportRectangle(dst_x[sx], dst_y[sy], dst_x[sx + 1], dst_y[sy + 1]); pos++; } } for (int i = 0; i < targetRect.length; i++) { /* sourceRect[i].xMax = roundPixels20(sourceRect[i].xMax); sourceRect[i].yMax = roundPixels20(sourceRect[i].yMax); sourceRect[i].xMin = roundPixels20(sourceRect[i].xMin); sourceRect[i].yMin = roundPixels20(sourceRect[i].yMin); */ //System.out.println("source[" + i + "]=" + sourceRect[i]); //System.out.println("target[" + i + "]=" + targetRect[i]); /*targetRect[i].xMax = roundPixels20(targetRect[i].xMax); targetRect[i].yMax = roundPixels20(targetRect[i].yMax); targetRect[i].xMin = roundPixels20(targetRect[i].xMin); targetRect[i].yMin = roundPixels20(targetRect[i].yMin); */ transforms[i] = rectToRectMatrix(sourceRect[i], targetRect[i]); targetRect[i].xMax = Math.rint(targetRect[i].xMax / SWF.unitDivisor); targetRect[i].yMax = Math.rint(targetRect[i].yMax / SWF.unitDivisor); targetRect[i].xMin = Math.rint(targetRect[i].xMin / SWF.unitDivisor); targetRect[i].yMin = Math.rint(targetRect[i].yMin / SWF.unitDivisor); //targetRect[i].xMax += maxStroke; //Round to pixel boundary } } }