152 lines
4.1 KiB
C#
152 lines
4.1 KiB
C#
/*
|
|
* Copyright (c) 2023 ETH Zürich, Educational Development and Technology (LET)
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
using System;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Windows.Forms;
|
|
using KGySoft.Drawing;
|
|
using KGySoft.Drawing.Imaging;
|
|
using SafeExamBrowser.Logging.Contracts;
|
|
using SafeExamBrowser.Settings.Proctoring;
|
|
using ImageFormat = SafeExamBrowser.Settings.Proctoring.ImageFormat;
|
|
|
|
namespace SafeExamBrowser.Proctoring.ScreenProctoring.Imaging
|
|
{
|
|
internal class ScreenShot : IDisposable
|
|
{
|
|
private readonly ILogger logger;
|
|
private readonly ScreenProctoringSettings settings;
|
|
|
|
internal Bitmap Bitmap { get; private set; }
|
|
internal byte[] Data { get; private set; }
|
|
internal ImageFormat Format { get; private set; }
|
|
internal int Height { get; private set; }
|
|
internal int Width { get; private set; }
|
|
|
|
public ScreenShot(ILogger logger, ScreenProctoringSettings settings)
|
|
{
|
|
this.logger = logger;
|
|
this.settings = settings;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Bitmap?.Dispose();
|
|
Bitmap = default;
|
|
Data = default;
|
|
}
|
|
|
|
public string ToReducedString()
|
|
{
|
|
return $"{Width}x{Height}, {Data.Length / 1000:N0} kB, {Format.ToString().ToUpper()}";
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return $"resolution: {Width}x{Height}, size: {Data.Length / 1000:N0} kB, format: {Format.ToString().ToUpper()}";
|
|
}
|
|
|
|
internal void Compress()
|
|
{
|
|
var order = ProcessingOrder.QuantizingDownscaling;
|
|
var original = ToReducedString();
|
|
var parameters = $"{order}, {settings.ImageQuantization}, 1:{settings.ImageDownscaling}";
|
|
|
|
switch (order)
|
|
{
|
|
case ProcessingOrder.DownscalingQuantizing:
|
|
Downscale();
|
|
Quantize();
|
|
Serialize();
|
|
break;
|
|
case ProcessingOrder.QuantizingDownscaling:
|
|
Quantize();
|
|
Downscale();
|
|
Serialize();
|
|
break;
|
|
}
|
|
|
|
logger.Debug($"Compressed from '{original}' to '{ToReducedString()}' ({parameters}).");
|
|
}
|
|
|
|
internal void Take()
|
|
{
|
|
var x = Screen.AllScreens.Min(s => s.Bounds.X);
|
|
var y = Screen.AllScreens.Min(s => s.Bounds.Y);
|
|
var width = Screen.AllScreens.Max(s => s.Bounds.X + s.Bounds.Width) - x;
|
|
var height = Screen.AllScreens.Max(s => s.Bounds.Y + s.Bounds.Height) - y;
|
|
|
|
Bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
|
|
Format = settings.ImageFormat;
|
|
Height = height;
|
|
Width = width;
|
|
|
|
using (var graphics = Graphics.FromImage(Bitmap))
|
|
{
|
|
graphics.CopyFromScreen(x, y, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy);
|
|
graphics.DrawCursorPosition();
|
|
}
|
|
|
|
Serialize();
|
|
}
|
|
|
|
private void Downscale()
|
|
{
|
|
if (settings.ImageDownscaling > 1)
|
|
{
|
|
Height = Convert.ToInt32(Height / settings.ImageDownscaling);
|
|
Width = Convert.ToInt32(Width / settings.ImageDownscaling);
|
|
|
|
var downscaled = new Bitmap(Width, Height, Bitmap.PixelFormat);
|
|
|
|
Bitmap.DrawInto(downscaled, new Rectangle(0, 0, Width, Height), ScalingMode.NearestNeighbor);
|
|
Bitmap.Dispose();
|
|
Bitmap = downscaled;
|
|
}
|
|
}
|
|
|
|
private void Quantize()
|
|
{
|
|
var ditherer = settings.ImageDownscaling > 1 ? OrderedDitherer.Bayer2x2 : default;
|
|
var pixelFormat = settings.ImageQuantization.ToPixelFormat();
|
|
var quantizer = settings.ImageQuantization.ToQuantizer();
|
|
|
|
Bitmap = Bitmap.ConvertPixelFormat(pixelFormat, quantizer, ditherer);
|
|
}
|
|
|
|
private void Serialize()
|
|
{
|
|
using (var memoryStream = new MemoryStream())
|
|
{
|
|
if (Format == ImageFormat.Jpg)
|
|
{
|
|
SerializeJpg(memoryStream);
|
|
}
|
|
else
|
|
{
|
|
Bitmap.Save(memoryStream, Format.ToSystemFormat());
|
|
}
|
|
|
|
Data = memoryStream.ToArray();
|
|
}
|
|
}
|
|
|
|
private void SerializeJpg(MemoryStream memoryStream)
|
|
{
|
|
var codec = ImageCodecInfo.GetImageEncoders().First(c => c.FormatID == System.Drawing.Imaging.ImageFormat.Jpeg.Guid);
|
|
var parameters = new EncoderParameters(1);
|
|
var quality = 100;
|
|
|
|
parameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
|
|
Bitmap.Save(memoryStream, codec, parameters);
|
|
}
|
|
}
|
|
}
|