Skip to content

Commit 9abef96

Browse files
committed
update(PDF-2231): Address PR Review Feedbacks
1 parent 6c3a9c9 commit 9abef96

2 files changed

Lines changed: 94 additions & 6 deletions

File tree

IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,23 @@ public void CreateMultiFrameTiffStream_Empty_Sequence_Throws()
676676
act.Should().Throw<ArgumentException>();
677677
}
678678

679+
[FactWithAutomaticDisplayName]
680+
public void CreateMultiFrameTiffBytes_PreservesResolution_FromPixelsPerMeterSource()
681+
{
682+
const double dpi = 300d;
683+
double pixelsPerMetre = dpi / 0.0254d; // 300 DPI expressed in pixels per metre
684+
685+
using var page = MakeBitmapWithResolution(220, 300, new Rgb24(40, 90, 160),
686+
pixelsPerMetre, PixelResolutionUnit.PixelsPerMeter);
687+
688+
byte[] tiff = AnyBitmap.CreateMultiFrameTiffBytes(new[] { page });
689+
var pages = ReadTiffDirectories(tiff);
690+
691+
pages.Should().ContainSingle();
692+
ToDotsPerInch(pages[0].XResolution, pages[0].ResolutionUnit)
693+
.Should().BeApproximately(dpi, 2d);
694+
}
695+
679696
[FactWithAutomaticDisplayName]
680697
public void CreateMultiFrameTiff_Preserves_Rgb24_Pixels()
681698
{
@@ -707,12 +724,68 @@ public void CreateMultiFrameTiff_Preserves_Rgb24_Pixels()
707724
}
708725
}
709726

727+
[TheoryWithAutomaticDisplayName]
728+
[InlineData("Rgb24")]
729+
[InlineData("Bgr24")]
730+
[InlineData("Rgba32")]
731+
[InlineData("Bgra32")]
732+
[InlineData("Abgr32")]
733+
[InlineData("Argb32")]
734+
public void CreateMultiFrameTiff_PreservesColors_ForAllPixelFormats(string pixelFormat)
735+
{
736+
const byte r = 10, g = 120, b = 240;
737+
using var bmp = MakeSolidBitmapOfFormat(pixelFormat, 64, 48, r, g, b);
738+
739+
using var result = AnyBitmap.CreateMultiFrameTiff(new[] { bmp });
740+
741+
result.Width.Should().Be(64);
742+
result.Height.Should().Be(48);
743+
744+
foreach (var (x, y) in new[] { (0, 0), (63, 0), (0, 47), (32, 24), (63, 47) })
745+
{
746+
var px = result.GetPixel(x, y);
747+
px.R.Should().Be(r, $"R at ({x},{y}) for {pixelFormat}");
748+
px.G.Should().Be(g, $"G at ({x},{y}) for {pixelFormat}");
749+
px.B.Should().Be(b, $"B at ({x},{y}) for {pixelFormat}");
750+
}
751+
}
752+
753+
/// <summary>
754+
/// Builds a solid <see cref="AnyBitmap"/> whose backing image uses the requested
755+
/// ImageSharp pixel format. The colour is given in logical R,G,B order regardless of
756+
/// the format's in-memory byte layout. The image is force-loaded so the original
757+
/// pixel format (not a re-encoded copy) reaches the TIFF writer.
758+
/// </summary>
759+
private static AnyBitmap MakeSolidBitmapOfFormat(string format, int width, int height, byte r, byte g, byte b)
760+
{
761+
Image image = format switch
762+
{
763+
"Rgb24" => new Image<Rgb24>(width, height, new Rgb24(r, g, b)),
764+
"Bgr24" => new Image<Bgr24>(width, height, new Bgr24(r, g, b)),
765+
"Rgba32" => new Image<Rgba32>(width, height, new Rgba32(r, g, b, 255)),
766+
"Bgra32" => new Image<Bgra32>(width, height, new Bgra32(r, g, b, 255)),
767+
"Abgr32" => new Image<Abgr32>(width, height, new Abgr32(r, g, b, 255)),
768+
"Argb32" => new Image<Argb32>(width, height, new Argb32(r, g, b, 255)),
769+
_ => throw new ArgumentOutOfRangeException(nameof(format), format, "Unsupported pixel format")
770+
};
771+
772+
var bitmap = (AnyBitmap)image;
773+
_ = bitmap.Width; // materialise so the original pixel format reaches the writer
774+
return bitmap;
775+
}
776+
710777
private static AnyBitmap CreateSolidBitmap(int width, int height, Rgb24 color, int dpi)
778+
{
779+
return MakeBitmapWithResolution(width, height, color, dpi, PixelResolutionUnit.PixelsPerInch);
780+
}
781+
782+
private static AnyBitmap MakeBitmapWithResolution(int width, int height, Rgb24 color,
783+
double resolution, PixelResolutionUnit unit)
711784
{
712785
var image = new SixLabors.ImageSharp.Image<Rgb24>(width, height, color);
713-
image.Metadata.HorizontalResolution = dpi;
714-
image.Metadata.VerticalResolution = dpi;
715-
image.Metadata.ResolutionUnits = PixelResolutionUnit.PixelsPerInch;
786+
image.Metadata.HorizontalResolution = resolution;
787+
image.Metadata.VerticalResolution = resolution;
788+
image.Metadata.ResolutionUnits = unit;
716789
return image;
717790
}
718791

IronSoftware.Drawing/IronSoftware.Drawing.Common/AnyBitmap.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3420,10 +3420,20 @@ private static void InternalSaveAsMultiPageTiff(IEnumerable<Image> images, Strea
34203420
}
34213421
break;
34223422
case Image<Abgr32> imageAsFormat:
3423-
imageAsFormat.CopyPixelDataTo(buffer);
3423+
{
3424+
// 4 bytes/pixel, but the bytes are A,B,G,R. The wrong sample
3425+
// order for PHOTOMETRIC.RGB (which expects R,G,B,A). Convert to
3426+
// Rgba32 so the channels are not written swapped.
3427+
using var rgba = imageAsFormat.CloneAs<Rgba32>();
3428+
rgba.CopyPixelDataTo(buffer);
3429+
}
34243430
break;
34253431
case Image<Argb32> imageAsFormat:
3426-
imageAsFormat.CopyPixelDataTo(buffer);
3432+
{
3433+
// Bytes are A,R,G,B; convert to Rgba32 for correct channel order.
3434+
using var rgba = imageAsFormat.CloneAs<Rgba32>();
3435+
rgba.CopyPixelDataTo(buffer);
3436+
}
34273437
break;
34283438
case Image<Bgr24> imageAsFormat:
34293439
{
@@ -3435,7 +3445,12 @@ private static void InternalSaveAsMultiPageTiff(IEnumerable<Image> images, Strea
34353445
}
34363446
break;
34373447
case Image<Bgra32> imageAsFormat:
3438-
imageAsFormat.CopyPixelDataTo(buffer);
3448+
{
3449+
// Bytes are B,G,R,A; convert to Rgba32 so they are not written
3450+
// channel-swapped under PHOTOMETRIC.RGB.
3451+
using var rgba = imageAsFormat.CloneAs<Rgba32>();
3452+
rgba.CopyPixelDataTo(buffer);
3453+
}
34393454
break;
34403455
default:
34413456
(image as Image<Rgba32>).CopyPixelDataTo(buffer);

0 commit comments

Comments
 (0)