Explorar o código

Work on SixelEncoder

tznind hai 11 meses
pai
achega
943fa11230
Modificáronse 1 ficheiros con 139 adicións e 13 borrados
  1. 139 13
      Terminal.Gui/Drawing/SixelEncoder.cs

+ 139 - 13
Terminal.Gui/Drawing/SixelEncoder.cs

@@ -1,6 +1,4 @@
-using System.Reflection.Metadata;
-
-namespace Terminal.Gui;
+namespace Terminal.Gui;
 
 /// <summary>
 /// Encodes a images into the sixel console image output format.
@@ -23,18 +21,144 @@ public class SixelEncoder
 
         string fillArea = GetFillArea (pixels);
 
-        string pallette = GetColorPallette (pixels, out var dictionary);
+        string pallette = GetColorPallette (pixels, out var quantizer);
 
-        const string pixelData =
-            "~~~~$-"
-            + // First 6 rows of red pixels
-            "~~~~$-";  // Next 6 rows of red pixels
+        string pixelData = WriteSixel (pixels, quantizer);
 
         const string terminator = "\u001b\\"; // End sixel sequence
 
         return start + defaultRatios + completeStartSequence + noScaling + fillArea + pallette + pixelData + terminator;
     }
 
+
+    /*
+        A sixel is a column of 6 pixels - with a width of 1 pixel
+
+     Column controlled by one sixel character:
+       [ ]  - Bit 0 (top-most pixel)
+       [ ]  - Bit 1
+       [ ]  - Bit 2
+       [ ]  - Bit 3
+       [ ]  - Bit 4
+       [ ]  - Bit 5 (bottom-most pixel)
+    */
+
+    private string WriteSixel (Color [,] pixels, ColorQuantizer quantizer)
+    {
+        StringBuilder sb = new StringBuilder ();
+        int height = pixels.GetLength (1);
+        int width = pixels.GetLength (0);
+        int n = 1; // Used for checking when to add the line terminator
+
+        // Iterate over each row of the image
+        for (int y = 0; y < height; y++)
+        {
+            int p = y * width;
+            Color cachedColor = pixels [0, y];
+            int cachedColorIndex = quantizer.GetNearestColor (cachedColor);
+            int count = 1;
+            int c = -1;
+
+            // Iterate through each column in the row
+            for (int x = 0; x < width; x++)
+            {
+                Color color = pixels [x, y];
+                int colorIndex = quantizer.GetNearestColor (color);
+
+                if (colorIndex == cachedColorIndex)
+                {
+                    count++;
+                }
+                else
+                {
+                    // Output the cached color first
+                    if (cachedColorIndex == -1)
+                    {
+                        c = 0x3f; // Key color or transparent
+                    }
+                    else
+                    {
+                        c = 0x3f + n;
+                        sb.AppendFormat ("#{0}", cachedColorIndex);
+                    }
+
+                    // If count is less than 3, we simply repeat the character
+                    if (count < 3)
+                    {
+                        sb.Append ((char)c, count);
+                    }
+                    else
+                    {
+                        // RLE if count is greater than 3
+                        sb.AppendFormat ("!{0}{1}", count, (char)c);
+                    }
+
+                    // Reset for the new color
+                    count = 1;
+                    cachedColorIndex = colorIndex;
+                }
+            }
+
+            // Handle the last run of the color
+            if (c != -1 && count > 1)
+            {
+                if (cachedColorIndex == -1)
+                {
+                    c = 0x3f; // Key color
+                }
+                else
+                {
+                    sb.AppendFormat ("#{0}", cachedColorIndex);
+                }
+
+                if (count < 3)
+                {
+                    sb.Append ((char)c, count);
+                }
+                else
+                {
+                    sb.AppendFormat ("!{0}{1}", count, (char)c);
+                }
+            }
+
+            // Line terminator or separator depending on `n`
+            if (n == 32)
+            {
+                /*
+                 2. Line Separator (-):
+                   
+                   The line separator instructs the sixel renderer to move to the next row of sixels.
+                   After a -, the renderer will start a new row from the leftmost column. This marks the end of one line of sixel data and starts a new line.
+                   This ensures that the sixel data drawn after the separator appears below the previous row rather than overprinting it.
+               
+                   Use case: When you want to start drawing a new line of sixels (e.g., after completing a row of sixel columns).
+                */
+
+                n = 1;
+                sb.Append ("-"); // Write sixel line separator
+            }
+            else
+            {
+                /*
+                 *1. Line Terminator ($):
+                   
+                   The line terminator instructs the sixel renderer to return to the start of the current row but allows subsequent sixel characters to be overprinted on the same row.
+                   This is used when you are working with multiple color layers or want to continue drawing in the same row but with a different color.
+                   The $ allows you to overwrite sixel characters in the same vertical position by using different colors, effectively allowing you to combine colors on a per-sixel basis.
+                   
+                   Use case: When you need to draw multiple colors within the same vertical slice of 6 pixels.
+                 */
+
+                n <<= 1;
+                sb.Append ("$"); // Write line terminator
+            }
+        }
+
+        return sb.ToString ();
+    }
+
+
+
     private string GetColorPallette (Color [,] pixels, out ColorQuantizer quantizer)
     {
         quantizer = new ColorQuantizer ();
@@ -65,15 +189,17 @@ public class SixelEncoder
 
         return $"{widthInChars};{heightInChars}";
     }
-
     private int GetHeightInChars (Color [,] pixels)
     {
-        // TODO
-        return 2;
-    }
+        // Height in pixels is equal to the number of rows in the pixel array
+        int height = pixels.GetLength (1);
 
+        // Each SIXEL character represents 6 pixels vertically
+        return (height + 5) / 6; // Equivalent to ceiling(height / 6)
+    }
     private int GetWidthInChars (Color [,] pixels)
     {
-        return 3;
+        // Width in pixels is equal to the number of columns in the pixel array
+        return pixels.GetLength (0);
     }
 }