Mostrar una imagen en una aplicación de consola

85

Tengo una aplicación de consola que gestiona imágenes. Ahora necesito algo como una vista previa de las imágenes dentro de la aplicación de consola. ¿Hay alguna forma de mostrarlos en la consola?

Aquí hay una comparación de las respuestas actuales basadas en caracteres:

Entrada:

ingrese la descripción de la imagen aquí

Salida:

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Byyo
fuente
¿En la consola, como en la ventana de la consola? No. Sin embargo, puede iniciar un diálogo / ventana por separado.
Christian.K
Las aplicaciones de consola se utilizan principalmente para aplicaciones de solo texto. No hay forma de mostrar una imagen. Puede iniciar otra aplicación que muestre la imagen. Esta otra aplicación probablemente necesitaría soportar una opción de línea de comando para pasarle una imagen.
Lindos Pechos
¿Por qué utiliza una aplicación de consola? ¿Dónde corre? siempre puede iniciar un proceso para abrir el visor de imágenes predeterminado o simplemente una aplicación propia de
winforms
1
Necesito una mejora del código de Antonín Lejsek (que es genial). Hay algunas coincidencias de color y con un rendimiento mejorado también podría mostrar gifs animados
Byyo

Respuestas:

55

Seguí jugando con el código de @DieterMeemken. Reduje a la mitad la resolución vertical y agregué difuminado vía °▒▓. A la izquierda está el resultado de Dieter Meemken, a la derecha mi. En la parte inferior hay una imagen original redimensionada para que coincida con la salida. Resultado de salida Si bien la función de conversión de Malwyns es impresionante, no utiliza todos los colores grises, lo que es una lástima.

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

uso:

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

EDITAR

La distancia de color es un tema complejo ( aquí , aquí y enlaces en esas páginas ...). Intenté calcular la distancia en YUV y los resultados fueron bastante peores que en RGB. Podrían ser mejores con Lab y DeltaE, pero no lo intenté. La distancia en RGB parece ser lo suficientemente buena. De hecho, los resultados son muy similares para la distancia euclidiana y manhattan en el espacio de color RGB, por lo que sospecho que hay muy pocos colores para elegir.

El resto es solo una comparación de fuerza bruta de color contra todas las combinaciones de colores y patrones (= símbolos). Dije que la proporción de llenado para °▒▓█ sea 1/4, 2/4, 3/4 y 4/4. En ese caso, el tercer símbolo es redundante con respecto al primero. Pero si las proporciones no fueran tan uniformes (depende de la fuente), los resultados podrían cambiar, así que lo dejé allí para futuras mejoras. El color promedio del símbolo se calcula como el promedio ponderado de foregroudColor y backgroundColor según la proporción de relleno. Asume colores lineales, lo que también es una gran simplificación. Por tanto, todavía hay margen de mejora.

Antonín Lejsek
fuente
Gracias @fubo. Por cierto, experimenté con RGB y Lab con corrección de gamma y ambos fueron una mejora. Pero la proporción de relleno tenía que configurarse para que coincidiera con la fuente utilizada y no funcionó en absoluto para las fuentes TrueType. Por lo tanto, ya no sería una solución única para todos.
Antonín Lejsek
88

Aunque mostrar una imagen en una consola no es el uso previsto de la consola, seguramente puede piratear las cosas, ya que la ventana de la consola es solo una ventana, como cualquier otra ventana.

De hecho, una vez comencé a desarrollar una biblioteca de controles de texto para aplicaciones de consola con soporte gráfico. Nunca terminé eso, aunque tengo una demostración de prueba de concepto en funcionamiento:

Controles de texto con imagen

Y si obtiene el tamaño de fuente de la consola, puede colocar la imagen con mucha precisión.

Así es como puedes hacerlo:

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

Así es como puede obtener el tamaño de fuente actual de la consola:

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

Y las llamadas, constantes y tipos adicionales necesarios de 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;

Y el resultado:

[Gráficos en consola

György Kőszeg
fuente
2
¡Vaya, eso es realmente interesante! ¿Podría explicar un poco lo que quiere decir con "nunca he terminado", aunque tengo una demostración de prueba de concepto que funciona ? ¿Algún inconveniente o simplemente falta el esmalte ..?
TaW el
Significa que el proyecto está muy incompleto. Hice la arquitectura básica, el entorno OO base controlado por eventos, el soporte del mouse, la bomba de mensajes, etc. Pero aún faltan los controles más fundamentales como Button, TextBoxetc. Mi sueño es hacer un soporte XAML bastante completo con enlace de datos y con la filosofía de "incrustar cualquier cosa en cualquier cosa" similar a WPF. Pero estoy muy lejos de eso ... bueno, en este momento :)
György Kőszeg
3
Bien, ya veo, pero el código parece que se puede usar para cualquier proyecto menos completo y menos ambicioso, ¿no?
TaW el
1
Bueno, en su forma actual ... no realmente. Pero planeo publicarlo en GitHub una vez que lo haga algo estable y coherente.
György Kőszeg
Esto se ve realmente genial, excepto por la necesidad de usar DLL no administradas. Además, ¿cómo podemos realizar un seguimiento del desarrollo de las bibliotecas?
una tijereta
56

Si usa ASCII 219 (█) dos veces, tiene algo como un píxel (██). Ahora está restringido por la cantidad de píxeles y la cantidad de colores en su aplicación de consola.

  • si mantiene la configuración predeterminada, tiene aproximadamente 39x39 píxeles, si desea más, puede cambiar el tamaño de su consola con Console.WindowHeight = resSize.Height + 1;yConsole.WindowWidth = resultSize.Width * 2;

  • tienes que mantener la relación de aspecto de la imagen en la medida de lo posible, por lo que no tendrás 39x39 en la mayoría de los casos

  • Malwyn publicó un método totalmente subestimado para convertir System.Drawing.ColoraSystem.ConsoleColor

entonces mi enfoque sería

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

así que puedes

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

entrada de muestra:

ingrese la descripción de la imagen aquí

salida de muestra:

ingrese la descripción de la imagen aquí

fubo
fuente
7
@willywonka_dailyblah - Es el tentáculo púrpura del Día del Tentáculo. Not Doom
Blaatz0r
@ Blaatz0r me refiero a gráficos tipo Doom ... soy un niño nuevo en el bloque, solo conozco a Doom
3
Todo está perdonado :-p. Si alguna vez tienes la oportunidad, prueba Day si el tentáculo es un gran juego antiguo pero genial.
Blaatz0r
37

eso fue divertido. Gracias fubo , probé su solución y pude aumentar la resolución de la vista previa en 4 (2x2).

Descubrí que puedes establecer el color de fondo para cada carácter individual. Entonces, en lugar de usar dos caracteres ASCII 219 (█), usé ASCII 223 (▀) dos veces con diferentes colores de primer plano y de fondo. Eso divide el gran Pixel (██) en 4 subpíxeles como este (▀▄).

En este ejemplo, coloco ambas imágenes una al lado de la otra, para que pueda ver la diferencia fácilmente:

ingrese la descripción de la imagen aquí

Aquí está el código:

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

Para ejecutar el ejemplo, el mapa de bits "image.bmp" tiene que estar en el mismo directorio que el ejecutable. Aumenté el tamaño de la consola, el tamaño de la vista previa sigue siendo 39 y se puede cambiar en int sMax = 39;.

La solución de taffer también es muy fría. Ustedes dos tienen mi voto a favor ...

Dieter Meemken
fuente
23

Estaba leyendo sobre espacios de color y el espacio LAB parece ser una buena opción para usted (vea estas preguntas: Encontrar una "distancia" precisa entre los colores y el algoritmo para verificar la similitud de colores )

Citando la página de Wikipedia CIELAB , las ventajas de este espacio de color son:

A diferencia de los modelos de color RGB y CMYK, el color Lab está diseñado para aproximarse a la visión humana. Aspira a la uniformidad perceptiva y su componente L se asemeja mucho a la percepción humana de la ligereza. Por lo tanto, se puede utilizar para realizar correcciones precisas del balance de color modificando las curvas de salida en los componentes ay b.

Para medir la distancia entre colores, puede usar la distancia Delta E.

Con esto puede aproximarse mejor de Colora ConsoleColor:

En primer lugar, puede definir una CieLabclase para representar colores en este espacio:

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

Hay dos métodos estáticos, uno para medir la distancia usando Delta E ( DeltaE) y otro para combinar dos colores especificando cuánto de cada color ( Combine).

Y para transformar de RGBa LABpuede usar el siguiente método (desde aquí ):

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

La idea es usar caracteres de sombra como @AntoninLejsek do ('█', '▓', '▒', '°'), esto le permite obtener más de 16 colores combinando los colores de la consola (usando el Combinemétodo).

Aquí, podemos hacer algunas mejoras calculando previamente los colores a usar:

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

Otra mejora podría ser el acceso directo a los datos de la imagen usando en LockBitslugar de usar GetPixel.

ACTUALIZACIÓN : Si la imagen tiene partes con el mismo color, puede acelerar considerablemente el proceso de dibujo de un grupo de caracteres que tengan los mismos colores, en lugar de caracteres individuales:

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

Finalmente, llame DrawImageasí:

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

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

}

Imágenes de resultado:

Consola1

Console2



Las siguientes soluciones no se basan en caracteres, pero proporcionan imágenes completamente detalladas


Puede dibujar sobre cualquier ventana usando su controlador para crear un Graphicsobjeto. Para obtener el controlador de una aplicación de consola, puede hacerlo importando GetConsoleWindow:

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

Luego, cree un gráfico con el controlador (usando Graphics.FromHwnd) y dibuje la imagen usando los métodos en el Graphicsobjeto, por ejemplo:

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

Versión 1

Esto se ve bien, pero si se cambia el tamaño de la consola o se desplaza, la imagen desaparece porque las ventanas se actualizan (tal vez implementar algún tipo de mecanismo para volver a dibujar la imagen es posible en su caso).


Otra solución es incrustar una ventana ( Form) en la aplicación de consola. Para hacer esto, debe importar SetParent(y MoveWindowreubicar la ventana dentro de la consola):

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

Luego solo necesita crear una propiedad Formy establecer BackgroundImagela imagen deseada (hágalo en Threado Taskpara evitar bloquear la consola):

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

Versión 2

Por supuesto, puede configurar FormBorderStyle = FormBorderStyle.Nonepara ocultar los bordes de las ventanas (imagen de la derecha)

En este caso, puede cambiar el tamaño de la consola y la imagen / ventana seguirá ahí.

Un beneficio de este enfoque es que puede ubicar la ventana donde desee y cambiar la imagen en cualquier momento simplemente cambiando la BackgroundImagepropiedad.

Arturo Menchaca
fuente
Gracias por su esfuerzo pero su acercamiento es 6 veces más lento que la solución de Antonín Lejsek. De todos modos, resultado de vuelta muy interesante.
Byyo
4

No hay forma directa. Pero puede intentar usar un convertidor de imagen a arte ascii como este

DarkWanderer
fuente
:-) Sin embargo, tenga en cuenta que las capacidades de color de la consola (ventana) también son bastante limitadas. Así que los efectos de "desvanecimiento", etc. ni siquiera son posibles.
Christian.K
1
Bueno, coincide con la resolución: P
DarkWanderer
1
@ Christian.K La respuesta de Antonín Lejsek hace posible el desvanecimiento
Byyo
1

Sí, puede hacerlo si estira un poco la pregunta abriendo un Formdesde la aplicación Consola.

Así es como puede hacer que su aplicación de consola abra un formulario y muestre una imagen:

  • incluya estas dos referencias en su proyecto: System.DrawingySystem.Windows.Forms
  • incluir los dos espacios de nombres también:

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

¡Vea esta publicación sobre cómo hacer eso !

Ahora todo lo que necesitas es agregar algo como esto:

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

Por supuesto, también puede utilizar un PictureBox..

Y puede utilizar form1.Show();para mantener viva la consola mientras se muestra la vista previa.

Publicación original: Por supuesto, no se puede mostrar correctamente una imagen dentro de una ventana de 25x80; Incluso si usa una ventana más grande y bloquea gráficos, ¡no sería una vista previa sino un desastre!

Actualización: parece que, después de todo, GDI puede dibujar una imagen en el formulario de la consola; ver la respuesta de taffer!

TaW
fuente