Files
2023-10-12 16:07:14 +11:00

123 lines
4.4 KiB
C#

using Disco.Models.Services.Expressions.Extensions;
using Disco.Services.Documents;
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using ZXing.QrCode.Internal;
namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
{
public class QrCodeImageExpressionResult : BaseImageExpressionResult
{
public static Func<byte[], int, int, byte[]> CCITTG4EncoderCompressDelegate;
public string Data { get; }
public char ErrorCorrectionLevel { get; }
private readonly ErrorCorrectionLevel ecLevel;
public QrCodeImageExpressionResult(string data, char errorCorrectionLevel)
{
Data = data;
ErrorCorrectionLevel = errorCorrectionLevel;
switch (errorCorrectionLevel)
{
case 'l':
case 'L':
ecLevel = ZXing.QrCode.Internal.ErrorCorrectionLevel.L;
break;
case 'm':
case 'M':
ecLevel = ZXing.QrCode.Internal.ErrorCorrectionLevel.M;
break;
case 'q':
case 'Q':
ecLevel = ZXing.QrCode.Internal.ErrorCorrectionLevel.Q;
break;
case 'h':
case 'H':
ecLevel = ZXing.QrCode.Internal.ErrorCorrectionLevel.H;
break;
default:
throw new ArgumentException("Error correction level must be L, M, Q or H");
}
Format = ImageExpressionFormat.CcittG4;
Quality = 90;
ShowField = false;
BackgroundPreferTransparent = true;
}
public override MemoryStream GetImage(int width, int height)
{
var rawImage = QRCodeBinaryEncoder.Encode(Data, ecLevel, width, height);
return RenderStream(rawImage, width, height);
}
public override MemoryStream GetImage(out int width, out int height)
{
var rawImage = QRCodeBinaryEncoder.Encode(Data, ecLevel, out width, out height);
return RenderStream(rawImage, width, height);
}
private MemoryStream RenderStream(byte[] data, int width, int height)
{
if (Format == ImageExpressionFormat.CcittG4)
{
if (CCITTG4EncoderCompressDelegate == null)
throw new InvalidOperationException($"The {CCITTG4EncoderCompressDelegate} delegate has not been initialized");
var result = CCITTG4EncoderCompressDelegate(data, width, height);
return new MemoryStream(result, 0, result.Length, false, true);
}
else
{
using (var bitmap = RenderBitmap(data, width, height))
{
return OutputBitmapImage(bitmap);
}
}
}
private Image RenderBitmap(byte[] data, int width, int height)
{
var bitmap = new Bitmap(width, height, PixelFormat.Format1bppIndexed);
bitmap.Palette.Entries[0] = Color.Black;
bitmap.Palette.Entries[0] = Color.White;
var pixelData = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
try
{
for (int y = 0; y < height; y++)
{
var offset = y * width;
var buffer = new byte[(width + 7) / 8];
var bufferOffset = 0;
var bitOffset = 0;
for (int x = 0; x < width; x++)
{
var pixel = data[offset + x];
if (pixel != 0)
{
buffer[bufferOffset] = (byte)(buffer[bufferOffset] | (byte)((128) >> bitOffset));
}
if (bitOffset == 8)
{
bufferOffset++;
bitOffset = 0;
}
}
Marshal.Copy(buffer, 0, pixelData.Scan0, buffer.Length);
}
}
finally
{
bitmap.UnlockBits(pixelData);
}
return bitmap;
}
}
}