From 717953bf2d41b1a092dc5be6e5018e5a9c3a4731 Mon Sep 17 00:00:00 2001 From: BlueMatthew Date: Mon, 25 Dec 2023 09:26:58 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0FreeType2=E7=9A=84=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=BA=93=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/cpp/CMakeLists.txt | 73 +--------- app/src/main/cpp/camera2/OpenCVFont.cpp | 185 ++++++++++++++++++++++++ app/src/main/cpp/camera2/OpenCVFont.h | 74 ++++++++++ 3 files changed, 265 insertions(+), 67 deletions(-) create mode 100644 app/src/main/cpp/camera2/OpenCVFont.cpp create mode 100644 app/src/main/cpp/camera2/OpenCVFont.h diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 2617e8e0..cbd16ec1 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -27,6 +27,7 @@ add_definitions(-DBOOST_ALL_NO_LIB) add_definitions(-DASIO_STANDALONE) # add_definitions(-DUSING_BREAK_PAD) + # set(OpenCV_DIR D:/Workspace/deps/OpenCV-android-sdk/sdk/native/jni/) set(OPENCV_EXTRA_MODULES_PATH D:/Workspace/Github/opencv_contrib/modules) @@ -154,73 +155,7 @@ SET(CAMERA2_SOURCES # ${CAMERA2_ROOT_DIR}/utils/camera_utils.cpp ${CAMERA2_ROOT_DIR}/ndkcamera.cpp) -#[[ -SET(EVPP_SOURCES - ${EVPP_SRC_DIR}/any.h - ${EVPP_SRC_DIR}/buffer.cc - ${EVPP_SRC_DIR}/buffer.h - ${EVPP_SRC_DIR}/connector.cc - ${EVPP_SRC_DIR}/connector.h - ${EVPP_SRC_DIR}/dns_resolver.cc - ${EVPP_SRC_DIR}/dns_resolver.h - ${EVPP_SRC_DIR}/duration.cc - ${EVPP_SRC_DIR}/duration.h - ${EVPP_SRC_DIR}/duration.inl.h - ${EVPP_SRC_DIR}/event_loop.cc - ${EVPP_SRC_DIR}/event_loop.h - ${EVPP_SRC_DIR}/event_loop_thread.cc - ${EVPP_SRC_DIR}/event_loop_thread.h - ${EVPP_SRC_DIR}/event_loop_thread_pool.cc - ${EVPP_SRC_DIR}/event_loop_thread_pool.h - ${EVPP_SRC_DIR}/event_watcher.cc - ${EVPP_SRC_DIR}/event_watcher.h - ${EVPP_SRC_DIR}/fd_channel.cc - ${EVPP_SRC_DIR}/fd_channel.h - ${EVPP_SRC_DIR}/gettimeofday.h - #${EVPP_SRC_DIR}/httpc\conn.cc - #${EVPP_SRC_DIR}/httpc\conn.h - #${EVPP_SRC_DIR}/httpc\conn_pool.cc - #${EVPP_SRC_DIR}/httpc\conn_pool.h - #${EVPP_SRC_DIR}/httpc\request.cc - #${EVPP_SRC_DIR}/httpc\request.h - #${EVPP_SRC_DIR}/httpc\response.cc - #${EVPP_SRC_DIR}/httpc\response.h - #${EVPP_SRC_DIR}/httpc\ssl.cc - #${EVPP_SRC_DIR}/httpc\ssl.h - #${EVPP_SRC_DIR}/httpc\url_parser.cc - #${EVPP_SRC_DIR}/httpc\url_parser.h - ${EVPP_SRC_DIR}/inner_pre.cc - ${EVPP_SRC_DIR}/inner_pre.h - ${EVPP_SRC_DIR}/invoke_timer.cc - ${EVPP_SRC_DIR}/invoke_timer.h - ${EVPP_SRC_DIR}/libevent.cc - ${EVPP_SRC_DIR}/libevent.h - ${EVPP_SRC_DIR}/listener.cc - ${EVPP_SRC_DIR}/listener.h - ${EVPP_SRC_DIR}/logging.h - ${EVPP_SRC_DIR}/memmem.h - ${EVPP_SRC_DIR}/platform_config.h - ${EVPP_SRC_DIR}/server_status.h - ${EVPP_SRC_DIR}/slice.h - ${EVPP_SRC_DIR}/sockets.cc - ${EVPP_SRC_DIR}/sockets.h - ${EVPP_SRC_DIR}/sys_addrinfo.h - ${EVPP_SRC_DIR}/sys_sockets.h - ${EVPP_SRC_DIR}/tcp_callbacks.h - ${EVPP_SRC_DIR}/tcp_client.cc - ${EVPP_SRC_DIR}/tcp_client.h - ${EVPP_SRC_DIR}/tcp_conn.cc - ${EVPP_SRC_DIR}/tcp_conn.h - ${EVPP_SRC_DIR}/tcp_server.cc - ${EVPP_SRC_DIR}/tcp_server.h - ${EVPP_SRC_DIR}/thread_dispatch_policy.h - ${EVPP_SRC_DIR}/timestamp.h - ${EVPP_SRC_DIR}/timestamp.inl.h - ${EVPP_SRC_DIR}/utility.h - ${EVPP_SRC_DIR}/windows_port.h - ) -]] - +add_definitions(-DFT2_BUILD_LIBRARY=1) SET(FREETYPE_SRC_FILES ${FREETYPE_ROOT}/src/autofit/autofit.c ${FREETYPE_ROOT}/src/base/ftbase.c @@ -264,6 +199,8 @@ SET(FREETYPE_SRC_FILES ${FREETYPE_ROOT}/src/type1/type1.c ${FREETYPE_ROOT}/src/type42/type42.c ${FREETYPE_ROOT}/src/winfonts/winfnt.c + ${FREETYPE_ROOT}/src/svg/svg.c + ${FREETYPE_ROOT}/src/sdf/sdf.c ) include_directories(${FREETYPE_ROOT}/include) @@ -336,6 +273,8 @@ add_library( # Sets the name of the library. ncnn/yolov5ncnn.cpp + camera2/OpenCVFont.cpp + ${CAMERA2_SOURCES} ${TERM_CORE_ROOT}/Factory.cpp diff --git a/app/src/main/cpp/camera2/OpenCVFont.cpp b/app/src/main/cpp/camera2/OpenCVFont.cpp new file mode 100644 index 00000000..0ce90fbe --- /dev/null +++ b/app/src/main/cpp/camera2/OpenCVFont.cpp @@ -0,0 +1,185 @@ +// +// Created by Matthew on 2023/12/24. +// + +#include "OpenCVFont.h" + +#include +#include +#include +#include +#include +#include + +cvx::CvxFont::CvxFont(const cv::String& fontType) +{ + assert(!fontType.empty()); + m_error = FT_Init_FreeType(&m_library); + if (m_error){ + std::cerr << "library initial error!" << std::endl; + return; + } + m_error = FT_New_Face(m_library, fontType.c_str(), 0, &m_face); + if (m_error == FT_Err_Unknown_File_Format){ + std::cerr << "unsupported font format!" << std::endl; + return; + } + else if (m_error){ + std::cerr << " can not open font files" << std::endl; + return; + } + // use default parameters + m_font = new FontProperty; + initFont(); + setlocale(LC_ALL, ""); +} + +// release freetype resource +cvx::CvxFont::~CvxFont() +{ + delete m_font; + FT_Done_Face(m_face); + FT_Done_FreeType(m_library); +} + +void cvx::CvxFont::setFontSize(const int fontSize) +{ + m_font->fontSize = fontSize; + FT_Set_Pixel_Sizes(m_face, fontSize, 0); +} + +// initial font +void cvx::CvxFont::initFont() +{ + setFontSize(16); + setSpaceRatio(0.5); + setFontRatio(0); + setRotateAngle(0); + setDiaphaneity(1); + setUnderline(false); + setVertical(false); + // set font + FT_Set_Pixel_Sizes(m_face, getFontSize(), 0); +} + +void cvx::CvxFont::rotateFont(double angle) { + angle = (angle / 360) * 3.14159 * 2; + /* set up matrix */ + m_matrix.xx = static_cast(cos(angle) * 0x10000L); + m_matrix.xy = static_cast(-sin(angle) * 0x10000L); + m_matrix.yx = static_cast(sin(angle) * 0x10000L); + m_matrix.yy = static_cast(cos(angle) * 0x10000L); + + FT_Set_Transform(m_face, &m_matrix, nullptr); +} + +void cvx::CvxFont::putTextStr(cv::Mat& img, const cv::String& text, cv::Point pos, const cv::Scalar& color) +{ + CV_Assert(!img.empty()); + CV_Assert(!text.empty()); + + int xStart = pos.x; + int yStart = pos.y; + m_maxDiffHeight = 0; + + const char* ptr = text.c_str(); + std::mbtowc(nullptr, nullptr, 0); // reset the conversion state + const char* end = ptr + std::strlen(ptr); + int ret; + for (wchar_t wc; (ret = std::mbtowc(&wc, ptr, end - ptr)) > 0; ptr += ret) { + putWChar(img, (wc & 0xffffffff), pos, color); + } + + int xEnd = pos.x; + int yEnd = pos.y; + if (getUnderline()) { + if (getVertical()) { + cv::line(img, cv::Point(xStart + m_maxDiffHeight, yStart), cv::Point(xStart + m_maxDiffHeight, yEnd), color, 2); + } + else { + cv::line(img, cv::Point(xStart, yStart + m_maxDiffHeight), cv::Point(xEnd, yStart + m_maxDiffHeight), color, 2); + } + } + +} + +void cvx::CvxFont::putWChar(cv::Mat& img, uint32_t wc, cv::Point& pos, const cv::Scalar& color) +{ + rotateFont(getAngle()); + const auto vertical = getVertical(); + const auto size = getFontSize(); + + // Converting a Character Code Into a Glyph Index + FT_UInt glyph_index = FT_Get_Char_Index(m_face, wc); + FT_Load_Glyph(m_face, glyph_index, FT_LOAD_DEFAULT); + FT_Render_Glyph(m_face->glyph, FT_RENDER_MODE_MONO); + + FT_GlyphSlot slot = m_face->glyph; + FT_Bitmap bitmap = slot->bitmap; + bool isSpace = wc == ' '; + + // get rows and cols of current wide char + auto rows = bitmap.rows; + auto cols = bitmap.width; + + cv::Point gPos = pos; + //gPos.y += m_font->fontSize; + if (vertical) + { + gPos.x += (slot->metrics.vertBearingX >> 6); + gPos.y += (slot->metrics.vertBearingY >> 6); + m_maxDiffHeight = std::max(m_maxDiffHeight, rows - (slot->metrics.vertBearingY >> 6)); + } + else + { + gPos.x += (slot->metrics.horiBearingX >> 6); + gPos.y -= (slot->metrics.horiBearingY >> 6); + m_maxDiffHeight = std::max(m_maxDiffHeight, rows - (slot->metrics.horiBearingY >> 6)); + } + + // https://stackoverflow.com/questions/52254639/how-to-access-pixels-state-in-monochrome-bitmap-using-freetype2 + for (auto i = 0; i < rows; ++i) + { + for (auto j = 0; j < cols; ++j) + { + int off = i * slot->bitmap.pitch + j / 8; + + if (slot->bitmap.buffer[off] & (0x80 >> (j % 8))) + { + const auto r = gPos.y + i; //vertical ? pos.y + i : pos.y + i + (size - rows); // to make align to bottom + const auto c = gPos.x + j; + + if (r >= 0 && r < img.rows && c >= 0 && c < img.cols) + { + cv::Vec3b scalar = img.at(cv::Point(c, r)); + + // merge set color with origin color + double p = getDiaphaneity(); + for (int k = 0; k < 3; ++k) + { + scalar.val[k] = static_cast(scalar.val[k] * (1 - p) + color.val[k] * p); + } + + img.at(cv::Point(c, r)) = cv::Vec3b(scalar[0], scalar[1], scalar[2]); + } + } + } + } + // modify position to next character + const auto space = static_cast(size * getSpaceRatio()); + const auto sep = static_cast(size * getFontRatio()); + // vertical string or not, default not vertical + if (vertical){ + const auto moveX = (static_cast(getAngle()) == 0) ? (slot->metrics.vertAdvance >> 6) : rows + 1; + pos.y += isSpace ? space : moveX + sep; + }else{ + const auto moveY = (static_cast(getAngle()) == 0) ? (slot->metrics.horiAdvance >> 6) : cols + 1; + pos.x += isSpace ? space : moveY + sep; + } +} + +void cvx::putText(cv::Mat& img, const std::string& text, cv::Point pos, cvx::CvxFont& fontFace, int fontSize, const cv::Scalar& color) { + fontFace.setFontSize(fontSize); + fontFace.putTextStr(img, text, std::move(pos), color); + fontFace.initFont(); +} diff --git a/app/src/main/cpp/camera2/OpenCVFont.h b/app/src/main/cpp/camera2/OpenCVFont.h new file mode 100644 index 00000000..d27086ad --- /dev/null +++ b/app/src/main/cpp/camera2/OpenCVFont.h @@ -0,0 +1,74 @@ +// +// Created by Matthew on 2023/12/24. +// + +#ifndef MICROPHOTO_OPENCVFONT_H +#define MICROPHOTO_OPENCVFONT_H + +#include +#include FT_FREETYPE_H +#include + +#include +#include +#include +#include + +namespace cvx { + struct FontProperty { + int fontSize; // font size (pixel) + double spaceRatio; // ratio of distance when meet a space, base on font size + double fontRatio; // ratio of distance between each character, base on font size + double fontRotateAngle; // rotate angle + double fontDiaphaneity; // merge ratio + bool fontIsUnderline; // underline + bool fontIsVertical; // put text in vertical + }; + + class CvxFont + { + public: + + explicit CvxFont(const cv::String& fontType); + virtual ~CvxFont(); + + void setFontSize(int fontSize); + void setSpaceRatio(const double spaceRatio) { m_font->spaceRatio = spaceRatio; } + void setFontRatio(const double fontRatio) { m_font->fontRatio = fontRatio; } + void setRotateAngle(const double angle) { m_font->fontRotateAngle = angle; } + void setUnderline(const bool isUnderline) { m_font->fontIsUnderline = isUnderline; } + void setDiaphaneity(const double diaphaneity) { m_font->fontDiaphaneity = diaphaneity; } + void setVertical(const bool vertical) { m_font->fontIsVertical = vertical; } + + int getFontSize() const { return m_font->fontSize; } + double getSpaceRatio() const { return m_font->spaceRatio; } + double getFontRatio() const { return m_font->fontRatio; } + double getAngle() const { return m_font->fontRotateAngle; } + bool getUnderline() const { return m_font->fontIsUnderline; } + double getDiaphaneity() const { return m_font->fontDiaphaneity; } + bool getVertical() const { return m_font->fontIsVertical; } + + private: + void initFont(); + void rotateFont(double angle); + void putTextStr(cv::Mat& img, const cv::String& text, cv::Point pos, const cv::Scalar& color); + void putWChar(cv::Mat& img, uint32_t wc, cv::Point& pos, const cv::Scalar& color); + friend void putText(cv::Mat&, const std::string&, cv::Point, cvx::CvxFont&, int, const cv::Scalar&); + FT_Library m_library{}; // font library + FT_Face m_face{}; // font type + FT_Matrix m_matrix{}; + FT_Vector m_pen{}; + FT_Error m_error; + + FontProperty* m_font; + long m_maxDiffHeight{ 0 }; + + }; + + void putText(cv::Mat& img, const std::string& text, cv::Point pos, cvx::CvxFont& fontFace, int fontSize, const cv::Scalar& color); + void putSymbols(cv::Mat& img, std::vector& symbols, cv::Point pos, cvx::CvxFont& fontFace, int fontSize, const cv::Scalar& color); + void putOneSymbol(cv::Mat& img, uint32_t symbol, cv::Point pos, cvx::CvxFont& fontFace, int fontSize, const cv::Scalar& color); +} + + +#endif //MICROPHOTO_OPENCVFONT_H