void SkScalerContext_FreeType_Base::emboldenOutline(FT_Face face, FT_Outline* outline) { FT_Pos strength; strength = FT_MulFix(face->units_per_EM, face->size->metrics.y_scale) / 24; FT_Outline_Embolden(outline, strength); }
void FT_BOLD(FT_GlyphSlot slot) { FT_Face face = slot->face; FT_Pos amount; if(slot->format != FT_GLYPH_FORMAT_OUTLINE) return; /* some reasonable strength */ amount = FT_MulFix(face->units_per_EM, face->size->metrics.y_scale ) / 18; (void)FT_Outline_Embolden( &slot->outline, amount); if ( slot->advance.x ) slot->advance.x += amount; if ( slot->advance.y ) slot->advance.y += amount; slot->metrics.width += amount; slot->metrics.height += amount; slot->metrics.horiBearingY += amount; slot->metrics.horiAdvance += amount; slot->metrics.vertBearingX -= amount / 2; slot->metrics.vertBearingY += amount; slot->metrics.vertAdvance += amount; }
FT_GlyphSlot_Embolden( FT_GlyphSlot slot ) { FT_Library library = slot->library; FT_Face face = FT_SLOT_FACE( slot ); FT_Error error; FT_Pos xstr, ystr; if ( slot->format != FT_GLYPH_FORMAT_OUTLINE && slot->format != FT_GLYPH_FORMAT_BITMAP ) return; /* some reasonable strength */ xstr = FT_MulFix( face->units_per_EM, face->size->metrics.y_scale ) / 35; ystr = xstr; if ( slot->format == FT_GLYPH_FORMAT_OUTLINE ) { error = FT_Outline_Embolden( &slot->outline, xstr ); /* ignore error */ /* this is more than enough for most glyphs; if you need accurate */ /* values, you have to call FT_Outline_Get_CBox */ xstr = xstr * 2; ystr = xstr; } else if ( slot->format == FT_GLYPH_FORMAT_BITMAP ) { xstr = FT_PIX_FLOOR( xstr ); if ( xstr == 0 ) xstr = 1 << 6; ystr = FT_PIX_FLOOR( ystr ); error = FT_GlyphSlot_Own_Bitmap( slot ); if ( error ) return; error = FT_Bitmap_Embolden( library, &slot->bitmap, xstr, ystr ); if ( error ) return; } if ( slot->advance.x ) slot->advance.x += xstr; if ( slot->advance.y ) slot->advance.y += ystr; slot->metrics.width += xstr; slot->metrics.height += ystr; slot->metrics.horiBearingY += ystr; slot->metrics.horiAdvance += xstr; slot->metrics.vertBearingX -= xstr / 2; slot->metrics.vertBearingY += ystr; slot->metrics.vertAdvance += ystr; if ( slot->format == FT_GLYPH_FORMAT_BITMAP ) slot->bitmap_top += ystr >> 6; }
const bool GDI2FT_RENDERER::generate_outline_glyph( FT_Glyph* glyph, WORD glyph_index, const FTC_Scaler scaler, FT_F26Dot6 embolden, bool is_italic ) const /* -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- */ { FT_Glyph cached_glyph; { GDI2FT_MUTEX mutex( GDI2FT_MUTEX::MUTEX_FREETYPE ); if( FTC_ImageCache_LookupScaler( ft_glyph_cache, scaler, FT_LOAD_NO_BITMAP | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_CROP_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH | FT_LOAD_TARGET_LIGHT, glyph_index, &cached_glyph, NULL ) != 0 ) return NULL; } if( cached_glyph->format != FT_GLYPH_FORMAT_OUTLINE ) return NULL; const bool oblique = ( ( context->outline_metrics->otmTextMetrics.tmItalic != 0 ) && !is_italic ); if( oblique || embolden ) { FT_Glyph_Copy( cached_glyph, glyph ); FT_Outline* glyph_outline = &( reinterpret_cast<FT_OutlineGlyph>( *glyph )->outline ); if( oblique ) { FT_Matrix oblique_mat = { static_cast<FT_Pos>( 65536 ), static_cast<FT_Pos>( 19661 ), 0, static_cast<FT_Pos>( 65536 ) }; FT_Outline_Transform( glyph_outline, &oblique_mat ); } if( embolden ) FT_Outline_Embolden( glyph_outline, embolden ); return true; } else *glyph = cached_glyph; return false; }
/* * Class: FreetypeFont * Method: loadGlyph0 * Signature: (JI)V */ JNIEXPORT void JNICALL Java_sage_FreetypeFont_loadGlyph0 (JNIEnv *env, jobject jo, jlong fontPtr, jint glyphCode) { //FT_Face face = (FT_Face) facePtr; FTDataStruct* fontData = (FTDataStruct*)(intptr_t) fontPtr; FT_Activate_Size(fontData->sizePtr); int error = FT_Load_Glyph(fontData->facePtr, glyphCode, FT_LOAD_FORCE_AUTOHINT); if ((fontData->style & FT_STYLE_FLAG_BOLD) != 0) { // Apply bold effect if (fontData->facePtr->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { /* some reasonable strength */ FT_Pos strength = FT_MulFix(fontData->facePtr->units_per_EM, fontData->facePtr->size->metrics.y_scale ) / 42; FT_BBox bbox_before, bbox_after; // The bounding box code was what XBMC was using to do this calculation; but the // examples in the freetype library use the *4 math below which then doesn't clip // the text when we render it. // FT_Outline_Get_CBox(&fontData->facePtr->glyph->outline, &bbox_before); FT_Outline_Embolden(&fontData->facePtr->glyph->outline, strength); // ignore error // FT_Outline_Get_CBox(&fontData->facePtr->glyph->outline, &bbox_after); // FT_Pos dx = bbox_after.xMax - bbox_before.xMax; // FT_Pos dy = bbox_after.yMax - bbox_before.yMax; FT_Pos dx = strength * 4; FT_Pos dy = dx; if (fontData->facePtr->glyph->advance.x) fontData->facePtr->glyph->advance.x += dx; if (fontData->facePtr->glyph->advance.y) fontData->facePtr->glyph->advance.y += dy; fontData->facePtr->glyph->metrics.width += dx; fontData->facePtr->glyph->metrics.height += dy; fontData->facePtr->glyph->metrics.horiBearingY += dy; fontData->facePtr->glyph->metrics.horiAdvance += dx; fontData->facePtr->glyph->metrics.vertBearingX -= dx / 2; fontData->facePtr->glyph->metrics.vertBearingY += dy; fontData->facePtr->glyph->metrics.vertAdvance += dy; } } if ((fontData->style & FT_STYLE_FLAG_ITALIC) != 0) { // Apply italics effect if (fontData->facePtr->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { /* For italic, simply apply a shear transform, with an angle */ /* of about 12 degrees. */ FT_Matrix transform; transform.xx = 0x10000L; transform.yx = 0x00000L; transform.xy = 0x06000L; transform.yy = 0x10000L; FT_BBox bbox_before, bbox_after; FT_Outline_Get_CBox(&fontData->facePtr->glyph->outline, &bbox_before); FT_Outline_Transform(&fontData->facePtr->glyph->outline, &transform); FT_Outline_Get_CBox(&fontData->facePtr->glyph->outline, &bbox_after); FT_Pos dx = bbox_after.xMax - bbox_before.xMax; FT_Pos dy = bbox_after.yMax - bbox_before.yMax; fontData->facePtr->glyph->metrics.width += dx; fontData->facePtr->glyph->metrics.height += dy; } } }
bool Font::insertGlyph(Char character) { if(!m_size) { insertPlaceholderGlyph(character); return false; } FT_UInt glyphIndex = FT_Get_Char_Index(m_size->face, character); if(!glyphIndex) { insertPlaceholderGlyph(character); return false; } FT_Int32 flags = FT_LOAD_TARGET_LIGHT; if(m_info.weight != 0) { flags |= FT_LOAD_FORCE_AUTOHINT; } if(FT_Load_Glyph(m_size->face, glyphIndex, flags)) { insertPlaceholderGlyph(character); return false; } FT_GlyphSlot ftGlyph = m_size->face->glyph; if(m_info.weight != 0 && ftGlyph->format == FT_GLYPH_FORMAT_OUTLINE) { FT_Pos strength = FT_MulFix(m_size->face->units_per_EM, m_size->metrics.y_scale) / 24 * m_info.weight / 4; FT_Outline_Embolden(&ftGlyph->outline, strength); } if(FT_Render_Glyph(ftGlyph, FT_RENDER_MODE_NORMAL)) { insertPlaceholderGlyph(character); return false; } // Fill in info for this glyph. Glyph & glyph = m_glyphs[character]; glyph.index = glyphIndex; glyph.size.x = ftGlyph->bitmap.width; glyph.size.y = ftGlyph->bitmap.rows; glyph.advance.x = float(ftGlyph->linearHoriAdvance) / 65536.f; glyph.advance.y = float(ftGlyph->linearVertAdvance) / 65536.f; glyph.lsb_delta = ftGlyph->lsb_delta; glyph.rsb_delta = ftGlyph->rsb_delta; glyph.draw_offset.x = ftGlyph->bitmap_left; glyph.draw_offset.y = ftGlyph->bitmap_top - ftGlyph->bitmap.rows; glyph.uv_start = Vec2f(0.f); glyph.uv_end = Vec2f(0.f); glyph.texture = 0; // Some glyphs like spaces have a size of 0... if(glyph.size.x != 0 && glyph.size.y != 0) { Image imgGlyph; imgGlyph.create(size_t(glyph.size.x), size_t(glyph.size.y), Image::Format_A8); FT_Bitmap & srcBitmap = ftGlyph->bitmap; arx_assert(srcBitmap.pitch >= 0); arx_assert(unsigned(srcBitmap.pitch) == unsigned(srcBitmap.width)); // Copy pixels unsigned char * src = srcBitmap.buffer; unsigned char * dst = imgGlyph.getData(); memcpy(dst, src, glyph.size.x * glyph.size.y); Vec2i offset; if(!m_textures->insertImage(imgGlyph, glyph.texture, offset)) { LogWarning << "Could not upload glyph for character U+" << std::hex << character << " (" << util::encode<util::UTF8>(character) << ") in font " << m_info.name; insertPlaceholderGlyph(character); return false; } // Compute UV mapping for each glyph. const float textureSize = float(m_textures->getTextureSize()); glyph.uv_start = Vec2f(offset) / Vec2f(textureSize); glyph.uv_end = Vec2f(offset + glyph.size) / Vec2f(textureSize); } return true; }
/** * Renders or Measures some Text * * @param aContext void context * @param aBitmap pointer to bitmap ( if not provided will just measure ) * @param aBitmapWidth width of the provided bitmap * @param aBitmapHeight height of the provided bitmap * @param aHarfBuzz Harfbuzz structure containing glyph indices * @param aIndex start with this character index * @param aCount render / measure aCount characters * @param aPen_x pointer to pen position that will be updated ( optional ) * @param aPen_y pointer to pen position that will be updated ( optional ) * @param aBBox pointer to Bounding Box that will be updated ( optional ) * @param resetBox if non-zero´Bounding Box will be reset before accumulating * @param isArabic if non-zero will treat as right to left * @param outlineStage 0 = draw ouside, 1 = draw inside of synth outline style */ static int PalFont_RenderHarfBuzz( void *aContext, unsigned int *aBitmap, int aBitmapWidth, int aBitmapHeight, HarfBuzz_t *aHarfBuzz, int aIndex, int aLength, int *aPen_x, int *aPen_y, PalFont_BBox_t*aBBox, int resetBox, int isArabic, int outlineStage ) { printf("PalFont_RenderHarfBuzz: context is %p\n", aContext); ft_Context_t *ftContext = FT_CONTEXT( aContext ); FT_GlyphSlot slot; PalFont_BBox_t theBBox; unsigned int i, j = outlineStage, k; int thePen_x = 0; int thePen_y = 0; FT_UInt aGlyphIndex = 0; FT_UInt aPrevGlyphIndex = 0; if ( ftContext == NULL ) { return -1; } if ( aPen_x != NULL ) { thePen_x = *aPen_x; } if ( aPen_y != NULL ) { thePen_y = *aPen_y; } slot = ftContext->m_face->glyph; if ( resetBox != 0 ) { /* make an invalid bounding box to start with */ theBBox.xMin = INT_MAX; theBBox.yMin = INT_MAX; theBBox.xMax = INT_MIN; theBBox.yMax = INT_MIN; } for ( i = 0; i < aHarfBuzz->n_Chars; thePen_x += ( int )( ( ( double )ftContext->m_Matrix.xx * ( double )slot->advance.x + ( double )ftContext->m_Matrix.xy * ( double )slot->advance.y ) / 65536.0 ), thePen_y += ( int )( ( ( double )ftContext->m_Matrix.yy * ( double )slot->advance.y + ( double )ftContext->m_Matrix.yx * ( double )slot->advance.x ) / 65536.0 ), aPrevGlyphIndex = aGlyphIndex, i++ ) { FT_Glyph aGlyph; int y; int x; k = isArabic ? ( aHarfBuzz->n_Chars - i - 1 ) : i; aGlyphIndex = aHarfBuzz->out_glyphs[ k ]; /* apply kerning if there is any */ if ( aPrevGlyphIndex != 0 && aGlyphIndex != 0 ) { FT_Vector delta; /* ignore errors */ if ( FT_Get_Kerning( ftContext->m_face, aPrevGlyphIndex, aGlyphIndex, FT_KERNING_DEFAULT, &delta ) == 0 ) { thePen_x += ( int )( ( ( double )ftContext->m_Matrix.xx * ( double )delta.x ) / ( 65536.0 ) ); } } /* load glyph image into the slot (erase previous one) */ if ( FT_Load_Glyph( ftContext->m_face, aGlyphIndex, FT_LOAD_DEFAULT ) != 0 ) { continue; /* ignore errors */ } // if we are synthesising italic then shear outline if ( ftContext->m_Synth & SynthItalic ) { FT_Matrix m; m.xx = 65536; m.yy = 65536; m.xy = 19660; m.yx = 0; FT_Outline_Transform( &slot->outline, &m ); } /* if we are doing first pass of two color then fatten */ if ( ( ftContext->m_Synth & SynthTwoColor ) && j == 0 ) { if ( FT_Outline_Embolden( &slot->outline, ftContext->m_StrokeWidth << 2 ) != 0 ) { continue; } } // if we are doing second pass of two color then translate if ( ( ftContext->m_Synth & SynthTwoColor ) && j == 1 ) { FT_Outline_Translate( &slot->outline, ftContext->m_StrokeWidth * 2, ftContext->m_StrokeWidth * 2 ); } /* if we are synthesising bold then perform fattening operation */ if ( ftContext->m_Synth & SynthBold ) { if ( FT_Outline_Embolden( &slot->outline, ftContext->m_Boldness << 1 ) != 0 ) { continue; } } /* if we are synthesising stroke font then stroke it */ if ( ftContext->m_Synth & SynthStroke ) { if ( ft_OutlineStroke( ftContext->m_library, &slot->outline, ftContext->m_StrokeWidth ) != 0 ) { continue; } } FT_Outline_Transform( &slot->outline, &ftContext->m_Matrix ); /* if bounding box is supplied then accumulate the glyphs bounding boxes */ if ( aBBox != NULL ) { FT_BBox theCBox; FT_Outline_Get_CBox( &slot->outline, &theCBox ); theCBox.xMin += thePen_x; theCBox.xMax += thePen_x; theCBox.yMin += thePen_y; theCBox.yMax += thePen_y; if ( theCBox.xMin < theBBox.xMin ) { theBBox.xMin = theCBox.xMin; } if ( theCBox.yMin < theBBox.yMin ) { theBBox.yMin = theCBox.yMin; } if ( theCBox.xMax > theBBox.xMax ) { theBBox.xMax = theCBox.xMax; } if ( theCBox.yMax > theBBox.yMax ) { theBBox.yMax = theCBox.yMax; } } /* if bitmap is supplied then draw into it */ if ( aBitmap == NULL ) { continue; } /* convert to an anti-aliased bitmap */ if ( FT_Render_Glyph( slot, FT_RENDER_MODE_NORMAL ) != 0 ) continue; /* render the bitmap... */ for ( y = 0; y < slot->bitmap.rows; y++ ) { for ( x = 0; x < slot->bitmap.width; x++ ) { unsigned int by = (-(thePen_y>>6)-slot->bitmap_top+y+aBitmapHeight); unsigned int bx = (thePen_x>>6)+slot->bitmap_left+x; if ( bx < aBitmapWidth && by < aBitmapHeight ) { unsigned int *pixel = &aBitmap[ by * aBitmapWidth + bx ]; *pixel = blend( *pixel, j ? ftContext->m_fontColor : ftContext->m_fontColorTwo, slot->bitmap.buffer[y*slot->bitmap.pitch+x], j ? ftContext->m_Alpha1 : ftContext->m_Alpha2 ); } } } } if ( aBBox != NULL ) { *aBBox = theBBox; } if ( aPen_x != NULL ) { *aPen_x = thePen_x; } if ( aPen_y != NULL ) { *aPen_y = thePen_y; } return 0; }
bool ExtractGlyph(unsigned int characterSize, char32_t character, UInt32 style, FontGlyph* dst) override { #ifdef NAZARA_DEBUG if (!dst) { NazaraError("Glyph destination cannot be null"); return false; } #endif SetCharacterSize(characterSize); if (FT_Load_Char(m_face, character, FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL) != 0) { NazaraError("Failed to load character"); return false; } FT_GlyphSlot& glyph = m_face->glyph; const FT_Pos boldStrength = 2 << 6; bool embolden = (style & TextStyle_Bold); dst->advance = (embolden) ? boldStrength >> 6 : 0; if (embolden && glyph->format == FT_GLYPH_FORMAT_OUTLINE) { // http://www.freetype.org/freetype2/docs/reference/ft2-outline_processing.html#FT_Outline_Embolden FT_Outline_Embolden(&glyph->outline, boldStrength); embolden = false; } // http://www.freetype.org/freetype2/docs/reference/ft2-glyph_management.html#FT_Glyph_To_Bitmap // Conversion du glyphe vers le format bitmap // Cette fonction ne fait rien dans le cas où le glyphe est déjà un bitmap if (FT_Render_Glyph(glyph, FT_RENDER_MODE_NORMAL) != 0) { NazaraError("Failed to convert glyph to bitmap"); return false; } // Dans le cas où nous voulons des caractères gras mais que nous n'avons pas pu agir plus tôt // nous demandons à FreeType d'agir directement sur le bitmap généré if (embolden) { // http://www.freetype.org/freetype2/docs/reference/ft2-bitmap_handling.html#FT_Bitmap_Embolden // "If you want to embolden the bitmap owned by a FT_GlyphSlot_Rec, you should call FT_GlyphSlot_Own_Bitmap on the slot first" FT_GlyphSlot_Own_Bitmap(glyph); FT_Bitmap_Embolden(s_library, &glyph->bitmap, boldStrength, boldStrength); } dst->advance += glyph->metrics.horiAdvance >> 6; dst->aabb.x = glyph->metrics.horiBearingX >> 6; dst->aabb.y = -(glyph->metrics.horiBearingY >> 6); // Inversion du repère dst->aabb.width = glyph->metrics.width >> 6; dst->aabb.height = glyph->metrics.height >> 6; unsigned int width = glyph->bitmap.width; unsigned int height = glyph->bitmap.rows; if (width > 0 && height > 0) { dst->image.Create(ImageType_2D, PixelFormatType_A8, width, height); UInt8* pixels = dst->image.GetPixels(); const UInt8* data = glyph->bitmap.buffer; // Selon la documentation FreeType, le glyphe peut être encodé en format A8 (huit bits d'alpha par pixel) // ou au format A1 (un bit d'alpha par pixel). // Cependant dans un cas comme dans l'autre, il nous faut gérer le pitch (les données peuvent ne pas être contigues) // ainsi que le padding dans le cas du format A1 (Chaque ligne prends un nombre fixe d'octets) if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { // Format A1 for (unsigned int y = 0; y < height; ++y) { for (unsigned int x = 0; x < width; ++x) *pixels++ = (data[x/8] & ((1 << (7 - x%8)) ? 255 : 0)); data += glyph->bitmap.pitch; } } else { // Format A8 if (glyph->bitmap.pitch == static_cast<int>(width*sizeof(UInt8))) // Pouvons-nous copier directement ? dst->image.Update(glyph->bitmap.buffer); else { for (unsigned int y = 0; y < height; ++y) { std::memcpy(pixels, data, width*sizeof(UInt8)); data += glyph->bitmap.pitch; pixels += width*sizeof(UInt8); } } } } else dst->image.Destroy(); // On s'assure que l'image ne contient alors rien return true; }
FT_Error ft_OutlineStroke( FT_Library library, FT_Outline *Outline, int Thickness ) { FT_Error err = 0; FT_Outline OutlineReversed; FT_Outline OutlineFattened; FT_Outline OutlineStroke; if ( Outline == NULL ) { goto failure; } err = FT_Outline_New( library, Outline->n_points, Outline->n_contours, &OutlineReversed ); if ( err != 0 ) { goto failure; } err = FT_Outline_New( library, Outline->n_points, Outline->n_contours, &OutlineFattened ); if ( err != 0 ) { goto failure; } err = FT_Outline_Copy( Outline, &OutlineReversed ); if ( err != 0 ) { goto failure; } err = FT_Outline_Copy( Outline, &OutlineFattened ); if ( err != 0 ) { goto failure; } err = FT_Outline_New( library, Outline->n_points * 2, Outline->n_contours * 2, &OutlineStroke ); if ( err != 0 ) { goto failure; } /* Perform fattening operation */ err = FT_Outline_Embolden( &OutlineFattened, Thickness << 1 ); if ( err != 0 ) { goto failure; } /* Perform reversal operation */ ft_OutlineReverse( Outline, &OutlineReversed ); FT_Outline_Translate( &OutlineReversed, Thickness, Thickness ); /* Merge outlines */ ft_OutlineMerge( &OutlineFattened, &OutlineReversed, &OutlineStroke ); /* delete temporary and input outline */ err = FT_Outline_Done( library, &OutlineReversed ); if ( err != 0 ) { goto failure; } err = FT_Outline_Done( library, &OutlineFattened ); if ( err != 0 ) { goto failure; } err = FT_Outline_Done( library, Outline ); if ( err != 0 ) { goto failure; } /* finally copy the outline - its not clear from ft docs if this does the right thing but i _think_ its correct */ memcpy( Outline, &OutlineStroke, sizeof( FT_Outline ) ); return 0; failure: return err; }
void FontRenderer::rasterize() { clear_bitmaps(); if (!m_ft_face) { return; } qDebug() << " begin rasterize_font "; if (m_config->italic()!=0) { FT_Matrix matrix; const float angle = (-M_PI*m_config->italic()) / 180.0f; matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L ); matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L ); matrix.yx = (FT_Fixed)( 0/*sin( angle )*/ * 0x10000L ); matrix.yy = (FT_Fixed)( 1/*cos( angle )*/ * 0x10000L ); FT_Set_Transform(m_ft_face,&matrix,0); } else { FT_Set_Transform(m_ft_face,0,0); } /// fill metrics if (FT_IS_SCALABLE(m_ft_face)) { m_rendered.metrics.ascender = m_ft_face->size->metrics.ascender / 64; m_rendered.metrics.descender = m_ft_face->size->metrics.descender/ 64; m_rendered.metrics.height = m_ft_face->size->metrics.height/ 64; } else { m_rendered.metrics.ascender = m_ft_face->ascender; m_rendered.metrics.descender = m_ft_face->descender; m_rendered.metrics.height = m_ft_face->height; } bool use_kerning = FT_HAS_KERNING( m_ft_face ); const ushort* chars = m_config->characters().utf16(); size_t amount = 0; while (chars[amount]!=0) amount++; int error = 0; for (size_t i=0;i<amount;i++) { int glyph_index = FT_Get_Char_Index( m_ft_face, chars[i] ); if (glyph_index==0 && !m_config->renderMissing()) continue; FT_Int32 flags = FT_LOAD_DEFAULT; if (!m_config->antialiased()) { flags = flags | FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO; } else { flags = flags | FT_LOAD_TARGET_NORMAL; } switch (m_config->hinting()) { case FontConfig::HintingDisable: flags = flags | FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT; break; case FontConfig::HintingForceFreetypeAuto: flags = flags | FT_LOAD_FORCE_AUTOHINT; break; case FontConfig::HintingDisableFreetypeAuto: flags = flags | FT_LOAD_NO_AUTOHINT; break; default: break; } error = FT_Load_Glyph( m_ft_face, glyph_index, flags ); if ( error ) continue; if (m_config->bold()!=0) { FT_Pos strength = m_config->size()*m_config->bold(); if ( m_ft_face->glyph->format == FT_GLYPH_FORMAT_OUTLINE ) FT_Outline_Embolden( &m_ft_face->glyph->outline, strength ); } if (m_ft_face->glyph->format!=FT_GLYPH_FORMAT_BITMAP) { error = FT_Render_Glyph( m_ft_face->glyph, m_config->antialiased() ? FT_RENDER_MODE_NORMAL:FT_RENDER_MODE_MONO ); } if ( error ) continue; if (append_bitmap(chars[i])) { if (use_kerning) append_kerning(chars[i],chars,amount); } } imagesChanged(m_chars); imagesChanged(); }
bool gfx_font_adapter::prepare_glyph(unsigned int code) { if (m_impl->font) { m_impl->cur_glyph_index = FT_Get_Char_Index(m_impl->font, code); int error = FT_Load_Glyph(m_impl->font, m_impl->cur_glyph_index, m_impl->hinting ? FT_LOAD_DEFAULT : FT_LOAD_NO_HINTING); bool is_sys_bitmap = false; if (m_impl->font->glyph->format == FT_GLYPH_FORMAT_BITMAP) is_sys_bitmap = true; if (error == 0) { if (m_impl->antialias && !is_sys_bitmap) { if (m_impl->weight == 500) { int strength = 1 << 5; FT_Outline_Embolden(&(m_impl->font->glyph->outline), strength); } else if (m_impl->weight == 700) { int strength = 1 << 6; FT_Outline_Embolden(&(m_impl->font->glyph->outline), strength); } else if (m_impl->weight == 900) { int strength = 1 << 7; FT_Outline_Embolden(&(m_impl->font->glyph->outline), strength); } // outline text m_impl->cur_data_type = glyph_type_outline; m_impl->cur_font_path.remove_all(); if (decompose_ft_outline(m_impl->font->glyph->outline, m_impl->flip_y, m_impl->matrix, m_impl->cur_font_path)) { m_impl->cur_bound_rect = get_bounding_rect(m_impl->cur_font_path); m_impl->cur_data_size = m_impl->cur_font_path.total_byte_size()+sizeof(unsigned int);//count data m_impl->cur_advance_x = FLT_TO_SCALAR(int26p6_to_flt(m_impl->font->glyph->advance.x)); m_impl->cur_advance_y = FLT_TO_SCALAR(int26p6_to_flt(m_impl->font->glyph->advance.y)); m_impl->matrix.transform(&m_impl->cur_advance_x, &m_impl->cur_advance_y); return true; } } else { m_impl->cur_data_type = glyph_type_mono; if (is_sys_bitmap || !FT_IS_SCALABLE(m_impl->font) || m_impl->matrix.is_identity()) { gfx_scanline_bin sl; error = FT_Render_Glyph(m_impl->font->glyph, FT_RENDER_MODE_MONO); if (error == 0) { decompose_ft_bitmap_mono(m_impl->font->glyph->bitmap, m_impl->font->glyph->bitmap_left, m_impl->flip_y ? -m_impl->font->glyph->bitmap_top : m_impl->font->glyph->bitmap_top, m_impl->flip_y, sl, m_impl->cur_font_scanlines_bin); m_impl->cur_bound_rect = rect(m_impl->cur_font_scanlines_bin.min_x(), m_impl->cur_font_scanlines_bin.min_y(), m_impl->cur_font_scanlines_bin.max_x() + 1, m_impl->cur_font_scanlines_bin.max_y() + 1); m_impl->cur_data_size = m_impl->cur_font_scanlines_bin.byte_size(); m_impl->cur_advance_x = FLT_TO_SCALAR(int26p6_to_flt(m_impl->font->glyph->advance.x)); m_impl->cur_advance_y = FLT_TO_SCALAR(int26p6_to_flt(m_impl->font->glyph->advance.y)); return true; } } else { if (m_impl->weight == 500) { int strength = 1 << 5; FT_Outline_Embolden(&(m_impl->font->glyph->outline), strength); } else if (m_impl->weight == 700) { int strength = 1 << 6; FT_Outline_Embolden(&(m_impl->font->glyph->outline), strength); } else if (m_impl->weight == 900) { int strength = 1 << 7; FT_Outline_Embolden(&(m_impl->font->glyph->outline), strength); } m_impl->cur_font_path.remove_all(); if (decompose_ft_outline(m_impl->font->glyph->outline, m_impl->flip_y, m_impl->matrix, m_impl->cur_font_path)) { gfx_rasterizer_scanline_aa<> rasterizer; picasso::conv_curve curves(m_impl->cur_font_path); curves.approximation_scale(4.0); rasterizer.add_path(curves); gfx_scanline_bin sl; m_impl->cur_font_scanlines_bin.prepare(); // Remove all gfx_render_scanlines(rasterizer, sl, m_impl->cur_font_scanlines_bin); m_impl->cur_bound_rect = rect(m_impl->cur_font_scanlines_bin.min_x(), m_impl->cur_font_scanlines_bin.min_y(), m_impl->cur_font_scanlines_bin.max_x() + 1, m_impl->cur_font_scanlines_bin.max_y() + 1); m_impl->cur_data_size = m_impl->cur_font_scanlines_bin.byte_size(); m_impl->cur_advance_x = FLT_TO_SCALAR(int26p6_to_flt(m_impl->font->glyph->advance.x)); m_impl->cur_advance_y = FLT_TO_SCALAR(int26p6_to_flt(m_impl->font->glyph->advance.y)); m_impl->matrix.transform(&m_impl->cur_advance_x, &m_impl->cur_advance_y); return true; } } } } } return false; }
/* * Function : libaroma_font_glyph_draw * Return Value: byte * Descriptions: draw glyph into canvas */ byte libaroma_font_glyph_draw( LIBAROMA_CANVASP dest, LIBAROMA_GLYPH aglyph_param, int x, int y, word color, byte flags, byte opacity ) { _LIBAROMA_FONT_SLOT_CACHEP aglyph = (_LIBAROMA_FONT_SLOT_CACHEP) aglyph_param; if (!aglyph) { return 0; } if (aglyph->glyph==NULL) { return 0; } if (dest == NULL) { dest = libaroma_fb()->canvas; } /* thread safe lock */ _libaroma_font_lock(1); /* copy & render */ FT_Glyph fglyph=NULL; fglyph=NULL; int cnt=0; if (FT_Glyph_Copy(aglyph->glyph, &fglyph)!=0){ _libaroma_font_lock(0); return 0; } if (cnt>0){ printf("[FT] FC: %i %i\n",cnt,aglyph->codepoint); } /* italic transform */ if (flags & _LIBAROMA_TEXTCHUNK_ITALIC) { FT_Matrix matrix; matrix.xx = 0x10000L; matrix.xy = 0x5000L; matrix.yx = 0; matrix.yy = 0x10000L; FT_Glyph_Transform(fglyph, &matrix, NULL); } /* embolden */ if (flags & _LIBAROMA_TEXTCHUNK_BOLD) { FT_Outline_Embolden( &((FT_OutlineGlyph) fglyph)->outline, aglyph->size * 2 ); } /* convert glyph to bitmap glyph */ if (FT_Glyph_To_Bitmap(&fglyph, _LIBAROMA_FONT_RENDER_FLAG, 0, 1)!=0){ /* release glyph */ printf("[FT] FR:%i\n",aglyph->codepoint); FT_Done_Glyph(fglyph); _libaroma_font_lock(0); return 0; } /* as bitmap glyph */ FT_BitmapGlyph bit = (FT_BitmapGlyph) fglyph; /* prepare locations */ /*int yy;*/ typeof(bit->bitmap.rows) yy; int xpos = x + bit->left; int xstart = 0; if (xpos < 0) { xstart = 0 - xpos; xpos = 0; } /* loop */ #ifndef LIBAROMA_CONFIG_NOFONT_SUBPIXEL int draw_w = (bit->bitmap.width / 3) - xstart; if (draw_w + xpos > dest->w) { draw_w = dest->w - xpos; } if (draw_w > 0) { wordp tmp_dst = NULL; if (opacity != 0xff) { tmp_dst = (wordp) malloc(draw_w * 2); } for (yy = 0; yy < bit->bitmap.rows; yy++) { /* drawing positions */ int yglp = (yy - bit->top); int ypos = (y + yglp) * dest->l; /* check position */ if ((ypos+draw_w>(dest->l*dest->h)) || (ypos<0)) { continue; } /* source * destination pointers */ int ysrc = yy * bit->bitmap.pitch; bytep src_line = bit->bitmap.buffer + ysrc + (xstart * 3); wordp dest_line = dest->data + ypos + xpos; /* draw line */ if (opacity == 0xff) { libaroma_alpha_multi_line( draw_w, dest_line, dest_line, color, src_line); } else { libaroma_alpha_multi_line( draw_w, tmp_dst, dest_line, color, src_line); libaroma_alpha_const( draw_w, dest_line, dest_line, tmp_dst, opacity); } } if (tmp_dst) { free(tmp_dst); } } #else int draw_w = bit->bitmap.width - xstart; if (draw_w + xpos > dest->w) { draw_w = dest->w - xpos; } if (draw_w > 0) { wordp tmp_dst = NULL; if (opacity != 0xff) { tmp_dst = (wordp) malloc(draw_w * 2); } /* line */ for (yy = 0; yy < bit->bitmap.rows; yy++) { /* drawing positions */ int yglp = (yy - bit->top); int ypos = (y + yglp) * dest->l; /* check position */ if ((ypos+draw_w>(dest->l*dest->h)) || (ypos<0)) { continue; } /* source & destination pointers */ int ysrc = yy * bit->bitmap.pitch; bytep src_line = bit->bitmap.buffer + ysrc + xstart; wordp dest_line = dest->data + ypos + xpos; /* draw line */ if (opacity == 0xff) { libaroma_alpha_mono( draw_w, dest_line, dest_line, color, src_line); } else { libaroma_alpha_mono( draw_w, tmp_dst, dest_line, color, src_line); libaroma_alpha_const( draw_w, dest_line, dest_line, tmp_dst, opacity); } } if (tmp_dst) { free(tmp_dst); } } #endif /* release glyph */ FT_Done_Glyph(fglyph); _libaroma_font_lock(0); return 1; } /* End of libaroma_font_glyph_draw */
Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) const { // The glyph to return Glyph glyph; // First, transform our ugly void* to a FT_Face FT_Face face = static_cast<FT_Face>(m_face); if (!face) return glyph; // Set the character size if (!setCurrentSize(characterSize)) return glyph; // Load the glyph corresponding to the code point if (FT_Load_Char(face, codePoint, FT_LOAD_TARGET_NORMAL | FT_LOAD_FORCE_AUTOHINT) != 0) return glyph; // Retrieve the glyph FT_Glyph glyphDesc; if (FT_Get_Glyph(face->glyph, &glyphDesc) != 0) return glyph; // Apply bold if necessary -- first technique using outline (highest quality) FT_Pos weight = 1 << 6; bool outline = (glyphDesc->format == FT_GLYPH_FORMAT_OUTLINE); if (bold && outline) { FT_OutlineGlyph outlineGlyph = (FT_OutlineGlyph)glyphDesc; FT_Outline_Embolden(&outlineGlyph->outline, weight); } // Convert the glyph to a bitmap (i.e. rasterize it) FT_Glyph_To_Bitmap(&glyphDesc, FT_RENDER_MODE_NORMAL, 0, 1); FT_Bitmap& bitmap = reinterpret_cast<FT_BitmapGlyph>(glyphDesc)->bitmap; // Apply bold if necessary -- fallback technique using bitmap (lower quality) if (bold && !outline) { FT_Bitmap_Embolden(static_cast<FT_Library>(m_library), &bitmap, weight, weight); } // Compute the glyph's advance offset glyph.advance = static_cast<float>(face->glyph->metrics.horiAdvance) / static_cast<float>(1 << 6); if (bold) glyph.advance += static_cast<float>(weight) / static_cast<float>(1 << 6); int width = bitmap.width; int height = bitmap.rows; if ((width > 0) && (height > 0)) { // Leave a small padding around characters, so that filtering doesn't // pollute them with pixels from neighbors const unsigned int padding = 1; // Get the glyphs page corresponding to the character size Page& page = m_pages[characterSize]; // Find a good position for the new glyph into the texture glyph.textureRect = findGlyphRect(page, width + 2 * padding, height + 2 * padding); // Make sure the texture data is positioned in the center // of the allocated texture rectangle glyph.textureRect.left += padding; glyph.textureRect.top += padding; glyph.textureRect.width -= 2 * padding; glyph.textureRect.height -= 2 * padding; // Compute the glyph's bounding box glyph.bounds.left = static_cast<float>(face->glyph->metrics.horiBearingX) / static_cast<float>(1 << 6); glyph.bounds.top = -static_cast<float>(face->glyph->metrics.horiBearingY) / static_cast<float>(1 << 6); glyph.bounds.width = static_cast<float>(face->glyph->metrics.width) / static_cast<float>(1 << 6); glyph.bounds.height = static_cast<float>(face->glyph->metrics.height) / static_cast<float>(1 << 6); // Extract the glyph's pixels from the bitmap m_pixelBuffer.resize(width * height * 4, 255); const Uint8* pixels = bitmap.buffer; if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { // Pixels are 1 bit monochrome values for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { // The color channels remain white, just fill the alpha channel std::size_t index = (x + y * width) * 4 + 3; m_pixelBuffer[index] = ((pixels[x / 8]) & (1 << (7 - (x % 8)))) ? 255 : 0; } pixels += bitmap.pitch; } } else { // Pixels are 8 bits gray levels for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { // The color channels remain white, just fill the alpha channel std::size_t index = (x + y * width) * 4 + 3; m_pixelBuffer[index] = pixels[x]; } pixels += bitmap.pitch; } } // Write the pixels to the texture unsigned int x = glyph.textureRect.left; unsigned int y = glyph.textureRect.top; unsigned int w = glyph.textureRect.width; unsigned int h = glyph.textureRect.height; page.texture.update(&m_pixelBuffer[0], w, h, x, y); } // Delete the FT glyph FT_Done_Glyph(glyphDesc); // Force an OpenGL flush, so that the font's texture will appear updated // in all contexts immediately (solves problems in multi-threaded apps) glCheck(glFlush()); // Done :) return glyph; }
static void loadglyph(font_t *f, glui32 cid) { FT_Vector v; int err; glui32 gid; int x; bitmap_t glyphs[GLI_SUBPIX]; int adv; gid = FT_Get_Char_Index(f->face, cid); if (gid == 0) gid = FT_Get_Char_Index(f->face, '?'); for (x = 0; x < GLI_SUBPIX; x++) { v.x = (x * 64) / GLI_SUBPIX; v.y = 0; FT_Set_Transform(f->face, 0, &v); err = FT_Load_Glyph(f->face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING); if (err) winabort("FT_Load_Glyph"); if (f->make_bold) FT_Outline_Embolden(&f->face->glyph->outline, FT_MulFix(f->face->units_per_EM, f->face->size->metrics.y_scale) / 24); if (f->make_oblique) FT_Outline_Transform(&f->face->glyph->outline, &ftmat); if (gli_conf_lcd) err = FT_Render_Glyph(f->face->glyph, FT_RENDER_MODE_LCD); else err = FT_Render_Glyph(f->face->glyph, FT_RENDER_MODE_LIGHT); if (err) winabort("FT_Render_Glyph"); adv = (f->face->glyph->advance.x * GLI_SUBPIX + 32) / 64; glyphs[x].lsb = f->face->glyph->bitmap_left; glyphs[x].top = f->face->glyph->bitmap_top; glyphs[x].w = f->face->glyph->bitmap.width; glyphs[x].h = f->face->glyph->bitmap.rows; glyphs[x].pitch = f->face->glyph->bitmap.pitch; glyphs[x].data = malloc(glyphs[x].pitch * glyphs[x].h); if (gli_conf_lcd) gammacopy_lcd(glyphs[x].data, f->face->glyph->bitmap.buffer, glyphs[x].w, glyphs[x].h, glyphs[x].pitch); else gammacopy(glyphs[x].data, f->face->glyph->bitmap.buffer, glyphs[x].pitch * glyphs[x].h); } if (cid < 256) { f->lowloaded[cid/8] |= (1 << (cid%8)); f->lowadvs[cid] = adv; memcpy(f->lowglyphs[cid], glyphs, sizeof glyphs); } else { int idx = findhighglyph(cid, f->highentries, f->num_highentries); if (idx < 0) { idx = ~idx; /* make room if needed */ if (f->alloced_highentries == f->num_highentries) { fentry_t *newentries; int newsize = f->alloced_highentries * 2; if (!newsize) newsize = 2; newentries = malloc(newsize * sizeof(fentry_t)); if (!newentries) return; if (f->highentries) { memcpy(newentries, f->highentries, f->num_highentries * sizeof(fentry_t)); free(f->highentries); } f->highentries = newentries; f->alloced_highentries = newsize; } /* insert new glyph */ memmove(&f->highentries[idx+1], &f->highentries[idx], (f->num_highentries - idx) * sizeof(fentry_t)); f->highentries[idx].cid = cid; f->highentries[idx].adv = adv; memcpy(f->highentries[idx].glyph, glyphs, sizeof glyphs); f->num_highentries++; } } }
int _PGFT_LoadGlyph(FontGlyph *glyph, PGFT_char character, const FontRenderMode *mode, void *internal) { static FT_Vector delta = {0, 0}; FT_Render_Mode rmode = (mode->render_flags & FT_RFLAG_ANTIALIAS ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO); FT_Vector strong_delta = {0, 0}; FT_Glyph image = 0; FT_Glyph_Metrics *ft_metrics; TextContext *context = (TextContext *)internal; FT_UInt32 load_flags; FT_UInt gindex; FT_Fixed rotation_angle = mode->rotation_angle; /* FT_Matrix transform; */ FT_Vector h_bearing_rotated; FT_Vector v_bearing_rotated; FT_Vector h_advance_rotated; FT_Vector v_advance_rotated; FT_Error error = 0; /* * Calculate the corresponding glyph index for the char */ gindex = FTC_CMapCache_Lookup(context->charmap, context->id, -1, (FT_UInt32)character); glyph->glyph_index = gindex; /* * Get loading information */ load_flags = get_load_flags(mode); /* * Load the glyph into the glyph slot */ if (FT_Load_Glyph(context->font, glyph->glyph_index, (FT_Int)load_flags) || FT_Get_Glyph(context->font->glyph, &image)) goto cleanup; /* * Perform any outline transformations */ if (mode->style & FT_STYLE_STRONG) { FT_UShort x_ppem = context->font->size->metrics.x_ppem; FT_Fixed bold_str; FT_BBox before; FT_BBox after; bold_str = FX16_CEIL_TO_FX6(mode->strength * x_ppem); FT_Outline_Get_CBox(&((FT_OutlineGlyph)image)->outline, &before); if (FT_Outline_Embolden(&((FT_OutlineGlyph)image)->outline, bold_str)) goto cleanup; FT_Outline_Get_CBox(&((FT_OutlineGlyph)image)->outline, &after); strong_delta.x += ((after.xMax - after.xMin) - (before.xMax - before.xMin)); strong_delta.y += ((after.yMax - after.yMin) - (before.yMax - before.yMin)); } if (context->do_transform) { if (FT_Glyph_Transform(image, &context->transform, &delta)) { goto cleanup; } } /* * Finished with outline transformations, now replace with a bitmap */ error = FT_Glyph_To_Bitmap(&image, rmode, 0, 1); if (error) { goto cleanup; } if (mode->style & FT_STYLE_WIDE) { FT_Bitmap *bitmap = &((FT_BitmapGlyph)image)->bitmap; int w = bitmap->width; FT_UShort x_ppem = context->font->size->metrics.x_ppem; FT_Pos x_strength; x_strength = FX16_CEIL_TO_FX6(mode->strength * x_ppem); /* FT_Bitmap_Embolden returns an error for a zero width bitmap */ if (w > 0) { error = FT_Bitmap_Embolden(context->lib, bitmap, x_strength, (FT_Pos)0); if (error) { goto cleanup; } strong_delta.x += INT_TO_FX6(bitmap->width - w); } else { strong_delta.x += x_strength; } } /* Fill the glyph */ ft_metrics = &context->font->glyph->metrics; h_advance_rotated.x = ft_metrics->horiAdvance + strong_delta.x; h_advance_rotated.y = 0; v_advance_rotated.x = 0; v_advance_rotated.y = ft_metrics->vertAdvance + strong_delta.y; if (rotation_angle != 0) { FT_Angle counter_rotation = INT_TO_FX6(360) - rotation_angle; FT_Vector_Rotate(&h_advance_rotated, rotation_angle); FT_Vector_Rotate(&v_advance_rotated, counter_rotation); } glyph->image = (FT_BitmapGlyph)image; glyph->width = INT_TO_FX6(glyph->image->bitmap.width); glyph->height = INT_TO_FX6(glyph->image->bitmap.rows); h_bearing_rotated.x = INT_TO_FX6(glyph->image->left); h_bearing_rotated.y = INT_TO_FX6(glyph->image->top); fill_metrics(&glyph->h_metrics, ft_metrics->horiBearingX, ft_metrics->horiBearingY, &h_bearing_rotated, &h_advance_rotated); if (rotation_angle == 0) { v_bearing_rotated.x = ft_metrics->vertBearingX - strong_delta.x / 2; v_bearing_rotated.y = ft_metrics->vertBearingY; } else { /* * Adjust the vertical metrics. */ FT_Vector v_origin; v_origin.x = (glyph->h_metrics.bearing_x - ft_metrics->vertBearingX + strong_delta.x / 2); v_origin.y = (glyph->h_metrics.bearing_y + ft_metrics->vertBearingY); FT_Vector_Rotate(&v_origin, rotation_angle); v_bearing_rotated.x = glyph->h_metrics.bearing_rotated.x - v_origin.x; v_bearing_rotated.y = v_origin.y - glyph->h_metrics.bearing_rotated.y; } fill_metrics(&glyph->v_metrics, ft_metrics->vertBearingX, ft_metrics->vertBearingY, &v_bearing_rotated, &v_advance_rotated); return 0; /* * Cleanup on error */ cleanup: if (image) { FT_Done_Glyph(image); } return -1; }
static say_glyph *say_font_load_glyph(say_font *font, say_font_page *page, uint32_t codepoint, uint8_t bold, size_t size) { uint32_t bold_codepoint = ((bold ? 1 : 0) << 31) | codepoint; say_glyph *glyph = malloc(sizeof(say_glyph)); say_table_set(page->glyphs, bold_codepoint, glyph); glyph->offset = 0; glyph->bounds = say_make_rect(2, 0, 2, 2); glyph->sub_rect = say_make_rect(2, 0, 2, 2); if (!(font->face && say_font_set_size(font, size))) return glyph; if (FT_Load_Char(font->face, codepoint, FT_LOAD_TARGET_NORMAL) != 0) return glyph; FT_Glyph ft_glyph; if (FT_Get_Glyph(font->face->glyph, &ft_glyph) != 0) return glyph; FT_Pos weight = 1 << 6; uint8_t outline = ft_glyph->format == FT_GLYPH_FORMAT_OUTLINE; if (bold && outline) { FT_OutlineGlyph outline_glyph = (FT_OutlineGlyph)ft_glyph; FT_Outline_Embolden(&outline_glyph->outline, weight); } FT_Glyph_To_Bitmap(&ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1); FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)ft_glyph; FT_Bitmap *bitmap = &bitmap_glyph->bitmap; if (bold && !outline) { FT_Bitmap_Embolden(font->library, bitmap, weight, weight); } glyph->offset = ft_glyph->advance.x >> 16; if (bold) glyph->offset += weight >> 6; int width = bitmap->width; int height = bitmap->rows; if (width > 0 && height > 0) { static const int padding = 1; glyph->sub_rect = say_page_find_rect(page, width + (2 * padding), height + (2 * padding)); glyph->bounds.x = +bitmap_glyph->left - padding; glyph->bounds.y = -bitmap_glyph->top - padding; glyph->bounds.w = width + (2 * padding); glyph->bounds.h = height + (2 * padding); say_rect actual_rect = glyph->sub_rect; actual_rect.x += padding; actual_rect.y += padding; actual_rect.w -= 2 * padding; actual_rect.h -= 2 * padding; uint8_t *pixels = bitmap->buffer; if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO) { for (int y = actual_rect.y; y < actual_rect.y + actual_rect.h; y++) { for (int x = actual_rect.x; x < actual_rect.x + actual_rect.w; x++) { int pixel_x = x - actual_rect.x; uint8_t alpha = ((pixels[pixel_x / 8]) & (1 << (7 - (pixel_x % 8)))) ? 255 : 0; say_image_set(page->image, x, y, say_make_color(255, 255, 255, alpha)); } pixels += bitmap->pitch; } } else { for (int y = actual_rect.y; y < actual_rect.y + actual_rect.h; y++) { for (int x = actual_rect.x; x < actual_rect.x + actual_rect.w; x++) { int pixel_x = x - actual_rect.x; say_image_set(page->image, x, y, say_make_color(255, 255, 255, pixels[pixel_x])); } pixels += bitmap->pitch; } } } FT_Done_Glyph(ft_glyph); return glyph; }
Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold, float outlineThickness) const { // The glyph to return Glyph glyph; // First, transform our ugly void* to a FT_Face FT_Face face = static_cast<FT_Face>(m_face); if (!face) return glyph; // Set the character size if (!setCurrentSize(characterSize)) return glyph; // Load the glyph corresponding to the code point FT_Int32 flags = FT_LOAD_TARGET_NORMAL | FT_LOAD_FORCE_AUTOHINT; if (outlineThickness != 0) flags |= FT_LOAD_NO_BITMAP; if (FT_Load_Char(face, codePoint, flags) != 0) return glyph; // Retrieve the glyph FT_Glyph glyphDesc; if (FT_Get_Glyph(face->glyph, &glyphDesc) != 0) return glyph; // Apply bold and outline (there is no fallback for outline) if necessary -- first technique using outline (highest quality) FT_Pos weight = 1 << 6; bool outline = (glyphDesc->format == FT_GLYPH_FORMAT_OUTLINE); if (outline) { if (bold) { FT_OutlineGlyph outlineGlyph = (FT_OutlineGlyph)glyphDesc; FT_Outline_Embolden(&outlineGlyph->outline, weight); } if (outlineThickness != 0) { FT_Stroker stroker = static_cast<FT_Stroker>(m_stroker); FT_Stroker_Set(stroker, static_cast<FT_Fixed>(outlineThickness * static_cast<float>(1 << 6)), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); FT_Glyph_Stroke(&glyphDesc, stroker, true); } } // Convert the glyph to a bitmap (i.e. rasterize it) FT_Glyph_To_Bitmap(&glyphDesc, FT_RENDER_MODE_NORMAL, 0, 1); FT_Bitmap& bitmap = reinterpret_cast<FT_BitmapGlyph>(glyphDesc)->bitmap; // Apply bold if necessary -- fallback technique using bitmap (lower quality) if (!outline) { if (bold) FT_Bitmap_Embolden(static_cast<FT_Library>(m_library), &bitmap, weight, weight); if (outlineThickness != 0) err() << "Failed to outline glyph (no fallback available)" << std::endl; } // Compute the glyph's advance offset glyph.advance = static_cast<float>(face->glyph->metrics.horiAdvance) / static_cast<float>(1 << 6); if (bold) glyph.advance += static_cast<float>(weight) / static_cast<float>(1 << 6); int width = bitmap.width; int height = bitmap.rows; if ((width > 0) && (height > 0)) { // Leave a small padding around characters, so that filtering doesn't // pollute them with pixels from neighbors const unsigned int padding = 1; width += 2 * padding; height += 2 * padding; // Get the glyphs page corresponding to the character size Page& page = m_pages[characterSize]; // Find a good position for the new glyph into the texture glyph.textureRect = findGlyphRect(page, width, height); // Make sure the texture data is positioned in the center // of the allocated texture rectangle glyph.textureRect.left += padding; glyph.textureRect.top += padding; glyph.textureRect.width -= 2 * padding; glyph.textureRect.height -= 2 * padding; // Compute the glyph's bounding box glyph.bounds.left = static_cast<float>(face->glyph->metrics.horiBearingX) / static_cast<float>(1 << 6); glyph.bounds.top = -static_cast<float>(face->glyph->metrics.horiBearingY) / static_cast<float>(1 << 6); glyph.bounds.width = static_cast<float>(face->glyph->metrics.width) / static_cast<float>(1 << 6) + outlineThickness * 2; glyph.bounds.height = static_cast<float>(face->glyph->metrics.height) / static_cast<float>(1 << 6) + outlineThickness * 2; // Resize the pixel buffer to the new size and fill it with transparent white pixels m_pixelBuffer.resize(width * height * 4); Uint8* current = &m_pixelBuffer[0]; Uint8* end = current + width * height * 4; while (current != end) { (*current++) = 255; (*current++) = 255; (*current++) = 255; (*current++) = 0; } // Extract the glyph's pixels from the bitmap const Uint8* pixels = bitmap.buffer; if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { // Pixels are 1 bit monochrome values for (unsigned int y = padding; y < height - padding; ++y) { for (unsigned int x = padding; x < width - padding; ++x) { // The color channels remain white, just fill the alpha channel std::size_t index = x + y * width; m_pixelBuffer[index * 4 + 3] = ((pixels[(x - padding) / 8]) & (1 << (7 - ((x - padding) % 8)))) ? 255 : 0; } pixels += bitmap.pitch; } } else { // Pixels are 8 bits gray levels for (unsigned int y = padding; y < height - padding; ++y) { for (unsigned int x = padding; x < width - padding; ++x) { // The color channels remain white, just fill the alpha channel std::size_t index = x + y * width; m_pixelBuffer[index * 4 + 3] = pixels[x - padding]; } pixels += bitmap.pitch; } } // Write the pixels to the texture unsigned int x = glyph.textureRect.left - padding; unsigned int y = glyph.textureRect.top - padding; unsigned int w = glyph.textureRect.width + 2 * padding; unsigned int h = glyph.textureRect.height + 2 * padding; page.texture.update(&m_pixelBuffer[0], w, h, x, y); } // Delete the FT glyph FT_Done_Glyph(glyphDesc); // Done :) return glyph; }
// ---------------------------------------------------------------------------- void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi) { assert(gi.glyph_index > 0); assert(gi.font_number < m_face_ttf->getTotalFaces()); FT_Face cur_face = m_face_ttf->getFace(gi.font_number); FT_GlyphSlot slot = cur_face->glyph; // Faces may be shared across regular and bold, // so reset dpi each time font_manager->checkFTError(FT_Set_Pixel_Sizes(cur_face, 0, getDPI()), "setting DPI"); font_manager->checkFTError(FT_Load_Glyph(cur_face, gi.glyph_index, FT_LOAD_DEFAULT), "loading a glyph"); if (dynamic_cast<BoldFace*>(this)) { // Embolden the outline of the glyph FT_Outline_Embolden(&(slot->outline), getDPI() * 2); } font_manager->checkFTError(FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL), "render a glyph to bitmap"); // Convert to an anti-aliased bitmap FT_Bitmap bits = slot->bitmap; core::dimension2du d(bits.width + 1, bits.rows + 1); core::dimension2du texture_size; texture_size = d.getOptimalSize(!(irr_driver->getVideoDriver() ->queryFeature(video::EVDF_TEXTURE_NPOT)), !(irr_driver ->getVideoDriver()->queryFeature(video::EVDF_TEXTURE_NSQUARE)), true, 0); if ((m_used_width + texture_size.Width > getGlyphPageSize() && m_used_height + m_temp_height + texture_size.Height > getGlyphPageSize()) || m_used_height + texture_size.Height > getGlyphPageSize()) { // Current glyph page is full: // Save the old glyph page video::ITexture* page_texture = irr_driver->getVideoDriver() ->addTexture("Glyph_page", m_page); m_spritebank->setTexture(m_spritebank->getTextureCount() - 1, page_texture); irr_driver->getVideoDriver()->removeTexture(page_texture); assert(page_texture->getReferenceCount() == 1); // Clear and add a new one createNewGlyphPage(); } video::IImage* glyph = NULL; switch (bits.pixel_mode) { case FT_PIXEL_MODE_GRAY: { // Create our blank image. glyph = irr_driver->getVideoDriver() ->createImage(video::ECF_A8R8G8B8, texture_size); glyph->fill(video::SColor(0, 255, 255, 255)); // Load the grayscale data in. const float gray_count = static_cast<float>(bits.num_grays); const unsigned int image_pitch = glyph->getPitch() / sizeof(unsigned int); unsigned int* image_data = (unsigned int*)glyph->lock(); unsigned char* glyph_data = bits.buffer; for (unsigned int y = 0; y < (unsigned int)bits.rows; y++) { unsigned char* row = glyph_data; for (unsigned int x = 0; x < (unsigned)bits.width; x++) { image_data[y * image_pitch + x] |= static_cast<unsigned int>(255.0f * (static_cast<float>(*row++) / gray_count)) << 24; } glyph_data += bits.pitch; } glyph->unlock(); break; } default: assert(false); } if (!glyph) Log::fatal("FontWithFace", "Failed to load a glyph"); // Done creating a single glyph, now copy to the glyph page... // Determine the linebreak location if (m_used_width + texture_size.Width > getGlyphPageSize()) { m_used_width = 0; m_used_height += m_temp_height; m_temp_height = 0; } // Copy to the full glyph page glyph->copyTo(m_page, core::position2di(m_used_width, m_used_height)); // Store the rectangle of current glyph gui::SGUISpriteFrame f; gui::SGUISprite s; core::rect<s32> rectangle(m_used_width, m_used_height, m_used_width + bits.width, m_used_height + bits.rows); f.rectNumber = m_spritebank->getPositions().size(); f.textureNumber = m_spritebank->getTextureCount() - 1; // Add frame to sprite s.Frames.push_back(f); s.frameTime = 0; m_spritebank->getPositions().push_back(rectangle); m_spritebank->getSprites().push_back(s); // Save glyph metrics FontArea a; a.advance_x = cur_face->glyph->advance.x / BEARING; a.bearing_x = cur_face->glyph->metrics.horiBearingX / BEARING; const int cur_height = (cur_face->glyph->metrics.height / BEARING); const int cur_offset_y = cur_height - (cur_face->glyph->metrics.horiBearingY / BEARING); a.offset_y = m_glyph_max_height - cur_height + cur_offset_y; a.offset_y_bt = -cur_offset_y; a.spriteno = f.rectNumber; m_character_area_map[c] = a; // Clean the temporary glyph glyph->drop(); glyph = NULL; // Store used area m_used_width += texture_size.Width; if (m_temp_height < texture_size.Height) m_temp_height = texture_size.Height; } // insertGlyph
void CFont2D::CharToImage(const wchar_t * Chars) { // wchar_t InputChar; // MultiByteToWideChar(CP_ACP,0,Chars,byteNum,&InputChar,1); if(FT_Load_Glyph( face, FT_Get_Char_Index( face, Chars[0] ), FT_LOAD_DEFAULT )) return; FT_Outline_Embolden( &(face->glyph->outline), 30 ); FT_Glyph glyph; if(FT_Get_Glyph( face->glyph, &glyph )) return; FT_Glyph_To_Bitmap( &glyph, ft_render_mode_normal, 0, 1 ); FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph; FT_Bitmap& bitmap=bitmap_glyph->bitmap; if(bitmap.width>=OnefontW) OneCharWidth[CharNum]=1.0f; else OneCharWidth[CharNum]=float(bitmap.width)/float(OnefontW); if(Chars[0]==L' ') OneCharWidth[CharNum]=0.5f; CharNum=CharNum+1; int Ymove=0; if(Chars[0]<0x80) Ymove=bitmap.rows/6; if(Chars[0]==L'-') Ymove=OnefontH/2-bitmap.rows*2; if(Chars[0]==L'.') Ymove=bitmap.rows/2; for(int j=Ymove; j <OnefontH;j++) { for(int i=0; i < OnefontW; i++){ OnefontData[i+(j-Ymove)*OnefontW] = (i>=bitmap.width || j<(OnefontH-bitmap.rows)) ? 0 : bitmap.buffer[i + bitmap.width*(j-OnefontH+bitmap.rows)]; } } for(int j=OnefontH-Ymove; j <OnefontH;j++) { for(int i=0; i < OnefontW; i++){ OnefontData[i+(j)*OnefontW] = 0; } } glBindTexture(GL_TEXTURE_2D, TexID); glTexSubImage2D( GL_TEXTURE_2D, 0, FontPosX, FontPosY, OnefontW, OnefontH, GL_ALPHA, GL_UNSIGNED_BYTE, OnefontData); FontPosX=FontPosX+OnefontW; if(FontPosX>=FontTexW) { FontPosX=0; FontPosY=FontPosY+OnefontH; if(FontPosY>=FontTexH) FontPosY=FontTexH; } FT_Done_Glyph(glyph); }
static PyObject* Py_Outline_decompose(Py_Outline* self, PyObject* args, PyObject* kwds) { /* TODO: Also implement this as an iterator */ DecomposeData data; PyObject *obj; const FT_Outline_Funcs funcs = { .move_to = Py_Outline_moveto_func, .line_to = Py_Outline_lineto_func, .conic_to = Py_Outline_conicto_func, .cubic_to = Py_Outline_cubicto_func, .shift = 0, .delta = 0 }; int error; const char* keywords[] = {"obj", "shift", "delta", NULL}; if (!PyArg_ParseTupleAndKeywords( args, kwds, "O|ii:decompose", (char **)keywords, &obj, &funcs.shift, &funcs.delta)) { return NULL; } if (!PyObject_HasAttrString(obj, "move_to")) { PyErr_SetString(PyExc_AttributeError, "obj has no move_to method"); return NULL; } if (!PyObject_HasAttrString(obj, "line_to")) { PyErr_SetString(PyExc_AttributeError, "obj has no line_to method"); return NULL; } if (!PyObject_HasAttrString(obj, "cubic_to")) { PyErr_SetString(PyExc_AttributeError, "obj has no cubic_to method"); return NULL; } data.has_conic_to = PyObject_HasAttrString(obj, "conic_to"); data.callback = obj; data.last_x = 0; data.last_y = 0; error = FT_Outline_Decompose(&self->x, &funcs, &data); if (PyErr_Occurred()) { return NULL; } else if (ftpy_exc(error)) { return NULL; } Py_RETURN_NONE; }; static PyObject* Py_Outline_embolden(Py_Outline* self, PyObject* args, PyObject* kwds) { double strength; const char* keywords[] = {"strength", NULL}; if (!PyArg_ParseTupleAndKeywords( args, kwds, "d:embolden", (char **)keywords, &strength)) { return NULL; } if (ftpy_exc( FT_Outline_Embolden(&self->x, TO_F26DOT6(strength)))) { return NULL; } Py_RETURN_NONE; };
int DrawGlyph( _In_ wchar_t c, _In_ int pixel_size, _Out_ _Notnull_ int* pitch, _Out_ _Notnull_ int* rows, _Out_ _Notnull_ int* advance, _Out_ _Notnull_ int* horiBearingY, _Inout_ _Notnull_ unsigned char** ppBitmap ) { FT_Error error; FT_UInt glyph_index; glyph_index = FT_Get_Char_Index(face, c); error = FT_Set_Pixel_Sizes(face, 0, pixel_size); if (error) { return error; } error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); if (error) { return error; } if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { FT_Pos strength = 30; error = FT_Outline_Embolden(&face->glyph->outline, strength); if (error) { //cl_debug_output("Font Embolden failed."); DebugBreak(); } } /* FT_Matrix matrix; matrix.xx = 0x07FFFL; matrix.xy = 0; matrix.yx = 0; matrix.yy = 0x10000L; FT_Set_Transform(face, &matrix, 0); */ error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); if (error) { return error; } *ppBitmap = (unsigned char*)malloc(face->glyph->bitmap.rows * face->glyph->bitmap.pitch); memcpy(*ppBitmap, face->glyph->bitmap.buffer, face->glyph->bitmap.rows * face->glyph->bitmap.pitch); *pitch = face->glyph->bitmap.pitch; *rows = face->glyph->bitmap.rows; *advance = face->glyph->advance.x / 64; *horiBearingY = face->glyph->metrics.horiBearingY / 64; return error; }