Skip to content

Commit 563130a

Browse files
committed
feat: improve caching and bitmap creation by enhancing cache key generation and optimizing image processing
1 parent 9c6609a commit 563130a

File tree

3 files changed

+65
-32
lines changed

3 files changed

+65
-32
lines changed

KuchiPaku.Psd/KuchiPaku.Psd.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<ImplicitUsings>enable</ImplicitUsings>
88
<Nullable>enable</Nullable>
99
<AccelerateBuildsInVisualStudio>true</AccelerateBuildsInVisualStudio>
10+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
1011
</PropertyGroup>
1112

1213
<ItemGroup>

KuchiPaku.Psd/PsdUtil.cs

Lines changed: 62 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,9 @@ await Parallel.ForEachAsync(
150150
},
151151
async (layer, ct) =>
152152
{
153-
string cacheKey = layer.Cid;
154-
var layerBitmap = await GetOrCreateBitmapAsync(cacheKey, layer, ReadImageAsync);
153+
string cacheKey = $"{layer.Cid}_{layer.Name}_{layer.Identifier}";
154+
var layerBitmap = await GetOrCreateBitmapAsync(cacheKey, layer, ReadImageAsync)
155+
.ConfigureAwait(false);
155156

156157
// ロックの取得を試みる(非同期版)
157158
await _resultLock.WaitAsync(ct).ConfigureAwait(false);
@@ -308,7 +309,7 @@ await CombineLayerRecursiveWithScaleAsync(tree, g, enabledLayers, scaleRatio)
308309
public static async Task<Bitmap> CreateImageFromLayerAsync(YmmPsdLayer layer)
309310
{
310311
// キャッシュキーを生成
311-
string cacheKey = layer.Cid;
312+
var cacheKey = $"{layer.Cid}_{layer.Identifier}";
312313

313314
// キャッシュから取得を試みる
314315
if (
@@ -331,7 +332,7 @@ public static async Task<Bitmap> CreateImageFromLayerAsync(YmmPsdLayer layer)
331332

332333
static async Task<byte[]> ReadImageAsync(YmmPsdLayer layer)
333334
{
334-
string cacheKey = $"raw_{layer.Cid}";
335+
var cacheKey = $"raw_{layer.Cid}_{layer.Identifier}";
335336

336337
// メモリ内キャッシュを先にチェック
337338
if (_rawDataCache.TryGetValue(cacheKey, out var cachedData))
@@ -348,9 +349,7 @@ static async Task<byte[]> ReadImageAsync(YmmPsdLayer layer)
348349
return cachedData;
349350
}
350351

351-
var sw = Stopwatch.StartNew();
352352
byte[] pixelData = await Task.Run(() => layer.Image.Read()).ConfigureAwait(false);
353-
sw.Stop();
354353

355354
// キャッシュに保存
356355
_rawDataCache[cacheKey] = pixelData;
@@ -432,61 +431,94 @@ static IEnumerable<YmmPsdLayer> FlattenLayersInDrawOrder(IEnumerable<YmmPsdLayer
432431
}
433432
}
434433

435-
static Bitmap CreateBitmapFromPixelData(byte[] pixelData, int width, int height)
434+
[SuppressMessage("Usage", "SMA0040:Missing Using Statement", Justification = "<保留中>")]
435+
static unsafe Bitmap CreateBitmapFromPixelData(byte[] pixelData, int width, int height)
436436
{
437437
// ピクセルデータのサイズが大きい場合は解像度を下げる
438-
const int maxPixels = 4000 * 3000; // 適切なしきい値
438+
const int maxPixels = 4000 * 4000; // しきい値を増やす
439439

440440
if (width * height > maxPixels)
441441
{
442442
// 大きすぎる画像は解像度を下げる
443443
double scale = Math.Sqrt((double)maxPixels / (width * height));
444-
int newWidth = (int)(width * scale);
445-
int newHeight = (int)(height * scale);
444+
int newWidth = Math.Max(1, (int)(width * scale));
445+
int newHeight = Math.Max(1, (int)(height * scale));
446446

447447
Debug.WriteLine(
448448
$"Downscaling large image from {width}x{height} to {newWidth}x{newHeight}"
449449
);
450450

451451
var scaledBitmap = new Bitmap(newWidth, newHeight, PixelFormat.Format32bppArgb);
452+
fixed (byte* pixelDataPtr = pixelData)
453+
{
454+
using var originalBitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
455+
var rect = new Rectangle(0, 0, width, height);
456+
var data = originalBitmap.LockBits(
457+
rect,
458+
ImageLockMode.WriteOnly,
459+
originalBitmap.PixelFormat
460+
);
452461

453-
// 元のビットマップを生成
454-
var originalBitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
455-
var rect = new Rectangle(0, 0, width, height);
456-
var data = originalBitmap.LockBits(
457-
rect,
458-
ImageLockMode.WriteOnly,
459-
originalBitmap.PixelFormat
460-
);
461-
Marshal.Copy(
462-
pixelData,
463-
0,
464-
data.Scan0,
465-
Math.Min(pixelData.Length, Math.Abs(data.Stride) * height)
466-
);
467-
originalBitmap.UnlockBits(data);
462+
try
463+
{
464+
// ストライドを考慮して各行を個別にコピー
465+
for (int y = 0; y < height; y++)
466+
{
467+
int sourceOffset = y * width * 4; // 4バイト/ピクセル (ARGB)
468+
int destOffset = y * data.Stride;
469+
470+
// 1行分のデータをコピー
471+
if (sourceOffset + width * 4 <= pixelData.Length)
472+
{
473+
Marshal.Copy(
474+
pixelData,
475+
sourceOffset,
476+
data.Scan0 + destOffset,
477+
width * 4
478+
);
479+
}
480+
}
481+
}
482+
finally
483+
{
484+
originalBitmap.UnlockBits(data);
485+
}
468486

469-
// 縮小
470-
using (Graphics g = Graphics.FromImage(scaledBitmap))
471-
{
487+
// 縮小
488+
using Graphics g = Graphics.FromImage(scaledBitmap);
489+
g.Clear(Color.Transparent); // 透明背景を明示的に設定
472490
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
473-
g.DrawImage(originalBitmap, 0, 0, newWidth, newHeight);
491+
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
492+
493+
// ImageAttributesを使用して透明度を保持
494+
using var imageAttr = new ImageAttributes();
495+
imageAttr.SetColorMatrix(new ColorMatrix { Matrix33 = 1.0f }); // アルファ値を保持
496+
497+
g.DrawImage(
498+
originalBitmap,
499+
new Rectangle(0, 0, newWidth, newHeight),
500+
0, 0, width, height,
501+
GraphicsUnit.Pixel,
502+
imageAttr
503+
);
474504
}
475505

476-
originalBitmap.Dispose(); // 元の大きなビットマップを破棄
477506
return scaledBitmap;
478507
}
479508

480509
// 通常サイズの場合は既存のコード
481510
var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
482511
var rectNormal = new Rectangle(0, 0, width, height);
483512
var dataNormal = bitmap.LockBits(rectNormal, ImageLockMode.WriteOnly, bitmap.PixelFormat);
513+
514+
// 高速コピー
484515
Marshal.Copy(
485516
pixelData,
486517
0,
487518
dataNormal.Scan0,
488519
Math.Min(pixelData.Length, Math.Abs(dataNormal.Stride) * height)
489520
);
521+
490522
bitmap.UnlockBits(dataNormal);
491523

492524
return bitmap;

KuchiPaku.Psd/YmmPsdLayer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ LayerRecordAndImage Layer
2323
public bool IsVisible { get; set; }
2424
= (Layer.Record.LayerFlags & LayerFlags.Visible) != LayerFlags.Visible;
2525

26-
//public bool IsOverrideLayer { get; set; }
27-
2826
public string Name => Layer.Record
2927
.AdditionalLayerInformations
3028
.OfType<UnicodeLayerName>()
@@ -35,4 +33,6 @@ LayerRecordAndImage Layer
3533
public bool IsDivider => Layer.IsDivider();
3634

3735
public bool IsNormalLayer => !Layer.IsFolderLike();
36+
37+
public string Identifier => $"{Cid}_{Name}_{Image.Width}x{Image.Height}";
3838
}

0 commit comments

Comments
 (0)