/* ******************************************************************************* * INCLUDE ******************************************************************************* */ #include "Image.h" #include "Logger.h" #include "util/Math.h" #include #include #include #include #include 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 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); } } }