由 freetype 渲染的意外字形距离

问题描述 投票:0回答:0

我使用 FreeType 库做了一些简单的文本渲染。使用字体“SourceHanSansCN-Medium”,字号为12像素。渲染结果字母之间的距离很奇怪。例如,如果我渲染“Wet”,字母

W
e
彼此远离,但
e
t
紧贴:

我尝试在 Inkscape 中使用相同的字体文件,正确的字母距离看起来好多了:

我对 FreeType 不是很熟悉,因为它是一个相当大的系统。是否有任何选项可以控制字形距离的行为?

是不是subpixel hinting造成的?如果是这样,是否可以仅在水平线上启用提示并在垂直线上禁用提示,从而保持水平距离?

下面附上文字渲染代码,和FreeType文档中的示例代码很像:

void MyPainter::paintOneGlyphImage( juce::Image& target, juce::Point< int > bitmap_offset, const FT_Bitmap& bitmap )
{

    if ( bitmap.pitch == 0 ) return;
    const int px_bits = (bitmap.pixel_mode == FT_PIXEL_MODE_LCD || bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) ? 3 : 1;

    int row_src_begin = std::max( -bitmap_offset.y, 0 );
    int row_src_end = std::min( target.getHeight() - bitmap_offset.y, bitmap.rows );

    int col_src_begin = std::max( -bitmap_offset.x, 0 );
    int col_src_end = std::min( target.getWidth() - bitmap_offset.x, bitmap.width / px_bits );

    for ( int row_src = row_src_begin; row_src < row_src_end; ++row_src )
    {
        int row_tgt = row_src + bitmap_offset.y;
        jassert( 0 <= row_tgt && row_tgt < target.getHeight() );
        const unsigned char* row_data = bitmap.buffer + row_src * bitmap.pitch;

        for ( int col_src = col_src_begin; col_src < col_src_end; col_src += 1 )
        {
            int col_tgt = col_src + bitmap_offset.x;
            jassert( 0 <= col_tgt && col_tgt < target.getWidth() );

            const unsigned char* px_data = row_data + col_src * px_bits;

            juce::Colour px_color;
            switch ( bitmap.pixel_mode )
            {
            case FT_PIXEL_MODE_MONO:
                if ( _glyph_bit_( bitmap, col_src, row_src ) )
                {
                    px_color = guts->mColor;
                    guts->mPixelNum++;
                }
                break;
            case FT_PIXEL_MODE_GRAY:
                px_color = guts->mColor.withAlpha( *px_data );  // withGamma( mColor.withAlpha( *px_data ), mGamma );
                if ( *px_data > 127 ) guts->mPixelNum++;
                break;
            case FT_PIXEL_MODE_LCD:
            case FT_PIXEL_MODE_LCD_V:
                px_color = getColorFromSubPixel( guts->mColor, px_data[0], px_data[1], px_data[2] );
                if ( (px_data[0] + px_data[1] + px_data[2]) / 3 > 127 ) guts->mPixelNum++;
                break;
            default:
                jassertfalse;
                px_color = guts->mColor;
            }
            target.setPixelAt( col_tgt, row_tgt, px_color );
        }
    }
}


void MyPainter::generateFontImage()
{
    // set font size
    // create target image object by font size and letter amount
    FT_Set_Char_Size( guts->mTypeface, 0, guts->m_font_size << 6, unsigned( 72 * guts->mDpiScale ), unsigned( 72 * guts->mDpiScale ) );
    auto scaled_metric = guts->mTypeface->size->metrics;
    juce::Image tmpImage( juce::Image::ARGB, int( scaled_metric.max_advance >> 6 ) * guts->mContent.length(), int( scaled_metric.height >> 6 ), true );

    auto chars = guts->mContent.toWideCharPointer();

    // step one by one for all letters
    FT_Vector pen = { 0, 0 };
    for ( int k = 0; k < guts->mContent.length(); ++k )
    {
        FT_Set_Transform( guts->mTypeface, nullptr, &pen );
        int re = FT_Load_Char( guts->mTypeface, FT_ULong( chars[k] ), FT_LOAD_RENDER | ((!guts->mHinting) ? FT_LOAD_NO_HINTING : 0) | int( guts->mRenderMode ) );
        if ( re != FT_Err_Ok )  // FT_LOAD_NO_HINTING
        {
            std::cout << "ERROR::FREETYTPE: Failed to load Glyph: " << re << std::endl;
            jassertfalse;
        }

        auto* glyph = guts->mTypeface->glyph;
        int paint_x = glyph->bitmap_left;
        int paint_y = int( (scaled_metric.ascender >> 6) - glyph->bitmap_top );

        paintOneGlyphImage( tmpImage, { paint_x, paint_y }, glyph->bitmap );

        pen.x += glyph->advance.x;
        pen.y += glyph->advance.y;
    }
}
freetype text-rendering
© www.soinside.com 2019 - 2024. All rights reserved.