| 184 | } |
| 185 | |
| 186 | void writeImageSixel(WriteBuffer & out, const UInt8 * pixels, size_t width, size_t height, size_t channels) |
| 187 | { |
| 188 | auto to_level = [](UInt8 c) -> int { return (c * (SIXEL_LEVELS - 1) + 127) / 255; }; |
| 189 | |
| 190 | auto pixel_color = [&](size_t x, size_t y) -> int |
| 191 | { |
| 192 | const UInt8 * p = pixels + (y * width + x) * channels; |
| 193 | UInt8 r = 0; |
| 194 | UInt8 g = 0; |
| 195 | UInt8 b = 0; |
| 196 | if (channels == 1) |
| 197 | { |
| 198 | r = g = b = p[0]; |
| 199 | } |
| 200 | else |
| 201 | { |
| 202 | r = p[0]; |
| 203 | g = p[1]; |
| 204 | b = p[2]; |
| 205 | if (channels == 4) |
| 206 | { |
| 207 | /// Composite over a black background using the alpha channel. |
| 208 | const UInt32 a = p[3]; |
| 209 | r = static_cast<UInt8>(r * a / 255); |
| 210 | g = static_cast<UInt8>(g * a / 255); |
| 211 | b = static_cast<UInt8>(b * a / 255); |
| 212 | } |
| 213 | } |
| 214 | return (to_level(r) * SIXEL_LEVELS + to_level(g)) * SIXEL_LEVELS + to_level(b); |
| 215 | }; |
| 216 | |
| 217 | /// Start of the Sixel data: DCS, then raster attributes (1:1 pixel aspect ratio and the image size). |
| 218 | writeCString("\033Pq", out); |
| 219 | writeCString("\"1;1;", out); |
| 220 | writeIntText(width, out); |
| 221 | writeChar(';', out); |
| 222 | writeIntText(height, out); |
| 223 | |
| 224 | /// Define the full fixed palette up front (color number; RGB color space [2]; R, G, B in percent [0, 100]). |
| 225 | for (int c = 0; c < SIXEL_LEVELS * SIXEL_LEVELS * SIXEL_LEVELS; ++c) |
| 226 | { |
| 227 | const int r = c / (SIXEL_LEVELS * SIXEL_LEVELS); |
| 228 | const int g = (c / SIXEL_LEVELS) % SIXEL_LEVELS; |
| 229 | const int b = c % SIXEL_LEVELS; |
| 230 | writeChar('#', out); |
| 231 | writeIntText(c, out); |
| 232 | writeCString(";2;", out); |
| 233 | writeIntText(r * 100 / (SIXEL_LEVELS - 1), out); |
| 234 | writeChar(';', out); |
| 235 | writeIntText(g * 100 / (SIXEL_LEVELS - 1), out); |
| 236 | writeChar(';', out); |
| 237 | writeIntText(b * 100 / (SIXEL_LEVELS - 1), out); |
| 238 | } |
| 239 | |
| 240 | /// Sixels encode 6 vertical pixels at a time, so the image is processed in horizontal bands of 6 rows. |
| 241 | VectorWithMemoryTracking<int> band_colors(width * 6); |
| 242 | std::array<bool, SIXEL_LEVELS * SIXEL_LEVELS * SIXEL_LEVELS> present{}; |
| 243 | StringWithMemoryTracking line; |
no test coverage detected