/********************************************************* * * New rendering algorithm (full thickness underlines) * *********************************************************/ static void render(FreeTypeInstance *ft, Layout *text, const FontRenderMode *mode, const FontColor *fg_color, FontSurface *surface, unsigned width, unsigned height, FT_Vector *offset, FT_Pos underline_top, FT_Fixed underline_size) { FT_Pos top; FT_Pos left; int x; int y; int n; int length = text->length; GlyphSlot *slots = text->glyphs; FT_BitmapGlyph image; FontRenderPtr render_gray = surface->render_gray; FontRenderPtr render_mono = surface->render_mono; int is_underline_gray = 0; if (length <= 0) { return; } left = offset->x; top = offset->y; for (n = 0; n < length; ++n) { image = slots[n].glyph->image; x = FX6_TRUNC(FX6_CEIL(left + slots[n].posn.x)); y = FX6_TRUNC(FX6_CEIL(top + slots[n].posn.y)); if (image->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { render_gray(x, y, surface, &(image->bitmap), fg_color); is_underline_gray = 1; } else { render_mono(x, y, surface, &(image->bitmap), fg_color); } } if (underline_size > 0) { if (is_underline_gray) { surface->fill(left + text->min_x, top + underline_top, INT_TO_FX6(width), underline_size, surface, fg_color); } else { surface->fill(FX6_CEIL(left + text->min_x), FX6_CEIL(top + underline_top), INT_TO_FX6(width), FX6_CEIL(underline_size), surface, fg_color); } } }
int _PGFT_Render_ExistingSurface(FreeTypeInstance *ft, PgFontObject *fontobj, const FontRenderMode *mode, PGFT_String *text, SDL_Surface *surface, int x, int y, FontColor *fgcolor, FontColor *bgcolor, SDL_Rect *r) { static const FontRenderPtr __SDLrenderFuncs[] = { 0, __render_glyph_RGB1, __render_glyph_RGB2, __render_glyph_RGB3, __render_glyph_RGB4 }; static const FontRenderPtr __MONOrenderFuncs[] = { 0, __render_glyph_MONO1, __render_glyph_MONO2, __render_glyph_MONO3, __render_glyph_MONO4 }; static const FontFillPtr __RGBfillFuncs[] = { 0, __fill_glyph_RGB1, __fill_glyph_RGB2, __fill_glyph_RGB3, __fill_glyph_RGB4 }; int locked = 0; unsigned width; unsigned height; FT_Vector offset; FT_Vector surf_offset; FT_Pos underline_top; FT_Fixed underline_size; FontSurface font_surf; Layout *font_text; if (SDL_MUSTLOCK(surface)) { if (SDL_LockSurface(surface) == -1) { SDL_FreeSurface(surface); PyErr_SetString(PyExc_SDLError, SDL_GetError()); return -1; } locked = 1; } /* build font text */ font_text = _PGFT_LoadLayout(ft, fontobj, mode, text); if (!font_text) { if (locked) { SDL_UnlockSurface(surface); } return -1; } if (font_text->length == 0) { /* Nothing to rendering */ r->x = 0; r->y = 0; r->w = 0; r->h = _PGFT_Font_GetHeightSized(ft, fontobj, mode->face_size); return 0; } _PGFT_GetRenderMetrics(mode, font_text, &width, &height, &offset, &underline_top, &underline_size); if (width == 0 || height == 0) { /* Nothing more to do. */ if (locked) { SDL_UnlockSurface(surface); } r->x = 0; r->y = 0; r->w = 0; r->h = _PGFT_Font_GetHeightSized(ft, fontobj, mode->face_size); return 0; } surf_offset.x = INT_TO_FX6(x); surf_offset.y = INT_TO_FX6(y); if (mode->render_flags & FT_RFLAG_ORIGIN) { x -= FX6_TRUNC(FX6_CEIL(offset.x)); y -= FX6_TRUNC(FX6_CEIL(offset.y)); } else { surf_offset.x += offset.x; surf_offset.y += offset.y; } /* * Setup target surface struct */ font_surf.buffer = surface->pixels; font_surf.width = surface->w; font_surf.height = surface->h; font_surf.pitch = surface->pitch; font_surf.format = surface->format; font_surf.render_gray = __SDLrenderFuncs[surface->format->BytesPerPixel]; font_surf.render_mono = __MONOrenderFuncs[surface->format->BytesPerPixel]; font_surf.fill = __RGBfillFuncs[surface->format->BytesPerPixel]; /* * if bg color exists, paint background */ if (bgcolor) { if (bgcolor->a == SDL_ALPHA_OPAQUE) { SDL_Rect bg_fill; FT_UInt32 fillcolor; fillcolor = SDL_MapRGBA(surface->format, bgcolor->r, bgcolor->g, bgcolor->b, bgcolor->a); bg_fill.x = (FT_Int16)x; bg_fill.y = (FT_Int16)y; bg_fill.w = (FT_UInt16)width; bg_fill.h = (FT_UInt16)height; SDL_FillRect(surface, &bg_fill, fillcolor); } else { font_surf.fill(INT_TO_FX6(x), INT_TO_FX6(y), INT_TO_FX6(width), INT_TO_FX6(height), &font_surf, bgcolor); } } /* * Render! */ render(ft, font_text, mode, fgcolor, &font_surf, width, height, &surf_offset, underline_top, underline_size); r->x = -(Sint16)FX6_TRUNC(FX6_FLOOR(offset.x)); r->y = (Sint16)FX6_TRUNC(FX6_CEIL(offset.y)); r->w = (Uint16)width; r->h = (Uint16)height; if (locked) { SDL_UnlockSurface(surface); } return 0; }
int _PGFT_Render_Array(FreeTypeInstance *ft, PgFontObject *fontobj, const FontRenderMode *mode, PyObject *arrayobj, PGFT_String *text, int invert, int x, int y, SDL_Rect *r) { static int view_init = 0; Pg_buffer pg_view; Py_buffer *view_p = (Py_buffer *)&pg_view; unsigned width; unsigned height; int itemsize; FT_Vector offset; FT_Vector array_offset; FT_Pos underline_top; FT_Fixed underline_size; FontSurface font_surf; SDL_PixelFormat format; Layout *font_text; /* Get target buffer */ if (!view_init) { import_pygame_base(); if (PyErr_Occurred()) { return -1; } } if (PgObject_GetBuffer(arrayobj, &pg_view, PyBUF_RECORDS)) { return -1; } if (view_p->ndim != 2) { PyErr_Format(PyExc_ValueError, "expecting a 2d target array: got %id array instead", (int)view_p->ndim); PgBuffer_Release(&pg_view); return -1; } if (_validate_view_format(view_p->format)) { PgBuffer_Release(&pg_view); return -1; } width = (unsigned)view_p->shape[0]; height = (unsigned)view_p->shape[1]; itemsize = (unsigned)view_p->itemsize; /* build font text */ font_text = _PGFT_LoadLayout(ft, fontobj, mode, text); if (!font_text) { PgBuffer_Release(&pg_view); return -1; } /* if empty string, then nothing more to do */ if (font_text->length == 0) { PgBuffer_Release(&pg_view); r->x = 0; r->y = 0; r->w = 0; r->h = _PGFT_Font_GetHeightSized(ft, fontobj, mode->face_size); return 0; } _PGFT_GetRenderMetrics(mode, font_text, &width, &height, &offset, &underline_top, &underline_size); if (width == 0 || height == 0) { /* Nothing more to do. */ PgBuffer_Release(&pg_view); r->x = 0; r->y = 0; r->w = 0; r->h = _PGFT_Font_GetHeightSized(ft, fontobj, mode->face_size); return 0; } array_offset.x = INT_TO_FX6(x); array_offset.y = INT_TO_FX6(y); if (mode->render_flags & FT_RFLAG_ORIGIN) { x -= FX6_TRUNC(FX6_CEIL(offset.x)); y -= FX6_TRUNC(FX6_CEIL(offset.y)); } else { array_offset.x += offset.x; array_offset.y += offset.y; } /* * Setup target surface struct */ format.BytesPerPixel = itemsize; #if SDL_BYTEORDER == SDL_LIL_ENDIAN format.Ashift = _is_swapped(view_p) ? (itemsize - 1) * 8 : 0; #else format.Ashift = _is_swapped(view_p) ? 0 : (itemsize - 1) * 8; #endif font_surf.buffer = view_p->buf; font_surf.width = (unsigned)view_p->shape[0]; font_surf.height = (unsigned)view_p->shape[1]; font_surf.item_stride = (unsigned)view_p->strides[0]; font_surf.pitch = (unsigned)view_p->strides[1]; font_surf.format = &format; font_surf.render_gray = __render_glyph_INT; font_surf.render_mono = __render_glyph_MONO_as_INT; font_surf.fill = __fill_glyph_INT; render(ft, font_text, mode, invert ? &mono_transparent : &mono_opaque, &font_surf, width, height, &array_offset, underline_top, underline_size); PgBuffer_Release(&pg_view); r->x = -(Sint16)FX6_TRUNC(FX6_FLOOR(offset.x)); r->y = (Sint16)FX6_TRUNC(FX6_CEIL(offset.y)); r->w = (Uint16)width; r->h = (Uint16)height; return 0; }
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; }