8000 SDL_ttf/SDL_ttf.c at main · Kade-github/SDL_ttf · GitHub
[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
3873 lines (3348 loc) · 133 KB

SDL_ttf.c

File metadata and controls

3873 lines (3348 loc) · 133 KB
/*
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
Copyright (C) 2001-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL.h"
#include "SDL_cpuinfo.h"
#include "SDL_endian.h"
#include "SDL_ttf.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#include FT_STROKER_H
#include FT_GLYPH_H
#include FT_TRUETYPE_IDS_H
#include FT_IMAGE_H
/* Enable rendering with color
* Freetype may need to be compiled with FT_CONFIG_OPTION_USE_PNG */
#if defined(FT_HAS_COLOR)
# define TTF_USE_COLOR 1
#else
# define TTF_USE_COLOR 0
#endif
/* Enable Signed Distance Field rendering (requires latest FreeType version) */
#if defined(FT_RASTER_FLAG_SDF)
# define TTF_USE_SDF 1
#else
# define TTF_USE_SDF 0
#endif
#if TTF_USE_SDF
#include FT_MODULE_H
#endif
/* Enable HarfBuzz for Complex text rendering
* Freetype may need to be compiled with FT_CONFIG_OPTION_USE_HARFBUZZ */
#ifndef TTF_USE_HARFBUZZ
# define TTF_USE_HARFBUZZ 0
#endif
#if TTF_USE_HARFBUZZ
#include <hb.h>
#include <hb-ft.h>
/* Default configuration */
static hb_direction_t g_hb_direction = HB_DIRECTION_LTR;
static hb_script_t g_hb_script = HB_SCRIPT_UNK 10000 NOWN;
#endif
/* Harfbuzz */
int TTF_SetDirection(int direction) /* hb_direction_t */
{
#if TTF_USE_HARFBUZZ
g_hb_direction = direction;
return 0;
#else
(void) direction;
return -1;
#endif
}
int TTF_SetScript(int script) /* hb_script_t */
{
#if TTF_USE_HARFBUZZ
g_hb_script = script;
return 0;
#else
(void) script;
return -1;
#endif
}
/* Round glyph to 16 bytes width and use SSE2 instructions */
#if defined(__SSE2__)
# define HAVE_SSE2_INTRINSICS 1
#endif
/* Round glyph width to 16 bytes use NEON instructions */
#if 0 /*defined(__ARM_NEON)*/
# define HAVE_NEON_INTRINSICS 1
#endif
/* Round glyph width to 8 bytes */
#define HAVE_BLIT_GLYPH_64
/* Android armeabi-v7a doesn't like int64 (Maybe all other __ARM_ARCH < 7 ?),
* un-activate it, especially if NEON isn't detected */
#if defined(__ARM_ARCH)
# if __ARM_ARCH < 8
# if defined(HAVE_BLIT_GLYPH_64)
# undef HAVE_BLIT_GLYPH_64
# endif
# endif
#endif
/* Default: round glyph width to 4 bytes to copy them faster */
#define HAVE_BLIT_GLYPH_32
/* Use Duff's device to unroll loops */
//#define USE_DUFFS_LOOP
#if defined(HAVE_SSE2_INTRINSICS)
static SDL_INLINE int hasSSE2()
{
static int val = -1;
if (val != -1) {
return val;
}
val = SDL_HasSSE2();
return val;
}
#endif
#if defined(HAVE_NEON_INTRINSICS)
static SDL_INLINE int hasNEON()
{
static int val = -1;
if (val != -1) {
return val;
}
val = SDL_HasNEON();
return val;
}
#endif
/* FIXME: Right now we assume the gray-scale renderer Freetype is using
supports 256 shades of gray, but we should instead key off of num_grays
in the result FT_Bitmap after the FT_Render_Glyph() call. */
#define NUM_GRAYS 256
/* x offset = cos(((90.0-12)/360) * 2 * M_PI), or 12 degree angle */
/* same value as in FT_GlyphSlot_Oblique, fixed point 16.16 */
#define GLYPH_ITALICS 0x0366AL
/* Handy routines for converting from fixed point 26.6 */
#define FT_FLOOR(X) (((X) & -64) / 64)
#define FT_CEIL(X) FT_FLOOR((X) + 63)
/* Handy routine for converting to fixed point 26.6 */
#define F26Dot6(X) ((X) << 6)
/* Faster divide by 255, with same result
* in range [0; 255]: (x + 1 + (x >> 8)) >> 8
* in range [-255; 0]: (x + 255 + (x >> 8)) >> 8 */
#define DIVIDE_BY_255_SIGNED(x, sign_val) (((x) + (sign_val) + ((x)>>8)) >> 8)
/* When x positive */
#define DIVIDE_BY_255(x) DIVIDE_BY_255_SIGNED(x, 1)
#define CACHED_METRICS 0x10
#define CACHED_BITMAP 0x01
#define CACHED_PIXMAP 0x02
#define CACHED_COLOR 0x04
#define CACHED_SUBPIX 0x08
typedef struct {
unsigned char *buffer; /* aligned */
int left;
int top;
int width;
int rows;
int pitch;
int is_color;
} TTF_Image;
/* Cached glyph information */
typedef struct cached_glyph {
int stored;
FT_UInt index;
TTF_Image bitmap;
TTF_Image pixmap;
int sz_left;
int sz_top;
int sz_width;
int sz_rows;
int advance;
union {
/* TTF_HINTING_LIGHT_SUBPIXEL (only pixmap) */
struct {
int lsb_minus_rsb;
int translation;
} subpixel;
/* Other hinting */
struct {
int rsb_delta;
int lsb_delta;
} kerning_smart;
};
} c_glyph;
/* Internal buffer to store positions computed by TTF_Size_Internal()
* for rendered string by Render_Line() */
typedef struct PosBuf {
FT_UInt index;
int x;
int y;
} PosBuf_t;
/* The structure used to hold internal font information */
struct _TTF_Font {
/* Freetype2 maintains all sorts of useful info itself */
FT_Face face;
/* We'll cache these ourselves */
int height;
int ascent;
int descent;
int lineskip;
/* The font style */
int style;
int outline_val;
/* Whether kerning is desired */
int allow_kerning;
int use_kerning;
/* Extra width in glyph bounds for text styles */
int glyph_overhang;
/* Information in the font for underlining */
int line_thickness;
int underline_top_row;
int strikethrough_top_row;
/* Cache for style-transformed glyphs */
c_glyph cache[256];
FT_UInt cache_index[128];
/* We are responsible for closing the font stream */
SDL_RWops *src;
int freesrc;
FT_Open_Args args;
/* Internal buffer to store positions computed by TTF_Size_Internal()
* for rendered string by Render_Line() */
PosBuf_t *pos_buf;
Uint32 pos_len;
Uint32 pos_max;
/* Hinting modes */
int ft_load_target;
int render_subpixel;
#if TTF_USE_HARFBUZZ
hb_font_t *hb_font;
hb_script_t hb_script;
hb_direction_t hb_direction;
#endif
int render_sdf;
};
/* Tell if SDL_ttf has to handle the style */
#define TTF_HANDLE_STYLE_BOLD(font) ((font)->style & TTF_STYLE_BOLD)
#define TTF_HANDLE_STYLE_ITALIC(font) ((font)->style & TTF_STYLE_ITALIC)
#define TTF_HANDLE_STYLE_UNDERLINE(font) ((font)->style & TTF_STYLE_UNDERLINE)
#define TTF_HANDLE_STYLE_STRIKETHROUGH(font) ((font)->style & TTF_STYLE_STRIKETHROUGH)
/* Font styles that does not impact glyph drawing */
#define TTF_STYLE_NO_GLYPH_CHANGE (TTF_STYLE_UNDERLINE | TTF_STYLE_STRIKETHROUGH)
/* The FreeType font engine/library */
static FT_Library library = NULL;
static int TTF_initialized = 0;
static SDL_bool TTF_byteswapped = SDL_FALSE;
#define TTF_CHECK_INITIALIZED(errval) \
if (!TTF_initialized) { \
TTF_SetError("Library not initialized"); \
return errval; \
}
#define TTF_CHECK_POINTER(p, errval) \
if (!(p)) { \
TTF_SetError("Passed a NULL pointer"); \
return errval; \
}
typedef enum {
RENDER_SOLID = 0,
RENDER_SHADED,
RENDER_BLENDED
} render_mode_t;
typedef enum {
STR_UTF8 = 0,
STR_TEXT,
STR_UNICODE
} str_type_t;
static int TTF_initFontMetrics(TTF_Font *font);
static int TTF_Size_Internal(TTF_Font *font, const char *text, str_type_t str_type,
int *w, int *h, int *xstart, int *ystart, int measure_width, int *extent, int *count);
#define NO_MEASUREMENT \
0, NULL, NULL
static SDL_Surface* TTF_Render_Internal(TTF_Font *font, const char *text, str_type_t str_type,
SDL_Color fg, SDL_Color bg, render_mode_t render_mode);
static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text, str_type_t str_type,
SDL_Color fg, SDL_Color bg, Uint32 wrapLength, render_mode_t render_mode);
static SDL_INLINE int Find_GlyphByIndex(TTF_Font *font, FT_UInt idx,
int want_bitmap, int want_pixmap, int want_color, int want_subpixel,
int translation, c_glyph **out_glyph, TTF_Image **out_image);
static void Flush_Cache(TTF_Font *font);
#if defined(USE_DUFFS_LOOP)
/* 4-times unrolled loop */
#define DUFFS_LOOP4(pixel_copy_increment, width) \
{ int n = (width+3)/4; \
switch (width & 3) { \
case 0: do { pixel_copy_increment; /* fallthrough */ \
case 3: pixel_copy_increment; /* fallthrough */ \
case 2: pixel_copy_increment; /* fallthrough */ \
case 1: pixel_copy_increment; /* fallthrough */ \
} while (--n > 0); \
} \
}
#else
/* Don't use Duff's device to unroll loops */
#define DUFFS_LOOP(pixel_copy_increment, width) \
{ int n; \
for ( n=width; n > 0; --n ) { \
pixel_copy_increment; \
} \
}
#define DUFFS_LOOP4(pixel_copy_increment, width) \
DUFFS_LOOP(pixel_copy_increment, width)
#endif
/* Blend colored glyphs */
static SDL_INLINE void BG_Blended_Color(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip, Uint8 fg_alpha)
{
const Uint32 *src = (Uint32 *)image->buffer;
Uint32 *dst = destination;
Uint32 width = image->width;
Uint32 height = image->rows;
if (fg_alpha == 0) { /* SDL_ALPHA_OPAQUE */
while (height--) {
/* *INDENT-OFF* */
DUFFS_LOOP4(
*dst++ = *src++;
, width);
/* *INDENT-ON* */
src = (const Uint32 *)((const Uint8 *)src + srcskip);
dst = (Uint32 *)((Uint8 *)dst + dstskip);
}
} else {
Uint32 alpha;
Uint32 tmp;
while (height--) {
/* *INDENT-OFF* */
DUFFS_LOOP4(
tmp = *src++;
alpha = tmp >> 24;
tmp &= ~0xFF000000;
alpha = fg_alpha * alpha;
alpha = DIVIDE_BY_255(alpha) << 24;
*dst++ = tmp | alpha
, width);
/* *INDENT-ON* */
src = (const Uint32 *)((const Uint8 *)src + srcskip);
dst = (Uint32 *)((Uint8 *)dst + dstskip);
}
}
}
#if TTF_USE_SDF
/* Blended Opaque SDF */
static SDL_INLINE void BG_Blended_Opaque_SDF(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip)
{
const Uint8 *src = image->buffer;
Uint32 *dst = destination;
Uint32 width = image->width;
Uint32 height = image->rows;
Uint32 s;
Uint32 d;
while (height--) {
/* *INDENT-OFF* */
DUFFS_LOOP4(
d = *dst;
s = *src++ << 24;
if (s > d) {
*dst = s;
}
dst++;
, width);
/* *INDENT-ON* */
src += srcskip;
dst = (Uint32 *)((Uint8 *)dst + dstskip);
}
}
/* Blended non-opaque SDF */
static SDL_INLINE void BG_Blended_SDF(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip, Uint8 fg_alpha)
{
const Uint8 *src = image->buffer;
Uint32 *dst = destination;
Uint32 width = image->width;
Uint32 height = image->rows;
Uint32 s;
Uint32 d;
Uint32 tmp;
while (height--) {
/* *INDENT-OFF* */
DUFFS_LOOP4(
d = *dst;
tmp = fg_alpha * (*src++);
s = DIVIDE_BY_255(tmp) << 24;
if (s > d) {
*dst = s;
}
dst++;
, width);
/* *INDENT-ON* */
src += srcskip;
dst = (Uint32 *)((Uint8 *)dst + dstskip);
}
}
#endif /* TTF_USE_SDF */
/* Blended Opaque */
static SDL_INLINE void BG_Blended_Opaque(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip)
{
const Uint8 *src = image->buffer;
Uint32 *dst = destination;
Uint32 width = image->width;
Uint32 height = image->rows;
while (height--) {
/* *INDENT-OFF* */
DUFFS_LOOP4(
*dst++ |= *src++ << 24;
, width);
/* *INDENT-ON* */
src += srcskip;
dst = (Uint32 *)((Uint8 *)dst + dstskip);
}
}
/* Blended non-opaque */
static SDL_INLINE void BG_Blended(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip, Uint8 fg_alpha)
{
const Uint8 *src = image->buffer;
Uint32 *dst = destination;
Uint32 width = image->width;
Uint32 height = image->rows;
Uint32 tmp;
while (height--) {
/* *INDENT-OFF* */
DUFFS_LOOP4(
tmp = fg_alpha * (*src++);
*dst++ |= DIVIDE_BY_255(tmp) << 24;
, width);
/* *INDENT-ON* */
src += srcskip;
dst = (Uint32 *)((Uint8 *)dst + dstskip);
}
}
#if defined(HAVE_BLIT_GLYPH_32) || defined(HAVE_BLIT_GLYPH_64)
static SDL_INLINE void BG_Blended_Opaque_32(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip)
{
const Uint8 *src = image->buffer;
Uint32 *dst = destination;
Uint32 width = image->width / 4;
Uint32 height = image->rows;
while (height--) {
/* *INDENT-OFF* */
DUFFS_LOOP4(
*dst++ |= *src++ << 24;
*dst++ |= *src++ << 24;
*dst++ |= *src++ << 24;
*dst++ |= *src++ << 24;
, width);
/* *INDENT-ON* */
src += srcskip;
dst = (Uint32 *)((Uint8 *)dst + dstskip);
}
}
static SDL_INLINE void BG_Blended_32(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip, Uint8 fg_alpha)
{
const Uint8 *src = image->buffer;
Uint32 *dst = destination;
Uint32 width = image->width / 4;
Uint32 height = image->rows;
Uint32 tmp0, tmp1, tmp2, tmp3;
while (height--) {
/* *INDENT-OFF* */
DUFFS_LOOP4(
tmp0 = fg_alpha * (*src++);
tmp1 = fg_alpha * (*src++);
tmp2 = fg_alpha * (*src++);
tmp3 = fg_alpha * (*src++);
*dst++ |= DIVIDE_BY_255(tmp0) << 24;
*dst++ |= DIVIDE_BY_255(tmp1) << 24;
*dst++ |= DIVIDE_BY_255(tmp2) << 24;
*dst++ |= DIVIDE_BY_255(tmp3) << 24;
, width);
/* *INDENT-ON* */
src += srcskip;
dst = (Uint32 *)((Uint8 *)dst + dstskip);
}
}
#endif
#if defined(HAVE_SSE2_INTRINSICS)
/* Apply: alpha_table[i] = i << 24; */
static SDL_INLINE void BG_Blended_Opaque_SSE(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip)
{
const __m128i *src = (__m128i *)image->buffer;
__m128i *dst = (__m128i *)destination;
Uint32 width = image->width / 16;
Uint32 height = image->rows;
__m128i s, s0, s1, s2, s3, d0, d1, d2, d3, r0, r1, r2, r3, L, H;
const __m128i zero = _mm_setzero_si128();
while (height--) {
/* *INDENT-OFF* */
DUFFS_LOOP4(
/* Read 16 Uint8 at once and put into 4 __m128i */
s = _mm_loadu_si128(src); // load unaligned
d0 = _mm_load_si128(dst); // load
d1 = _mm_load_si128(dst + 1); // load
d2 = _mm_load_si128(dst + 2); // load
d3 = _mm_load_si128(dst + 3); // load
L = _mm_unpacklo_epi8(zero, s);
H = _mm_unpackhi_epi8(zero, s);
s0 = _mm_unpacklo_epi8(zero, L);
s1 = _mm_unpackhi_epi8(zero, L);
s2 = _mm_unpacklo_epi8(zero, H);
s3 = _mm_unpackhi_epi8(zero, H);
// already shifted by 24
r0 = _mm_or_si128(d0, s0); // or
r1 = _mm_or_si128(d1, s1); // or
r2 = _mm_or_si128(d2, s2); // or
r3 = _mm_or_si128(d3, s3); // or
_mm_store_si128(dst, r0); // store
_mm_store_si128(dst + 1, r1); // store
_mm_store_si128(dst + 2, r2); // store
_mm_store_si128(dst + 3, r3); // store
dst += 4;
src += 1;
, width);
/* *INDENT-ON* */
src = (const __m128i *)((const Uint8 *)src + srcskip);
dst = (__m128i *)((Uint8 *)dst + dstskip);
}
}
static SDL_INLINE void BG_Blended_SSE(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip, Uint8 fg_alpha)
{
const __m128i *src = (__m128i *)image->buffer;
__m128i *dst = (__m128i *)destination;
Uint32 width = image->width / 16;
Uint32 height = image->rows;
const __m128i alpha = _mm_set1_epi16(fg_alpha);
const __m128i >
const __m128i zero = _mm_setzero_si128();
__m128i s, s0, s1, s2, s3, d0, d1, d2, d3, r0, r1, r2, r3, L, H, Ls8, Hs8;
while (height--) {
/* *INDENT-OFF* */
DUFFS_LOOP4(
/* Read 16 Uint8 at once and put into 4 __m128i */
s = _mm_loadu_si128(src); // load unaligned
d0 = _mm_load_si128(dst); // load
d1 = _mm_load_si128(dst + 1); // load
d2 = _mm_load_si128(dst + 2); // load
d3 = _mm_load_si128(dst + 3); // load
L = _mm_unpacklo_epi8(s, zero); // interleave, no shifting
H = _mm_unpackhi_epi8(s, zero); // enough room to multiply
/* Apply: alpha_table[i] = ((i * fg.a / 255) << 24; */
/* Divide by 255 is done as: (x + 1 + (x >> 8)) >> 8 */
L = _mm_mullo_epi16(L, alpha); // x := i * fg.a
H = _mm_mullo_epi16(H, alpha);
Ls8 = _mm_srli_epi16(L, 8); // x >> 8
Hs8 = _mm_srli_epi16(H, 8);
L = _mm_add_epi16(L, one); // x + 1
H = _mm_add_epi16(H, one);
L = _mm_add_epi16(L, Ls8); // x + 1 + (x >> 8)
H = _mm_add_epi16(H, Hs8);
L = _mm_srli_epi16(L, 8); // ((x + 1 + (x >> 8)) >> 8
H = _mm_srli_epi16(H, 8);
L = _mm_slli_epi16(L, 8); // shift << 8, so we're prepared
H = _mm_slli_epi16(H, 8); // to have final format << 24
s0 = _mm_unpacklo_epi8(zero, L);
s1 = _mm_unpackhi_epi8(zero, L);
s2 = _mm_unpacklo_epi8(zero, H);
s3 = _mm_unpackhi_epi8(zero, H);
// already shifted by 24
r0 = _mm_or_si128(d0, s0); // or
r1 = _mm_or_si128(d1, s1); // or
r2 = _mm_or_si128(d2, s2); // or
r3 = _mm_or_si128(d3, s3); // or
_mm_store_si128(dst, r0); // store
_mm_store_si128(dst + 1, r1); // store
_mm_store_si128(dst + 2, r2); // store
_mm_store_si128(dst + 3, r3); // store
dst += 4;
src += 1;
, width);
/* *INDENT-ON* */
src = (const __m128i *)((const Uint8 *)src + srcskip);
dst = (__m128i *)((Uint8 *)dst + dstskip);
}
}
#endif
#if defined(HAVE_NEON_INTRINSICS)
/* Apply: alpha_table[i] = i << 24; */
static SDL_INLINE void BG_Blended_Opaque_NEON(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip)
{
const Uint32 *src = (Uint32 *)image->buffer;
Uint32 *dst = destination;
Uint32 width = image->width / 16;
Uint32 height = image->rows;
uint32x4_t s, d0, d1, d2, d3, r0, r1, r2, r3;
uint8x16x2_t sx, sx01, sx23;
uint32x4_t zero = vmovq_n_u32(0);
while (height--) {
/* *INDENT-OFF* */
DUFFS_LOOP4(
/* Read 4 Uint32 and put 16 Uint8 into uint32x4x2_t (uint8x16x2_t)
* takes advantage of vzipq_u8 which produces two lanes */
s = vld1q_u32(src); // load
d0 = vld1q_u32(dst); // load
d1 = vld1q_u32(dst + 4); // load
d2 = vld1q_u32(dst + 8); // load
d3 = vld1q_u32(dst + 12); // load
sx = vzipq_u8(zero, s); // interleave
sx01 = vzipq_u8(zero, sx.val[0]); // interleave
sx23 = vzipq_u8(zero, sx.val[1]); // interleave
// already shifted by 24
r0 = vorrq_u32(d0, sx01.val[0]); // or
r1 = vorrq_u32(d1, sx01.val[1]); // or
r2 = vorrq_u32(d2, sx23.val[0]); // or
r3 = vorrq_u32(d3, sx23.val[1]); // or
vst1q_u32(dst, r0); // store
vst1q_u32(dst + 4, r1); // store
vst1q_u32(dst + 8, r2); // store
vst1q_u32(dst + 12, r3); // store
dst += 16;
src += 4;
, width);
/* *INDENT-ON* */
src = (const Uint32 *)((const Uint8 *)src + srcskip);
dst = (Uint32 *)((Uint8 *)dst + dstskip);
}
}
/* Non-opaque, computes alpha blending on the fly */
static SDL_INLINE void BG_Blended_NEON(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip, Uint8 fg_alpha)
{
const Uint32 *src = (Uint32 *)image->buffer;
Uint32 *dst = destination;
Uint32 width = image->width / 16;
Uint32 height = image->rows;
uint32x4_t s, d0, d1, d2, d3, r0, r1, r2, r3;
uint16x8_t Ls8, Hs8;
uint8x16x2_t sx, sx01, sx23;
const uint16x8_t alpha = vmovq_n_u16(fg_alpha);
const uint16x8_t >
const uint32x4_t zero = vmovq_n_u32(0);
while (height--) {
/* *INDENT-OFF* */
DUFFS_LOOP4(
/* Read 4 Uint32 and put 16 Uint8 into uint32x4x2_t (uint8x16x2_t)
* takes advantage of vzipq_u8 which produces two lanes */
s = vld1q_u32(src); // load
d0 = vld1q_u32(dst); // load
d1 = vld1q_u32(dst + 4); // load
d2 = vld1q_u32(dst + 8); // load
d3 = vld1q_u32(dst + 12); // load
sx = vzipq_u8(s, zero); // interleave, no shifting
// enough room to multiply
/* Apply: alpha_table[i] = ((i * fg.a / 255) << 24; */
/* Divide by 255 is done as: (x + 1 + (x >> 8)) >> 8 */
sx.val[0] = vmulq_u16(sx.val[0], alpha); // x := i * fg.a
sx.val[1] = vmulq_u16(sx.val[1], alpha);
Ls8 = vshrq_n_u16(sx.val[0], 8); // x >> 8
Hs8 = vshrq_n_u16(sx.val[1], 8);
sx.val[0] = vaddq_u16(sx.val[0], one); // x + 1
sx.val[1] = vaddq_u16(sx.val[1], one);
sx.val[0] = vaddq_u16(sx.val[0], Ls8); // x + 1 + (x >> 8)
sx.val[1] = vaddq_u16(sx.val[1], Hs8);
sx.val[0] = vshrq_n_u16(sx.val[0], 8); // ((x + 1 + (x >> 8)) >> 8
sx.val[1] = vshrq_n_u16(sx.val[1], 8);
sx.val[0] = vshlq_n_u16(sx.val[0], 8); // shift << 8, so we're prepared
sx.val[1] = vshlq_n_u16(sx.val[1], 8); // to have final format << 24
sx01 = vzipq_u8(zero, sx.val[0]); // interleave
sx23 = vzipq_u8(zero, sx.val[1]); // interleave
// already shifted by 24
r0 = vorrq_u32(d0, sx01.val[0]); // or
r1 = vorrq_u32(d1, sx01.val[1]); // or
r2 = vorrq_u32(d2, sx23.val[0]); // or
r3 = vorrq_u32(d3, sx23.val[1]); // or
vst1q_u32(dst, r0); // store
vst1q_u32(dst + 4, r1); // store
vst1q_u32(dst + 8, r2); // store
vst1q_u32(dst + 12, r3); // store
dst += 16;
src += 4;
, width);
/* *INDENT-ON* */
src = (const Uint32 *)((const Uint8 *)src + srcskip);
dst = (Uint32 *)((Uint8 *)dst + dstskip);
}
}
#endif
static SDL_INLINE void BG(const TTF_Image *image, Uint8 *destination, Sint32 srcskip, Uint32 dstskip)
{
const Uint8 *src = image->buffer;
Uint8 *dst = destination;
Uint32 width = image->width;
Uint32 height = image->rows;
while (height--) {
/* *INDENT-OFF* */
DUFFS_LOOP4(
*dst++ |= *src++;
, width);
/* *INDENT-ON* */
src += srcskip;
dst += dstskip;
}
}
#if defined(HAVE_BLIT_GLYPH_64)
static SDL_INLINE void BG_64(const TTF_Image *image, Uint8 *destination, Sint32 srcskip, Uint32 dstskip)
{
const Uint64 *src = (Uint64 *)image->buffer;
Uint64 *dst = (Uint64 *)destination;
Uint32 width = image->width / 8;
Uint32 height = image->rows;
while (height--) {
/* *INDENT-OFF* */
DUFFS_LOOP4(
*dst++ |= *src++;
, width);
/* *INDENT-ON* */
src = (const Uint64 *)((const Uint8 *)src + srcskip);
dst = (Uint64 *)((Uint8 *)dst + dstskip);
}
}
#elif defined(HAVE_BLIT_GLYPH_32)
static SDL_INLINE void BG_32(const TTF_Image *image, Uint8 *destination, Sint32 srcskip, Uint32 dstskip)
{
const Uint32 *src = (Uint32 *)image->buffer;
Uint32 *dst = (Uint32 *)destination;
Uint32 width = image->width / 4;
Uint32 height = image->rows;
while (height--) {
/* *INDENT-OFF* */
DUFFS_LOOP4(
*dst++ |= *src++;
, width);
/* *INDENT-ON* */
src = (const Uint32 *)((const Uint8 *)src + srcskip);
dst = (Uint32 *)((Uint8 *)dst + dstskip);
}
}
#endif
#if defined(HAVE_SSE2_INTRINSICS)
static SDL_INLINE void BG_SSE(const TTF_Image *image, Uint8 *destination, Sint32 srcskip, Uint32 dstskip)
{
const __m128i *src = (__m128i *)image->buffer;
__m128i *dst = (__m128i *)destination;
Uint32 width = image->width / 16;
Uint32 height = image->rows;
__m128i s, d, r;
while (height--) {
8
/* *INDENT-OFF* */
DUFFS_LOOP4(
s = _mm_loadu_si128(src); // load unaligned
d = _mm_load_si128(dst); // load
r = _mm_or_si128(d, s); // or
_mm_store_si128(dst, r); // store
src += 1;
dst += 1;
, width);
/* *INDENT-ON* */
src = (const __m128i *)((const Uint8 *)src + srcskip);
dst = (__m128i *)((Uint8 *)dst + dstskip);
}
}
#endif
#if defined(HAVE_NEON_INTRINSICS)
static SDL_INLINE void BG_NEON(const TTF_Image *image, Uint8 *destination, Sint32 srcskip, Uint32 dstskip)
{
const Uint8 *src = image->buffer;
Uint8 *dst = destination;
Uint32 width = image->width / 16;
Uint32 height = image->rows;
uint8x16_t s, d, r;
while (height--) {
/* *INDENT-OFF* */
DUFFS_LOOP4(
s = vld1q_u8(src); // load
d = vld1q_u8(dst); // load
r = vorrq_u8(d, s); // or
vst1q_u8(dst, r); // store
src += 16;
dst += 16;
, width);
/* *INDENT-ON* */
src = (const Uint8 *)((const Uint8 *)src + srcskip);
dst += dstskip;
}
}
#endif
/* Underline and Strikethrough style. Draw a line at the given row. */
static void Draw_Line(TTF_Font *font, const SDL_Surface *textbuf, int row, int line_width, int line_thickness, Uint32 color, const render_mode_t render_mode)
{
int tmp = row + line_thickness - textbuf->h;
Uint8 *dst = (Uint8 *)textbuf->pixels + row * textbuf->pitch;
#if TTF_USE_HARFBUZZ
/* No Underline/Strikethrough style if direction is vertical */
if (font->hb_direction == HB_DIRECTION_TTB || font->hb_direction == HB_DIRECTION_BTT) {
return;
}
#endif
/* Not needed because of "font->height = SDL_max(font->height, bottom_row);".
* But if you patch to render textshaping and break line in middle of a cluster,
* (which is a bad usage and a corner case), you need this to prevent out of bounds.
* You can get an "ystart" for the "whole line", which is different (and smaller)
* than the ones of the "splitted lines". */
if (tmp > 0) {
line_thickness -= tmp;
}
/* Wrapped mode with an unbroken line: 'line_width' is greater that 'textbuf->w' */
line_width = SDL_min(line_width, textbuf->w);
if (render_mode == RENDER_BLENDED) {
while (line_thickness--) {
SDL_memset4(dst, color, line_width);
dst += textbuf->pitch;
}
} else {
while (line_thickness--) {
SDL_memset(dst, color, line_width);
dst += textbuf->pitch;
}
}
}
static void clip_glyph(int *_x, int *_y, TTF_Image *image, const SDL_Surface *textbuf)
{
int above_w;
int above_h;
int x = *_x;
int y = *_y;
int srcbpp = 1;
if (image->is_color) {
/* This isn't tested because colored image never ends up left clipped */
srcbpp = 4;
}
/* Don't go below x=0 */
if (x < 0) {
int tmp = -x;
x = 0;
image->width -= tmp;
image->buffer += srcbpp * tmp;
}
/* Don't go above textbuf->w */
above_w = x + image->width - textbuf->w;
if (above_w > 0) {
image->width -= above_w;
}
/* Don't go below y=0 */
if (y < 0) {
int tmp = -y;
y = 0;
image->rows -= tmp;
image->buffer += tmp * image->pitch;
}
/* Don't go above textbuf->h */
above_h = y + image->rows - textbuf->h;
if (above_h > 0) {
image->rows -= above_h;
}
/* Could be negative if (x > textbuf->w), or if (x + width < 0) */
image->width = SDL_max(0, image->width);
image->rows = SDL_max(0, image->rows);
/* After 'image->width' clipping:
* Make sure 'rows' is also 0, so it doesn't break USE_DUFFS_LOOP */
if (image->width == 0) {
image->rows = 0;
}
*_x = x;
*_y = y;
}
/* Glyph width is rounded, dst addresses are aligned, src addresses are not aligned */
static int Get_Alignement()
{
#if defined(HAVE_NEON_INTRINSICS)
if (hasNEON()) {
return 16;
}
0