Browse Source

Merge branch 'main' into stefannikolei/nullable/exifprofile

pull/2320/head
Stefan Nikolei 3 years ago
committed by GitHub
parent
commit
49d332eb18
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 302
      src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs
  2. 6
      src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs
  3. 4
      src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
  4. 40
      tests/ImageSharp.Tests/Drawing/DrawImageTests.cs
  5. 3
      tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_10_10.png
  6. 3
      tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_25_50.png
  7. 3
      tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_50_25.png
  8. 3
      tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_50_50.png

302
src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs

@ -12,168 +12,280 @@ namespace SixLabors.ImageSharp.Processing;
public static class DrawImageExtensions
{
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// Draws the given image together with the currently processing image by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
/// <param name="source">The current image processing context.</param>
/// <param name="image">The image to draw on the currently processing image.</param>
/// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
float opacity)
{
var options = source.GetGraphicsOptions();
return source.ApplyProcessor(
new DrawImageProcessor(
image,
Point.Empty,
options.ColorBlendingMode,
options.AlphaCompositionMode,
opacity));
GraphicsOptions options = source.GetGraphicsOptions();
return DrawImage(source, image, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
}
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// Draws the given image together with the currently processing image by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="colorBlending">The blending mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
/// <param name="source">The current image processing context.</param>
/// <param name="image">The image to draw on the currently processing image.</param>
/// <param name="rectangle">The rectangle structure that specifies the portion of the image to draw.</param>
/// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Rectangle rectangle,
float opacity)
{
GraphicsOptions options = source.GetGraphicsOptions();
return DrawImage(source, image, rectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
}
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
/// </summary>
/// <param name="source">The current image processing context.</param>
/// <param name="image">The image to draw on the currently processing image.</param>
/// <param name="colorBlending">The color blending mode.</param>
/// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
PixelColorBlendingMode colorBlending,
float opacity) =>
source.ApplyProcessor(
new DrawImageProcessor(
image,
Point.Empty,
colorBlending,
source.GetGraphicsOptions().AlphaCompositionMode,
opacity));
float opacity)
=> DrawImage(source, image, Point.Empty, colorBlending, opacity);
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// Draws the given image together with the currently processing image by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="image">The image to draw on the currently processing image.</param>
/// <param name="rectangle">The rectangle structure that specifies the portion of the image to draw.</param>
/// <param name="colorBlending">The color blending mode.</param>
/// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Rectangle rectangle,
PixelColorBlendingMode colorBlending,
float opacity)
=> DrawImage(source, image, rectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
/// </summary>
/// <param name="source">The current image processing context.</param>
/// <param name="image">The image to draw on the currently processing image.</param>
/// <param name="colorBlending">The color blending mode.</param>
/// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
/// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity) =>
source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, colorBlending, alphaComposition, opacity));
float opacity)
=> DrawImage(source, image, Point.Empty, colorBlending, alphaComposition, opacity);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
/// </summary>
/// <param name="source">The current image processing context.</param>
/// <param name="image">The image to draw on the currently processing image.</param>
/// <param name="rectangle">The rectangle structure that specifies the portion of the image to draw.</param>
/// <param name="colorBlending">The color blending mode.</param>
/// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Rectangle rectangle,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity)
=> DrawImage(source, image, Point.Empty, rectangle, colorBlending, alphaComposition, opacity);
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// Draws the given image together with the currently processing image by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="image">The image to draw on the currently processing image.</param>
/// <param name="options">The options, including the blending type and blending amount.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
GraphicsOptions options) =>
source.ApplyProcessor(
new DrawImageProcessor(
image,
Point.Empty,
options.ColorBlendingMode,
options.AlphaCompositionMode,
options.BlendPercentage));
GraphicsOptions options)
=> DrawImage(source, image, Point.Empty, options);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
/// </summary>
/// <param name="source">The current image processing context.</param>
/// <param name="image">The image to draw on the currently processing image.</param>
/// <param name="rectangle">The rectangle structure that specifies the portion of the image to draw.</param>
/// <param name="options">The options, including the blending type and blending amount.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Rectangle rectangle,
GraphicsOptions options)
=> DrawImage(source, image, Point.Empty, rectangle, options);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
/// </summary>
/// <param name="source">The current image processing context.</param>
/// <param name="image">The image to draw on the currently processing image.</param>
/// <param name="location">The location on the currenty processing image at which to draw.</param>
/// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Point location,
float opacity)
{
GraphicsOptions options = source.GetGraphicsOptions();
return DrawImage(source, image, location, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
}
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// Draws the given image together with the currently processing image by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
/// <param name="source">The current image processing context.</param>
/// <param name="image">The image to draw on the currently processing image.</param>
/// <param name="location">The location on the currenty processing image at which to draw.</param>
/// <param name="rectangle">The rectangle structure that specifies the portion of the image to draw.</param>
/// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Point location,
Rectangle rectangle,
float opacity)
{
var options = source.GetGraphicsOptions();
return source.ApplyProcessor(
new DrawImageProcessor(
image,
location,
options.ColorBlendingMode,
options.AlphaCompositionMode,
opacity));
GraphicsOptions options = source.GetGraphicsOptions();
return DrawImage(source, image, location, rectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
}
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// Draws the given image together with the currently processing image by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="image">The image to draw on the currently processing image.</param>
/// <param name="location">The location on the currenty processing image at which to draw.</param>
/// <param name="colorBlending">The color blending to apply.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
/// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Point location,
PixelColorBlendingMode colorBlending,
float opacity) =>
source.ApplyProcessor(
new DrawImageProcessor(
image,
location,
colorBlending,
source.GetGraphicsOptions().AlphaCompositionMode,
opacity));
float opacity)
=> DrawImage(source, image, location, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// Draws the given image together with the currently processing image by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="image">The image to draw on the currently processing image.</param>
/// <param name="location">The location on the currenty processing image at which to draw.</param>
/// <param name="rectangle">The rectangle structure that specifies the portion of the image to draw.</param>
/// <param name="colorBlending">The color blending to apply.</param>
/// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Point location,
Rectangle rectangle,
PixelColorBlendingMode colorBlending,
float opacity)
=> DrawImage(source, image, location, rectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
/// </summary>
/// <param name="source">The current image processing context.</param>
/// <param name="image">The image to draw on the currently processing image.</param>
/// <param name="location">The location on the currenty processing image at which to draw.</param>
/// <param name="options">The options containing the blend mode and opacity.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Point location,
GraphicsOptions options)
=> DrawImage(source, image, location, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
/// </summary>
/// <param name="source">The current image processing context.</param>
/// <param name="image">The image to draw on the currently processing image.</param>
/// <param name="location">The location on the currenty processing image at which to draw.</param>
/// <param name="rectangle">The rectangle structure that specifies the portion of the image to draw.</param>
/// <param name="options">The options containing the blend mode and opacity.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Point location,
Rectangle rectangle,
GraphicsOptions options)
=> DrawImage(source, image, location, rectangle, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
/// </summary>
/// <param name="source">The current image processing context.</param>
/// <param name="image">The image to draw on the currently processing image.</param>
/// <param name="location">The location on the currenty processing image at which to draw.</param>
/// <param name="colorBlending">The color blending to apply.</param>
/// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
/// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Point location,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity) =>
source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity));
float opacity)
=> source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// Draws the given image together with the currently processing image by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="options">The options containing the blend mode and opacity.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
/// <param name="source">The current image processing context.</param>
/// <param name="image">The image to draw on the currently processing image.</param>
/// <param name="location">The location on the currenty processing image at which to draw.</param>
/// <param name="rectangle">The rectangle structure that specifies the portion of the image to draw.</param>
/// <param name="colorBlending">The color blending to apply.</param>
/// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Point location,
GraphicsOptions options) =>
Rectangle rectangle,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity) =>
source.ApplyProcessor(
new DrawImageProcessor(
image,
location,
options.ColorBlendingMode,
options.AlphaCompositionMode,
options.BlendPercentage));
new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity),
rectangle);
}

6
src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs

@ -63,7 +63,7 @@ public class DrawImageProcessor : IImageProcessor
public IImageProcessor<TPixelBg> CreatePixelSpecificProcessor<TPixelBg>(Configuration configuration, Image<TPixelBg> source, Rectangle sourceRectangle)
where TPixelBg : unmanaged, IPixel<TPixelBg>
{
var visitor = new ProcessorFactoryVisitor<TPixelBg>(configuration, this, source, sourceRectangle);
ProcessorFactoryVisitor<TPixelBg> visitor = new(configuration, this, source, sourceRectangle);
this.Image.AcceptVisitor(visitor);
return visitor.Result;
}
@ -88,8 +88,7 @@ public class DrawImageProcessor : IImageProcessor
public void Visit<TPixelFg>(Image<TPixelFg> image)
where TPixelFg : unmanaged, IPixel<TPixelFg>
{
this.Result = new DrawImageProcessor<TPixelBg, TPixelFg>(
=> this.Result = new DrawImageProcessor<TPixelBg, TPixelFg>(
this.configuration,
image,
this.source,
@ -98,6 +97,5 @@ public class DrawImageProcessor : IImageProcessor
this.definition.ColorBlendingMode,
this.definition.AlphaCompositionMode,
this.definition.Opacity);
}
}
}

4
src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs

@ -89,7 +89,7 @@ internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
int width = maxX - minX;
var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
Rectangle workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
// Not a valid operation because rectangle does not overlap with this image.
if (workingRect.Width <= 0 || workingRect.Height <= 0)
@ -98,7 +98,7 @@ internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
"Cannot draw image because the source image does not overlap the target image.");
}
var operation = new RowOperation(source.PixelBuffer, targetImage.Frames.RootFrame.PixelBuffer, blender, configuration, minX, width, locationY, targetX, this.Opacity);
DrawImageProcessor<TPixelBg, TPixelFg>.RowOperation operation = new(source.PixelBuffer, targetImage.Frames.RootFrame.PixelBuffer, blender, configuration, minX, width, locationY, targetX, this.Opacity);
ParallelRowIterator.IterateRows(
configuration,
workingRect,

40
tests/ImageSharp.Tests/Drawing/DrawImageTests.cs

@ -119,9 +119,7 @@ public class DrawImageTests
public void WorksWithDifferentLocations(TestImageProvider<Rgba32> provider, int x, int y)
{
using Image<Rgba32> background = provider.GetImage();
using Image<Rgba32> overlay = new(50, 50);
Assert.True(overlay.DangerousTryGetSinglePixelMemory(out Memory<Rgba32> overlayMem));
overlayMem.Span.Fill(Color.Black);
using Image<Rgba32> overlay = new(50, 50, Color.Black.ToRgba32());
background.Mutate(c => c.DrawImage(overlay, new Point(x, y), PixelColorBlendingMode.Normal, 1F));
@ -138,6 +136,31 @@ public class DrawImageTests
appendSourceFileOrDescription: false);
}
[Theory]
[WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 10, 10)]
[WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 50, 25)]
[WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 25, 50)]
[WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 50, 50)]
public void WorksWithDifferentBounds(TestImageProvider<Rgba32> provider, int width, int height)
{
using Image<Rgba32> background = provider.GetImage();
using Image<Rgba32> overlay = new(50, 50, Color.Black.ToRgba32());
background.Mutate(c => c.DrawImage(overlay, new Rectangle(0, 0, width, height), PixelColorBlendingMode.Normal, 1F));
background.DebugSave(
provider,
testOutputDetails: $"{width}_{height}",
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false);
background.CompareToReferenceOutput(
provider,
testOutputDetails: $"{width}_{height}",
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false);
}
[Theory]
[WithFile(TestImages.Png.Splash, PixelTypes.Rgba32)]
public void DrawTransformed<TPixel>(TestImageProvider<TPixel> provider)
@ -158,12 +181,12 @@ public class DrawImageTests
Point position = new((image.Width - blend.Width) / 2, (image.Height - blend.Height) / 2);
image.Mutate(x => x.DrawImage(blend, position, .75F));
image.DebugSave(provider, appendSourceFileOrDescription: false, appendPixelTypeToFileName: false);
image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false);
image.CompareToReferenceOutput(
ImageComparer.TolerantPercentage(0.002f),
provider,
appendSourceFileOrDescription: false,
appendPixelTypeToFileName: false);
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false);
}
[Theory]
@ -179,9 +202,6 @@ public class DrawImageTests
Assert.Contains("does not overlap", ex.ToString());
void Test()
{
background.Mutate(context => context.DrawImage(overlay, new Point(x, y), new GraphicsOptions()));
}
void Test() => background.Mutate(context => context.DrawImage(overlay, new Point(x, y), new GraphicsOptions()));
}
}

3
tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_10_10.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f353ee06664a6ff27c533af63cffb9eac4917103ba0b7fff9084fb4d2d42fed7
size 155

3
tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_25_50.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:af198da8f611eb97f3e4e358cb097cd292771d52e991de76495f073c1f1b9338
size 154

3
tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_50_25.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:35bda87f28e03c5ed0d45412e367fae9b04b7684f5242dc20e7709e8ed71cd86
size 156

3
tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_50_50.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f8b9ff745592bb0e0a365cb0985a5519bf567fc73a09211c88bcda51356f9202
size 155
Loading…
Cancel
Save