From ba1658928b43cbdac4fb0fd771328c1e141911ad Mon Sep 17 00:00:00 2001 From: AnonymouX47 Date: Thu, 30 May 2024 09:34:02 +0100 Subject: [PATCH 1/9] feat: ctlseqs: Add SGR indexed-color sequences --- src/term_image/ctlseqs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/term_image/ctlseqs.py b/src/term_image/ctlseqs.py index ba3094be..2ccd1fde 100644 --- a/src/term_image/ctlseqs.py +++ b/src/term_image/ctlseqs.py @@ -43,6 +43,8 @@ SGR_FG_RGB_2 = f"{CSI}38:2::{Ps}:{Ps}:{Ps}m" SGR_BG_RGB = f"{CSI}48;2;{Pm(3)}m" SGR_BG_RGB_2 = f"{CSI}48:2::{Ps}:{Ps}:{Ps}m" +SGR_FG_INDEXED = f"{CSI}38:5:{Ps}m" +SGR_BG_INDEXED = f"{CSI}48:5:{Ps}m" # DEC Modes DECSET = f"{CSI}?{Ps}h" From 3e945a63d089db5ce6328a0e1ef37f724b62e7fd Mon Sep 17 00:00:00 2001 From: AnonymouX47 Date: Thu, 30 May 2024 09:35:17 +0100 Subject: [PATCH 2/9] feat: utils: Add default XTerm 256-color palette --- src/term_image/utils.py | 259 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) diff --git a/src/term_image/utils.py b/src/term_image/utils.py index 67433289..d246aae7 100644 --- a/src/term_image/utils.py +++ b/src/term_image/utils.py @@ -770,3 +770,262 @@ def _process_run_wrapper(self, *args, **kwargs): if isinstance(value, BuiltinFunctionType): setattr(termios, name, lock_tty(value)) """ + +XTERM_256_PALETTE = ( + 0, 0, 0, + 205, 0, 0, + 0, 205, 0, + 205, 205, 0, + 0, 0, 238, + 205, 0, 205, + 0, 205, 205, + 229, 229, 229, + 127, 127, 127, + 255, 0, 0, + 0, 255, 0, + 255, 255, 0, + 92, 92, 255, + 255, 0, 255, + 0, 255, 255, + 255, 255, 255, + 0, 0, 0, + 0, 0, 95, + 0, 0, 135, + 0, 0, 175, + 0, 0, 215, + 0, 0, 255, + 0, 95, 0, + 0, 95, 95, + 0, 95, 135, + 0, 95, 175, + 0, 95, 215, + 0, 95, 255, + 0, 135, 0, + 0, 135, 95, + 0, 135, 135, + 0, 135, 175, + 0, 135, 215, + 0, 135, 255, + 0, 175, 0, + 0, 175, 95, + 0, 175, 135, + 0, 175, 175, + 0, 175, 215, + 0, 175, 255, + 0, 215, 0, + 0, 215, 95, + 0, 215, 135, + 0, 215, 175, + 0, 215, 215, + 0, 215, 255, + 0, 255, 0, + 0, 255, 95, + 0, 255, 135, + 0, 255, 175, + 0, 255, 215, + 0, 255, 255, + 95, 0, 0, + 95, 0, 95, + 95, 0, 135, + 95, 0, 175, + 95, 0, 215, + 95, 0, 255, + 95, 95, 0, + 95, 95, 95, + 95, 95, 135, + 95, 95, 175, + 95, 95, 215, + 95, 95, 255, + 95, 135, 0, + 95, 135, 95, + 95, 135, 135, + 95, 135, 175, + 95, 135, 215, + 95, 135, 255, + 95, 175, 0, + 95, 175, 95, + 95, 175, 135, + 95, 175, 175, + 95, 175, 215, + 95, 175, 255, + 95, 215, 0, + 95, 215, 95, + 95, 215, 135, + 95, 215, 175, + 95, 215, 215, + 95, 215, 255, + 95, 255, 0, + 95, 255, 95, + 95, 255, 135, + 95, 255, 175, + 95, 255, 215, + 95, 255, 255, + 135, 0, 0, + 135, 0, 95, + 135, 0, 135, + 135, 0, 175, + 135, 0, 215, + 135, 0, 255, + 135, 95, 0, + 135, 95, 95, + 135, 95, 135, + 135, 95, 175, + 135, 95, 215, + 135, 95, 255, + 135, 135, 0, + 135, 135, 95, + 135, 135, 135, + 135, 135, 175, + 135, 135, 215, + 135, 135, 255, + 135, 175, 0, + 135, 175, 95, + 135, 175, 135, + 135, 175, 175, + 135, 175, 215, + 135, 175, 255, + 135, 215, 0, + 135, 215, 95, + 135, 215, 135, + 135, 215, 175, + 135, 215, 215, + 135, 215, 255, + 135, 255, 0, + 135, 255, 95, + 135, 255, 135, + 135, 255, 175, + 135, 255, 215, + 135, 255, 255, + 175, 0, 0, + 175, 0, 95, + 175, 0, 135, + 175, 0, 175, + 175, 0, 215, + 175, 0, 255, + 175, 95, 0, + 175, 95, 95, + 175, 95, 135, + 175, 95, 175, + 175, 95, 215, + 175, 95, 255, + 175, 135, 0, + 175, 135, 95, + 175, 135, 135, + 175, 135, 175, + 175, 135, 215, + 175, 135, 255, + 175, 175, 0, + 175, 175, 95, + 175, 175, 135, + 175, 175, 175, + 175, 175, 215, + 175, 175, 255, + 175, 215, 0, + 175, 215, 95, + 175, 215, 135, + 175, 215, 175, + 175, 215, 215, + 175, 215, 255, + 175, 255, 0, + 175, 255, 95, + 175, 255, 135, + 175, 255, 175, + 175, 255, 215, + 175, 255, 255, + 215, 0, 0, + 215, 0, 95, + 215, 0, 135, + 215, 0, 175, + 215, 0, 215, + 215, 0, 255, + 215, 95, 0, + 215, 95, 95, + 215, 95, 135, + 215, 95, 175, + 215, 95, 215, + 215, 95, 255, + 215, 135, 0, + 215, 135, 95, + 215, 135, 135, + 215, 135, 175, + 215, 135, 215, + 215, 135, 255, + 215, 175, 0, + 215, 175, 95, + 215, 175, 135, + 215, 175, 175, + 215, 175, 215, + 215, 175, 255, + 215, 215, 0, + 215, 215, 95, + 215, 215, 135, + 215, 215, 175, + 215, 215, 215, + 215, 215, 255, + 215, 255, 0, + 215, 255, 95, + 215, 255, 135, + 215, 255, 175, + 215, 255, 215, + 215, 255, 255, + 255, 0, 0, + 255, 0, 95, + 255, 0, 135, + 255, 0, 175, + 255, 0, 215, + 255, 0, 255, + 255, 95, 0, + 255, 95, 95, + 255, 95, 135, + 255, 95, 175, + 255, 95, 215, + 255, 95, 255, + 255, 135, 0, + 255, 135, 95, + 255, 135, 135, + 255, 135, 175, + 255, 135, 215, + 255, 135, 255, + 255, 175, 0, + 255, 175, 95, + 255, 175, 135, + 255, 175, 175, + 255, 175, 215, + 255, 175, 255, + 255, 215, 0, + 255, 215, 95, + 255, 215, 135, + 255, 215, 175, + 255, 215, 215, + 255, 215, 255, + 255, 255, 0, + 255, 255, 95, + 255, 255, 135, + 255, 255, 175, + 255, 255, 215, + 255, 255, 255, + 8, 8, 8, + 18, 18, 18, + 28, 28, 28, + 38, 38, 38, + 48, 48, 48, + 58, 58, 58, + 68, 68, 68, + 78, 78, 78, + 88, 88, 88, + 98, 98, 98, + 108, 108, 108, + 118, 118, 118, + 128, 128, 128, + 138, 138, 138, + 148, 148, 148, + 158, 158, 158, + 168, 168, 168, + 178, 178, 178, + 188, 188, 188, + 198, 198, 198, + 208, 208, 208, + 218, 218, 218, + 228, 228, 228, + 238, 238, 238, +) # fmt: skip From 8efcf3fc937792bb7fdca2bbf23f1e2d93b1061b Mon Sep 17 00:00:00 2001 From: AnonymouX47 Date: Thu, 30 May 2024 09:41:15 +0100 Subject: [PATCH 3/9] feat: TextImage: Implement image quantization - Add: `TextImage._get_render_data()`, implementing image quantization for text-based styles towards indexed-color rendering support. - Add: `TextImage._XTERM_240_PALETTE_IMAGE`. --- src/term_image/image/common.py | 58 ++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/term_image/image/common.py b/src/term_image/image/common.py index c51054b8..c8d26ee5 100644 --- a/src/term_image/image/common.py +++ b/src/term_image/image/common.py @@ -44,6 +44,7 @@ URLNotFoundError, ) from ..utils import ( + XTERM_256_PALETTE, ClassInstanceMethod, ClassProperty, arg_type_error, @@ -1941,6 +1942,10 @@ class TextImage(BaseImage): from its subclasses. """ + # 240 colors i.e excluding the first 16 + _XTERM_240_PALETTE_IMAGE = Image.new("P", (1, 1)) + _XTERM_240_PALETTE_IMAGE.putpalette(XTERM_256_PALETTE[16 * 3 :]) + # Pixels are represented in a 1-to-2 ratio within one character cell # pixel-size == width * height/2 # pixel-ratio == width / (height/2) == 2 * (width / height) == 2 * cell-ratio @@ -1951,6 +1956,59 @@ class TextImage(BaseImage): def _is_on_kitty() -> bool: return get_terminal_name_version()[0] == "kitty" + def _get_render_data( + self, + img: PIL.Image.Image, + *args, + frame: bool = False, + indexed_color: bool = False, + **kwargs, + ) -> tuple[ + PIL.Image.Image, + list[tuple[int, int, int]] | list[int] | None, + list[int] | None, + ]: + """ + See :py:meth:`BaseImage._render_image` for the description of the method and + all other parameters not described here. + + Args: + indexed_color: Whether to quantize the render image to a 240-color palette. + + Returns: + The same as the overriden method if *indexed_color* is ``False``. Otherwise, + + * The returned image has mode ``P`` or ``PA``, depending on the mode of the + source image. + * ``rgb`` is a list of integers in the range [0, 255], where each integer + is a valid index for a 256-color terminal palette. + """ + if indexed_color: + frame_img = img if frame else None + img, rgb, a = super()._get_render_data(img, *args, frame=frame, **kwargs) + + if indexed_color: + orig_img = img + + img = img.copy() if img.mode == "RGB" else img.convert("RGB") + with img: + quantized_img = img.quantize( + palette=__class__._XTERM_240_PALETTE_IMAGE, dither=Image.Dither.NONE + ) + + if orig_img.mode == "RGBA": + with quantized_img: + quantized_img = quantized_img.convert("PA") + quantized_img.putalpha(img.getchannel("A")) + + if frame_img is not orig_img: + self._close_image(orig_img) + + img = quantized_img + rgb = [index + 16 for index in img.getdata(0)] + + return (img, rgb, a) + @abstractmethod def _render_image( self, From 00d182f3511767b260ea007f45c4e940700043bb Mon Sep 17 00:00:00 2001 From: AnonymouX47 Date: Thu, 30 May 2024 11:30:15 +0100 Subject: [PATCH 4/9] feat: BlockImage: Add render methods - Add: `DIRECT` and `INDEXED` render methods. - Add: *method* style parameter and format spec field. - Add: Attributes: - `_FORMAT_SPEC` - `_render_methods` - `_default_render_method` - `_render_method` - `_style_args` - Add: `._check_style_format_spec()`. - Add: *method* parameter to `._render_image()`. - Change: Update class docstring. --- src/term_image/image/block.py | 103 +++++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 2 deletions(-) diff --git a/src/term_image/image/block.py b/src/term_image/image/block.py index 9fd9b2f9..72df3031 100644 --- a/src/term_image/image/block.py +++ b/src/term_image/image/block.py @@ -4,9 +4,10 @@ import io import os +import re from math import ceil from operator import mul -from typing import Optional, Tuple, Union +from typing import Any, Optional, Tuple, Union import PIL @@ -17,13 +18,99 @@ LOWER_PIXEL = "\u2584" # lower-half block element UPPER_PIXEL = "\u2580" # upper-half block element +# Constants for render methods +DIRECT = "direct" +INDEXED = "indexed" + class BlockImage(TextImage): - """A render style using unicode half blocks and 24-bit colour escape codes. + """A render style using Unicode half blocks with direct-color or indexed-color + control sequences. See :py:class:`TextImage` for the description of the constructor. + + | + + **Render Methods** + + :py:class:`BlockImage` provides two methods of :term:`rendering` images, namely: + + DIRECT (default) + Renders an image using direct-color (truecolor) control sequences. + + Pros: + + * Better color reproduction. + + Cons: + + * Lesser terminal emulator support (though any terminal emulator worthy of use + today actually does provide support). + + INDEXED + Renders an image using indexed-color control sequences but with only the upper + **240 colors** of the terminal's 256-color palette. + + Pros: + + * Wider terminal emulator support. + + Cons: + + * Worse color reproduction. + + The render method can be set with + :py:meth:`set_render_method() ` using the names + specified above. + + | + + **Style-Specific Render Parameters** + + See :py:meth:`BaseImage.draw` (particularly the *style* parameter). + + * **method** (*None | str*) → Render method override. + + * ``None`` → the current effective render method of the instance is used + * A valid render method name (as specified in the **Render Methods** section + above) → used instead of the current effective render method of the instance + * *default* → ``None`` + + | + + **Format Specification** + + See :ref:`format-spec`. + + :: + + [ ] + + * ``method`` → render method override + + * ``D`` → **DIRECT** render method (current frame only, for animated images) + * ``I`` → **INDEXED** render method (current frame only, for animated images) + * *default* → Current effective render method of the image """ + _FORMAT_SPEC: tuple[re.Pattern] = (re.compile("[DI]"),) + _render_methods: set[str] = {DIRECT, INDEXED} + _default_render_method: str = DIRECT + _render_method: str = DIRECT + _style_args = { + "method": ( + None, + ( + lambda x: isinstance(x, str), + "Render method must be a string", + ), + ( + lambda x: x.lower() in __class__._render_methods, + "Unknown render method for 'block' render style", + ), + ), + } + @classmethod def is_supported(cls): if cls._supported is None: @@ -35,6 +122,17 @@ def is_supported(cls): return cls._supported + @classmethod + def _check_style_format_spec(cls, spec: str, original: str) -> dict[str, Any]: + parent, (method,) = cls._get_style_format_spec(spec, original) + args = {} + if parent: + args.update(super()._check_style_format_spec(parent, original)) + if method: + args["method"] = DIRECT if method == "D" else INDEXED + + return cls._check_style_args(args) + def _get_render_size(self) -> Tuple[int, int]: return tuple(map(mul, self.rendered_size, (1, 2))) @@ -56,6 +154,7 @@ def _render_image( alpha: Union[None, float, str], *, frame: bool = False, + method: str | None = None, split_cells: bool = False, ) -> str: # NOTE: From c0ebac183099de645603185d8d0aa8ae42babeb7 Mon Sep 17 00:00:00 2001 From: AnonymouX47 Date: Thu, 30 May 2024 11:55:23 +0100 Subject: [PATCH 5/9] feat:BlockImage: Implement indexed-color rendering - Add: Use the *method* parameter of `._render_image()`. - Add: Implement the `INDEXED` render method. --- src/term_image/image/block.py | 41 ++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/src/term_image/image/block.py b/src/term_image/image/block.py index 72df3031..f595847c 100644 --- a/src/term_image/image/block.py +++ b/src/term_image/image/block.py @@ -11,7 +11,7 @@ import PIL -from ..ctlseqs import SGR_BG_RGB, SGR_FG_RGB, SGR_NORMAL +from ..ctlseqs import SGR_BG_INDEXED, SGR_BG_RGB, SGR_FG_INDEXED, SGR_FG_RGB, SGR_NORMAL from ..utils import get_fg_bg_colors from .common import TextImage @@ -169,32 +169,39 @@ def update_buffer(): buf_write(blank * n) elif a_cluster1 == 0: # up is transparent buf_write(SGR_NORMAL) - buf_write(SGR_FG_RGB % cluster2) + buf_write(sgr_fg % cluster2) buf_write(lower_pixel * n) elif a_cluster2 == 0: # down is transparent buf_write(SGR_NORMAL) - buf_write(SGR_FG_RGB % cluster1) + buf_write(sgr_fg % cluster1) buf_write(upper_pixel * n) else: no_alpha = True if not alpha or no_alpha: - r, g, b = cluster2 - # Kitty does not render BG colors equal to the default BG color - if is_on_kitty and cluster2 == bg_color: - r += r < 255 or -1 - buf_write(SGR_BG_RGB % (r, g, b)) + if method_is_direct: + r, g, b = cluster2 + # Kitty does not render BG colors equal to the default BG color + if is_on_kitty and cluster2 == bg_color: + r += r < 255 or -1 + buf_write(sgr_bg % (r, g, b)) + else: + buf_write(sgr_bg % cluster2) + if cluster1 == cluster2: buf_write(blank * n) else: - buf_write(SGR_FG_RGB % cluster1) + buf_write(sgr_fg % cluster1) buf_write(upper_pixel * n) buffer = io.StringIO() buf_write = buffer.write # Eliminate attribute resolution cost - bg_color = get_fg_bg_colors()[1] - is_on_kitty = self._is_on_kitty() + render_method = (method or self._render_method).lower() + method_is_direct = render_method == DIRECT + if method_is_direct: + bg_color = get_fg_bg_colors()[1] + is_on_kitty = self._is_on_kitty() if split_cells: blank = " \0" lower_pixel = LOWER_PIXEL + "\0" @@ -204,11 +211,19 @@ def update_buffer(): lower_pixel = LOWER_PIXEL upper_pixel = UPPER_PIXEL end_of_line = SGR_NORMAL + "\n" + sgr_fg = SGR_FG_RGB if method_is_direct else SGR_FG_INDEXED + sgr_bg = SGR_BG_RGB if method_is_direct else SGR_BG_INDEXED width, height = self._get_render_size() frame_img = img if frame else None - img, rgb, a = self._get_render_data(img, alpha, round_alpha=True, frame=frame) - alpha = img.mode == "RGBA" + img, rgb, a = self._get_render_data( + img, + alpha, + round_alpha=True, + frame=frame, + indexed_color=not method_is_direct, + ) + alpha = img.mode == ("RGBA" if method_is_direct else "PA") # clean up (ImageIterator uses one PIL image throughout) if frame_img is not img: From af040792285a799ad5b1030073b18c70d9067d90 Mon Sep 17 00:00:00 2001 From: AnonymouX47 Date: Thu, 30 May 2024 22:10:05 +0100 Subject: [PATCH 6/9] fix: TextImage: Fix image quantization with alpha - Fix: Typo in `._get_render_data()`. --- src/term_image/image/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/term_image/image/common.py b/src/term_image/image/common.py index c8d26ee5..f03c67bc 100644 --- a/src/term_image/image/common.py +++ b/src/term_image/image/common.py @@ -1999,7 +1999,7 @@ def _get_render_data( if orig_img.mode == "RGBA": with quantized_img: quantized_img = quantized_img.convert("PA") - quantized_img.putalpha(img.getchannel("A")) + quantized_img.putalpha(orig_img.getchannel("A")) if frame_img is not orig_img: self._close_image(orig_img) From 5421b2b5faff3f8076151a1226355f652006dd31 Mon Sep 17 00:00:00 2001 From: AnonymouX47 Date: Thu, 30 May 2024 22:22:19 +0100 Subject: [PATCH 7/9] docs: Update terminal support information - Add: Mention indexed-color support in the requirements. - Change: Update the list of supported terminal emulators. --- README.md | 2 +- docs/source/faqs.rst | 4 ++-- docs/source/start/installation.rst | 22 ++++++++++++---------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 6cea7eae..ab247fdd 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ - support for the [Kitty graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/). - support for the [iTerm2 inline image protocol](https://iterm2.com/documentation-images.html). - - full Unicode support and ANSI 24-bit color support + - Unicode support and direct-color (truecolor) or indexed-color (256-color) support. **Plans to support a wider variety of terminal emulators are in motion** (see [Planned Features](#planned-features)). diff --git a/docs/source/faqs.rst b/docs/source/faqs.rst index 4ee5b656..96b1fd2a 100644 --- a/docs/source/faqs.rst +++ b/docs/source/faqs.rst @@ -11,8 +11,8 @@ What about Windows support? - Drawing images and animations doesn't work completely well with Python for Windows. See :doc:`issues`. - If stuck on Windows and want to use all features, you could use WSL + Windows Terminal. -Why are colours not properly reproduced? - - Some terminals support 24-bit colors but have a **256-color pallete**. This limits color reproduction. +Why are colours not properly reproduced with :py:class:`BlockImage`'s ``DIRECT`` render method? + - Some terminals support direct-color control sequences but actually use a **256-color pallete**. This limits color reproduction. Why are images out of scale? - If :ref:`auto-cell-ratio` is supported and enabled, call diff --git a/docs/source/start/installation.rst b/docs/source/start/installation.rst index a37e5aed..3ebe2ff2 100644 --- a/docs/source/start/installation.rst +++ b/docs/source/start/installation.rst @@ -10,7 +10,7 @@ Requirements * support for the `Kitty graphics protocol `_. * support for the `iTerm2 inline image protocol `_. - * full Unicode support and ANSI 24-bit color support + * Unicode support and direct-color (truecolor) or indexed-color (256-color) support. **Plans to support a wider variety of terminal emulators are in motion** (see :doc:`/planned`). @@ -36,21 +36,23 @@ Supported Terminal Emulators Some terminals emulators that have been tested to meet the requirements for at least one render style include: +- Alacritty +- iTerm2 +- Kitty +- Konsole +- MinTTY (on Windows) +- Terminal (on Mac OS) +- Terminology +- Termux (on Android) +- WezTerm +- Windows Terminal +- XTerm - **libvte**-based terminal emulators such as: - Gnome Terminal - Terminator - Tilix -- Kitty -- Konsole -- iTerm2 -- WezTerm -- Alacritty -- Windows Terminal -- MinTTY (on Windows) -- Termux (on Android) - .. note:: If you've tested ``term-image`` on any other terminal emulator that meets all requirements, please mention the name in a new thread under `this discussion From c6ca1e135fc6ce8cabe34b7dd05784072326e6ee Mon Sep 17 00:00:00 2001 From: AnonymouX47 Date: Tue, 4 Jun 2024 07:32:18 +0100 Subject: [PATCH 8/9] fix: ctlseqs: Use non-standard indexed-color seqs - Fix: Use the non-standard semi-colon-delimited indexed-color SGR control sequences, as they're more widely supported than the standard colon-delimited counterparts. Refs: https://github.com/AnonymouX47/term-image/issues/90#issuecomment-2144037780 --- src/term_image/ctlseqs.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/term_image/ctlseqs.py b/src/term_image/ctlseqs.py index 2ccd1fde..50e2da01 100644 --- a/src/term_image/ctlseqs.py +++ b/src/term_image/ctlseqs.py @@ -43,8 +43,10 @@ SGR_FG_RGB_2 = f"{CSI}38:2::{Ps}:{Ps}:{Ps}m" SGR_BG_RGB = f"{CSI}48;2;{Pm(3)}m" SGR_BG_RGB_2 = f"{CSI}48:2::{Ps}:{Ps}:{Ps}m" -SGR_FG_INDEXED = f"{CSI}38:5:{Ps}m" -SGR_BG_INDEXED = f"{CSI}48:5:{Ps}m" +SGR_FG_INDEXED = f"{CSI}38;5;{Ps}m" +SGR_FG_INDEXED_2 = f"{CSI}38:5:{Ps}m" +SGR_BG_INDEXED = f"{CSI}48;5;{Ps}m" +SGR_BG_INDEXED_2 = f"{CSI}48:5:{Ps}m" # DEC Modes DECSET = f"{CSI}?{Ps}h" From f0adc3edaa0566c360fa775251e064ca434a13be Mon Sep 17 00:00:00 2001 From: AnonymouX47 Date: Thu, 6 Jun 2024 22:40:40 +0100 Subject: [PATCH 9/9] chore: Update CHANGELOG for #109 --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3442c7f2..7935ef77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added +- indexed-color (256-color) rendering for `BlockImage` ([#109]). + - `DIRECT` and `INDEXED` render methods. + - *method* style-specific render parameter and format spec field. + +[#109]: https://github.com/AnonymouX47/term-image/pull/109 + + ## [0.7.1] - 2023-02-10 ### Fixed - Undefined references in some top-level functions ([497d9b7], [4e8b3e7]).