diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4260584 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog + +### [1.3.2-rc.3] - 2025-12-05 +- Converted from System.Drawing.Bitmap to SkiaSharp.SKBitmap for cross-platform support +- Replaced System.Drawing.Common package with SkiaSharp (2.88.8) +- Updated memory operations to work on both Windows and Linux +- Fixed color accuracy by correctly handling BGR format from Ghostscript +- Updated platform-specific structures for Linux compatibility \ No newline at end of file diff --git a/Ghostscript.NET.DisplayTest/FMain.cs b/Ghostscript.NET.DisplayTest/FMain.cs index eef9b02..66f4c25 100644 --- a/Ghostscript.NET.DisplayTest/FMain.cs +++ b/Ghostscript.NET.DisplayTest/FMain.cs @@ -9,6 +9,8 @@ using Ghostscript.NET; using Ghostscript.NET.Viewer; using Ghostscript.NET.Interpreter; +using SkiaSharp; +using SkiaSharp.Views.Desktop; namespace Ghostscript.NET.DisplayTest { @@ -17,6 +19,7 @@ public partial class FMain : Form private GhostscriptViewer _viewer; private FPreview _preview = new FPreview(); private StdIOHandler _stdioHandler; + private SKBitmap _currentBitmap = null; public FMain() { @@ -48,19 +51,17 @@ private void FMain_Load(object sender, EventArgs e) void _viewer_DisplayPage(object sender, GhostscriptViewerViewEventArgs e) { - _preview.pbDisplay.Invalidate(); - _preview.pbDisplay.Update(); + _preview.UpdateImage(e.Image); } void _viewer_DisplayUpdate(object sender, GhostscriptViewerViewEventArgs e) { - _preview.pbDisplay.Invalidate(); - _preview.pbDisplay.Update(); + _preview.UpdateImage(e.Image); } void _viewer_DisplaySize(object sender, GhostscriptViewerViewEventArgs e) { - _preview.pbDisplay.Image = e.Image; + _preview.UpdateImage(e.Image); } private void btnRun_Click(object sender, EventArgs e) diff --git a/Ghostscript.NET.DisplayTest/FPreview.Designer.cs b/Ghostscript.NET.DisplayTest/FPreview.Designer.cs index d9c248a..0d39bac 100644 --- a/Ghostscript.NET.DisplayTest/FPreview.Designer.cs +++ b/Ghostscript.NET.DisplayTest/FPreview.Designer.cs @@ -28,8 +28,7 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - this.pbDisplay = new System.Windows.Forms.PictureBox(); - ((System.ComponentModel.ISupportInitialize)(this.pbDisplay)).BeginInit(); + this.pbDisplay = new SkiaSharp.Views.Desktop.SKControl(); this.SuspendLayout(); // // pbDisplay @@ -37,9 +36,7 @@ private void InitializeComponent() this.pbDisplay.Location = new System.Drawing.Point(0, 0); this.pbDisplay.Name = "pbDisplay"; this.pbDisplay.Size = new System.Drawing.Size(75, 75); - this.pbDisplay.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; this.pbDisplay.TabIndex = 0; - this.pbDisplay.TabStop = false; // // FPreview // @@ -52,14 +49,12 @@ private void InitializeComponent() this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; this.Text = "Ghostscript.NET Display"; this.Load += new System.EventHandler(this.FPreview_Load); - ((System.ComponentModel.ISupportInitialize)(this.pbDisplay)).EndInit(); this.ResumeLayout(false); - this.PerformLayout(); } #endregion - public System.Windows.Forms.PictureBox pbDisplay; + public SkiaSharp.Views.Desktop.SKControl pbDisplay; } } \ No newline at end of file diff --git a/Ghostscript.NET.DisplayTest/FPreview.cs b/Ghostscript.NET.DisplayTest/FPreview.cs index c0ce04e..1e3032f 100644 --- a/Ghostscript.NET.DisplayTest/FPreview.cs +++ b/Ghostscript.NET.DisplayTest/FPreview.cs @@ -6,22 +6,77 @@ using System.Linq; using System.Text; using System.Windows.Forms; +using SkiaSharp; +using SkiaSharp.Views.Desktop; namespace Ghostscript.NET.DisplayTest { public partial class FPreview : Form { + private SKBitmap _currentBitmap = null; + public FPreview() { InitializeComponent(); this.Left = 0; this.Top = 0; + + pbDisplay.PaintSurface += PbDisplay_PaintSurface; + } + + private void PbDisplay_PaintSurface(object sender, SKPaintSurfaceEventArgs e) + { + var canvas = e.Surface.Canvas; + canvas.Clear(SKColors.White); + + if (_currentBitmap != null) + { + var destRect = SKRect.Create(pbDisplay.Width, pbDisplay.Height); + var sourceRect = SKRect.Create(_currentBitmap.Width, _currentBitmap.Height); + canvas.DrawBitmap(_currentBitmap, sourceRect, destRect); + } + } + + public void UpdateImage(SKBitmap bitmap) + { + _currentBitmap = bitmap; + ResizeCanvas(bitmap); + RefreshCanvas(); } private void FPreview_Load(object sender, EventArgs e) { } + + private void ResizeCanvas(SKBitmap bitmap) + { + if (bitmap == null) + { + return; + } + + if (pbDisplay.Width == bitmap.Width && pbDisplay.Height == bitmap.Height) + { + return; + } + + pbDisplay.SuspendLayout(); + pbDisplay.Width = bitmap.Width; + pbDisplay.Height = bitmap.Height; + pbDisplay.ResumeLayout(); + } + + private void RefreshCanvas() + { + if (!pbDisplay.IsHandleCreated) + { + return; + } + + pbDisplay.Invalidate(); + pbDisplay.Update(); + } } } diff --git a/Ghostscript.NET.DisplayTest/Ghostscript.NET.DisplayTest.csproj b/Ghostscript.NET.DisplayTest/Ghostscript.NET.DisplayTest.csproj index c005817..58b4002 100644 --- a/Ghostscript.NET.DisplayTest/Ghostscript.NET.DisplayTest.csproj +++ b/Ghostscript.NET.DisplayTest/Ghostscript.NET.DisplayTest.csproj @@ -1,8 +1,9 @@  net6.0-windows - x86 - WinExe + AnyCPU + Exe + AnyCPU;x64 @@ -34,4 +35,8 @@ + + + + \ No newline at end of file diff --git a/Ghostscript.NET.Samples/Program.cs b/Ghostscript.NET.Samples/Program.cs index 9586a1e..e8ac4b6 100644 --- a/Ghostscript.NET.Samples/Program.cs +++ b/Ghostscript.NET.Samples/Program.cs @@ -24,10 +24,13 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -using System; -using System.Collections.Generic; using Ghostscript.NET; +using Ghostscript.NET.Rasterizer; using Ghostscript.NET.Samples; +using SkiaSharp; +using System; +using System.Collections.Generic; +using System.IO; Console.WriteLine("Ghostscript.NET Samples"); @@ -58,4 +61,26 @@ Console.WriteLine($"Sample '{sample.GetType().Name}' run successful!"); } -Console.ReadLine(); +GhostscriptVersionInfo lastVersion = GhostscriptVersionInfo.GetLastInstalledVersion(); + +using (var rasterizer = new GhostscriptRasterizer()) +{ + rasterizer.Open(@"e:\Tmp\test.pdf", lastVersion, false); + + for (var pageNumber = 1; pageNumber <= rasterizer.PageCount; pageNumber++) + { + var pageFilePath = Path.Combine(@"e:\Tmp\", string.Format("SkisSharp-{0}.png", pageNumber)); + + var img = rasterizer.GetPage(300, pageNumber); + using (var image = SKImage.FromBitmap(img)) + using (var data = image.Encode(SKEncodedImageFormat.Png, 100)) + using (var stream = File.OpenWrite(pageFilePath)) + { + data.SaveTo(stream); + } + + Console.WriteLine(pageFilePath); + } +} + +Console.ReadKey(); \ No newline at end of file diff --git a/Ghostscript.NET.Samples/Samples/RasterizerCropSample.cs b/Ghostscript.NET.Samples/Samples/RasterizerCropSample.cs index 87bf7fd..7a0d9a8 100644 --- a/Ghostscript.NET.Samples/Samples/RasterizerCropSample.cs +++ b/Ghostscript.NET.Samples/Samples/RasterizerCropSample.cs @@ -25,9 +25,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; -using System.Drawing; -using System.Drawing.Imaging; using System.IO; +using SkiaSharp; // required Ghostscript.NET namespaces using Ghostscript.NET; @@ -63,8 +62,16 @@ public void Start() { string pageFilePath = Path.Combine(outputPath, "Page-" + pageNumber.ToString() + ".png"); - Image img = rasterizer.GetPage(desired_dpi, pageNumber); - img.Save(pageFilePath, ImageFormat.Png); + SKBitmap img = rasterizer.GetPage(desired_dpi, pageNumber); + if (img != null) + { + using (var image = SKImage.FromBitmap(img)) + using (var data = image.Encode(SKEncodedImageFormat.Png, 100)) + using (var stream = File.OpenWrite(pageFilePath)) + { + data.SaveTo(stream); + } + } Console.WriteLine(pageFilePath); } diff --git a/Ghostscript.NET.Samples/Samples/RasterizerSample1.cs b/Ghostscript.NET.Samples/Samples/RasterizerSample1.cs index c211f77..2c060c9 100644 --- a/Ghostscript.NET.Samples/Samples/RasterizerSample1.cs +++ b/Ghostscript.NET.Samples/Samples/RasterizerSample1.cs @@ -30,6 +30,7 @@ using System.IO; using Ghostscript.NET.Rasterizer; using Ghostscript.NET.Samples.StdIOHandlers; +using SkiaSharp; namespace Ghostscript.NET.Samples { @@ -64,7 +65,15 @@ public void Sample1() var pageFilePath = Path.Combine(outputPath, string.Format("Page-{0}.png", pageNumber)); var img = rasterizer.GetPage(desired_dpi, pageNumber); - img.Save(pageFilePath, ImageFormat.Png); + if (img != null) + { + using (var image = SKImage.FromBitmap(img)) + using (var data = image.Encode(SKEncodedImageFormat.Png, 100)) + using (var stream = File.OpenWrite(pageFilePath)) + { + data.SaveTo(stream); + } + } Console.WriteLine(pageFilePath); } @@ -92,7 +101,15 @@ public void Sample2() var pageFilePath = Path.Combine(outputPath, string.Format("Page-{0}.png", pageNumber)); var img = rasterizer.GetPage(desired_dpi, pageNumber); - img.Save(pageFilePath, ImageFormat.Png); + if (img != null) + { + using (var image = SKImage.FromBitmap(img)) + using (var data = image.Encode(SKEncodedImageFormat.Png, 100)) + using (var stream = File.OpenWrite(pageFilePath)) + { + data.SaveTo(stream); + } + } Console.WriteLine(pageFilePath); } diff --git a/Ghostscript.NET.Samples/Samples/RasterizerSample2.cs b/Ghostscript.NET.Samples/Samples/RasterizerSample2.cs index 90f9735..20f0d66 100644 --- a/Ghostscript.NET.Samples/Samples/RasterizerSample2.cs +++ b/Ghostscript.NET.Samples/Samples/RasterizerSample2.cs @@ -32,6 +32,7 @@ // required Ghostscript.NET namespaces using Ghostscript.NET; using Ghostscript.NET.Rasterizer; +using SkiaSharp; namespace Ghostscript.NET.Samples { @@ -67,8 +68,16 @@ public void Start() { string pageFilePath = Path.Combine(outputPath, "Page-" + pageNumber.ToString() + ".png"); - Image img = rasterizer.GetPage(desired_dpi, pageNumber); - img.Save(pageFilePath, ImageFormat.Png); + var img = rasterizer.GetPage(desired_dpi, pageNumber); + if (img != null) + { + using (var image = SKImage.FromBitmap(img)) + using (var data = image.Encode(SKEncodedImageFormat.Png, 100)) + using (var stream = File.OpenWrite(pageFilePath)) + { + data.SaveTo(stream); + } + } Console.WriteLine(pageFilePath); } diff --git a/Ghostscript.NET.Samples/Samples/ViewerSample.cs b/Ghostscript.NET.Samples/Samples/ViewerSample.cs index 7a0920e..5e440d4 100644 --- a/Ghostscript.NET.Samples/Samples/ViewerSample.cs +++ b/Ghostscript.NET.Samples/Samples/ViewerSample.cs @@ -26,7 +26,7 @@ using System; using System.Collections.Generic; -using System.Drawing; +using SkiaSharp; // required Ghostscript.NET namespaces using Ghostscript.NET; @@ -38,7 +38,7 @@ public class ViewerSample : ISample { private GhostscriptVersionInfo _lastInstalledVersion = null; private GhostscriptViewer _viewer = null; - private Bitmap _pdfPage = null; + private SKBitmap _pdfPage = null; public void Start() { @@ -92,7 +92,7 @@ void _viewer_DisplayUpdate(object sender, GhostscriptViewerViewEventArgs e) // it by calling PictureBox.Invalidate() and PictureBox.Update() // methods. We dont need to set image reference again because // Ghostscript.NET is changing Image object directly in the - // memory and does not create new Bitmap instance. + // memory and does not create new SKBitmap instance. } // this is the last raised event after complete page is rasterized diff --git a/Ghostscript.NET.VS2022.sln b/Ghostscript.NET.VS2022.sln index 063c814..9d05cd1 100644 --- a/Ghostscript.NET.VS2022.sln +++ b/Ghostscript.NET.VS2022.sln @@ -46,18 +46,18 @@ Global {294FE872-0D67-45B7-ADFB-1BA2F9EA01CD}.Release|x64.Build.0 = Release|x64 {294FE872-0D67-45B7-ADFB-1BA2F9EA01CD}.Release|x86.ActiveCfg = Release|Any CPU {294FE872-0D67-45B7-ADFB-1BA2F9EA01CD}.Release|x86.Build.0 = Release|Any CPU - {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Debug|Any CPU.ActiveCfg = Debug|x86 - {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Debug|Any CPU.Build.0 = Debug|x86 - {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Debug|x64.ActiveCfg = Debug|Any CPU - {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Debug|x64.Build.0 = Debug|Any CPU - {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Debug|x86.ActiveCfg = Debug|x86 - {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Debug|x86.Build.0 = Debug|x86 - {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Release|Any CPU.ActiveCfg = Release|x86 - {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Release|Any CPU.Build.0 = Release|x86 - {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Release|x64.ActiveCfg = Release|Any CPU - {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Release|x64.Build.0 = Release|Any CPU - {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Release|x86.ActiveCfg = Release|x86 - {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Release|x86.Build.0 = Release|x86 + {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Debug|x64.ActiveCfg = Debug|x64 + {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Debug|x64.Build.0 = Debug|x64 + {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Debug|x86.ActiveCfg = Debug|x64 + {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Debug|x86.Build.0 = Debug|x64 + {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Release|Any CPU.Build.0 = Release|Any CPU + {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Release|x64.ActiveCfg = Release|x64 + {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Release|x64.Build.0 = Release|x64 + {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Release|x86.ActiveCfg = Release|x64 + {C099EB3A-B4D7-4379-AFAB-4FCD4DF4F003}.Release|x86.Build.0 = Release|x64 {48CD0D6E-58A0-472C-8696-42084DECEF69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {48CD0D6E-58A0-472C-8696-42084DECEF69}.Debug|Any CPU.Build.0 = Debug|Any CPU {48CD0D6E-58A0-472C-8696-42084DECEF69}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -70,16 +70,16 @@ Global {48CD0D6E-58A0-472C-8696-42084DECEF69}.Release|x64.Build.0 = Release|Any CPU {48CD0D6E-58A0-472C-8696-42084DECEF69}.Release|x86.ActiveCfg = Release|Any CPU {48CD0D6E-58A0-472C-8696-42084DECEF69}.Release|x86.Build.0 = Release|Any CPU - {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Debug|Any CPU.ActiveCfg = Debug|x64 - {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Debug|Any CPU.Build.0 = Debug|x64 + {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Debug|Any CPU.Build.0 = Debug|Any CPU {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Debug|x64.ActiveCfg = Debug|x64 {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Debug|x64.Build.0 = Debug|x64 {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Debug|x86.ActiveCfg = Debug|x86 {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Debug|x86.Build.0 = Debug|x86 - {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Release|Any CPU.ActiveCfg = Release|x64 - {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Release|Any CPU.Build.0 = Release|x64 - {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Release|x64.ActiveCfg = Release|Any CPU - {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Release|x64.Build.0 = Release|Any CPU + {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Release|Any CPU.Build.0 = Release|Any CPU + {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Release|x64.ActiveCfg = Release|x64 + {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Release|x64.Build.0 = Release|x64 {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Release|x86.ActiveCfg = Release|x86 {3509F0F7-A7AD-4FEE-B388-AA817F3413E9}.Release|x86.Build.0 = Release|x86 EndGlobalSection diff --git a/Ghostscript.NET.Viewer/FDebug.cs b/Ghostscript.NET.Viewer/FDebug.cs index 41eb357..865d455 100644 --- a/Ghostscript.NET.Viewer/FDebug.cs +++ b/Ghostscript.NET.Viewer/FDebug.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.Data; using System.Drawing; +using SkiaSharp; using System.Linq; using System.Text; using System.Windows.Forms; diff --git a/Ghostscript.NET.Viewer/FMain.Designer.cs b/Ghostscript.NET.Viewer/FMain.Designer.cs index e768994..0a7120a 100644 --- a/Ghostscript.NET.Viewer/FMain.Designer.cs +++ b/Ghostscript.NET.Viewer/FMain.Designer.cs @@ -69,7 +69,7 @@ private void InitializeComponent() this.panel1 = new System.Windows.Forms.Panel(); this.panel2 = new System.Windows.Forms.Panel(); this.panel3 = new System.Windows.Forms.Panel(); - this.pbPage = new System.Windows.Forms.PictureBox(); + this.pbPage = new SkiaSharp.Views.Desktop.SKControl(); this.mnuMain.SuspendLayout(); this.statusStrip1.SuspendLayout(); this.panTop.SuspendLayout(); @@ -78,7 +78,6 @@ private void InitializeComponent() this.panGDN.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); this.panel3.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.pbPage)).BeginInit(); this.SuspendLayout(); // // mnuMain @@ -444,9 +443,7 @@ private void InitializeComponent() this.pbPage.Location = new System.Drawing.Point(4, 4); this.pbPage.Name = "pbPage"; this.pbPage.Size = new System.Drawing.Size(126, 118); - this.pbPage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; this.pbPage.TabIndex = 1; - this.pbPage.TabStop = false; // // FMain // @@ -477,8 +474,6 @@ private void InitializeComponent() this.panGDN.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); this.panel3.ResumeLayout(false); - this.panel3.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.pbPage)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); @@ -517,7 +512,7 @@ private void InitializeComponent() private System.Windows.Forms.Panel panel3; private System.Windows.Forms.ToolStripStatusLabel lblGsVersion; private System.Windows.Forms.ToolStripLabel tbTotalPages; - private System.Windows.Forms.PictureBox pbPage; + private SkiaSharp.Views.Desktop.SKControl pbPage; private System.Windows.Forms.ToolStripStatusLabel lblSystemInformation; private System.Windows.Forms.ToolStripButton tpZoomOut; private System.Windows.Forms.ToolStripButton tpZoomIn; diff --git a/Ghostscript.NET.Viewer/FMain.cs b/Ghostscript.NET.Viewer/FMain.cs index 71a7b3c..570e139 100644 --- a/Ghostscript.NET.Viewer/FMain.cs +++ b/Ghostscript.NET.Viewer/FMain.cs @@ -35,6 +35,8 @@ using System.IO; using System.Runtime.InteropServices; using Ghostscript.NET.Viewer; +using SkiaSharp; +using SkiaSharp.Views.Desktop; namespace Ghostscript.NET.Viewer { @@ -45,6 +47,7 @@ public partial class FMain : Form private StringBuilder _stdOut = new StringBuilder(); private StringBuilder _stdErr = new StringBuilder(); private bool _supressPageNumberChangeEvent = false; + private SKBitmap _currentBitmap = null; public FMain() { @@ -54,6 +57,7 @@ public FMain() pbPage.Width = 100; pbPage.Height = 100; + pbPage.PaintSurface += PbPage_PaintSurface; _viewer = new GhostscriptViewer(); _viewer.AttachStdIO(new GhostscriptStdIOHandler(_stdOut, _stdErr)); @@ -63,21 +67,37 @@ public FMain() _viewer.DisplayPage += new GhostscriptViewerViewEventHandler(_viewer_DisplayPage); } + private void PbPage_PaintSurface(object sender, SKPaintSurfaceEventArgs e) + { + var canvas = e.Surface.Canvas; + canvas.Clear(SKColors.White); + + if (_currentBitmap != null) + { + var destRect = SKRect.Create(pbPage.Width, pbPage.Height); + var sourceRect = SKRect.Create(_currentBitmap.Width, _currentBitmap.Height); + canvas.DrawBitmap(_currentBitmap, sourceRect, destRect); + } + } + void _viewer_DisplaySize(object sender, GhostscriptViewerViewEventArgs e) { - pbPage.Image = e.Image; + _currentBitmap = e.Image; + ResizeCanvas(e.Image); + RefreshCanvas(); } void _viewer_DisplayUpdate(object sender, GhostscriptViewerViewEventArgs e) { - pbPage.Invalidate(); - pbPage.Update(); + _currentBitmap = e.Image; + RefreshCanvas(); } void _viewer_DisplayPage(object sender, GhostscriptViewerViewEventArgs e) { - pbPage.Invalidate(); - pbPage.Update(); + _currentBitmap = e.Image; + ResizeCanvas(e.Image); + RefreshCanvas(); _supressPageNumberChangeEvent = true; tbPageNumber.Text = _viewer.CurrentPageNumber.ToString(); @@ -86,6 +106,35 @@ void _viewer_DisplayPage(object sender, GhostscriptViewerViewEventArgs e) tbTotalPages.Text = " / " + _viewer.LastPageNumber.ToString(); } + private void ResizeCanvas(SKBitmap bitmap) + { + if (bitmap == null) + { + return; + } + + if (pbPage.Width == bitmap.Width && pbPage.Height == bitmap.Height) + { + return; + } + + pbPage.SuspendLayout(); + pbPage.Width = bitmap.Width; + pbPage.Height = bitmap.Height; + pbPage.ResumeLayout(); + } + + private void RefreshCanvas() + { + if (!pbPage.IsHandleCreated) + { + return; + } + + pbPage.Invalidate(); + pbPage.Update(); + } + private void FMain_Load(object sender, EventArgs e) { lblSystemInformation.Text = "Operating system: " + (Environment.Is64BitOperatingSystem ? "64-bit" : "32-bit") + " " + @@ -120,7 +169,8 @@ private void mnuFileClose_Click(object sender, EventArgs e) _stdOut.Clear(); _stdErr.Clear(); - pbPage.Image = null; + _currentBitmap = null; + pbPage.Invalidate(); this.Text = Program.NAME; tbPageNumber.Text = string.Empty; tbTotalPages.Text = string.Empty; diff --git a/Ghostscript.NET.Viewer/Ghostscript.NET.Viewer.csproj b/Ghostscript.NET.Viewer/Ghostscript.NET.Viewer.csproj index fd43b33..14a952c 100644 --- a/Ghostscript.NET.Viewer/Ghostscript.NET.Viewer.csproj +++ b/Ghostscript.NET.Viewer/Ghostscript.NET.Viewer.csproj @@ -29,4 +29,8 @@ + + + + \ No newline at end of file diff --git a/Ghostscript.NET/Ghostscript.NET.csproj b/Ghostscript.NET/Ghostscript.NET.csproj index 9f628cf..bb3d2bc 100644 --- a/Ghostscript.NET/Ghostscript.NET.csproj +++ b/Ghostscript.NET/Ghostscript.NET.csproj @@ -6,7 +6,7 @@ bin\$(Configuration)\$(TargetFramework)\Ghostscript.NET.xml True Ghostscript.NET - 1.3.1 + 1.3.2-rc.3 Artifex Software Inc. Artifex A C# binding for Ghostscript library @@ -55,7 +55,7 @@ - + diff --git a/Ghostscript.NET/GhostscriptLibrary.cs b/Ghostscript.NET/GhostscriptLibrary.cs index 50e8892..ac7c63e 100644 --- a/Ghostscript.NET/GhostscriptLibrary.cs +++ b/Ghostscript.NET/GhostscriptLibrary.cs @@ -277,7 +277,7 @@ private void InitializeWindows() gsapi_revision_s rev = new gsapi_revision_s(); if (this.gsapi_revision(ref rev, System.Runtime.InteropServices.Marshal.SizeOf(rev)) == 0) { - _revision = rev.revision; + _revision = (int)rev.revision; } this.gsapi_new_instance = _library.GetDelegateForFunction("gsapi_new_instance"); @@ -374,7 +374,7 @@ private void InitializeLinux() gsapi_revision_s rev = new gsapi_revision_s(); if (this.gsapi_revision(ref rev, System.Runtime.InteropServices.Marshal.SizeOf(rev)) == 0) { - _revision = rev.revision; + _revision = (int)rev.revision; } this.gsapi_new_instance = _crossPlatformLibrary.GetDelegateForFunction("gsapi_new_instance"); diff --git a/Ghostscript.NET/GhostscriptVersionInfo.cs b/Ghostscript.NET/GhostscriptVersionInfo.cs index 95789be..d43248d 100644 --- a/Ghostscript.NET/GhostscriptVersionInfo.cs +++ b/Ghostscript.NET/GhostscriptVersionInfo.cs @@ -307,109 +307,88 @@ public static List GetInstalledVersionsLinux(Ghostscript return versions; } + // Library names to search for (in order of preference - newer versions first) + string[] libraryNames = { "libgs.so.10", "libgs.so.9", "libgs.so" }; + // Search for libgs.so in common locations string[] searchPaths = CrossPlatformNativeLibraryHelper.GetCommonInstallationPaths(); foreach (string basePath in searchPaths) { - if (Directory.Exists(basePath)) + if (!Directory.Exists(basePath)) + { + continue; + } + + // Check for libraries directly in basePath + foreach (string libName in libraryNames) { - // Look for libgs.so directly - string libPath = Path.Combine(basePath, "libgs.so.10"); + string libPath = Path.Combine(basePath, libName); if (File.Exists(libPath)) { - try - { - // Try to get version information from the library - Version version = GetVersionFromLinuxLibrary(libPath); - if (version != null) - { - versions.Add(new GhostscriptVersionInfo(version, libPath, basePath, licenseType)); - } - else - { - // If we can't get version info, create a generic version - versions.Add(new GhostscriptVersionInfo(new Version(0, 0), libPath, basePath, licenseType)); - } - } - catch - { - // If we can't get version info, create a generic version - versions.Add(new GhostscriptVersionInfo(new Version(0, 0), libPath, basePath, licenseType)); - } + TryAddVersion(versions, libPath, basePath, licenseType); + break; // Found a library in this path, no need to check other names } - else + } + + // Look in subdirectories + try + { + string[] subdirs = Directory.GetDirectories(basePath); + string[] subdirPaths = { "lib", "lib64", "bin" }; + + foreach (string subdir in subdirs) { - libPath = Path.Combine(basePath, "libgs.so"); - if (File.Exists(libPath)) + foreach (string subdirPath in subdirPaths) { - try - { - // Try to get version information from the library - Version version = GetVersionFromLinuxLibrary(libPath); - if (version != null) - { - versions.Add(new GhostscriptVersionInfo(version, libPath, basePath, licenseType)); - } - else - { - // If we can't get version info, create a generic version - versions.Add(new GhostscriptVersionInfo(new Version(0, 0), libPath, basePath, licenseType)); - } - } - catch + string subdirFullPath = Path.Combine(subdir, subdirPath); + if (!Directory.Exists(subdirFullPath)) { - // If we can't get version info, create a generic version - versions.Add(new GhostscriptVersionInfo(new Version(0, 0), libPath, basePath, licenseType)); + continue; } - } - } - // Look in subdirectories - try - { - string[] subdirs = Directory.GetDirectories(basePath); - foreach (string subdir in subdirs) - { - string[] possiblePaths = { - Path.Combine(subdir, "lib", "libgs.so.10"), - Path.Combine(subdir, "lib64", "libgs.so.10"), - Path.Combine(subdir, "bin", "libgs.so.10"), - Path.Combine(subdir, "lib", "libgs.so"), - Path.Combine(subdir, "lib64", "libgs.so"), - Path.Combine(subdir, "bin", "libgs.so") - }; - - foreach (string possiblePath in possiblePaths) + foreach (string libName in libraryNames) { + string possiblePath = Path.Combine(subdirFullPath, libName); if (File.Exists(possiblePath)) { - try - { - Version version = GetVersionFromLinuxLibrary(possiblePath); - if (version != null) - { - versions.Add(new GhostscriptVersionInfo(version, possiblePath, subdir, licenseType)); - } - } - catch - { - versions.Add(new GhostscriptVersionInfo(new Version(0, 0), possiblePath, subdir, licenseType)); - } + TryAddVersion(versions, possiblePath, subdir, licenseType); + break; // Found a library in this subdirectory, no need to check other names } } } } - catch - { - // Ignore directory access errors - } + } + catch + { + // Ignore directory access errors } } return versions; } + /// + /// Helper method to try adding a version from a library path. + /// + private static void TryAddVersion(List versions, string libPath, string basePath, GhostscriptLicense licenseType) + { + try + { + Version version = GetVersionFromLinuxLibrary(libPath); + versions.Add(new GhostscriptVersionInfo( + version ?? new Version(0, 0), + libPath, + basePath, + licenseType)); + } + catch + { + // If we can't get version info, create a generic version + versions.Add(new GhostscriptVersionInfo(new Version(0, 0), libPath, basePath, licenseType)); + } + } + #endregion #region GetVersionFromLinuxLibrary diff --git a/Ghostscript.NET/Helpers/ImageMemoryHelper.cs b/Ghostscript.NET/Helpers/ImageMemoryHelper.cs index 24e3123..10e763a 100644 --- a/Ghostscript.NET/Helpers/ImageMemoryHelper.cs +++ b/Ghostscript.NET/Helpers/ImageMemoryHelper.cs @@ -36,17 +36,18 @@ internal class ImageMemoryHelper public unsafe static void Set24bppRgbImageColor(IntPtr image, int width, int height, byte r, byte g, byte b) { byte* ptr = (byte*)image; - int stride = (((width * 3) + 3) & ~3); + int stride = (((width * 4) + 3) & ~3); // BGRA8888 is 4 bytes per pixel - int padding = stride - (width * 3); + int padding = stride - (width * 4); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - *ptr++ = r; - *ptr++ = g; - *ptr++ = b; + *ptr++ = b; // B + *ptr++ = g; // G + *ptr++ = r; // R + *ptr++ = 255; // A (fully opaque) } ptr+=padding; @@ -55,6 +56,35 @@ public unsafe static void Set24bppRgbImageColor(IntPtr image, int width, int hei #endregion + #region ConvertRgb24ToBgra32 + + public unsafe static void ConvertRgb24ToBgra32(IntPtr srcBgr24, IntPtr destBgra32, int width, int height, int srcStride, int destStride) + { + byte* srcPtr = (byte*)srcBgr24; + byte* destPtr = (byte*)destBgra32; + + for (int y = 0; y < height; y++) + { + byte* srcRow = srcPtr + (y * srcStride); + byte* destRow = destPtr + (y * destStride); + + for (int x = 0; x < width; x++) + { + // Ghostscript provides BGR format (little-endian: Blue/Black first) + byte b = *srcRow++; + byte g = *srcRow++; + byte r = *srcRow++; + + *destRow++ = b; // B + *destRow++ = g; // G + *destRow++ = r; // R + *destRow++ = 255; // A (fully opaque) + } + } + } + + #endregion + #region CopyImagePartFrom public static void CopyImagePartFrom(IntPtr src, IntPtr dest, int x, int y, int width, int height, int stride, int bytesPerPixel) @@ -66,13 +96,17 @@ public static void CopyImagePartFrom(IntPtr src, IntPtr dest, int x, int y, int int srcBottom = y + height - 1; int posSrcTop = 0; int posDestTop = 0; + int bytesToCopy = width * bytesPerPixel; while (srcTop <= srcBottom) { posSrcTop = (srcTop * (stride)) + (x * bytesPerPixel); posDestTop = (destTop * (destStride)); - wdm.MoveMemory(new IntPtr((long)dest + posDestTop), new IntPtr((long)src + posSrcTop), (uint)(width * bytesPerPixel)); + // Cross-platform memory copy using Marshal.Copy + byte[] buffer = new byte[bytesToCopy]; + Marshal.Copy(new IntPtr((long)src + posSrcTop), buffer, 0, bytesToCopy); + Marshal.Copy(buffer, 0, new IntPtr((long)dest + posDestTop), bytesToCopy); srcTop++; destTop++; @@ -92,13 +126,17 @@ public static void CopyImagePartTo(IntPtr dest, IntPtr src, int x, int y, int wi int destBottom = y + height - 1; int posDestTop = 0; int posSrcTop = 0; + int bytesToCopy = width * bytesPerPixel; while (destTop <= destBottom) { posDestTop = (destTop * stride) + (x * bytesPerPixel); posSrcTop = (srcTop * partStride); - wdm.MoveMemory(new IntPtr((long)dest + posDestTop), new IntPtr((long)src + posSrcTop), (uint)(width * bytesPerPixel)); + // Cross-platform memory copy using Marshal.Copy + byte[] buffer = new byte[bytesToCopy]; + Marshal.Copy(new IntPtr((long)src + posSrcTop), buffer, 0, bytesToCopy); + Marshal.Copy(buffer, 0, new IntPtr((long)dest + posDestTop), bytesToCopy); destTop++; srcTop++; @@ -107,6 +145,43 @@ public static void CopyImagePartTo(IntPtr dest, IntPtr src, int x, int y, int wi #endregion + #region CopyImagePartToRgb24ToBgra32 + + public unsafe static void CopyImagePartToRgb24ToBgra32(IntPtr destBgra32, IntPtr srcBgr24, int x, int y, int width, int height, int destStride, int srcStride) + { + byte* srcPtr = (byte*)srcBgr24; + byte* destPtr = (byte*)destBgra32; + + for (int row = 0; row < height; row++) + { + int srcY = row; + int destY = y + row; + + byte* srcRow = srcPtr + (srcY * srcStride); + byte* destRow = destPtr + (destY * destStride) + (x * 4); // BGRA is 4 bytes per pixel + + for (int col = 0; col < width; col++) + { + int srcX = col; + byte* srcPixel = srcRow + (srcX * 3); // BGR is 3 bytes per pixel + + // Ghostscript provides BGR format (little-endian: Blue/Black first) + byte b = srcPixel[0]; + byte g = srcPixel[1]; + byte r = srcPixel[2]; + + destRow[0] = b; // B + destRow[1] = g; // G + destRow[2] = r; // R + destRow[3] = 255; // A (fully opaque) + + destRow += 4; + } + } + } + + #endregion + #region FlipImageVertically public static void FlipImageVertically(IntPtr src, IntPtr dest, int height, int stride) diff --git a/Ghostscript.NET/OutputDevices/GhostscriptDevice.cs b/Ghostscript.NET/OutputDevices/GhostscriptDevice.cs index 6ffa9c9..7a0ab9b 100644 --- a/Ghostscript.NET/OutputDevices/GhostscriptDevice.cs +++ b/Ghostscript.NET/OutputDevices/GhostscriptDevice.cs @@ -27,7 +27,7 @@ using System; using System.Collections.Generic; using System.Reflection; -using System.Drawing; +using SkiaSharp; using Ghostscript.NET.Processor; namespace Ghostscript.NET @@ -189,11 +189,11 @@ public string[] GetSwitches() parameters.Add(string.Format(switchName, valueAttribute.Value)); } } - else if (valueType == typeof(Color)) + else if (valueType == typeof(SKColor)) { - Color color = (Color)value; + SKColor color = (SKColor)value; - string hexColor = "16#" + color.R.ToString("X2") + color.G.ToString("X2") + color.B.ToString("X2"); + string hexColor = "16#" + color.Red.ToString("X2") + color.Green.ToString("X2") + color.Blue.ToString("X2"); parameters.Add(string.Format(switchName, hexColor)); } diff --git a/Ghostscript.NET/OutputDevices/GhostscriptJpegDevice.cs b/Ghostscript.NET/OutputDevices/GhostscriptJpegDevice.cs index 8b96ae3..772500b 100644 --- a/Ghostscript.NET/OutputDevices/GhostscriptJpegDevice.cs +++ b/Ghostscript.NET/OutputDevices/GhostscriptJpegDevice.cs @@ -25,7 +25,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; -using System.Drawing; +using SkiaSharp; namespace Ghostscript.NET { diff --git a/Ghostscript.NET/OutputDevices/GhostscriptPngDevice.cs b/Ghostscript.NET/OutputDevices/GhostscriptPngDevice.cs index 9537050..d154a94 100644 --- a/Ghostscript.NET/OutputDevices/GhostscriptPngDevice.cs +++ b/Ghostscript.NET/OutputDevices/GhostscriptPngDevice.cs @@ -25,7 +25,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; -using System.Drawing; +using SkiaSharp; namespace Ghostscript.NET { @@ -142,7 +142,7 @@ public GhostscriptPngDevice(GhostscriptPngDeviceType deviceType) #region BackgroundColor [GhostscriptSwitch("-dBackgroundColor={0}")] - public Color? BackgroundColor { get; set; } + public SKColor? BackgroundColor { get; set; } #endregion diff --git a/Ghostscript.NET/Rasterizer/GhostscriptRasterizer.cs b/Ghostscript.NET/Rasterizer/GhostscriptRasterizer.cs index 7d915cc..a3faee4 100644 --- a/Ghostscript.NET/Rasterizer/GhostscriptRasterizer.cs +++ b/Ghostscript.NET/Rasterizer/GhostscriptRasterizer.cs @@ -26,9 +26,9 @@ using System; using System.IO; -using System.Drawing; using Ghostscript.NET.Viewer; using System.Collections.Generic; +using SkiaSharp; namespace Ghostscript.NET.Rasterizer { @@ -39,7 +39,7 @@ public class GhostscriptRasterizer : IDisposable private bool _disposed = false; private GhostscriptViewer _viewer; - private Image _lastRasterizedImage = null; + private SKBitmap _lastRasterizedImage = null; private GhostscriptViewerState _gsViewState; #endregion @@ -280,12 +280,12 @@ public int PageCount #region GetPage /// - /// Gets PDF page as System.Drawing.Image. + /// Gets PDF page as SkiaSharp.SKBitmap. /// /// Desired dpi. /// The page number. - /// PDF page represented as System.Drawing.Image. - public Image GetPage(int dpi, int pageNumber) + /// PDF page represented as SkiaSharp.SKBitmap. + public SKBitmap GetPage(int dpi, int pageNumber) { _viewer.Dpi = dpi; _viewer.ShowPage(pageNumber, true); @@ -300,7 +300,7 @@ void _viewer_DisplayPage(object sender, GhostscriptViewerViewEventArgs e) { if (e.Image != null) { - _lastRasterizedImage = e.Image.Clone() as Image; + _lastRasterizedImage = e.Image.Copy(); } } diff --git a/Ghostscript.NET/Viewer/FormatHandlers/GhostscriptViewerDefaultFormatHandler.cs b/Ghostscript.NET/Viewer/FormatHandlers/GhostscriptViewerDefaultFormatHandler.cs index 658f08d..6b8434f 100644 --- a/Ghostscript.NET/Viewer/FormatHandlers/GhostscriptViewerDefaultFormatHandler.cs +++ b/Ghostscript.NET/Viewer/FormatHandlers/GhostscriptViewerDefaultFormatHandler.cs @@ -27,7 +27,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; -using System.Drawing; namespace Ghostscript.NET.Viewer { diff --git a/Ghostscript.NET/Viewer/FormatHandlers/GhostscriptViewerPdfFormatHandler.cs b/Ghostscript.NET/Viewer/FormatHandlers/GhostscriptViewerPdfFormatHandler.cs index db13b45..40c2390 100644 --- a/Ghostscript.NET/Viewer/FormatHandlers/GhostscriptViewerPdfFormatHandler.cs +++ b/Ghostscript.NET/Viewer/FormatHandlers/GhostscriptViewerPdfFormatHandler.cs @@ -27,7 +27,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; -using System.Drawing; using System.Globalization; namespace Ghostscript.NET.Viewer diff --git a/Ghostscript.NET/Viewer/FormatHandlers/GhostscriptViewerPsFormatHandler.cs b/Ghostscript.NET/Viewer/FormatHandlers/GhostscriptViewerPsFormatHandler.cs index b9819fe..d0f3c0d 100644 --- a/Ghostscript.NET/Viewer/FormatHandlers/GhostscriptViewerPsFormatHandler.cs +++ b/Ghostscript.NET/Viewer/FormatHandlers/GhostscriptViewerPsFormatHandler.cs @@ -27,7 +27,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; -using System.Drawing; using System.IO; using System.Text; using System.Collections.Generic; diff --git a/Ghostscript.NET/Viewer/GhostscriptViewer.cs b/Ghostscript.NET/Viewer/GhostscriptViewer.cs index 7f21b75..665cbe1 100644 --- a/Ghostscript.NET/Viewer/GhostscriptViewer.cs +++ b/Ghostscript.NET/Viewer/GhostscriptViewer.cs @@ -26,7 +26,6 @@ using System; using System.IO; -using System.Drawing; using System.Collections.Generic; using System.Globalization; using Ghostscript.NET.Interpreter; diff --git a/Ghostscript.NET/Viewer/GhostscriptViewerDisplayHandler.cs b/Ghostscript.NET/Viewer/GhostscriptViewerDisplayHandler.cs index 292248f..a281447 100644 --- a/Ghostscript.NET/Viewer/GhostscriptViewerDisplayHandler.cs +++ b/Ghostscript.NET/Viewer/GhostscriptViewerDisplayHandler.cs @@ -26,9 +26,8 @@ using System; using System.Text; -using System.Drawing; -using System.Drawing.Imaging; using System.Runtime.InteropServices; +using SkiaSharp; namespace Ghostscript.NET.Viewer { @@ -99,7 +98,7 @@ public override int Presize(IntPtr handle, IntPtr device, int width, int height, _destImage.Dispose(); _destImage = null; } - _destImage = GhostscriptViewerImage.Create(width, height, raster, PixelFormat.Format24bppRgb); + _destImage = GhostscriptViewerImage.Create(width, height, raster, SKColorType.Bgra8888); return 0; } @@ -119,11 +118,11 @@ public override int Size(IntPtr handle, IntPtr device, int width, int height, in _destImage.Dispose(); _destImage = null; } - _destImage = GhostscriptViewerImage.Create(width, height, raster, PixelFormat.Format24bppRgb); + _destImage = GhostscriptViewerImage.Create(width, height, raster, SKColorType.Bgra8888); _viewer.FormatHandler.ShowPagePostScriptCommandInvoked = false; - _viewer.RaiseDisplaySize(new GhostscriptViewerViewEventArgs(_destImage, new Rectangle(0, 0, width, height))); + _viewer.RaiseDisplaySize(new GhostscriptViewerViewEventArgs(_destImage, new SKRectI(0, 0, width, height))); return 0; } @@ -149,21 +148,30 @@ public override int Page(IntPtr handle, IntPtr device, int copies, int flush) if (!_viewer.ProgressiveUpdate || _viewer.Interpreter.GhostscriptLibrary.Revision > 950) { - int bytesPerPixel = 3; + int srcBytesPerPixel = 3; // BGR from Ghostscript (little-endian: Blue/Black first) + int destBytesPerPixel = 4; // BGRA for SkiaSharp _destImage.Lock(); - IntPtr tempTile = Marshal.AllocHGlobal(_destImage.Stride * _destImage.Height); + // Allocate temporary buffer for BGR24 data + int srcStrideSize = (((_destImage.Width * srcBytesPerPixel) + 3) & ~3); + IntPtr tempTile = Marshal.AllocHGlobal(srcStrideSize * _destImage.Height); - ImageMemoryHelper.CopyImagePartFrom(_srcImage, tempTile, 0, 0, _destImage.Width, _destImage.Height, _srcStride, bytesPerPixel); - ImageMemoryHelper.FlipImageVertically(tempTile, _destImage.Scan0, _destImage.Height, _destImage.Stride); + // Copy BGR24 from source + ImageMemoryHelper.CopyImagePartFrom(_srcImage, tempTile, 0, 0, _destImage.Width, _destImage.Height, _srcStride, srcBytesPerPixel); + + // Flip vertically + ImageMemoryHelper.FlipImageVertically(tempTile, tempTile, _destImage.Height, srcStrideSize); + + // Convert BGR24 to BGRA32 and copy to destination + ImageMemoryHelper.ConvertRgb24ToBgra32(tempTile, _destImage.Scan0, _destImage.Width, _destImage.Height, srcStrideSize, _destImage.Stride); Marshal.FreeHGlobal(tempTile); _destImage.Unlock(); } - _viewer.RaiseDisplayPage(new GhostscriptViewerViewEventArgs(_destImage, new Rectangle(0, 0, _destImage.Width, _destImage.Height))); + _viewer.RaiseDisplayPage(new GhostscriptViewerViewEventArgs(_destImage, new SKRectI(0, 0, _destImage.Width, _destImage.Height))); return 0; } @@ -176,14 +184,14 @@ public override int Update(IntPtr handle, IntPtr device, int x, int y, int w, in { if (_viewer.ProgressiveUpdate) { - int bytesPerPixel = 3; + int srcBytesPerPixel = 3; // BGR from Ghostscript (little-endian: Blue/Black first) + int destBytesPerPixel = 4; // BGRA for SkiaSharp if (_srcImage != IntPtr.Zero) { _destImage.Lock(); - int destStrideSize = (((_destImage.Width * bytesPerPixel) + 3) & ~3); - int tileStride = (((w * bytesPerPixel) + 3) & ~3); + int srcTileStride = (((w * srcBytesPerPixel) + 3) & ~3); if (_synchTriggered) { @@ -199,15 +207,19 @@ public override int Update(IntPtr handle, IntPtr device, int x, int y, int w, in return 0; } - IntPtr tempTile = Marshal.AllocHGlobal(tileStride * h); + // Allocate temporary buffer for BGR24 tile + IntPtr tempTile = Marshal.AllocHGlobal(srcTileStride * h); - ImageMemoryHelper.CopyImagePartFrom(_srcImage, tempTile, x, y, w, h, _srcStride, bytesPerPixel); + // Copy BGR24 tile from source + ImageMemoryHelper.CopyImagePartFrom(_srcImage, tempTile, x, y, w, h, _srcStride, srcBytesPerPixel); - ImageMemoryHelper.FlipImageVertically(tempTile, tempTile, h, tileStride); + // Flip vertically + ImageMemoryHelper.FlipImageVertically(tempTile, tempTile, h, srcTileStride); int tileMirrorY = _destImage.Height - y - h; - ImageMemoryHelper.CopyImagePartTo(_destImage.Scan0, tempTile, x, tileMirrorY, w, h, destStrideSize, bytesPerPixel); + // Convert BGR24 to BGRA32 and copy to destination + ImageMemoryHelper.CopyImagePartToRgb24ToBgra32(_destImage.Scan0, tempTile, x, tileMirrorY, w, h, _destImage.Stride, srcTileStride); Marshal.FreeHGlobal(tempTile); @@ -217,7 +229,7 @@ public override int Update(IntPtr handle, IntPtr device, int x, int y, int w, in { _lastUpdateTime = Environment.TickCount; - _viewer.RaiseDisplayUpdate(new GhostscriptViewerViewEventArgs(_destImage, new Rectangle(0, 0, _destImage.Width, _destImage.Height))); + _viewer.RaiseDisplayUpdate(new GhostscriptViewerViewEventArgs(_destImage, new SKRectI(0, 0, _destImage.Width, _destImage.Height))); } } } diff --git a/Ghostscript.NET/Viewer/GhostscriptViewerFormatHandler.cs b/Ghostscript.NET/Viewer/GhostscriptViewerFormatHandler.cs index 0ed6234..42321b9 100644 --- a/Ghostscript.NET/Viewer/GhostscriptViewerFormatHandler.cs +++ b/Ghostscript.NET/Viewer/GhostscriptViewerFormatHandler.cs @@ -25,7 +25,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; -using System.Drawing; namespace Ghostscript.NET.Viewer { diff --git a/Ghostscript.NET/Viewer/GhostscriptViewerImage.cs b/Ghostscript.NET/Viewer/GhostscriptViewerImage.cs index 78064f6..9334ab2 100644 --- a/Ghostscript.NET/Viewer/GhostscriptViewerImage.cs +++ b/Ghostscript.NET/Viewer/GhostscriptViewerImage.cs @@ -25,8 +25,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; -using System.Drawing; -using System.Drawing.Imaging; +using SkiaSharp; namespace Ghostscript.NET.Viewer { @@ -38,24 +37,22 @@ public class GhostscriptViewerImage : IDisposable private bool _disposed = false; private int _width; private int _height; - private Rectangle _rect; private int _stride; - private Bitmap _bitmap; - private BitmapData _bitmapData; + private SKBitmap _bitmap; + private IntPtr _pixelsPtr; + private bool _isLocked = false; #endregion #region Constructor - public GhostscriptViewerImage(int width, int height, int stride, PixelFormat format) + public GhostscriptViewerImage(int width, int height, int stride, SKColorType colorType) { _width = width; _height = height; _stride = stride; - _rect = new Rectangle(0, 0, _width, _height); - - _bitmap = new Bitmap(width, height, format); + _bitmap = new SKBitmap(width, height, colorType, SKAlphaType.Opaque); } #endregion @@ -89,12 +86,12 @@ protected virtual void Dispose(bool disposing) { if (disposing) { - if (_bitmapData != null) + if (_isLocked) { this.Unlock(); } - _bitmap.Dispose(); + _bitmap?.Dispose(); _bitmap = null; } @@ -108,9 +105,9 @@ protected virtual void Dispose(bool disposing) #region Create - public static GhostscriptViewerImage Create(int width, int height, int stride, PixelFormat format) + public static GhostscriptViewerImage Create(int width, int height, int stride, SKColorType colorType) { - GhostscriptViewerImage gvi = new GhostscriptViewerImage(width, height, stride, format); + GhostscriptViewerImage gvi = new GhostscriptViewerImage(width, height, stride, colorType); return gvi; } @@ -120,9 +117,10 @@ public static GhostscriptViewerImage Create(int width, int height, int stride, P internal void Lock() { - if (_bitmapData == null) + if (!_isLocked) { - _bitmapData = _bitmap.LockBits(_rect, ImageLockMode.WriteOnly, _bitmap.PixelFormat); + _pixelsPtr = _bitmap.GetPixels(); + _isLocked = true; } } @@ -132,7 +130,7 @@ internal void Lock() internal IntPtr Scan0 { - get { return _bitmapData.Scan0; } + get { return _pixelsPtr; } } #endregion @@ -141,7 +139,7 @@ internal IntPtr Scan0 public int Stride { - get { return _bitmapData.Stride; } + get { return _bitmap.RowBytes; } } #endregion @@ -150,10 +148,10 @@ public int Stride internal void Unlock() { - if (_bitmapData != null) + if (_isLocked) { - _bitmap.UnlockBits(_bitmapData); - _bitmapData = null; + _pixelsPtr = IntPtr.Zero; + _isLocked = false; } } @@ -179,7 +177,7 @@ public int Height #region Bitmap - public Bitmap @Bitmap + public SKBitmap @Bitmap { get { return _bitmap; } } diff --git a/Ghostscript.NET/Viewer/GhostscriptViewerView.cs b/Ghostscript.NET/Viewer/GhostscriptViewerView.cs index 0ae01b0..8f2317e 100644 --- a/Ghostscript.NET/Viewer/GhostscriptViewerView.cs +++ b/Ghostscript.NET/Viewer/GhostscriptViewerView.cs @@ -25,7 +25,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; -using System.Drawing; +using SkiaSharp; namespace Ghostscript.NET.Viewer { @@ -40,23 +40,23 @@ public class GhostscriptViewerViewEventArgs : EventArgs #region Private variables private GhostscriptViewerImage _image; - private RectangleF _mediaBox; + private SKRect _mediaBox; #endregion #region Constructor - internal GhostscriptViewerViewEventArgs(GhostscriptViewerImage image, Rectangle mediaBox) + internal GhostscriptViewerViewEventArgs(GhostscriptViewerImage image, SKRectI mediaBox) { _image = image; - _mediaBox = mediaBox; + _mediaBox = new SKRect(mediaBox.Left, mediaBox.Top, mediaBox.Right, mediaBox.Bottom); } #endregion #region Image - public Bitmap Image + public SKBitmap Image { get { return _image.Bitmap; } } @@ -66,7 +66,7 @@ public Bitmap Image #region MediaBox - public RectangleF MediaBox + public SKRect MediaBox { get { return _mediaBox; } } diff --git a/Ghostscript.NET/gs/iapi.h.cs b/Ghostscript.NET/gs/iapi.h.cs index 9717661..eb17c14 100644 --- a/Ghostscript.NET/gs/iapi.h.cs +++ b/Ghostscript.NET/gs/iapi.h.cs @@ -39,8 +39,8 @@ public struct gsapi_revision_s { public IntPtr product; public IntPtr copyright; - public int revision; - public int revisiondate; + public long revision; + public long revisiondate; } #endregion