forked from opap-jp/StoryboardTrain
/
PsdCropper.cs
190 lines (149 loc) · 6.83 KB
/
PsdCropper.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PhotoshopFile;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace StoryboardTrain.Core
{
public class PsdCropper
{
public PsdFile BasePsd { get; set; }
public PsdCropper(PsdFile basePsd)
{
this.BasePsd = basePsd;
}
/// <summary>
/// 指定した位置とサイズで切り抜いたPSDを生成する
/// </summary>
/// <param name="cropRect">切り抜く位置とサイズ</param>
/// <returns>切り抜かれたPSDファイル</returns>
public PsdFile GenerateCroppedPsd(Rectangle cropRect)
{
var newPsd = ClonePsdFile(BasePsd);
foreach (var layer in newPsd.Layers)
{
CropLayer(layer, cropRect);
}
CropLayer(newPsd.BaseLayer, cropRect);
newPsd.ColumnCount = cropRect.Width;
newPsd.RowCount = cropRect.Height;
newPsd.PrepareSave();
return newPsd;
}
/// <summary>
/// PsdFileオブジェクトを複製する
/// </summary>
/// <param name="psd">複製元</param>
/// <returns></returns>
private static PsdFile ClonePsdFile(PsdFile psd)
{
using (MemoryStream ms = new MemoryStream()) {
psd.PrepareSave();
psd.Save(ms, Encoding.UTF8);
ms.Seek(0, SeekOrigin.Begin);
var newPsd = new PsdFile(ms, new LoadContext());
return newPsd;
}
}
/// <summary>
/// レイヤーを指定した位置とサイズで切り抜く
/// </summary>
/// <param name="layer"></param>
/// <param name="cropRect">画像全体の左上を基準とした、切り出す位置とサイズ</param>
private static void CropLayer(Layer layer, Rectangle cropRect)
{
var originalRect = layer.Rect;
var translatedRect = new Rectangle(originalRect.X - cropRect.X, originalRect.Y - cropRect.Y, originalRect.Width, originalRect.Height);
if (layer.AdditionalInfo.OfType<LayerSectionInfo>().Any())
{
//レイヤーグループ(レイヤーセット)の場合は処理しない
return;
}
else if (Rectangle.Intersect(cropRect, originalRect) == Rectangle.Empty)
{
// 完全にはみ出している場合
foreach (var c in layer.Channels)
{
CropChannel(c, originalRect.Size, Rectangle.Empty);
}
layer.Rect = Rectangle.Empty;
}
else
{
// 一部がはみ出している場合
// はみ出し部分を計算する
int overflowLeft = Math.Max(0 - translatedRect.X, 0);
int overflowRight = Math.Max(translatedRect.X + translatedRect.Width - cropRect.Width, 0);
int overflowTop = Math.Max(0 - translatedRect.Y, 0);
int overflowBottom = Math.Max(translatedRect.Y + translatedRect.Height - cropRect.Height, 0);
// はみ出し部分を削った場合の位置とサイズを求める
var cropLayerRect = new Rectangle(
overflowLeft,
overflowTop,
translatedRect.Width - overflowLeft - overflowRight,
translatedRect.Height - overflowTop - overflowBottom
);
// はみ出し部分のデータを削除する
foreach (var c in layer.Channels)
{
CropChannel(c, originalRect.Size, cropLayerRect);
}
// はみ出し部分の削除後の位置補正
var newRect = new Rectangle(translatedRect.X + cropLayerRect.X, translatedRect.Y + cropLayerRect.Y, cropLayerRect.Width, cropLayerRect.Height);
layer.Rect = newRect;
}
if (layer.Rect.Size == Size.Empty)
{
//空のレイヤーか、完全にはみ出したレイヤーの場合
// CLIP STUDIO (Version 1.7.2)との相性問題の暫定的な回避策
// サイズゼロのレイヤーを出力すると、それより前面のレイヤーで正常に表示されない(空白になる等)不具合が発生するため、
// 暫定的な回避策として、1x1ピクセルの透明ピクセルを含むレイヤーを出力する。
layer.Rect = new Rectangle(-1, -1, 1, 1);
foreach (var c in layer.Channels)
{
c.ImageData = new byte[] { 0 };
c.ImageDataRaw = null;
}
}
}
/// <summary>
/// チャンネルデータを指定したサイズで切り抜く
/// </summary>
/// <param name="c">チャンネル</param>
/// <param name="originalLayerSize">変更前のレイヤーサイズ</param>
/// <param name="croppingRect">レイヤーデータの左上を基準とした、切り出す位置とサイズ</param>
private static void CropChannel(Channel c, Size originalLayerSize, Rectangle croppingRect)
{
if (croppingRect.Size == originalLayerSize)
{
return;
}
if (croppingRect.Width == 0 || croppingRect.Height == 0)
{
c.ImageData = new byte[0];
return;
}
if (croppingRect.Width > originalLayerSize.Width || croppingRect.Height > originalLayerSize.Height)
{
throw new InvalidOperationException();
}
byte[] originalData = c.ImageData;
int newDataLength = croppingRect.Width * croppingRect.Height;
byte[] newData = new byte[newDataLength];
int originalDataIndex = croppingRect.Y * originalLayerSize.Width + croppingRect.X;
int newDataIndex = 0;
while (newDataIndex < newDataLength)
{
Array.Copy(originalData, originalDataIndex, newData, newDataIndex, croppingRect.Width);
originalDataIndex += originalLayerSize.Width;
newDataIndex += croppingRect.Width;
}
c.ImageData = newData;
c.ImageDataRaw = null; //圧縮処理を再実行させるためにnullを設定
}
}
}