asmerge(3x)
π asmerge(3x) β AfterStep X11 window manager π
okay so bestie, welcome to asmerge, straight from the AfterStep X11 window manager manual, section 3x. π it's a tutorial page from libAfterImage/tutorials/ASMerge and it's all about blending a whole stack of images together with different algorithms. it's giving β¨image-composition main character energyβ¨ and honestly? obsessed.
this page is long so it comes in parts, and this is part 1 β buckle in, we are not skimming. π
π NAME π
asmerge β demonstrates blending of multiple image using different algorithms libAfterImage/tutorials/ASMerge
π¦ NAMEASMerge π¦
Scaling and blending of arbitrary number of images using libAfterImage.
πΈ SYNOPSIS πΈ
Scaling and blending of arbitrary number of images using libAfterImage.
β¨ DESCRIPTION β¨
So here's the vibe: we're gonna take the command line arguments and read them as a sequence of image filenames, geometries and blending types. π then we load all those images up, scale the very first one to the size we asked for, and blend all the rest of them onto specific locations of that first image. then we pop the whole result into a simple little window, and we justβ¦ wait. patiently. until the user closes our window. π truly the group chat sitting around waiting for the one friend who's still typing.
The new steps this tutorial walks through are:
- Layers.
- Merging methods.
- Layer parameters.
- Actual blending of the set of images.
no cap, that's the whole plot. π«Ά
π SEE ALSO π
- Tutorial 1:
ASViewβ explanation of basic steps needed to uselibAfterImageand some other simple things. - Tutorial 2:
ASScaleβ image scaling basics. - Tutorial 3:
ASTileβ image tiling and tinting
π₯ SOURCE π₯
#include "../afterbase.h" #include "../afterimage.h" #include
"common.h"
char *burning_rose[] = {
"asmerge",
"rose512.jpg",
"add",
"back.xpm:512x386",
"hue",
"fore.xpm:512x386" };
void usage() {
printf( "Usage: asmerge [-h]|[image op1 image1 [op2 image2
[...]]]\n");
printf( "Where: image - is background image filename\n");
printf( " image1 - is first overlay's filename\n");
printf( " op1,op2,... - overlay operation."
" Supported operations are :\n");
list_scanline_merging( stdout,
" %-15.15s- %s\n"); }
int main(int argc, char* argv[]) {
Display *dpy = NULL;
ASVisual *asv ;
int screen = 0, depth = 0;
int to_width = 1, to_height = 1;
ASImageLayer *layers ;
int layers_num = 0, i;
ASImage *merged_im ;
/* see ASView.1 : */
set_application_name( argv[0] ); #if (HAVE_AFTERBASE_FLAG==1)
set_output_threshold(OUTPUT_LEVEL_DEBUG); #endif
if( argc == 2 && strncmp(argv[1],"-h", 2) == 0 )
{
usage();
return 0;
}
if( argc <= 3 )
{
show_error( "not enough arguments, please see usage:%s", "
");
usage() ;
printf( "Using the default, \"The Burning Rose\",
composition :\n");
printf( "\n\trose512.jpg add back.xpm:512x386 hue "
"fore.xpm:512x386\n");
argv = &(burning_rose[0]) ;
argc = 6;
}
#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 ASView.3 : */
asv = create_asvisual( dpy, screen, depth, NULL );
/* see ASMerge.1 : */
layers = safecalloc( argc/2, sizeof(ASImageLayer) );
for( i = 1 ; i < argc ; i++ )
{
int x = 0, y = 0;
unsigned int width, height ;
int geom_flags = 0 ;
char *separator;
char *filename ;
/* see ASMerge.2 */
if( i > 1 )
{
/* see blend_scanlines_name2func() : */
if((layers[layers_num].merge_scanlines =
blend_scanlines_name2func( argv[i] )) == NULL )
continue ;
if( ++i >= argc )
break;
}
if( (separator = strchr( argv[i], ':' )) != NULL )
{ /* see ASTile.1 : */
geom_flags = XParseGeometry( separator+1,
&x, &y, &width, &height);
filename = mystrndup( argv[i], separator-argv[i] );
}else
filename = argv[i] ;
layers[layers_num].im = file2ASImage( filename, 0xFFFFFFFF,
SCREEN_GAMMA, 100,
getenv("IMAGE_PATH"), NULL );
if( filename != argv[i] )
free( filename );
if( layers[layers_num].im != NULL )
{
if( !get_flags(geom_flags, WidthValue) )
width = layers[layers_num].im->width ;
if( !get_flags(geom_flags, HeightValue) )
height = layers[layers_num].im->height ;
/* see ASMerge.3 : */
if( layers[layers_num].merge_scanlines == NULL )
layers[layers_num].merge_scanlines =
alphablend_scanlines ;
layers[layers_num].clip_width = width ;
layers[layers_num].clip_height = height ;
if( layers_num > 0 )
{
layers[layers_num].dst_x = x ;
layers[layers_num].dst_y = y ;
}else
{
to_width = width ;
to_height = height ;
if( width != layers[layers_num].im->width ||
height != layers[layers_num].im->height )
{
ASImage *scaled_bottom ;
/* see ASScale.2 : */
scaled_bottom = scale_asimage( asv,
layers[layers_num].im,
width, height,
False, 100,
ASIMAGE_QUALITY_DEFAULT
);
destroy_asimage( &(layers[layers_num].im) );
layers[layers_num].im = scaled_bottom ;
}
}
++layers_num ;
}
}
if( layers_num <= 0 )
{
show_error( "there is no images to merge. Aborting");
return 2;
}
/* see ASMerge.4 */
merged_im = merge_layers( asv, layers, layers_num,
to_width, to_height, #ifndef
X_DISPLAY_MISSING
ASA_XImage, #else
ASA_ASImage, #endif
0, ASIMAGE_QUALITY_DEFAULT );
while( --layers_num >= 0 )
destroy_asimage( &(layers[layers_num].im) );
free( layers );
if( merged_im )
{ #ifndef X_DISPLAY_MISSING
/* see ASView.4 : */
Window w = create_top_level_window( asv,
DefaultRootWindow(dpy),
32, 32,
to_width, to_height,
1, 0, NULL,
"ASMerge", NULL );
if( w != None )
{
Pixmap p ;
XMapRaised (dpy, w);
/* see ASView.5 : */
p = asimage2pixmap( asv, DefaultRootWindow(dpy),
merged_im,
NULL, True );
destroy_asimage( &merged_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( merged_im, NULL, "asmerge.jpg", ASIT_Jpeg,
NULL );
destroy_asimage( &merged_im ); #endif
} #ifdef DEBUG_ALLOCS
build_xpm_colormap(NULL);
print_unfreed_mem(); #endif
return 0 ; }
π libAfterImage/tutorials/ASMerge.1 [4.1] π
β SYNOPSIS β
Step 1. Layers.
π« DESCRIPTION π«
So libAfterImage does its blending/merging of different images using arrays of ASImageLayer structures, and the first element in that array is the bottommost image. π everyone else stacks on top.
Each one of those structures tells you:
- the exact position on the resulting image where that overlay should be blended to,
- the tint of the overlay,
- the size, to which the overlay should be tiled,
- the overlay's origin,
- and the overlay's background color.
and the slay part? an arbitrary number of layers can be merged within a single run. π₯ no cap.
So accordingly, all you need to merge a bunch of images is to create an array of ASImageLayer structures and fill it up appropriately. that's it, that's the step. π
π¦ EXAMPLE π¦
layers = safecalloc( argc/2, sizeof(ASImageLayer) );
πΈ libAfterImage/tutorials/ASMerge.2 [4.2] πΈ
β SYNOPSIS β
Step 2. Merging methods.
π« DESCRIPTION π«
Every single layer can be merged in using its own method β there are about 15 different methods implemented in libAfterImage, and your app can implement other methods of its own too. π so custom, so iconic.
to specify a method, all you gotta do is set the merge_scanlines member of ASImageLayer to a pointer to the function that implements that specific method.
and libAfterImage provides a facility to parse method name strings into actual function pointers β which you can use to simplify scripting, etc. π«Ά convenient much?
π¦ EXAMPLE π¦
if((layers[layers_num].merge_scanlines =
blend_scanlines_name2func( argv[i] )) == NULL )
continue ;
NOTE β all layers MUST have a validmerge_scanlinespointer, even the bottommost layer, even though it lowkey has nothing to be merged with. π« ifmerge_scanlinesis set toNULL, that layer will be ignored. which, honestly, means you can use it to turn particular layers on and off. π
π libAfterImage/tutorials/ASMerge.3 [4.3] π
β SYNOPSIS β
Step 3. Layer parameters.
π« DESCRIPTION π«
Several ASImageLayer members are mandatory and cannot be set to 0, bestie. those are:
imβ the image to be merged;clip_width,clip_heightβ these will be used to tile the image;merge_scanlinesβ must be set to a pointer to the function implementing the merging method.
if any of these is set to 0, then the layer will be ignored. π«
The rest of the parameters are optional. but note that the tint parameter will tint the overlay's RGB components and the alpha component β and as a result, it could be used to make opaque images semitransparent. πβ¨
β¨ EXAMPLE β¨
okay so real talk, here's the actual prepping-a-layer moment in code, no edits, exactly as it lives:
if( layers[layers_num].merge_scanlines == NULL )
layers[layers_num].merge_scanlines =
alphablend_scanlines ;
layers[layers_num].clip_width = width ;
layers[layers_num].clip_height = height ;
if( layers_num > 0 )
{
layers[layers_num].dst_x = x ;
layers[layers_num].dst_y = y ;
}
bestie the way this little chunk justβ¦ sets merge_scanlines to alphablend_scanlines if there isn't one already? iconic. it stamps the clip_width and clip_height, and if you're not on layer zero it also drops in dst_x and dst_y. main character behaviour fr fr π
and where does this gorgeous specimen live? libAfterImage/tutorials/ASMerge.4 [4.4] π
π SYNOPSIS π
Step 4. the actual blending. the moment we've all been waiting for. the layers get fused together, for real this time β¨
π DESCRIPTION π
so once your whole lil set of layers is prepped and ready and lookin cute β you hand it off to the merge_layers() function. she takes that stack and creates a brand-new ASImage of the size you specified, and then blends every single layer together to fill that image up. that's the whole vibe. that's the point. periodt π
π¦ EXAMPLE π¦
here's the glow-up in motion:
merged_im = merge_layers( asv, layers, layers_num,
to_width, to_height,
ASA_ASImage, 0,
ASIMAGE_QUALITY_DEFAULT );
while( --layers_num >= 0 )
destroy_asimage( &(layers[layers_num].im) );
free( layers );
look at her go π blending everything, then walking backwards through the layer count to destroy_asimage( &(layers[layers_num].im) ) on each one, then free( layers ) at the end. clean. tidy. no leaks. we love to see it π«Ά
πΈ NOTES πΈ
so once the layers are blended β plot twist β we do not need that ASImageLayer array anymore. so obviously we clean it up. the order matters though, bestie: you destroy the overlay AsImages first, and then you free the array itself. do it in that order. don't be messy about it π
β SEE ALSO β
merge_asimage().
and that's a wrap on this whole saga, bestie π this page came to you from the 3rd Berkeley Distribution, shipping with AfterStep v.2.2.12, dated back in the day as asmerge(3x). go blend something gorgeous, you absolute legend πβ¨π
π¦ 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