Files
Aaru/Aaru.Core/Graphics/BlockMap.cs

281 lines
9.6 KiB
C#
Raw Normal View History

2022-12-05 21:28:43 +00:00
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : BlockMap.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Core algorithms.
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General public License for more details.
//
// You should have received a copy of the GNU General public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Aaru.CommonTypes.Interfaces;
using SkiaSharp;
namespace Aaru.Core.Graphics;
public class BlockMap : IMediaGraph
{
readonly SKBitmap _bitmap;
readonly SKCanvas _canvas;
readonly int _columns;
readonly int _sectorsPerSquare;
readonly int _squareSize;
public BlockMap(int width, int height, ulong maxSectors)
{
_squareSize = 8;
_columns = (width - 1) / (_squareSize + 1);
int rows = (height - 1) / (_squareSize + 1);
int squares = _columns * rows;
// Check if we can get bigger squares
while(squares - _columns > (long)maxSectors)
{
_squareSize++;
_columns = (width - 1) / (_squareSize + 1);
rows = (height - 1) / (_squareSize + 1);
squares = _columns * rows;
}
_sectorsPerSquare = (int)((long)maxSectors / squares);
while(_sectorsPerSquare > 0 &&
_squareSize > 4)
{
_squareSize--;
_columns = (width - 1) / (_squareSize + 1);
rows = (height - 1) / (_squareSize + 1);
squares = _columns * rows;
_sectorsPerSquare = (int)((long)maxSectors / squares);
}
int removeSquaresAtLastRow = 0;
int removeRows = 0;
// If we have spare squares, remove them
if(squares > (long)maxSectors)
{
int removeSquares = (int)(squares - (long)maxSectors);
removeRows = removeSquares / _columns;
removeSquaresAtLastRow = removeSquares % _columns;
}
float w = (_columns * (_squareSize + 1)) + 1;
float h = (rows * (_squareSize + 1)) + 1;
_bitmap = new SKBitmap((int)w, (int)h);
_canvas = new SKCanvas(_bitmap);
// Paint background white
_canvas.DrawRect(0, 0, w, h, new SKPaint
{
Style = SKPaintStyle.StrokeAndFill,
Color = SKColors.White
});
// Paint undumped sectors
_canvas.DrawRect(0, 0, w, h - (removeRows * (_squareSize + 1)) - _squareSize - 2, new SKPaint
{
Style = SKPaintStyle.StrokeAndFill,
Color = SKColors.Gray
});
_canvas.DrawRect(0, h - (removeRows * (_squareSize + 1)) - _squareSize - 2,
(_columns - removeSquaresAtLastRow) * (_squareSize + 1), _squareSize + 2, new SKPaint
{
Style = SKPaintStyle.StrokeAndFill,
Color = SKColors.Gray
});
// Draw grid
for(float y = 0; y < h - (removeRows * (_squareSize + 1)); y += _squareSize + 1)
{
if(y > h - (removeRows * (_squareSize + 1)) - (_squareSize + 2))
{
int cw = _columns - removeSquaresAtLastRow;
_canvas.DrawLine(0f, y, cw * (_squareSize + 1), y, new SKPaint
{
StrokeWidth = 1f,
Color = SKColors.Black
});
}
else
_canvas.DrawLine(0f, y, w, y, new SKPaint
{
StrokeWidth = 1f,
Color = SKColors.Black
});
}
for(float x = 0; x < w; x += _squareSize + 1)
{
float currentColumn = x / (_squareSize + 1);
if(_columns - currentColumn + 1 > removeSquaresAtLastRow)
_canvas.DrawLine(x, 0, x, h - (removeRows * (_squareSize + 1)), new SKPaint
{
StrokeWidth = 1f,
Color = SKColors.Black
});
else
_canvas.DrawLine(x, 0, x, h - (removeRows * (_squareSize + 1)) - _squareSize - 2, new SKPaint
{
StrokeWidth = 1f,
Color = SKColors.Black
});
}
}
/// <inheritdoc />
public void PaintSectorGood(ulong sector) => PaintSector(sector, SKColors.Green);
/// <inheritdoc />
public void PaintSectorBad(ulong sector) => PaintSector(sector, SKColors.Red);
/// <inheritdoc />
public void PaintSectorUnknown(ulong sector) => PaintSector(sector, SKColors.Yellow);
/// <inheritdoc />
public void PaintSectorUndumped(ulong sector) => PaintSector(sector, SKColors.Gray);
/// <inheritdoc />
public void PaintSector(ulong sector, byte red, byte green, byte blue, byte opacity = 255) =>
PaintSector(sector, new SKColor(red, green, blue, opacity));
/// <inheritdoc />
public void PaintSectorsUndumped(ulong startingSector, uint length) =>
PaintSectors(startingSector, length, SKColors.Gray);
/// <inheritdoc />
public void PaintSectorsGood(ulong startingSector, uint length) =>
PaintSectors(startingSector, length, SKColors.Green);
/// <inheritdoc />
public void PaintSectorsBad(ulong startingSector, uint length) =>
PaintSectors(startingSector, length, SKColors.Red);
/// <inheritdoc />
public void PaintSectorsUnknown(ulong startingSector, uint length) =>
PaintSectors(startingSector, length, SKColors.Yellow);
/// <inheritdoc />
public void PaintSectors(ulong startingSector, uint length, byte red, byte green, byte blue, byte opacity = 255) =>
PaintSectors(startingSector, length, new SKColor(red, green, blue, opacity));
/// <inheritdoc />
public void PaintSectorsUndumped(IEnumerable<ulong> sectors) => PaintSectors(sectors, SKColors.Gray);
/// <inheritdoc />
public void PaintSectorsGood(IEnumerable<ulong> sectors) => PaintSectors(sectors, SKColors.Green);
/// <inheritdoc />
public void PaintSectorsBad(IEnumerable<ulong> sectors) => PaintSectors(sectors, SKColors.Red);
/// <inheritdoc />
public void PaintSectorsUnknown(IEnumerable<ulong> sectors) => PaintSectors(sectors, SKColors.Yellow);
/// <inheritdoc />
public void PaintSectorsUnknown(IEnumerable<ulong> sectors, byte red, byte green, byte blue, byte opacity = 255) =>
PaintSectors(sectors, new SKColor(red, green, blue, opacity));
/// <inheritdoc />
public void PaintRecordableInformationGood()
{
// Do nothing
}
/// <inheritdoc />
public void WriteTo(Stream stream)
{
var image = SKImage.FromBitmap(_bitmap);
SKData data = image.Encode();
data.SaveTo(stream);
}
/// <inheritdoc />
public void WriteTo(string path)
{
using var fs = new FileStream(path, FileMode.Create);
WriteTo(fs);
fs.Close();
}
void PaintSector(ulong sector, SKColor color)
{
SKRect rect =
GetSquareRectangle(_sectorsPerSquare == 0 ? (int)sector : (int)(sector / (ulong)_sectorsPerSquare));
_canvas.DrawRect(rect, new SKPaint
{
Style = SKPaintStyle.StrokeAndFill,
Color = color
});
}
void PaintSectors(ulong startingSector, uint length, SKColor color)
{
for(ulong sector = startingSector; sector < startingSector + length; sector++)
{
SKRect rect =
GetSquareRectangle(_sectorsPerSquare == 0 ? (int)sector : (int)(sector / (ulong)_sectorsPerSquare));
_canvas.DrawRect(rect, new SKPaint
{
Style = SKPaintStyle.StrokeAndFill,
Color = color
});
}
}
void PaintSectors(IEnumerable<ulong> sectors, SKColor color)
{
foreach(SKRect rect in sectors.Select(sector => GetSquareRectangle(_sectorsPerSquare == 0 ? (int)sector
: (int)
(sector / (ulong)_sectorsPerSquare))))
{
_canvas.DrawRect(rect, new SKPaint
{
Style = SKPaintStyle.StrokeAndFill,
Color = color
});
}
}
SKRect GetSquareRectangle(int square)
{
int row = square / _columns;
int column = square % _columns;
float x = 1 + (column * (_squareSize + 1));
float y = 1 + (row * (_squareSize + 1));
float xp = x + _squareSize;
float yp = y + _squareSize;
return new SKRect(x, y, xp, yp);
}
}