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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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