Refactoring Progress
This commit is contained in:
parent
ab3f7de52c
commit
cfbcbcab12
@ -1,94 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace CdgLib
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public class CdgFileIoStream
|
||||
{
|
||||
private Stream _cdgFile;
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public CdgFileIoStream()
|
||||
{
|
||||
_cdgFile = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified buf.
|
||||
/// </summary>
|
||||
/// <param name="buf">The buf.</param>
|
||||
/// <param name="bufSize">The buf_size.</param>
|
||||
/// <returns></returns>
|
||||
public int Read(ref byte[] buf, int bufSize)
|
||||
{
|
||||
return _cdgFile.Read(buf, 0, bufSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified buf.
|
||||
/// </summary>
|
||||
/// <param name="buf">The buf.</param>
|
||||
/// <param name="bufSize">The buf_size.</param>
|
||||
/// <returns></returns>
|
||||
public int Write(ref byte[] buf, int bufSize)
|
||||
{
|
||||
_cdgFile.Write(buf, 0, bufSize);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seeks the specified offset.
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <param name="whence">The whence.</param>
|
||||
/// <returns></returns>
|
||||
public int Seek(int offset, SeekOrigin whence)
|
||||
{
|
||||
return (int) _cdgFile.Seek(offset, whence);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EOFs this instance.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool Eof()
|
||||
{
|
||||
return _cdgFile.Position >= _cdgFile.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Getsizes this instance.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int Getsize()
|
||||
{
|
||||
return (int) _cdgFile.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the specified filename.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <returns></returns>
|
||||
public bool Open(string filename)
|
||||
{
|
||||
Close();
|
||||
_cdgFile = new FileStream(filename, FileMode.Open, FileAccess.Read);
|
||||
return _cdgFile != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes this instance.
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
if (_cdgFile != null)
|
||||
{
|
||||
_cdgFile.Close();
|
||||
_cdgFile = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -42,15 +42,12 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Extension\Extensions.cs" />
|
||||
<Compile Include="GraphicFileStream.cs" />
|
||||
<Compile Include="SubCode\Command.cs" />
|
||||
<Compile Include="SubCode\Instruction.cs" />
|
||||
<Compile Include="GraphicsFile.cs" />
|
||||
<Compile Include="CdgFileIoStream.cs" />
|
||||
<Compile Include="Graphic.cs" />
|
||||
<Compile Include="SubCode\Packet.cs" />
|
||||
<Compile Include="GraphicUtil.cs" />
|
||||
<Compile Include="Surface.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CdgLib.Extension
|
||||
{
|
||||
internal class Extensions
|
||||
{
|
||||
void tr
|
||||
}
|
||||
}
|
||||
@ -1,24 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.InteropServices;
|
||||
using CdgLib.SubCode;
|
||||
|
||||
namespace CdgLib
|
||||
{
|
||||
public class Graphic
|
||||
{
|
||||
private const int ColourTableSize = 16;
|
||||
private const int TileHeight = 12;
|
||||
private const int TileWidth = 6;
|
||||
public const int FullWidth = 300;
|
||||
public const int FullHeight = 216;
|
||||
|
||||
private readonly int[] _colourTable = new int[ColourTableSize];
|
||||
private readonly byte[,] _pixelColours = new byte[FullHeight, FullWidth];
|
||||
private int[,] _graphicData;
|
||||
|
||||
public Graphic(IEnumerable<Packet> packets)
|
||||
{
|
||||
foreach (var packet in packets)
|
||||
{
|
||||
packet.ApplyTransform(ref _graphicData);
|
||||
Process(packet);
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,21 +33,339 @@ namespace CdgLib
|
||||
Bitmap myBitmap;
|
||||
using (var bitmapStream = new MemoryStream())
|
||||
{
|
||||
foreach (var colourValue in _mPSurface.RgbData)
|
||||
foreach (var colourValue in _graphicData)
|
||||
{
|
||||
var colour = BitConverter.GetBytes(colourValue);
|
||||
bitmapStream.Write(colour, 0, 4);
|
||||
}
|
||||
myBitmap = GraphicUtil.StreamToBitmap(bitmapStream, FullWidth, FullHeight);
|
||||
myBitmap = StreamToBitmap(bitmapStream, FullWidth, FullHeight);
|
||||
}
|
||||
myBitmap.MakeTransparent(myBitmap.GetPixel(1, 1));
|
||||
|
||||
return myBitmap;
|
||||
}
|
||||
|
||||
private Bitmap StreamToBitmap(Stream stream, int width, int height)
|
||||
{
|
||||
//create a new bitmap
|
||||
var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
|
||||
var bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bmp.PixelFormat);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
//copy the stream of pixel
|
||||
for (var n = 0; n <= stream.Length - 1; n++)
|
||||
{
|
||||
var myByte = new byte[1];
|
||||
stream.Read(myByte, 0, 1);
|
||||
Marshal.WriteByte(bmpData.Scan0, n, myByte[0]);
|
||||
}
|
||||
bmp.UnlockBits(bmpData);
|
||||
return bmp;
|
||||
}
|
||||
|
||||
|
||||
private void Process(Packet packet)
|
||||
{
|
||||
if (packet.Command != Command.Graphic) return;
|
||||
switch (packet.Instruction)
|
||||
{
|
||||
case Instruction.MemoryPreset:
|
||||
MemoryPreset(packet);
|
||||
break;
|
||||
case Instruction.BorderPreset:
|
||||
BorderPreset(packet);
|
||||
break;
|
||||
case Instruction.TileBlockNormal:
|
||||
TileBlock(packet, false);
|
||||
break;
|
||||
case Instruction.ScrollPreset:
|
||||
Scroll(packet, false);
|
||||
break;
|
||||
case Instruction.ScrollCopy:
|
||||
Scroll(packet, true);
|
||||
break;
|
||||
case Instruction.DefineTransparentColor:
|
||||
DefineTransparentColour(packet);
|
||||
break;
|
||||
case Instruction.LoadColorTableLower:
|
||||
LoadColorTable(packet, 0);
|
||||
break;
|
||||
case Instruction.LoadColorTableUpper:
|
||||
LoadColorTable(packet, 1);
|
||||
break;
|
||||
case Instruction.TileBlockXor:
|
||||
TileBlock(packet, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void MemoryPreset(Packet packet)
|
||||
{
|
||||
var colour = packet.Data[0] & 0xf;
|
||||
var repeat = packet.Data[1] & 0xf;
|
||||
|
||||
//we have a reliable packet stream, so the repeat command
|
||||
//is executed only the first time
|
||||
if (repeat == 0)
|
||||
{
|
||||
//Note that this may be done before any load colour table
|
||||
//commands by some CDGs. So the load colour table itself
|
||||
//actual recalculates the RGB values for all pixels when
|
||||
//the colour table changes.
|
||||
|
||||
//Set the preset colour for every pixel. Must be stored in
|
||||
//the pixel colour table indeces array
|
||||
for (int rowIndex = 0; rowIndex < _pixelColours.GetLength(0); rowIndex++)
|
||||
{
|
||||
for (int columnIndex = 0; columnIndex < _pixelColours.GetLength(1); columnIndex++)
|
||||
{
|
||||
_pixelColours[rowIndex, columnIndex] = (byte)colour;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BorderPreset(Packet packet)
|
||||
{
|
||||
int rowIndex;
|
||||
int columnIndex;
|
||||
|
||||
var colour = packet.Data[0] & 0xf;
|
||||
|
||||
//The border area is the area contained with a rectangle
|
||||
//defined by (0,0,300,216) minus the interior pixels which are contained
|
||||
//within a rectangle defined by (6,12,294,204).
|
||||
|
||||
for (rowIndex = 0; rowIndex < _pixelColours.GetLength(0); rowIndex++)
|
||||
{
|
||||
for (columnIndex = 0; columnIndex < 6; columnIndex++)
|
||||
{
|
||||
_pixelColours[rowIndex, columnIndex] = (byte)colour;
|
||||
}
|
||||
|
||||
for (columnIndex = _pixelColours.GetLength(1) - 6; columnIndex < _pixelColours.GetLength(1); columnIndex++)
|
||||
{
|
||||
_pixelColours[rowIndex, columnIndex] = (byte)colour;
|
||||
}
|
||||
}
|
||||
|
||||
for (columnIndex = 6; columnIndex < _pixelColours.GetLength(1) - 6; columnIndex++)
|
||||
{
|
||||
for (rowIndex = 0; rowIndex < 12; rowIndex++)
|
||||
{
|
||||
_pixelColours[rowIndex, columnIndex] = (byte)colour;
|
||||
}
|
||||
|
||||
for (rowIndex = _pixelColours.GetLength(1) - 12; rowIndex < _pixelColours.GetLength(1); rowIndex++)
|
||||
{
|
||||
_pixelColours[rowIndex, columnIndex] = (byte)colour;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void LoadColorTable(Packet packet, int table)
|
||||
{
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
//[---high byte---] [---low byte----]
|
||||
//7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
|
||||
//X X r r r r g g X X g g b b b b
|
||||
var highByte = packet.Data[2 * i];
|
||||
var lowByte = packet.Data[2 * i + 1];
|
||||
|
||||
var red = (highByte & 0x3f) >> 2;
|
||||
var green = ((highByte & 0x3) << 2) | ((lowByte & 0x3f) >> 4);
|
||||
var blue = lowByte & 0xf;
|
||||
|
||||
//4 bit colour to 8 bit colour
|
||||
red *= 17;
|
||||
green *= 17;
|
||||
blue *= 17;
|
||||
|
||||
_colourTable[i + table*8] = Color.FromArgb(red, green, blue).ToArgb();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void TileBlock(Packet packet, bool bXor)
|
||||
{
|
||||
var colour0 = packet.Data[0] & 0xf;
|
||||
var colour1 = packet.Data[1] & 0xf;
|
||||
var rowIndex = (packet.Data[2] & 0x1f) * 12;
|
||||
var columnIndex = (packet.Data[3] & 0x3f) * 6;
|
||||
|
||||
if (rowIndex > FullHeight - TileHeight)
|
||||
return;
|
||||
if (columnIndex > FullWidth - TileWidth)
|
||||
return;
|
||||
|
||||
//Set the pixel array for each of the pixels in the 12x6 tile.
|
||||
//Normal = Set the colour to either colour0 or colour1 depending
|
||||
//on whether the pixel value is 0 or 1.
|
||||
//XOR = XOR the colour with the colour index currently there.
|
||||
|
||||
|
||||
for (var i = 0; i <= 11; i++)
|
||||
{
|
||||
var myByte = packet.Data[4 + i] & 0x3f;
|
||||
for (var j = 0; j <= 5; j++)
|
||||
{
|
||||
var pixel = (myByte >> (5 - j)) & 0x1;
|
||||
var newCol = 0;
|
||||
if (bXor)
|
||||
{
|
||||
//Tile Block XOR
|
||||
var xorCol = pixel == 0 ? colour0 : colour1;
|
||||
|
||||
//Get the colour index currently at this location, and xor with it
|
||||
int currentColourIndex = _pixelColours[rowIndex + i, columnIndex + j];
|
||||
newCol = currentColourIndex ^ xorCol;
|
||||
}
|
||||
else
|
||||
{
|
||||
newCol = pixel == 0 ? colour0 : colour1;
|
||||
}
|
||||
|
||||
//Set the pixel with the new colour. We set both the surfarray
|
||||
//containing actual RGB values, as well as our array containing
|
||||
//the colour indexes into our colour table.
|
||||
_pixelColours[rowIndex + i, columnIndex + j] = (byte)newCol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DefineTransparentColour(Packet packet)
|
||||
{
|
||||
//_mTransparentColour = packet.Data[0] & 0xf;
|
||||
}
|
||||
|
||||
|
||||
private void Scroll(Packet packet, bool copy)
|
||||
{
|
||||
//Decode the scroll command parameters
|
||||
var colour = packet.Data[0] & 0xf;
|
||||
var horizontalScroll = packet.Data[1] & 0x3f;
|
||||
var verticalScroll = packet.Data[2] & 0x3f;
|
||||
|
||||
var horizontalScrollCommand = (horizontalScroll & 0x30) >> 4;
|
||||
var horizontalOffset = horizontalScroll & 0x7;
|
||||
var verticalScrollCommand = (verticalScroll & 0x30) >> 4;
|
||||
var verticalOffset = verticalScroll & 0xf;
|
||||
|
||||
|
||||
_mHOffset = horizontalOffset < 5 ? horizontalOffset : 5;
|
||||
_mVOffset = verticalOffset < 11 ? verticalOffset : 11;
|
||||
|
||||
//Scroll Vertical - Calculate number of pixels
|
||||
|
||||
var verticalScrollPixels = 0;
|
||||
switch (verticalScrollCommand)
|
||||
{
|
||||
case 2:
|
||||
verticalScrollPixels = -12;
|
||||
break;
|
||||
case 1:
|
||||
verticalScrollPixels = 12;
|
||||
break;
|
||||
}
|
||||
|
||||
//Scroll Horizontal- Calculate number of pixels
|
||||
|
||||
var horizontalScrollPixels = 0;
|
||||
switch (horizontalScrollCommand)
|
||||
{
|
||||
case 2:
|
||||
horizontalScrollPixels = -6;
|
||||
break;
|
||||
case 1:
|
||||
horizontalScrollPixels = 6;
|
||||
break;
|
||||
}
|
||||
|
||||
if (horizontalScrollPixels == 0 && verticalScrollPixels == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Perform the actual scroll.
|
||||
|
||||
var temp = new byte[FullHeight + 1, FullWidth + 1];
|
||||
var vInc = verticalScrollPixels + FullHeight;
|
||||
var hInc = horizontalScrollPixels + FullWidth;
|
||||
var rowIndex = 0;
|
||||
//row index
|
||||
var columnIndex = 0;
|
||||
//column index
|
||||
|
||||
for (rowIndex = 0; rowIndex <= FullHeight - 1; rowIndex++)
|
||||
{
|
||||
for (columnIndex = 0; columnIndex <= FullWidth - 1; columnIndex++)
|
||||
{
|
||||
temp[(rowIndex + vInc) % FullHeight, (columnIndex + hInc) % FullWidth] = _pixelColours[rowIndex, columnIndex];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//if copy is false, we were supposed to fill in the new pixels
|
||||
//with a new colour. Go back and do that now.
|
||||
|
||||
|
||||
if (copy == false)
|
||||
{
|
||||
if (verticalScrollPixels > 0)
|
||||
{
|
||||
for (columnIndex = 0; columnIndex <= FullWidth - 1; columnIndex++)
|
||||
{
|
||||
for (rowIndex = 0; rowIndex <= verticalScrollPixels - 1; rowIndex++)
|
||||
{
|
||||
temp[rowIndex, columnIndex] = (byte)colour;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (verticalScrollPixels < 0)
|
||||
{
|
||||
for (columnIndex = 0; columnIndex <= FullWidth - 1; columnIndex++)
|
||||
{
|
||||
for (rowIndex = FullHeight + verticalScrollPixels; rowIndex <= FullHeight - 1; rowIndex++)
|
||||
{
|
||||
temp[rowIndex, columnIndex] = (byte)colour;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (horizontalScrollPixels > 0)
|
||||
{
|
||||
for (columnIndex = 0; columnIndex <= horizontalScrollPixels - 1; columnIndex++)
|
||||
{
|
||||
for (rowIndex = 0; rowIndex <= FullHeight - 1; rowIndex++)
|
||||
{
|
||||
temp[rowIndex, columnIndex] = (byte)colour;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (horizontalScrollPixels < 0)
|
||||
{
|
||||
for (columnIndex = FullWidth + horizontalScrollPixels; columnIndex <= FullWidth - 1; columnIndex++)
|
||||
{
|
||||
for (rowIndex = 0; rowIndex <= FullHeight - 1; rowIndex++)
|
||||
{
|
||||
temp[rowIndex, columnIndex] = (byte)colour;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Now copy the temporary buffer back to our array
|
||||
|
||||
for (rowIndex = 0; rowIndex <= FullHeight - 1; rowIndex++)
|
||||
{
|
||||
for (columnIndex = 0; columnIndex <= FullWidth - 1; columnIndex++)
|
||||
{
|
||||
_pixelColours[rowIndex, columnIndex] = temp[rowIndex, columnIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
46
CdgLib/GraphicFileStream.cs
Normal file
46
CdgLib/GraphicFileStream.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CdgLib.SubCode;
|
||||
|
||||
namespace CdgLib
|
||||
{
|
||||
public class GraphicFileStream : FileStream
|
||||
{
|
||||
private const int PacketSize = 24;
|
||||
|
||||
public GraphicFileStream(string path) : base(path, FileMode.Open, FileAccess.Read, FileShare.Read)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Packet>> ReadFile()
|
||||
{
|
||||
Position = 0;
|
||||
var subCodePackets = new List<Packet>();
|
||||
var buffer = new byte[Length];
|
||||
var bytesRead = await ReadAsync(buffer, 0, buffer.Length);
|
||||
|
||||
for (var i = 0; i < bytesRead/PacketSize; i++)
|
||||
{
|
||||
var subCodePacket = new Packet(buffer.Skip(i*PacketSize).Take(PacketSize).ToArray());
|
||||
subCodePackets.Add(subCodePacket);
|
||||
}
|
||||
return subCodePackets;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<Packet>> ReadSubCodeAsync(long numberOfPackets)
|
||||
{
|
||||
var subCodePackets = new List<Packet>();
|
||||
var buffer = new byte[PacketSize*numberOfPackets];
|
||||
var bytesRead = await ReadAsync(buffer, 0, buffer.Length);
|
||||
|
||||
for (var i = 0; i < bytesRead/PacketSize; i++)
|
||||
{
|
||||
var subCodePacket = new Packet(buffer.Skip(i*PacketSize).Take(PacketSize).ToArray());
|
||||
subCodePackets.Add(subCodePacket);
|
||||
}
|
||||
return subCodePackets;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,109 +0,0 @@
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace CdgLib
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public class GraphicUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Bitmaps to stream.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <returns></returns>
|
||||
public static Stream BitmapToStream(string filename)
|
||||
{
|
||||
var oldBmp = (Bitmap) Image.FromFile(filename);
|
||||
var oldData = oldBmp.LockBits(new Rectangle(0, 0, oldBmp.Width, oldBmp.Height), ImageLockMode.WriteOnly,
|
||||
PixelFormat.Format24bppRgb);
|
||||
var length = oldData.Stride*oldBmp.Height;
|
||||
var stream = new byte[length];
|
||||
Marshal.Copy(oldData.Scan0, stream, 0, length);
|
||||
oldBmp.UnlockBits(oldData);
|
||||
oldBmp.Dispose();
|
||||
return new MemoryStream(stream);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Streams to bitmap.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <param name="width">The width.</param>
|
||||
/// <param name="height">The height.</param>
|
||||
/// <returns></returns>
|
||||
public static Bitmap StreamToBitmap(Stream stream, int width, int height)
|
||||
{
|
||||
//create a new bitmap
|
||||
var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
|
||||
var bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bmp.PixelFormat);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
//copy the stream of pixel
|
||||
for (var n = 0; n <= stream.Length - 1; n++)
|
||||
{
|
||||
var myByte = new byte[1];
|
||||
stream.Read(myByte, 0, 1);
|
||||
Marshal.WriteByte(bmpData.Scan0, n, myByte[0]);
|
||||
}
|
||||
bmp.UnlockBits(bmpData);
|
||||
return bmp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the CDG size bitmap.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <returns></returns>
|
||||
public static Bitmap GetCdgSizeBitmap(string filename)
|
||||
{
|
||||
var bm = new Bitmap(filename);
|
||||
return ResizeBitmap(ref bm, GraphicsFile.FullWidth, GraphicsFile.FullHeight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resizes the bitmap.
|
||||
/// </summary>
|
||||
/// <param name="bm">The bm.</param>
|
||||
/// <param name="width">The width.</param>
|
||||
/// <param name="height">The height.</param>
|
||||
/// <returns></returns>
|
||||
public static Bitmap ResizeBitmap(ref Bitmap bm, int width, int height)
|
||||
{
|
||||
var thumb = new Bitmap(width, height);
|
||||
using (bm)
|
||||
{
|
||||
using (var g = Graphics.FromImage(thumb))
|
||||
{
|
||||
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
g.DrawImage(bm, new Rectangle(0, 0, width, height), new Rectangle(0, 0, bm.Width, bm.Height),
|
||||
GraphicsUnit.Pixel);
|
||||
}
|
||||
}
|
||||
return thumb;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges the images with transparency.
|
||||
/// </summary>
|
||||
/// <param name="picture1">The pic1.</param>
|
||||
/// <param name="picture2">The pic2.</param>
|
||||
/// <returns></returns>
|
||||
public static Bitmap MergeImagesWithTransparency(Bitmap picture1, Bitmap picture2)
|
||||
{
|
||||
Bitmap mergedImage;
|
||||
var bm = new Bitmap(picture1.Width, picture1.Height);
|
||||
using (var gr = Graphics.FromImage(bm))
|
||||
{
|
||||
gr.DrawImage(picture1, 0, 0);
|
||||
picture2.MakeTransparent(picture2.GetPixel(1, 1));
|
||||
gr.DrawImage(picture2, 0, 0);
|
||||
mergedImage = bm;
|
||||
}
|
||||
return mergedImage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,105 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CdgLib.SubCode;
|
||||
|
||||
namespace CdgLib
|
||||
{
|
||||
public class GraphicsFile : FileStream
|
||||
public class GraphicsFile
|
||||
{
|
||||
private const int ColourTableSize = 16;
|
||||
|
||||
private const int PacketSize = 24;
|
||||
private const int TileHeight = 12;
|
||||
private const int TileWidth = 6;
|
||||
public const int FullWidth = 300;
|
||||
public const int FullHeight = 216;
|
||||
private IEnumerable<Packet> _packets;
|
||||
|
||||
private readonly int[] _colourTable = new int[ColourTableSize];
|
||||
private readonly byte[,] _pixelColours = new byte[FullHeight, FullWidth];
|
||||
|
||||
|
||||
private long _previousPosition;
|
||||
|
||||
public GraphicsFile(string path) : base(path, FileMode.Open, FileAccess.Read, FileShare.Read)
|
||||
private GraphicsFile()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Transparent => true;
|
||||
|
||||
public long Duration => Length/PacketSize*1000/300;
|
||||
public long Duration => _packets.Count()/PacketSize*1000/300;
|
||||
|
||||
public async Task<Bitmap> RenderAtTime(long position = -1)
|
||||
public static async Task<GraphicsFile> LoadAsync(string fileName)
|
||||
{
|
||||
if (position < 0)
|
||||
{
|
||||
position = _previousPosition + PacketSize;
|
||||
}
|
||||
var graphicsFile = new GraphicsFile {_packets = await LoadFileAsync(fileName)};
|
||||
return graphicsFile;
|
||||
}
|
||||
|
||||
if (position < _previousPosition)
|
||||
private static async Task<IEnumerable<Packet>> LoadFileAsync(string fileName)
|
||||
{
|
||||
using (var fileStream = new GraphicFileStream(fileName))
|
||||
{
|
||||
// Reset();
|
||||
return await fileStream.ReadFile();
|
||||
}
|
||||
}
|
||||
|
||||
public Bitmap RenderAtTime(long time)
|
||||
{
|
||||
//duration of one packet is 1/300 seconds (4 packets per sector, 75 sectors per second)
|
||||
//p=t*3/10 t=p*10/3 t=milliseconds, p=packets
|
||||
var timeToRender = position - _previousPosition;
|
||||
_previousPosition += timeToRender;
|
||||
var numberOfSubCodePackets = timeToRender*3/10;
|
||||
|
||||
var subCodePackets = await ReadSubCodeAsync(numberOfSubCodePackets);
|
||||
foreach (var subCodePacket in subCodePackets)
|
||||
{
|
||||
// ProcessPacket(subCodePacket);
|
||||
}
|
||||
|
||||
RenderSurface();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private async Task<IEnumerable<Packet>> ReadSubCodeAsync(long numberOfPackets)
|
||||
{
|
||||
var subCodePackets = new List<Packet>();
|
||||
var buffer = new byte[PacketSize*numberOfPackets];
|
||||
var bytesRead = await ReadAsync(buffer, 0, buffer.Length);
|
||||
|
||||
for (var i = 0; i < bytesRead/PacketSize; i++)
|
||||
{
|
||||
var subCodePacket = new Packet(buffer.Skip(i* PacketSize).Take(PacketSize).ToArray());
|
||||
subCodePackets.Add(subCodePacket);
|
||||
}
|
||||
return subCodePackets;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void RenderSurface()
|
||||
{
|
||||
if (_mPSurface == null)
|
||||
return;
|
||||
for (var ri = 0; ri <= FullHeight - 1; ri++)
|
||||
{
|
||||
for (var ci = 0; ci <= FullWidth - 1; ci++)
|
||||
{
|
||||
if (ri < TileHeight || ri >= FullHeight - TileHeight || ci < TileWidth ||
|
||||
ci >= FullWidth - TileWidth)
|
||||
{
|
||||
// _mPSurface.RgbData[ri, ci] = _colourTable[_mBorderColourIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
_mPSurface.RgbData[ri, ci] = _colourTable[_pixelColours[ri + _mVOffset, ci + _mHOffset]];
|
||||
}
|
||||
}
|
||||
}
|
||||
var numberOfSubCodePackets = (int) (time*3/10);
|
||||
var graphic = new Graphic(_packets.Take(numberOfSubCodePackets));
|
||||
var image = graphic.ToBitmap();
|
||||
return image;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,340 +4,19 @@ namespace CdgLib.SubCode
|
||||
{
|
||||
public class Packet
|
||||
{
|
||||
public Command Command { get; }
|
||||
|
||||
public Instruction Instruction { get; }
|
||||
|
||||
public byte[] ParityQ { get; } = new byte[2];
|
||||
|
||||
public byte[] Data { get; } = new byte[16];
|
||||
|
||||
public byte[] ParityP { get; } = new byte[4];
|
||||
|
||||
|
||||
public Packet(byte[] data)
|
||||
{
|
||||
Command = (Command)(data[0] & 0x3F);
|
||||
Instruction = (Instruction)(data[1] & 0x3F);
|
||||
Command = (Command) (data[0] & 0x3F);
|
||||
Instruction = (Instruction) (data[1] & 0x3F);
|
||||
Array.Copy(data, 2, ParityQ, 0, 2);
|
||||
Array.Copy(data, 4, Data, 0, 16);
|
||||
Array.Copy(data, 20, ParityP, 0, 4);
|
||||
}
|
||||
|
||||
public void ApplyTransform(int[,] data)
|
||||
{
|
||||
if (Command != Command.Graphic) return;
|
||||
switch (Instruction)
|
||||
{
|
||||
case Instruction.MemoryPreset:
|
||||
MemoryPreset(data);
|
||||
break;
|
||||
case Instruction.BorderPreset:
|
||||
BorderPreset(data);
|
||||
break;
|
||||
case Instruction.TileBlockNormal:
|
||||
TileBlock(data,false);
|
||||
break;
|
||||
case Instruction.ScrollPreset:
|
||||
Scroll(data,false);
|
||||
break;
|
||||
case Instruction.ScrollCopy:
|
||||
Scroll(data,true);
|
||||
break;
|
||||
case Instruction.DefineTransparentColor:
|
||||
DefineTransparentColour(data);
|
||||
break;
|
||||
case Instruction.LoadColorTableLower:
|
||||
LoadColorTable(data,0);
|
||||
break;
|
||||
case Instruction.LoadColorTableUpper:
|
||||
LoadColorTable(data,1);
|
||||
break;
|
||||
case Instruction.TileBlockXor:
|
||||
TileBlock(data,true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void MemoryPreset(int[,] data)
|
||||
{
|
||||
var colour = Data[0] & 0xf;
|
||||
var repeat = Data[1] & 0xf;
|
||||
|
||||
//we have a reliable data stream, so the repeat command
|
||||
//is executed only the first time
|
||||
if (repeat == 0)
|
||||
{
|
||||
//Note that this may be done before any load colour table
|
||||
//commands by some CDGs. So the load colour table itself
|
||||
//actual recalculates the RGB values for all pixels when
|
||||
//the colour table changes.
|
||||
|
||||
//Set the preset colour for every pixel. Must be stored in
|
||||
//the pixel colour table indeces array
|
||||
for (int rowIndex = 0; rowIndex < data.GetLength(0); rowIndex++)
|
||||
{
|
||||
for (int columnIndex = 0; columnIndex < data.GetLength(1); columnIndex++)
|
||||
{
|
||||
data[rowIndex, columnIndex] = (byte) colour;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BorderPreset(int[,] data)
|
||||
{
|
||||
int rowIndex;
|
||||
int columnIndex;
|
||||
|
||||
var colour = Data[0] & 0xf;
|
||||
|
||||
//The border area is the area contained with a rectangle
|
||||
//defined by (0,0,300,216) minus the interior pixels which are contained
|
||||
//within a rectangle defined by (6,12,294,204).
|
||||
|
||||
for (rowIndex = 0; rowIndex < data.GetLength(0); rowIndex++)
|
||||
{
|
||||
for (columnIndex = 0; columnIndex < 6; columnIndex++)
|
||||
{
|
||||
data[rowIndex, columnIndex] = (byte) colour;
|
||||
}
|
||||
|
||||
for (columnIndex = data.GetLength(1) - 6; columnIndex < data.GetLength(1); columnIndex++)
|
||||
{
|
||||
data[rowIndex, columnIndex] = (byte) colour;
|
||||
}
|
||||
}
|
||||
|
||||
for (columnIndex = 6; columnIndex < data.GetLength(1) - 6; columnIndex++)
|
||||
{
|
||||
for (rowIndex = 0; rowIndex < 12; rowIndex++)
|
||||
{
|
||||
data[rowIndex, columnIndex] = (byte) colour;
|
||||
}
|
||||
|
||||
for (rowIndex = data.GetLength(1) - 12; rowIndex < data.GetLength(1); rowIndex++)
|
||||
{
|
||||
data[rowIndex, columnIndex] = (byte) colour;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void LoadColorTable(int[,] data,int table)
|
||||
{
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
//[---high byte---] [---low byte----]
|
||||
//7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
|
||||
//X X r r r r g g X X g g b b b b
|
||||
|
||||
var byte0 = Data[2*i];
|
||||
var byte1 = Data[2*i + 1];
|
||||
var red = (byte0 & 0x3f) >> 2;
|
||||
var green = ((byte0 & 0x3) << 2) | ((byte1 & 0x3f) >> 4);
|
||||
var blue = byte1 & 0xf;
|
||||
|
||||
red *= 17;
|
||||
green *= 17;
|
||||
blue *= 17;
|
||||
|
||||
if (_mPSurface != null)
|
||||
{
|
||||
_mColourTable[i + table*8] = _mPSurface.MapRgbColour(red, green, blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void TileBlock(int[,] data,bool bXor)
|
||||
{
|
||||
var colour0 = 0;
|
||||
var colour1 = 0;
|
||||
var columnIndex = 0;
|
||||
var rowIndex = 0;
|
||||
var myByte = 0;
|
||||
var pixel = 0;
|
||||
var xorCol = 0;
|
||||
var currentColourIndex = 0;
|
||||
var newCol = 0;
|
||||
|
||||
colour0 = pack.Data[0] & 0xf;
|
||||
colour1 = pack.Data[1] & 0xf;
|
||||
rowIndex = (pack.Data[2] & 0x1f)*12;
|
||||
columnIndex = (pack.Data[3] & 0x3f)*6;
|
||||
|
||||
if (rowIndex > FullHeight - TileHeight)
|
||||
return;
|
||||
if (columnIndex > FullWidth - TileWidth)
|
||||
return;
|
||||
|
||||
//Set the pixel array for each of the pixels in the 12x6 tile.
|
||||
//Normal = Set the colour to either colour0 or colour1 depending
|
||||
//on whether the pixel value is 0 or 1.
|
||||
//XOR = XOR the colour with the colour index currently there.
|
||||
|
||||
|
||||
for (var i = 0; i <= 11; i++)
|
||||
{
|
||||
myByte = pack.Data[4 + i] & 0x3f;
|
||||
for (var j = 0; j <= 5; j++)
|
||||
{
|
||||
pixel = (myByte >> (5 - j)) & 0x1;
|
||||
if (bXor)
|
||||
{
|
||||
//Tile Block XOR
|
||||
xorCol = pixel == 0 ? colour0 : colour1;
|
||||
|
||||
//Get the colour index currently at this location, and xor with it
|
||||
currentColourIndex = _mPixelColours[rowIndex + i, columnIndex + j];
|
||||
newCol = currentColourIndex ^ xorCol;
|
||||
}
|
||||
else
|
||||
{
|
||||
newCol = pixel == 0 ? colour0 : colour1;
|
||||
}
|
||||
|
||||
//Set the pixel with the new colour. We set both the surfarray
|
||||
//containing actual RGB values, as well as our array containing
|
||||
//the colour indexes into our colour table.
|
||||
_mPixelColours[rowIndex + i, columnIndex + j] = (byte) newCol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DefineTransparentColour(int[,] data)
|
||||
{
|
||||
_mTransparentColour = Data[0] & 0xf;
|
||||
}
|
||||
|
||||
|
||||
private void Scroll(int[,] data,bool copy)
|
||||
{
|
||||
//Decode the scroll command parameters
|
||||
var colour = Data[0] & 0xf;
|
||||
var horizontalScroll = Data[1] & 0x3f;
|
||||
var verticalScroll = Data[2] & 0x3f;
|
||||
|
||||
var horizontalScrollCommand = (horizontalScroll & 0x30) >> 4;
|
||||
var horizontalOffset = horizontalScroll & 0x7;
|
||||
var verticalScrollCommand = (verticalScroll & 0x30) >> 4;
|
||||
var verticalOffset = verticalScroll & 0xf;
|
||||
|
||||
|
||||
_mHOffset = horizontalOffset < 5 ? horizontalOffset : 5;
|
||||
_mVOffset = verticalOffset < 11 ? verticalOffset : 11;
|
||||
|
||||
//Scroll Vertical - Calculate number of pixels
|
||||
|
||||
var verticalScrollPixels = 0;
|
||||
switch (verticalScrollCommand)
|
||||
{
|
||||
case 2:
|
||||
verticalScrollPixels = -12;
|
||||
break;
|
||||
case 1:
|
||||
verticalScrollPixels = 12;
|
||||
break;
|
||||
}
|
||||
|
||||
//Scroll Horizontal- Calculate number of pixels
|
||||
|
||||
var horizontalScrollPixels = 0;
|
||||
switch (horizontalScrollCommand)
|
||||
{
|
||||
case 2:
|
||||
horizontalScrollPixels = -6;
|
||||
break;
|
||||
case 1:
|
||||
horizontalScrollPixels = 6;
|
||||
break;
|
||||
}
|
||||
|
||||
if (horizontalScrollPixels == 0 && verticalScrollPixels == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Perform the actual scroll.
|
||||
|
||||
var temp = new byte[FullHeight + 1, FullWidth + 1];
|
||||
var vInc = verticalScrollPixels + FullHeight;
|
||||
var hInc = horizontalScrollPixels + FullWidth;
|
||||
var rowIndex = 0;
|
||||
//row index
|
||||
var columnIndex = 0;
|
||||
//column index
|
||||
|
||||
for (rowIndex = 0; rowIndex <= FullHeight - 1; rowIndex++)
|
||||
{
|
||||
for (columnIndex = 0; columnIndex <= FullWidth - 1; columnIndex++)
|
||||
{
|
||||
temp[(rowIndex + vInc)%FullHeight, (columnIndex + hInc)%FullWidth] = _mPixelColours[rowIndex, columnIndex];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//if copy is false, we were supposed to fill in the new pixels
|
||||
//with a new colour. Go back and do that now.
|
||||
|
||||
|
||||
if (copy == false)
|
||||
{
|
||||
if (verticalScrollPixels > 0)
|
||||
{
|
||||
for (columnIndex = 0; columnIndex <= FullWidth - 1; columnIndex++)
|
||||
{
|
||||
for (rowIndex = 0; rowIndex <= verticalScrollPixels - 1; rowIndex++)
|
||||
{
|
||||
temp[rowIndex, columnIndex] = (byte) colour;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (verticalScrollPixels < 0)
|
||||
{
|
||||
for (columnIndex = 0; columnIndex <= FullWidth - 1; columnIndex++)
|
||||
{
|
||||
for (rowIndex = FullHeight + verticalScrollPixels; rowIndex <= FullHeight - 1; rowIndex++)
|
||||
{
|
||||
temp[rowIndex, columnIndex] = (byte) colour;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (horizontalScrollPixels > 0)
|
||||
{
|
||||
for (columnIndex = 0; columnIndex <= horizontalScrollPixels - 1; columnIndex++)
|
||||
{
|
||||
for (rowIndex = 0; rowIndex <= FullHeight - 1; rowIndex++)
|
||||
{
|
||||
temp[rowIndex, columnIndex] = (byte) colour;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (horizontalScrollPixels < 0)
|
||||
{
|
||||
for (columnIndex = FullWidth + horizontalScrollPixels; columnIndex <= FullWidth - 1; columnIndex++)
|
||||
{
|
||||
for (rowIndex = 0; rowIndex <= FullHeight - 1; rowIndex++)
|
||||
{
|
||||
temp[rowIndex, columnIndex] = (byte) colour;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Now copy the temporary buffer back to our array
|
||||
|
||||
for (rowIndex = 0; rowIndex <= FullHeight - 1; rowIndex++)
|
||||
{
|
||||
for (columnIndex = 0; columnIndex <= FullWidth - 1; columnIndex++)
|
||||
{
|
||||
_mPixelColours[rowIndex, columnIndex] = temp[rowIndex, columnIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
public Command Command { get; }
|
||||
public Instruction Instruction { get; }
|
||||
public byte[] ParityQ { get; } = new byte[2];
|
||||
public byte[] Data { get; } = new byte[16];
|
||||
public byte[] ParityP { get; } = new byte[4];
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace CdgLib
|
||||
{
|
||||
public class Surface
|
||||
{
|
||||
public int[,] RgbData = new int[GraphicsFile.FullHeight, GraphicsFile.FullWidth];
|
||||
|
||||
public int MapRgbColour(int red, int green, int blue)
|
||||
{
|
||||
return Color.FromArgb(red, green, blue).ToArgb();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user