Отображение изображения в консольном приложении

У меня есть консольное приложение, которое управляет изображениями. Теперь мне нужно что-то вроде предварительного просмотра изображений в консольном приложении. Есть ли способ отобразить их в консоли?

Ниже приведено сравнение текущих ответов на основе символов:

Input:

введите описание изображения здесь

Вывод:

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь

Ответ 1

Далее я играл с кодом из @DieterMeemken. Я уменьшил вдвое вертикальное разрешение и добавил сглаживание через ░▒▓. Слева - результат Дитера Меменка, справа мой. На нижней части находится исходное изображение, измененное по размеру, чтобы точно соответствовать выходному значению. Результат вывода Хотя функция преобразования Malwyns впечатляет, она не использует все серые цвета, что жалко.

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();
}

использование:

Bitmap bmpSrc = new Bitmap(@"HuwnC.gif", true);    
ConsoleWriteImage(bmpSrc);

ИЗМЕНИТЬ

Цветное расстояние - сложная тема (здесь, здесь и ссылки на этих страницах...). Я попытался рассчитать расстояние в YUV, и результаты оказались хуже, чем в RGB. Они могут быть лучше с Lab и DeltaE, но я этого не делал. Расстояние в RGB кажется достаточно хорошим. Фактически результаты очень похожи на эвклидовое и манхэттенское расстояние в цветовом пространстве RGB, поэтому я подозреваю, что на выбор слишком мало цветов.

Остальное - это просто сравнение грубой силы цвета со всеми комбинациями цветов и узоров (= символы). Я сказал, что коэффициент заполнения для ░▒▓█ равен 1/4, 2/4, 3/4 и 4/4. В этом случае третий символ фактически избыточен для первого. Но если коэффициенты не были такими единообразными (зависит от шрифта), результаты могут измениться, поэтому я оставил его там для будущих улучшений. Средний цвет символа рассчитывается как взвешенное среднее значение foregroudColor и backgroundColor в соответствии с коэффициентом заполнения. Он предполагает линейные цвета, что также является большим упрощением. Таким образом, еще есть возможности для улучшения.

Ответ 2

Хотя показ изображения в консоли не предназначен для использования консоли, вы можете, конечно, взломать вещи, поскольку окно консоли - это просто окно, как и любые другие окна.

Собственно, как только я начал разрабатывать библиотеку текстовых элементов управления для консольных приложений с поддержкой графики. Я никогда этого не делал, хотя у меня есть демонстрация демонстрации работоспособных концепций:

Текстовые элементы управления с изображением

И если вы получите размер шрифта консоли, вы можете разместить изображение очень точно.

Вот как вы можете это сделать:

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);
        }
    }
}

Вот как вы можете получить текущий размер шрифта консоли:

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);            
}

И необходимые дополнительные вызовы 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;

И результат:

[Graphics in Console

Ответ 3

Если вы используете ASCII 219 (█) дважды, у вас есть что-то вроде пикселя (██). Теперь вы ограничены количеством пикселей и количеством цветов в консольном приложении.

  • Если вы сохраните настройки по умолчанию у вас примерно 39x39 пикселей, если вы хотите больше, вы можете изменить размер своей консоли с помощью Console.WindowHeight = resSize.Height + 1; и Console.WindowWidth = resultSize.Width * 2;

  • вам нужно как можно больше сохранить соотношение сторон изображения, так что в большинстве случаев у вас не будет 39x39.

  • Malwyn опубликовал полностью недооцененный метод преобразования System.Drawing.Color в System.ConsoleColor

поэтому мой подход был бы

using System.Drawing;

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);
    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("██");
        }
        System.Console.WriteLine();
    }
}

чтобы вы могли

ConsoleWriteImage(new Bitmap(@"C:\image.gif"));

ввод образца:

введите описание изображения здесь

вывод образца:

введите описание изображения здесь

Ответ 4

Это было весело. Спасибо fubo, я попробовал ваше решение и смог увеличить разрешение предварительного просмотра на 4 (2x2).

Я обнаружил, что вы можете установить цвет фона для каждого отдельного char. Итак, вместо использования двух символов ASCII 219 (█) я использовал ASCII 223 (▀) два раза с разными цветами переднего и заднего плана. Это делит большой пиксель (██) на 4 подпикселя, подобный этому (▀▄).

В этом примере я помещаю оба изображения рядом друг с другом, чтобы вы могли легко увидеть разницу:

введите описание изображения здесь

Вот код:

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();
    }
  }
}

Для запуска примера растровое изображение "image.bmp" должно находиться в том же каталоге, что и исполняемый файл. Я увеличил размер консоли, размер предварительного просмотра еще 39 и может быть изменен на int sMax = 39;.

Решение от taffer также очень круто. У вас двоих есть моя возвышенность...

Ответ 5

Я читал о цветовых пространствах, и пространство LAB кажется хорошим вариантом для вас (см. следующие вопросы: Поиск точного "расстояния" между цветами и Алгоритм проверки подобия цветов)

Цитата Wikipedia страница CIELAB, преимущества этого цветового пространства:

В отличие от цветных моделей RGB и CMYK, цвет Lab разработан для приближения человеческого зрения. Он стремится к единообразию восприятия, а его компонент L близко соответствует восприятию человеком легкости. Таким образом, его можно использовать для точной коррекции цветового баланса путем изменения выходных кривых в компонентах a и b.

Чтобы измерить расстояние между цветами, вы можете использовать расстояние Delta E.

С этим вы можете приблизиться лучше от Color до ConsoleColor:

Во-первых, вы можете определить класс CieLab для представления цветов в этом пространстве:

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 };
    }
}

Существует два статических метода: один для измерения расстояния с помощью Delta E (DeltaE) и другого для объединения двух цветов, определяющих, сколько из каждого цвета (Combine).

И для преобразования из RGB в LAB вы можете использовать следующий метод (из здесь):

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))
    };
}

Идея заключается в том, чтобы использовать тени, такие как @AntoninLejsek do ('█', '▓', '▒', '░'), это позволяет получить более 16 цветов, сочетающих цвета консоли (используя метод Combine).

Здесь мы можем сделать некоторые улучшения, предварительно вычислив используемые цвета:

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
                });
            }
        }
}

Другим улучшением может быть доступ непосредственно к данным изображения с помощью LockBits вместо использования GetPixel.

ОБНОВЛЕНИЕ. Если изображение имеет части с одним и тем же цветом, вы можете значительно ускорить процесс рисования куска символов с одинаковыми цветами вместо символов персонажей:

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;
}

Наконец, вызовите DrawImage следующим образом:

static void Main(string[] args)
{
    ComputeColors();

    Bitmap image = new Bitmap("image.jpg", true);
    DrawImage(image);

}

Изображение результатов:

Консоль1

Консоль2



Следующие решения основаны не на символах, а на полных подробных изображениях


Вы можете рисовать через любое окно с помощью своего обработчика для создания объекта Graphics. Чтобы получить обработчик консольного приложения, вы можете импортировать GetConsoleWindow:

[DllImport("kernel32.dll", EntryPoint = "GetConsoleWindow", SetLastError = true)]
private static extern IntPtr GetConsoleHandle();

Затем создайте графику с обработчиком (используя Graphics.FromHwnd) и нарисуйте изображение с помощью методов в объекте Graphics, например:

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);
}

Версия 1

Это выглядит отлично, но если консоль изменена или прокручена, изображение исчезает, потому что окна обновлены (возможно, в вашем случае возможно внедрение какого-либо механизма для перерисовки изображения).


Другое решение - это вложение окна (Form) в консольное приложение. Для этого вам нужно импортировать SetParentMoveWindow, чтобы переместить окно внутри консоли):

[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);

Затем вам просто нужно создать Form и установить свойство BackgroundImage на нужное изображение (сделать это на Thread или Task, чтобы избежать блокировки консоли):

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);
}

Версия2

Конечно, вы можете установить FormBorderStyle = FormBorderStyle.None, чтобы скрыть границы окон (правое изображение)

В этом случае вы можете изменить размер консоли, и изображение/окно все равно там.

Одним из преимуществ этого подхода является то, что вы можете найти нужное окно и изменить изображение в любое время, просто изменив свойство BackgroundImage.

Ответ 6

Нет прямого пути. Но вы можете попытаться использовать конвертер image-to-ascii-art, например этот

Ответ 7

Да, вы можете это сделать, если немного растянуть вопрос, открыв Form из приложения Console.

Вот как вы можете консольное приложение открыть форму и отобразить изображение:

  • включить эти две ссылки в ваш проект: System.Drawing и System.Windows.Forms
  • включают два пространства имен:

using System.Windows.Forms;
using System.Drawing;

Смотрите этот пост о том, как это сделать!

Теперь вам нужно добавить что-то вроде этого:

Form form1 = new Form();
form1.BackgroundImage = bmp;
form1.ShowDialog();

Конечно, вы также можете использовать PictureBox..

И вы можете использовать form1.Show();, чтобы сохранить консоль в живом состоянии, пока отображается предварительный просмотр.

Оригинальное сообщение: Конечно, вы не можете правильно отобразить изображение внутри окна 25x80; даже если вы используете большее окно и блокируете графику, это будет не предварительный просмотр, а беспорядок!

Обновление: Похоже, вы можете в конце концов отобразить изображение на консольной форме; см. ответ taffer!