@@ -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 ;
0 commit comments