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


85

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

Вот сравнение текущих ответов на основе персонажей:

Вход:

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

Выход:

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

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

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

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


В консоли, как в окне консоли? Нет. Однако вы можете запустить отдельный диалог / окно.
Christian.K

Консольные приложения используются в основном для текстовых приложений. Нет возможности отобразить изображение. Вы можете запустить другое приложение, которое отображает изображение. Это другое приложение, скорее всего, должно поддерживать параметр командной строки для передачи ему изображения.
Lindos Pechos

Почему вы используете консольное приложение? Куда он бежит? вы всегда можете запустить процесс, чтобы открыть программу просмотра изображений по умолчанию или просто собственное приложение winforms и т. д.
TaW

1
Мне нужно улучшить код Антонина Лейсека (и это здорово). Есть несколько несоответствий цветов, и с улучшенной производительностью я также
могу

Ответы:


55

Далее я поигрался с кодом от @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 в соответствии с коэффициентом заполнения. Он предполагает линейные цвета, что также является большим упрощением. Так что есть возможности для улучшения.


Спасибо @fubo. Между прочим, я экспериментировал с гамма-коррекцией RGB и Lab, и оба были улучшены. Но коэффициент заполнения должен был соответствовать используемому шрифту, и он вообще не работал для шрифтов TrueType. Так что это уже не будет универсальным решением.
Антонин Лейсек

88

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

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

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

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

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

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;

И результат:

[Графика в консоли


2
Вау, это действительно интересно! Не могли бы вы пояснить, что вы имеете в виду под тем, что я никогда не заканчивал это, хотя у меня есть работающая демонстрационная версия концепции ? Есть недостатки или просто отсутствует полироль ..?
TaW

Значит, проект очень незавершенный. Я создал базовую архитектуру, базовую объектно-ориентированную среду, управляемую событиями, поддержку мыши, насос сообщений и т. Д. Но даже самые фундаментальные элементы управления, такие как Buttonи TextBoxт. Д., По-прежнему отсутствуют. Моя мечта - сделать достаточно полную поддержку XAML с привязкой данных и с WPF-подобной философией «встраивать что-нибудь во что угодно». Но я очень далек от этого ... ну, в данный момент :)
Дьёрдь Кёсег

3
Хорошо, понятно, но код выглядит так, как будто его можно использовать для любого менее полного, менее амбициозного проекта, да?
TaW

1
Ну, в нынешнем виде ... не совсем. Но я планирую опубликовать его на GitHub, как только сделаю его более стабильным и последовательным.
Дьердь Кёсег

Это выглядит действительно круто, за исключением необходимости использования неуправляемых DLL. Кроме того, как мы можем отслеживать развитие библиотек?
уховертка

56

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

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

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

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

так что мой подход был бы

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

так что вы можете

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

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

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

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

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


7
@willywonka_dailyblah - Это пурпурное щупальце из "Дня щупальца". Not Doom
Blaatz0r

@ Blaatz0r, я имею в виду графику, похожую на Doom ... Imma новый ребенок на блоке, я знаю только Doom

3
Все прощено :-p. Если у вас когда-нибудь будет возможность попробовать Day if the щупальца, это отличная игра, старая, но отличная.
Blaatz0r

37

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

Я обнаружил, что вы можете установить цвет фона для каждого отдельного символа. Итак, вместо использования двух символов 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 тоже очень крутой. Мой голос за вас двоих ...


23

Я читал о цветовых пространствах, и пространство 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();

Затем создайте графику с помощью обработчика (using 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

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


Другое решение - встроить window ( 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 раз медленнее, чем решение Антонина Лейсека. В любом случае очень интересный результат круга.
Byyo

4

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


:-) Однако обратите внимание, что цветовые возможности консоли (окна) также довольно ограничены. Так что эффекты "затухания" и т.п. невозможны.
Christian.K

1
Что ж, это соответствует разрешению: P
DarkWanderer

1
@ Christian.K Ответ Антонина Лейсека делает возможным исчезновение
Byyo

1

Да, вы можете это сделать, если немного растянете вопрос, открыв 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; даже если вы используете большее окно и блокируете графику, это будет не предварительный просмотр, а беспорядок!

Обновление: похоже, вы все-таки можете нарисовать изображение в форме консоли с помощью GDI; см. ответ Таффера!

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.