Mam aplikację konsoli, która zarządza obrazami. Teraz potrzebuję czegoś w rodzaju podglądu obrazów w aplikacji konsoli. Czy jest sposób, aby wyświetlić je w konsoli?
Oto porównanie aktualnych odpowiedzi opartych na znakach:
Wejście:
Wynik:
Odpowiedzi:
Dalej bawiłem się kodem z @DieterMeemken. Zmniejszyłem o połowę rozdzielczość pionową i dodałem dithering przez ░▒▓. Po lewej wynik Dietera Meemkena, po prawej mój. U dołu znajduje się oryginalny obraz, którego rozmiar został zmieniony w celu zgrubnego dopasowania do obrazu wyjściowego. O ile funkcja konwersji Malwynsa robi wrażenie, to nie wykorzystuje wszystkich szarych kolorów, szkoda.
static int[] cColors = { 0x000000, 0x000080, 0x008000, 0x008080, 0x800000, 0x800080, 0x808000, 0xC0C0C0, 0x808080, 0x0000FF, 0x00FF00, 0x00FFFF, 0xFF0000, 0xFF00FF, 0xFFFF00, 0xFFFFFF };
public static void ConsoleWritePixel(Color cValue)
{
Color[] cTable = cColors.Select(x => Color.FromArgb(x)).ToArray();
char[] rList = new char[] { (char)9617, (char)9618, (char)9619, (char)9608 }; // 1/4, 2/4, 3/4, 4/4
int[] bestHit = new int[] { 0, 0, 4, int.MaxValue }; //ForeColor, BackColor, Symbol, Score
for (int rChar = rList.Length; rChar > 0; rChar--)
{
for (int cFore = 0; cFore < cTable.Length; cFore++)
{
for (int cBack = 0; cBack < cTable.Length; cBack++)
{
int R = (cTable[cFore].R * rChar + cTable[cBack].R * (rList.Length - rChar)) / rList.Length;
int G = (cTable[cFore].G * rChar + cTable[cBack].G * (rList.Length - rChar)) / rList.Length;
int B = (cTable[cFore].B * rChar + cTable[cBack].B * (rList.Length - rChar)) / rList.Length;
int iScore = (cValue.R - R) * (cValue.R - R) + (cValue.G - G) * (cValue.G - G) + (cValue.B - B) * (cValue.B - B);
if (!(rChar > 1 && rChar < 4 && iScore > 50000)) // rule out too weird combinations
{
if (iScore < bestHit[3])
{
bestHit[3] = iScore; //Score
bestHit[0] = cFore; //ForeColor
bestHit[1] = cBack; //BackColor
bestHit[2] = rChar; //Symbol
}
}
}
}
}
Console.ForegroundColor = (ConsoleColor)bestHit[0];
Console.BackgroundColor = (ConsoleColor)bestHit[1];
Console.Write(rList[bestHit[2] - 1]);
}
public static void ConsoleWriteImage(Bitmap source)
{
int sMax = 39;
decimal percent = Math.Min(decimal.Divide(sMax, source.Width), decimal.Divide(sMax, source.Height));
Size dSize = new Size((int)(source.Width * percent), (int)(source.Height * percent));
Bitmap bmpMax = new Bitmap(source, dSize.Width * 2, dSize.Height);
for (int i = 0; i < dSize.Height; i++)
{
for (int j = 0; j < dSize.Width; j++)
{
ConsoleWritePixel(bmpMax.GetPixel(j * 2, i));
ConsoleWritePixel(bmpMax.GetPixel(j * 2 + 1, i));
}
System.Console.WriteLine();
}
Console.ResetColor();
}
stosowanie:
Bitmap bmpSrc = new Bitmap(@"HuwnC.gif", true);
ConsoleWriteImage(bmpSrc);
EDYTOWAĆ
Odległość kolorów to złożony temat ( tutaj , tutaj i linki na tych stronach ...). Próbowałem obliczyć odległość w YUV i wyniki były raczej gorsze niż w RGB. Mogłyby być lepsze z Lab i DeltaE, ale nie próbowałem tego. Odległość w RGB wydaje się wystarczająca. W rzeczywistości wyniki są bardzo podobne dla odległości euklidesowej i manhattanu w przestrzeni kolorów RGB, więc podejrzewam, że jest zbyt mało kolorów do wyboru.
Reszta to po prostu brutalne porównanie koloru ze wszystkimi kombinacjami kolorów i wzorów (= symboli). Podałem, że współczynnik wypełnienia ░▒▓█ wynosi 1/4, 2/4, 3/4 i 4/4. W takim przypadku trzeci symbol jest w rzeczywistości zbędny w stosunku do pierwszego. Ale gdyby proporcje nie były tak jednolite (w zależności od czcionki), wyniki mogłyby się zmienić, więc zostawiłem to na przyszłe ulepszenia. Średni kolor symbolu jest obliczany jako średnia ważona z foregroudColor i backgroundColor zgodnie ze współczynnikiem wypełnienia. Przyjmuje liniowe kolory, co jest również dużym uproszczeniem. Jest więc jeszcze miejsce na ulepszenia.
Chociaż wyświetlanie obrazu w konsoli nie jest zamierzonym użyciem konsoli, z pewnością możesz zhakować rzeczy, ponieważ okno konsoli jest tylko oknem, jak każde inne okno.
Właściwie to kiedyś zacząłem tworzyć bibliotekę kontrolek tekstu dla aplikacji konsolowych z obsługą grafiki. Nigdy tego nie skończyłem, chociaż mam działające demo typu proof-of-concept:
A jeśli uzyskasz rozmiar czcionki konsoli, możesz bardzo precyzyjnie umieścić obraz.
Oto jak możesz to zrobić:
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);
}
}
}
Oto jak możesz uzyskać aktualny rozmiar czcionki konsoli:
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);
}
Oraz wymagane dodatkowe wywołania, stałe i typy WinApi:
[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;
A wynik:
[
Button
, TextBox
etc nadal brakuje. Moim marzeniem jest stworzenie w miarę pełnej obsługi języka XAML z wiązaniem danych i podobną do WPF filozofią „osadzania wszystkiego w czymkolwiek”. Ale jestem od tego bardzo daleki ... cóż, w tej chwili :)
Jeśli użyjesz ASCII 219 (█) dwa razy, otrzymasz coś w rodzaju piksela (██). Teraz ogranicza Cię liczba pikseli i liczba kolorów w aplikacji konsolowej.
jeśli zachowasz ustawienia domyślne, masz około 39x39 pikseli, jeśli chcesz więcej, możesz zmienić rozmiar konsoli za pomocą Console.WindowHeight = resSize.Height + 1;
iConsole.WindowWidth = resultSize.Width * 2;
musisz zachować jak największy współczynnik proporcji obrazu, więc w większości przypadków nie będziesz mieć 39 x 39
Malwyn opublikował całkowicie niedocenianą metodę konwersji System.Drawing.Color
naSystem.ConsoleColor
więc moje podejście byłoby
using System.Drawing;
public static int ToConsoleColor(System.Drawing.Color c)
{
int index = (c.R > 128 | c.G > 128 | c.B > 128) ? 8 : 0;
index |= (c.R > 64) ? 4 : 0;
index |= (c.G > 64) ? 2 : 0;
index |= (c.B > 64) ? 1 : 0;
return index;
}
public static void ConsoleWriteImage(Bitmap src)
{
int min = 39;
decimal pct = Math.Min(decimal.Divide(min, src.Width), decimal.Divide(min, src.Height));
Size res = new Size((int)(src.Width * pct), (int)(src.Height * pct));
Bitmap bmpMin = new Bitmap(src, res);
for (int i = 0; i < res.Height; i++)
{
for (int j = 0; j < res.Width; j++)
{
Console.ForegroundColor = (ConsoleColor)ToConsoleColor(bmpMin.GetPixel(j, i));
Console.Write("██");
}
System.Console.WriteLine();
}
}
więc możesz
ConsoleWriteImage(new Bitmap(@"C:\image.gif"));
przykładowe dane wejściowe:
przykładowe wyjście:
to było zabawne. Dzięki fubo , wypróbowałem Twoje rozwiązanie i udało mi się zwiększyć rozdzielczość podglądu o 4 (2x2).
Odkryłem, że możesz ustawić kolor tła dla każdego pojedynczego znaku. Tak więc, zamiast używać dwóch znaków ASCII 219 (█), użyłem dwa razy ASCII 223 (▀) z różnymi kolorami pierwszego planu i tła. To dzieli duży piksel (██) na 4 takie subpiksele (▀▄).
W tym przykładzie umieściłem oba obrazy obok siebie, aby łatwo było zobaczyć różnicę:
Oto kod:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace ConsoleWithImage
{
class Program
{
public static void ConsoleWriteImage(Bitmap bmpSrc)
{
int sMax = 39;
decimal percent = Math.Min(decimal.Divide(sMax, bmpSrc.Width), decimal.Divide(sMax, bmpSrc.Height));
Size resSize = new Size((int)(bmpSrc.Width * percent), (int)(bmpSrc.Height * percent));
Func<System.Drawing.Color, int> ToConsoleColor = c =>
{
int index = (c.R > 128 | c.G > 128 | c.B > 128) ? 8 : 0;
index |= (c.R > 64) ? 4 : 0;
index |= (c.G > 64) ? 2 : 0;
index |= (c.B > 64) ? 1 : 0;
return index;
};
Bitmap bmpMin = new Bitmap(bmpSrc, resSize.Width, resSize.Height);
Bitmap bmpMax = new Bitmap(bmpSrc, resSize.Width * 2, resSize.Height * 2);
for (int i = 0; i < resSize.Height; i++)
{
for (int j = 0; j < resSize.Width; j++)
{
Console.ForegroundColor = (ConsoleColor)ToConsoleColor(bmpMin.GetPixel(j, i));
Console.Write("██");
}
Console.BackgroundColor = ConsoleColor.Black;
Console.Write(" ");
for (int j = 0; j < resSize.Width; j++)
{
Console.ForegroundColor = (ConsoleColor)ToConsoleColor(bmpMax.GetPixel(j * 2, i * 2));
Console.BackgroundColor = (ConsoleColor)ToConsoleColor(bmpMax.GetPixel(j * 2, i * 2 + 1));
Console.Write("▀");
Console.ForegroundColor = (ConsoleColor)ToConsoleColor(bmpMax.GetPixel(j * 2 + 1, i * 2));
Console.BackgroundColor = (ConsoleColor)ToConsoleColor(bmpMax.GetPixel(j * 2 + 1, i * 2 + 1));
Console.Write("▀");
}
System.Console.WriteLine();
}
}
static void Main(string[] args)
{
System.Console.WindowWidth = 170;
System.Console.WindowHeight = 40;
Bitmap bmpSrc = new Bitmap(@"image.bmp", true);
ConsoleWriteImage(bmpSrc);
System.Console.ReadLine();
}
}
}
Aby uruchomić przykład, mapa bitowa „image.bmp” musi znajdować się w tym samym katalogu, co plik wykonywalny. Zwiększyłem rozmiar konsoli, rozmiar podglądu nadal wynosi 39 i można go zmienić na int sMax = 39;
.
Rozwiązanie od taffer jest również bardzo fajne. Wy dwoje macie moje głosy za ...
Czytałem o przestrzeniach kolorów, a przestrzeń LAB wydaje się być dla Ciebie dobrym rozwiązaniem (zobacz to pytanie: Znajdowanie dokładnej „odległości” między kolorami i Algorytm, aby sprawdzić podobieństwo kolorów )
Cytując stronę Wikipedii CIELAB , zalety tej przestrzeni kolorów to:
W przeciwieństwie do modeli kolorów RGB i CMYK, kolory Lab są zaprojektowane tak, aby przybliżać ludzkie widzenie. Dąży do jednorodności percepcji, a jej składnik L ściśle pasuje do ludzkiego postrzegania lekkości. W związku z tym może być używany do dokładnych korekt balansu kolorów poprzez modyfikację krzywych wyjściowych w składowych a i b.
Aby zmierzyć odległość między kolorami, możesz użyć odległości Delta E.
Dzięki temu możesz lepiej przybliżać od Color
do ConsoleColor
:
Po pierwsze, możesz zdefiniować CieLab
klasę reprezentującą kolory w tej przestrzeni:
public class CieLab
{
public double L { get; set; }
public double A { get; set; }
public double B { get; set; }
public static double DeltaE(CieLab l1, CieLab l2)
{
return Math.Pow(l1.L - l2.L, 2) + Math.Pow(l1.A - l2.A, 2) + Math.Pow(l1.B - l2.B, 2);
}
public static CieLab Combine(CieLab l1, CieLab l2, double amount)
{
var l = l1.L * amount + l2.L * (1 - amount);
var a = l1.A * amount + l2.A * (1 - amount);
var b = l1.B * amount + l2.B * (1 - amount);
return new CieLab { L = l, A = a, B = b };
}
}
Istnieją dwie statyczne metody, jedna do pomiaru odległości za pomocą Delta E ( DeltaE
), a druga do łączenia dwóch kolorów, określając ilość każdego koloru ( Combine
).
A do przekształcenia z RGB
do LAB
możesz użyć następującej metody ( stąd ):
public static CieLab RGBtoLab(int red, int green, int blue)
{
var rLinear = red / 255.0;
var gLinear = green / 255.0;
var bLinear = blue / 255.0;
double r = rLinear > 0.04045 ? Math.Pow((rLinear + 0.055) / (1 + 0.055), 2.2) : (rLinear / 12.92);
double g = gLinear > 0.04045 ? Math.Pow((gLinear + 0.055) / (1 + 0.055), 2.2) : (gLinear / 12.92);
double b = bLinear > 0.04045 ? Math.Pow((bLinear + 0.055) / (1 + 0.055), 2.2) : (bLinear / 12.92);
var x = r * 0.4124 + g * 0.3576 + b * 0.1805;
var y = r * 0.2126 + g * 0.7152 + b * 0.0722;
var z = r * 0.0193 + g * 0.1192 + b * 0.9505;
Func<double, double> Fxyz = t => ((t > 0.008856) ? Math.Pow(t, (1.0 / 3.0)) : (7.787 * t + 16.0 / 116.0));
return new CieLab
{
L = 116.0 * Fxyz(y / 1.0) - 16,
A = 500.0 * (Fxyz(x / 0.9505) - Fxyz(y / 1.0)),
B = 200.0 * (Fxyz(y / 1.0) - Fxyz(z / 1.0890))
};
}
Pomysł polega na użyciu znaków cieni, takich jak @AntoninLejsek do ('█', '▓', '▒', '░'), co pozwala uzyskać więcej niż 16 kolorów łączących kolory konsoli ( Combine
metodą).
Tutaj możemy wprowadzić pewne ulepszenia, wstępnie obliczając kolory do użycia:
class ConsolePixel
{
public char Char { get; set; }
public ConsoleColor Forecolor { get; set; }
public ConsoleColor Backcolor { get; set; }
public CieLab Lab { get; set; }
}
static List<ConsolePixel> pixels;
private static void ComputeColors()
{
pixels = new List<ConsolePixel>();
char[] chars = { '█', '▓', '▒', '░' };
int[] rs = { 0, 0, 0, 0, 128, 128, 128, 192, 128, 0, 0, 0, 255, 255, 255, 255 };
int[] gs = { 0, 0, 128, 128, 0, 0, 128, 192, 128, 0, 255, 255, 0, 0, 255, 255 };
int[] bs = { 0, 128, 0, 128, 0, 128, 0, 192, 128, 255, 0, 255, 0, 255, 0, 255 };
for (int i = 0; i < 16; i++)
for (int j = i + 1; j < 16; j++)
{
var l1 = RGBtoLab(rs[i], gs[i], bs[i]);
var l2 = RGBtoLab(rs[j], gs[j], bs[j]);
for (int k = 0; k < 4; k++)
{
var l = CieLab.Combine(l1, l2, (4 - k) / 4.0);
pixels.Add(new ConsolePixel
{
Char = chars[k],
Forecolor = (ConsoleColor)i,
Backcolor = (ConsoleColor)j,
Lab = l
});
}
}
}
Innym ulepszeniem może być bezpośredni dostęp do danych obrazu przy użyciu LockBits
zamiast używania GetPixel
.
AKTUALIZACJA : Jeśli obraz ma części o tym samym kolorze, możesz znacznie przyspieszyć proces rysowania fragmentów znaków o tych samych kolorach zamiast pojedynczych znaków:
public static void DrawImage(Bitmap source)
{
int width = Console.WindowWidth - 1;
int height = (int)(width * source.Height / 2.0 / source.Width);
using (var bmp = new Bitmap(source, width, height))
{
var unit = GraphicsUnit.Pixel;
using (var src = bmp.Clone(bmp.GetBounds(ref unit), PixelFormat.Format24bppRgb))
{
var bits = src.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, src.PixelFormat);
byte[] data = new byte[bits.Stride * bits.Height];
Marshal.Copy(bits.Scan0, data, 0, data.Length);
for (int j = 0; j < height; j++)
{
StringBuilder builder = new StringBuilder();
var fore = ConsoleColor.White;
var back = ConsoleColor.Black;
for (int i = 0; i < width; i++)
{
int idx = j * bits.Stride + i * 3;
var pixel = DrawPixel(data[idx + 2], data[idx + 1], data[idx + 0]);
if (pixel.Forecolor != fore || pixel.Backcolor != back)
{
Console.ForegroundColor = fore;
Console.BackgroundColor = back;
Console.Write(builder);
builder.Clear();
}
fore = pixel.Forecolor;
back = pixel.Backcolor;
builder.Append(pixel.Char);
}
Console.ForegroundColor = fore;
Console.BackgroundColor = back;
Console.WriteLine(builder);
}
Console.ResetColor();
}
}
}
private static ConsolePixel DrawPixel(int r, int g, int b)
{
var l = RGBtoLab(r, g, b);
double diff = double.MaxValue;
var pixel = pixels[0];
foreach (var item in pixels)
{
var delta = CieLab.DeltaE(l, item.Lab);
if (delta < diff)
{
diff = delta;
pixel = item;
}
}
return pixel;
}
Na koniec zadzwoń w ten DrawImage
sposób:
static void Main(string[] args)
{
ComputeColors();
Bitmap image = new Bitmap("image.jpg", true);
DrawImage(image);
}
Obrazy wyników:
Poniższe rozwiązania nie są oparte na znakach, ale zapewniają pełne szczegółowe obrazy
Możesz rysować po dowolnym oknie, używając jego obsługi, aby utworzyć Graphics
obiekt. Aby uzyskać obsługę aplikacji konsolowej, możesz to zrobić importując GetConsoleWindow
:
[DllImport("kernel32.dll", EntryPoint = "GetConsoleWindow", SetLastError = true)]
private static extern IntPtr GetConsoleHandle();
Następnie utwórz grafikę za pomocą handlera (używając Graphics.FromHwnd
) i narysuj obraz używając metod w Graphics
obiekcie, na przykład:
static void Main(string[] args)
{
var handler = GetConsoleHandle();
using (var graphics = Graphics.FromHwnd(handler))
using (var image = Image.FromFile("img101.png"))
graphics.DrawImage(image, 50, 50, 250, 200);
}
Wygląda dobrze, ale jeśli rozmiar konsoli zostanie zmieniony lub przewinięty, obraz znika, ponieważ okna są odświeżane (być może w twoim przypadku możliwe jest zaimplementowanie jakiegoś mechanizmu do przerysowania obrazu).
Innym rozwiązaniem jest osadzenie Form
metody window ( ) w aplikacji konsoli. Aby to zrobić, musisz zaimportować SetParent
(i MoveWindow
przenieść okno wewnątrz konsoli):
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
Następnie wystarczy utworzyć Form
i ustawić BackgroundImage
właściwość na żądany obraz (zrób to na Thread
lub, Task
aby uniknąć blokowania konsoli):
static void Main(string[] args)
{
Task.Factory.StartNew(ShowImage);
Console.ReadLine();
}
static void ShowImage()
{
var form = new Form
{
BackgroundImage = Image.FromFile("img101.png"),
BackgroundImageLayout = ImageLayout.Stretch
};
var parent = GetConsoleHandle();
var child = form.Handle;
SetParent(child, parent);
MoveWindow(child, 50, 50, 250, 200, true);
Application.Run(form);
}
Oczywiście możesz ustawić FormBorderStyle = FormBorderStyle.None
ukrywanie granic okien (prawy obraz)
W takim przypadku możesz zmienić rozmiar konsoli, a obraz / okno nadal tam będzie.
Jedną z korzyści tego podejścia jest to, że możesz zlokalizować okno w wybranym miejscu i zmienić obraz w dowolnym momencie, po prostu zmieniając BackgroundImage
właściwość.
Nie ma bezpośredniego sposobu. Ale możesz spróbować użyć konwertera obrazu na ascii-art, takiego jak ten
Tak, możesz to zrobić, jeśli trochę rozciągniesz pytanie, otwierając Form
z poziomu aplikacji Console.
Oto, jak możesz otworzyć formularz w aplikacji konsolowej i wyświetlić obraz:
System.Drawing
iSystem.Windows.Forms
using System.Windows.Forms;
using System.Drawing;
Zobacz ten post, jak to zrobić !
Teraz wszystko, czego potrzebujesz, aby dodać coś takiego:
Form form1 = new Form();
form1.BackgroundImage = bmp;
form1.ShowDialog();
Oczywiście możesz także użyć PictureBox
...
Możesz też użyć, form1.Show();
aby utrzymać konsolę przy życiu podczas wyświetlania podglądu.
Oryginalny post: Oczywiście nie możesz poprawnie wyświetlić obrazu w oknie 25x80; nawet jeśli używasz większego okna i blokowej grafiki, nie byłby to podgląd, ale bałagan!
Aktualizacja: Wygląda na to, że możesz mimo wszystko GDI narysować obraz na formularzu konsoli; zobacz odpowiedź Taffera!