Open
Description
freetype: adding advance parameter to getTextSize for positioning bounding box
System information (version)
- OpenCV => recent 4.0 branch
- Operating System / Platform => Ubuntu 18.10 64bit
- Compiler => GCC 8.2 + CUDA 10
running example code from freetype documentation
the font come from
{here}
the complete sentence in the example means:
// ▄▄▄ ▄ ▄▄▄▄▄ ▄ ▄ ▄▄▄ ▄ ▄▄▄▄▄▄▄ ▄ ▄ ▄▄▄ ▄ ▄ ▄ ▄▄▄ ▄ ▄ ▄ ▄
// █▄█▄█▄▄▄█ ▄ █ █ █ ▄ █▄▄▄▄▄█ ▄ █ █ ▄▄█ █ █ █ █▄█▄█▄█▄█▄█
// █▄▄ ▄ ▄ ▄▄█▄█ █ █▄▄▄█ █▄█ ▄▄█▄█ █ █▄█▄█▄█ █ █▄▄▄▄▄▄▄▄ ▄
// In the name of Allah, Most Gracious, Most Merciful
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/freetype.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
int main()
{
//cv::String text = "\ufc45\ufb50\ufc44\ufb50\ufc43\ufb50\ufc42\ufb50\ufc41";
//cv::String text = "\ufc44\ufb50\ufc43\ufb50\ufc42\ufb50\ufc41";
cv::String text = "\ufc43";
int fontHeight = 150;
int thickness = 1;
int linestyle = 8;
cv::Mat img( 400, 1200, CV_8UC3, cv::Scalar::all( 0 ) );
int baseline = 0;
cv::Ptr<cv::freetype::FreeType2> ft2;
ft2 = cv::freetype::createFreeType2();
ft2->setSplitNumber( 8 );
ft2->loadFontData( "QCF2001.ttf", 0 );
cv::Size textSize = ft2->getTextSize( text,
fontHeight,
thickness,
&baseline );
if ( thickness > 0 ) {
baseline += thickness;
}
// show baseline and advance
std::cout << "baseline " << baseline << std::endl;
// center the text
cv::Point textOrg( ( img.cols - textSize.width ) / 2,
( img.rows + textSize.height ) / 2 );
// draw the box
std::cout << "rectanle " << cv::Point( 0, baseline )
<< ", " << cv::Point( textSize.width, -textSize.height ) << std::endl;
cv::rectangle( img, textOrg + cv::Point( 0, baseline ),
textOrg + cv::Point( textSize.width, -textSize.height ),
cv::Scalar( 0, 255, 0 ), 1, 8 );
// ... and the baseline first
std::cout << "baseline " << cv::Point( 0, thickness )
<< ", " << cv::Point( textSize.width, thickness ) << std::endl;
cv::line( img, textOrg + cv::Point( 0, thickness ),
textOrg + cv::Point( textSize.width, thickness ),
cv::Scalar( 0, 0, 255 ), 1, 8 );
// then put the text itself
ft2->putText( img, text, textOrg, fontHeight,
cv::Scalar::all( 255 ), thickness, linestyle, true );
cv::imshow( "img_path", img );
cv::waitKey( 0 );
}
without surprise, the result is
and now, adding advance to getTextSize
#include "freetype.hpp"
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
int main()
{
//cv::String text = "\ufc45\ufb50\ufc44\ufb50\ufc43\ufb50\ufc42\ufb50\ufc41";
//cv::String text = "\ufc44\ufb50\ufc43\ufb50\ufc42\ufb50\ufc41";
cv::String text = "\ufc43";
int fontHeight = 150;
int thickness = 1;
int linestyle = 8;
cv::Mat img( 400, 1200, CV_8UC3, cv::Scalar::all( 0 ) );
int baseline = 0;
int advance = 0;
cv::Ptr<cv::freetype::FreeType2> ft2;
ft2 = cv::freetype::createFreeType2();
ft2->setSplitNumber( 8 );
ft2->loadFontData( "QCF2001.ttf", 0 );
cv::Size textSize = ft2->getTextSize( text,
fontHeight,
thickness,
&baseline,
&advance );
if ( thickness > 0 ) {
baseline += thickness;
}
// show baseline and advance
std::cout << "baseline " << baseline << ", advance "
<< advance << ", Size " << textSize << std::endl;
// center the text
cv::Point textOrg( ( img.cols - advance ) / 2, // ( img.cols - textSize.width ) / 2,
( img.rows + textSize.height ) / 2 );
// draw the box
std::cout << "rectanle " << cv::Point( advance - textSize.width, baseline )
<< ", " << cv::Point( advance, -textSize.height ) << std::endl;
cv::rectangle( img, textOrg + cv::Point( advance - textSize.width, baseline ),
textOrg + cv::Point( advance, -textSize.height ),
cv::Scalar( 0, 255, 0 ), 1, 8 );
// ... and the vertical origin
std::cout << "origin " << cv::Point( 0, baseline )
<< ", " << cv::Point( 0, thickness ) << std::endl;
cv::line( img, textOrg + cv::Point( 0, baseline ),
textOrg + cv::Point( 0, -textSize.height ),
cv::Scalar( 0, 0, 255 ), 1, 8 );
// ... and the baseline first
std::cout << "baseline " << cv::Point( advance - textSize.width, thickness )
<< ", " << cv::Point( advance, thickness ) << std::endl;
cv::line( img, textOrg + cv::Point( advance - textSize.width, thickness ),
textOrg + cv::Point( advance, thickness ),
cv::Scalar( 0, 0, 255 ), 1, 8 );
// then put the text itself
ft2->putText( img, text, textOrg, fontHeight,
cv::Scalar::all( 255 ), thickness, linestyle, true );
cv::imshow( "img_path", img );
cv::waitKey( 0 );
}
and the result is a matching bounding box
the submited proposition is
add this function to freetype.hpp
CV_WRAP virtual Size getTextSize(const String& text,
int fontHeight, int thickness,
CV_OUT int* baseLine,
CV_OUT int* advanceX) = 0;
add this implementation to freetype.cpp
...
Size getTextSize(
const String& text, int fontHeight, int thickness,
CV_OUT int* baseLine, CV_OUT int* advanceX ) CV_OVERRIDE;
...
...
Size FreeType2Impl::getTextSize(
const String& _text,
int _fontHeight,
int _thickness,
CV_OUT int* _baseLine,
CV_OUT int* _advanceX )
{
if ( _text.empty() ) {
return Size( 0, 0 );
}
CV_Assert( _fontHeight >= 0 );
if ( _fontHeight == 0 ) {
return Size( 0, 0 );
}
CV_Assert( !FT_Set_Pixel_Sizes( mFace, _fontHeight, _fontHeight ) );
hb_buffer_t* hb_buffer = hb_buffer_create();
CV_Assert( hb_buffer != NULL );
Point _org( 0, 0 );
if (advanceX)
*_advanceX = 0;
unsigned int textLen;
hb_buffer_guess_segment_properties( hb_buffer );
hb_buffer_add_utf8( hb_buffer, _text.c_str(), -1, 0, -1 );
hb_glyph_info_t* info = hb_buffer_get_glyph_infos( hb_buffer, &textLen );
CV_Assert( info != NULL );
hb_shape( mHb_font, hb_buffer, NULL, 0 );
_org.y -= _fontHeight;
int xMin = INT_MAX, xMax = INT_MIN;
int yMin = INT_MAX, yMax = INT_MIN;
for ( unsigned int i = 0; i < textLen; i++ ) {
CV_Assert( !FT_Load_Glyph( mFace, info[i].codepoint, 0 ) );
FT_GlyphSlot slot = mFace->glyph;
FT_Outline outline = slot->outline;
FT_BBox bbox;
if (advanceX)
*_advanceX += slot->advance.x >> 6;
// Flip
FT_Matrix mtx = { 1 << 16, 0, 0, -( 1 << 16 ) };
FT_Outline_Transform( &outline, &mtx );
// Move
FT_Outline_Translate( &outline,
cOutlineOffset,
cOutlineOffset );
// Move
FT_Outline_Translate( &outline,
( FT_Pos )( _org.x << 6 ),
( FT_Pos )( ( _org.y + _fontHeight ) << 6 ) );
CV_Assert( !FT_Outline_Get_BBox( &outline, &bbox ) );
// If codepoint is space(0x20), it has no glyph.
// A dummy boundary box is needed when last code is space.
if (
( bbox.xMin == 0 ) && ( bbox.xMax == 0 ) && ( bbox.yMin == 0 ) && ( bbox.yMax == 0 ) ) {
bbox.xMin = ( _org.x << 6 );
bbox.xMax = ( _org.x << 6 ) + ( mFace->glyph->advance.x );
bbox.yMin = yMin;
bbox.yMax = yMax;
bbox.xMin += cOutlineOffset;
bbox.xMax += cOutlineOffset;
bbox.yMin += cOutlineOffset;
bbox.yMax += cOutlineOffset;
}
xMin = cv::min( xMin, ftd( bbox.xMin ) );
xMax = cv::max( xMax, ftd( bbox.xMax ) );
yMin = cv::min( yMin, ftd( bbox.yMin ) );
yMax = cv::max( yMax, ftd( bbox.yMax ) );
_org.x += ( mFace->glyph->advance.x ) >> 6;
_org.y += ( mFace->glyph->advance.y ) >> 6;
}
hb_buffer_destroy( hb_buffer );
int width = xMax - xMin;
int height = -yMin;
if ( _thickness > 0 ) {
width = cvRound( width + _thickness * 2 );
height = cvRound( height + _thickness * 1 );
} else {
width = cvRound( width + 1 );
height = cvRound( height + 1 );
}
if ( _baseLine ) {
*_baseLine = yMax;
}
return Size( width, height );
}
...
and the whole story become