p26001-store-acs-soc-fw-app/video/core/Image.cpp
2026-09-28 21:33:04 +08:00

279 lines
8.6 KiB
C++

/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Image.h"
#include "Logger.h"
#include "util/Math.h"
#include <freetype/ftglyph.h>
#include <freetype/fterrors.h>
#include <Poco/Exception.h>
#include <Poco/Format.h>
#include <Poco/UnicodeConverter.h>
namespace Video
{
namespace Core
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
void Image::drawText(const std::string& text, const std::string& font, int size, Color foreColor, Color bgColor)
{
get_shared_channel(0).fill(bgColor.r);
get_shared_channel(1).fill(bgColor.g);
get_shared_channel(2).fill(bgColor.b);
get_shared_channel(3).fill(bgColor.a);
int x = 0, y = 0;
y = height() - y;
std::wstring wtext;
convertText(text, wtext);
runOnFreeType(font, size,
[&](FT_Face& face) -> void
{
FT_BBox bbox;
computeStringBBox(wtext, face, bbox);
FT_Vector pen;
pen.x = (x - bbox.xMin) * 64;
pen.y = (y - bbox.yMax) * 64;
for (size_t i = 0; i < wtext.length(); i++)
{
FT_Set_Transform(face, 0, &pen);
FT_UInt glyph_index = FT_Get_Char_Index(face, wtext.at(i));
int error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
if (error)
{
throw Poco::InvalidAccessException("字形加载失败", error);
}
error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
if (error)
{
throw Poco::InvalidAccessException("字形渲染失败", error);
}
int offsetX = face->glyph->bitmap_left;
int offsetY = height() - face->glyph->bitmap_top;
for (size_t y = 0; y < face->glyph->bitmap.rows; y++)
{
for (size_t x = 0; x < face->glyph->bitmap.width; x++)
{
uint8_t c = face->glyph->bitmap.buffer[y * face->glyph->bitmap.width + x];
if (c < 50)
continue;
(*this)(x + offsetX, y + offsetY, 0) = foreColor.r * c / 255;
(*this)(x + offsetX, y + offsetY, 1) = foreColor.g * c / 255;
(*this)(x + offsetX, y + offsetY, 2) = foreColor.b * c / 255;
(*this)(x + offsetX, y + offsetY, 3) = foreColor.a * c / 255;
}
}
pen.x += face->glyph->advance.x;
pen.y += face->glyph->advance.y;
}
});
}
void Image::drawRectangle(const Rect& rect, Color& color, int border)
{
if (border <= 0)
{
get_shared_channel(0).fill(color.r);
get_shared_channel(1).fill(color.g);
get_shared_channel(2).fill(color.b);
get_shared_channel(3).fill(color.a);
}
else
{
get_shared_channel(3).fill(0);
uint8_t c[4] = {color.r, color.g, color.b, color.a};
draw_rectangle(rect.x, rect.y, rect.x + rect.width, rect.y + border, c);
draw_rectangle(rect.x, rect.y + border, rect.x + border, rect.y + rect.height, c);
draw_rectangle(rect.x + rect.width - border, rect.y + border, rect.x + rect.width, rect.y + rect.height, c);
draw_rectangle(rect.x + border, rect.y + rect.height - border, rect.x + rect.width - border,
rect.y + rect.height, c);
}
}
void Image::drawImage(int x, int y, Ptr pImage)
{
const int width = pImage->width();
const int height = pImage->height();
for (int sy = 0; sy < height; ++sy)
{
for (int sx = 0; sx < width; ++sx)
{
const int dx = x + sx;
const int dy = y + sy;
if (dx >= 0 && dx < this->width() && dy >= 0 && dy < this->height())
{
Color color;
pImage->getPixel(sx, sy, color);
(*this)(dx, dy, 0, 0) = color.r;
(*this)(dx, dy, 0, 1) = color.g;
(*this)(dx, dy, 0, 2) = color.b;
(*this)(dx, dy, 0, 3) = color.a;
}
}
}
}
void Image::getPixel(int x, int y, Color& color)
{
uint8_t originAlpha = 0xff;
if (spectrum() == 1)
{
color.r = (*this)(x, y, 0, 0);
color.g = color.r;
color.b = color.r;
}
else if (spectrum() == 2)
{
color.r = (*this)(x, y, 0, 0);
color.g = (*this)(x, y, 0, 1);
color.b = 0;
}
else if (spectrum() == 3)
{
color.r = (*this)(x, y, 0, 0);
color.g = (*this)(x, y, 0, 1);
color.b = (*this)(x, y, 0, 2);
}
else if (spectrum() == 4)
{
color.r = (*this)(x, y, 0, 0);
color.g = (*this)(x, y, 0, 1);
color.b = (*this)(x, y, 0, 2);
originAlpha = (*this)(x, y, 0, 3);
}
if (_forceAlphaEnabled && originAlpha > _forceAlphaValue)
color.a = _forceAlphaValue;
else
color.a = originAlpha;
}
void Image::forceAlpha(uint8_t alpha)
{
_forceAlphaEnabled = true;
_forceAlphaValue = alpha;
}
void Image::computeStringSize(const std::string& text, const std::string& font, int fontsz, Size& size)
{
runOnFreeType(font, fontsz,
[&](FT_Face& face) -> void
{
FT_BBox bbox;
std::wstring wstr;
convertText(text, wstr);
computeStringBBox(wstr, face, bbox);
size.width = bbox.xMax - bbox.xMin;
size.height = bbox.yMax - bbox.yMin;
size.width = Util::Math::alignup(size.width, 2);
size.height = Util::Math::alignup(size.height, 2);
});
}
int Image::computeStringBBox(std::wstring text, FT_Face face, FT_BBox& bbox)
{
size_t i;
int error;
FT_BBox glyph_bbox;
FT_Vector pen;
FT_Glyph glyph;
FT_GlyphSlot slot = face->glyph;
bbox.xMin = bbox.yMin = 32000;
bbox.xMax = bbox.yMax = -32000;
pen.x = 0;
pen.y = 0;
for (i = 0; i < text.length(); i++)
{
FT_Set_Transform(face, 0, &pen);
error = FT_Load_Char(face, text.at(i), FT_LOAD_RENDER);
if (error)
{
logger().error("FT_Load_Char error: %d(%s)", error, std::string(FT_Error_String(error)));
return -1;
}
error = FT_Get_Glyph(face->glyph, &glyph);
if (error)
{
logger().error("FT_Get_Glyph error: %d(%s)", error, std::string(FT_Error_String(error)));
return -1;
}
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &glyph_bbox);
if (glyph_bbox.xMin < bbox.xMin)
bbox.xMin = glyph_bbox.xMin;
if (glyph_bbox.yMin < bbox.yMin)
bbox.yMin = glyph_bbox.yMin;
if (glyph_bbox.xMax > bbox.xMax)
bbox.xMax = glyph_bbox.xMax;
if (glyph_bbox.yMax > bbox.yMax)
bbox.yMax = glyph_bbox.yMax;
FT_Done_Glyph(glyph);
pen.x += slot->advance.x;
pen.y += slot->advance.y;
}
return 0;
}
void Image::convertText(const std::string& from, std::wstring& to)
{
Poco::UnicodeConverter::toUTF16(from, to);
}
void Image::runOnFreeType(const std::string& font, int size, std::function<void(FT_Face&)> func)
{
FT_Library library;
FT_Face face;
FT_Error error;
error = FT_Init_FreeType(&library);
if (error)
throw Poco::InvalidAccessException(
Poco::format("FreeType init error: %d(%s)", error, std::string(FT_Error_String(error))), error);
error = FT_New_Face(library, font.c_str(), 0, &face);
if (error)
throw Poco::InvalidAccessException(
Poco::format("FreeType new face error: %d(%s)", error, std::string(FT_Error_String(error))), error);
FT_Set_Pixel_Sizes(face, 0, size);
func(face);
FT_Done_Face(face);
FT_Done_FreeType(library);
}
}
}