Skip to content

Commit 3951e03

Browse files
authored
Merge pull request #3288 from Kumataro:4.x-issue_contrib3276
freetype: support 8UC1/8UC4 image * freetype: add parameter test * freetype: support CV_8UC3 and CV_8C4 * freetype:fix for gcc5.4 * freetype: delete newline * freetype:fix for python binding * freetype: Revert 3rd argument type of loadFontData() to use int. - Revert 3rd argument type of loadFontData() to use "int". - Use cvtest::debugLevel instead to OUTPUT_FILE define. - Change type of idx_range list and ctol_range list to use "int". - fix typo.
1 parent d502f4c commit 3951e03

File tree

6 files changed

+633
-36
lines changed

6 files changed

+633
-36
lines changed

modules/freetype/include/opencv2/freetype.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,10 @@ class CV_EXPORTS_W FreeType2 : public Algorithm
7979
The function loadFontData loads font data.
8080
8181
@param fontFileName FontFile Name
82-
@param id face_index to select a font faces in a single file.
82+
@param idx face_index to select a font faces in a single file.
8383
*/
8484

85-
CV_WRAP virtual void loadFontData(String fontFileName, int id) = 0;
85+
CV_WRAP virtual void loadFontData(String fontFileName, int idx) = 0;
8686

8787
/** @brief Set Split Number from Bezier-curve to line
8888
@@ -99,7 +99,7 @@ If you want to draw small glyph, small is better.
9999
100100
The function putText renders the specified text string in the image. Symbols that cannot be rendered using the specified font are replaced by "Tofu" or non-drawn.
101101
102-
@param img Image. (Only 8UC3 image is supported.)
102+
@param img Image. (Only 8UC1/8UC3/8UC4 2D mat is supported.)
103103
@param text Text string to be drawn.
104104
@param org Bottom-left/Top-left corner of the text string in the image.
105105
@param fontHeight Drawing font size by pixel unit.
@@ -123,7 +123,7 @@ That is, the following code renders some text, the tight box surrounding it, and
123123
String text = "Funny text inside the box";
124124
int fontHeight = 60;
125125
int thickness = -1;
126-
int linestyle = 8;
126+
int linestyle = LINE_8;
127127
128128
Mat img(600, 800, CV_8UC3, Scalar::all(0));
129129

modules/freetype/src/freetype.cpp

Lines changed: 162 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class CV_EXPORTS_W FreeType2Impl CV_FINAL : public FreeType2
6666
public:
6767
FreeType2Impl();
6868
~FreeType2Impl();
69-
void loadFontData(String fontFileName, int id) CV_OVERRIDE;
69+
void loadFontData(String fontFileName, int idx) CV_OVERRIDE;
7070
void setSplitNumber( int num ) CV_OVERRIDE;
7171
void putText(
7272
InputOutputArray img, const String& text, Point org,
@@ -92,17 +92,28 @@ class CV_EXPORTS_W FreeType2Impl CV_FINAL : public FreeType2
9292
int fontHeight, Scalar color,
9393
int thickness, int line_type, bool bottomLeftOrigin
9494
);
95+
9596
void putTextBitmapBlend(
9697
InputOutputArray img, const String& text, Point org,
9798
int fontHeight, Scalar color,
9899
int thickness, int line_type, bool bottomLeftOrigin
99100
);
101+
100102
void putTextOutline(
101103
InputOutputArray img, const String& text, Point org,
102104
int fontHeight, Scalar color,
103105
int thickness, int line_type, bool bottomLeftOrigin
104106
);
105107

108+
typedef void (putPixel_mono_fn)( Mat& _dst, const int _py, const int _px, const uint8_t *_col);
109+
putPixel_mono_fn putPixel_8UC1_mono;
110+
putPixel_mono_fn putPixel_8UC3_mono;
111+
putPixel_mono_fn putPixel_8UC4_mono;
112+
113+
typedef void (putPixel_blend_fn)( Mat& _dst, const int _py, const int _px, const uint8_t *_col, const uint8_t alpha);
114+
putPixel_blend_fn putPixel_8UC1_blend;
115+
putPixel_blend_fn putPixel_8UC3_blend;
116+
putPixel_blend_fn putPixel_8UC4_blend;
106117

107118
static int mvFn( const FT_Vector *to, void * user);
108119
static int lnFn( const FT_Vector *to, void * user);
@@ -158,7 +169,8 @@ FreeType2Impl::FreeType2Impl()
158169

159170
FreeType2Impl::~FreeType2Impl()
160171
{
161-
if( mIsFaceAvailable == true ){
172+
if( mIsFaceAvailable == true )
173+
{
162174
hb_font_destroy (mHb_font);
163175
CV_Assert(!FT_Done_Face(mFace));
164176
mIsFaceAvailable = false;
@@ -168,12 +180,22 @@ FreeType2Impl::~FreeType2Impl()
168180

169181
void FreeType2Impl::loadFontData(String fontFileName, int idx)
170182
{
171-
if( mIsFaceAvailable == true ){
183+
CV_Assert( idx >= 0 );
184+
if( mIsFaceAvailable == true )
185+
{
172186
hb_font_destroy (mHb_font);
173187
CV_Assert(!FT_Done_Face(mFace));
174188
}
175-
CV_Assert(!FT_New_Face( mLibrary, fontFileName.c_str(), idx, &(mFace) ) );
189+
190+
mIsFaceAvailable = false;
191+
CV_Assert( !FT_New_Face( mLibrary, fontFileName.c_str(), static_cast<FT_Long>(idx), &(mFace) ) );
192+
176193
mHb_font = hb_ft_font_create (mFace, NULL);
194+
if ( mHb_font == NULL )
195+
{
196+
CV_Assert(!FT_Done_Face(mFace));
197+
return;
198+
}
177199
CV_Assert( mHb_font != NULL );
178200
mIsFaceAvailable = true;
179201
}
@@ -189,16 +211,17 @@ void FreeType2Impl::putText(
189211
int _thickness, int _line_type, bool _bottomLeftOrigin
190212
)
191213
{
192-
CV_Assert( mIsFaceAvailable == true );
193-
CV_Assert( ( _img.empty() == false ) &&
194-
( _img.isMat() == true ) &&
195-
( _img.depth() == CV_8U ) &&
196-
( _img.dims() == 2 ) &&
197-
( _img.channels() == 3 ) );
198-
CV_Assert( ( _line_type == CV_AA) ||
199-
( _line_type == 4 ) ||
200-
( _line_type == 8 ) );
201-
CV_Assert( _fontHeight >= 0 );
214+
CV_Assert ( mIsFaceAvailable == true );
215+
CV_Assert ( _img.empty() == false );
216+
CV_Assert ( _img.isMat() == true );
217+
CV_Assert ( _img.dims() == 2 );
218+
CV_Assert( ( _img.type() == CV_8UC1 ) ||
219+
( _img.type() == CV_8UC3 ) ||
220+
( _img.type() == CV_8UC4 ) );
221+
CV_Assert( ( _line_type == LINE_AA) ||
222+
( _line_type == LINE_4 ) ||
223+
( _line_type == LINE_8 ) );
224+
CV_Assert ( _fontHeight >= 0 );
202225

203226
if ( _text.empty() )
204227
{
@@ -209,15 +232,11 @@ void FreeType2Impl::putText(
209232
return;
210233
}
211234

212-
if( _line_type == CV_AA && _img.depth() != CV_8U ){
213-
_line_type = 8;
214-
}
215-
216235
CV_Assert(!FT_Set_Pixel_Sizes( mFace, _fontHeight, _fontHeight ));
217236

218237
if( _thickness < 0 ) // CV_FILLED
219238
{
220-
if ( _line_type == CV_AA ) {
239+
if ( _line_type == LINE_AA ) {
221240
putTextBitmapBlend( _img, _text, _org, _fontHeight, _color,
222241
_thickness, _line_type, _bottomLeftOrigin );
223242
}else{
@@ -292,13 +311,36 @@ void FreeType2Impl::putTextOutline(
292311
hb_buffer_destroy (hb_buffer);
293312
}
294313

314+
void FreeType2Impl::putPixel_8UC1_mono( Mat& _dst, const int _py, const int _px, const uint8_t *_col)
315+
{
316+
uint8_t* ptr = _dst.ptr<uint8_t>( _py, _px );
317+
(*ptr) = _col[0];
318+
}
319+
320+
void FreeType2Impl::putPixel_8UC3_mono ( Mat& _dst, const int _py, const int _px, const uint8_t *_col)
321+
{
322+
cv::Vec3b* ptr = _dst.ptr<cv::Vec3b>( _py, _px );
323+
(*ptr)[0] = _col[0];
324+
(*ptr)[1] = _col[1];
325+
(*ptr)[2] = _col[2];
326+
}
327+
328+
void FreeType2Impl::putPixel_8UC4_mono( Mat& _dst, const int _py, const int _px, const uint8_t *_col)
329+
{
330+
cv::Vec4b* ptr = _dst.ptr<cv::Vec4b>( _py, _px );
331+
(*ptr)[0] = _col[0];
332+
(*ptr)[1] = _col[1];
333+
(*ptr)[2] = _col[2];
334+
(*ptr)[3] = _col[3];
335+
}
336+
295337
void FreeType2Impl::putTextBitmapMono(
296338
InputOutputArray _img, const String& _text, Point _org,
297339
int _fontHeight, Scalar _color,
298340
int _thickness, int _line_type, bool _bottomLeftOrigin )
299341
{
300342
CV_Assert( _thickness < 0 );
301-
CV_Assert( _line_type == 4 || _line_type == 8);
343+
CV_Assert( _line_type == LINE_4 || _line_type == LINE_8);
302344

303345
Mat dst = _img.getMat();
304346
hb_buffer_t *hb_buffer = hb_buffer_create ();
@@ -318,6 +360,17 @@ void FreeType2Impl::putTextBitmapMono(
318360
_org.y -= _fontHeight;
319361
}
320362

363+
const uint8_t _colorUC8n[4] = {
364+
static_cast<uint8_t>(_color[0]),
365+
static_cast<uint8_t>(_color[1]),
366+
static_cast<uint8_t>(_color[2]),
367+
static_cast<uint8_t>(_color[3]) };
368+
369+
void (cv::freetype::FreeType2Impl::*putPixel)( Mat&, const int, const int, const uint8_t*) =
370+
(_img.type() == CV_8UC4)?(&FreeType2Impl::putPixel_8UC4_mono):
371+
(_img.type() == CV_8UC3)?(&FreeType2Impl::putPixel_8UC3_mono):
372+
(&FreeType2Impl::putPixel_8UC1_mono);
373+
321374
for( unsigned int i = 0 ; i < textLen ; i ++ ){
322375
CV_Assert( !FT_Load_Glyph(mFace, info[i].codepoint, 0 ) );
323376
CV_Assert( !FT_Render_Glyph( mFace->glyph, FT_RENDER_MODE_MONO ) );
@@ -351,10 +404,7 @@ void FreeType2Impl::putTextBitmapMono(
351404
}
352405

353406
if ( ( (cl >> bit) & 0x01 ) == 1 ) {
354-
cv::Vec3b* ptr = dst.ptr<cv::Vec3b>( gPos.y + row, gPos.x + col * 8 + (7 - bit) );
355-
(*ptr)[0] = _color[0];
356-
(*ptr)[1] = _color[1];
357-
(*ptr)[2] = _color[2];
407+
(this->*putPixel)( dst, gPos.y + row, gPos.x + col * 8 + (7 - bit), _colorUC8n );
358408
}
359409
}
360410
}
@@ -366,14 +416,88 @@ void FreeType2Impl::putTextBitmapMono(
366416
hb_buffer_destroy (hb_buffer);
367417
}
368418

419+
// Alpha composite algorithm is porting from imgproc.
420+
// See https://github.com/opencv/opencv/blob/4.6.0/modules/imgproc/src/drawing.cpp
421+
// static void LineAA( Mat& img, Point2l pt1, Point2l pt2, const void* color )
422+
// ICV_PUT_POINT Macro.
423+
424+
void FreeType2Impl::putPixel_8UC1_blend( Mat& _dst, const int _py, const int _px, const uint8_t *_col, const uint8_t alpha)
425+
{
426+
const int a = alpha;
427+
const int cb = _col[0];
428+
uint8_t* tptr = _dst.ptr<uint8_t>( _py, _px );
429+
430+
int _cb = static_cast<int>(tptr[0]);
431+
_cb += ((cb - _cb)*a + 127)>> 8;
432+
_cb += ((cb - _cb)*a + 127)>> 8;
433+
434+
tptr[0] = static_cast<uint8_t>(_cb);
435+
}
436+
437+
void FreeType2Impl::putPixel_8UC3_blend ( Mat& _dst, const int _py, const int _px, const uint8_t *_col, const uint8_t alpha)
438+
{
439+
const int a = alpha;
440+
const int cb = _col[0];
441+
const int cg = _col[1];
442+
const int cr = _col[2];
443+
uint8_t* tptr = _dst.ptr<uint8_t>( _py, _px );
444+
445+
int _cb = static_cast<int>(tptr[0]);
446+
_cb += ((cb - _cb)*a + 127)>> 8;
447+
_cb += ((cb - _cb)*a + 127)>> 8;
448+
449+
int _cg = static_cast<int>(tptr[1]);
450+
_cg += ((cg - _cg)*a + 127)>> 8;
451+
_cg += ((cg - _cg)*a + 127)>> 8;
452+
453+
int _cr = static_cast<int>(tptr[2]);
454+
_cr += ((cr - _cr)*a + 127)>> 8;
455+
_cr += ((cr - _cr)*a + 127)>> 8;
456+
457+
tptr[0] = static_cast<uint8_t>(_cb);
458+
tptr[1] = static_cast<uint8_t>(_cg);
459+
tptr[2] = static_cast<uint8_t>(_cr);
460+
}
461+
462+
void FreeType2Impl::putPixel_8UC4_blend( Mat& _dst, const int _py, const int _px, const uint8_t *_col, const uint8_t alpha)
463+
{
464+
const uint8_t a = alpha;
465+
const int cb = _col[0];
466+
const int cg = _col[1];
467+
const int cr = _col[2];
468+
const int ca = _col[3];
469+
uint8_t* tptr = _dst.ptr<uint8_t>( _py, _px );
470+
471+
int _cb = static_cast<int>(tptr[0]);
472+
_cb += ((cb - _cb)*a + 127)>> 8;
473+
_cb += ((cb - _cb)*a + 127)>> 8;
474+
475+
int _cg = static_cast<int>(tptr[1]);
476+
_cg += ((cg - _cg)*a + 127)>> 8;
477+
_cg += ((cg - _cg)*a + 127)>> 8;
478+
479+
int _cr = static_cast<int>(tptr[2]);
480+
_cr += ((cr - _cr)*a + 127)>> 8;
481+
_cr += ((cr - _cr)*a + 127)>> 8;
482+
483+
int _ca = static_cast<int>(tptr[3]);
484+
_ca += ((ca - _ca)*a + 127)>> 8;
485+
_ca += ((ca - _ca)*a + 127)>> 8;
486+
487+
tptr[0] = static_cast<uint8_t>(_cb);
488+
tptr[1] = static_cast<uint8_t>(_cg);
489+
tptr[2] = static_cast<uint8_t>(_cr);
490+
tptr[3] = static_cast<uint8_t>(_ca);
491+
}
492+
369493
void FreeType2Impl::putTextBitmapBlend(
370494
InputOutputArray _img, const String& _text, Point _org,
371495
int _fontHeight, Scalar _color,
372496
int _thickness, int _line_type, bool _bottomLeftOrigin )
373497
{
374498

375499
CV_Assert( _thickness < 0 );
376-
CV_Assert( _line_type == 16 );
500+
CV_Assert( _line_type == LINE_AA );
377501

378502
Mat dst = _img.getMat();
379503
hb_buffer_t *hb_buffer = hb_buffer_create ();
@@ -393,6 +517,17 @@ void FreeType2Impl::putTextBitmapBlend(
393517
_org.y -= _fontHeight;
394518
}
395519

520+
const uint8_t _colorUC8n[4] = {
521+
static_cast<uint8_t>(_color[0]),
522+
static_cast<uint8_t>(_color[1]),
523+
static_cast<uint8_t>(_color[2]),
524+
static_cast<uint8_t>(_color[3]) };
525+
526+
void (cv::freetype::FreeType2Impl::*putPixel)( Mat&, const int, const int, const uint8_t*, const uint8_t) =
527+
(_img.type() == CV_8UC4)?(&FreeType2Impl::putPixel_8UC4_blend):
528+
(_img.type() == CV_8UC3)?(&FreeType2Impl::putPixel_8UC3_blend):
529+
(&FreeType2Impl::putPixel_8UC1_blend);
530+
396531
for( unsigned int i = 0 ; i < textLen ; i ++ ){
397532
CV_Assert( !FT_Load_Glyph(mFace, info[i].codepoint, 0 ) );
398533
CV_Assert( !FT_Render_Glyph( mFace->glyph, FT_RENDER_MODE_NORMAL ) );
@@ -411,7 +546,7 @@ void FreeType2Impl::putTextBitmapBlend(
411546
}
412547

413548
for (int col = 0; col < bmp->pitch; col ++) {
414-
int cl = bmp->buffer[ row * bmp->pitch + col ];
549+
uint8_t cl = bmp->buffer[ row * bmp->pitch + col ];
415550
if ( cl == 0 ) {
416551
continue;
417552
}
@@ -424,12 +559,7 @@ void FreeType2Impl::putTextBitmapBlend(
424559
break;
425560
}
426561

427-
cv::Vec3b* ptr = dst.ptr<cv::Vec3b>( gPos.y + row , gPos.x + col);
428-
double blendAlpha = (double ) cl / 255.0;
429-
430-
(*ptr)[0] = (double) _color[0] * blendAlpha + (*ptr)[0] * (1.0 - blendAlpha );
431-
(*ptr)[1] = (double) _color[1] * blendAlpha + (*ptr)[1] * (1.0 - blendAlpha );
432-
(*ptr)[2] = (double) _color[2] * blendAlpha + (*ptr)[2] * (1.0 - blendAlpha );
562+
(this->*putPixel)( dst, gPos.y + row, gPos.x + col, _colorUC8n, cl );
433563
}
434564
}
435565
_org.x += ( mFace->glyph->advance.x ) >> 6;

0 commit comments

Comments
 (0)