diff --git a/src/SharpEmf.Svg/BitmapUtils.cs b/src/SharpEmf.Svg/BitmapUtils.cs new file mode 100644 index 0000000..2c55b9d --- /dev/null +++ b/src/SharpEmf.Svg/BitmapUtils.cs @@ -0,0 +1,81 @@ +using SharpEmf.WmfTypes.Bitmap; +using SkiaSharp; + +namespace SharpEmf.Svg; + +internal static class BitmapUtils +{ + public static unsafe string DibToPngBase64(byte[] dibData, BitmapInfoHeader bitmapHeader) + { + // TODO: this assumes that the DIB is 24-bit RGB, handle other cases + + using var bitmap = new SKBitmap(); + + var width = bitmapHeader.Width; + var height = bitmapHeader.Height; + + var rgbaData = DibToRgba(dibData, bitmapHeader); + + fixed(byte* rgbaDataPtr = rgbaData) + { + var rgbaDataPtr2 = (IntPtr)rgbaDataPtr; + bitmap.InstallPixels(new SKImageInfo(width, height, SKColorType.Rgba8888, SKAlphaType.Unpremul), rgbaDataPtr2, width * 4); + } + + using var pngData = bitmap.Encode(SKEncodedImageFormat.Png, 100); + var resultBase64Str = Convert.ToBase64String(pngData.Span); + + return resultBase64Str; + } + + public static unsafe byte[] DibToRgba(byte[] dibData, BitmapInfoHeader bitmapHeader) + { + var usedBytes = (ushort)bitmapHeader.BitCount / 8 * bitmapHeader.Width; + // DIB data is padded to the nearest DWORD (4-byte) boundary + var padding = RoundUpToNearestMultipleOf4(usedBytes) - usedBytes; + + var rgbaStride = bitmapHeader.Width * 4; + + byte[] rgbaData = new byte[rgbaStride * bitmapHeader.Height]; + + fixed (byte* dibDataPtr = dibData) + { + fixed (byte* rgbaDataPtr = rgbaData) + { + var dibDataPtr2 = dibDataPtr; + var rgbaDataPtr2 = rgbaDataPtr; + + for (int y = 0; y < bitmapHeader.Height; y++) + { + // Due to the fact that emf arrays after reading are reversed, padding skipping is done before the X-row loop + // Bytes order in EMF file: B, G, R, PADDED, PADDED, B, G, R, PADDED, PADDED, ... + // After reversing: PADDED, PADDED, R, G, B, PADDED, PADDED, R, G, B, ... + dibDataPtr2 += padding; + + for (int x = 0; x < bitmapHeader.Width; x++) + { + var r = *dibDataPtr2; + var g = *(dibDataPtr2 + 1); + var b = *(dibDataPtr2 + 2); + const byte a = 0xFF; + + *rgbaDataPtr2 = r; + *(rgbaDataPtr2 + 1) = g; + *(rgbaDataPtr2 + 2) = b; + *(rgbaDataPtr2 + 3) = a; + + dibDataPtr2 += 3; + rgbaDataPtr2 += 4; + } + } + } + } + + return rgbaData; + } + + private static int RoundUpToNearestMultipleOf4(int num) + { + return (num + 3) / 4 * 4; + } +} \ No newline at end of file diff --git a/src/SharpEmf.Svg/RecordsProcessing/EmfBitmapRecordsHandlers.cs b/src/SharpEmf.Svg/RecordsProcessing/EmfBitmapRecordsHandlers.cs index d8ec25b..513fab1 100644 --- a/src/SharpEmf.Svg/RecordsProcessing/EmfBitmapRecordsHandlers.cs +++ b/src/SharpEmf.Svg/RecordsProcessing/EmfBitmapRecordsHandlers.cs @@ -7,18 +7,13 @@ internal static class EmfBitmapRecordsHandlers { public static void HandleStretchDIBits(StringBuilder svgSb, EmfState state, EmrStretchDiBits stretchDiBits) { - // TODO: fix this - var bitmapBase64 = Convert.ToBase64String(stretchDiBits.BitsSrc); - - var scalingForMapMode = state.GetScalingForCurrentMapMode(); - var scaleMatrix = $"matrix({scalingForMapMode.X},0,0,{scalingForMapMode.Y},0,0)"; + var bitmapBase64 = BitmapUtils.DibToPngBase64(stretchDiBits.BitsSrc, stretchDiBits.BmiHeader); var x = stretchDiBits.XDest; var y = stretchDiBits.YDest; var width = stretchDiBits.CXDest; var height = stretchDiBits.CYDest; - - svgSb.AppendLine($""); + svgSb.AppendLine($""); } } \ No newline at end of file diff --git a/src/SharpEmf.Svg/SharpEmf.Svg.csproj b/src/SharpEmf.Svg/SharpEmf.Svg.csproj index 34e08d0..e1c1937 100644 --- a/src/SharpEmf.Svg/SharpEmf.Svg.csproj +++ b/src/SharpEmf.Svg/SharpEmf.Svg.csproj @@ -4,10 +4,15 @@ net8.0 enable enable + true + + + + diff --git a/src/SharpEmf/Extensions/StreamExtensions.cs b/src/SharpEmf/Extensions/StreamExtensions.cs index 7aca345..cad631b 100644 --- a/src/SharpEmf/Extensions/StreamExtensions.cs +++ b/src/SharpEmf/Extensions/StreamExtensions.cs @@ -75,6 +75,8 @@ internal static byte[] ReadByteArray(this Stream stream, int length) var buffer = new byte[length]; stream.ReadExactly(buffer); + buffer.AsSpan().Reverse(); + return buffer; }