Though showing an image in a console is not the intended usage of the console, you can surely hack the things, as the console window is just a window, like any other windows.
Actually, once I have started to develop a text controls library for console applications with graphics support. I have never finished that, though I have a working proof-of-concept demo:
And if you obtain the console font size, you can place the image very precisely.
This is how you can do it:
static void Main(string[] args)
{
Console.WriteLine("Graphics in console window!");
Point location = new Point(10, 10);
Size imageSize = new Size(20, 10); // desired image size in characters
// draw some placeholders
Console.SetCursorPosition(location.X - 1, location.Y);
Console.Write(">");
Console.SetCursorPosition(location.X + imageSize.Width, location.Y);
Console.Write("<");
Console.SetCursorPosition(location.X - 1, location.Y + imageSize.Height - 1);
Console.Write(">");
Console.SetCursorPosition(location.X + imageSize.Width, location.Y + imageSize.Height - 1);
Console.WriteLine("<");
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonPictures), @"Sample Pictures\tulips.jpg");
using (Graphics g = Graphics.FromHwnd(GetConsoleWindow()))
{
using (Image image = Image.FromFile(path))
{
Size fontSize = GetConsoleFontSize();
// translating the character positions to pixels
Rectangle imageRect = new Rectangle(
location.X * fontSize.Width,
location.Y * fontSize.Height,
imageSize.Width * fontSize.Width,
imageSize.Height * fontSize.Height);
g.DrawImage(image, imageRect);
}
}
}
Here is how you can obtain the current console font size:
private static Size GetConsoleFontSize()
{
// getting the console out buffer handle
IntPtr outHandle = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero,
OPEN_EXISTING,
0,
IntPtr.Zero);
int errorCode = Marshal.GetLastWin32Error();
if (outHandle.ToInt32() == INVALID_HANDLE_VALUE)
{
throw new IOException("Unable to open CONOUT$", errorCode);
}
ConsoleFontInfo cfi = new ConsoleFontInfo();
if (!GetCurrentConsoleFont(outHandle, false, cfi))
{
throw new InvalidOperationException("Unable to get font information.");
}
return new Size(cfi.dwFontSize.X, cfi.dwFontSize.Y);
}
And the required additional WinApi calls, constants and types:
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetConsoleWindow();
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateFile(
string lpFileName,
int dwDesiredAccess,
int dwShareMode,
IntPtr lpSecurityAttributes,
int dwCreationDisposition,
int dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetCurrentConsoleFont(
IntPtr hConsoleOutput,
bool bMaximumWindow,
[Out][MarshalAs(UnmanagedType.LPStruct)]ConsoleFontInfo lpConsoleCurrentFont);
[StructLayout(LayoutKind.Sequential)]
internal class ConsoleFontInfo
{
internal int nFont;
internal Coord dwFontSize;
}
[StructLayout(LayoutKind.Explicit)]
internal struct Coord
{
[FieldOffset(0)]
internal short X;
[FieldOffset(2)]
internal short Y;
}
private const int GENERIC_READ = unchecked((int)0x80000000);
private const int GENERIC_WRITE = 0x40000000;
private const int FILE_SHARE_READ = 1;
private const int FILE_SHARE_WRITE = 2;
private const int INVALID_HANDLE_VALUE = -1;
private const int OPEN_EXISTING = 3;
And the result:
[