// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md /* * Context.cpp * * Created on: 6 maj 2019 * Author: robert */ #include "Context.hpp" #include #include #include #include namespace gui { Context::Context(std::uint16_t width, std::uint16_t height) : w{width}, h{height}, data(new std::uint8_t[w * h]) { memset(data.get(), clearColor, w * h); } Context Context::get(std::int16_t gx, std::int16_t gy, std::uint16_t width, std::uint16_t height) const { Context retContext = Context(width, height); // 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; } // create bounding boxes for the current context and return context BoundingBox currentBox = BoundingBox(0, 0, w, h); BoundingBox newBox = BoundingBox(gx, gy, width, height); BoundingBox resultBox; // if boxes overlap copy part defined by result from current context to the new context. if (BoundingBox::intersect(currentBox, newBox, resultBox)) { 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); sourceOffset += w; destOffset += width; } } // else just return context filled with white colour. return retContext; } void Context::insert(std::int16_t ix, std::int16_t iy, const 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 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 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; 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) { // create bounding boxes for the current context and return context BoundingBox currentBox = BoundingBox(0, 0, w, h); BoundingBox insertBox = BoundingBox(ix, iy, iareaW, iareaH); BoundingBox resultBox; // 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; if (iareaX < 0) xBoxOffset = iareaX; if (iareaY < 0) yBoxOffset = iareaY; 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; 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 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(ctx1.getData()); const auto data2 = reinterpret_cast(ctx2.getData()); std::deque 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) { if (data) { memset(data.get(), colour, w * h); } } std::ostream &operator<<(std::ostream &out, const Context &c) { out << "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); std::cout << std::hex << value; offset++; } std::cout << std::endl; } return out; } std::string Context::toAsciiScaled(std::uint16_t scale) const { scale = std::max(std::uint16_t(1), std::min(scale, std::min(w, h))); const std::uint16_t pixelsPerChar = scale * scale; const std::uint16_t sw = w / scale; const std::uint16_t sh = h / scale; const std::uint8_t white = 15; const char *chars = " .,-\"*^:;+=!?%#@"; std::vector accum(sw * sh, 0); for (std::uint16_t j = 0; j < h; ++j) { for (std::uint16_t i = 0; i < w; ++i) { const std::uint8_t c = std::min(data[j * w + i], white); const auto off = (j / scale) * sw + (i / scale); accum[off] += c; } } const std::uint16_t sw_nl = sw + 1; std::string result(sw_nl * sh - 1, '\n'); // last new line is not needed for (std::uint16_t j = 0; j < sh; ++j) { for (std::uint16_t i = 0; i < sw; ++i) { const auto off_nl = j * sw_nl + i; const auto off = j * sw + i; result[off_nl] = chars[accum[off] / pixelsPerChar]; } } return result; } } /* namespace gui */