Відображення зображення у консольному додатку


85

У мене є консольний додаток, який управляє зображеннями. Тепер мені потрібно щось на зразок попереднього перегляду зображень у консольній програмі. Чи є спосіб відобразити їх у консолі?

Ось порівняння поточних відповідей на основі символів:

Вхідні дані:

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

Вихід:

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

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

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

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


У консолі, як у вікні консолі? Ні. Однак ви можете запустити окреме діалогове вікно / вікно.
Крістіан.

Консольні програми використовуються в основному лише для текстових програм. Немає можливості відобразити зображення. Ви можете запустити іншу програму, яка відображає зображення. Цей інший додаток, швидше за все, повинен підтримувати опцію командного рядка, щоб передати йому зображення.
Ліндос Печос

Чому ви використовуєте консольний додаток? Куди це біжить? Ви завжди можете розпочати процес, щоб відкрити засіб перегляду зображень за замовчуванням або просто додаток WinForms тощо.
TaW

1
Мені потрібне вдосконалення коду Антоніна Лейсека (що чудово). Є кілька невідповідностей кольорів, і з поліпшенням продуктивності я міг би також відображати анімовані GIF-файли
Byyo,

Відповіді:


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, і обидва були вдосконаленнями. Але коефіцієнт заповнення потрібно було встановити так, щоб він відповідав використаному шрифту, і він не працював взагалі для шрифтів справжнього типу. Тож це вже не буде єдиний розмір, який підходить для всіх рішень.
Антонін Лейсек,

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

Це означає, що проект дуже неповний. Я створив базову архітектуру, базове середовище OO, кероване подіями, підтримку миші, насос повідомлень тощо. Але навіть такі основні елементи керування, як Button, TextBoxтощо, все ще відсутні. Я мрію зробити досить повну підтримку XAML із прив'язкою даних та філософією "вбудовувати що-небудь у що завгодно" за допомогою WPF. Але я дуже далеко від цього ... ну, на даний момент :)
Дьєрдь Кшег,

3
Добре, я розумію, але код, схоже, може бути використаний для будь-якого менш повного, менш амбітного проекту, так?
TaW

1
Ну, у теперішньому вигляді ... не дуже. Але я планую опублікувати його на GitHub, коли я зроблю це дещо стабільним і цілісним.
György Kőszeg

Це виглядає по-справжньому круто, за винятком необхідності використання некерованих бібліотек DLL. Крім того, як ми можемо відстежувати розвиток бібліотек?
вухатка

56

Якщо ви двічі використовуєте 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 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 new kid on the block I only know Doom

3
Все пробачено :-p. Якщо у вас коли-небудь є шанс спробувати День, якщо щупальце - це чудова гра стара, але чудова.
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;.

Розчин з тафера також дуже прохолодний. Ви удвох підтримали моє ...


23

Я читав про кольорові простори, і LAB- простір, здається, є хорошим варіантом для вас (див. Ці запитання: Пошук точної "відстані" між кольорами та алгоритм для перевірки схожості кольорів )

Цитуючи сторінку CIELAB у Вікіпедії , перевагами цього кольорового простору є:

На відміну від кольорових моделей RGB та CMYK, Lab color призначений для наближення людського зору. Він прагне до перцептивної рівномірності, а його 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 разів повільніше, ніж рішення Антоніна Лейсека. У будь-якому випадку дуже цікавий результат круга.
Byyo,

4

Прямого шляху немає. Але ви можете спробувати скористатись перетворювачем зображень у формат ascii-art, як цей


:-) однак, зверніть увагу, що можливості кольорів консолі (вікна) також досить обмежені. Тож ефекти «зникання» тощо неможливі навіть.
Крістіан.

1
Ну, це відповідає роздільній здатності: P
DarkWanderer

1
@ Christian.K Відповідь Антоніна Лейсека робить можливим згасання
Бьйо

1

Так, ви можете це зробити, якщо трохи розтягнути питання, відкривши a 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.