mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-11-01 01:25:27 +08:00 
			
		
		
		
	Add canvas and image support
Adds support for drawing "pixels" and displaying images in the terminal.
This commit is contained in:
		 Patrik Svensson
					Patrik Svensson
				
			
				
					committed by
					
						 Patrik Svensson
						Patrik Svensson
					
				
			
			
				
	
			
			
			 Patrik Svensson
						Patrik Svensson
					
				
			
						parent
						
							4f6eca4fcb
						
					
				
				
					commit
					2a9fa223de
				
			
							
								
								
									
										125
									
								
								src/Spectre.Console.ImageSharp/CanvasImage.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								src/Spectre.Console.ImageSharp/CanvasImage.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using SixLabors.ImageSharp.PixelFormats; | ||||
| using SixLabors.ImageSharp.Processing; | ||||
| using SixLabors.ImageSharp.Processing.Processors.Transforms; | ||||
| using Spectre.Console.Rendering; | ||||
|  | ||||
| namespace Spectre.Console | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Represents a renderable image. | ||||
|     /// </summary> | ||||
|     public sealed class CanvasImage : Renderable | ||||
|     { | ||||
|         private static readonly IResampler _defaultResampler = KnownResamplers.Bicubic; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets the image width. | ||||
|         /// </summary> | ||||
|         public int Width => Image.Width; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets the image height. | ||||
|         /// </summary> | ||||
|         public int Height => Image.Height; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the render width of the canvas. | ||||
|         /// </summary> | ||||
|         public int? MaxWidth { get; set; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the render width of the canvas. | ||||
|         /// </summary> | ||||
|         public int PixelWidth { get; set; } = 2; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the <see cref="IResampler"/> that should | ||||
|         /// be used when scaling the image. Defaults to bicubic sampling. | ||||
|         /// </summary> | ||||
|         public IResampler? Resampler { get; set; } | ||||
|  | ||||
|         internal SixLabors.ImageSharp.Image<Rgba32> Image { get; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="CanvasImage"/> class. | ||||
|         /// </summary> | ||||
|         /// <param name="filename">The image filename.</param> | ||||
|         public CanvasImage(string filename) | ||||
|         { | ||||
|             Image = SixLabors.ImageSharp.Image.Load<Rgba32>(filename); | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         protected override Measurement Measure(RenderContext context, int maxWidth) | ||||
|         { | ||||
|             if (PixelWidth < 0) | ||||
|             { | ||||
|                 throw new InvalidOperationException("Pixel width must be greater than zero."); | ||||
|             } | ||||
|  | ||||
|             var width = MaxWidth ?? Width; | ||||
|             if (maxWidth < width * PixelWidth) | ||||
|             { | ||||
|                 return new Measurement(maxWidth, maxWidth); | ||||
|             } | ||||
|  | ||||
|             return new Measurement(width * PixelWidth, width * PixelWidth); | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth) | ||||
|         { | ||||
|             var image = Image; | ||||
|  | ||||
|             var width = Width; | ||||
|             var height = Height; | ||||
|  | ||||
|             // Got a max width? | ||||
|             if (MaxWidth != null) | ||||
|             { | ||||
|                 height = (int)(height * ((float)MaxWidth.Value) / Width); | ||||
|                 width = MaxWidth.Value; | ||||
|             } | ||||
|  | ||||
|             // Exceed the max width when we take pixel width into account? | ||||
|             if (width * PixelWidth > maxWidth) | ||||
|             { | ||||
|                 height = (int)(height * (maxWidth / (float)(width * PixelWidth))); | ||||
|                 width = maxWidth / PixelWidth; | ||||
|             } | ||||
|  | ||||
|             // Need to rescale the pixel buffer? | ||||
|             if (width != Width || height != Height) | ||||
|             { | ||||
|                 var resampler = Resampler ?? _defaultResampler; | ||||
|                 image = image.Clone(); // Clone the original image | ||||
|                 image.Mutate(i => i.Resize(width, height, resampler)); | ||||
|             } | ||||
|  | ||||
|             var canvas = new Canvas(width, height) | ||||
|             { | ||||
|                 MaxWidth = MaxWidth, | ||||
|                 PixelWidth = PixelWidth, | ||||
|                 Scale = false, | ||||
|             }; | ||||
|  | ||||
|             for (var y = 0; y < image.Height; y++) | ||||
|             { | ||||
|                 for (var x = 0; x < image.Width; x++) | ||||
|                 { | ||||
|                     if (image[x, y].A == 0) | ||||
|                     { | ||||
|                         continue; | ||||
|                     } | ||||
|  | ||||
|                     canvas.SetPixel(x, y, new Color( | ||||
|                         image[x, y].R, image[x, y].G, image[x, y].B)); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return ((IRenderable)canvas).Render(context, maxWidth); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										135
									
								
								src/Spectre.Console.ImageSharp/CanvasImageExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								src/Spectre.Console.ImageSharp/CanvasImageExtensions.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,135 @@ | ||||
| using System; | ||||
| using SixLabors.ImageSharp.Processing; | ||||
|  | ||||
| namespace Spectre.Console | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Contains extension methods for <see cref="CanvasImage"/>. | ||||
|     /// </summary> | ||||
|     public static class CanvasImageExtensions | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Sets the maximum width of the rendered image. | ||||
|         /// </summary> | ||||
|         /// <param name="image">The canvas image.</param> | ||||
|         /// <param name="maxWidth">The maximum width.</param> | ||||
|         /// <returns>The same instance so that multiple calls can be chained.</returns> | ||||
|         public static CanvasImage MaxWidth(this CanvasImage image, int? maxWidth) | ||||
|         { | ||||
|             if (image is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(image)); | ||||
|             } | ||||
|  | ||||
|             image.MaxWidth = maxWidth; | ||||
|             return image; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Disables the maximum width of the rendered image. | ||||
|         /// </summary> | ||||
|         /// <param name="image">The canvas image.</param> | ||||
|         /// <returns>The same instance so that multiple calls can be chained.</returns> | ||||
|         public static CanvasImage NoMaxWidth(this CanvasImage image) | ||||
|         { | ||||
|             if (image is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(image)); | ||||
|             } | ||||
|  | ||||
|             image.MaxWidth = null; | ||||
|             return image; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Sets the pixel width. | ||||
|         /// </summary> | ||||
|         /// <param name="image">The canvas image.</param> | ||||
|         /// <param name="width">The pixel width.</param> | ||||
|         /// <returns>The same instance so that multiple calls can be chained.</returns> | ||||
|         public static CanvasImage PixelWidth(this CanvasImage image, int width) | ||||
|         { | ||||
|             if (image is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(image)); | ||||
|             } | ||||
|  | ||||
|             image.PixelWidth = width; | ||||
|             return image; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Mutates the underlying image. | ||||
|         /// </summary> | ||||
|         /// <param name="image">The canvas image.</param> | ||||
|         /// <param name="action">The action that mutates the underlying image.</param> | ||||
|         /// <returns>The same instance so that multiple calls can be chained.</returns> | ||||
|         public static CanvasImage Mutate(this CanvasImage image, Action<IImageProcessingContext> action) | ||||
|         { | ||||
|             if (image is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(image)); | ||||
|             } | ||||
|  | ||||
|             if (action is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(action)); | ||||
|             } | ||||
|  | ||||
|             image.Image.Mutate(action); | ||||
|             return image; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Uses a bicubic sampler that implements the bicubic kernel algorithm W(x). | ||||
|         /// </summary> | ||||
|         /// <param name="image">The canvas image.</param> | ||||
|         /// <returns>The same instance so that multiple calls can be chained.</returns> | ||||
|         public static CanvasImage BicubicResampler(this CanvasImage image) | ||||
|         { | ||||
|             if (image is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(image)); | ||||
|             } | ||||
|  | ||||
|             image.Resampler = KnownResamplers.Bicubic; | ||||
|             return image; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Uses a bilinear sampler. This interpolation algorithm | ||||
|         /// can be used where perfect image transformation with pixel matching is impossible, | ||||
|         /// so that one can calculate and assign appropriate intensity values to pixels. | ||||
|         /// </summary> | ||||
|         /// <param name="image">The canvas image.</param> | ||||
|         /// <returns>The same instance so that multiple calls can be chained.</returns> | ||||
|         public static CanvasImage BilinearResampler(this CanvasImage image) | ||||
|         { | ||||
|             if (image is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(image)); | ||||
|             } | ||||
|  | ||||
|             image.Resampler = KnownResamplers.Triangle; | ||||
|             return image; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Uses a Nearest-Neighbour sampler that implements the nearest neighbor algorithm. | ||||
|         /// This uses a very fast, unscaled filter which will select the closest pixel to | ||||
|         /// the new pixels position. | ||||
|         /// </summary> | ||||
|         /// <param name="image">The canvas image.</param> | ||||
|         /// <returns>The same instance so that multiple calls can be chained.</returns> | ||||
|         public static CanvasImage NearestNeighborResampler(this CanvasImage image) | ||||
|         { | ||||
|             if (image is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(image)); | ||||
|             } | ||||
|  | ||||
|             image.Resampler = KnownResamplers.NearestNeighbor; | ||||
|             return image; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,22 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|  | ||||
|   <PropertyGroup> | ||||
|     <TargetFrameworks>netstandard2.0</TargetFrameworks> | ||||
|     <Nullable>enable</Nullable> | ||||
|     <Description>A library that extends Spectre.Console with ImageSharp super powers.</Description> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <AdditionalFiles Include="..\stylecop.json" Link="Properties/stylecop.json" /> | ||||
|     <None Include="../../resources/gfx/small-logo.png" Pack="true" PackagePath="\" Link="Properties/small-logo.png" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="SixLabors.ImageSharp" Version="1.0.2" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <ProjectReference Include="..\Spectre.Console\Spectre.Console.csproj" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
| </Project> | ||||
| @@ -54,6 +54,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prompt", "..\examples\Promp | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Figlet", "..\examples\Figlet\Figlet.csproj", "{45BF6302-6553-4E52-BF0F-B10D1AA9A6D1}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Canvas", "..\examples\Canvas\Canvas.csproj", "{5693761A-754A-40A8-9144-36510D6A4D69}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.ImageSharp", "Spectre.Console.ImageSharp\Spectre.Console.ImageSharp.csproj", "{0EFE694D-0770-4E71-BF4E-EC2B41362F79}" | ||||
| EndProject | ||||
| Global | ||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
| 		Debug|Any CPU = Debug|Any CPU | ||||
| @@ -268,6 +272,30 @@ Global | ||||
| 		{45BF6302-6553-4E52-BF0F-B10D1AA9A6D1}.Release|x64.Build.0 = Release|Any CPU | ||||
| 		{45BF6302-6553-4E52-BF0F-B10D1AA9A6D1}.Release|x86.ActiveCfg = Release|Any CPU | ||||
| 		{45BF6302-6553-4E52-BF0F-B10D1AA9A6D1}.Release|x86.Build.0 = Release|Any CPU | ||||
| 		{5693761A-754A-40A8-9144-36510D6A4D69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{5693761A-754A-40A8-9144-36510D6A4D69}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{5693761A-754A-40A8-9144-36510D6A4D69}.Debug|x64.ActiveCfg = Debug|Any CPU | ||||
| 		{5693761A-754A-40A8-9144-36510D6A4D69}.Debug|x64.Build.0 = Debug|Any CPU | ||||
| 		{5693761A-754A-40A8-9144-36510D6A4D69}.Debug|x86.ActiveCfg = Debug|Any CPU | ||||
| 		{5693761A-754A-40A8-9144-36510D6A4D69}.Debug|x86.Build.0 = Debug|Any CPU | ||||
| 		{5693761A-754A-40A8-9144-36510D6A4D69}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{5693761A-754A-40A8-9144-36510D6A4D69}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{5693761A-754A-40A8-9144-36510D6A4D69}.Release|x64.ActiveCfg = Release|Any CPU | ||||
| 		{5693761A-754A-40A8-9144-36510D6A4D69}.Release|x64.Build.0 = Release|Any CPU | ||||
| 		{5693761A-754A-40A8-9144-36510D6A4D69}.Release|x86.ActiveCfg = Release|Any CPU | ||||
| 		{5693761A-754A-40A8-9144-36510D6A4D69}.Release|x86.Build.0 = Release|Any CPU | ||||
| 		{0EFE694D-0770-4E71-BF4E-EC2B41362F79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{0EFE694D-0770-4E71-BF4E-EC2B41362F79}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{0EFE694D-0770-4E71-BF4E-EC2B41362F79}.Debug|x64.ActiveCfg = Debug|Any CPU | ||||
| 		{0EFE694D-0770-4E71-BF4E-EC2B41362F79}.Debug|x64.Build.0 = Debug|Any CPU | ||||
| 		{0EFE694D-0770-4E71-BF4E-EC2B41362F79}.Debug|x86.ActiveCfg = Debug|Any CPU | ||||
| 		{0EFE694D-0770-4E71-BF4E-EC2B41362F79}.Debug|x86.Build.0 = Debug|Any CPU | ||||
| 		{0EFE694D-0770-4E71-BF4E-EC2B41362F79}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{0EFE694D-0770-4E71-BF4E-EC2B41362F79}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{0EFE694D-0770-4E71-BF4E-EC2B41362F79}.Release|x64.ActiveCfg = Release|Any CPU | ||||
| 		{0EFE694D-0770-4E71-BF4E-EC2B41362F79}.Release|x64.Build.0 = Release|Any CPU | ||||
| 		{0EFE694D-0770-4E71-BF4E-EC2B41362F79}.Release|x86.ActiveCfg = Release|Any CPU | ||||
| 		{0EFE694D-0770-4E71-BF4E-EC2B41362F79}.Release|x86.Build.0 = Release|Any CPU | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(SolutionProperties) = preSolution | ||||
| 		HideSolutionNode = FALSE | ||||
| @@ -289,6 +317,7 @@ Global | ||||
| 		{75C608C3-ABB4-4168-A229-7F8250B946D1} = {F0575243-121F-4DEE-9F6B-246E26DC0844} | ||||
| 		{6351C70F-F368-46DB-BAED-9B87CCD69353} = {F0575243-121F-4DEE-9F6B-246E26DC0844} | ||||
| 		{45BF6302-6553-4E52-BF0F-B10D1AA9A6D1} = {F0575243-121F-4DEE-9F6B-246E26DC0844} | ||||
| 		{5693761A-754A-40A8-9144-36510D6A4D69} = {F0575243-121F-4DEE-9F6B-246E26DC0844} | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(ExtensibilityGlobals) = postSolution | ||||
| 		SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C} | ||||
|   | ||||
							
								
								
									
										151
									
								
								src/Spectre.Console/Widgets/Canvas.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								src/Spectre.Console/Widgets/Canvas.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,151 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using Spectre.Console.Rendering; | ||||
|  | ||||
| namespace Spectre.Console | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Represents a renderable canvas. | ||||
|     /// </summary> | ||||
|     public sealed class Canvas : Renderable | ||||
|     { | ||||
|         private readonly Color?[,] _pixels; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets the width of the canvas. | ||||
|         /// </summary> | ||||
|         public int Width { get; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets the height of the canvas. | ||||
|         /// </summary> | ||||
|         public int Height { get; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the render width of the canvas. | ||||
|         /// </summary> | ||||
|         public int? MaxWidth { get; set; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets a value indicating whether or not | ||||
|         /// to scale the canvas when rendering. | ||||
|         /// </summary> | ||||
|         public bool Scale { get; set; } = true; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the pixel width. | ||||
|         /// </summary> | ||||
|         public int PixelWidth { get; set; } = 2; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="Canvas"/> class. | ||||
|         /// </summary> | ||||
|         /// <param name="width">The canvas width.</param> | ||||
|         /// <param name="height">The canvas height.</param> | ||||
|         public Canvas(int width, int height) | ||||
|         { | ||||
|             Width = width; | ||||
|             Height = height; | ||||
|  | ||||
|             _pixels = new Color?[Width, Height]; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Sets a pixel with the specified color in the canvas at the specified location. | ||||
|         /// </summary> | ||||
|         /// <param name="x">The X coordinate for the pixel.</param> | ||||
|         /// <param name="y">The Y coordinate for the pixel.</param> | ||||
|         /// <param name="color">The pixel color.</param> | ||||
|         public void SetPixel(int x, int y, Color color) | ||||
|         { | ||||
|             _pixels[x, y] = color; | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         protected override Measurement Measure(RenderContext context, int maxWidth) | ||||
|         { | ||||
|             if (PixelWidth < 0) | ||||
|             { | ||||
|                 throw new InvalidOperationException("Pixel width must be greater than zero."); | ||||
|             } | ||||
|  | ||||
|             var width = MaxWidth ?? Width; | ||||
|  | ||||
|             if (maxWidth < width * PixelWidth) | ||||
|             { | ||||
|                 return new Measurement(maxWidth, maxWidth); | ||||
|             } | ||||
|  | ||||
|             return new Measurement(width * PixelWidth, width * PixelWidth); | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth) | ||||
|         { | ||||
|             if (PixelWidth < 0) | ||||
|             { | ||||
|                 throw new InvalidOperationException("Pixel width must be greater than zero."); | ||||
|             } | ||||
|  | ||||
|             var pixels = _pixels; | ||||
|             var pixel = new string(' ', PixelWidth); | ||||
|             var width = Width; | ||||
|             var height = Height; | ||||
|  | ||||
|             // Got a max width? | ||||
|             if (MaxWidth != null) | ||||
|             { | ||||
|                 height = (int)(height * ((float)MaxWidth.Value) / Width); | ||||
|                 width = MaxWidth.Value; | ||||
|             } | ||||
|  | ||||
|             // Exceed the max width when we take pixel width into account? | ||||
|             if (width * PixelWidth > maxWidth) | ||||
|             { | ||||
|                 height = (int)(height * (maxWidth / (float)(width * PixelWidth))); | ||||
|                 width = maxWidth / PixelWidth; | ||||
|             } | ||||
|  | ||||
|             // Need to rescale the pixel buffer? | ||||
|             if (Scale && (width != Width || height != Height)) | ||||
|             { | ||||
|                 pixels = ScaleDown(width, height); | ||||
|             } | ||||
|  | ||||
|             for (var y = 0; y < height; y++) | ||||
|             { | ||||
|                 for (var x = 0; x < width; x++) | ||||
|                 { | ||||
|                     var color = pixels[x, y]; | ||||
|                     if (color != null) | ||||
|                     { | ||||
|                         yield return new Segment(pixel, new Style(background: color)); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         yield return new Segment(pixel); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 yield return Segment.LineBreak; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private Color?[,] ScaleDown(int newWidth, int newHeight) | ||||
|         { | ||||
|             var buffer = new Color?[newWidth, newHeight]; | ||||
|             var xRatio = ((Width << 16) / newWidth) + 1; | ||||
|             var yRatio = ((Height << 16) / newHeight) + 1; | ||||
|  | ||||
|             for (var i = 0; i < newHeight; i++) | ||||
|             { | ||||
|                 for (var j = 0; j < newWidth; j++) | ||||
|                 { | ||||
|                     buffer[j, i] = _pixels[(j * xRatio) >> 16, (i * yRatio) >> 16]; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return buffer; | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user