Skip to content

Commit

Permalink
pimg to psd #109
Browse files Browse the repository at this point in the history
  • Loading branch information
UlyssesWu committed May 30, 2023
1 parent d72ab39 commit b486f4c
Show file tree
Hide file tree
Showing 4 changed files with 234 additions and 14 deletions.
231 changes: 220 additions & 11 deletions FreeMote.Plugins/Shells/PsdShell.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Text;
Expand All @@ -19,6 +20,8 @@ namespace FreeMote.Plugins.Shells
[ExportMetadata("Comment", "PSD export.")]
class PsdShell : IPsbShell
{
public byte[] Signature { get; } = {(byte) '8', (byte) 'B', (byte) 'P', (byte) 'S'};

public int Width { get; set; } = -1;
public int Height { get; set; } = -1;

Expand Down Expand Up @@ -65,25 +68,30 @@ public MemoryStream ToShell(Stream stream, Dictionary<string, object> context =
throw new BadImageFormatException("Not a valid PSB file.");
}

if (psb.Type == PsbType.Pimg)
{
return ConvertPImgToPsd(psb);
}

EmtPainter painter = new EmtPainter(psb);

if (TryGetCanvasSize && painter.Resources.Count > 0)
{
if (psb.TryGetCanvasSize(out var cw, out var ch))
{
Width = (int)(cw * 1.8f);
Height = (int)(ch * 1.4f);
Width = (int) (cw * 1.8f);
Height = (int) (ch * 1.4f);
}
else
{
//Try get from painter, not accurate if the PSB center is not (0,0)
Width = (int)(painter.Resources.Max(r => r.OriginX + r.Width / 2.0f) -
painter.Resources.Min(r => r.OriginX - r.Width / 2.0f));
Height = (int)(painter.Resources.Max(r => r.OriginY + r.Height / 2.0f) -
painter.Resources.Min(r => r.OriginY - r.Height / 2.0f));
Width = (int) (painter.Resources.Max(r => r.OriginX + r.Width / 2.0f) -
painter.Resources.Min(r => r.OriginX - r.Width / 2.0f));
Height = (int) (painter.Resources.Max(r => r.OriginY + r.Height / 2.0f) -
painter.Resources.Min(r => r.OriginY - r.Height / 2.0f));

Width = (int)(Width * 1.4f);
Height = (int)(Height * 1.4f);
Width = (int) (Width * 1.4f);
Height = (int) (Height * 1.4f);
}

if (context != null)
Expand All @@ -106,6 +114,139 @@ public MemoryStream ToShell(Stream stream, Dictionary<string, object> context =
return ms;
}

private MemoryStream ConvertPImgToPsd(PSB psb)
{
var width = psb.Objects["width"].GetInt();
var height = psb.Objects["height"].GetInt();

PsdFile psd = new PsdFile
{
Width = width,
Height = height,
Resolution = new ResolutionInfo
{
HeightDisplayUnit = ResolutionInfo.Unit.Centimeters,
WidthDisplayUnit = ResolutionInfo.Unit.Centimeters,
HResDisplayUnit = ResolutionInfo.ResUnit.PxPerInch,
VResDisplayUnit = ResolutionInfo.ResUnit.PxPerInch,
HDpi = new UFixed16_16(0, 350),
VDpi = new UFixed16_16(0, 350)
},
ImageCompression = ImageCompression.Rle
};

psd.ImageResources.Add(new XmpResource("") {XmpMetaString = Resources.Xmp});
psd.BaseLayer.SetBitmap(new Bitmap(width, height, PixelFormat.Format32bppArgb),
ImageReplaceOption.KeepCenter, psd.ImageCompression);

var images = psb.CollectResources<ImageMetadata>();

//layer type: 0 = image, 2 = folder
List<ILayer> layers = new List<ILayer>();
List<GroupLayer> groupLayers = new List<GroupLayer>();

var layerObjects = (PsbList) psb.Objects["layers"];

GroupLayer CreateGroupLayer(PsbDictionary lObj, ILayer child = null)
{
var layerId = lObj["layer_id"].GetInt();
var existedGroup = groupLayers.FirstOrDefault(l => l.LayerId == layerId);
if (existedGroup != null)
{
if (child != null)
{
existedGroup.Children.Add(child);
child.Parent = existedGroup;
}
return null;
}

var groupLayer = new GroupLayer {Object = lObj, LayerId = layerId, Name = lObj["name"].ToString() };
if (child != null)
{
groupLayer.Children.Add(child);
child.Parent = groupLayer;
}

groupLayers.Add(groupLayer);
if (lObj["group_layer_id"] is PsbNumber groupLayerId)
{
var parent1 = groupLayers.FirstOrDefault(g => g.LayerId == groupLayerId.IntValue);
if (parent1 != null)
{
parent1.Children.Add(groupLayer);
groupLayer.Parent = parent1;
return null;
}

var parent = layerObjects.FirstOrDefault(l =>
l is PsbDictionary lo && lo["layer_type"].GetInt() == 2 && lo["layer_id"].GetInt() == groupLayerId.GetInt());
if (parent is PsbDictionary parentObj)
{
return CreateGroupLayer(parentObj, groupLayer);
}
}

return groupLayer;
}

foreach (var layer in layerObjects)
{
if (layer is not PsbDictionary layerObj)
{
continue;
}

if (layerObj["layer_type"].GetInt() == 2) // layer group
{
var g = CreateGroupLayer(layerObj);
if (g != null)
{
layers.Add(g);
}
}
else
{
var layerId = layerObj["layer_id"].GetInt();
var imageLayer = new ImageLayer
{Object = layerObj, Name = layerObj["name"].ToString(), LayerId = layerId, ImageMetadata = images.FirstOrDefault(md => md.Index == (uint) layerId)};
if (imageLayer.ImageMetadata == null && layerObj.TryGetValue("same_image", out var sameImageId))
{
var sameImage = sameImageId.GetInt();
imageLayer.ImageMetadata = images.FirstOrDefault(md => md.Index == (uint) sameImage);
}
if (layerObj["group_layer_id"] is PsbNumber groupLayerId)
{
var parent = layerObjects.FirstOrDefault(l =>
l is PsbDictionary lo && lo["layer_type"].GetInt() == 2 && lo["layer_id"].GetInt() == groupLayerId.GetInt());
if (parent is PsbDictionary parentObj)
{
var g = CreateGroupLayer(parentObj, imageLayer);
if (g != null)
{
layers.Add(g);
}
}
}
else
{
layers.Add(imageLayer);
}
}
}

foreach (var layer in layers)
{
layer.CreateLayers(psd);
}

psd.Layers.Reverse();

var ms = new MemoryStream();
psd.Save(ms, Encoding.UTF8);
return ms;
}

private PsdFile ConvertToPsd(EmtPainter painter, int width, int height)
{
float offsetX = 0;
Expand Down Expand Up @@ -170,7 +311,7 @@ private PsdFile ConvertToPsd(EmtPainter painter, int width, int height)
}
else
{
layer.Opacity = (byte)(resMd.Opacity /10.0f * 255);
layer.Opacity = (byte) (resMd.Opacity / 10.0f * 255);
}

if (resMd.MotionName != currentGroup)
Expand Down Expand Up @@ -206,7 +347,7 @@ private Bitmap GenerateMarkText(string text, int width, int height, string fontN
{
Font drawFont = new Font(fontName, fontSize, FontStyle.Bold);
var size = PsbResHelper.MeasureString(text, drawFont);
Bitmap bmp = new Bitmap((int)Math.Ceiling(size.Width + 1), (int)Math.Ceiling(size.Height + 1), PixelFormat.Format32bppArgb);
Bitmap bmp = new Bitmap((int) Math.Ceiling(size.Width + 1), (int) Math.Ceiling(size.Height + 1), PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(bmp);
g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
StringFormat sf = StringFormat.GenericTypographic;
Expand All @@ -216,7 +357,75 @@ private Bitmap GenerateMarkText(string text, int width, int height, string fontN
g.Dispose();
return bmp;
}
}

public byte[] Signature { get; } = {(byte) '8', (byte) 'B', (byte) 'P', (byte) 'S'};
interface ILayer
{
public GroupLayer Parent { get; set; }
public int LayerId { get; }
PsbDictionary Object { get; }
public string Name { get; set; }

void CreateLayers(PsdFile psd);
}

[DebuggerDisplay("{Name,nq} ({LayerId})")]
class GroupLayer : ILayer
{
public GroupLayer Parent { get; set; }
public int LayerId { get; set; }
public PsbDictionary Object { get; set; }
public List<ILayer> Children { get; private set; } = new();
public string Name { get; set; }

public void CreateLayers(PsdFile psd)
{
var beginLayer = psd.MakeSectionLayers(Name, out var endLayer, false);
psd.Layers.Add(beginLayer);
foreach (var child in Children)
{
child.CreateLayers(psd);
}
psd.Layers.Add(endLayer);
}
}

[DebuggerDisplay("{Name,nq} ({LayerId})")]
class ImageLayer : ILayer
{
public GroupLayer Parent { get; set; }
public int LayerId { get; set; }
public PsbDictionary Object { get; set; }
public string Name { get; set; }
public ImageMetadata ImageMetadata { get; set; }

public void CreateLayers(PsdFile psd)
{
var md = ImageMetadata;
if (md == null)
{
var layerWidth = Object["width"].GetInt();
var layerHeight = Object["height"].GetInt();
var layerTop = Object["top"].GetInt();
var layerLeft = Object["left"].GetInt();
var emptyLayer = psd.MakeImageLayer(new Bitmap(layerWidth, layerHeight), Name, layerLeft, layerTop);
emptyLayer.Visible = Object["visible"].GetInt() != 0;
emptyLayer.Opacity = (byte) Object["opacity"].GetInt();
psd.Layers.Add(emptyLayer);
}
else
{
var imageLayer = psd.MakeImageLayer(md.ToImage(), Name, md.Left, md.Top);
//var idLayer = new RawLayerInfo("lyid");
//create 8 bytes array, first 4 is int value 4, second 4 is Id
//idLayer.Data = new byte[8];
//BitConverter.GetBytes(4).CopyTo(idLayer.Data, 0);
//BitConverter.GetBytes(LayerId).CopyTo(idLayer.Data, 4);
//imageLayer.AdditionalInfo.Add(idLayer);
imageLayer.Visible = md.Visible;
imageLayer.Opacity = (byte) md.Opacity;
psd.Layers.Add(imageLayer);
}
}
}
}
4 changes: 2 additions & 2 deletions FreeMote.Psb/EmtPainter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -243,14 +243,14 @@ void Travel(IPsbCollection collection, string motionName, (float x, float y, flo

if (baseLocation == null)
{
//Console.WriteLine($"Set coord: {coordTuple.x},{coordTuple.y},{coordTuple.z}");
Debug.WriteLine($"Set coord: {coordTuple.x},{coordTuple.y},{coordTuple.z} | {dic.Path}");
baseLocation = coordTuple;
}
else
{
var loc = baseLocation.Value;
baseLocation = (loc.x + coordTuple.x, loc.y + coordTuple.y, loc.z + coordTuple.z);
//Console.WriteLine($"Update coord: {loc.x},{loc.y},{loc.z} -> {baseLocation?.x},{baseLocation?.y},{baseLocation?.z}");
Debug.WriteLine($"Update coord: {loc.x},{loc.y},{loc.z} + {coordTuple.x},{coordTuple.y},{coordTuple.z} -> {baseLocation?.x},{baseLocation?.y},{baseLocation?.z} | {dic.Path}");
}
}

Expand Down
4 changes: 4 additions & 0 deletions FreeMote.Psb/Resources/ImageMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ public uint Index
public PsbString TypeString { get; set; }
public RectangleF Clip { get; set; }
public PsbResource Resource { get; set; }
/// <summary>
/// PIMG layer_type
/// </summary>
public int LayerType { get; set; }

/// <summary>
/// Pal
Expand Down
9 changes: 8 additions & 1 deletion FreeMote.Psb/Types/PimgType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ private static void FindPimgResources<T>(List<T> list, IPsbValue obj, bool deDup
continue;
}

var res = (ImageMetadata)(IResourceMetadata)list.FirstOrDefault(k => k.Name.StartsWith(layerId.ToString(), true, null));
var res =
(ImageMetadata) (IResourceMetadata) (list.FirstOrDefault(k => k.Name.StartsWith($"{layerId}.", true, null)) ??
list.FirstOrDefault(k => k.Name.StartsWith($"{layerId}", true, null)));
if (res == null)
{
continue;
Expand Down Expand Up @@ -121,6 +123,11 @@ private static void FindPimgResources<T>(List<T> list, IPsbValue obj, bool deDup
{
res.Label = nn.Value;
}

if (dic["layer_type"] is PsbNumber lt)
{
res.LayerType = lt.IntValue;
}
}
}
}
Expand Down

0 comments on commit b486f4c

Please sign in to comment.