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/>.
|
|
|
|
|
//
|
|
|
|
|
// ----------------------------------------------------------------------------
|
2024-05-01 04:17:32 +01:00
|
|
|
// Copyright © 2011-2024 Natalia Portillo
|
2022-12-05 21:28:43 +00:00
|
|
|
// ****************************************************************************/
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2023-10-04 17:34:40 +01:00
|
|
|
while(_sectorsPerSquare > 0 && _squareSize > 4)
|
2022-12-05 21:28:43 +00:00
|
|
|
{
|
|
|
|
|
_squareSize--;
|
|
|
|
|
|
|
|
|
|
_columns = (width - 1) / (_squareSize + 1);
|
|
|
|
|
rows = (height - 1) / (_squareSize + 1);
|
|
|
|
|
squares = _columns * rows;
|
|
|
|
|
|
|
|
|
|
_sectorsPerSquare = (int)((long)maxSectors / squares);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-03 22:57:50 +01:00
|
|
|
var removeSquaresAtLastRow = 0;
|
|
|
|
|
var removeRows = 0;
|
2022-12-05 21:28:43 +00:00
|
|
|
|
|
|
|
|
// If we have spare squares, remove them
|
|
|
|
|
if(squares > (long)maxSectors)
|
|
|
|
|
{
|
2023-10-03 22:57:50 +01:00
|
|
|
var removeSquares = (int)(squares - (long)maxSectors);
|
2022-12-05 21:28:43 +00:00
|
|
|
removeRows = removeSquares / _columns;
|
|
|
|
|
removeSquaresAtLastRow = removeSquares % _columns;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-03 22:57:50 +01:00
|
|
|
float w = _columns * (_squareSize + 1) + 1;
|
|
|
|
|
float h = rows * (_squareSize + 1) + 1;
|
2022-12-05 21:28:43 +00:00
|
|
|
|
|
|
|
|
_bitmap = new SKBitmap((int)w, (int)h);
|
|
|
|
|
_canvas = new SKCanvas(_bitmap);
|
|
|
|
|
|
|
|
|
|
// Paint background white
|
2024-05-01 04:05:22 +01:00
|
|
|
_canvas.DrawRect(0,
|
|
|
|
|
0,
|
|
|
|
|
w,
|
|
|
|
|
h,
|
|
|
|
|
new SKPaint
|
|
|
|
|
{
|
|
|
|
|
Style = SKPaintStyle.StrokeAndFill,
|
|
|
|
|
Color = SKColors.White
|
|
|
|
|
});
|
2022-12-05 21:28:43 +00:00
|
|
|
|
|
|
|
|
// Paint undumped sectors
|
2024-05-01 04:05:22 +01:00
|
|
|
_canvas.DrawRect(0,
|
|
|
|
|
0,
|
|
|
|
|
w,
|
|
|
|
|
h - removeRows * (_squareSize + 1) - _squareSize - 2,
|
|
|
|
|
new SKPaint
|
|
|
|
|
{
|
|
|
|
|
Style = SKPaintStyle.StrokeAndFill,
|
|
|
|
|
Color = SKColors.Gray
|
|
|
|
|
});
|
2022-12-05 21:28:43 +00:00
|
|
|
|
2024-05-01 04:05:22 +01:00
|
|
|
_canvas.DrawRect(0,
|
|
|
|
|
h - removeRows * (_squareSize + 1) - _squareSize - 2,
|
|
|
|
|
(_columns - removeSquaresAtLastRow) * (_squareSize + 1),
|
|
|
|
|
_squareSize + 2,
|
|
|
|
|
new SKPaint
|
2022-12-05 21:28:43 +00:00
|
|
|
{
|
|
|
|
|
Style = SKPaintStyle.StrokeAndFill,
|
|
|
|
|
Color = SKColors.Gray
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Draw grid
|
2023-10-03 22:57:50 +01:00
|
|
|
for(float y = 0; y < h - removeRows * (_squareSize + 1); y += _squareSize + 1)
|
2022-12-05 21:28:43 +00:00
|
|
|
{
|
2023-10-03 22:57:50 +01:00
|
|
|
if(y > h - removeRows * (_squareSize + 1) - (_squareSize + 2))
|
2022-12-05 21:28:43 +00:00
|
|
|
{
|
|
|
|
|
int cw = _columns - removeSquaresAtLastRow;
|
|
|
|
|
|
2024-05-01 04:05:22 +01:00
|
|
|
_canvas.DrawLine(0f,
|
|
|
|
|
y,
|
|
|
|
|
cw * (_squareSize + 1),
|
|
|
|
|
y,
|
|
|
|
|
new SKPaint
|
|
|
|
|
{
|
|
|
|
|
StrokeWidth = 1f,
|
|
|
|
|
Color = SKColors.Black
|
|
|
|
|
});
|
2022-12-05 21:28:43 +00:00
|
|
|
}
|
|
|
|
|
else
|
2023-10-03 22:57:50 +01:00
|
|
|
{
|
2024-05-01 04:05:22 +01:00
|
|
|
_canvas.DrawLine(0f,
|
|
|
|
|
y,
|
|
|
|
|
w,
|
|
|
|
|
y,
|
|
|
|
|
new SKPaint
|
|
|
|
|
{
|
|
|
|
|
StrokeWidth = 1f,
|
|
|
|
|
Color = SKColors.Black
|
|
|
|
|
});
|
2023-10-03 22:57:50 +01:00
|
|
|
}
|
2022-12-05 21:28:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(float x = 0; x < w; x += _squareSize + 1)
|
|
|
|
|
{
|
|
|
|
|
float currentColumn = x / (_squareSize + 1);
|
|
|
|
|
|
|
|
|
|
if(_columns - currentColumn + 1 > removeSquaresAtLastRow)
|
2023-10-03 22:57:50 +01:00
|
|
|
{
|
2024-05-01 04:05:22 +01:00
|
|
|
_canvas.DrawLine(x,
|
|
|
|
|
0,
|
|
|
|
|
x,
|
|
|
|
|
h - removeRows * (_squareSize + 1),
|
|
|
|
|
new SKPaint
|
|
|
|
|
{
|
|
|
|
|
StrokeWidth = 1f,
|
|
|
|
|
Color = SKColors.Black
|
|
|
|
|
});
|
2023-10-03 22:57:50 +01:00
|
|
|
}
|
2022-12-05 21:28:43 +00:00
|
|
|
else
|
2023-10-03 22:57:50 +01:00
|
|
|
{
|
2024-05-01 04:05:22 +01:00
|
|
|
_canvas.DrawLine(x,
|
|
|
|
|
0,
|
|
|
|
|
x,
|
|
|
|
|
h - removeRows * (_squareSize + 1) - _squareSize - 2,
|
|
|
|
|
new SKPaint
|
|
|
|
|
{
|
|
|
|
|
StrokeWidth = 1f,
|
|
|
|
|
Color = SKColors.Black
|
|
|
|
|
});
|
2023-10-03 22:57:50 +01:00
|
|
|
}
|
2022-12-05 21:28:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-03 22:57:50 +01:00
|
|
|
#region IMediaGraph Members
|
|
|
|
|
|
2022-12-05 21:28:43 +00:00
|
|
|
/// <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();
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-03 22:57:50 +01:00
|
|
|
#endregion
|
|
|
|
|
|
2022-12-05 21:28:43 +00:00
|
|
|
void PaintSector(ulong sector, SKColor color)
|
|
|
|
|
{
|
|
|
|
|
SKRect rect =
|
|
|
|
|
GetSquareRectangle(_sectorsPerSquare == 0 ? (int)sector : (int)(sector / (ulong)_sectorsPerSquare));
|
|
|
|
|
|
2024-05-01 04:05:22 +01:00
|
|
|
_canvas.DrawRect(rect,
|
|
|
|
|
new SKPaint
|
|
|
|
|
{
|
|
|
|
|
Style = SKPaintStyle.StrokeAndFill,
|
|
|
|
|
Color = color
|
|
|
|
|
});
|
2022-12-05 21:28:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
|
2024-05-01 04:05:22 +01:00
|
|
|
_canvas.DrawRect(rect,
|
|
|
|
|
new SKPaint
|
|
|
|
|
{
|
|
|
|
|
Style = SKPaintStyle.StrokeAndFill,
|
|
|
|
|
Color = color
|
|
|
|
|
});
|
2022-12-05 21:28:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PaintSectors(IEnumerable<ulong> sectors, SKColor color)
|
|
|
|
|
{
|
2023-10-03 22:57:50 +01:00
|
|
|
foreach(SKRect rect in sectors.Select(sector => GetSquareRectangle(_sectorsPerSquare == 0
|
|
|
|
|
? (int)sector
|
2023-10-04 17:34:40 +01:00
|
|
|
: (int)(sector /
|
|
|
|
|
(ulong)_sectorsPerSquare))))
|
2022-12-05 21:28:43 +00:00
|
|
|
{
|
2024-05-01 04:05:22 +01:00
|
|
|
_canvas.DrawRect(rect,
|
|
|
|
|
new SKPaint
|
|
|
|
|
{
|
|
|
|
|
Style = SKPaintStyle.StrokeAndFill,
|
|
|
|
|
Color = color
|
|
|
|
|
});
|
2022-12-05 21:28:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SKRect GetSquareRectangle(int square)
|
|
|
|
|
{
|
|
|
|
|
int row = square / _columns;
|
|
|
|
|
int column = square % _columns;
|
|
|
|
|
|
2023-10-03 22:57:50 +01:00
|
|
|
float x = 1 + column * (_squareSize + 1);
|
|
|
|
|
float y = 1 + row * (_squareSize + 1);
|
2022-12-05 21:28:43 +00:00
|
|
|
float xp = x + _squareSize;
|
|
|
|
|
float yp = y + _squareSize;
|
|
|
|
|
|
|
|
|
|
return new SKRect(x, y, xp, yp);
|
|
|
|
|
}
|
|
|
|
|
}
|