/********************************************************* * * 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); } } }
void _PGFT_GetRenderMetrics(const FontRenderMode *mode, Layout *text, unsigned *w, unsigned *h, FT_Vector *offset, FT_Pos *underline_top, FT_Fixed *underline_size) { FT_Pos min_x = text->min_x; FT_Pos max_x = text->max_x; FT_Pos min_y = text->min_y; FT_Pos max_y = text->max_y; *underline_top = 0; *underline_size = 0; if (mode->style & FT_STYLE_UNDERLINE) { FT_Fixed half_size = (text->underline_size + 1) / 2; FT_Fixed adjusted_pos; FT_Fixed uline_top; FT_Fixed uline_bottom; if (mode->underline_adjustment < 0) { adjusted_pos = FT_MulFix(text->ascender, mode->underline_adjustment); } else { adjusted_pos = FT_MulFix(text->underline_pos, mode->underline_adjustment); } uline_top = adjusted_pos - half_size; uline_bottom = uline_top + text->underline_size; if (uline_bottom > max_y) { max_y = uline_bottom; } if (uline_top < min_y) { min_y = uline_top; } *underline_size = text->underline_size; *underline_top = uline_top; } offset->x = -min_x; offset->y = -min_y; *w = (unsigned)FX6_TRUNC(FX6_CEIL(max_x) - FX6_FLOOR(min_x)); *h = (unsigned)FX6_TRUNC(FX6_CEIL(max_y) - FX6_FLOOR(min_y)); }
long _PGFT_Face_GetAscenderSized(FreeTypeInstance *ft, PgFaceObject *faceobj, FT_UInt16 ptsize) { FT_Face face = _PGFT_GetFaceSized(ft, faceobj, ptsize); if (!face) { RAISE(PyExc_RuntimeError, _PGFT_GetError(ft)); return 0; } return (long)FX6_TRUNC(FX6_CEIL(face->size->metrics.ascender)); }
long _PGFT_Font_GetAscenderSized(FreeTypeInstance *ft, PgFontObject *fontobj, FT_UInt16 ptsize) { FT_Face font = _PGFT_GetFontSized(ft, fontobj, ptsize); if (!font) { RAISE(PyExc_RuntimeError, _PGFT_GetError(ft)); return 0; } return (long)FX6_TRUNC(FX6_CEIL(font->size->metrics.ascender)); }
long _PGFT_Face_GetGlyphHeightSized(FreeTypeInstance *ft, PgFaceObject *faceobj, FT_UInt16 ptsize) { /* * Based on the SDL_ttf height calculation. */ FT_Face face = _PGFT_GetFaceSized(ft, faceobj, ptsize); FT_Size_Metrics *metrics; if (!face) { RAISE(PyExc_RuntimeError, _PGFT_GetError(ft)); return 0; } metrics = &face->size->metrics; return (long)FX6_TRUNC(FX6_CEIL(metrics->ascender) - FX6_FLOOR(metrics->descender)) + /* baseline */ 1; }
int _PGFT_GetTextRect(FreeTypeInstance *ft, PgFaceObject *faceobj, const FaceRenderMode *mode, PGFT_String *text, SDL_Rect *r) { FaceText *face_text; unsigned width; unsigned height; FT_Vector offset; FT_Pos underline_size; FT_Pos underline_top; face_text = _PGFT_LoadFaceText(ft, faceobj, mode, text); if (!face_text) { return -1; } _PGFT_GetRenderMetrics(mode, face_text, &width, &height, &offset, &underline_size, &underline_top); 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_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; }
SDL_Surface *_PGFT_Render_NewSurface(FreeTypeInstance *ft, PgFontObject *fontobj, const FontRenderMode *mode, PGFT_String *text, FontColor *fgcolor, FontColor *bgcolor, SDL_Rect *r) { #if SDL_BYTEORDER == SDL_BIG_ENDIAN FT_UInt32 rmask = 0xff000000; FT_UInt32 gmask = 0x00ff0000; FT_UInt32 bmask = 0x0000ff00; FT_UInt32 amask = 0x000000ff; #else FT_UInt32 rmask = 0x000000ff; FT_UInt32 gmask = 0x0000ff00; FT_UInt32 bmask = 0x00ff0000; FT_UInt32 amask = 0xff000000; #endif int locked = 0; FT_UInt32 fillcolor; SDL_Surface *surface = 0; int bits_per_pixel = (bgcolor || mode->render_flags & FT_RFLAG_ANTIALIAS) ? 32 : 8; FT_UInt32 surface_flags = SDL_SWSURFACE; FontSurface font_surf; Layout *font_text; unsigned width; unsigned height; FT_Vector offset; FT_Pos underline_top; FT_Fixed underline_size; FontColor mono_fgcolor = {0, 0, 0, 1}; FontColor mono_bgcolor = {0, 0, 0, 0}; /* build font text */ font_text = _PGFT_LoadLayout(ft, fontobj, mode, text); if (!font_text) { return 0; } if (font_text->length > 0) { _PGFT_GetRenderMetrics(mode, font_text, &width, &height, &offset, &underline_top, &underline_size); } else { width = 1; height = _PGFT_Font_GetHeightSized(ft, fontobj, mode->face_size); offset.x = -font_text->min_x; offset.y = -font_text->min_y; } surface = SDL_CreateRGBSurface(surface_flags, width, height, bits_per_pixel, rmask, gmask, bmask, bits_per_pixel == 32 ? amask : 0); if (!surface) { PyErr_SetString(PyExc_SDLError, SDL_GetError()); return 0; } if (SDL_MUSTLOCK(surface)) { if (SDL_LockSurface(surface) == -1) { PyErr_SetString(PyExc_SDLError, SDL_GetError()); SDL_FreeSurface(surface); return 0; } locked = 1; } 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; if (bits_per_pixel == 32) { font_surf.render_gray = __render_glyph_RGB4; font_surf.render_mono = __render_glyph_MONO4; font_surf.fill = __fill_glyph_RGB4; /* * Fill our texture with the required bg color */ if (bgcolor) { fillcolor = SDL_MapRGBA( surface->format, bgcolor->r, bgcolor->g, bgcolor->b, bgcolor->a); } else { fillcolor = SDL_MapRGBA(surface->format, 0, 0, 0, SDL_ALPHA_TRANSPARENT); } SDL_FillRect(surface, 0, fillcolor); } else { SDL_Color colors[2]; colors[1].r = fgcolor->r; /* Foreground */ colors[1].g = fgcolor->g; colors[1].b = fgcolor->b; colors[0].r = ~colors[1].r; /* Background */ colors[0].g = ~colors[1].g; colors[0].b = ~colors[1].b; if (!SDL_SetColors(surface, colors, 0, 2)) { PyErr_SetString(PyExc_SystemError, "Pygame bug in _PGFT_Render_NewSurface: " "SDL_SetColors failed"); SDL_FreeSurface(surface); return 0; } SDL_SetColorKey(surface, SDL_SRCCOLORKEY, (FT_UInt32)0); if (fgcolor->a != SDL_ALPHA_OPAQUE) { SDL_SetAlpha(surface, SDL_SRCALPHA, fgcolor->a); } fgcolor = &mono_fgcolor; bgcolor = &mono_bgcolor; font_surf.render_gray = __render_glyph_GRAY_as_MONO1; font_surf.render_mono = __render_glyph_MONO_as_GRAY1; font_surf.fill = __fill_glyph_GRAY1; /* * Fill our texture with the required bg color */ SDL_FillRect(surface, 0, 0); } /* * Render the text! */ render(ft, font_text, mode, fgcolor, &font_surf, width, height, &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 surface; }
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; }
FontText * _PGFT_LoadFontText(FreeTypeInstance *ft, PgFontObject *fontobj, const FontRenderMode *mode, PGFT_String *text) { Py_ssize_t string_length = PGFT_String_GET_LENGTH(text); PGFT_char * buffer = PGFT_String_GET_DATA(text); PGFT_char * buffer_end; PGFT_char * ch; FontText *ftext = &(PGFT_INTERNALS(fontobj)->active_text); FontGlyph *glyph = 0; FontGlyph **glyph_array = 0; FontMetrics *metrics; FT_BitmapGlyph image; TextContext context; FT_Face font; FT_Size_Metrics *sz_metrics; FT_Vector pen = {0, 0}; /* untransformed origin */ FT_Vector pen1 = {0, 0}; FT_Vector pen2; FT_Vector *next_pos; int vertical = mode->render_flags & FT_RFLAG_VERTICAL; int use_kerning = mode->render_flags & FT_RFLAG_KERNING; int pad = mode->render_flags & FT_RFLAG_PAD; FT_UInt prev_glyph_index = 0; /* All these are 16.16 precision */ FT_Angle rotation_angle = mode->rotation_angle; /* All these are 26.6 precision */ FT_Vector kerning; FT_Pos min_x = FX6_MAX; FT_Pos max_x = FX6_MIN; FT_Pos min_y = FX6_MAX; FT_Pos max_y = FX6_MIN; FT_Pos glyph_width; FT_Pos glyph_height; FT_Pos top = FX6_MIN; FT_Fixed y_scale; FT_Error error = 0; /* load our sized font */ font = _PGFT_GetFontSized(ft, fontobj, mode->pt_size); if (!font) { PyErr_SetString(PyExc_SDLError, _PGFT_GetError(ft)); return 0; } sz_metrics = &font->size->metrics; y_scale = sz_metrics->y_scale; /* cleanup the cache */ _PGFT_Cache_Cleanup(&ftext->glyph_cache); /* create the text struct */ if (string_length > ftext->buffer_size) { _PGFT_free(ftext->glyphs); ftext->glyphs = (FontGlyph **) _PGFT_malloc((size_t)string_length * sizeof(FontGlyph *)); if (!ftext->glyphs) { PyErr_NoMemory(); return 0; } _PGFT_free(ftext->posns); ftext->posns = (FT_Vector *) _PGFT_malloc((size_t)string_length * sizeof(FT_Vector)); if (!ftext->posns) { PyErr_NoMemory(); return 0; } ftext->buffer_size = string_length; } ftext->length = string_length; ftext->ascender = sz_metrics->ascender; ftext->underline_pos = -FT_MulFix(font->underline_position, y_scale); ftext->underline_size = FT_MulFix(font->underline_thickness, y_scale); if (mode->style & FT_STYLE_STRONG) { FT_Fixed bold_str = mode->strength * sz_metrics->x_ppem; ftext->underline_size = FT_MulFix(ftext->underline_size, FX16_ONE + bold_str / 4); } /* fill it with the glyphs */ fill_context(&context, ft, fontobj, mode, font); glyph_array = ftext->glyphs; next_pos = ftext->posns; for (ch = buffer, buffer_end = ch + string_length; ch < buffer_end; ++ch) { pen2.x = pen1.x; pen2.y = pen1.y; pen1.x = pen.x; pen1.y = pen.y; /* * Load the corresponding glyph from the cache */ glyph = _PGFT_Cache_FindGlyph(*((FT_UInt32 *)ch), mode, &ftext->glyph_cache, &context); if (!glyph) { --ftext->length; continue; } image = glyph->image; glyph_width = glyph->width; glyph_height = glyph->height; /* * Do size calculations for all the glyphs in the text */ if (use_kerning && prev_glyph_index) { error = FT_Get_Kerning(font, prev_glyph_index, glyph->glyph_index, FT_KERNING_UNFITTED, &kerning); if (error) { _PGFT_SetError(ft, "Loading glyphs", error); PyErr_SetString(PyExc_SDLError, _PGFT_GetError(ft)); return 0; } if (rotation_angle != 0) { FT_Vector_Rotate(&kerning, rotation_angle); } pen.x += FX6_ROUND(kerning.x); pen.y += FX6_ROUND(kerning.y); if (FT_Vector_Length(&pen2) > FT_Vector_Length(&pen)) { pen.x = pen2.x; pen.y = pen2.y; } } prev_glyph_index = glyph->glyph_index; metrics = vertical ? &glyph->v_metrics : &glyph->h_metrics; if (metrics->bearing_rotated.y > top) { top = metrics->bearing_rotated.y; } if (pen.x + metrics->bearing_rotated.x < min_x) { min_x = pen.x + metrics->bearing_rotated.x; } if (pen.x + metrics->bearing_rotated.x + glyph_width > max_x) { max_x = pen.x + metrics->bearing_rotated.x + glyph_width; } next_pos->x = pen.x + metrics->bearing_rotated.x; pen.x += metrics->advance_rotated.x; if (vertical) { if (pen.y + metrics->bearing_rotated.y < min_y) { min_y = pen.y + metrics->bearing_rotated.y; } if (pen.y + metrics->bearing_rotated.y + glyph_height > max_y) { max_y = pen.y + metrics->bearing_rotated.y + glyph_height; } next_pos->y = pen.y + metrics->bearing_rotated.y; pen.y += metrics->advance_rotated.y; } else { if (pen.y - metrics->bearing_rotated.y < min_y) { min_y = pen.y - metrics->bearing_rotated.y; } if (pen.y - metrics->bearing_rotated.y + glyph_height > max_y) { max_y = pen.y - metrics->bearing_rotated.y + glyph_height; } next_pos->y = pen.y - metrics->bearing_rotated.y; pen.y -= metrics->advance_rotated.y; } *glyph_array++ = glyph; ++next_pos; } if (ftext->length == 0) { min_x = 0; max_x = 0; if (vertical) { ftext->min_y = 0; max_y = sz_metrics->height; } else { FT_Size_Metrics *sz_metrics = &font->size->metrics; min_y = -sz_metrics->ascender; max_y = -sz_metrics->descender; } } if (pad) { FT_Size_Metrics *sz_metrics = &font->size->metrics; if (pen.x > max_x) { max_x = pen.x; } else if (pen.x < min_x) { min_x = pen.x; } if (pen.y > max_y) { max_y = pen.y; } else if (pen.y < min_y) { min_y = pen.y; } if (vertical) { FT_Fixed right = sz_metrics->max_advance / 2; if (max_x < right) { max_x = right; } if (min_x > -right) { min_x = -right; } if (min_y > 0) { min_y = 0; } else if (max_y < pen.y) { max_y = pen.y; } } else { FT_Fixed ascender = sz_metrics->ascender; FT_Fixed descender = sz_metrics->descender; if (min_x > 0) { min_x = 0; } if (max_x < pen.x) { max_x = pen.x; } if (min_y > -ascender) { min_y = -ascender; } if (max_y <= -descender) { max_y = -descender + /* baseline */ FX6_ONE; } } } ftext->left = FX6_TRUNC(FX6_FLOOR(min_x)); ftext->top = FX6_TRUNC(FX6_CEIL(top)); ftext->min_x = min_x; ftext->max_x = max_x; ftext->min_y = min_y; ftext->max_y = max_y; ftext->advance.x = pen.x; ftext->advance.y = pen.y; return ftext; }