namespace Uwaa.PNG;
internal static class Adam7
{
///
/// For a given pass number (1 indexed) the scanline indexes of the lines included in that pass in the 8x8 grid.
///
static readonly IReadOnlyDictionary PassToScanlineGridIndex = new Dictionary
{
{ 1, [ 0 ] },
{ 2, [ 0 ] },
{ 3, [ 4 ] },
{ 4, [ 0, 4 ] },
{ 5, [ 2, 6 ] },
{ 6, [ 0, 2, 4, 6 ] },
{ 7, [ 1, 3, 5, 7 ] }
};
static readonly IReadOnlyDictionary PassToScanlineColumnIndex = new Dictionary
{
{ 1, [ 0 ] },
{ 2, [ 4 ] },
{ 3, [ 0, 4 ] },
{ 4, [ 2, 6 ] },
{ 5, [ 0, 2, 4, 6 ] },
{ 6, [ 1, 3, 5, 7 ] },
{ 7, [ 0, 1, 2, 3, 4, 5, 6, 7 ] }
};
/*
* To go from raw image data to interlaced:
*
* An 8x8 grid is repeated over the image. There are 7 passes and the indexes in this grid correspond to the
* pass number including that pixel. Each row in the grid corresponds to a scanline.
*
* 1 6 4 6 2 6 4 6 - Scanline 0: pass 1 has pixel 0, 8, 16, etc. pass 2 has pixel 4, 12, 20, etc.
* 7 7 7 7 7 7 7 7
* 5 6 5 6 5 6 5 6
* 7 7 7 7 7 7 7 7
* 3 6 4 6 3 6 4 6
* 7 7 7 7 7 7 7 7
* 5 6 5 6 5 6 5 6
* 7 7 7 7 7 7 7 7
*
*
*
*/
public static int GetNumberOfScanlinesInPass(ImageHeader header, int pass)
{
int[] indices = PassToScanlineGridIndex[pass + 1];
int mod = header.Height % 8;
if (mod == 0) //fits exactly
return indices.Length * (header.Height / 8);
int additionalLines = 0;
for (int i = 0; i < indices.Length; i++)
if (indices[i] < mod)
additionalLines++;
return (indices.Length * (header.Height / 8)) + additionalLines;
}
public static int GetPixelsPerScanlineInPass(ImageHeader header, int pass)
{
int[] indices = PassToScanlineColumnIndex[pass + 1];
int mod = header.Width % 8;
if (mod == 0) //fits exactly
return indices.Length * (header.Width / 8);
int additionalColumns = 0;
for (int i = 0; i < indices.Length; i++)
if (indices[i] < mod)
additionalColumns++;
return (indices.Length * (header.Width / 8)) + additionalColumns;
}
public static (int x, int y) GetPixelIndexForScanlineInPass(int pass, int scanlineIndex, int indexInScanline)
{
int[] columnIndices = PassToScanlineColumnIndex[pass + 1];
int[] rows = PassToScanlineGridIndex[pass + 1];
int actualRow = scanlineIndex % rows.Length;
int actualCol = indexInScanline % columnIndices.Length;
int precedingRows = 8 * (scanlineIndex / rows.Length);
int precedingCols = 8 * (indexInScanline / columnIndices.Length);
return (precedingCols + columnIndices[actualCol], precedingRows + rows[actualRow]);
}
}