-
Notifications
You must be signed in to change notification settings - Fork 6
Open
Description
Hello,
I just added this class to export DefineShape4Tag
`using SwfLib.Shapes.FillStyles;
using SwfLib.Shapes.LineStyles;
using SwfLib.Shapes.Records;
using SwfLib.Tags.ShapeTags;
using EdgeMap = System.Collections.Generic.Dictionary<int, System.Collections.Generic.List<SwfShapeExporter.IEdge>>;
using CoordMap = System.Collections.Generic.Dictionary<SixLabors.ImageSharp.Point, System.Collections.Generic.List<SwfShapeExporter.IEdge>>;
using Path = System.Collections.Generic.List<SwfShapeExporter.IEdge>;
namespace SwfShapeExporter;
public class SwfShapeEx : ISwfShape
{
public List<IShapeRecordEx> RecordsEx { get; set; } = new();
public List<FillStyleRGBA> FillStyles { get; set; } = new();
public List<LineStyleEx> LineStylesEx { get; set; } = new();
public List<EdgeMap> FillEdgeMaps { get; set; } = new();
public EdgeMap CurrentFillEdgeMap { get; set; } = new();
public List<EdgeMap> LineEdgeMaps { get; set; } = new();
public EdgeMap CurrentLineEdgeMap { get; set; } = new();
public int NumGroups { get; set; } = 0;
public CoordMap CoordMap { get; set; } = new();
public CoordMap ReverseCoordMap { get; set; } = new();
public bool EdgeMapsCreated { get; private set; } = false;
public SwfShapeEx(IList<IShapeRecordEx> records, IList<FillStyleRGBA> fillStyles, IList<LineStyleEx> lineStyles)
{
RecordsEx = records.ToList();
FillStyles = fillStyles.ToList();
LineStylesEx = lineStyles.ToList();
}
public SwfShapeEx(DefineShape4Tag shape) : this(shape.ShapeRecords, shape.FillStyles, shape.LineStyles) { }
public void Export(IShapeHandler handler)
{
CreateEdgeMaps();
handler.BeginShape();
for (int i = 0; i < NumGroups; ++i)
{
ExportFillPath(handler, i);
ExportLinePath(handler, i);
}
handler.EndShape();
}
public void ExportFillPath(IShapeHandler handler, int i)
{
var path = PathFromEdgeMap(FillEdgeMaps[i]);
if (path.Count == 0) return;
var pos = new Point(int.MaxValue, int.MaxValue);
var fillStyleIdx = int.MaxValue;
handler.BeginFills();
foreach (var edge in path)
{
if (fillStyleIdx != edge.FillStyleIdx)
{
if (fillStyleIdx != int.MaxValue) handler.EndFill();
fillStyleIdx = edge.FillStyleIdx;
pos = new Point(int.MaxValue, int.MaxValue);
var fillStyle = (fillStyleIdx == 0) ? null : FillStyles[fillStyleIdx - 1];
if (fillStyle is null) handler.BeginFill(Color.Black);
else switch (fillStyle.Type)
{
case FillStyleType.SolidColor:
var solidFillStyle = (SolidFillStyleRGBA)fillStyle;
handler.BeginFill(solidFillStyle.Color.ToImageSharpColor());
break;
case FillStyleType.LinearGradient:
case FillStyleType.RadialGradient:
case FillStyleType.FocalGradient:
case FillStyleType.RepeatingBitmap:
case FillStyleType.ClippedBitmap:
case FillStyleType.NonSmoothedRepeatingBitmap:
case FillStyleType.NonSmoothedClippedBitmap:
throw new NotImplementedException($"Unsupported fill style type {fillStyle.Type}");
default:
throw new ArgumentException($"Invalid fill style type {fillStyle.Type}");
}
}
if (pos != edge.From)
handler.MoveTo(edge.From);
if (edge is CurvedEdge cedge)
handler.CurveTo(cedge.Control, cedge.To);
else
handler.LineTo(edge.To);
pos = edge.To;
}
if (fillStyleIdx != int.MaxValue) handler.EndFill();
handler.EndFills();
}
public void ExportLinePath(IShapeHandler handler, int i)
{
var path = PathFromEdgeMap(LineEdgeMaps[i]);
if (path.Count == 0) return;
var pos = new Point(int.MaxValue, int.MaxValue);
var lastMove = pos;
var lineStyleIdx = int.MaxValue;
handler.BeginLines();
foreach (var edge in path)
{
if (lineStyleIdx != edge.LineStyleIdx)
{
lineStyleIdx = edge.LineStyleIdx;
pos = new Point(int.MaxValue, int.MaxValue);
if (lineStyleIdx == 0) handler.LineStyle(0, Color.Black);
else
{
var lineStyle = LineStylesEx[lineStyleIdx - 1];
handler.LineStyle(lineStyle.Width, lineStyle.Color.ToImageSharpColor());
}
}
if (pos != edge.From)
{
handler.MoveTo(edge.From);
lastMove = edge.From;
}
if (edge is CurvedEdge cedge)
handler.CurveTo(cedge.Control, cedge.To);
else
handler.LineTo(edge.To);
pos = edge.To;
}
handler.EndLines(pos == lastMove);
}
public void CreateEdgeMaps()
{
if (EdgeMapsCreated) return;
Point pos = Point.Empty;
Point from = Point.Empty;
Point to = Point.Empty;
Point control = Point.Empty;
int fillStyleIdxOffset = 0;
int lineStyleIdxOffset = 0;
int currentFillStyleIdx0 = 0;
int currentFillStyleIdx1 = 0;
int currentLineStyleIdx = 0;
Path subPath = new();
NumGroups = 0;
FillEdgeMaps.Clear();
LineEdgeMaps.Clear();
CurrentFillEdgeMap.Clear();
CurrentLineEdgeMap.Clear();
for (int i = 0; i < RecordsEx.Count; ++i)
{
var shapeRecord = RecordsEx[i];
switch (shapeRecord.Type)
{
case ShapeRecordType.StyleChangeRecord:
var styleChangeRecord = (StyleChangeShapeRecordEx)shapeRecord;
if (styleChangeRecord.LineStyle is not null || styleChangeRecord.FillStyle0 is not null || styleChangeRecord.FillStyle1 is not null)
{
ProcessSubPath(subPath, currentLineStyleIdx, currentFillStyleIdx0, currentFillStyleIdx1);
subPath.Clear();
}
if (styleChangeRecord.StateNewStyles)
{
fillStyleIdxOffset = FillStyles.Count;
lineStyleIdxOffset = LineStylesEx.Count;
FillStyles.AddRange(styleChangeRecord.FillStyles);
LineStylesEx.AddRange(styleChangeRecord.LineStyles);
}
if (styleChangeRecord.LineStyle is not null && styleChangeRecord.LineStyle == 0 &&
styleChangeRecord.FillStyle0 is not null && styleChangeRecord.FillStyle0 == 0 &&
styleChangeRecord.FillStyle1 is not null && styleChangeRecord.FillStyle1 == 0)
{
CleanEdgeMap(CurrentFillEdgeMap);
CleanEdgeMap(CurrentLineEdgeMap);
FillEdgeMaps.Add(CurrentFillEdgeMap);
LineEdgeMaps.Add(CurrentLineEdgeMap);
//we must create new instead of Clear because the edge map lists hold a reference
CurrentFillEdgeMap = new EdgeMap();
CurrentLineEdgeMap = new EdgeMap();
currentLineStyleIdx = 0;
currentFillStyleIdx0 = 0;
currentFillStyleIdx1 = 0;
NumGroups++;
}
else
{
if (styleChangeRecord.LineStyle is not null)
{
currentLineStyleIdx = (int)styleChangeRecord.LineStyle;
if (currentLineStyleIdx > 0) currentLineStyleIdx += lineStyleIdxOffset;
}
if (styleChangeRecord.FillStyle0 is not null)
{
currentFillStyleIdx0 = (int)styleChangeRecord.FillStyle0;
if (currentFillStyleIdx0 > 0) currentFillStyleIdx0 += fillStyleIdxOffset;
}
if (styleChangeRecord.FillStyle1 is not null)
{
currentFillStyleIdx1 = (int)styleChangeRecord.FillStyle1;
if (currentFillStyleIdx1 > 0) currentFillStyleIdx1 += fillStyleIdxOffset;
}
}
if (styleChangeRecord.StateMoveTo)
{
pos = new Point(styleChangeRecord.MoveDeltaX, styleChangeRecord.MoveDeltaY);
}
break;
case ShapeRecordType.StraightEdge:
var straightEdgeRecord = (StraightEdgeShapeRecord)shapeRecord;
from = new Point(pos.X, pos.Y);
Size delta = new(straightEdgeRecord.DeltaX, straightEdgeRecord.DeltaY);
pos += delta;
to = new Point(pos.X, pos.Y);
subPath.Add(new StraightEdge { From = from, To = to, LineStyleIdx = currentLineStyleIdx, FillStyleIdx = currentFillStyleIdx1 });
break;
case ShapeRecordType.CurvedEdgeRecord:
var curvedEdgeRecord = (CurvedEdgeShapeRecord)shapeRecord;
from = new Point(pos.X, pos.Y);
Size controlDelta = new(curvedEdgeRecord.ControlDeltaX, curvedEdgeRecord.ControlDeltaY);
control = pos + controlDelta;
Size anchorDelta = new(curvedEdgeRecord.AnchorDeltaX, curvedEdgeRecord.AnchorDeltaY);
pos = control + anchorDelta;
to = new Point(pos.X, pos.Y);
subPath.Add(new CurvedEdge { From = from, Control = control, To = to, LineStyleIdx = currentLineStyleIdx, FillStyleIdx = currentFillStyleIdx1 });
break;
case ShapeRecordType.EndRecord:
ProcessSubPath(subPath, currentLineStyleIdx, currentFillStyleIdx0, currentFillStyleIdx1);
CleanEdgeMap(CurrentFillEdgeMap);
CleanEdgeMap(CurrentLineEdgeMap);
FillEdgeMaps.Add(CurrentFillEdgeMap);
LineEdgeMaps.Add(CurrentLineEdgeMap);
NumGroups++;
break;
default:
throw new ArgumentException($"Invalid record type {shapeRecord.Type}");
}
}
EdgeMapsCreated = true;
}
public void ProcessSubPath(Path subPath, int lineStyleIdx, int fillStyleIdx0, int fillStyleIdx1)
{
if (fillStyleIdx0 != 0)
{
if (!CurrentFillEdgeMap.ContainsKey(fillStyleIdx0)) CurrentFillEdgeMap[fillStyleIdx0] = new();
CurrentFillEdgeMap[fillStyleIdx0].AddRange(
subPath.Select(e => e.ReverseWithStyle(fillStyleIdx0)).Reverse()
);
}
if (fillStyleIdx1 != 0)
{
if (!CurrentFillEdgeMap.ContainsKey(fillStyleIdx1)) CurrentFillEdgeMap[fillStyleIdx1] = new();
CurrentFillEdgeMap[fillStyleIdx1].AddRange(subPath);
}
if (lineStyleIdx != 0)
{
if (!CurrentLineEdgeMap.ContainsKey(lineStyleIdx)) CurrentLineEdgeMap[lineStyleIdx] = new();
CurrentLineEdgeMap[lineStyleIdx].AddRange(subPath);
}
}
public void CleanEdgeMap(EdgeMap edgeMap)
{
foreach (var (styleIdx, subPath) in edgeMap)
{
if (subPath.Count == 0) continue;
IEdge? prevEdge = null;
IEdge? edge = null;
Path tmpPath = new();
CreateCoordMap(subPath);
CreateReverseCoordMap(subPath);
while (subPath.Count > 0)
{
var idx = 0;
while (idx < subPath.Count)
{
if (prevEdge is not null)
{
if (prevEdge.To != subPath[idx].From)
{
edge = FindNextEdgeInCoordMap(prevEdge);
if (edge is not null)
{
idx = subPath.IndexOf(edge);
}
else
{
var revEdge = FindNextEdgeInReverseCoordMap(prevEdge);
if (revEdge is not null)
{
idx = subPath.IndexOf(revEdge);
var r = revEdge.ReverseWithStyle(revEdge.FillStyleIdx);
UpdateEdgeInCoordMap(revEdge, r);
UpdateEdgeInReverseCoordMap(revEdge, r);
subPath[idx] = r;
}
else
{
idx = 0;
prevEdge = null;
}
}
continue;
}
}
edge = subPath[idx];
subPath.RemoveAt(idx);
tmpPath.Add(edge);
RemoveEdgeFromCoordMap(edge);
RemoveEdgeFromReverseCoordMap(edge);
prevEdge = edge;
}
}
edgeMap[styleIdx] = tmpPath;
}
}
public IEdge? FindNextEdgeInCoordMap(IEdge edge)
{
var key = edge.To;
Path? path = null;
if (!CoordMap.TryGetValue(key, out path))
return null;
if (path.Count == 0)
return null;
return path[0];
}
public IEdge? FindNextEdgeInReverseCoordMap(IEdge edge)
{
var key = edge.To;
Path? path = null;
if (!ReverseCoordMap.TryGetValue(key, out path))
return null;
if (path.Count == 0)
return null;
return path[0];
}
public void RemoveEdgeFromCoordMap(IEdge edge)
{
var key = edge.From;
if (CoordMap.ContainsKey(key))
{
if (CoordMap[key].Count == 1) CoordMap.Remove(key);
else CoordMap[key].Remove(edge);
}
}
public void RemoveEdgeFromReverseCoordMap(IEdge edge)
{
var key = edge.To;
if (ReverseCoordMap.ContainsKey(key))
{
if (ReverseCoordMap[key].Count == 1) ReverseCoordMap.Remove(key);
else ReverseCoordMap[key].Remove(edge);
}
}
public void CreateCoordMap(Path path)
{
CoordMap.Clear();
for (int i = 0; i < path.Count; ++i)
{
var key = path[i].From;
if (!CoordMap.ContainsKey(key)) CoordMap[key] = new();
CoordMap[key].Add(path[i]);
}
}
public void CreateReverseCoordMap(Path path)
{
ReverseCoordMap.Clear();
for (int i = 0; i < path.Count; ++i)
{
var key = path[i].To;
if (!ReverseCoordMap.ContainsKey(key)) ReverseCoordMap[key] = new();
ReverseCoordMap[key].Add(path[i]);
}
}
public void UpdateEdgeInCoordMap(IEdge edge, IEdge newEdge)
{
var key1 = edge.From;
CoordMap[key1].Remove(edge);
var key2 = newEdge.From;
if (!CoordMap.ContainsKey(key2)) CoordMap[key2] = new();
CoordMap[key2].Add(newEdge);
}
public void UpdateEdgeInReverseCoordMap(IEdge edge, IEdge newEdge)
{
var key1 = edge.To;
ReverseCoordMap[key1].Remove(edge);
var key2 = newEdge.To;
if (!ReverseCoordMap.ContainsKey(key2)) ReverseCoordMap[key2] = new();
ReverseCoordMap[key2].Add(newEdge);
}
public Path PathFromEdgeMap(EdgeMap edgeMap) =>
edgeMap.Keys.OrderBy(i => i).SelectMany(i => edgeMap[i]).ToList();
}
`
Metadata
Metadata
Assignees
Labels
No labels