winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

Sixel.h (10637B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #pragma once
      4 #include "ChannelStreams.h"
      5 #include "VTSupport.h"
      6 #include <winget/ManifestLocalization.h>
      7 #include <wincodec.h>
      8 #include <wil/com.h>
      9 #include <filesystem>
     10 
     11 namespace AppInstaller::CLI::VirtualTerminal::Sixel
     12 {
     13     // Determines the height to width ratio of the pixels that make up a sixel (a sixel is 6 pixels tall and 1 pixel wide).
     14     // Note that each cell is always a height of 20 and a width of 10, regardless of the screen resolution of the terminal.
     15     // The 2:1 ratio will then result in each sixel being 12 of the 20 pixels of a cell.
     16     enum class AspectRatio
     17     {
     18         OneToOne = 7,
     19         TwoToOne = 0,
     20         ThreeToOne = 3,
     21         FiveToOne = 2,
     22     };
     23 
     24     // Determines the algorithm used when resizing the image.
     25     enum class InterpolationMode
     26     {
     27         NearestNeighbor = WICBitmapInterpolationModeNearestNeighbor,
     28         Linear = WICBitmapInterpolationModeLinear,
     29         Cubic = WICBitmapInterpolationModeCubic,
     30         Fant = WICBitmapInterpolationModeFant,
     31         HighQualityCubic = WICBitmapInterpolationModeHighQualityCubic,
     32     };
     33 
     34     // Contains the palette used by a sixel image.
     35     struct Palette
     36     {
     37         // Limit to 256 both as the defacto maximum supported colors and to enable always using 8bpp indexed pixel format.
     38         static constexpr UINT MaximumColorCount = 256;
     39 
     40         // Creates an empty palette.
     41         Palette() = default;
     42 
     43         // Create a palette from the given source image, color count, transparency setting.
     44         Palette(IWICImagingFactory* factory, IWICBitmapSource* bitmapSource, UINT colorCount, bool transparencyEnabled);
     45 
     46         // Create a palette combining the two palettes. Throws an exception if there are more than MaximumColorCount unique
     47         // colors between the two. This can be avoided by intentionally dividing the available colors between the palettes
     48         // when creating them.
     49         Palette(const Palette& first, const Palette& second);
     50 
     51         // Gets the WIC palette object.
     52         IWICPalette* Get() const;
     53 
     54         // Gets the color count for the palette.
     55         size_t Size() const;
     56 
     57         // Gets the color at the given index in the palette.
     58         WICColor& operator[](size_t index);
     59         WICColor operator[](size_t index) const;
     60 
     61     private:
     62         wil::com_ptr<IWICImagingFactory> m_factory;
     63         wil::com_ptr<IWICPalette> m_paletteObject;
     64         std::vector<WICColor> m_palette;
     65     };
     66 
     67     // Allows access to the pixel data of an image source.
     68     // Can be configured to translate and/or tile the view.
     69     struct ImageView
     70     {
     71         // Creates a non-owning view using the given data.
     72         ImageView(UINT width, UINT height, UINT stride, UINT byteCount, BYTE* bytes);
     73 
     74         // Create a view by locking a bitmap.
     75         // This must be used from the same thread as the bitmap.
     76         static ImageView Lock(IWICBitmap* imageSource);
     77 
     78         // Create a view by copying the pixels from the image.
     79         static ImageView Copy(IWICBitmapSource* imageSource);
     80 
     81         // Translate the view by the given pixel counts.
     82         // The pixel at [0, 0] of the original will be at [x, y].
     83         // If tile is true, the view will % coordinates outside of its dimensions back into its own view.
     84         // If tile is false, coordinates outside of the view will be null.
     85         void Translate(INT x, INT y, bool tile);
     86 
     87         // Gets the pixel of the view at the given coordinate.
     88         // Returns null if the coordinate is outside of the view.
     89         const BYTE* GetPixel(UINT x, UINT y) const;
     90 
     91         // Get the dimensions of the view.
     92         UINT Width() const;
     93         UINT Height() const;
     94 
     95     private:
     96         ImageView() = default;
     97 
     98         bool m_tile = false;
     99         UINT m_translateX = 0;
    100         UINT m_translateY = 0;
    101 
    102         wil::com_ptr<IWICBitmapLock> m_lockedImage;
    103         std::unique_ptr<BYTE[]> m_copiedImage;
    104 
    105         UINT m_viewWidth = 0;
    106         UINT m_viewHeight = 0;
    107         UINT m_viewStride = 0;
    108         UINT m_viewByteCount = 0;
    109         BYTE* m_viewBytes = nullptr;
    110     };
    111 
    112     // The set of values that defines the rendered output.
    113     struct RenderControls
    114     {
    115         // Yes, its right there in the name but the compiler can't read...
    116         static constexpr UINT PixelsPerSixel = 6;
    117 
    118         // Each cell is always a height of 20 and a width of 10, regardless of the screen resolution of the terminal.
    119         static constexpr UINT CellHeightInPixels = 20;
    120         static constexpr UINT CellWidthInPixels = 10;
    121 
    122         Sixel::AspectRatio AspectRatio = AspectRatio::OneToOne;
    123         bool TransparencyEnabled = true;
    124         bool StretchSourceToFill = false;
    125         bool UseRepeatSequence = false;
    126         UINT ColorCount = Palette::MaximumColorCount;
    127         UINT PixelWidth = 0;
    128         UINT PixelHeight = 0;
    129         Sixel::InterpolationMode InterpolationMode = InterpolationMode::HighQualityCubic;
    130 
    131         // The resulting sixel image will render to this size in terminal cells,
    132         // consuming as much as possible of the given size without going over.
    133         void RenderSizeInCells(UINT width, UINT height);
    134     };
    135 
    136     // Contains an image that can be manipulated and rendered to sixels.
    137     struct ImageSource
    138     {
    139         // Create an image source from a file.
    140         explicit ImageSource(const std::filesystem::path& imageFilePath);
    141 
    142         // Create an empty image source.
    143         ImageSource() = default;
    144 
    145         // Create an image source from a stream.
    146         ImageSource(std::istream& imageStream, Manifest::IconFileTypeEnum imageEncoding);
    147 
    148         // Resize the image to the given width and height, factoring in the target aspect ratio for rendering.
    149         // If stretchToFill is true, the resulting image will be both the given width and height.
    150         // If false, the resulting image will be at most the given width or height while preserving the aspect ratio.
    151         void Resize(UINT pixelWidth, UINT pixelHeight, AspectRatio targetRenderRatio, bool stretchToFill = false, InterpolationMode interpolationMode = InterpolationMode::HighQualityCubic);
    152 
    153         // Resizes the image using the given render controls.
    154         void Resize(const RenderControls& controls);
    155 
    156         // Creates a palette from the current image.
    157         Palette CreatePalette(UINT colorCount, bool transparencyEnabled) const;
    158 
    159         // Creates a palette from the current image.
    160         Palette CreatePalette(const RenderControls& controls) const;
    161 
    162         // Converts the image to be 8bpp indexed for the given palette.
    163         void ApplyPalette(const Palette& palette);
    164 
    165         // Create a view by locking the image source.
    166         // This must be used from the same thread as the image source.
    167         ImageView Lock() const;
    168 
    169         // Create a view by copying the pixels from the image source.
    170         ImageView Copy() const;
    171 
    172     private:
    173         wil::com_ptr<IWICImagingFactory> m_factory;
    174         wil::com_ptr<IWICBitmap> m_sourceImage;
    175     };
    176 
    177     // Allows one or more image sources to be rendered to a sixel output.
    178     struct Compositor
    179     {
    180         // Create an empty compositor.
    181         Compositor() = default;
    182 
    183         // Set the palette to be used by the compositor.
    184         void Palette(Palette palette);
    185 
    186         // Adds a new view to the compositor. Each successive view will be behind all of the others.
    187         void AddView(ImageView&& view);
    188 
    189         // Gets the number of views in the compositor.
    190         size_t ViewCount() const;
    191 
    192         // Gets the color at the given index in the palette.
    193         ImageView& operator[](size_t index);
    194         const ImageView& operator[](size_t index) const;
    195 
    196         // Get the render controls for the compositor.
    197         RenderControls& Controls();
    198         const RenderControls& Controls() const;
    199 
    200         // Render to sixel format for storage / use multiple times.
    201         ConstructedSequence Render();
    202 
    203         // Renders to sixel format directly to the stream.
    204         void RenderTo(Execution::BaseStream& stream);
    205 
    206         // Renders to sixel format directly to the stream.
    207         void RenderTo(Execution::OutputStream& stream);
    208 
    209     private:
    210         RenderControls m_renderControls;
    211         Sixel::Palette m_palette;
    212         std::vector<ImageView> m_views;
    213     };
    214 
    215     // A helpful wrapper around the sixel image primitives that makes rendering a single image easier.
    216     struct Image
    217     {
    218         // Create an image from a file.
    219         Image(const std::filesystem::path& imageFilePath);
    220 
    221         // Create an image from a stream.
    222         Image(std::istream& imageStream, Manifest::IconFileTypeEnum imageEncoding);
    223 
    224         // Set the aspect ratio of the result.
    225         Image& AspectRatio(AspectRatio aspectRatio);
    226 
    227         // Determine whether transparency is enabled.
    228         // This will affect whether transparent pixels are rendered or not.
    229         Image& Transparency(bool transparencyEnabled);
    230 
    231         // If transparency is enabled, one of the colors will be reserved for it.
    232         Image& ColorCount(UINT colorCount);
    233 
    234         // The resulting sixel image will render to this size in terminal cell pixels.
    235         Image& RenderSizeInPixels(UINT width, UINT height);
    236 
    237         // The resulting sixel image will render to this size in terminal cells,
    238         // consuming as much as possible of the given size without going over.
    239         Image& RenderSizeInCells(UINT width, UINT height);
    240 
    241         // Only affects the scaling of the image that occurs when render size is set.
    242         // When true, the source image will be stretched to fill the target size.
    243         // When false, the source image will be scaled while keeping its original aspect ratio.
    244         Image& StretchSourceToFill(bool stretchSourceToFill);
    245 
    246         // Compresses the output using repeat sequences.
    247         Image& UseRepeatSequence(bool useRepeatSequence);
    248 
    249         // Render to sixel format for storage / use multiple times.
    250         ConstructedSequence Render();
    251 
    252         // Renders to sixel format directly to the output stream.
    253         void RenderTo(Execution::OutputStream& stream);
    254 
    255     private:
    256         // Creates a compositor for the image using the current render controls.
    257         std::pair<ImageSource, Compositor> CreateCompositor();
    258 
    259         ImageSource m_imageSource;
    260         RenderControls m_renderControls;
    261     };
    262 }