Skip to content

Commit

Permalink
Feature/image buffer (#95)
Browse files Browse the repository at this point in the history
* #93 (#94)

Added the result byte array as parameter for polling the arrays.

* update write to buffer API

* fix spacing

---------

Co-authored-by: Hafman08 <Hafman08@users.noreply.github.com>
  • Loading branch information
Modest-as and Hafman08 authored Sep 4, 2023
1 parent 67fdf5c commit 03191d3
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 21 deletions.
2 changes: 1 addition & 1 deletion src/Docnet.Core/Converters/IImageBytesConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ public interface IImageBytesConverter
/// Input is in B-G-R-A format.
/// </summary>
/// <param name="bytes">Image bytes.</param>
byte[] Convert(byte[] bytes);
void Convert(byte[] bytes);
}
}
4 changes: 1 addition & 3 deletions src/Docnet.Core/Converters/NaiveTransparencyRemover.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public NaiveTransparencyRemover(byte red, byte green, byte blue)
/// </summary>
/// <param name="bytes">Image bytes.</param>
/// <returns>Same B-G-R-A array with alpha filled with white color.</returns>
public byte[] Convert(byte[] bytes)
public void Convert(byte[] bytes)
{
for (var i = 0; i < bytes.Length / 4; i++)
{
Expand All @@ -39,8 +39,6 @@ public byte[] Convert(byte[] bytes)
bytes[j + 2] = (byte)((red * alpha + _backgroundRed * (255 - alpha)) >> 8);
bytes[j + 3] = byte.MaxValue;
}

return bytes;
}
}
}
20 changes: 20 additions & 0 deletions src/Docnet.Core/Readers/IPageReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,26 @@ public interface IPageReader : IDisposable
/// </summary>
byte[] GetImage(IImageBytesConverter converter, RenderFlags flags);

/// <summary>
/// Return a byte representation
/// of the page image.
/// Byte array is formatted as
/// B-G-R-A ordered list.
/// Use result if you create the array before
/// </summary>
void WriteImageToBuffer(RenderFlags flags, byte[] result);

/// <summary>
/// Return a byte representation
/// of the page image.
/// Byte array is formatted as
/// B-G-R-A ordered list. Then it
/// applies a predefined byte transformation
/// to modify the image.
/// Use result if you create the array before
/// </summary>
void WriteImageToBuffer(IImageBytesConverter converter, RenderFlags flags, byte[] result);

/// <summary>
/// Renders the page onto a device context.
/// </summary>
Expand Down
58 changes: 41 additions & 17 deletions src/Docnet.Core/Readers/PageReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,39 @@ private static int AdjustToRange(int coord, int range)
}

/// <inheritdoc />
public byte[] GetImage() => GetImage(0);
public byte[] GetImage() => WriteImageToBufferInternal(0);

/// <inheritdoc />
public byte[] GetImage(RenderFlags flags)
public byte[] GetImage(RenderFlags flags) => WriteImageToBufferInternal(flags);

/// <inheritdoc />
public byte[] GetImage(IImageBytesConverter converter) => GetImage(converter, 0);

/// <inheritdoc />
public byte[] GetImage(IImageBytesConverter converter, RenderFlags flags)
{
var bytes = WriteImageToBufferInternal(flags);

converter.Convert(bytes);

return bytes;
}

/// <inheritdoc />
public void WriteImageToBuffer(RenderFlags flags, byte[] result)
{
WriteImageToBufferInternal(flags, result: result);
}

/// <inheritdoc />
public void WriteImageToBuffer(IImageBytesConverter converter, RenderFlags flags, byte[] result)
{
WriteImageToBufferInternal(flags, result: result);

converter.Convert(result);
}

private byte[] WriteImageToBufferInternal(RenderFlags flags, byte[] result = null)
{
lock (DocLib.Lock)
{
Expand All @@ -195,8 +224,14 @@ public byte[] GetImage(RenderFlags flags)
}

var stride = fpdf_view.FPDFBitmapGetStride(bitmap);
var length = stride * height;

result = result ?? new byte[length];

var result = new byte[stride * height];
if (result.Length < length)
{
throw new DocnetException($"result array length should be greater or equal than {length}");
}

try
{
Expand Down Expand Up @@ -234,7 +269,7 @@ public byte[] GetImage(RenderFlags flags)

var buffer = fpdf_view.FPDFBitmapGetBuffer(bitmap);

Marshal.Copy(buffer, result, 0, result.Length);
Marshal.Copy(buffer, result, 0, length);
}
}
catch (Exception ex)
Expand All @@ -245,20 +280,9 @@ public byte[] GetImage(RenderFlags flags)
{
fpdf_view.FPDFBitmapDestroy(bitmap);
}

return result;
}
}

/// <inheritdoc />
public byte[] GetImage(IImageBytesConverter converter) => GetImage(converter, 0);

/// <inheritdoc />
public byte[] GetImage(IImageBytesConverter converter, RenderFlags flags)
{
var bytes = GetImage(flags);

return converter.Convert(bytes);
return result;
}

public void RenderDeviceContext(IntPtr deviceContext, Rectangle bounds) =>
Expand Down Expand Up @@ -295,4 +319,4 @@ public void Dispose()
}
}
}
}
}
70 changes: 70 additions & 0 deletions src/Docnet.Tests.Integration/PageReaderTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.Linq;
using System.Threading.Tasks;
using Docnet.Core.Converters;
Expand Down Expand Up @@ -339,6 +340,75 @@ public void GetImage_WhenAnnotationsRendered_ShouldHaveDifferentBytes(string fil
});
}

[Theory]
[InlineData(Input.FromFile, "Docs/simple_3.pdf", null, 1)]
[InlineData(Input.FromFile, "Docs/simple_0.pdf", null, 18)]
[InlineData(Input.FromFile, "Docs/protected_0.pdf", "password", 0)]
[InlineData(Input.FromBytes, "Docs/simple_3.pdf", null, 1)]
[InlineData(Input.FromBytes, "Docs/simple_0.pdf", null, 18)]
[InlineData(Input.FromBytes, "Docs/protected_0.pdf", "password", 0)]
public void WriteImageToBufferWithResultByteAndNoRenderFlags_WhenCalled_ShouldReturnNonZeroRawByteArray(Input type, string filePath, string password, int pageIndex)
{
ExecuteForDocument(type, filePath, password, 1, pageIndex, pageReader =>
{
var height = pageReader.GetPageHeight();
var stride = 4 * pageReader.GetPageWidth(); //4 is for B-G-R-A format
var bytes = new byte[stride * height];
pageReader.WriteImageToBuffer(0, bytes);
Assert.True(bytes.Length > 0);
Assert.NotEmpty(bytes.Where(x => x != 0));
});
}

[Theory]
[InlineData(Input.FromFile, "Docs/simple_3.pdf", null, 1)]
[InlineData(Input.FromFile, "Docs/simple_0.pdf", null, 18)]
[InlineData(Input.FromFile, "Docs/protected_0.pdf", "password", 0)]
[InlineData(Input.FromBytes, "Docs/simple_3.pdf", null, 1)]
[InlineData(Input.FromBytes, "Docs/simple_0.pdf", null, 18)]
[InlineData(Input.FromBytes, "Docs/protected_0.pdf", "password", 0)]
public void WriteImageToBufferWithResultByteWithLowerLengthAndNoRenderFlags_WhenCalled_ShouldReturnException(Input type, string filePath, string password, int pageIndex)
{
ExecuteForDocument(type, filePath, password, 1, pageIndex, pageReader =>
{
var height = pageReader.GetPageHeight();
var stride = 4 * pageReader.GetPageWidth(); //4 is for B-G-R-A format
var bytes = new byte[stride * height - 1];
Assert.Throws<DocnetException>(() => pageReader.WriteImageToBuffer(0, bytes));
});
}

[Theory]
[InlineData(Input.FromFile, "Docs/simple_3.pdf", null, 1)]
[InlineData(Input.FromFile, "Docs/simple_0.pdf", null, 18)]
[InlineData(Input.FromFile, "Docs/protected_0.pdf", "password", 0)]
[InlineData(Input.FromBytes, "Docs/simple_3.pdf", null, 1)]
[InlineData(Input.FromBytes, "Docs/simple_0.pdf", null, 18)]
[InlineData(Input.FromBytes, "Docs/protected_0.pdf", "password", 0)]
public void WriteImageToBufferWithResultFromArrayPoolNoRenderFlags_WhenCalled_ShouldReturnNonZeroRawByteArray(Input type, string filePath, string password, int pageIndex)
{
ExecuteForDocument(type, filePath, password, 1, pageIndex, pageReader =>
{
var height = pageReader.GetPageHeight();
var stride = 4 * pageReader.GetPageWidth(); //4 is for B-G-R-A format
var arrayPool = ArrayPool<byte>.Shared;
var bytes = arrayPool.Rent(stride * height);
pageReader.WriteImageToBuffer(0, bytes);
Assert.True(bytes.Length > 0);
Assert.NotEmpty(bytes.Where(x => x != 0));
arrayPool.Return(bytes);
});
}

private static int GetNonZeroByteCount(Input type, string filePath, LibFixture fixture)
{
using (var reader = fixture.GetDocReader(type, filePath, null, 1000, 1000))
Expand Down

0 comments on commit 03191d3

Please sign in to comment.