astext(3x)
β¨ astext(3x) β¨
AfterStep X11 window manager π bestie we are in section 3x and we are about to make text look absolutely GORGEOUS π
π NAME
astextβ demonstrates antialiased texturized text renderinglibAfterImage/tutorials/ASText
πΈ NAMEASText πΈ
okay this heading literally has no body text, it's just standing there being iconic π the real tea starts right below, keep scrolling boo π«Ά
π SYNOPSIS
libAfterImage application for rendering texturized text.
π¦ DESCRIPTION
So here's the vibe: in this tutorial we're gonna try to render arbitrary text inside a window, with optional texturized background and foreground π. And because plain text is giving nothing, we also wrap the rendered text in a beveled frame, which creates the illusion of a button β¨. A whole button, bestie. From text. Iconic behaviour fr fr.
The new steps described in this tutorial are:
- ASText.1. Opening and closing fonts π€
- ASText.2. Approximating rendered text size π
- ASText.3. Rendering texturized text π¨
- ASText.4. Merging foreground and background with bevel π§
π SEE ALSO
- Tutorial 1: ASView β explanation of basic steps needed to use
libAfterImageand some other simple things. - Tutorial 2: ASScale β image scaling basics.
- Tutorial 3: ASTile β image tiling and tinting.
- Tutorial 4: ASMerge β scaling and blending of arbitrary number of images.
- Tutorial 5: ASGrad β drawing multipoint linear gradients.
- Tutorial 6: ASFlip β image rotation.
π SOURCE
no cap, here is the whole thing, character for character, do NOT let me catch you retyping it by hand π
#include "../afterbase.h" #include "../afterimage.h" #include
"common.h"
/* Usage: astext [-f font] [-s size] [-t text] [-S 3D_style]
[-c text_color] [-b background_color]
[-T foreground_texture] [-B background_image]
[-r foreground_resize_type] [-R
background_resize_type]
*/
#define BEVEL_HI_WIDTH 20 #define BEVEL_LO_WIDTH 30 #define
BEVEL_ADDON (BEVEL_HI_WIDTH+BEVEL_LO_WIDTH)
void usage() {
printf( " Usage: astext [-h] [-f font] [-s size] [-t text] "
"[-S 3D_style] \n");
printf( " [-c text_color] [-b
background_color]\n");
printf( " [-T foreground_texture] "
"[-B background_image]\n");
printf( " [-r foreground_resize_type] "
"[-R background_resize_type]\n");
printf( " [-m ]\n");
printf( " Where: font - TrueType font's filename or X font spec
or "
"alias;\n");
printf( " size - size in points for TrueType fonts;\n");
printf( " text - text to be drawn;\n");
printf( " 3D_style - 3D style of text. "
"One of the following:\n");
printf( " 0 - plain 2D tetx, 1 - embossed, 2 -
sunken, "
"3 - shade above,\n");
printf( " 4 - shade below, 5 - embossed thick, "
"6 - sunken thick.\n");
printf( " 7 - ouline above, 8 - ouline below, "
"9 - full ouline.\n");
printf( " resize_type - tells how texture/image should
be "
"transformed to fit\n");
printf( " the text size. Could be: scale or tile."
"Default is tile\n");
printf( " -m make font monospaced. \n");
}
int main(int argc, char* argv[]) {
Display *dpy = NULL;
ASVisual *asv = NULL ;
int screen = 0, depth = 0;
char *font_name = "test.ttf";
int size = 32 ; #if 0
char *text = "ããè¿~沑æ"; /* chinese */ #else
char *text = "Smart Brown Dog jumps\nOver The Lazy Fox,\n"
"and falls into the ditch."; #endif
ARGB32 text_color = ARGB32_White, back_color = ARGB32_Black;
char *text_color_name = "#FFFFFFFF", *back_color_name =
"#FF000000";
char *fore_image_file = "fore.xpm" ;
char *back_image_file = "back.xpm" ;
Bool scale_fore_image = False, scale_back_image = False ;
ASImage *fore_im = NULL, *back_im = NULL;
ASImage *text_im = NULL ;
struct ASFontManager *fontman = NULL;
struct ASFont *font = NULL;
unsigned int width, height ;
int i ;
int text_margin = size/2 ;
Bool monospaced = False ;
char * font_path = NULL;
ASTextAttributes attr = {ASTA_VERSION_1, ASTA_UseTabStops,
AST_ShadeBelow, ASCT_UTF8, 8, 0,
NULL, 0, ARGB32_White };
/* see ASView.1 : */
set_application_name( argv[0] ); #if (HAVE_AFTERBASE_FLAG==1)
set_output_threshold(OUTPUT_LEVEL_DEBUG); #endif
if( argc == 1 )
usage();
else for( i = 1 ; i < argc ; i++ )
{
if( strncmp( argv[i], "-h", 2 ) == 0 )
{
usage();
return 0;
}
if( strncmp( argv[i], "-m", 2 ) == 0 )
{
monospaced = True ;
}else if( i+1 < argc )
{
if( strncmp( argv[i], "-f", 2 ) == 0 )
font_name = argv[i+1] ;
else if( strncmp( argv[i], "-s", 2 ) == 0 )
{
size = atoi(argv[i+1]);
text_margin = size/2 ;
}else if( strncmp( argv[i], "-t", 2 ) == 0 )
text = argv[i+1] ;
else if( strncmp( argv[i], "-S", 2 ) == 0 )
{
attr.type = atoi(argv[i+1]);
if( attr.type >= AST_3DTypes )
{
show_error( "3D type is wrong. "
"Using 2D Plain instead.");
attr.type = AST_Plain ;
}
}else if( strncmp( argv[i], "-c", 2 ) == 0 )
text_color_name = argv[i+1] ;
else if( strncmp( argv[i], "-b", 2 ) == 0 )
back_color_name = argv[i+1] ;
else if( strncmp( argv[i], "-T", 2 ) == 0 )
fore_image_file = argv[i+1] ;
else if( strncmp( argv[i], "-B", 2 ) == 0 )
back_image_file = argv[i+1] ;
else if( strncmp( argv[i], "-r", 2 ) == 0 )
scale_fore_image = (strcmp( argv[i+1], "scale") ==
0);
else if( strncmp( argv[i], "-R", 2 ) == 0 )
scale_back_image = (strcmp( argv[i+1], "scale") ==
0);
}
}
#ifndef X_DISPLAY_MISSING
dpy = XOpenDisplay(NULL);
_XA_WM_DELETE_WINDOW = XInternAtom( dpy, "WM_DELETE_WINDOW",
False);
screen = DefaultScreen(dpy);
depth = DefaultDepth( dpy, screen ); #endif
/* see ASText.1 : */
if( getenv("FONT_PATH") != NULL )
{
font_path = safemalloc( strlen(getenv("FONT_PATH"))+1+2+1);
sprintf( font_path, "%s:./", getenv("FONT_PATH") );
}
if( (fontman = create_font_manager( dpy, font_path, NULL )) !=
NULL )
font = get_asfont( fontman, font_name, 0, size,
ASF_GuessWho|(monospaced?ASF_Monospaced:0)
);
if( font == NULL )
{
show_error( "unable to load requested font \"%s\". "
"Falling back to \"fixed\".", font_name );
font = get_asfont( fontman, "fixed", 0, size, ASF_GuessWho
);
if( font == NULL )
{
show_error("font \"fixed\" is not available either.
Aborting.");
return 1;
}
}
/*for( i = 0 ; i < 128 ; ++i )
print_asglyph( stderr, font, i); */
parse_argb_color( text_color_name, &text_color );
parse_argb_color( back_color_name, &back_color );
attr.fore_color = text_color ;
if( attr.type >= AST_OutlineAbove )
fore_image_file = NULL ;
/* see ASView.3 : */
asv = create_asvisual( dpy, screen, depth, NULL );
/* see ASText.2 : */
/*set_asfont_glyph_spacing( font, 10, 40 );*/
/*Simple way:get_text_size( text, font, attr.type, &width,
&height ); */
/*Fancy way : */
get_fancy_text_size( text, font, &attr, &width, &height, 0, NULL
); /* show_progress( "extimated text size = %dx%d", width, height );
*/
if( fore_image_file )
{
ASImage *tmp = file2ASImage( fore_image_file, 0xFFFFFFFF,
SCREEN_GAMMA, 0,
getenv("IMAGE_PATH"), NULL );
if( tmp )
{
if( tmp->width != width || tmp->height != height )
{ /* see ASScale.2 : */
if( scale_fore_image )
fore_im = scale_asimage( asv, tmp, width,
height,
ASA_ASImage, 0,
ASIMAGE_QUALITY_DEFAULT
);
else
fore_im = tile_asimage( asv, tmp, 0, 0,
width, height, 0,
ASA_ASImage, 0,
ASIMAGE_QUALITY_DEFAULT
);
destroy_asimage( &tmp );
}else
fore_im = tmp ;
}else
fore_im = NULL ;
}
width += text_margin*2 + BEVEL_ADDON;
height += text_margin*2 + BEVEL_ADDON;
if( back_image_file )
{ /* see ASView.2 : */
ASImage *tmp = file2ASImage( back_image_file, 0xFFFFFFFF,
SCREEN_GAMMA, 0,
getenv("IMAGE_PATH"), NULL );
if( tmp )
{
if( scale_back_image &&
(tmp->width != width || tmp->height != height) )
{ /* see ASScale.2 : */
back_im = scale_asimage( asv, tmp, width, height,
ASA_ASImage, 0,
ASIMAGE_QUALITY_DEFAULT );
destroy_asimage( &tmp );
}else
back_im = tmp ;
}else
back_im = NULL ;
}
(part 2 is on its way bestie, don't go anywhere π)
...and we're still SO inside that same example, bestie β no interruptions, just the rest of it, okay? here we go π
/* see ASText.3 : */
/* simple way : text_im = draw_text( text, font, attr.type, 0 );
*/
text_im = draw_fancy_text( text, font, &attr, 0, 0 );
if( fore_im )
{
move_asimage_channel( fore_im, IC_ALPHA, text_im, IC_ALPHA
);
destroy_asimage( &text_im );
}else
fore_im = text_im ;
/* see ASText.1 : */
release_font( font );
destroy_font_manager( fontman, False );
if( fore_im )
{
ASImage *rendered_im ;
ASImageLayer layers[2] ;
ASImageBevel bevel = {0/*BEVEL_SOLID_INLINE*/, 0xFFDDDDDD,
0xFF555555, 0xFFFFFFFF,
0xFF777777, 0xFF222222,
BEVEL_HI_WIDTH, BEVEL_HI_WIDTH,
BEVEL_LO_WIDTH, BEVEL_LO_WIDTH,
BEVEL_HI_WIDTH, BEVEL_HI_WIDTH,
BEVEL_LO_WIDTH, BEVEL_LO_WIDTH } ;
/* see ASText.4 : */
init_image_layers( &(layers[0]), 2 );
if( back_im )
back_im->back_color = back_color ;
if( fore_im )
fore_im->back_color = text_color ;
layers[0].im = back_im ;
layers[0].dst_x = 0 ;
layers[0].dst_y = 0 ;
layers[0].clip_width = width ;
layers[0].clip_height = height ;
layers[0].bevel = &bevel ;
layers[1].im = fore_im ;
layers[1].dst_x = text_margin+BEVEL_HI_WIDTH*2 ;
layers[1].dst_y = text_margin+
MIN( (int)text_margin,
((int)font->max_height-
(int)font->max_ascend))/2+
BEVEL_HI_WIDTH*2;
if( fore_im )
{
layers[1].clip_width = fore_im->width ;
layers[1].clip_height = fore_im->height ;
}
rendered_im = merge_layers( asv, &(layers[0]), 2,
width+BEVEL_ADDON,
height+BEVEL_ADDON, #ifndef X_DISPLAY_MISSING
ASA_XImage, #else
ASA_ASImage, #endif
0, ASIMAGE_QUALITY_DEFAULT);
destroy_asimage( &fore_im );
destroy_asimage( &back_im );
if( rendered_im )
{ #ifndef X_DISPLAY_MISSING
Window w;
/* see ASView.4 : */
w = create_top_level_window( asv,
DefaultRootWindow(dpy),
32, 32,
width+BEVEL_ADDON,
height+BEVEL_ADDON,
1, 0, NULL,
"ASText", text );
if( w != None )
{
Pixmap p ;
XMapRaised (dpy, w);
/* see ASView.5 : */
p = asimage2pixmap( asv, DefaultRootWindow(dpy),
rendered_im, NULL, True );
destroy_asimage( &rendered_im );
/* see common.c: set_window_background_and_free() :
*/
p = set_window_background_and_free( w, p );
/* see common.c: wait_closedown() : */
}
wait_closedown(w);
dpy = NULL; #else
/* writing result into the file */
ASImage2file( rendered_im, NULL, "astext.jpg",
ASIT_Jpeg,
NULL );
destroy_asimage( &rendered_im ); #endif
}
}
return 0 ; }
okay and that's the whole example, slayed β¨ now let's do the little mini-tutorials one by one π«Ά
π libAfterImage/tutorials/ASText.1 [7.1] π
β¨ SYNOPSIS β¨
Step 1. Opening and closing fonts.
πΈ DESCRIPTION πΈ
Before libAfterImage can render literally any text, the font you want has to be opened for use β that's just the rules, bestie. Opening a font is a two-step process, no skips:
First you create a font manager (ASFontManager). You do this once, and the same font manager can be used through your entire application. It's holding info about the external libraries in play, a hash of the opened fonts, and some other info too.
Once the ASFontManager exists, you use it to get the actual fonts. The get_asfont() call peeks at the ASFontManager's cache to check whether that font is already loaded β and if it isn't, it loads the font and gets it all prepped for drawing. iconic behavior, honestly π
π EXAMPLE π
if( (fontman = create_font_manager( dpy, NULL, NULL )) != NULL )
font = get_asfont( fontman, font_name, 0, size, ASF_GuessWho);
if( font == NULL )
{
font = get_asfont( fontman, "fixed", 0, size, ASF_GuessWho );
if( font == NULL )
{
show_error( "font \"fixed\" is not available either.
Aborting.");
return 1;
}
}
...
destroy_font( font );
destroy_font_manager( fontman, False );
π¦ SEE ALSO π¦
create_font_manager()get_asfont()destroy_font()destroy_font_manager()
π libAfterImage/tutorials/ASText.2 [7.2] π
β¨ SYNOPSIS β¨
Step 2. Approximating rendered text size.
πΈ DESCRIPTION πΈ
Before you actually commit to drawing the text, it's genuinely useful to estimate the size of the image that the rendered text will take up β because that way you can make your window the right size and lay the other interface elements out accordingly, and not have everything looking a mess π
get_text_size() is the one for this: it hands you the rendered text size without actually drawing anything. so convenient, fr fr.
π EXAMPLE π
get_text_size( text, font, type_3d, &width, &height );
if( fore_image_file )
{
ASImage *tmp = file2ASImage( fore_image_file, 0xFFFFFFFF,
SCREEN_GAMMA, 0, NULL );
if( tmp )
{
if( tmp->width != width || tmp->height != height )
{
if( scale_fore_image )
fore_im = scale_asimage( asv, tmp, width, height,
ASA_ASImage, 0,
ASIMAGE_QUALITY_DEFAULT );
else
fore_im = tile_asimage( asv, tmp, 0, 0,
width, height, 0,
ASA_ASImage, 0,
ASIMAGE_QUALITY_DEFAULT );
destroy_asimage( &tmp );
}else
fore_im = tmp ;
}
}
π NOTES π
In this specific example we either tile or scale the foreground texture so it fits the text. To texturize the text we need to use the rendered text as an alpha channel on the texture image β and that can only easily happen if both images are the same size. no cap.
π¦ SEE ALSO π¦
get_text_size()scale_asimage()tile_asimage()ASText.3
π libAfterImage/tutorials/ASText.3 [7.3] π
β¨ SYNOPSIS β¨
Step 3. Rendering texturized text.
πΈ DESCRIPTION πΈ
The most effective text texturization technique that libAfterImage provides is swapping the alpha channel of the texture out for the rendered text. That works because all the text gets rendered into the alpha channel only. The move_asimage_channel() call is what makes it happen.
Heads up though: this call actually removes the channel data from the original image and stores it in the destination image. And if the destination image's channel already had something in it? yeah, that's destroyed, π gone.
All kinds of nifty things can be done with this call, actually β like, for example, the rendered text can be moved into the green channel of the texture for a funky little effect. obsessed. π₯
π EXAMPLE π
text_im = draw_text( text, font, 0 );
if( fore_im )
{
move_asimage_channel( fore_im, IC_ALPHA, text_im, IC_ALPHA );
destroy_asimage( &text_im );
}else
fore_im = text_im ;
π NOTES π
move_asimage_channel() only works if both images are exactly the same size. If they're not, it does nothing. periodt.
π¦ SEE ALSO π¦
draw_text()move_asimage_channel()
π libAfterImage/tutorials/ASText.4 [7.4] π
β¨ SYNOPSIS β¨
Step 4. Merging foreground and background with bevel.
πΈ DESCRIPTION πΈ
At this step we've got two images ready to go β the background and the texturized text. Now we need to merge them together by alpha-blending the text over the background (remember, the text is the alpha channel of the foreground texture, bestie).
And while we're at it, we want to add a cute little bevel border around the whole entire image. So all we have to do is set up an ASImageLayer structure for both the background and the foreground, then apply merge_layers on them. Once that's done, the original images aren't needed anymore, so we destroy them to free up some memory. slay, bye π
π EXAMPLE π
ASImageLayer layers[2] ;
ASImageBevel bevel = {0, 0xFFDDDDDD, 0xFF555555,
0xFFFFFFFF, 0xFF777777, 0xFF444444,
BEVEL_HI_WIDTH, BEVEL_HI_WIDTH,
BEVEL_LO_WIDTH, BEVEL_LO_WIDTH,
BEVEL_HI_WIDTH, BEVEL_HI_WIDTH,
BEVEL_LO_WIDTH, BEVEL_LO_WIDTH } ;
memset( &(layers[0]), 0x00, sizeof(layers) );
layers[0].im = back_im ;
layers[0].clip_width = width ;
layers[0].clip_height = height ;
layers[0].merge_scanlines = alphablend_scanlines ;
layers[0].bevel = &bevel ;
layers[1].im = fore_im ;
layers[1].dst_x = TEXT_MARGIN ;
layers[1].dst_y = TEXT_MARGIN ;
layers[1].clip_width = fore_im->width ;
layers[1].clip_height = fore_im->height ;
layers[1].back_color = text_color ;
layers[1].merge_scanlines = alphablend_scanlines ;
rendered_im = merge_layers( asv, &(layers[0]), 2,
width+BEVEL_ADDON, height+BEVEL_ADDON,
ASA_XImage, 0, ASIMAGE_QUALITY_DEFAULT);
destroy_asimage( &fore_im );
destroy_asimage( &back_im );
π NOTES π
We have to remember that the outside bevel border adds up to the image size β which is why the desired image size has to be width+BEVEL_ADDON, height+BEVEL_ADDON. don't skip this part, I'm begging π
π¦ SEE ALSO π¦
ASImageLayerASImageBevelmerge_layers()
and that's a wrap, bestie π«Ά that little footer is serving 3rd Berkeley Distribution β AfterStep v.2.2.12 β astext(3x), and it's giving very much "go forth and render pretty text" energy. now go texturize something iconic, you've got this πβ¨π
π¦ where this page comes from
- ubuntu resolute: libafterimage-dev 2.2.12-19 Β· GPL-2.0-or-later
- ubuntu stonking: libafterimage-dev 2.2.12-19 Β· GPL-2.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms