freetype
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [ft] Rendering Arabic


From: Tim Orton
Subject: Re: [ft] Rendering Arabic
Date: Thu, 14 Nov 2019 11:21:01 +0000

Hello,

Thanks for your response, but unfortunately I'm not getting the results I
expected:
1) I am no longer getting any Arabic characters, and the English characters
are all wrong too. (Admittedly I was expecting the English text to go wrong
because I am hard-coding libraqm to use Arabic).
 - Instead of Arabic characters, I am getting mostly symbols, blank spaces
and occasional capital English letters. Instead of English text, I am only
getting Capital English letters, and the wrong letters at that.
2) The character metrics libraqm returns are unusable - for example the x
advance libraqm gives for most characters is between 200 and 900,
whereas the x advance I was getting from freetype is usually less than 10 -
so the result is that all the characters are incredibly far spread out.

I am getting the glyph data from libraqm in my text rendering method
(below), and within that method, I am calling a method called
GetGlyphPixmap (further below) which calls FT_Load_Char.

My text rendering method looks like this at the moment:

int TextManager::RenderText(const std::string& text,
    const FontType font_type, const FontStyle font_style,
    const int x, const int y, const int max_width, const Vector4& colour)
{
const unsigned int font_slot = font_style & FONT_STYLE_MASK;
Font* font = m_font_grid[font_type][font_slot];
static const float wfactor = 1.0f / float(TEXTURE_WIDTH);
static const float hfactor = 1.0f / float(TEXTURE_HEIGHT);
int width = 0;
size_t position = 0;
int origin_x = x;
const int origin_y = y;

const auto rq = raqm_create ();
if (nullptr == rq ||
!raqm_set_text_utf8(rq, text.c_str(), strlen(text.c_str())) ||
   !raqm_set_freetype_face(rq, font->ftface) ||
   !raqm_set_par_direction(rq, RAQM_DIRECTION_RTL) ||
   !raqm_set_language(rq, "ar", 0, strlen(text.c_str())) ||
   !raqm_layout(rq))
{
return 0;
}
size_t count;
raqm_glyph_t *glyphs = raqm_get_glyphs (rq, &count);

for (size_t i = 0; i < count; ++i)
{
CharacterInfo* info = new CharacterInfo();
info->advance   = glyphs[i].x_advance;
info->bearing_x = glyphs[i].x_offset;
info->bearing_y = glyphs[i].y_offset;
info->texture_x = -1;// Mark as not having been rendered to a texture yet
info->texture_y = -1;// Mark as not having been rendered to a texture yet

if ((max_width > 0.0f) && ((width + info->advance) > max_width))
{
// Bail if this character won't fit (and we asked for width to be checked)
break;
}
if (info->texture_x < 0)
{
// This character has not been rendered to a texture yet...
// Get an RGBA pixmap for the character's glyph
// Row 0 of the generated pixmap represents the topmost part of the glyph
// Column 0 of the generated pixmap represents the leftmost part of the
glyph
if (!GetGlyphPixmap(glyphs[i], BOX_SIZE, BOX_SIZE, m_rgba_pixmap_buffer))
continue;
// Upload the RGBA pixmap to the texture at the current slot
m_texture_manager->ActivateTexture(m_current_texture);
m_texture_manager->UpdateTexture(m_current_texture_x, m_current_texture_y,
BOX_SIZE, BOX_SIZE, m_rgba_pixmap_buffer);
// Store the details of the texture and the position used to store the glyph
info->texture = m_current_texture;
info->texture_x = m_current_texture_x;
info->texture_y = m_current_texture_y;
// Get ready with a new slot
m_current_texture_x += BOX_SIZE;
if (m_current_texture_x == TEXTURE_WIDTH)
{
m_current_texture_x = 0;
m_current_texture_y += BOX_SIZE;
if (m_current_texture_y == TEXTURE_HEIGHT)
{
// The current texture is full, create a new one
m_texture_manager->CreateTexture(TEXTURE_WIDTH, TEXTURE_HEIGHT,
m_current_texture);
m_textures.push_back(m_current_texture);
m_current_texture_y = 0;
}
}
}
const float left = (float)(origin_x + info->bearing_x);
const float top  = (float)(origin_y + font->leading + (font->ascent -
info->bearing_y));
FloatRectangle coords(left, top, left + BOX_SIZE, top + BOX_SIZE);

// slight texcoord corrections for bilinear filtering
const bool needsOffset(m_rectangle_batcher->GetRTTFrame() ||
!ApproxEquals(m_rectangle_batcher->GetPointPixelScale(), 1.0f));
        const float beginOffset = needsOffset ? -1.0f : 0.0001f;
        const float endOffset   = needsOffset ? -0.5f : -0.0001f;
const float tleft       = (info->texture_x +beginOffset)*wfactor;
const float ttop        = (info->texture_y +beginOffset)*hfactor;
const float tright      = (info->texture_x + BOX_SIZE +endOffset)*wfactor;
const float tbottom     = (info->texture_y + BOX_SIZE +endOffset)*hfactor;
FloatRectangle texcoords(tleft, ttop, tright, tbottom);

m_rectangle_batcher->AddRectangle(coords, texcoords, colour, info->texture);
origin_x += info->advance;
width    += info->advance;
}
if ((font_style & FONT_STYLE_UNDERLINE) && (width > 0))
{
const float x1 = (float)(x);
const float x2 = (float)(x + width);
const float py = (float)(y + font->leading + font->ascent - font->descent)
- 0.5f;
const Vector2 start(x1, py);
const Vector2 end(x2, py);
m_line_batcher->AddLine(start, end, colour, colour, 1.0f);
}

raqm_destroy(rq);
return width;
}

And the method GetGlyphPixmap() looks like this:

// 4 bytes (R, G, B, A) per pixel in the pixmap buffer
// Row 0 of the generated RGBA pixmap represents the topmost part of the
glyph
// Column 0 of the generated RGBA pixmap represents the leftmost part of
the glyph
// Parts of the pixmap extending beyond the dimensions of the glyph will be
transparent
bool TextManager::GetGlyphPixmap(raqm_glyph_t& glyph,
                                 const int rgba_pixmap_width,
                                 const int rgba_pixmap_height,
                                 unsigned char* rgba_pixmap_buffer)
{
FT_Activate_Size( glyph.ftface->size );
FT_Load_Char( glyph.ftface, glyph.index, FT_LOAD_NO_BITMAP );

// Create an anti-aliased alpha pixmap for the glyph
// Each pixel in the pixmap represents an alpha value between 0 and 255
const int error = FT_Render_Glyph(glyph.ftface->glyph,
FT_RENDER_MODE_NORMAL);
if (error != 0)
{
// Fail
return false;
}
// Row 0 of the alpha pixmap represents the topmost part of the glyph
FT_Bitmap& alpha_pixmap = glyph.ftface->glyph->bitmap;
const int rows = (int)alpha_pixmap.rows;
const int columns = (int)alpha_pixmap.width;

// Default the RGBA pixmap to be all transparent
const int bytes_per_row = rgba_pixmap_width * 4;
memset(rgba_pixmap_buffer, 0, rgba_pixmap_height * bytes_per_row);

// Put the alpha values from the alpha pixmap into the RGBA pixmap
unsigned char* p = alpha_pixmap.buffer;
for (int row=0; row < rows; ++row)
{
for (int column=0; column < columns; ++column)
{
const unsigned char alpha = p[column];
unsigned char* base = rgba_pixmap_buffer + (row * bytes_per_row) + (column
* 4);
*base++ = 255;   // Red
*base++ = 255;   // Green
*base++ = 255;   // Blue
*base++ = alpha; // Alpha
}
// Move to next row of the alpha pixmap
p += alpha_pixmap.pitch;
}

// Success
return true;
}

Any help would be greatly appreciated,

Thanks,
Tim Orton

On Mon, 4 Nov 2019 at 10:30, Simon Cozens <address@hidden> wrote:

> On 04/11/2019 10:19, Tim Orton wrote:
> > I can render simple text with freetype, and I have just started using
> > libraqm for glyph shaping, for complex text.
> > Basically, I can get information from libraqm about the glyph index,
> > offset, advance, etc... but I don't know how to use that to get the
> > bitmap of the glyph to render.
> > Sorry this question is more about libraqm than freetype, but I can't
> > find another community to ask this question.
>
> Hi again Tim,
>
>         Actually, this *is* a freetype question! There is a division of
> labour
> between the shaping engine and the glyph rendering engine. Shaping
> (libraqm) tells you what glyph you want and where to put it. Rendering
> tells you what it looks like.
>
>         So, you've already got the glyph selection and positioning
> information
> from libraqm. That's step one. Step two depends on a bit on what kind of
> canvas you're rendering onto, but the basic idea is that you can now
> (using freetype) select the glyph from the font by ID:
>
>         FT_Load_Char( face, glyph_index, FT_LOAD_DEFAULT );
>
> and then you can get its bitmap as you normally would and position it
> onto your canvas in the place that libraqm told you to put it.
>
> Hope that is clearer - if not, keep asking (you may need to show some
> code)!
>
> Simon
>


-- 

address@hidden
+44 (0)1223 421034

*O**SO*
*SIM*

learning brought to life

www.ososim.com


Ososim Limited, Registered in England and Wales No. 06608651. Registered
Office: St John's Innovation Centre, Cowley Road, Cambridge, CB4 0WS


reply via email to

[Prev in Thread] Current Thread [Next in Thread]