Skip to content

Commit 79eaefa

Browse files
committed
SkiaSharp implementation
1 parent 5aeee49 commit 79eaefa

File tree

12 files changed

+159
-132
lines changed

12 files changed

+159
-132
lines changed

src/Spectrogram.MicrophoneDemo/FormMicrophone.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Text;
1010
using System.Threading.Tasks;
1111
using System.Windows.Forms;
12+
using SkiaSharp.Views.Desktop;
1213

1314
namespace Spectrogram.MicrophoneDemo
1415
{
@@ -64,7 +65,7 @@ private void StartListening()
6465
pbSpectrogram.Height = spec.Height;
6566

6667
pbScaleVert.Image?.Dispose();
67-
pbScaleVert.Image = spec.GetVerticalScale(pbScaleVert.Width);
68+
pbScaleVert.Image = spec.GetVerticalScale(pbScaleVert.Width).ToBitmap();
6869
pbScaleVert.Height = spec.Height;
6970
}
7071

@@ -85,7 +86,7 @@ private void timer1_Tick(object sender, EventArgs e)
8586
using (var gfx = Graphics.FromImage(bmpSpec))
8687
using (var pen = new Pen(Color.White))
8788
{
88-
gfx.DrawImage(bmpSpecIndexed, 0, 0);
89+
gfx.DrawImage(bmpSpecIndexed.ToBitmap(), 0, 0);
8990
if (cbRoll.Checked)
9091
{
9192
gfx.DrawLine(pen, spec.NextColumnIndex, 0, spec.NextColumnIndex, pbSpectrogram.Height);

src/Spectrogram.MicrophoneDemo/Spectrogram.Demo.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
<ProjectReference Include="..\Spectrogram\Spectrogram.csproj" />
1111
</ItemGroup>
1212
<ItemGroup>
13-
<PackageReference Include="FftSharp" Version="2.0.0" />
13+
<PackageReference Include="FftSharp" Version="2.1.0" />
1414
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
1515
<PackageReference Include="NAudio" Version="1.10.0" />
16+
<PackageReference Include="SkiaSharp.Views" Version="2.88.8" />
1617
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
17-
<PackageReference Include="System.Drawing.Common" Version="5.0.0" />
1818
</ItemGroup>
1919
</Project>

src/Spectrogram.Tests/ColormapValues.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
using Microsoft.VisualStudio.TestPlatform.Utilities;
2-
using NUnit.Framework;
1+
using NUnit.Framework;
32
using System;
4-
using System.Collections.Generic;
53
using System.Drawing;
6-
using System.Text;
4+
using SkiaSharp;
75

86
namespace Spectrogram.Tests
97
{
@@ -36,9 +34,9 @@ public void Test_Colormap_IntegerMatchesRGBColors()
3634
var (r, g, b) = cmap.GetRGB(pixelIntensity);
3735
int int32 = cmap.GetInt32(pixelIntensity);
3836

39-
Color color1 = Color.FromArgb(255, r, g, b);
40-
Color color2 = Color.FromArgb(int32);
41-
Color color3 = cmap.GetColor(pixelIntensity);
37+
SKColor color1 = new SKColor(r, g, b, 255);
38+
SKColor color2 = new SKColor((uint)int32);
39+
SKColor color3 = cmap.GetColor(pixelIntensity);
4240

4341
Assert.AreEqual(color1, color2);
4442
Assert.AreEqual(color1, color3);

src/Spectrogram.Tests/ImageTests.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using NUnit.Framework;
2+
using SkiaSharp;
23

34
namespace Spectrogram.Tests;
45

@@ -12,10 +13,10 @@ public void Test_Image_Rotations()
1213
SpectrogramGenerator sg = new(sampleRate, 4096, 500, maxFreq: 3000);
1314
sg.Add(audio);
1415

15-
System.Drawing.Bitmap bmp1 = sg.GetBitmap(rotate: false);
16-
bmp1.Save("test-image-original.png");
16+
SKBitmap bmp1 = sg.GetBitmap(rotate: false);
17+
bmp1.SaveTo("test-image-original.png", SKEncodedImageFormat.Png);
1718

18-
System.Drawing.Bitmap bmp2 = sg.GetBitmap(rotate: true);
19-
bmp2.Save("test-image-rotated.png");
19+
SKBitmap bmp2 = sg.GetBitmap(rotate: true);
20+
bmp2.SaveTo("test-image-rotated.png", SKEncodedImageFormat.Png);
2021
}
2122
}

src/Spectrogram.Tests/Mel.cs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Drawing;
55
using System.Drawing.Imaging;
66
using System.Text;
7+
using SkiaSharp;
78

89
namespace Spectrogram.Tests
910
{
@@ -17,16 +18,38 @@ public void Test_MelSpectrogram_MelScale()
1718
var sg = new SpectrogramGenerator(sampleRate, fftSize, stepSize: 500);
1819
sg.Add(audio);
1920

20-
Bitmap bmpMel = sg.GetBitmapMel(250);
21-
bmpMel.Save("../../../../../dev/graphics/halMel-MelScale.png", ImageFormat.Png);
21+
// Ottieni l'immagine Mel-scaled come SKBitmap
22+
SKBitmap bmpMel = sg.GetBitmapMel(250); // Presuppone che sg abbia un metodo GetSKBitmapMel
23+
using (var image = SKImage.FromBitmap(bmpMel))
24+
using (var data = image.Encode(SKEncodedImageFormat.Png, 100))
25+
{
26+
// Salva l'immagine Mel-scaled
27+
using (var stream = System.IO.File.OpenWrite("../../../../../dev/graphics/halMel-MelScale.png"))
28+
{
29+
data.SaveTo(stream);
30+
}
31+
}
32+
33+
// Ottieni l'immagine originale come SKBitmap
34+
SKBitmap bmpRaw = sg.GetBitmap(); // Presuppone che sg abbia un metodo GetSKBitmap
35+
SKBitmap bmpCropped = new SKBitmap(bmpRaw.Width, bmpMel.Height);
2236

23-
Bitmap bmpRaw = sg.GetBitmap();
24-
Bitmap bmpCropped = new Bitmap(bmpRaw.Width, bmpMel.Height);
25-
using (Graphics gfx = Graphics.FromImage(bmpCropped))
37+
// Disegna bmpRaw su bmpCropped usando SKCanvas
38+
using (var canvas = new SKCanvas(bmpCropped))
2639
{
27-
gfx.DrawImage(bmpRaw, 0, bmpMel.Height - bmpRaw.Height);
40+
canvas.Clear(SKColors.Transparent);
41+
canvas.DrawBitmap(bmpRaw, new SKRect(0, bmpMel.Height - bmpRaw.Height, bmpRaw.Width, bmpMel.Height));
42+
}
43+
44+
using (var imageCropped = SKImage.FromBitmap(bmpCropped))
45+
using (var dataCropped = imageCropped.Encode(SKEncodedImageFormat.Png, 100))
46+
{
47+
// Salva l'immagine croppata
48+
using (var streamCropped = System.IO.File.OpenWrite("../../../../../dev/graphics/halMel-LinearCropped.png"))
49+
{
50+
dataCropped.SaveTo(streamCropped);
51+
}
2852
}
29-
bmpCropped.Save("../../../../../dev/graphics/halMel-LinearCropped.png", ImageFormat.Png);
3053
}
3154

3255
[Test]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using SkiaSharp;
2+
3+
namespace Spectrogram.Tests;
4+
5+
internal static class SkExtensions
6+
{
7+
internal static void SaveTo(this SKBitmap bitmap, string fileName, SKEncodedImageFormat format, int quality = 100)
8+
{
9+
using var data = bitmap.Encode(format, quality);
10+
using var stream = System.IO.File.OpenWrite(fileName);
11+
data.SaveTo(stream);
12+
}
13+
}

src/Spectrogram/Colormap.cs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System;
2-
using System.Drawing;
32
using System.Linq;
3+
using SkiaSharp;
44

55
namespace Spectrogram
66
{
@@ -74,22 +74,24 @@ public int GetInt32(double fraction)
7474
return 255 << 24 | r << 16 | g << 8 | b;
7575
}
7676

77-
public Color GetColor(byte value)
77+
public SKColor GetColor(byte value)
7878
{
79-
return Color.FromArgb(GetInt32(value));
79+
var color = GetInt32(value);
80+
return new SKColor((uint)color);
8081
}
8182

82-
public Color GetColor(double fraction)
83+
public SKColor GetColor(double fraction)
8384
{
84-
return Color.FromArgb(GetInt32(fraction));
85+
var color = GetInt32(fraction);
86+
return new SKColor((uint)color);
8587
}
8688

87-
public void Apply(Bitmap bmp)
89+
public void Apply(SKBitmap bmp)
8890
{
89-
System.Drawing.Imaging.ColorPalette pal = bmp.Palette;
91+
var pal = bmp.Pixels;
9092
for (int i = 0; i < 256; i++)
91-
pal.Entries[i] = GetColor((byte)i);
92-
bmp.Palette = pal;
93+
pal[i] = GetColor((byte)i);
94+
bmp.Pixels = pal;
9395
}
9496
}
9597
}

src/Spectrogram/Image.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Drawing;
4-
using System.Drawing.Imaging;
5-
using System.Runtime.InteropServices;
6-
using System.Text;
7-
using System.Threading.Tasks;
1+
using System.Collections.Generic;
2+
using SkiaSharp;
83

94
namespace Spectrogram
105
{
116
public static class Image
127
{
13-
public static Bitmap GetBitmap(List<double[]> ffts, Colormap cmap, double intensity = 1,
8+
public static SKBitmap GetBitmap(List<double[]> ffts, Colormap cmap, double intensity = 1,
149
bool dB = false, double dBScale = 1, bool roll = false, int rollOffset = 0, bool rotate = false)
1510
{
1611

src/Spectrogram/ImageMaker.cs

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Drawing;
4-
using System.Drawing.Imaging;
53
using System.Runtime.InteropServices;
6-
using System.Text;
74
using System.Threading.Tasks;
5+
using SkiaSharp;
86

97
namespace Spectrogram
108
{
@@ -54,52 +52,48 @@ public ImageMaker()
5452

5553
}
5654

57-
public Bitmap GetBitmap(List<double[]> ffts)
55+
public SKBitmap GetBitmap(List<double[]> ffts)
5856
{
5957
if (ffts.Count == 0)
6058
throw new ArgumentException("Not enough data in FFTs to generate an image yet.");
6159

62-
int Width = IsRotated ? ffts[0].Length : ffts.Count;
63-
int Height = IsRotated ? ffts.Count : ffts[0].Length;
60+
int width = IsRotated ? ffts[0].Length : ffts.Count;
61+
int height = IsRotated ? ffts.Count : ffts[0].Length;
6462

65-
Bitmap bmp = new(Width, Height, PixelFormat.Format8bppIndexed);
66-
Colormap.Apply(bmp);
63+
var imageInfo = new SKImageInfo(width, height, SKColorType.Gray8);
64+
var bitmap = new SKBitmap(imageInfo);
6765

68-
Rectangle lockRect = new(0, 0, Width, Height);
69-
BitmapData bitmapData = bmp.LockBits(lockRect, ImageLockMode.ReadOnly, bmp.PixelFormat);
70-
int stride = bitmapData.Stride;
71-
72-
byte[] bytes = new byte[bitmapData.Stride * bmp.Height];
73-
Parallel.For(0, Width, col =>
66+
var pixelBuffer = bitmap.Bytes;
67+
Parallel.For(0, width, col =>
7468
{
7569
int sourceCol = col;
7670
if (IsRoll)
7771
{
78-
sourceCol += Width - RollOffset % Width;
79-
if (sourceCol >= Width)
80-
sourceCol -= Width;
72+
sourceCol += width - RollOffset % width;
73+
if (sourceCol >= width)
74+
sourceCol -= width;
8175
}
8276

83-
for (int row = 0; row < Height; row++)
77+
for (int row = 0; row < height; row++)
8478
{
8579
double value = IsRotated
86-
? ffts[Height - row - 1][sourceCol]
80+
? ffts[height - row - 1][sourceCol]
8781
: ffts[sourceCol][row];
8882

8983
if (IsDecibel)
9084
value = 20 * Math.Log10(value * DecibelScaleFactor + 1);
9185

9286
value *= Intensity;
9387
value = Math.Min(value, 255);
94-
int bytePosition = (Height - 1 - row) * stride + col;
95-
bytes[bytePosition] = (byte)value;
88+
int bytePosition = row * width + col;
89+
pixelBuffer[bytePosition] = (byte)value;
9690
}
9791
});
9892

99-
Marshal.Copy(bytes, 0, bitmapData.Scan0, bytes.Length);
100-
bmp.UnlockBits(bitmapData);
101-
102-
return bmp;
93+
94+
var gcHandle = GCHandle.Alloc(pixelBuffer, GCHandleType.Pinned);
95+
bitmap.InstallPixels(imageInfo, gcHandle.AddrOfPinnedObject(), imageInfo.RowBytes, delegate { gcHandle.Free(); }, null);
96+
return bitmap;
10397
}
10498
}
10599
}

0 commit comments

Comments
 (0)