Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drawing part of an image onto another image throws ImageProcessingException or ArgumentOutOfRangeException #2447

Closed
4 tasks done
Visual-Vincent opened this issue May 4, 2023 · 3 comments · Fixed by #2474
Labels

Comments

@Visual-Vincent
Copy link
Sponsor

Prerequisites

  • I have written a descriptive issue title
  • I have verified that I am running the latest version of ImageSharp
  • I have verified if the problem exist in both DEBUG and RELEASE mode
  • I have searched open and closed issues to ensure it has not already been reported

ImageSharp version

3.0.1, 3.0.2-alpha.0.15

Other ImageSharp packages and versions

(None)

Environment (Operating system, version and so on)

Windows 7 SP1 x64, Windows 11 21H2 x64

.NET Framework version

.NET 6.0

Description

The DrawImage(Image, Point, Rectangle, float) method was added in 3.0.0 after #2112 and #2102 requested a method which could draw part of one image onto another. However, the method does not seem to work as intended as more often than not it throws one of two exceptions, or simply fails to produce the correct image.

Steps to Reproduce

The following code tries to draw a 32 x 32 piece of a 512 x 512 image onto a 100 x 100 image.

ImageProcessingException
using var src = Image.Load<Rgba32>("numbergrid.png");
using var dst = new Image<Rgba32>(100, 100, new Rgba32(0, 255, 255));

dst.Mutate(c => {
    c.DrawImage(src, new Point(64, 10), new Rectangle(32, 32, 32, 32), 1.0f);
});

dst.SaveAsPng("result.png");

Expected result:

Expected result

Exception:

SixLabors.ImageSharp.ImageProcessingException
  HResult=0x80131500
  Message=Cannot draw image because the source image does not overlap the target image.
  Source=SixLabors.ImageSharp
  StackTrace:
   at SixLabors.ImageSharp.Processing.Processors.Drawing.DrawImageProcessor`2.OnFrameApply(ImageFrame`1 source)
   at SixLabors.ImageSharp.Processing.Processors.ImageProcessor`1.Apply(ImageFrame`1 source)
   at SixLabors.ImageSharp.Processing.Processors.ImageProcessor`1.SixLabors.ImageSharp.Processing.Processors.IImageProcessor<TPixel>.Execute()
   at SixLabors.ImageSharp.Processing.DefaultImageProcessorContext`1.ApplyProcessor(IImageProcessor processor, Rectangle rectangle)
   at SixLabors.ImageSharp.Processing.DrawImageExtensions.DrawImage(IImageProcessingContext source, Image image, Point location, Rectangle rectangle, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, Single opacity)
   at SixLabors.ImageSharp.Processing.DrawImageExtensions.DrawImage(IImageProcessingContext source, Image image, Point location, Rectangle rectangle, Single opacity)
   at ImageSharp_DrawImage_SrcRect_Test.Form1.<>c__DisplayClass2_0.<Form1_Shown>b__0(IImageProcessingContext c) in [REDACTED]
   at SixLabors.ImageSharp.Processing.ProcessingExtensions.Mutate[TPixel](Image`1 source, Configuration configuration, Action`1 operation)
   at SixLabors.ImageSharp.Processing.ProcessingExtensions.Mutate[TPixel](Image`1 source, Action`1 operation)

   (...continues with my application's stack trace...)
ArgumentOutOfRangeException
using var src = Image.Load<Rgba32>("numbergrid.png");
using var dst = new Image<Rgba32>(100, 100, new Rgba32(0, 255, 255));

dst.Mutate(c => {
    c.DrawImage(src, new Point(10, 10), new Rectangle(320, 128, 32, 32), 1.0f);
});

dst.SaveAsPng("result.png");

Expected result:

Expected result

Exception:

System.ArgumentOutOfRangeException
  HResult=0x80131502
  Message=Specified argument was out of the range of valid values. Arg_ParamName_Name
  Source=SixLabors.ImageSharp
  ParamName=DangerousGetRowSpan(128). Y was out of range. Height=100
  StackTrace:
   at SixLabors.ImageSharp.Memory.Buffer2D`1.ThrowYOutOfRangeException(Int32 y)
   at SixLabors.ImageSharp.Memory.Buffer2D`1.DangerousGetRowSpan(Int32 y)
   at SixLabors.ImageSharp.Processing.Processors.Drawing.DrawImageProcessor`2.RowOperation.Invoke(Int32 y)
   at SixLabors.ImageSharp.Advanced.ParallelRowIterator.IterateRows[T](Rectangle rectangle, ParallelExecutionSettings& parallelSettings, T& operation)
   at SixLabors.ImageSharp.Advanced.ParallelRowIterator.IterateRows[T](Configuration configuration, Rectangle rectangle, T& operation)
   at SixLabors.ImageSharp.Processing.Processors.Drawing.DrawImageProcessor`2.OnFrameApply(ImageFrame`1 source)
   at SixLabors.ImageSharp.Processing.Processors.ImageProcessor`1.Apply(ImageFrame`1 source)
   at SixLabors.ImageSharp.Processing.Processors.ImageProcessor`1.SixLabors.ImageSharp.Processing.Processors.IImageProcessor<TPixel>.Execute()
   at SixLabors.ImageSharp.Processing.DefaultImageProcessorContext`1.ApplyProcessor(IImageProcessor processor, Rectangle rectangle)
   at SixLabors.ImageSharp.Processing.DrawImageExtensions.DrawImage(IImageProcessingContext source, Image image, Point location, Rectangle rectangle, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, Single opacity)
   at SixLabors.ImageSharp.Processing.DrawImageExtensions.DrawImage(IImageProcessingContext source, Image image, Point location, Rectangle rectangle, Single opacity)
   at ImageSharp_DrawImage_SrcRect_Test.Form1.<>c__DisplayClass2_0.<Form1_Shown>b__0(IImageProcessingContext c) in [REDACTED]
   at SixLabors.ImageSharp.Processing.ProcessingExtensions.Mutate[TPixel](Image`1 source, Configuration configuration, Action`1 operation)
   at SixLabors.ImageSharp.Processing.ProcessingExtensions.Mutate[TPixel](Image`1 source, Action`1 operation)

   (...continues with my application's stack trace...)
No exception, but incorrect result
using var src = Image.Load<Rgba32>("numbergrid.png");
using var dst = new Image<Rgba32>(100, 100, new Rgba32(0, 255, 255));

dst.Mutate(c => {
    c.DrawImage(src, new Point(10, 10), new Rectangle(32, 32, 32, 32), 1.0f);
});

dst.SaveAsPng("result.png");

Expected result:

Expected result

Actual result:

Actual result

Alternative code that produces the correct result

The following code clones and crops the source image instead, which produces the expected result regardless of the coordinates used:

using var src = Image.Load<Rgba32>("numbergrid.png");
using var dst = new Image<Rgba32>(100, 100, new Rgba32(0, 255, 255));

dst.Mutate(c => {
    using var slice = src.Clone(c => c.Crop(new Rectangle(32, 32, 32, 32)));
    c.DrawImage(slice, new Point(10, 10), 1.0f);
});

Result:

Correct result

Since this does unnecessary cloning and cropping of the source image, this is not a viable option.

Images

The image numbergrid.png used in the code:

numbergrid.png

@JimBobSquarePants
Copy link
Member

Thanks for raising such a detailed issue! 😍

There's definitely issues here and I'll have a look at fixing them using your examples as a test asap.

@Visual-Vincent
Copy link
Sponsor Author

Thank you. Feel free to use these images for anything you may need them to, including unit tests.

I look forward to see what you discover!

@JimBobSquarePants
Copy link
Member

I've opened a PR for this now. Thanks again for providing such detail. It enabled me to write a bunch of tests much more easily! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants