mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-01-24 05:47:58 -05:00
This commit fixes full white color not being changed by Color Test window in case of clear command and vector images.
292 lines
9.9 KiB
C++
292 lines
9.9 KiB
C++
// 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"
|
|
#include "Common.hpp"
|
|
|
|
// gui components
|
|
#include "Color.hpp"
|
|
#include "Context.hpp"
|
|
#include "ImageManager.hpp"
|
|
// renderers
|
|
#include "renderers/LineRenderer.hpp"
|
|
#include "renderers/ArcRenderer.hpp"
|
|
#include "renderers/CircleRenderer.hpp"
|
|
#include "renderers/RectangleRenderer.hpp"
|
|
#include <renderers/PixelRenderer.hpp>
|
|
// text rendering
|
|
#include "FontManager.hpp"
|
|
#include "RawFont.hpp"
|
|
// utils
|
|
#include "log/log.hpp"
|
|
// module-utils
|
|
#include <cmath>
|
|
#include <cassert>
|
|
|
|
#if DEBUG_FONT == 1
|
|
#define log_warn_glyph(...) LOG_WARN(__VA_ARGS__)
|
|
#else
|
|
#define log_warn_glyph(...)
|
|
#endif
|
|
|
|
namespace gui
|
|
{
|
|
void Clear::draw(Context *ctx) const
|
|
{
|
|
ctx->fill(renderer::PixelRenderer::getColor(gui::ColorFullWhite.intensity));
|
|
}
|
|
|
|
void DrawLine::draw(Context *ctx) const
|
|
{
|
|
renderer::LineRenderer::draw(ctx, start, end, color);
|
|
}
|
|
|
|
void DrawRectangle::draw(Context *ctx) const
|
|
{
|
|
using renderer::RectangleRenderer;
|
|
|
|
// First, check if there is anything to draw
|
|
if (width == 0 || height == 0) {
|
|
return;
|
|
}
|
|
if (fillColor.alpha == Color::FullTransparent && borderColor.alpha == Color::FullTransparent) {
|
|
return;
|
|
}
|
|
if (!filled && borderColor.alpha == Color::FullTransparent) {
|
|
return;
|
|
}
|
|
|
|
Context *drawingContext = ctx;
|
|
Point position;
|
|
|
|
if (yaps & (RectangleYap::BottomLeft | RectangleYap::TopLeft)) {
|
|
position.x += yapSize;
|
|
}
|
|
|
|
Length adjustedWidth = areaW;
|
|
Length adjustedHeight = areaH;
|
|
if (yaps != RectangleYap::None) {
|
|
adjustedWidth -= yapSize;
|
|
}
|
|
|
|
if (areaW == width && areaH == height) {
|
|
position.x += origin.x;
|
|
position.y += origin.y;
|
|
}
|
|
else {
|
|
const auto xCtx = areaX < 0 ? origin.x + areaX : origin.x;
|
|
const auto yCtx = areaY < 0 ? origin.y + areaY : origin.y;
|
|
drawingContext = ctx->get(xCtx, yCtx, areaW, areaH);
|
|
}
|
|
|
|
if (radius == 0) {
|
|
RectangleRenderer::drawFlat(
|
|
drawingContext, position, adjustedWidth, adjustedHeight, RectangleRenderer::DrawableStyle::from(*this));
|
|
}
|
|
else {
|
|
RectangleRenderer::draw(
|
|
drawingContext, position, adjustedWidth, adjustedHeight, RectangleRenderer::DrawableStyle::from(*this));
|
|
}
|
|
|
|
if (drawingContext != ctx) {
|
|
ctx->insertArea(origin.x, origin.y, areaX, areaY, width, height, drawingContext);
|
|
delete drawingContext;
|
|
}
|
|
}
|
|
|
|
void DrawArc::draw(Context *ctx) const
|
|
{
|
|
renderer::ArcRenderer::draw(
|
|
ctx, center, radius, start, sweep, renderer::ArcRenderer::DrawableStyle::from(*this));
|
|
}
|
|
|
|
void DrawCircle::draw(Context *ctx) const
|
|
{
|
|
renderer::CircleRenderer::draw(ctx, center, radius, renderer::CircleRenderer::DrawableStyle::from(*this));
|
|
}
|
|
|
|
void DrawText::drawChar(Context *ctx, const Point glyphOrigin, FontGlyph *glyph) const
|
|
{
|
|
auto *glyphPtr = glyph->data - glyphOrigin.x;
|
|
assert(glyph->data);
|
|
|
|
Point position = glyphOrigin;
|
|
const Position glyphMaxY = glyphOrigin.y - glyph->yoffset + glyph->height;
|
|
const Position glyphMaxX = glyphOrigin.x + glyph->width;
|
|
|
|
for (position.y = glyphOrigin.y - glyph->yoffset; position.y < glyphMaxY; ++position.y) {
|
|
for (position.x = glyphOrigin.x; position.x < glyphMaxX; ++position.x) {
|
|
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;
|
|
}
|
|
|
|
if (*(glyphPtr + position.x) == ColorFullBlack.intensity) {
|
|
renderer::PixelRenderer::draw(ctx, position, color);
|
|
}
|
|
}
|
|
glyphPtr += glyph->width;
|
|
}
|
|
}
|
|
|
|
void DrawText::draw(Context *ctx) const
|
|
{
|
|
// check if there are any characters to draw in the string provided with message.
|
|
if (str.length() == 0) {
|
|
return;
|
|
}
|
|
|
|
// get copy of original context using x,y of draw coordinates and original size of the widget
|
|
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;
|
|
drawCtx = ctx->get(origin.x, origin.y, areaW, areaH);
|
|
}
|
|
|
|
// retrieve font used to draw text
|
|
RawFont *font = FontManager::getInstance().getFont(fontID);
|
|
|
|
// draw every sign
|
|
uint32_t idLast = 0, idCurrent = 0;
|
|
Point position = textOrigin;
|
|
|
|
for (uint32_t i = 0; i < str.length(); ++i) {
|
|
idCurrent = str[i]; // id stands for glued together utf-16 with no order bytes (0xFF 0xFE)
|
|
FontGlyph *glyph = font->getGlyph(idCurrent);
|
|
|
|
// do not start drawing outside of draw context.
|
|
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);
|
|
return;
|
|
}
|
|
if ((widgetOrigin.y + position.y >= drawCtx->getH()) || (widgetOrigin.y + position.y < 0)) {
|
|
LOG_FATAL("Drawing outside context's Y boundary for glyph: %" PRIu32, glyph->id);
|
|
return;
|
|
}
|
|
|
|
int32_t kernValue = 0;
|
|
if (i > 0) {
|
|
kernValue = font->getKerning(idLast, idCurrent);
|
|
}
|
|
|
|
drawChar(drawCtx,
|
|
{widgetOrigin.x + position.x + glyph->xoffset + kernValue, widgetOrigin.y + position.y},
|
|
glyph);
|
|
position.x += glyph->xadvance + kernValue;
|
|
|
|
idLast = idCurrent;
|
|
}
|
|
|
|
// if drawing was performed in temporary context
|
|
// reinsert drawCtx into bast context
|
|
if (copyContext) {
|
|
ctx->insert(origin.x, origin.y, drawCtx);
|
|
delete drawCtx;
|
|
}
|
|
}
|
|
|
|
inline void DrawImage::checkImageSize(Context *ctx, ImageMap *image) const
|
|
{
|
|
if (image->getHeight() > ctx->getH() || image->getWidth() > ctx->getW()) {
|
|
LOG_WARN("image %s {w: %d,h %d} > context {w %d,h %d}",
|
|
image->getName().c_str(),
|
|
image->getWidth(),
|
|
ctx->getW(),
|
|
image->getHeight(),
|
|
ctx->getH());
|
|
}
|
|
}
|
|
|
|
void DrawImage::drawPixMap(Context *ctx, PixMap *pixMap) const
|
|
{
|
|
uint32_t offsetImage = 0;
|
|
uint32_t offsetContext = 0;
|
|
uint8_t *pixData = pixMap->getData();
|
|
const auto ctxData = ctx->getData();
|
|
checkImageSize(ctx, pixMap);
|
|
|
|
for (uint32_t row = 0; row < std::min(ctx->getH(), pixMap->getHeight()); row++) {
|
|
std::memcpy(ctxData + offsetContext, pixData + offsetImage, std::min(ctx->getW(), pixMap->getWidth()));
|
|
offsetImage += pixMap->getWidth();
|
|
offsetContext += ctx->getW();
|
|
}
|
|
}
|
|
|
|
void DrawImage::drawVecMap(Context *ctx, VecMap *vecMap) const
|
|
{
|
|
uint32_t offsetContext = 0;
|
|
uint32_t offsetRowContext = 0;
|
|
uint32_t imageOffset = 0;
|
|
uint8_t alphaColor = vecMap->getAlphaColor();
|
|
|
|
for (uint32_t row = 0; row < std::min(vecMap->getHeight(), ctx->getH()); row++) {
|
|
checkImageSize(ctx, vecMap);
|
|
uint16_t vecCount = *(vecMap->getData() + imageOffset);
|
|
imageOffset += sizeof(uint16_t);
|
|
|
|
const auto ctxData = ctx->getData();
|
|
offsetRowContext = offsetContext;
|
|
|
|
for (uint32_t vec = 0; vec < vecCount; ++vec) {
|
|
|
|
uint16_t vecOffset = *(vecMap->getData() + imageOffset);
|
|
imageOffset += sizeof(uint16_t);
|
|
uint16_t vecLength = *(vecMap->getData() + imageOffset);
|
|
imageOffset += sizeof(uint8_t);
|
|
uint8_t vecColor = *(vecMap->getData() + imageOffset);
|
|
imageOffset += sizeof(uint8_t);
|
|
|
|
offsetRowContext += vecOffset;
|
|
if (vecColor != alphaColor) {
|
|
std::memset(ctxData + offsetRowContext,
|
|
renderer::PixelRenderer::getColor(vecColor),
|
|
std::min(ctx->getW(), vecLength));
|
|
}
|
|
offsetRowContext += vecLength;
|
|
}
|
|
offsetContext += ctx->getW();
|
|
}
|
|
}
|
|
|
|
void DrawImage::draw(Context *ctx) const
|
|
{
|
|
// retrieve pixmap from the pixmap manager
|
|
ImageMap *imageMap = ImageManager::getInstance().getImageMap(imageID);
|
|
|
|
// if image is not found return;
|
|
if (imageMap == nullptr) {
|
|
return;
|
|
}
|
|
|
|
// 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);
|
|
|
|
if (imageMap->getType() == gui::ImageMap::Type::PIXMAP) {
|
|
auto pixMap = dynamic_cast<PixMap *>(imageMap);
|
|
assert(pixMap);
|
|
drawPixMap(drawCtx, pixMap);
|
|
}
|
|
else if (imageMap->getType() == gui::ImageMap::Type::VECMAP) {
|
|
auto vecMap = dynamic_cast<VecMap *>(imageMap);
|
|
assert(vecMap);
|
|
drawVecMap(drawCtx, vecMap);
|
|
}
|
|
|
|
// reinsert drawCtx into bast context
|
|
ctx->insert(origin.x, origin.y, drawCtx);
|
|
|
|
// remove draw context
|
|
delete drawCtx;
|
|
}
|
|
} /* namespace gui */
|