Fladimir's alpha blending routines
Paul whoknows

Please excuse my lack of experience but I just don't know how to use Fladimir's alpha blender, here is an example using allegro's alpha blender functions, but I can't figure out how to replace them with Fladimir's alpha blender routines.

Example

1#include <allegro.h>
2#define VIDEO_BPP 32
3#define VIDEO_W 640
4#define VIDEO_H 480
5
6int main(void)
7{
8 BITMAP *buffer;
9 BITMAP *bg;
10 BITMAP *sprite;
11 BITMAP *alpha;
12 
13 allegro_init();
14 install_keyboard();
15 install_mouse();
16
17 set_color_depth(VIDEO_BPP);
18 //set_gfx_mode(GFX_AUTODETECT, VIDEO_W, VIDEO_H, 0, 0);
19 set_gfx_mode(GFX_AUTODETECT_WINDOWED, VIDEO_W, VIDEO_H, 0, 0);
20
21 //load files
22 bg = load_bitmap("pucca.pcx", NULL);
23 sprite = load_bitmap("item.pcx", NULL);
24 alpha = load_bitmap("item_alpha.pcx", NULL);
25
26 //Write alpha blender
27 set_write_alpha_blender();
28 draw_trans_sprite(sprite, alpha, 0, 0);
29 destroy_bitmap(alpha);
30
31 //create memory buffer
32 buffer = create_bitmap(SCREEN_W, SCREEN_H);
33
34 //loop
35 while (!key[KEY_ESC])
36 {
37 blit(bg, buffer, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
38 set_alpha_blender();
39 draw_trans_sprite(buffer, sprite, mouse_x - sprite->w / 2, mouse_y - sprite->h / 2);
40 blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
41 }
42
43 show_mouse(NULL);
44 destroy_bitmap(buffer);
45 destroy_bitmap(sprite);
46 return EXIT_SUCCESS;
47}
48END_OF_MAIN()

Fladimir's routines

1inline unsigned long BlendColorsNoEmms( unsigned int dst, unsigned int src, unsigned int factor ) {
2 static unsigned short INVERT_MASK[4] = { 0x00FF, 0x00FF, 0x00FF, 0x00FF };
3 unsigned long returnParam;
4 factor = 255 - factor;
5 asm(
6 "movd %1, %%mm0\n"
7 "movd %2, %%mm1\n"
8 "pxor %%mm2, %%mm2\n"
9 "punpcklbw %%mm2, %%mm0\n"
10 "punpcklbw %%mm2, %%mm1\n"
11
12 // Get the alpha value //
13 "movd %4, %%mm3\n"
14 "punpcklwd %%mm3, %%mm3\n"
15 "punpcklwd %%mm3, %%mm3\n"
16
17 // (alpha * (source + 255 - dest))/255 + dest - alpha //
18 "paddw (%3), %%mm0\n"
19 "psubw %%mm1, %%mm0\n"
20 "psrlw $1, %%mm0\n"
21 "pmullw %%mm3, %%mm0\n"
22 "psrlw $7, %%mm0\n"
23 "paddw %%mm1, %%mm0\n"
24 "psubw %%mm3, %%mm0\n"
25
26 "packuswb %%mm0, %%mm0\n"
27 "movd %%mm0, %0\n"
28 : "=&a" (returnParam)
29 : "rm" (dst), "rm" (src), "rm" (INVERT_MASK), "rm" (factor)
30 : "memory"
31 );
32 return returnParam;
33}
34 
35 
36// ... but remember to call this one before you use any floats or doubles //
37inline void CallEmms() {
38 asm(
39 "emms\n"
40 );
41}
42 
43 
44// This is a shorthand to blend single pixels at

Milan Mimica

BlendColorsNoEmms() is used to blend only two colors: dst and src, with factor factor. Format of the colors is RGB32, factor is [0, 255].

If you want to blend a whole bitmap you have to do it pixel by pixel.

Paul whoknows

Does that mean that I have to write my own draw_trans_sprite and set_write_alpha_blender?:'(
BTW does someone knows if Fladimir´s functions are really so fast?
How much faster are they?

Quote:

If you want to blend a whole bitmap you have to do it pixel by pixel.

Would that be faster than allegro´s blender?

Neil Walker

I'm probably completely wrong here, not really using ol that much, but what you need to do is not use magic pink bitmaps, instead use graphic files with an alpha channel then call Blenders::Set( ALPHA_BLENDER );

Fladimir da Gorf

Don't worry, I've also written the function for you. Here it is: (slightly edited, I hope it still works ;))

1typedef unsigned int PixelType;
2 
3void BasicAlphaBlend( BITMAP *src, BITMAP *dst, int dst_x, int dst_y ) {
4 ASSERT( src != 0 );
5 ASSERT( dst != 0 );
6
7 int src_x = 0;
8 int src_y = 0;
9 int w = src->w;
10 int h = src->h;
11 if( dst_x < 0 ) {
12 w += dst_x;
13 src_x -= dst_x;
14 dst_x = 0;
15 }
16 if( dst_y < 0 ) {
17 h += dst_y;
18 src_y -= dst_y;
19 dst_y = 0;
20 }
21 if( dst_x + w > dst->w ) {
22 w -= dst_x + w - dst->w;
23 }
24 if( w <= 0 ) return;
25 if( dst_y + h > dst->h ) {
26 h -= dst_y + h - dst->h;
27 }
28 if( h <= 0 ) return;
29
30 for( int j = 0; j < h; j++ ) {
31 PixelType *src_ptr = ((PixelType *)src->line[src_y+j]) + src_x;
32 PixelType *dst_ptr = ((PixelType *)dst->line[dst_y+j]) + dst_x;
33 for( int i = 0; i < w; i++ ) {
34 unsigned char fact = geta32( *src_ptr );
35
36 if( fact != 0 ) {
37 if( fact == 255 ) *dst_ptr = *src_ptr;
38 else *dst_ptr = BlendColorsNoEmms( *dst_ptr, *src_ptr, fact );
39 }
40 src_ptr++;
41 dst_ptr++;
42 }
43 }
44
45 CallEmms();
46}

Quote:

How much faster are they?

IIRC it's 300-570% faster than Allegro's alpha blender, when compiled with -O3.

Paul whoknows

Wow! thank you very very much!:D
What compiler are you using?
I am using MSVC6 and I have some problems with the assembler lines inside your functions.
MSVC6 uses __asm {}, instead of asm(), I replaced that and other microsoft specific stuffs but still it doesn't compile, I don't know what is wrong here.
I get multiple errors like this
C2400: in-line assembler syntax error in 'opcode'; found 'bad token'

HoHo

He uses gcc style for ASM (I can't remember the correct name). To make it work with MSVC you would have to rewrite the whole ASM stuff with Intel syntax. In other words, if you don't know ASM then use GCC :)

Fladimir da Gorf

Maybe switching the opcode parameters would be sufficient?

For example, "movd %2, %%mm1\n" becomes "movd %%mm1, %2\n"

Though I also think that you'll also need to remove one % from them.

Is it possible to compile the source with GCC and then import the object file in a MSVC project?

Milan Mimica
Quote:

Is it possible to compile the source with GCC and then import the object file in a MSVC project?

Allegro does something like that in the build process.

Neil Walker

ah, I've just realised what you wanted :)

There's also FBlend.

Edward Sheets

Any chance Flad's blender could be incorporated into Allegro? Seems like a speed increase of that magnitude would be well worth it.

Fblend is sweet but I don't think it's been worked on in a while and it has some bugs. I seem to remember there being a problem with running it on 64-bit processors...?

Paul whoknows
Quote:

Maybe switching the opcode parameters would be sufficient?

I tried that, but it didn't work, I'll try to download GCC later and compile your routines to obtain the .obj as you said, I really want to include your routines in my game, but right now I am really busy trying to solve a lot of problems with the game programming itself.
I didn't know that the AT&T and Intel syntaxes were so difficult to translate between each other.

Quote:

There's also FBlend.

Fblend doesn't support alpha blending.

Neil Walker

I know it doesn't support alpha blending, but I said it as just another library to help with blending in general.

Paul whoknows
Quote:

Is it possible to compile the source with GCC and then import the object file in a MSVC project?

Can someone compile Fladimir's alpha blend routines and give me the .obj so I can use them in MSVC6?
My dial-up connection dosen't allow me to download the GCC compiler right now:-/
Thanks in advance.

HoHo

http://www.bloodshed.net/dev/devcpp.html

Dev-cpp with GCC 3.4.2 is around 9.2MiB or half an hour with good dial-up connection. If noone else has time or willing to compile it for you then that might help you. I might help you myself but unfortunately I don't have Windows.

Neil Walker

The best minds in the world have tried to compile OL with MSVC but it fails due to MSVC's support of the STL and other related issues.

As hoho, get devcpp, or the free msvc8 (negating the need to have msvc6), mingw, code:blocks, etc.

tobing
Quote:

The best minds in the world have tried to compile OL with MSVC but it fails due to MSVC's support of the STL and other related issues.

Not true. At least since MSVC 7.1 support of STL 'and other related issues' are good enough to compile OpenLayer. Version 6.0 of MSVC does not work well though.

HoHo

MSVC is irrelevant since he is trying to compile AT&T ASM routines, not OL itself.

Neil Walker
Quote:

Not true. At least since MSVC 7.1 support of ST

Yes True ;) he was asking about MSVC6 and I was replying about MSVC6 :)

tobing

Ah. I thought from your post that you were talking about MSVC in general...

Fladimir da Gorf

I actually found my old sources from my hard drive, here's the complete source:

1#include <allegro.h>
2#include <loadpng.h>
3 
4 
5/*
6 * BlendColors32 by Esa Tanskanen
7 * Fast 32-bit color mixing routines
8 * Use the BlendColors32 function if unsure
9 */
10
11
12// If you wish to process multiple pixels at once, use this function... //
13inline unsigned long BlendColors32NoEmms( unsigned long dst, unsigned long src, unsigned long factor ) {
14 static unsigned short INVERT_MASK[4] = { 0x00FF, 0x00FF, 0x00FF, 0x00FF };
15 unsigned long returnParam;
16 factor = 255 - factor;
17 asm(
18 "movd %1, %%mm0\n"
19 "movd %2, %%mm1\n"
20 "pxor %%mm2, %%mm2\n"
21 "punpcklbw %%mm2, %%mm0\n"
22 "punpcklbw %%mm2, %%mm1\n"
23
24 // Get the alpha value //
25 "movd %4, %%mm3\n"
26 "punpcklwd %%mm3, %%mm3\n"
27 "punpcklwd %%mm3, %%mm3\n"
28
29 // (alpha * (source + 255 - dest))/255 + dest - alpha //
30 "paddw (%3), %%mm0\n"
31 "psubw %%mm1, %%mm0\n"
32 "psrlw $1, %%mm0\n"
33 "pmullw %%mm3, %%mm0\n"
34 "psrlw $7, %%mm0\n"
35 "paddw %%mm1, %%mm0\n"
36 "psubw %%mm3, %%mm0\n"
37
38 "packuswb %%mm0, %%mm0\n"
39 "movd %%mm0, %0\n"
40 : "=&a" (returnParam)
41 : "rm" (dst), "rm" (src), "rm" (INVERT_MASK), "rm" (factor)
42 : "memory"
43 );
44 return returnParam;
45}
46 
47 
48// ... but remember to call this one before you use any floats or doubles //
49inline void CallEmms() {
50 asm(
51 "emms\n"
52 );
53}
54 
55 
56// This is a shorthand to blend single pixels at a time //
57inline unsigned long BlendColors32( unsigned long dst, unsigned long src, unsigned long factor ) {
58 unsigned long returnParam = BlendColors32NoEmms( dst, src, factor );
59 CallEmms();
60 return returnParam;
61}
62 
63 
64 
65/*
66 * AlphaBlend32 by Esa Tanskanen
67 * 32-bit alpha blending routines with a global alpha value
68 * Notice that the source bitmap comes before the destination bitmap!
69 */
70 
71 
72void AlphaBlend32( BITMAP *src, BITMAP *dst, int dst_x, int dst_y, int globalAlpha ) {
73 /* The following conditions must be true */
74 ASSERT( src );
75 ASSERT( dst );
76 ASSERT( bitmap_color_depth( src ) == 32 );
77 ASSERT( bitmap_color_depth( dst ) == 32 );
78
79 /* Apply clipping */
80 int src_x = 0;
81 int src_y = 0;
82 int w = src->w;
83 int h = src->h;
84
85 if( dst_x < 0 ) {
86 w += dst_x;
87 src_x -= dst_x;
88 dst_x = 0;
89 }
90
91 if( dst_y < 0 ) {
92 h += dst_y;
93 src_y -= dst_y;
94 dst_y = 0;
95 }
96
97 if( dst_x + w > dst->w ) {
98 w -= dst_x + w - dst->w;
99 }
100
101 if( w <= 0 ) return;
102
103 if( dst_y + h > dst->h ) {
104 h -= dst_y + h - dst->h;
105 }
106
107 if( h <= 0 ) return;
108
109 /* Select destination surface for reading and writing */
110 acquire_bitmap( dst );
111 bmp_select( dst );
112
113 /* Loop through all pixels */
114 for( int j = 0; j < h; j++ ) {
115 unsigned int *src_ptr = ((unsigned int *)src->line[src_y+j]) + src_x;
116 unsigned int dst_address = bmp_write_line( dst, dst_y+j ) + 4*dst_x;
117
118 for( int i = 0; i < w; i++ ) {
119 unsigned int srcColor = *src_ptr;
120 /* Extract the alpha value */
121 unsigned int fact = ( geta32( srcColor ) * globalAlpha ) >> 8;
122
123 /* Blend the colors if required */
124 if( fact != 0 ) {
125 if( fact >= 255 ) bmp_write32( dst_address, srcColor );
126 else bmp_write32( dst_address, BlendColors32NoEmms( *((unsigned int *) dst_address), *src_ptr, fact ));
127 }
128
129 /* Advance to the next pixel */
130 dst_address += 4;
131 src_ptr++;
132 }
133 }
134
135 /* Finish blending */
136 CallEmms();
137
138 /* Finish messing with the destination bitmap */
139 bmp_unwrite_line( dst );
140 release_bitmap( dst );
141}
142 
143 
144 
145/*
146 * AlphaBlend32 by Esa Tanskanen
147 * 32-bit alpha blending routines
148 * Notice that the source bitmap comes before the destination bitmap!
149 */
150 
151 
152void AlphaBlend32( BITMAP *src, BITMAP *dst, int dst_x, int dst_y ) {
153 /* The following conditions must be true */
154 ASSERT( src );
155 ASSERT( dst );
156 ASSERT( bitmap_color_depth( src ) == 32 );
157 ASSERT( bitmap_color_depth( dst ) == 32 );
158
159 /* Apply clipping */
160 int src_x = 0;
161 int src_y = 0;
162 int w = src->w;
163 int h = src->h;
164
165 if( dst_x < 0 ) {
166 w += dst_x;
167 src_x -= dst_x;
168 dst_x = 0;
169 }
170
171 if( dst_y < 0 ) {
172 h += dst_y;
173 src_y -= dst_y;
174 dst_y = 0;
175 }
176
177 if( dst_x + w > dst->w ) {
178 w -= dst_x + w - dst->w;
179 }
180
181 if( w <= 0 ) return;
182
183 if( dst_y + h > dst->h ) {
184 h -= dst_y + h - dst->h;
185 }
186
187 if( h <= 0 ) return;
188
189 /* Select destination surface for reading and writing */
190 acquire_bitmap( dst );
191 bmp_select( dst );
192
193 /* Loop through all pixels */
194 for( int j = 0; j < h; j++ ) {
195 unsigned int *src_ptr = ((unsigned int *)src->line[src_y+j]) + src_x;
196 unsigned int dst_address = bmp_write_line( dst, dst_y+j ) + 4*dst_x;
197
198 for( int i = 0; i < w; i++ ) {
199 unsigned int srcColor = *src_ptr;
200 /* Extract the alpha value */
201 unsigned int fact = geta32( srcColor );
202
203 /* Blend the colors if required */
204 if( fact != 0 ) {
205 if( fact >= 255 ) bmp_write32( dst_address, srcColor );
206 else bmp_write32( dst_address, BlendColors32NoEmms( *((unsigned int *) dst_address), *src_ptr, fact ));
207 }
208
209 /* Advance to the next pixel */
210 dst_address += 4;
211 src_ptr++;
212 }
213 }
214
215 /* Finish blending */
216 CallEmms();
217
218 /* Finish messing with the destination bitmap */
219 bmp_unwrite_line( dst );
220 release_bitmap( dst );
221}
222 
223 
224 
225int main() {
226 /* Setup Allegro */
227 allegro_init();
228 install_keyboard();
229
230 set_color_depth( 32 );
231 set_gfx_mode( GFX_AUTODETECT_WINDOWED, 800, 600, 0, 0 );
232
233 BITMAP *buffer = create_bitmap( SCREEN_W, SCREEN_H );
234
235 /* mybmp.png must be a 32-bit bitmap with an alpha channel! */
236 BITMAP *sprite = load_png( "mybitmap.png", 0 );
237
238 /* Draw it to the buffer */
239 AlphaBlend32( sprite, buffer, 0, 100 );
240
241 /* Draw it 3 times to the buffer with different apha values */
242 AlphaBlend32( sprite, buffer, 100 , 100, 255 );
243 AlphaBlend32( sprite, buffer, 100 + sprite->w, 100, 150 );
244 AlphaBlend32( sprite, buffer, 100 + 2*sprite->w, 100, 50 );
245
246 blit( buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H );
247
248 readkey();
249
250 /* You can also draw directly to the screen, but that's not recommended
251 as it's much slower */
252
253 AlphaBlend32( sprite, screen, 0, 100 + sprite->h );
254
255 AlphaBlend32( sprite, screen, 100 , 100 + sprite->h, 255 );
256 AlphaBlend32( sprite, screen, 100 + sprite->w, 100 + sprite->h, 150 );
257 AlphaBlend32( sprite, screen, 100 + 2*sprite->w, 100 + sprite->h, 50 );
258
259 readkey();
260
261 return 0;
262}
263END_OF_MAIN()

I've attached the .o file, the source file and the bitmap which the test program uses.

Paul whoknows
Quote:

I've attached the .o file, the source file and the bitmap which the test program uses.

I can't download your attachment, I can't see it, can you check your attachment please?, and thanks again for all your help I really appreciate it!

Quote:

Dev-cpp with GCC 3.4.2 is around 9.2MiB or half an hour with good dial-up connection

What! only 9MB?, I only knew the official GCC site and it was very intimidating, I did not know about the link you posted, I'll download it right now!

[EDIT]

I followed Hoho's advice, Devcpp makes everything so nice and easy, specially after a long time using MSVC6!
Fladimir eveything works perfectly now! thank you very much, you gave me a lot of help and I'll add your name in the credits!

Fladimir da Gorf

OK, I had forgot to press the "Upload" -button... But great that it works!

Thread #589627. Printed from Allegro.cc