SixelEncoderTests.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. using Color = Terminal.Gui.Color;
  2. namespace UnitTests.Drawing;
  3. public class SixelEncoderTests
  4. {
  5. [Fact]
  6. public void EncodeSixel_RedSquare12x12_ReturnsExpectedSixel ()
  7. {
  8. string expected = "\u001bP" // Start sixel sequence
  9. + "0;0;0" // Defaults for aspect ratio and grid size
  10. + "q" // Signals beginning of sixel image data
  11. + "\"1;1;12;12" // no scaling factors (1x1) and filling 12x12 pixel area
  12. /*
  13. * Definition of the color palette
  14. * #<index>;<type>;<R>;<G>;<B>" - 2 means RGB. The values range 0 to 100
  15. */
  16. + "#0;2;100;0;0" // Red color definition
  17. /*
  18. * Start of the Pixel data
  19. * We draw 6 rows at once, so end up with 2 'lines'
  20. * Both are basically the same and terminate with dollar hyphen (except last row)
  21. * Format is:
  22. * #0 (selects to use color palette index 0 i.e. red)
  23. * !12 (repeat next byte 12 times i.e. the whole length of the row)
  24. * ~ (the byte 111111 i.e. fill completely)
  25. * $ (return to start of line)
  26. * - (move down to next line)
  27. */
  28. + "#0!12~$-"
  29. + "#0!12~$" // Next 6 rows of red pixels
  30. + "\u001b\\"; // End sixel sequence
  31. // Arrange: Create a 12x12 bitmap filled with red
  32. Color [,] pixels = new Color [12, 12];
  33. for (var x = 0; x < 12; x++)
  34. {
  35. for (var y = 0; y < 12; y++)
  36. {
  37. pixels [x, y] = new (255, 0, 0);
  38. }
  39. }
  40. // Act: Encode the image
  41. var encoder = new SixelEncoder (); // Assuming SixelEncoder is the class that contains the EncodeSixel method
  42. string result = encoder.EncodeSixel (pixels);
  43. // Since image is only red we should only have 1 color definition
  44. Color c1 = Assert.Single (encoder.Quantizer.Palette);
  45. Assert.Equal (new (255, 0, 0), c1);
  46. Assert.Equal (expected, result);
  47. }
  48. [Fact]
  49. public void EncodeSixel_12x12GridPattern3x3_ReturnsExpectedSixel ()
  50. {
  51. /*
  52. * Each block is a 3x3 square, alternating black and white.
  53. * The pattern alternates between rows, creating a checkerboard.
  54. * We have 4 blocks per row, and this repeats over 12x12 pixels.
  55. *
  56. * ███...███...
  57. * ███...███...
  58. * ███...███...
  59. * ...███...███
  60. * ...███...███
  61. * ...███...███
  62. * ███...███...
  63. * ███...███...
  64. * ███...███...
  65. * ...███...███
  66. * ...███...███
  67. * ...███...███
  68. *
  69. * Because we are dealing with sixels (drawing 6 rows at once), we will
  70. * see 2 bands being drawn. We will also see how we have to 'go back over'
  71. * the current line after drawing the black (so we can draw the white).
  72. */
  73. string expected = "\u001bP" // Start sixel sequence
  74. + "0;0;0" // Defaults for aspect ratio and grid size
  75. + "q" // Signals beginning of sixel image data
  76. + "\"1;1;12;12" // no scaling factors (1x1) and filling 12x12 pixel area
  77. /*
  78. * Definition of the color palette
  79. */
  80. + "#0;2;0;0;0" // Black color definition (index 0: RGB 0,0,0)
  81. + "#1;2;100;100;100" // White color definition (index 1: RGB 100,100,100)
  82. /*
  83. * Start of the Pixel data
  84. *
  85. * Lets consider only the first 6 pixel (vertically). We have to fill the top 3 black and bottom 3 white.
  86. * So we need to select black and fill 000111. To convert this into a character we must +63 and convert to ASCII.
  87. * Later on we will also need to select white and fill the inverse, i.e. 111000.
  88. *
  89. * 111000 (binary) → w (ASCII 119).
  90. * 000111 (binary) → F (ASCII 70).
  91. *
  92. * Therefore the lines become
  93. *
  94. * #0 (Select black)
  95. * FFF (fill first 3 pixels horizontally - and top half of band black)
  96. * www (fill next 3 pixels horizontally - bottom half of band black)
  97. * FFFwww (as above to finish the line)
  98. *
  99. * Next we must go back and fill the white (on the same band)
  100. * #1 (Select white)
  101. */
  102. + "#0FFFwwwFFFwww$" // First pass of top band (Filling black)
  103. + "#1wwwFFFwwwFFF$-" // Second pass of top band (Filling white)
  104. // Sequence repeats exactly the same because top band is actually identical pixels to bottom band
  105. + "#0FFFwwwFFFwww$" // First pass of bottom band (Filling black)
  106. + "#1wwwFFFwwwFFF$" // Second pass of bottom band (Filling white)
  107. + "\u001b\\"; // End sixel sequence
  108. // Arrange: Create a 12x12 bitmap with a 3x3 checkerboard pattern
  109. Color [,] pixels = new Color [12, 12];
  110. for (var y = 0; y < 12; y++)
  111. {
  112. for (var x = 0; x < 12; x++)
  113. {
  114. // Create a 3x3 checkerboard by alternating the color based on pixel coordinates
  115. if ((x / 3 + y / 3) % 2 == 0)
  116. {
  117. pixels [x, y] = new (0, 0, 0); // Black
  118. }
  119. else
  120. {
  121. pixels [x, y] = new (255, 255, 255); // White
  122. }
  123. }
  124. }
  125. // Act: Encode the image
  126. var encoder = new SixelEncoder (); // Assuming SixelEncoder is the class that contains the EncodeSixel method
  127. string result = encoder.EncodeSixel (pixels);
  128. // We should have only black and white in the palette
  129. Assert.Equal (2, encoder.Quantizer.Palette.Count);
  130. Color black = encoder.Quantizer.Palette.ElementAt (0);
  131. Color white = encoder.Quantizer.Palette.ElementAt (1);
  132. Assert.Equal (new (0, 0, 0), black);
  133. Assert.Equal (new (255, 255, 255), white);
  134. // Compare the generated SIXEL string with the expected one
  135. Assert.Equal (expected, result);
  136. }
  137. [Fact]
  138. public void EncodeSixel_Transparent12x12_ReturnsExpectedSixel ()
  139. {
  140. string expected = "\u001bP" // Start sixel sequence
  141. + "0;1;0" // Defaults for aspect ratio and grid size
  142. + "q" // Signals beginning of sixel image data
  143. + "\"1;1;12;12" // no scaling factors (1x1) and filling 12x12 pixel area
  144. + "#0;2;0;0;0" // Black transparent (TODO: Shouldn't really be output this if it is transparent)
  145. // Since all pixels are transparent we don't output any colors at all, so its just newline
  146. + "-" // Nothing on first or second lines
  147. + "\u001b\\"; // End sixel sequence
  148. // Arrange: Create a 12x12 bitmap filled with fully transparent pixels
  149. Color [,] pixels = new Color [12, 12];
  150. for (var x = 0; x < 12; x++)
  151. {
  152. for (var y = 0; y < 12; y++)
  153. {
  154. pixels [x, y] = new (0, 0, 0, 0); // Fully transparent
  155. }
  156. }
  157. // Act: Encode the image
  158. var encoder = new SixelEncoder ();
  159. string result = encoder.EncodeSixel (pixels);
  160. // Assert: Expect the result to be fully transparent encoded output
  161. Assert.Equal (expected, result);
  162. }
  163. [Fact]
  164. public void EncodeSixel_VerticalMix_TransparentAndColor_ReturnsExpectedSixel ()
  165. {
  166. string expected = "\u001bP" // Start sixel sequence
  167. + "0;1;0" // Defaults for aspect ratio and grid size (1 indicates support for transparent pixels)
  168. + "q" // Signals beginning of sixel image data
  169. + "\"1;1;12;12" // No scaling factors (1x1) and filling 12x12 pixel area
  170. /*
  171. * Define the color palette:
  172. * We'll use one color (Red) for the colored pixels.
  173. */
  174. + "#0;2;100;0;0" // Red color definition (index 0: RGB 100,0,0)
  175. + "#1;2;0;0;0" // Black transparent (TODO: Shouldn't really be output this if it is transparent)
  176. /*
  177. * Start of the Pixel data
  178. * We have alternating transparent (0) and colored (red) pixels in a vertical band.
  179. * The pattern for each sixel byte is 101010, which in binary (+63) converts to ASCII character 'T'.
  180. * Since we have 12 pixels horizontally, we'll see this pattern repeat across the row so we see
  181. * the 'sequence repeat' 12 times i.e. !12 (do the next letter 'T' 12 times).
  182. */
  183. + "#0!12T$-" // First band of alternating red and transparent pixels
  184. + "#0!12T$" // Second band, same alternating red and transparent pixels
  185. + "\u001b\\"; // End sixel sequence
  186. // Arrange: Create a 12x12 bitmap with alternating transparent and red pixels in a vertical band
  187. Color [,] pixels = new Color [12, 12];
  188. for (var x = 0; x < 12; x++)
  189. {
  190. for (var y = 0; y < 12; y++)
  191. {
  192. // For simplicity, we'll make every other row transparent
  193. if (y % 2 == 0)
  194. {
  195. pixels [x, y] = new (255, 0, 0); // Red pixel
  196. }
  197. else
  198. {
  199. pixels [x, y] = new (0, 0, 0, 0); // Transparent pixel
  200. }
  201. }
  202. }
  203. // Act: Encode the image
  204. var encoder = new SixelEncoder ();
  205. string result = encoder.EncodeSixel (pixels);
  206. // Assert: Expect the result to match the expected sixel output
  207. Assert.Equal (expected, result);
  208. }
  209. }