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; }
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; }
void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGlyph& glyph) { const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag); const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag); switch ( face->glyph->format ) { case FT_GLYPH_FORMAT_OUTLINE: { FT_Outline* outline = &face->glyph->outline; FT_BBox bbox; FT_Bitmap target; if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) { emboldenOutline(face, outline); } int dx = 0, dy = 0; if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) { dx = SkFixedToFDot6(glyph.getSubXFixed()); dy = SkFixedToFDot6(glyph.getSubYFixed()); // negate dy since freetype-y-goes-up and skia-y-goes-down dy = -dy; } FT_Outline_Get_CBox(outline, &bbox); /* what we really want to do for subpixel is offset(dx, dy) compute_bounds offset(bbox & !63) but that is two calls to offset, so we do the following, which achieves the same thing with only one offset call. */ FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63), dy - ((bbox.yMin + dy) & ~63)); if (SkMask::kLCD16_Format == glyph.fMaskFormat) { FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD); if (fPreBlend.isApplicable()) { copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } else { copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } } else { target.width = glyph.fWidth; target.rows = glyph.fHeight; target.pitch = glyph.rowBytes(); target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage); target.pixel_mode = compute_pixel_mode( (SkMask::Format)fRec.fMaskFormat); target.num_grays = 256; memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); FT_Outline_Get_Bitmap(face->glyph->library, outline, &target); } } break; case FT_GLYPH_FORMAT_BITMAP: { if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) { FT_GlyphSlot_Own_Bitmap(face->glyph); FT_Bitmap_Embolden(face->glyph->library, &face->glyph->bitmap, kBitmapEmboldenStrength, 0); } SkASSERT_CONTINUE(glyph.fWidth == face->glyph->bitmap.width); SkASSERT_CONTINUE(glyph.fHeight == face->glyph->bitmap.rows); SkASSERT_CONTINUE(glyph.fTop == -face->glyph->bitmap_top); SkASSERT_CONTINUE(glyph.fLeft == face->glyph->bitmap_left); const uint8_t* src = (const uint8_t*)face->glyph->bitmap.buffer; uint8_t* dst = (uint8_t*)glyph.fImage; if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY || (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO && glyph.fMaskFormat == SkMask::kBW_Format)) { unsigned srcRowBytes = face->glyph->bitmap.pitch; unsigned dstRowBytes = glyph.rowBytes(); unsigned minRowBytes = SkMin32(srcRowBytes, dstRowBytes); unsigned extraRowBytes = dstRowBytes - minRowBytes; for (int y = face->glyph->bitmap.rows - 1; y >= 0; --y) { memcpy(dst, src, minRowBytes); memset(dst + minRowBytes, 0, extraRowBytes); src += srcRowBytes; dst += dstRowBytes; } } else if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO && glyph.fMaskFormat == SkMask::kA8_Format) { for (int y = 0; y < face->glyph->bitmap.rows; ++y) { uint8_t byte = 0; int bits = 0; const uint8_t* src_row = src; uint8_t* dst_row = dst; for (int x = 0; x < face->glyph->bitmap.width; ++x) { if (!bits) { byte = *src_row++; bits = 8; } *dst_row++ = byte & 0x80 ? 0xff : 0; bits--; byte <<= 1; } src += face->glyph->bitmap.pitch; dst += glyph.rowBytes(); } } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) { if (fPreBlend.isApplicable()) { copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } else { copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } } else { SkDEBUGFAIL("unknown glyph bitmap transform needed"); } } break; default: SkDEBUGFAIL("unknown glyph format"); memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); return; } // We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum, // it is optional #if defined(SK_GAMMA_APPLY_TO_A8) if (SkMask::kA8_Format == glyph.fMaskFormat && fPreBlend.isApplicable()) { uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage; unsigned rowBytes = glyph.rowBytes(); for (int y = glyph.fHeight - 1; y >= 0; --y) { for (int x = glyph.fWidth - 1; x >= 0; --x) { dst[x] = fPreBlend.fG[dst[x]]; } dst += rowBytes; } } #endif }
static FT_Error Render_Embolden( int num_indices, int first_index ) { int start_x, start_y, step_y, x, y; int i; FT_Size size; FT_Face face; FT_GlyphSlot slot; FT_Pos xstr, ystr; error = FTDemo_Get_Size( handle, &size ); if ( error ) { /* probably a non-existent bitmap font size */ return error; } INIT_SIZE( size, start_x, start_y, step_y, x, y ); face = size->face; slot = face->glyph; ystr = status.ptsize * status.res / 72; xstr = status.xbold_factor * ystr; ystr = status.ybold_factor * ystr; for ( i = first_index; i < num_indices; i++ ) { int gindex; if ( handle->encoding == FT_ENCODING_NONE ) gindex = i; else gindex = FTDemo_Get_Index( handle, i ); error = FT_Load_Glyph( face, gindex, handle->load_flags ); if ( !error ) { /* this is essentially the code of function */ /* `FT_GlyphSlot_Embolden' */ if ( slot->format == FT_GLYPH_FORMAT_OUTLINE ) { error = FT_Outline_EmboldenXY( &slot->outline, xstr, ystr ); /* ignore error */ } else if ( slot->format == FT_GLYPH_FORMAT_BITMAP ) { /* round to full pixels */ xstr &= ~63; ystr &= ~63; error = FT_GlyphSlot_Own_Bitmap( slot ); if ( error ) goto Next; error = FT_Bitmap_Embolden( slot->library, &slot->bitmap, xstr, ystr ); if ( error ) goto Next; } else goto Next; 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.horiAdvance += xstr; slot->metrics.vertAdvance += ystr; if ( slot->format == FT_GLYPH_FORMAT_BITMAP ) slot->bitmap_top += ystr >> 6; error = FTDemo_Draw_Slot( handle, display, slot, &x, &y ); if ( error ) goto Next; else if ( X_TOO_LONG( x, size, display ) ) { x = start_x; y += step_y; if ( Y_TOO_LONG( y, size, display ) ) break; } } else Next: status.Fail++; }
void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGlyph& glyph) { const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag); const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag); switch ( face->glyph->format ) { case FT_GLYPH_FORMAT_OUTLINE: { FT_Outline* outline = &face->glyph->outline; FT_BBox bbox; FT_Bitmap target; if (fRec.fFlags & SkScalerContext::kEmbolden_Flag && !(face->style_flags & FT_STYLE_FLAG_BOLD)) { emboldenOutline(face, outline); } int dx = 0, dy = 0; if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) { dx = SkFixedToFDot6(glyph.getSubXFixed()); dy = SkFixedToFDot6(glyph.getSubYFixed()); // negate dy since freetype-y-goes-up and skia-y-goes-down dy = -dy; } FT_Outline_Get_CBox(outline, &bbox); /* what we really want to do for subpixel is offset(dx, dy) compute_bounds offset(bbox & !63) but that is two calls to offset, so we do the following, which achieves the same thing with only one offset call. */ FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63), dy - ((bbox.yMin + dy) & ~63)); if (SkMask::kLCD16_Format == glyph.fMaskFormat) { FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD); SkMask mask; glyph.toMask(&mask); if (fPreBlend.isApplicable()) { copyFT2LCD16<true>(face->glyph->bitmap, mask, doBGR, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } else { copyFT2LCD16<false>(face->glyph->bitmap, mask, doBGR, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } } else { target.width = glyph.fWidth; target.rows = glyph.fHeight; target.pitch = glyph.rowBytes(); target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage); target.pixel_mode = compute_pixel_mode( (SkMask::Format)fRec.fMaskFormat); target.num_grays = 256; memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); FT_Outline_Get_Bitmap(face->glyph->library, outline, &target); } } break; case FT_GLYPH_FORMAT_BITMAP: { FT_Pixel_Mode pixel_mode = static_cast<FT_Pixel_Mode>(face->glyph->bitmap.pixel_mode); SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat); // Assume that the other formats do not exist. SkASSERT(FT_PIXEL_MODE_MONO == pixel_mode || FT_PIXEL_MODE_GRAY == pixel_mode || FT_PIXEL_MODE_BGRA == pixel_mode); // These are the only formats this ScalerContext should request. SkASSERT(SkMask::kBW_Format == maskFormat || SkMask::kA8_Format == maskFormat || SkMask::kARGB32_Format == maskFormat || SkMask::kLCD16_Format == maskFormat); if (fRec.fFlags & SkScalerContext::kEmbolden_Flag && !(face->style_flags & FT_STYLE_FLAG_BOLD)) { FT_GlyphSlot_Own_Bitmap(face->glyph); FT_Bitmap_Embolden(face->glyph->library, &face->glyph->bitmap, kBitmapEmboldenStrength, 0); } // If no scaling needed, directly copy glyph bitmap. if (glyph.fWidth == face->glyph->bitmap.width && glyph.fHeight == face->glyph->bitmap.rows && glyph.fTop == -face->glyph->bitmap_top && glyph.fLeft == face->glyph->bitmap_left) { SkMask dstMask; glyph.toMask(&dstMask); copyFTBitmap(face->glyph->bitmap, dstMask); break; } // Otherwise, scale the bitmap. // Copy the FT_Bitmap into an SkBitmap (either A8 or ARGB) SkBitmap unscaledBitmap; unscaledBitmap.setConfig(SkBitmapConfig_for_FTPixelMode(pixel_mode), face->glyph->bitmap.width, face->glyph->bitmap.rows); unscaledBitmap.allocPixels(); SkMask unscaledBitmapAlias; unscaledBitmapAlias.fImage = reinterpret_cast<uint8_t*>(unscaledBitmap.getPixels()); unscaledBitmapAlias.fBounds.set(0, 0, unscaledBitmap.width(), unscaledBitmap.height()); unscaledBitmapAlias.fRowBytes = unscaledBitmap.rowBytes(); unscaledBitmapAlias.fFormat = SkMaskFormat_for_SkBitmapConfig(unscaledBitmap.config()); copyFTBitmap(face->glyph->bitmap, unscaledBitmapAlias); // Wrap the glyph's mask in a bitmap, unless the glyph's mask is BW or LCD. // BW requires an A8 target for resizing, which can then be down sampled. // LCD should use a 4x A8 target, which will then be down sampled. // For simplicity, LCD uses A8 and is replicated. int bitmapRowBytes = 0; if (SkMask::kBW_Format != maskFormat && SkMask::kLCD16_Format != maskFormat) { bitmapRowBytes = glyph.rowBytes(); } SkBitmap dstBitmap; dstBitmap.setConfig(SkBitmapConfig_for_SkMaskFormat(maskFormat), glyph.fWidth, glyph.fHeight, bitmapRowBytes); if (SkMask::kBW_Format == maskFormat || SkMask::kLCD16_Format == maskFormat) { dstBitmap.allocPixels(); } else { dstBitmap.setPixels(glyph.fImage); } // Scale unscaledBitmap into dstBitmap. SkCanvas canvas(dstBitmap); canvas.clear(SK_ColorTRANSPARENT); canvas.scale(SkIntToScalar(glyph.fWidth) / SkIntToScalar(face->glyph->bitmap.width), SkIntToScalar(glyph.fHeight) / SkIntToScalar(face->glyph->bitmap.rows)); SkPaint paint; paint.setFilterLevel(SkPaint::kLow_FilterLevel); canvas.drawBitmap(unscaledBitmap, 0, 0, &paint); // If the destination is BW or LCD, convert from A8. if (SkMask::kBW_Format == maskFormat) { // Copy the A8 dstBitmap into the A1 glyph.fImage. SkMask dstMask; glyph.toMask(&dstMask); packA8ToA1(dstMask, dstBitmap.getAddr8(0, 0), dstBitmap.rowBytes()); } else if (SkMask::kLCD16_Format == maskFormat) { // Copy the A8 dstBitmap into the LCD16 glyph.fImage. uint8_t* src = dstBitmap.getAddr8(0, 0); uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage); for (int y = dstBitmap.height(); y --> 0;) { for (int x = 0; x < dstBitmap.width(); ++x) { dst[x] = grayToRGB16(src[x]); } dst = (uint16_t*)((char*)dst + glyph.rowBytes()); src += dstBitmap.rowBytes(); } } } break; default: SkDEBUGFAIL("unknown glyph format"); memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); return; } // We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum, // it is optional #if defined(SK_GAMMA_APPLY_TO_A8) if (SkMask::kA8_Format == glyph.fMaskFormat && fPreBlend.isApplicable()) { uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage; unsigned rowBytes = glyph.rowBytes(); for (int y = glyph.fHeight - 1; y >= 0; --y) { for (int x = glyph.fWidth - 1; x >= 0; --x) { dst[x] = fPreBlend.fG[dst[x]]; } dst += rowBytes; } } #endif }