1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or(at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "ags/plugins/ags_blend/ags_blend.h"
24 #include "ags/shared/core/platform.h"
25 #include "common/algorithm.h"
26 
27 namespace AGS3 {
28 namespace Plugins {
29 namespace AGSBlend {
30 
31 #pragma region Defines_and_Includes
32 
33 #define MIN_EDITOR_VERSION 1
34 #define MIN_ENGINE_VERSION 3
35 
36 #define DEFAULT_RGB_R_SHIFT_32  16
37 #define DEFAULT_RGB_G_SHIFT_32  8
38 #define DEFAULT_RGB_B_SHIFT_32  0
39 #define DEFAULT_RGB_A_SHIFT_32  24
40 
41 #if !AGS_PLATFORM_OS_WINDOWS
42 #define min(x,y) (((x) < (y)) ? (x) : (y))
43 #define max(x,y) (((x) > (y)) ? (x) : (y))
44 #endif
45 
46 #define abs(a)                       ((a)<0 ? -(a) : (a))
47 #define ChannelBlend_Normal(B,L)     ((uint8)(B))
48 #define ChannelBlend_Lighten(B,L)    ((uint8)((L > B) ? L:B))
49 #define ChannelBlend_Darken(B,L)     ((uint8)((L > B) ? B:L))
50 #define ChannelBlend_Multiply(B,L)   ((uint8)((B * L) / 255))
51 #define ChannelBlend_Average(B,L)    ((uint8)((B + L) / 2))
52 #define ChannelBlend_Add(B,L)        ((uint8)(min(255, (B + L))))
53 #define ChannelBlend_Subtract(B,L)   ((uint8)((B + L < 255) ? 0:(B + L - 255)))
54 #define ChannelBlend_Difference(B,L) ((uint8)(abs(B - L)))
55 #define ChannelBlend_Negation(B,L)   ((uint8)(255 - abs(255 - B - L)))
56 #define ChannelBlend_Screen(B,L)     ((uint8)(255 - (((255 - B) * (255 - L)) >> 8)))
57 #define ChannelBlend_Exclusion(B,L)  ((uint8)(B + L - 2 * B * L / 255))
58 #define ChannelBlend_Overlay(B,L)    ((uint8)((L < 128) ? (2 * B * L / 255):(255 - 2 * (255 - B) * (255 - L) / 255)))
59 #define ChannelBlend_SoftLight(B,L)  ((uint8)((L < 128)?(2*((B>>1)+64))*((float)L/255):(255-(2*(255-((B>>1)+64))*(float)(255-L)/255))))
60 #define ChannelBlend_HardLight(B,L)  (ChannelBlend_Overlay(L,B))
61 #define ChannelBlend_ColorDodge(B,L) ((uint8)((L == 255) ? L:min(255, ((B << 8 ) / (255 - L)))))
62 #define ChannelBlend_ColorBurn(B,L)  ((uint8)((L == 0) ? L:max(0, (255 - ((255 - B) << 8 ) / L))))
63 #define ChannelBlend_LinearDodge(B,L)(ChannelBlend_Add(B,L))
64 #define ChannelBlend_LinearBurn(B,L) (ChannelBlend_Subtract(B,L))
65 #define ChannelBlend_LinearLight(B,L)((uint8)(L < 128)?ChannelBlend_LinearBurn(B,(2 * L)):ChannelBlend_LinearDodge(B,(2 * (L - 128))))
66 #define ChannelBlend_VividLight(B,L) ((uint8)(L < 128)?ChannelBlend_ColorBurn(B,(2 * L)):ChannelBlend_ColorDodge(B,(2 * (L - 128))))
67 #define ChannelBlend_PinLight(B,L)   ((uint8)(L < 128)?ChannelBlend_Darken(B,(2 * L)):ChannelBlend_Lighten(B,(2 * (L - 128))))
68 #define ChannelBlend_HardMix(B,L)    ((uint8)((ChannelBlend_VividLight(B,L) < 128) ? 0:255))
69 #define ChannelBlend_Reflect(B,L)    ((uint8)((L == 255) ? L:min(255, (B * B / (255 - L)))))
70 #define ChannelBlend_Glow(B,L)       (ChannelBlend_Reflect(L,B))
71 #define ChannelBlend_Phoenix(B,L)    ((uint8)(min(B,L) - max(B,L) + 255))
72 #define ChannelBlend_Alpha(B,L,O)    ((uint8)(O * B + (1 - O) * L))
73 #define ChannelBlend_AlphaF(B,L,F,O) (ChannelBlend_Alpha(F(B,L),B,O))
74 
75 #define STRINGIFY(s) STRINGIFY_X(s)
76 #define STRINGIFY_X(s) #s
77 
78 #pragma endregion
79 
AGS_GetPluginName()80 const char *AGSBlend::AGS_GetPluginName() {
81 	return "AGSBlend";
82 }
83 
AGS_EngineStartup(IAGSEngine * engine)84 void AGSBlend::AGS_EngineStartup(IAGSEngine *engine) {
85 	PluginBase::AGS_EngineStartup(engine);
86 
87 	// Make sure it's got the version with the features we need
88 	if (_engine->version < MIN_ENGINE_VERSION)
89 		_engine->AbortGame("Plugin needs engine version " STRINGIFY(MIN_ENGINE_VERSION) " or newer.");
90 
91 	// Register functions
92 	SCRIPT_METHOD(GetAlpha, AGSBlend::GetAlpha);
93 	SCRIPT_METHOD(PutAlpha, AGSBlend::PutAlpha);
94 	SCRIPT_METHOD(DrawAlpha, AGSBlend::DrawAlpha);
95 	SCRIPT_METHOD(Blur, AGSBlend::Blur);
96 	SCRIPT_METHOD(HighPass, AGSBlend::HighPass);
97 	SCRIPT_METHOD(DrawAdd, AGSBlend::DrawAdd);
98 	SCRIPT_METHOD(DrawSprite, AGSBlend::DrawSprite);
99 }
100 
101 //------------------------------------------------------------------------------
102 
103 #pragma region Color_Functions
104 
getr32(int c)105 int getr32(int c) {
106 	return ((c >> DEFAULT_RGB_R_SHIFT_32) & 0xFF);
107 }
108 
getg32(int c)109 int getg32(int c) {
110 	return ((c >> DEFAULT_RGB_G_SHIFT_32) & 0xFF);
111 }
112 
getb32(int c)113 int getb32(int c) {
114 	return ((c >> DEFAULT_RGB_B_SHIFT_32) & 0xFF);
115 }
116 
geta32(int c)117 int geta32(int c) {
118 	return ((c >> DEFAULT_RGB_A_SHIFT_32) & 0xFF);
119 }
120 
makeacol32(int r,int g,int b,int a)121 int makeacol32(int r, int g, int b, int a) {
122 	return ((r << DEFAULT_RGB_R_SHIFT_32) |
123 	        (g << DEFAULT_RGB_G_SHIFT_32) |
124 	        (b << DEFAULT_RGB_B_SHIFT_32) |
125 	        (a << DEFAULT_RGB_A_SHIFT_32));
126 }
127 
128 #pragma endregion
129 
130 #pragma region Pixel32_Definition
131 
132 struct Pixel32 {
133 public:
134 	int Red = 0;
135 	int Green = 0;
136 	int Blue = 0;
137 	int Alpha = 0;
138 
GetColorAsIntAGS3::Plugins::AGSBlend::Pixel32139 	int GetColorAsInt() const {
140 		return makeacol32(Red, Green, Blue, Alpha);
141 	}
142 };
143 
144 #pragma endregion
145 
GetAlpha(ScriptMethodParams & params)146 void AGSBlend::GetAlpha(ScriptMethodParams &params) {
147 	PARAMS3(int, sprite, int, x, int, y);
148 	BITMAP *engineSprite = _engine->GetSpriteGraphic(sprite);
149 
150 	uint8 *charbuffer = _engine->GetRawBitmapSurface(engineSprite);
151 	uint32 *longbuffer = (uint32 *)charbuffer;
152 	int pitch = _engine->GetBitmapPitch(engineSprite) / 4;
153 
154 	int alpha = geta32(longbuffer[y * pitch + x]);
155 
156 	_engine->ReleaseBitmapSurface(engineSprite);
157 
158 	params._result = alpha;
159 }
160 
PutAlpha(ScriptMethodParams & params)161 void AGSBlend::PutAlpha(ScriptMethodParams &params) {
162 	PARAMS4(int, sprite, int, x, int, y, int, alpha);
163 	BITMAP *engineSprite = _engine->GetSpriteGraphic(sprite);
164 
165 	uint8 *charbuffer = _engine->GetRawBitmapSurface(engineSprite);
166 	uint32 *longbuffer = (uint32 *)charbuffer;
167 	int pitch = _engine->GetBitmapPitch(engineSprite) / 4;
168 
169 	int pixel = y * pitch + x;
170 	int r = getr32(longbuffer[pixel]);
171 	int g = getg32(longbuffer[pixel]);
172 	int b = getb32(longbuffer[pixel]);
173 	longbuffer[pixel] = makeacol32(r, g, b, alpha);
174 
175 	_engine->ReleaseBitmapSurface(engineSprite);
176 
177 	params._result = alpha;
178 }
179 
xytolocale(int x,int y,int width)180 int AGSBlend::xytolocale(int x, int y, int width) {
181 	return (y * width + x);
182 }
183 
HighPass(ScriptMethodParams & params)184 void AGSBlend::HighPass(ScriptMethodParams &params) {
185 	PARAMS2(int, sprite, int, threshold);
186 	BITMAP *src = _engine->GetSpriteGraphic(sprite);
187 	int32 srcWidth, srcHeight;
188 
189 	_engine->GetBitmapDimensions(src, &srcWidth, &srcHeight, nullptr);
190 
191 	uint8 *srccharbuffer = _engine->GetRawBitmapSurface(src);
192 	uint32 *srclongbuffer = (uint32 *)srccharbuffer;
193 	int pitch = _engine->GetBitmapPitch(src) / 4;
194 
195 	for (int y = 0, yi = 0; y < srcHeight; y++, yi += pitch) {
196 
197 		for (int x = 0; x < srcWidth; x++) {
198 
199 			int srcr = getb32(srclongbuffer[yi + x]);
200 			int srcg = getg32(srclongbuffer[yi + x]);
201 			int srcb = getr32(srclongbuffer[yi + x]);
202 			int tempmaxim = max(srcr, srcg);
203 			int maxim = max(tempmaxim, srcb);
204 			int tempmin = min(srcr, srcg);
205 			int minim = min(srcb, tempmin);
206 			int light = (maxim + minim) / 2 ;
207 			if (light < threshold)
208 				srclongbuffer[yi + x] = makeacol32(0, 0, 0, 0);
209 
210 		}
211 
212 	}
213 
214 	params._result = 0;
215 
216 }
217 
Blur(ScriptMethodParams & params)218 void AGSBlend::Blur(ScriptMethodParams &params) {
219 	PARAMS2(int, sprite, int, radius);
220 	BITMAP *src = _engine->GetSpriteGraphic(sprite);
221 
222 	int32 srcWidth, srcHeight;
223 	_engine->GetBitmapDimensions(src, &srcWidth, &srcHeight, nullptr);
224 
225 	uint8 *srccharbuffer = _engine->GetRawBitmapSurface(src);
226 	uint32 *srclongbuffer = (uint32 *)srccharbuffer;
227 	int pitch = _engine->GetBitmapPitch(src) / 4;
228 	int negrad = -1 * radius;
229 
230 	//use a 1Dimensional array since the array is on the free store, not the stack
231 	Pixel32 *Pixels = new Pixel32[(srcWidth + (radius * 2)) * (srcHeight + (radius * 2))];  // this defines a copy of the individual channels in class form.
232 	Pixel32 *Dest = new Pixel32[(srcWidth + (radius * 2)) * (srcHeight + (radius * 2))];  // this is the destination sprite. both have a border all the way round equal to the radius for the blurring.
233 	Pixel32 *Temp = new Pixel32[(srcWidth + (radius * 2)) * (srcHeight + (radius * 2))];
234 
235 	int arraywidth = srcWidth + (radius * 2); //define the array width since its used many times in the algorithm
236 
237 	for (int y = 0, yi = 0; y < srcHeight; y++, yi += pitch) { //copy the sprite to the Pixels class array
238 		for (int x = 0; x < srcWidth; x++) {
239 			int locale = xytolocale(x + radius, y + radius, arraywidth);
240 
241 			Pixels[locale].Red = getr32(srclongbuffer[yi + x]);
242 			Pixels[locale].Green = getg32(srclongbuffer[yi + x]);
243 			Pixels[locale].Blue = getb32(srclongbuffer[yi + x]);
244 			Pixels[locale].Alpha = geta32(srclongbuffer[yi + x]);
245 		}
246 	}
247 
248 	int  numofpixels = (radius * 2 + 1);
249 	for (int y = 0; y < srcHeight; y++) {
250 		int totalr = 0;
251 		int totalg = 0;
252 		int totalb = 0;
253 		int totala = 0;
254 
255 		// Process entire window for first pixel
256 		for (int kx = negrad; kx <= radius; kx++) {
257 			int locale = xytolocale(kx + radius, y + radius, arraywidth);
258 			totala += Pixels[locale].Alpha;
259 			totalr += (Pixels[locale].Red * Pixels[locale].Alpha) / 255;
260 			totalg += (Pixels[locale].Green * Pixels[locale].Alpha) / 255;
261 			totalb += (Pixels[locale].Blue * Pixels[locale].Alpha) / 255;
262 		}
263 
264 		int locale = xytolocale(radius, y + radius, arraywidth);
265 		Temp[locale].Red = totalr / numofpixels; // take an average and assign it to the destination array
266 		Temp[locale].Green = totalg / numofpixels;
267 		Temp[locale].Blue = totalb / numofpixels;
268 		Temp[locale].Alpha = totala / numofpixels;
269 
270 		// Subsequent pixels just update window total
271 		for (int x = 1; x < srcWidth; x++) {
272 			// Subtract pixel leaving window
273 			locale = xytolocale(x - 1, y + radius, arraywidth);
274 			totala -= Pixels[locale].Alpha;
275 			totalr -= (Pixels[locale].Red * Pixels[locale].Alpha) / 255;
276 			totalg -= (Pixels[locale].Green * Pixels[locale].Alpha) / 255;
277 			totalb -= (Pixels[locale].Blue * Pixels[locale].Alpha) / 255;
278 
279 			// Add pixel entering window
280 			locale = xytolocale(x + radius + radius, y + radius, arraywidth);
281 			totala += Pixels[locale].Alpha;
282 			totalr += (Pixels[locale].Red * Pixels[locale].Alpha) / 255;
283 			totalg += (Pixels[locale].Green * Pixels[locale].Alpha) / 255;
284 			totalb += (Pixels[locale].Blue * Pixels[locale].Alpha) / 255;
285 
286 
287 			locale = xytolocale(x + radius, y + radius, arraywidth);
288 			Temp[locale].Red = totalr / numofpixels; // take an average and assign it to the destination array
289 			Temp[locale].Green = totalg / numofpixels;
290 			Temp[locale].Blue = totalb / numofpixels;
291 			Temp[locale].Alpha = totala / numofpixels;
292 		}
293 	}
294 
295 	for (int x = 0; x < srcWidth; x++) {
296 		int totalr = 0;
297 		int totalg = 0;
298 		int totalb = 0;
299 		int totala = 0;
300 
301 		// Process entire window for first pixel
302 		for (int ky = negrad; ky <= radius; ky++) {
303 			int locale = xytolocale(x + radius, ky + radius, arraywidth);
304 			totala += Temp[locale].Alpha;
305 			totalr += (Temp[locale].Red * Temp[locale].Alpha) / 255;
306 			totalg += (Temp[locale].Green * Temp[locale].Alpha) / 255;
307 			totalb += (Temp[locale].Blue * Temp[locale].Alpha) / 255;
308 		}
309 
310 		int locale = xytolocale(x + radius, radius, arraywidth);
311 		Dest[locale].Red = totalr / numofpixels; // take an average and assign it to the destination array
312 		Dest[locale].Green = totalg / numofpixels;
313 		Dest[locale].Blue = totalb / numofpixels;
314 		Dest[locale].Alpha = totala / numofpixels;
315 
316 		// Subsequent pixels just update window total
317 		for (int y = 1; y < srcHeight; y++) {
318 			// Subtract pixel leaving window
319 			locale = xytolocale(x + radius, y - 1, arraywidth);
320 			totala -= Temp[locale].Alpha;
321 			totalr -= (Temp[locale].Red * Temp[locale].Alpha) / 255;
322 			totalg -= (Temp[locale].Green * Temp[locale].Alpha) / 255;
323 			totalb -= (Temp[locale].Blue * Temp[locale].Alpha) / 255;
324 
325 
326 			// Add pixel entering window
327 
328 			locale = xytolocale(x + radius, y + radius + radius, arraywidth);
329 			totala += Temp[locale].Alpha;
330 			totalr += (Temp[locale].Red * Temp[locale].Alpha) / 255;
331 			totalg += (Temp[locale].Green * Temp[locale].Alpha) / 255;
332 			totalb += (Temp[locale].Blue * Temp[locale].Alpha) / 255;
333 
334 
335 			locale = xytolocale(x + radius, y + radius, arraywidth);
336 			Dest[locale].Red = totalr / numofpixels; // take an average and assign it to the destination array
337 			Dest[locale].Green = totalg / numofpixels;
338 			Dest[locale].Blue = totalb / numofpixels;
339 			Dest[locale].Alpha = totala / numofpixels;
340 		}
341 	}
342 
343 	for (int y = 0, yi = 0; y < srcHeight; y++, yi += pitch) {
344 
345 		for (int x = 0; x < srcWidth; x++) {
346 			int locale = xytolocale(x + radius, y + radius, arraywidth);
347 			srclongbuffer[yi + x] = Dest[locale].GetColorAsInt(); //write the destination array to the main buffer
348 		}
349 	}
350 
351 	delete[] Pixels;
352 	delete[] Dest;
353 	delete[] Temp;
354 	_engine->ReleaseBitmapSurface(src);
355 	delete srclongbuffer;
356 	delete srccharbuffer;
357 
358 	params._result = 0;
359 }
360 
DrawSprite(ScriptMethodParams & params)361 void AGSBlend::DrawSprite(ScriptMethodParams &params) {
362 	PARAMS6(int, destination, int, sprite, int, x, int, y, int, DrawMode, int, trans);
363 	trans = 100 - trans;
364 	int32 srcWidth, srcHeight, destWidth, destHeight;
365 
366 	BITMAP *src = _engine->GetSpriteGraphic(sprite);
367 	BITMAP *dest = _engine->GetSpriteGraphic(destination);
368 
369 	_engine->GetBitmapDimensions(src, &srcWidth, &srcHeight, nullptr);
370 	_engine->GetBitmapDimensions(dest, &destWidth, &destHeight, nullptr);
371 
372 	if (x > destWidth || y > destHeight || x + srcWidth < 0 || y + srcHeight < 0) {
373 		// offscreen
374 		params._result = 1;
375 		return;
376 	}
377 
378 	uint8 *srccharbuffer = _engine->GetRawBitmapSurface(src);
379 	uint32 *srclongbuffer = (uint32 *)srccharbuffer;
380 	int srcPitch = _engine->GetBitmapPitch(src) / 4;
381 
382 	uint8 *destcharbuffer = _engine->GetRawBitmapSurface(dest);
383 	uint32 *destlongbuffer = (uint32 *)destcharbuffer;
384 	int destPitch = _engine->GetBitmapPitch(dest) / 4;
385 
386 	if (srcWidth + x > destWidth) srcWidth = destWidth - x - 1;
387 	if (srcHeight + y > destHeight) srcHeight = destHeight - y - 1;
388 
389 	int srcr, srcg, srcb, srca, destr, destg, destb, desta;
390 	int finalr = 0, finalg = 0, finalb = 0, finala = 0;
391 	unsigned int col;
392 	int starty = 0;
393 	int startx = 0;
394 
395 	if (x < 0) startx = -1 * x;
396 	if (y < 0) starty = -1 * y;
397 
398 	int ycount = 0;
399 	int xcount = 0;
400 	int srcy = starty * srcPitch;
401 	int desty = (starty + y) * destPitch;
402 	for (ycount = starty; ycount < srcHeight; ycount ++, srcy += srcPitch, desty += destPitch) {
403 		for (xcount = startx; xcount < srcWidth; xcount ++) {
404 			int destx = xcount + x;
405 
406 			srca = (geta32(srclongbuffer[srcy + xcount]));
407 
408 			if (srca != 0) {
409 				srca = srca * trans / 100;
410 				srcr =  getr32(srclongbuffer[srcy + xcount]);
411 				srcg =  getg32(srclongbuffer[srcy + xcount]);
412 				srcb =  getb32(srclongbuffer[srcy + xcount]);
413 
414 				destr =  getr32(destlongbuffer[desty + destx]);
415 				destg =  getg32(destlongbuffer[desty + destx]);
416 				destb =  getb32(destlongbuffer[desty + destx]);
417 				desta =  geta32(destlongbuffer[desty + destx]);
418 
419 				switch (DrawMode) {
420 				case 0:
421 					finalr = srcr;
422 					finalg = srcg;
423 					finalb = srcb;
424 					break;
425 
426 				case 1:
427 					finalr = ChannelBlend_Lighten(srcr, destr);
428 					finalg = ChannelBlend_Lighten(srcg, destg);
429 					finalb = ChannelBlend_Lighten(srcb, destb);
430 					break;
431 
432 				case 2:
433 					finalr = ChannelBlend_Darken(srcr, destr);
434 					finalg = ChannelBlend_Darken(srcg, destg);
435 					finalb = ChannelBlend_Darken(srcb, destb);
436 					break;
437 
438 				case 3:
439 					finalr = ChannelBlend_Multiply(srcr, destr);
440 					finalg = ChannelBlend_Multiply(srcg, destg);
441 					finalb = ChannelBlend_Multiply(srcb, destb);
442 					break;
443 
444 				case 4:
445 					finalr = ChannelBlend_Add(srcr, destr);
446 					finalg = ChannelBlend_Add(srcg, destg);
447 					finalb = ChannelBlend_Add(srcb, destb);
448 					break;
449 
450 				case 5:
451 					finalr = ChannelBlend_Subtract(srcr, destr);
452 					finalg = ChannelBlend_Subtract(srcg, destg);
453 					finalb = ChannelBlend_Subtract(srcb, destb);
454 					break;
455 
456 				case 6:
457 					finalr = ChannelBlend_Difference(srcr, destr);
458 					finalg = ChannelBlend_Difference(srcg, destg);
459 					finalb = ChannelBlend_Difference(srcb, destb);
460 					break;
461 
462 				case 7:
463 					finalr = ChannelBlend_Negation(srcr, destr);
464 					finalg = ChannelBlend_Negation(srcg, destg);
465 					finalb = ChannelBlend_Negation(srcb, destb);
466 					break;
467 
468 				case 8:
469 					finalr = ChannelBlend_Screen(srcr, destr);
470 					finalg = ChannelBlend_Screen(srcg, destg);
471 					finalb = ChannelBlend_Screen(srcb, destb);
472 					break;
473 
474 				case 9:
475 					finalr = ChannelBlend_Exclusion(srcr, destr);
476 					finalg = ChannelBlend_Exclusion(srcg, destg);
477 					finalb = ChannelBlend_Exclusion(srcb, destb);
478 					break;
479 
480 				case 10:
481 					finalr = ChannelBlend_Overlay(srcr, destr);
482 					finalg = ChannelBlend_Overlay(srcg, destg);
483 					finalb = ChannelBlend_Overlay(srcb, destb);
484 					break;
485 
486 				case 11:
487 					finalr = ChannelBlend_SoftLight(srcr, destr);
488 					finalg = ChannelBlend_SoftLight(srcg, destg);
489 					finalb = ChannelBlend_SoftLight(srcb, destb);
490 					break;
491 
492 				case 12:
493 					finalr = ChannelBlend_HardLight(srcr, destr);
494 					finalg = ChannelBlend_HardLight(srcg, destg);
495 					finalb = ChannelBlend_HardLight(srcb, destb);
496 					break;
497 
498 				case 13:
499 					finalr = ChannelBlend_ColorDodge(srcr, destr);
500 					finalg = ChannelBlend_ColorDodge(srcg, destg);
501 					finalb = ChannelBlend_ColorDodge(srcb, destb);
502 					break;
503 
504 				case 14:
505 					finalr = ChannelBlend_ColorBurn(srcr, destr);
506 					finalg = ChannelBlend_ColorBurn(srcg, destg);
507 					finalb = ChannelBlend_ColorBurn(srcb, destb);
508 					break;
509 
510 				case 15:
511 					finalr = ChannelBlend_LinearDodge(srcr, destr);
512 					finalg = ChannelBlend_LinearDodge(srcg, destg);
513 					finalb = ChannelBlend_LinearDodge(srcb, destb);
514 					break;
515 
516 				case 16:
517 					finalr = ChannelBlend_LinearBurn(srcr, destr);
518 					finalg = ChannelBlend_LinearBurn(srcg, destg);
519 					finalb = ChannelBlend_LinearBurn(srcb, destb);
520 					break;
521 
522 				case 17:
523 					finalr = ChannelBlend_LinearLight(srcr, destr);
524 					finalg = ChannelBlend_LinearLight(srcg, destg);
525 					finalb = ChannelBlend_LinearLight(srcb, destb);
526 					break;
527 
528 				case 18:
529 					finalr = ChannelBlend_VividLight(srcr, destr);
530 					finalg = ChannelBlend_VividLight(srcg, destg);
531 					finalb = ChannelBlend_VividLight(srcb, destb);
532 					break;
533 
534 				case 19:
535 					finalr = ChannelBlend_PinLight(srcr, destr);
536 					finalg = ChannelBlend_PinLight(srcg, destg);
537 					finalb = ChannelBlend_PinLight(srcb, destb);
538 					break;
539 
540 				case 20:
541 					finalr = ChannelBlend_HardMix(srcr, destr);
542 					finalg = ChannelBlend_HardMix(srcg, destg);
543 					finalb = ChannelBlend_HardMix(srcb, destb);
544 					break;
545 
546 				case 21:
547 					finalr = ChannelBlend_Reflect(srcr, destr);
548 					finalg = ChannelBlend_Reflect(srcg, destg);
549 					finalb = ChannelBlend_Reflect(srcb, destb);
550 					break;
551 
552 				case 22:
553 					finalr = ChannelBlend_Glow(srcr, destr);
554 					finalg = ChannelBlend_Glow(srcg, destg);
555 					finalb = ChannelBlend_Glow(srcb, destb);
556 					break;
557 
558 				case 23:
559 					finalr = ChannelBlend_Phoenix(srcr, destr);
560 					finalg = ChannelBlend_Phoenix(srcg, destg);
561 					finalb = ChannelBlend_Phoenix(srcb, destb);
562 					break;
563 
564 				default:
565 					break;
566 				}
567 
568 				finala = 255 - (255 - srca) * (255 - desta) / 255;
569 				finalr = srca * finalr / finala + desta * destr * (255 - srca) / finala / 255;
570 				finalg = srca * finalg / finala + desta * destg * (255 - srca) / finala / 255;
571 				finalb = srca * finalb / finala + desta * destb * (255 - srca) / finala / 255;
572 				col = makeacol32(finalr, finalg, finalb, finala);
573 				destlongbuffer[desty + destx] = col;
574 
575 			}
576 
577 
578 		}
579 
580 	}
581 
582 	_engine->ReleaseBitmapSurface(src);
583 	_engine->ReleaseBitmapSurface(dest);
584 	_engine->NotifySpriteUpdated(destination);
585 	params._result = 0;
586 
587 }
588 
DrawAdd(ScriptMethodParams & params)589 void AGSBlend::DrawAdd(ScriptMethodParams &params) {
590 	PARAMS5(int, destination, int, sprite, int, x, int, y, float, scale);
591 	int32 srcWidth, srcHeight, destWidth, destHeight;
592 
593 	BITMAP *src = _engine->GetSpriteGraphic(sprite);
594 	BITMAP *dest = _engine->GetSpriteGraphic(destination);
595 
596 	_engine->GetBitmapDimensions(src, &srcWidth, &srcHeight, nullptr);
597 	_engine->GetBitmapDimensions(dest, &destWidth, &destHeight, nullptr);
598 
599 	if (x > destWidth || y > destHeight) {
600 		// offscreen
601 		params._result = 1;
602 		return;
603 	}
604 
605 	uint8 *srccharbuffer = _engine->GetRawBitmapSurface(src);
606 	uint32 *srclongbuffer = (uint32 *)srccharbuffer;
607 	int srcPitch = _engine->GetBitmapPitch(src) / 4;
608 
609 	uint8 *destcharbuffer = _engine->GetRawBitmapSurface(dest);
610 	uint32 *destlongbuffer = (uint32 *)destcharbuffer;
611 	int destPitch = _engine->GetBitmapPitch(dest) / 4;
612 
613 	if (srcWidth + x > destWidth) srcWidth = destWidth - x - 1;
614 	if (srcHeight + y > destHeight) srcHeight = destHeight - y - 1;
615 
616 	int srcr, srcg, srcb, srca, destr, destg, destb, desta, finalr, finalg, finalb, finala;
617 	unsigned int col;
618 	int ycount = 0;
619 	int xcount = 0;
620 
621 	int starty = 0;
622 	int startx = 0;
623 
624 	if (x < 0) startx = -1 * x;
625 	if (y < 0) starty = -1 * y;
626 
627 	int srcy = starty * srcPitch;
628 	int desty = (starty + y) * destPitch;
629 	for (ycount = starty; ycount < srcHeight; ycount ++, srcy += srcPitch, desty += destPitch) {
630 		for (xcount = startx; xcount < srcWidth; xcount ++) {
631 			int destx = xcount + x;
632 
633 			srca = (geta32(srclongbuffer[srcy + xcount]));
634 
635 			if (srca != 0) {
636 				srcr =  getr32(srclongbuffer[srcy + xcount]) * srca / 255 * scale;
637 				srcg =  getg32(srclongbuffer[srcy + xcount]) * srca / 255 * scale;
638 				srcb =  getb32(srclongbuffer[srcy + xcount]) * srca / 255 * scale;
639 				desta =  geta32(destlongbuffer[desty + destx]);
640 
641 				if (desta == 0) {
642 					destr = 0;
643 					destg = 0;
644 					destb = 0;
645 
646 				} else {
647 					destr =  getr32(destlongbuffer[desty + destx]);
648 					destg =  getg32(destlongbuffer[desty + destx]);
649 					destb =  getb32(destlongbuffer[desty + destx]);
650 				}
651 
652 				finala = 255 - (255 - srca) * (255 - desta) / 255;
653 				finalr = CLIP(srcr + destr, 0, 255);
654 				finalg = CLIP(srcg + destg, 0, 255);
655 				finalb = CLIP(srcb + destb, 0, 255);
656 				col = makeacol32(finalr, finalg, finalb, finala);
657 				destlongbuffer[desty + destx] = col;
658 			}
659 		}
660 	}
661 
662 	_engine->ReleaseBitmapSurface(src);
663 	_engine->ReleaseBitmapSurface(dest);
664 	_engine->NotifySpriteUpdated(destination);
665 
666 	params._result = 0;
667 }
668 
DrawAlpha(ScriptMethodParams & params)669 void AGSBlend::DrawAlpha(ScriptMethodParams &params) {
670 	PARAMS5(int, destination, int, sprite, int, x, int, y, int, trans);
671 	trans = 100 - trans;
672 
673 	int32 srcWidth, srcHeight, destWidth, destHeight;
674 
675 	BITMAP *src = _engine->GetSpriteGraphic(sprite);
676 	BITMAP *dest = _engine->GetSpriteGraphic(destination);
677 
678 	_engine->GetBitmapDimensions(src, &srcWidth, &srcHeight, nullptr);
679 	_engine->GetBitmapDimensions(dest, &destWidth, &destHeight, nullptr);
680 
681 	if (x > destWidth || y > destHeight) {
682 		// offscreen
683 		params._result = 1;
684 		return;
685 	}
686 
687 	uint8 *srccharbuffer = _engine->GetRawBitmapSurface(src);
688 	uint32 *srclongbuffer = (uint32 *)srccharbuffer;
689 	int srcPitch = _engine->GetBitmapPitch(src) / 4;
690 
691 	uint8 *destcharbuffer = _engine->GetRawBitmapSurface(dest);
692 	uint32 *destlongbuffer = (uint32 *)destcharbuffer;
693 	int destPitch = _engine->GetBitmapPitch(dest) / 4;
694 
695 	if (srcWidth + x > destWidth) srcWidth = destWidth - x - 1;
696 	if (srcHeight + y > destHeight) srcHeight = destHeight - y - 1;
697 
698 	int srcr, srcg, srcb, srca, destr, destg, destb, desta, finalr, finalg, finalb, finala;
699 
700 	int ycount = 0;
701 	int xcount = 0;
702 
703 	int starty = 0;
704 	int startx = 0;
705 
706 	if (x < 0) startx = -1 * x;
707 	if (y < 0) starty = -1 * y;
708 
709 	int srcy = starty * srcPitch;
710 	int desty = (starty + y) * destPitch;
711 	for (ycount = starty; ycount < srcHeight; ycount ++, srcy += srcPitch, desty += destPitch) {
712 		for (xcount = startx; xcount < srcWidth; xcount ++) {
713 			int destx = xcount + x;
714 
715 			srca = (geta32(srclongbuffer[srcy + xcount])) * trans / 100;
716 
717 			if (srca != 0) {
718 				srcr =  getr32(srclongbuffer[srcy + xcount]);
719 				srcg =  getg32(srclongbuffer[srcy + xcount]);
720 				srcb =  getb32(srclongbuffer[srcy + xcount]);
721 
722 				destr =  getr32(destlongbuffer[desty + destx]);
723 				destg =  getg32(destlongbuffer[desty + destx]);
724 				destb =  getb32(destlongbuffer[desty + destx]);
725 				desta =  geta32(destlongbuffer[desty + destx]);
726 
727 				finala = 255 - (255 - srca) * (255 - desta) / 255;
728 				finalr = srca * srcr / finala + desta * destr * (255 - srca) / finala / 255;
729 				finalg = srca * srcg / finala + desta * destg * (255 - srca) / finala / 255;
730 				finalb = srca * srcb / finala + desta * destb * (255 - srca) / finala / 255;
731 
732 				destlongbuffer[desty + destx] = makeacol32(finalr, finalg, finalb, finala);
733 			}
734 		}
735 	}
736 
737 	_engine->ReleaseBitmapSurface(src);
738 	_engine->ReleaseBitmapSurface(dest);
739 	_engine->NotifySpriteUpdated(destination);
740 
741 	params._result = 0;
742 }
743 
744 } // namespace AGSBlend
745 } // namespace Plugins
746 } // namespace AGS3
747