~aleteoryx/muditaos

f7771013f9ca518a20e95b996a663200a65da5c9 — Adam Wulkiewicz 3 years ago b98e439
[MOS-550] Revert - Implement eink partial refresh

This reverts commit 41d6e786b6264925caac34650d512e30b8848f8d.
M .vscode/launch.json => .vscode/launch.json +1 -2
@@ 64,7 64,7 @@
            "cwd": "${workspaceFolder}",
            "gdbPath": "arm-none-eabi-gdb",
            // "debuggerArgs": ["-x", "${workspaceFolder}/.gdbinit-1051"],
            "serverArgs": ["-strict", "-ir", "-singlerun", "-speed", "25000"],
            // "serverArgs": ["-strict", "-ir", "-singlerun", "-speed", "25000"],
            // "runToEntryPoint": "main",
            // "runToMain": true,
            "preLaunchCommands": [],


@@ 109,7 109,6 @@
            "serverpath": "JLinkGDBServerCLExe",
            "cwd": "${workspaceFolder}",
            "gdbPath": "arm-none-eabi-gdb",
            "serverArgs": ["-strict", "-ir", "-singlerun", "-speed", "25000"],
            "interface": "swd",
            "device": "MCIMXRT1051",
            "jlinkscript": "${workspaceFolder}/evkbimxrt1050_sdram_init_T6.jlinkscript",

M .vscode/tasks.json => .vscode/tasks.json +0 -38
@@ 169,44 169,6 @@
            "detail": "Configure cmake project and build with Ninja",
        },

        {
            "type": "shell",
            "label": "Configure PurePhone (RT1051, DEV)",
            "command": "./configure.sh",
            "args": [
                "pure",
                "rt1051",
                "RelWithDebInfo",
                "-DWITH_DEVELOPMENT_FEATURES=ON",
                "-G",
                "Ninja"
            ],
            "options": {
                "cwd": "${workspaceFolder}"
            },
            "group": "build",
            "detail": "Run cmake configure script",
        },

        {
            "type": "shell",
            "label": "Configure BellHybrid (RT1051, DEV)",
            "command": "./configure.sh",
            "args": [
                "bell",
                "rt1051",
                "RelWithDebInfo",
                "-DWITH_DEVELOPMENT_FEATURES=ON",
                "-G",
                "Ninja"
            ],
            "options": {
                "cwd": "${workspaceFolder}"
            },
            "group": "build",
            "detail": "Run cmake configure script",
        },

        // {
        //     "type": "shell",
        //     "label": "Run JLink Server for PurePhone (RT1051)",

M harmony_changelog.md => harmony_changelog.md +0 -1
@@ 12,7 12,6 @@

#### UI/UX:
* UI update (Home Screen settings).
* Implemented partial refresh of the display.

#### Translations:
* Completed missing translations.

M module-apps/application-calendar/windows/CalendarMainWindow.cpp => module-apps/application-calendar/windows/CalendarMainWindow.cpp +1 -1
@@ 44,7 44,7 @@ namespace gui

        offsetFromTop = style::window::default_vertical_pos + style::window::calendar::month_year_height;
        monthWidth    = style::window::default_body_width;
        monthHeight   = style::window_height - offsetFromTop;
        monthHeight   = style::window_height - style::window::default_vertical_pos - style::nav_bar::height;
        dayWidth      = style::window::calendar::day_cell_width;
        dayHeight     = style::window::calendar::day_cell_height;


M module-bsp/board/linux/eink/ED028TC1.c => module-bsp/board/linux/eink/ED028TC1.c +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/**


@@ 119,7 119,7 @@ EinkStatus_e EinkResetAndInitialize()
    return EinkOK;
}

EinkStatus_e EinkUpdateFrame(EinkFrame_t frame, const uint8_t *buffer)
EinkStatus_e EinkUpdateFrame(EinkFrame_t frame, uint8_t *buffer)
{
    uint32_t offset_eink   = frame.pos_y * BOARD_EINK_DISPLAY_RES_X + frame.pos_x;
    uint32_t offset_buffer = 0;

M module-bsp/board/linux/eink/ED028TC1.h => module-bsp/board/linux/eink/ED028TC1.h +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/**


@@ 90,7 90,7 @@ extern "C"
     * @return  EinkNoMem - Could not allocate the temporary buffer
     *          EinkOK - Part of image send successfully
     */
    EinkStatus_e EinkUpdateFrame(EinkFrame_t frame, const uint8_t *buffer);
    EinkStatus_e EinkUpdateFrame(EinkFrame_t frame, uint8_t *buffer);

    /**
     * @brief This function sets the waveform to the \ref EinkWaveformINIT to make the display clearing more deep and

M module-bsp/board/linux/eink/LinuxEinkDisplay.cpp => module-bsp/board/linux/eink/LinuxEinkDisplay.cpp +2 -12
@@ 47,19 47,9 @@ namespace hal::eink
        displayColorMode = mode;
    }

    EinkStatus LinuxEinkDisplay::showImage(const std::vector<EinkFrame> &updateFrames,
                                           const EinkFrame &refreshFrame,
                                           const std::uint8_t *frameBuffer,
                                           const EinkRefreshMode refreshMode)
    EinkStatus LinuxEinkDisplay::showImage(std::uint8_t *frameBuffer, const EinkRefreshMode refreshMode)
    {
        for (const EinkFrame &frame : updateFrames) {
            const std::uint8_t *buffer = frameBuffer + frame.pos_y * frame.size.width;
            const auto status          = translateStatus(
                EinkUpdateFrame({frame.pos_x, frame.pos_y, frame.size.width, frame.size.height}, buffer));
            if (status != EinkStatus::EinkOK)
                return status;
        }
        return EinkStatus::EinkOK;
        return translateStatus(EinkUpdateFrame(EinkFrame_t{0, 0, size.width, size.height}, frameBuffer));
    }

    void LinuxEinkDisplay::prepareEarlyRequest([[maybe_unused]] const EinkRefreshMode refreshMode,

M module-bsp/board/linux/eink/LinuxEinkDisplay.hpp => module-bsp/board/linux/eink/LinuxEinkDisplay.hpp +1 -4
@@ 15,10 15,7 @@ namespace hal::eink

      private:
        void setMode(const EinkDisplayColorMode mode) noexcept override;
        EinkStatus showImage(const std::vector<EinkFrame> &updateFrames,
                             const EinkFrame &refreshFrame,
                             const std::uint8_t *frameBuffer,
                             const EinkRefreshMode refreshMode) override;
        EinkStatus showImage(std::uint8_t *frameBuffer, const EinkRefreshMode refreshMode) override;
        void prepareEarlyRequest(const EinkRefreshMode refreshMode, const WaveformTemperature behaviour) override;

        void dither() override;

M module-bsp/board/rt1051/bsp/eink/ED028TC1.cpp => module-bsp/board/rt1051/bsp/eink/ED028TC1.cpp +19 -22
@@ 138,7 138,7 @@ static uint8_t s_einkMaskLut_2Bpp[16] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 
 *
 * @return
 */
static uint8_t *s_EinkTransformFrameCoordinateSystem_1Bpp(const uint8_t *dataIn,
static uint8_t *s_EinkTransformFrameCoordinateSystem_1Bpp(uint8_t *dataIn,
                                                          uint16_t windowWidthPx,
                                                          uint16_t windowHeightPx,
                                                          uint8_t *dataOut,


@@ 187,7 187,7 @@ static uint8_t *s_EinkTransformFrameCoordinateSystem_1Bpp(const uint8_t *dataIn,
 *
 * @return
 */
static uint8_t *s_EinkTransformFrameCoordinateSystem_2Bpp(const uint8_t *dataIn,
static uint8_t *s_EinkTransformFrameCoordinateSystem_2Bpp(uint8_t *dataIn,
                                                          uint16_t windowWidthPx,
                                                          uint16_t windowHeightPx,
                                                          uint8_t *dataOut,


@@ 236,7 236,7 @@ static uint8_t *s_EinkTransformFrameCoordinateSystem_2Bpp(const uint8_t *dataIn,
 *
 * @return
 */
static uint8_t *s_EinkTransformFrameCoordinateSystem_3Bpp(const uint8_t *dataIn,
static uint8_t *s_EinkTransformFrameCoordinateSystem_3Bpp(uint8_t *dataIn,
                                                          uint16_t windowWidthPx,
                                                          uint16_t windowHeightPx,
                                                          uint8_t *dataOut,


@@ 285,7 285,7 @@ static uint8_t *s_EinkTransformFrameCoordinateSystem_3Bpp(const uint8_t *dataIn,
 *
 * @return
 */
static uint8_t *s_EinkTransformFrameCoordinateSystem_4Bpp(const uint8_t *dataIn,
static uint8_t *s_EinkTransformFrameCoordinateSystem_4Bpp(uint8_t *dataIn,
                                                          uint16_t windowWidthPx,
                                                          uint16_t windowHeightPx,
                                                          uint8_t *dataOut,


@@ 334,7 334,7 @@ static uint8_t *s_EinkTransformFrameCoordinateSystem_4Bpp(const uint8_t *dataIn,
 *
 * @return
 */
static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_1Bpp(const uint8_t *dataIn,
static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_1Bpp(uint8_t *dataIn,
                                                                   uint16_t windowWidthPx,
                                                                   uint16_t windowHeightPx,
                                                                   uint8_t *dataOut,


@@ 383,7 383,7 @@ static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_1Bpp(const uint8_t
 *
 * @return
 */
static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_2Bpp(const uint8_t *dataIn,
static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_2Bpp(uint8_t *dataIn,
                                                                   uint16_t windowWidthPx,
                                                                   uint16_t windowHeightPx,
                                                                   uint8_t *dataOut,


@@ 432,7 432,7 @@ static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_2Bpp(const uint8_t
 *
 * @return
 */
static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_3Bpp(const uint8_t *dataIn,
static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_3Bpp(uint8_t *dataIn,
                                                                   uint16_t windowWidthPx,
                                                                   uint16_t windowHeightPx,
                                                                   uint8_t *dataOut,


@@ 487,7 487,7 @@ static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_3Bpp(const uint8_t
 * It is used when EINK_ROTATE_90_CLOCKWISE is not defined.
 */

static uint8_t *s_EinkTransformFrameCoordinateSystemNoRotation_4Bpp(const uint8_t *dataIn,
static uint8_t *s_EinkTransformFrameCoordinateSystemNoRotation_4Bpp(uint8_t *dataIn,
                                                                    uint16_t windowWidthPx,
                                                                    uint16_t windowHeightPx,
                                                                    uint8_t *dataOut,


@@ 799,10 799,7 @@ EinkStatus_e EinkDitherDisplay()
    return EinkOK;
}

EinkStatus_e EinkUpdateFrame(EinkFrame_t frame,
                             const uint8_t *buffer,
                             EinkBpp_e bpp,
                             EinkDisplayColorMode_e invertColors)
EinkStatus_e EinkUpdateFrame(EinkFrame_t frame, uint8_t *buffer, EinkBpp_e bpp, EinkDisplayColorMode_e invertColors)
{
    uint8_t buf[10];
    uint8_t pixelsInByte = 8 / bpp;


@@ 1007,7 1004,7 @@ EinkStatus_e EinkRefreshImage(EinkFrame_t frame, EinkDisplayTimingsMode_e refres
    return EinkOK;
}

__attribute__((optimize("O3"))) void EinkARGBToLuminance(const uint8_t *dataIn,
__attribute__((optimize("O3"))) void EinkARGBToLuminance(uint8_t *dataIn,
                                                         uint8_t *dataOut,
                                                         uint32_t displayWidth,
                                                         uint32_t displayHeight)


@@ 1040,7 1037,7 @@ __attribute__((optimize("O3"))) void EinkARGBToLuminance(const uint8_t *dataIn,
}

__attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSystem_1Bpp(
    const uint8_t *dataIn,
    uint8_t *dataIn,
    uint16_t windowWidthPx,
    uint16_t windowHeightPx,
    uint8_t *dataOut,


@@ 1087,7 1084,7 @@ __attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSy
}

__attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSystem_2Bpp(
    const uint8_t *dataIn,
    uint8_t *dataIn,
    uint16_t windowWidthPx,
    uint16_t windowHeightPx,
    uint8_t *dataOut,


@@ 1138,7 1135,7 @@ __attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSy
}

__attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSystem_3Bpp(
    const uint8_t *dataIn,
    uint8_t *dataIn,
    uint16_t windowWidthPx,
    uint16_t windowHeightPx,
    uint8_t *dataOut,


@@ 1149,7 1146,7 @@ __attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSy
}

__attribute__((optimize("O1"))) static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_1Bpp(
    const uint8_t *dataIn,
    uint8_t *dataIn,
    uint16_t windowWidthPx,
    uint16_t windowHeightPx,
    uint8_t *dataOut,


@@ 1196,7 1193,7 @@ __attribute__((optimize("O1"))) static uint8_t *s_EinkTransformAnimationFrameCoo
}

__attribute__((optimize("O1"))) static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_2Bpp(
    const uint8_t *dataIn,
    uint8_t *dataIn,
    uint16_t windowWidthPx,
    uint16_t windowHeightPx,
    uint8_t *dataOut,


@@ 1245,7 1242,7 @@ __attribute__((optimize("O1"))) static uint8_t *s_EinkTransformAnimationFrameCoo
}

__attribute__((optimize("O3"))) static uint8_t *s_EinkTransformAnimationFrameCoordinateSystem_3Bpp(
    const uint8_t *dataIn,
    uint8_t *dataIn,
    uint16_t windowWidthPx,
    uint16_t windowHeightPx,
    uint8_t *dataOut,


@@ 1256,7 1253,7 @@ __attribute__((optimize("O3"))) static uint8_t *s_EinkTransformAnimationFrameCoo
}

__attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSystem_4Bpp(
    const uint8_t *dataIn,
    uint8_t *dataIn,
    uint16_t windowWidthPx,
    uint16_t windowHeightPx,
    uint8_t *dataOut,


@@ 1303,7 1300,7 @@ __attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSy
}

__attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSystemNoRotation_4Bpp(
    const uint8_t *dataIn,
    uint8_t *dataIn,
    uint16_t windowWidthPx,
    uint16_t windowHeightPx,
    uint8_t *dataOut,


@@ 1318,7 1315,7 @@ __attribute__((optimize("O1"))) static uint8_t *s_EinkTransformFrameCoordinateSy
    int32_t inputRow   = 0;
    int32_t inputCol   = 0;

    for (inputRow = 0; inputRow < windowHeightPx; ++inputRow) {
    for (inputRow = 0; inputRow < windowHeightPx - 1; ++inputRow) {
        for (inputCol = windowWidthPx - 7; inputCol >= 0; inputCol -= pixelsInByte) {
            // HACK: Did not create the loop for accessing pixels and merging them in the single byte for better
            // performance.

M module-bsp/board/rt1051/bsp/eink/ED028TC1.h => module-bsp/board/rt1051/bsp/eink/ED028TC1.h +2 -2
@@ 382,7 382,7 @@ extern "C"
     *          EinkOK - Part of image send successfully
     */
    EinkStatus_e EinkUpdateFrame(EinkFrame_t frame,
                                 const uint8_t *buffer,
                                 uint8_t *buffer,
                                 EinkBpp_e bpp,
                                 EinkDisplayColorMode_e invertColors);



@@ 431,7 431,7 @@ extern "C"
     * @param displayWidth    [in] - display width in pixels
     * @param displayHeight   [in] - display height in pixels
     */
    void EinkARGBToLuminance(const uint8_t *dataIn, uint8_t *dataOut, uint32_t displayWidth, uint32_t displayHeight);
    void EinkARGBToLuminance(uint8_t *dataIn, uint8_t *dataOut, uint32_t displayWidth, uint32_t displayHeight);

#if defined(__cplusplus)
}

M module-bsp/board/rt1051/bsp/eink/EinkDisplay.cpp => module-bsp/board/rt1051/bsp/eink/EinkDisplay.cpp +16 -25
@@ 4,10 4,10 @@
#include "EinkDisplay.hpp"

#include <board/BoardDefinitions.hpp>

#include <log/log.hpp>

#include <gsl/util>

#include <stdexcept>
#include <cstdio>
#include <cstring>


@@ 132,15 132,15 @@ namespace hal::eink
        delete[] currentWaveform.LUTDData;
    }

    EinkStatus EinkDisplay::updateDisplay(EinkFrame frame, const std::uint8_t *frameBuffer)
    EinkStatus EinkDisplay::updateDisplay(std::uint8_t *frameBuffer, [[maybe_unused]] const EinkRefreshMode refreshMode)
    {
        return update(frame, frameBuffer);
        return update(frameBuffer);
    }

    EinkStatus EinkDisplay::refreshDisplay(EinkFrame frame, const EinkRefreshMode refreshMode)
    EinkStatus EinkDisplay::refreshDisplay(const EinkRefreshMode refreshMode)
    {
        const auto isDeepRefresh = refreshMode == EinkRefreshMode::REFRESH_DEEP;
        return refresh(frame, isDeepRefresh ? EinkDisplayTimingsDeepCleanMode : EinkDisplayTimingsFastRefreshMode);
        return refresh(isDeepRefresh ? EinkDisplayTimingsDeepCleanMode : EinkDisplayTimingsFastRefreshMode);
    }

    EinkStatus EinkDisplay::prepareDisplay(const EinkRefreshMode refreshMode, const WaveformTemperature behaviour)


@@ 160,27 160,20 @@ namespace hal::eink
        return setWaveform(EinkWaveforms_e::EinkWaveformDU2, temperature);
    }

    EinkStatus EinkDisplay::showImage(const std::vector<EinkFrame> &updateFrames,
                                      const EinkFrame &refreshFrame,
                                      const std::uint8_t *frameBuffer,
                                      const EinkRefreshMode refreshMode)
    EinkStatus EinkDisplay::showImage(std::uint8_t *frameBuffer, const EinkRefreshMode refreshMode)
    {
        if (const auto status = prepareDisplay(refreshMode, WaveformTemperature::KEEP_CURRENT);
            status != EinkStatus::EinkOK) {
            return status;
        }

        for (const EinkFrame &frame : updateFrames) {
            const std::uint8_t *buffer = frameBuffer + frame.pos_y * frame.size.width;
            if (const auto status = updateDisplay(frame, buffer); status != EinkStatus::EinkOK) {
                return status;
            }
        if (const auto status = updateDisplay(frameBuffer, refreshMode); status != EinkStatus::EinkOK) {
            return status;
        }

        if (const auto status = refreshDisplay(refreshFrame, refreshMode); status != EinkStatus::EinkOK) {
        if (const auto status = refreshDisplay(refreshMode); status != EinkStatus::EinkOK) {
            return status;
        }

        return EinkStatus::EinkOK;
    }



@@ 225,13 218,12 @@ namespace hal::eink
        EinkFillScreenWithColor(EinkDisplayColorFilling_e::EinkDisplayColorWhite);
    }

    EinkStatus EinkDisplay::update(EinkFrame frame, const std::uint8_t *displayBuffer)
    EinkStatus EinkDisplay::update(std::uint8_t *displayBuffer)
    {
        return translateStatus(
            EinkUpdateFrame(EinkFrame_t{frame.pos_x, frame.pos_y, frame.size.width, frame.size.height},
                            displayBuffer,
                            getCurrentBitsPerPixelFormat(),
                            translateDisplayColorMode(displayMode)));
        return translateStatus(EinkUpdateFrame(EinkFrame_t{0, 0, size.width, size.height},
                                               displayBuffer,
                                               getCurrentBitsPerPixelFormat(),
                                               translateDisplayColorMode(displayMode)));
    }

    EinkBpp_e EinkDisplay::getCurrentBitsPerPixelFormat() const noexcept


@@ 242,11 234,10 @@ namespace hal::eink
        return Eink4Bpp;
    }

    EinkStatus EinkDisplay::refresh(EinkFrame frame, const EinkDisplayTimingsMode_e refreshMode)
    EinkStatus EinkDisplay::refresh(const EinkDisplayTimingsMode_e refreshMode)
    {
        currentWaveform.useCounter += 1;
        return translateStatus(
            EinkRefreshImage(EinkFrame_t{frame.pos_x, frame.pos_y, frame.size.width, frame.size.height}, refreshMode));
        return translateStatus(EinkRefreshImage(EinkFrame_t{0, 0, size.width, size.height}, refreshMode));
    }

    bool EinkDisplay::isNewWaveformNeeded(const EinkWaveforms_e newMode, const std::int32_t newTemperature) const

M module-bsp/board/rt1051/bsp/eink/EinkDisplay.hpp => module-bsp/board/rt1051/bsp/eink/EinkDisplay.hpp +5 -8
@@ 26,10 26,7 @@ namespace hal::eink
        ~EinkDisplay() noexcept;

        void setMode(EinkDisplayColorMode mode) noexcept override;
        EinkStatus showImage(const std::vector<EinkFrame> &updateFrames,
                             const EinkFrame &refreshFrame,
                             const std::uint8_t *frameBuffer,
                             const EinkRefreshMode refreshMode) override;
        EinkStatus showImage(std::uint8_t *frameBuffer, const EinkRefreshMode refreshMode) override;
        void prepareEarlyRequest(EinkRefreshMode refreshMode, const WaveformTemperature behaviour) override;

        EinkStatus resetAndInit() override;


@@ 51,11 48,11 @@ namespace hal::eink

        std::int32_t getLastTemperature() const noexcept;

        EinkStatus update(EinkFrame frame, const std::uint8_t *displayBuffer);
        EinkStatus refresh(EinkFrame frame, const EinkDisplayTimingsMode_e refreshMode);
        EinkStatus update(std::uint8_t *displayBuffer);
        EinkStatus refresh(const EinkDisplayTimingsMode_e refreshMode);

        EinkStatus updateDisplay(EinkFrame frame, const std::uint8_t *frameBuffer);
        EinkStatus refreshDisplay(EinkFrame frame, const EinkRefreshMode refreshMode);
        EinkStatus updateDisplay(std::uint8_t *frameBuffer, const EinkRefreshMode refreshMode);
        EinkStatus refreshDisplay(const EinkRefreshMode refreshMode);
        EinkStatus prepareDisplay(const EinkRefreshMode refreshMode, const WaveformTemperature behaviour);

        FrameSize size;

M module-bsp/board/rt1051/bsp/eink/eink_dimensions.cpp => module-bsp/board/rt1051/bsp/eink/eink_dimensions.cpp +1 -2
@@ 22,8 22,7 @@ namespace hal::eink
#if defined(EINK_ROTATE_90_CLOCKWISE)
        return BOARD_EINK_DISPLAY_RES_X - frame.pos_x - frame.width;
#else
        // return BOARD_EINK_DISPLAY_RES_Y - frame.pos_y - frame.height;
        return frame.pos_y;
        return BOARD_EINK_DISPLAY_RES_Y - frame.pos_y - frame.height;
#endif
    }


M module-bsp/hal/include/hal/eink/AbstractEinkDisplay.hpp => module-bsp/hal/include/hal/eink/AbstractEinkDisplay.hpp +1 -5
@@ 2,7 2,6 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <memory>
#include <vector>

#include <devices/Device.hpp>



@@ 67,10 66,7 @@ namespace hal::eink
        virtual ~AbstractEinkDisplay() = default;

        virtual void setMode(const EinkDisplayColorMode mode) noexcept                                     = 0;
        virtual EinkStatus showImage(const std::vector<EinkFrame> &updateFrames,
                                     const EinkFrame &refreshFrame,
                                     const std::uint8_t *frameBuffer,
                                     const EinkRefreshMode refreshMode)                                    = 0;
        virtual EinkStatus showImage(std::uint8_t *frameBuffer, const EinkRefreshMode refreshMode)         = 0;
        virtual void prepareEarlyRequest(EinkRefreshMode refreshMode, const WaveformTemperature behaviour) = 0;

        virtual void dither()                                               = 0;

M module-gui/gui/core/BoundingBox.hpp => module-gui/gui/core/BoundingBox.hpp +3 -4
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 31,8 31,8 @@ namespace gui
            Position x = zero_position, y = zero_position;
            Length w = zero_size, h = zero_size;
        };
        BoundingBox() = default;
        BoundingBox(Position x, Position y, Length w, Length h);
        BoundingBox(Position x = zero_position, Position y = zero_position, Length w = 0, Length h = 0);
        virtual ~BoundingBox() = default;

        static bool intersect(const BoundingBox &box1, const BoundingBox &box2, BoundingBox &result);



@@ 44,7 44,6 @@ namespace gui
        Position pos(gui::Axis axis) const;
        std::string str() const;
        /// logical sum of bounding box by another bounding box values
        // NOTE: This function only increases the size, position is intact so this is not really a sum
        void sum(const BoundingBox &box);
        bool operator==(const BoundingBox &box) const;
        bool operator!=(const BoundingBox &box) const;

M module-gui/gui/core/Context.cpp => module-gui/gui/core/Context.cpp +43 -106
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*


@@ 8,28 8,35 @@
 *      Author: robert
 */

#include "Context.hpp"

#include <cassert>
#include <cstring>
#include <ios>
#include <cstring>

#include "BoundingBox.hpp"
#include "Context.hpp"

namespace gui
{
    Context::Context(std::uint16_t width, std::uint16_t height) : w{width}, h{height}, data(new std::uint8_t[w * h])

    Context::Context() : x{0}, y{0}, w{0}, h{0}, data{nullptr}
    {}

    Context::Context(uint16_t width, uint16_t height) : x{0}, y{0}, w{width}, h{height}, data(new uint8_t[w * h])
    {
        memset(data.get(), clearColor, w * h);
        memset(data, 15, w * h);
    }

    Context Context::get(std::int16_t gx, std::int16_t gy, std::uint16_t width, std::uint16_t height) const
    Context::~Context()
    {
        Context retContext = Context(width, height);
        if (data != nullptr)
            delete[] data;
    }

        // Copy the whole block if context is fully inside and covering the whole width
        if (gx == 0 && width == w && gy >= 0 && std::uint16_t(gy + height) <= h) {
            memcpy(retContext.data.get(), data.get() + gy * w, w * height);
            return retContext;
        }
    Context *Context::get(int16_t gx, int16_t gy, uint16_t width, uint16_t height)
    {
        Context *retContext = new Context(width, height);

        retContext->x = gx;
        retContext->y = gy;

        // create bounding boxes for the current context and return context
        BoundingBox currentBox = BoundingBox(0, 0, w, h);


@@ 41,7 48,7 @@ namespace gui
            Length sourceOffset = resultBox.y * w + resultBox.x;
            Length destOffset   = (resultBox.y - gy) * width + (resultBox.x - gx);
            for (Length h = 0; h < resultBox.h; h++) {
                memcpy(retContext.data.get() + destOffset, data.get() + sourceOffset, resultBox.w);
                memcpy(retContext->data + destOffset, data + sourceOffset, resultBox.w);
                sourceOffset += w;
                destOffset += width;
            }


@@ 51,38 58,27 @@ namespace gui
        return retContext;
    }

    void Context::insert(std::int16_t ix, std::int16_t iy, const Context &context)
    void Context::insert(int16_t ix, int16_t iy, Context *context)
    {
        // Copy the whole block if context is fully inside and covering the whole width
        if (ix == 0 && context.w == w && iy >= 0 && std::uint16_t(iy + context.h) <= h) {
            memcpy(data.get() + iy * w, context.data.get(), w * context.h);
            return;
        }

        // create bounding boxes for the current context and return context
        BoundingBox currentBox = BoundingBox(0, 0, w, h);
        BoundingBox insertBox  = BoundingBox(ix, iy, context.w, context.h);
        BoundingBox insertBox  = BoundingBox(ix, iy, context->w, context->h);
        BoundingBox resultBox;

        // if boxes overlap copy part defined by result from current context to the new context.
        if (BoundingBox::intersect(currentBox, insertBox, resultBox)) {
            Length sourceOffset = (resultBox.y - iy) * context.w + (resultBox.x - ix);
            Length sourceOffset = (resultBox.y - iy) * context->w + (resultBox.x - ix);
            Length destOffset   = (resultBox.y) * w + (resultBox.x);
            for (Length h = 0; h < resultBox.h; h++) {
                memcpy(data.get() + destOffset, context.data.get() + sourceOffset, resultBox.w);
                sourceOffset += context.w;
                memcpy(data + destOffset, context->data + sourceOffset, resultBox.w);
                sourceOffset += context->w;
                destOffset += w;
            }
        }
    }

    void Context::insertArea(std::int16_t ix,
                             std::int16_t iy,
                             std::int16_t iareaX,
                             std::int16_t iareaY,
                             std::int16_t iareaW,
                             std::int16_t iareaH,
                             const Context &context)
    void Context::insertArea(
        int16_t ix, int16_t iy, int16_t iareaX, int16_t iareaY, int16_t iareaW, int16_t iareaH, Context *context)
    {
        // create bounding boxes for the current context and return context
        BoundingBox currentBox = BoundingBox(0, 0, w, h);


@@ 91,98 87,39 @@ namespace gui

        // if boxes overlap copy part defined by result from current context to the new context.
        if (BoundingBox::intersect(currentBox, insertBox, resultBox)) {
            std::int16_t xBoxOffset = 0;
            std::int16_t yBoxOffset = 0;
            int16_t xBoxOffset = 0;
            int16_t yBoxOffset = 0;
            if (iareaX < 0)
                xBoxOffset = iareaX;
            if (iareaY < 0)
                yBoxOffset = iareaY;
            Length sourceOffset = (resultBox.y - iy - yBoxOffset) * context.w + (resultBox.x - ix - xBoxOffset);
            Length sourceOffset = (resultBox.y - iy - yBoxOffset) * context->w + (resultBox.x - ix - xBoxOffset);
            Length destOffset   = (resultBox.y) * w + (resultBox.x);
            for (Length h = 0; h < resultBox.h; h++) {
                memcpy(data.get() + destOffset, context.data.get() + sourceOffset, resultBox.w);
                sourceOffset += context.w;
                memcpy(data + destOffset, context->data + sourceOffset, resultBox.w);
                sourceOffset += context->w;
                destOffset += w;
            }
        }
    }

    struct LRange
    {
        LRange(std::uint16_t begin, std::uint16_t end) : begin(begin), end(end)
        {}
        void expand(LRange const &other)
        {
            begin = std::min(begin, other.begin);
            end   = std::max(end, other.end);
        }
        static LRange inversed(std::uint16_t end)
        {
            return {end, 0};
        }
        std::uint16_t begin, end;
    };

    inline BoundingBox makeBoundingBox(const LRange &rangeX, const LRange &rangeY)
    {
        return {rangeX.begin,
                rangeY.begin,
                std::uint16_t(rangeX.end - rangeX.begin),
                std::uint16_t(rangeY.end - rangeY.begin)};
    }

    // Currently the algorithm only works properly for width divisible by 8 due to the use of 64b integers.
    std::deque<BoundingBox> gui::Context::linesDiffs(const gui::Context &ctx1, const gui::Context &ctx2)
    {
        using casted_t        = std::uint64_t;
        const std::uint16_t w = ctx1.getW();
        const std::uint16_t h = ctx1.getH();
        assert(w == ctx2.getW() && h == ctx2.getH() && w % 8 == 0);
        const std::uint16_t cw = w / sizeof(casted_t);
        const auto data1       = reinterpret_cast<const casted_t *>(ctx1.getData());
        const auto data2       = reinterpret_cast<const casted_t *>(ctx2.getData());

        std::deque<BoundingBox> result;
        LRange rangeY = LRange::inversed(h);
        for (std::uint16_t y = 0; y < h; ++y) {
            const auto begin1 = data1 + y * cw;
            const auto end1   = begin1 + cw;
            const auto begin2 = data2 + y * cw;
            if (std::mismatch(begin1, end1, begin2).first != end1) {
                if (rangeY.begin == h) { // diff pixels found first time
                    rangeY.begin = y;
                }
            }
            else {
                if (rangeY.begin != h) { // diff pixels found before
                    rangeY.end = y;
                    result.push_back(makeBoundingBox({0, w}, rangeY));
                    rangeY = LRange::inversed(h);
                }
            }
        }
        if (rangeY.begin != h) { // diff pixels found before
            rangeY.end = h;
            result.push_back(makeBoundingBox({0, w}, rangeY));
        }
        return result;
    }

    void Context::fill(std::uint8_t colour)
    void Context::fill(uint8_t colour)
    {
        if (data) {
            memset(data.get(), colour, w * h);
            memset(data, colour, w * h);
        }
        //	uint32_t size = 480*600;
        //	memset( data, colour, size );
    }

    std::ostream &operator<<(std::ostream &out, const Context &c)
    {
        out << "w:" << c.w << "h:" << c.h << std::endl;
        out << "x:" << c.x << "y:" << c.y << "w:" << c.w << "h:" << c.h << std::endl;

        std::uint32_t offset = 0;
        for (std::uint32_t y = 0; y < c.h; y++) {
            for (std::uint32_t x = 0; x < c.w; x++) {
                std::uint32_t value = *(c.data.get() + offset);
        uint32_t offset = 0;
        for (uint32_t y = 0; y < c.h; y++) {
            for (uint32_t x = 0; x < c.w; x++) {
                uint32_t value = *(c.data + offset);
                std::cout << std::hex << value;
                offset++;
            }

M module-gui/gui/core/Context.hpp => module-gui/gui/core/Context.hpp +36 -50
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*


@@ 11,97 11,83 @@
#ifndef GUI_CORE_CONTEXT_HPP_
#define GUI_CORE_CONTEXT_HPP_

#include "BoundingBox.hpp"

#include "module-gui/gui/Common.hpp"

#include <cstdint>
#include <deque>
#include <iostream>
#include <memory>
#include "module-gui/gui/Common.hpp"

namespace gui
{

    class Context
    {
        static constexpr std::uint8_t clearColor = 15;
      protected:
        int16_t x, y;
        uint32_t w, h;
        uint8_t *data;

      public:
        Context() = default;
        Context(std::uint16_t width, std::uint16_t height);
        Context();
        Context(uint16_t width, uint16_t height);
        virtual ~Context();

        /**
         * @brief Creates new context using provided coordinates. If there is no common part Context filled with
         * clearColor is returned. The size of the returned context is defiend by parameters, so it may represent
         * area outside the original context.
         * @brief Creates new context using provided coordinates. If there is no common part Context with 0 width and 0
         * height is returned.
         */
        Context get(std::int16_t gx, std::int16_t gy, std::uint16_t width, std::uint16_t height) const;
        Context *get(int16_t gx, int16_t gy, uint16_t width, uint16_t height);
        /**
         * @brief Pastes provided context into current one. Overlapping content will be inserted into current context.
         */
        void insert(std::int16_t ix, std::int16_t iy, const Context &context);
        void insert(int16_t ix, int16_t iy, Context *context);
        /**
         * @brief Pastes provided context into current one. Overlapping content will be inserted into current context.
         */
        void insertArea(std::int16_t ix,
                        std::int16_t iy,
                        std::int16_t iareaX,
                        std::int16_t iareaY,
                        std::int16_t iareaW,
                        std::int16_t iareaH,
                        const Context &context);

        /**
         * @brief Calculate regions of difference between contexts. Each bounding box covers the whole width of the
         * context. They are disjoint and sorted by y coordinate. The contexts has to have the same sizes.
         */
        static std::deque<BoundingBox> linesDiffs(const gui::Context &ctx1, const gui::Context &ctx2);

        void insertArea(
            int16_t ix, int16_t iy, int16_t iareaX, int16_t iareaY, int16_t iareaW, int16_t iareaH, Context *context);
        /**
         * @brief Fills whole context with specified colour;
         */
        void fill(std::uint8_t colour);
        void fill(uint8_t colour);
        /**
         * @brief returns pointer to context's data;
         */
        inline const std::uint8_t *getData() const
        inline const uint8_t *getData() const
        {
            return data.get();
            return data;
        }
        inline std::uint8_t *getData()
        inline uint8_t *getData()
        {
            return data.get();
            return data;
        }
        inline std::uint16_t getW() const
        inline int16_t getX() const
        {
            return w;
            return x;
        }
        inline std::uint16_t getH() const
        inline int16_t getY() const
        {
            return h;
            return y;
        }
        inline uint16_t getW() const
        {
            return w;
        }
        inline BoundingBox getBoundingBox() const
        inline uint16_t getH() const
        {
            return {0, 0, w, h};
            return h;
        }

        inline std::uint8_t getPixel(const Point point, uint8_t defaultColor = clearColor) const
        inline bool addressInData(const uint8_t *ptr) const
        {
            return hasPixel(point) ? data[point.y * w + point.x] : defaultColor;
            return (ptr >= data) && (ptr < data + w * h);
        }

        inline bool hasPixel(const Point point) const noexcept
        inline bool addressInData(const Point point) const noexcept
        {
            return (point.x >= 0 && static_cast<std::uint32_t>(point.x) < w) &&
                   (point.y >= 0 && static_cast<std::uint32_t>(point.y) < h);
            return (point.x >= 0 && static_cast<uint32_t>(point.x) <= w) &&
                   (point.y >= 0 && static_cast<uint32_t>(point.y) <= h);
        }

        friend std::ostream &operator<<(std::ostream &out, const Context &c);

      private:
        std::uint32_t w = 0;
        std::uint32_t h = 0;
        std::unique_ptr<uint8_t[]> data;
    };

} /* namespace gui */

M module-gui/gui/core/DrawCommand.cpp => module-gui/gui/core/DrawCommand.cpp +18 -15
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "DrawCommand.hpp"


@@ 56,9 56,8 @@ namespace gui
            return;
        }

        Context tempContext;
        Context *drawingContext = ctx;
        Point position(0, 0);
        Point position;

        if (yaps & (RectangleYap::BottomLeft | RectangleYap::TopLeft)) {
            position.x += yapSize;


@@ 77,8 76,7 @@ namespace gui
        else {
            const auto xCtx = areaX < 0 ? origin.x + areaX : origin.x;
            const auto yCtx = areaY < 0 ? origin.y + areaY : origin.y;
            tempContext     = ctx->get(xCtx, yCtx, areaW, areaH);
            drawingContext  = &tempContext;
            drawingContext  = ctx->get(xCtx, yCtx, areaW, areaH);
        }

        if (radius == 0) {


@@ 91,7 89,8 @@ namespace gui
        }

        if (drawingContext != ctx) {
            ctx->insertArea(origin.x, origin.y, areaX, areaY, width, height, tempContext);
            ctx->insertArea(origin.x, origin.y, areaX, areaY, width, height, drawingContext);
            delete drawingContext;
        }
    }



@@ 117,7 116,7 @@ namespace gui

        for (position.y = glyphOrigin.y - glyph->yoffset; position.y < glyphMaxY; ++position.y) {
            for (position.x = glyphOrigin.x; position.x < glyphMaxX; ++position.x) {
                if (!ctx->hasPixel(position)) {
                if (!ctx->addressInData(position)) {
                    log_warn_glyph(
                        "drawing out of: {x=%d,y=%d} vs {w=%d,h=%d}", position.x, position.y, ctx->getW(), ctx->getH());
                    return;


@@ 139,17 138,18 @@ namespace gui
        }

        // get copy of original context using x,y of draw coordinates and original size of the widget
        Context ctxCopy;
        Context *drawCtx;
        bool copyContext   = false;
        Point widgetOrigin = {0, 0};

        // check if there is a need of making copy of context to use it as background
        if ((areaW == width) && (areaH == height)) {
            drawCtx      = ctx;
            widgetOrigin = origin;
        }
        else {
            copyContext = true;
            ctxCopy     = ctx->get(origin.x, origin.y, areaW, areaH);
            drawCtx     = ctx->get(origin.x, origin.y, areaW, areaH);
        }

        // retrieve font used to draw text


@@ 164,7 164,6 @@ namespace gui
            FontGlyph *glyph = font->getGlyph(idCurrent);

            // do not start drawing outside of draw context.
            Context *drawCtx = copyContext ? &ctxCopy : ctx;
            if ((widgetOrigin.x + position.x + glyph->xoffset >= drawCtx->getW()) ||
                (widgetOrigin.x + position.x + glyph->xoffset < 0)) {
                LOG_FATAL("Drawing outside context's X boundary for glyph: %" PRIu32, glyph->id);


@@ 189,9 188,10 @@ namespace gui
        }

        // if drawing was performed in temporary context
        // reinsert drawCtx into base context
        // reinsert drawCtx into bast context
        if (copyContext) {
            ctx->insert(origin.x, origin.y, ctxCopy);
            ctx->insert(origin.x, origin.y, drawCtx);
            delete drawCtx;
        }
    }



@@ 269,20 269,23 @@ namespace gui
        }

        // get copy of original context using x,y of draw coordinates and original size of the widget
        Context drawCtx = ctx->get(origin.x, origin.y, areaW, areaH);
        Context *drawCtx = ctx->get(origin.x, origin.y, areaW, areaH);

        if (imageMap->getType() == gui::ImageMap::Type::PIXMAP) {
            auto pixMap = dynamic_cast<PixMap *>(imageMap);
            assert(pixMap);
            drawPixMap(&drawCtx, pixMap);
            drawPixMap(drawCtx, pixMap);
        }
        else if (imageMap->getType() == gui::ImageMap::Type::VECMAP) {
            auto vecMap = dynamic_cast<VecMap *>(imageMap);
            assert(vecMap);
            drawVecMap(&drawCtx, vecMap);
            drawVecMap(drawCtx, vecMap);
        }

        // reinsert drawCtx into bast context
        ctx->insert(origin.x, origin.y, drawCtx);

        // remove draw context
        delete drawCtx;
    }
} /* namespace gui */

M module-gui/gui/core/ImageManager.cpp => module-gui/gui/core/ImageManager.cpp +26 -22
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ImageManager.hpp"


@@ 176,27 176,31 @@ namespace gui
        // Creation of square with crossed lines as fallback image
        constexpr auto squareWidth = 15;

        DrawRectangle rectangle;
        rectangle.origin = {0, 0};
        rectangle.width  = squareWidth;
        rectangle.height = squareWidth;
        rectangle.areaX  = 0;
        rectangle.areaY  = 0;
        rectangle.areaW  = squareWidth;
        rectangle.areaH  = squareWidth;

        DrawLine line1;
        line1.start = {0, 0};
        line1.end   = {squareWidth, squareWidth};

        DrawLine line2;
        line2.start = {squareWidth - 1, 0};
        line2.end   = {0, squareWidth - 1};

        Context renderContext(squareWidth, squareWidth);
        Renderer().render(renderContext, rectangle, line1, line2);

        return new PixMap(squareWidth, squareWidth, renderContext.getData());
        std::list<std::unique_ptr<gui::DrawCommand>> commands;
        auto rectangle    = std::make_unique<DrawRectangle>();
        rectangle->origin = {0, 0};
        rectangle->width  = squareWidth;
        rectangle->height = squareWidth;
        rectangle->areaX  = 0;
        rectangle->areaY  = 0;
        rectangle->areaW  = squareWidth;
        rectangle->areaH  = squareWidth;
        commands.emplace_back(std::move(rectangle));

        auto line1   = std::make_unique<DrawLine>();
        line1->start = {0, 0};
        line1->end   = {squareWidth, squareWidth};
        commands.emplace_back(std::move(line1));

        auto line2   = std::make_unique<DrawLine>();
        line2->start = {squareWidth - 1, 0};
        line2->end   = {0, squareWidth - 1};
        commands.emplace_back(std::move(line2));

        auto renderContext = std::make_unique<Context>(squareWidth, squareWidth);
        Renderer().render(renderContext.get(), commands);

        return new PixMap(squareWidth, squareWidth, renderContext->getData());
    }

    auto ImageManager::getImageMapList(std::string ext1, std::string ext2)

M module-gui/gui/core/RawFont.cpp => module-gui/gui/core/RawFont.cpp +15 -13
@@ 239,22 239,24 @@ namespace gui
        unsupported->xadvance =
            unsupported->width + (2 * unsupported->xoffset); // use xoffset as margins on the left/right of the glyph
        // populate with a bitmap (glyph)
        DrawRectangle commandRect;
        commandRect.origin   = {0, 0};
        commandRect.width    = unsupported->width;
        commandRect.height   = unsupported->height;
        commandRect.areaX    = 0;
        commandRect.areaY    = 0;
        commandRect.areaW    = unsupported->width;
        commandRect.areaH    = unsupported->height;
        commandRect.penWidth = unsupported->xoffset;

        Context renderCtx(unsupported->width, unsupported->height);
        Renderer().render(renderCtx, commandRect);
        auto commandRect      = std::make_unique<DrawRectangle>();
        commandRect->origin   = {0, 0};
        commandRect->width    = unsupported->width;
        commandRect->height   = unsupported->height;
        commandRect->areaX    = 0;
        commandRect->areaY    = 0;
        commandRect->areaW    = unsupported->width;
        commandRect->areaH    = unsupported->height;
        commandRect->penWidth = unsupported->xoffset;

        auto renderCtx = std::make_unique<Context>(unsupported->width, unsupported->height);
        std::list<std::unique_ptr<gui::DrawCommand>> commands;
        commands.emplace_back(std::move(commandRect));
        Renderer().render(renderCtx.get(), commands);

        auto size         = unsupported->width * unsupported->height;
        unsupported->data = new uint8_t[size];
        std::memcpy(unsupported->data, renderCtx.getData(), size);
        std::memcpy(unsupported->data, renderCtx->getData(), size);
    }

    void RawFont::setFallbackFont(RawFont *fallback)

M module-gui/gui/core/Renderer.cpp => module-gui/gui/core/Renderer.cpp +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

// for memset


@@ 10,12 10,12 @@

namespace gui
{
    void Renderer::changeColorScheme(const std::unique_ptr<ColorScheme> &scheme) const
    void Renderer::changeColorScheme(const std::unique_ptr<ColorScheme> &scheme)
    {
        renderer::PixelRenderer::updateColorScheme(scheme);
    }

    void Renderer::render(Context *ctx, const std::list<std::unique_ptr<DrawCommand>> &commands) const
    void Renderer::render(Context *ctx, std::list<std::unique_ptr<DrawCommand>> &commands)
    {
        if (ctx == nullptr) {
            return;

M module-gui/gui/core/Renderer.hpp => module-gui/gui/core/Renderer.hpp +5 -9
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 34,14 34,10 @@ namespace gui
         */

      public:
        void changeColorScheme(const std::unique_ptr<ColorScheme> &scheme) const;
        void render(Context *ctx, const std::list<std::unique_ptr<DrawCommand>> &commands) const;

        template <typename... Commands>
        void render(Context &ctx, const Commands &...commands) const
        {
            (..., commands.draw(&ctx));
        }
        virtual ~Renderer() = default;

        void render(Context *ctx, std::list<std::unique_ptr<DrawCommand>> &commands);
        void changeColorScheme(const std::unique_ptr<ColorScheme> &scheme);
    };

} /* namespace gui */

M module-gui/gui/core/renderers/RectangleRenderer.cpp => module-gui/gui/core/renderers/RectangleRenderer.cpp +15 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "RectangleRenderer.hpp"


@@ 13,6 13,19 @@

namespace gui::renderer
{
    namespace
    {
        std::uint8_t getPixelColor(Context *ctx, const Point &point, std::uint8_t defaultColor)
        {
            const auto contextWidth = ctx->getW();
            const auto position     = point.y * contextWidth + point.x;
            if (!ctx->addressInData(ctx->getData() + position)) {
                return defaultColor;
            }
            return *(ctx->getData() + position);
        }
    } // namespace

    auto RectangleRenderer::DrawableStyle::from(const DrawRectangle &command) -> DrawableStyle
    {
        return DrawableStyle{command.penWidth,


@@ 170,7 183,7 @@ namespace gui::renderer
        while (!q.empty()) {
            const auto currPoint = q.front();
            q.pop();
            if (const auto color = ctx->getPixel(currPoint, PixelRenderer::getColor(borderColor.intensity));
            if (const auto color = getPixelColor(ctx, currPoint, PixelRenderer::getColor(borderColor.intensity));
                color == PixelRenderer::getColor(borderColor.intensity) ||
                color == PixelRenderer::getColor(fillColor.intensity)) {
                continue;

M module-gui/gui/widgets/Rect.cpp => module-gui/gui/widgets/Rect.cpp +1 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*

M module-gui/test/test-catch/test-context.cpp => module-gui/test/test-catch/test-context.cpp +51 -37
@@ 14,63 14,77 @@

TEST_CASE("test context size and position")
{
    auto ctx = gui::Context(30, 30);
    ctx.fill(0);
    auto ctx = new gui::Context(30, 30);
    ctx->fill(0);

    gui::Context test = ctx.get(17, 17, 30, 30);
    gui::Context *test = ctx->get(17, 17, 30, 30);

    test = ctx.get(-17, -17, 30, 30);
    delete test;
    test = ctx->get(-17, -17, 30, 30);
    delete test;

    delete ctx;
}

TEST_CASE("insertContextTest")
{

    gui::Context dstCtx;
    gui::Context insCtx;
    gui::Context *dstCtx;
    gui::Context *insCtx;

    SECTION("RECTANGLE INSIDE")
    {
        dstCtx = gui::Context(30, 30);
        dstCtx.fill(0);
        insCtx = gui::Context(28, 28);
        insCtx.fill(15);
        dstCtx.insert(1, 1, insCtx);
        dstCtx = new gui::Context(30, 30);
        dstCtx->fill(0);
        insCtx = new gui::Context(static_cast<unsigned short>(28), static_cast<unsigned short>(28));
        insCtx->fill(15);
        dstCtx->insert(1, 1, insCtx);
        delete dstCtx;
        delete insCtx;
    }

    SECTION("2 COLUMNS ON RIGHT SIDE, TOP AND BOTTOM ROW UNTOUCHED")
    {
        dstCtx = gui::Context(30, 30);
        dstCtx.fill(0);
        insCtx = gui::Context(28, 28);
        insCtx.fill(15);
        dstCtx.insert(28, 1, insCtx);
        dstCtx = new gui::Context(static_cast<unsigned short>(30), static_cast<unsigned short>(30));
        dstCtx->fill(0);
        insCtx = new gui::Context(static_cast<unsigned short>(28), static_cast<unsigned short>(28));
        insCtx->fill(15);
        dstCtx->insert(28, 1, insCtx);
        delete dstCtx;
        delete insCtx;
    }

    SECTION("2 COLUMNS ON LEFT SIDE, TOP AND BOTTOM ROW UNTOUCHED")
    {
        dstCtx = gui::Context(30, 30);
        dstCtx.fill(0);
        insCtx = gui::Context(28, 28);
        insCtx.fill(15);
        dstCtx.insert(-26, 1, insCtx);
        dstCtx = new gui::Context(static_cast<unsigned short>(30), static_cast<unsigned short>(30));
        dstCtx->fill(0);
        insCtx = new gui::Context(static_cast<unsigned short>(28), static_cast<unsigned short>(28));
        insCtx->fill(15);
        dstCtx->insert(-26, 1, insCtx);
        delete dstCtx;
        delete insCtx;
    }

    SECTION("2 COLUMNS ON RIGHT SIDE")
    {
        dstCtx = gui::Context(30, 30);
        dstCtx.fill(0);
        insCtx = gui::Context(32, 32);
        insCtx.fill(15);
        dstCtx.insert(28, -1, insCtx);
        dstCtx = new gui::Context(static_cast<unsigned short>(30), static_cast<unsigned short>(30));
        dstCtx->fill(0);
        insCtx = new gui::Context(static_cast<unsigned short>(32), static_cast<unsigned short>(32));
        insCtx->fill(15);
        dstCtx->insert(28, -1, insCtx);
        delete dstCtx;
        delete insCtx;
    }

    SECTION("2 COLUMNS ON LEFT SIDE")
    {
        dstCtx = gui::Context(30, 30);
        dstCtx.fill(0);
        insCtx = gui::Context(32, 32);
        insCtx.fill(15);
        dstCtx.insert(-30, -1, insCtx);
        dstCtx = new gui::Context(static_cast<unsigned short>(30), static_cast<unsigned short>(30));
        dstCtx->fill(0);
        insCtx = new gui::Context(static_cast<unsigned short>(32), static_cast<unsigned short>(32));
        insCtx->fill(15);
        dstCtx->insert(-30, -1, insCtx);
        delete dstCtx;
        delete insCtx;
    }
}



@@ 110,7 124,7 @@ TEST_CASE("Draw vector image test")
                                                   "ffffffffffffffffffffffffffffffff\n"
                                                   "ffffffffffffffffffffffffffffffff\n";

    auto context = gui::Context(32, 32);
    auto context = std::make_unique<gui::Context>(static_cast<unsigned short>(32), static_cast<unsigned short>(32));

    gui::ImageManager::getInstance().init(".");
    auto id                 = gui::ImageManager::getInstance().getImageMapID("plus_32px_W_M");


@@ 124,13 138,13 @@ TEST_CASE("Draw vector image test")
    drawCommand.imageID = id;
    drawCommand.areaH   = 32;
    drawCommand.areaW   = 32;
    drawCommand.draw(&context);
    drawCommand.draw(context.get());
    std::string dump;

    std::uint32_t offset = 0;
    for (std::uint32_t y = 0; y < context.getH(); y++) {
        for (std::uint32_t x = 0; x < context.getW(); x++) {
            std::uint32_t value = *(context.getData() + offset);
    uint32_t offset = 0;
    for (uint32_t y = 0; y < context->getH(); y++) {
        for (uint32_t x = 0; x < context->getW(); x++) {
            uint32_t value = *(context->getData() + offset);
            std::stringstream stream;
            stream << std::hex << value;
            dump.append(stream.str());

M module-services/service-eink/ServiceEink.cpp => module-services/service-eink/ServiceEink.cpp +4 -130
@@ 175,78 175,7 @@ namespace service::eink
            display->setMode(hal::eink::EinkDisplayColorMode::EinkDisplayColorModeStandard);
        }
        internal::StaticData::get().setInvertedMode(invertedModeRequested);

        previousContext.reset();
        previousRefreshMode = hal::eink::EinkRefreshMode::REFRESH_NONE;
    }

    // Merge boxes if the gap between them is lesser than the threshold
    template <typename BoxesContainer>
    inline std::vector<gui::BoundingBox> mergeBoundingBoxes(const BoxesContainer &boxes, std::uint16_t gapThreshold)
    {
        std::vector<gui::BoundingBox> mergedBoxes;
        if (boxes.empty())
            return mergedBoxes;
        mergedBoxes.reserve(boxes.size());
        gui::BoundingBox merged = boxes.front();
        for (std::size_t i = 1; i < boxes.size(); ++i) {
            const auto &bb = boxes[i];
            const auto gap = bb.y - (merged.y + merged.h);
            if (gap < gapThreshold) {
                merged.h = (bb.y + bb.h) - merged.y;
            }
            else {
                mergedBoxes.push_back(merged);
                merged = bb;
            }
        }
        mergedBoxes.push_back(merged);
        return mergedBoxes;
    }

    // Enlarge each box to match alignment-wide grid in y coordinate
    template <typename BoxesContainer>
    inline std::vector<hal::eink::EinkFrame> makeAlignedFrames(const BoxesContainer &boxes, std::uint16_t alignment)
    {
        std::vector<hal::eink::EinkFrame> frames;
        if (boxes.empty())
            return frames;
        frames.reserve(boxes.size());
        auto a = alignment;
        for (const auto &bb : boxes) {
            auto f = hal::eink::EinkFrame{
                std::uint16_t(bb.x), std::uint16_t(bb.y), {std::uint16_t(bb.w), std::uint16_t(bb.h)}};
            auto y        = f.pos_y;
            auto h        = f.size.height;
            f.pos_y       = y / a * a;
            f.size.height = (y - f.pos_y + h + (a - 1)) / a * a;
            frames.push_back(f);
        }
        return frames;
    }

#if DEBUG_EINK_REFRESH == 1
    inline std::string debug_toString(const gui::BoundingBox &bb)
    {
        return bb.str();
    }

    inline std::string debug_toString(const hal::eink::EinkFrame &f)
    {
        std::stringstream ss;
        ss << '{' << f.pos_x << ' ' << f.pos_y << ' ' << f.size.width << ' ' << f.size.height << "}";
        return ss.str();
    }

    template <typename Container>
    inline void debug_handleImageMessage(const char *name, const Container &container)
    {
        std::stringstream ss;
        for (auto const &el : container)
            ss << debug_toString(el) << ' ';
        LOG_INFO("%s: %s", name, ss.str().c_str());
    }
#endif

    sys::MessagePointer ServiceEink::handleImageMessage(sys::Message *request)
    {


@@ 260,67 189,12 @@ namespace service::eink
        displayPowerOffTimer.stop();
        auto displayPowerOffTimerReload = gsl::finally([this]() { displayPowerOffTimer.start(); });

        const gui::Context &ctx = *message->getContext();
        auto refreshMode        = translateToEinkRefreshMode(message->getRefreshMode());

        // Get areas changed since last update
        std::vector<hal::eink::EinkFrame> updateFrames;
        if (!previousContext) {
            updateFrames = {{0, 0, {ctx.getW(), ctx.getH()}}};

            previousContext.reset(new gui::Context(ctx.get(0, 0, ctx.getW(), ctx.getH())));
        }
        else if (refreshMode > previousRefreshMode) {
            updateFrames = {{0, 0, {ctx.getW(), ctx.getH()}}};

            previousContext->insert(0, 0, ctx);
        }
        else {
            // Each bounding box cover the whole width of the context. They are disjoint and sorted by y coordinate.
            const auto diffBoundingBoxes = gui::Context::linesDiffs(ctx, *previousContext);
            if (!diffBoundingBoxes.empty()) {
                const std::uint16_t gapThreshold = ctx.getH() / 4;
                const std::uint16_t alignment    = 8;

                const auto mergedBoxes = mergeBoundingBoxes(diffBoundingBoxes, gapThreshold);
                updateFrames           = makeAlignedFrames(mergedBoxes, alignment);

#if DEBUG_EINK_REFRESH == 1
                debug_handleImageMessage("Boxes", diffBoundingBoxes);
                debug_handleImageMessage("Merged boxes", mergedBoxes);
#endif

                previousContext->insert(0, 0, ctx);
            }
        }

        if (!updateFrames.empty()) {
            hal::eink::EinkFrame refreshFrame;
            refreshFrame = updateFrames.front();
            refreshFrame.size.height =
                updateFrames.back().pos_y + updateFrames.back().size.height - updateFrames.front().pos_y;

            eInkSentinel->HoldMinimumFrequency();

            const auto status = display->showImage(updateFrames, refreshFrame, ctx.getData(), refreshMode);
            if (status != hal::eink::EinkStatus::EinkOK) {
                previousContext.reset();
                refreshMode = hal::eink::EinkRefreshMode::REFRESH_NONE;
                LOG_ERROR("Error during drawing image on eink: %s", magic_enum::enum_name(status).data());
            }

#if DEBUG_EINK_REFRESH == 1
            debug_handleImageMessage("Frames", updateFrames);
            debug_handleImageMessage("Refresh frame", std::vector<hal::eink::EinkFrame>({refreshFrame}));
#endif
        eInkSentinel->HoldMinimumFrequency();
        auto status = display->showImage(message->getData(), translateToEinkRefreshMode(message->getRefreshMode()));
        if (status != hal::eink::EinkStatus::EinkOK) {
            LOG_ERROR("Error during drawing image on eink: %s", magic_enum::enum_name(status).data());
        }

#if DEBUG_EINK_REFRESH == 1
        LOG_INFO("Refresh context: %d %d, mode: %d", (int)ctx.getW(), (int)ctx.getH(), (int)refreshMode);
#endif

        previousRefreshMode = refreshMode;

        return std::make_shared<service::eink::ImageDisplayedNotification>(message->getContextId());
    }


M module-services/service-eink/ServiceEink.hpp => module-services/service-eink/ServiceEink.hpp +0 -3
@@ 3,7 3,6 @@

#pragma once

#include <module-gui/gui/core/Context.hpp>
#include <system/Common.hpp>
#include <Service/Message.hpp>
#include <Service/Service.hpp>


@@ 69,8 68,6 @@ namespace service::eink
        sys::TimerHandle displayPowerOffTimer;
        std::shared_ptr<EinkSentinel> eInkSentinel;
        std::unique_ptr<settings::Settings> settings;
        std::unique_ptr<::gui::Context> previousContext;
        hal::eink::EinkRefreshMode previousRefreshMode = hal::eink::EinkRefreshMode::REFRESH_NONE;
    };
} // namespace service::eink


M module-services/service-eink/messages/ImageMessage.cpp => module-services/service-eink/messages/ImageMessage.cpp +2 -2
@@ 10,9 10,9 @@ namespace service::eink
        : contextId{contextId}, context{context}, refreshMode{refreshMode}
    {}

    auto ImageMessage::getContext() noexcept -> ::gui::Context *
    auto ImageMessage::getData() noexcept -> std::uint8_t *
    {
        return context;
        return context->getData();
    }

    auto ImageMessage::getRefreshMode() const noexcept -> ::gui::RefreshModes

M module-services/service-eink/messages/ImageMessage.hpp => module-services/service-eink/messages/ImageMessage.hpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 17,7 17,7 @@ namespace service::eink
        ImageMessage(int contextId, ::gui::Context *context, ::gui::RefreshModes refreshMode);

        [[nodiscard]] auto getContextId() const noexcept -> int;
        [[nodiscard]] auto getContext() noexcept -> ::gui::Context *;
        [[nodiscard]] auto getData() noexcept -> std::uint8_t *;
        [[nodiscard]] auto getRefreshMode() const noexcept -> ::gui::RefreshModes;

      private:

M module-utils/log/api/log/debug.hpp => module-utils/log/api/log/debug.hpp +6 -7
@@ 4,20 4,19 @@
#pragma once

#define DEBUG_APPLICATION_MANAGEMENT 0 /// show verbose logs in ApplicationManager
#define DEBUG_SCOPED_TIMINGS         0 /// show timings in measured functions
#define DEBUG_CELLULAR_UART          0 /// show full modem uart communication
#define DEBUG_BLUETOOTH_HCI_COMS     0 /// show communication with BT module - transactions
#define DEBUG_BLUETOOTH_HCI_BYTES    0 /// show communication with BT module - all the HCI bytes
#define DEBUG_CELLULAR_UART          0 /// show full modem uart communication
#define DEBUG_SERVICE_MESSAGES       0 /// show messages prior to handling in service
#define DEBUG_DB_MODEL_DATA          0 /// show messages prior to handling in service
#define DEBUG_EINK_REFRESH           0 /// show bounding boxes and frames in partial refresh
#define DEBUG_SIM_IMPORT_DATA        0 /// show messages connected to sim data imports
#define DEBUG_FONT                   0 /// show Font debug messages
#define DEBUG_GUI_TEXT               0 /// show basic debug messages for gui::Text - warning this can be hard on cpu
#define DEBUG_GUI_TEXT_LINES         0 /// show extended debug messages for gui::Text - lines building
#define DEBUG_GUI_TEXT_CURSOR        0 /// show extended debug messages for gui::Text - cursor handling
#define DEBUG_INPUT_EVENTS           0 /// show input events prints in system
#define DEBUG_MISSING_ASSETS         0 /// show debug concerning missing assets
#define DEBUG_SCOPED_TIMINGS         0 /// show timings in measured functions
#define DEBUG_TIMER                  0 /// debug timers system utility
#define DEBUG_SETTINGS_DB            0 /// show extensive settings logs for all applications
#define DEBUG_SERVICE_CELLULAR       0 /// show various logs in cellular service
#define DEBUG_SERVICE_MESSAGES       0 /// show messages prior to handling in service
#define DEBUG_SIM_IMPORT_DATA        0 /// show messages connected to sim data imports
#define DEBUG_TIMER                  0 /// debug timers system utility
#define DEBUG_MISSING_ASSETS         0 /// show debug concerning missing assets

M pure_changelog.md => pure_changelog.md +0 -1
@@ 6,7 6,6 @@
* Added Polish translation to a calendar title
* Changed USB logging
* Separated system volume from Bluetooth device volume for A2DP
* Implemented partial refresh of the display

### Fixed
* Fixed issue with inability to send SMS