1 //=============================================================================
2 //
3 // Adventure Game Studio (AGS)
4 //
5 // Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
6 // The full list of copyright holders can be found in the Copyright.txt
7 // file, which is part of this source code distribution.
8 //
9 // The AGS source code is provided under the Artistic License 2.0.
10 // A copy of this license can be found in the file License.txt and at
11 // http://www.opensource.org/licenses/artistic-license-2.0.php
12 //
13 //=============================================================================
14
15 #include <algorithm>
16 #include "aastr.h"
17 #include "ac/common.h"
18 #include "util/compress.h"
19 #include "ac/view.h"
20 #include "ac/charactercache.h"
21 #include "ac/characterextras.h"
22 #include "ac/characterinfo.h"
23 #include "ac/display.h"
24 #include "ac/draw.h"
25 #include "ac/gamesetup.h"
26 #include "ac/gamesetupstruct.h"
27 #include "ac/gamestate.h"
28 #include "ac/global_game.h"
29 #include "ac/global_gui.h"
30 #include "ac/global_region.h"
31 #include "ac/gui.h"
32 #include "ac/mouse.h"
33 #include "ac/objectcache.h"
34 #include "ac/overlay.h"
35 #include "ac/record.h"
36 #include "ac/roomobject.h"
37 #include "ac/roomstatus.h"
38 #include "ac/roomstruct.h"
39 #include "ac/runtime_defines.h"
40 #include "ac/screenoverlay.h"
41 #include "ac/sprite.h"
42 #include "ac/spritelistentry.h"
43 #include "ac/string.h"
44 #include "ac/system.h"
45 #include "ac/viewframe.h"
46 #include "ac/viewport.h"
47 #include "ac/walkablearea.h"
48 #include "ac/walkbehind.h"
49 #include "ac/dynobj/scriptsystem.h"
50 #include "debug/debugger.h"
51 #include "debug/debug_log.h"
52 #include "font/fonts.h"
53 #include "gui/guimain.h"
54 #include "media/audio/audio.h"
55 #include "media/audio/soundclip.h"
56 #include "platform/base/agsplatformdriver.h"
57 #include "plugin/agsplugin.h"
58 #include "plugin/plugin_engine.h"
59 #include "ac/spritecache.h"
60 #include "gfx/gfx_util.h"
61 #include "gfx/graphicsdriver.h"
62 #include "gfx/ali3dexception.h"
63 #include "gfx/blender.h"
64
65 using namespace AGS::Common;
66 using namespace AGS::Engine;
67
68 #if defined(ANDROID_VERSION)
69 #include <sys/stat.h>
70 #include <android/log.h>
71
72 extern "C" void android_render();
73 #endif
74
75 #if defined(IOS_VERSION)
76 extern "C" void ios_render();
77 #endif
78
79 extern GameSetup usetup;
80 extern GameSetupStruct game;
81 extern GameState play;
82 extern int current_screen_resolution_multiplier;
83 extern int convert_16bit_bgr;
84 extern ScriptSystem scsystem;
85 extern AGSPlatformDriver *platform;
86 extern roomstruct thisroom;
87 extern char noWalkBehindsAtAll;
88 extern unsigned int loopcounter;
89 extern char *walkBehindExists; // whether a WB area is in this column
90 extern int *walkBehindStartY, *walkBehindEndY;
91 extern int walkBehindLeft[MAX_OBJ], walkBehindTop[MAX_OBJ];
92 extern int walkBehindRight[MAX_OBJ], walkBehindBottom[MAX_OBJ];
93 extern IDriverDependantBitmap *walkBehindBitmap[MAX_OBJ];
94 extern int walkBehindsCachedForBgNum;
95 extern WalkBehindMethodEnum walkBehindMethod;
96 extern int walk_behind_baselines_changed;
97 extern int spritewidth[MAX_SPRITES],spriteheight[MAX_SPRITES];
98 extern SpriteCache spriteset;
99 extern RoomStatus*croom;
100 extern int our_eip;
101 extern int in_new_room;
102 extern RoomObject*objs;
103 extern ViewStruct*views;
104 extern CharacterCache *charcache;
105 extern ObjectCache objcache[MAX_INIT_SPR];
106 extern int displayed_room;
107 extern CharacterExtras *charextra;
108 extern CharacterInfo*playerchar;
109 extern int eip_guinum;
110 extern ScreenOverlay screenover[MAX_SCREEN_OVERLAYS];
111 extern int numscreenover;
112 extern int screen_reset;
113 extern int is_complete_overlay;
114 extern int cur_mode,cur_cursor;
115 extern int mouse_frame,mouse_delay;
116 extern int lastmx,lastmy;
117 extern int replay_time;
118 extern int mouse_on_iface_button;
119 extern IDriverDependantBitmap *mouseCursor;
120 extern int hotx,hoty;
121 extern int bg_just_changed;
122
123 // How is this actually used??
124 // We need COLOR_DEPTH_24 to allow it to load the preload PCX in hi-col
125 BEGIN_COLOR_DEPTH_LIST
126 COLOR_DEPTH_8
127 COLOR_DEPTH_15
128 COLOR_DEPTH_16
129 COLOR_DEPTH_24
130 COLOR_DEPTH_32
131 END_COLOR_DEPTH_LIST
132
133
134 color palette[256];
135
136 COLOR_MAP maincoltable;
137
138 IGraphicsDriver *gfxDriver;
139 IDriverDependantBitmap *blankImage = NULL;
140 IDriverDependantBitmap *blankSidebarImage = NULL;
141 IDriverDependantBitmap *debugConsole = NULL;
142
143 // actsps is used for temporary storage of the bitamp image
144 // of the latest version of the sprite
145 int actSpsCount = 0;
146 Bitmap **actsps;
147 IDriverDependantBitmap* *actspsbmp;
148 // temporary cache of walk-behind for this actsps image
149 Bitmap **actspswb;
150 IDriverDependantBitmap* *actspswbbmp;
151 CachedActSpsData* actspswbcache;
152
153 Bitmap *virtual_screen;
154
155 bool current_background_is_dirty = false;
156
157 Bitmap *_old_screen=NULL;
158 Bitmap *_sub_screen=NULL;
159
160 int offsetx = 0, offsety = 0;
161 int wasShakingScreen = 0;
162
163 IDriverDependantBitmap* roomBackgroundBmp = NULL;
164
165
166 std::vector<SpriteListEntry> sprlist;
167 std::vector<SpriteListEntry> thingsToDrawList;
168
169 //GUIMain dummygui;
170 //GUIButton dummyguicontrol;
171 Bitmap **guibg = NULL;
172 IDriverDependantBitmap **guibgbmp = NULL;
173
174
175 Bitmap *debugConsoleBuffer = NULL;
176
177 // whether there are currently remnants of a DisplaySpeech
178 int screen_is_dirty = 0;
179
180 Bitmap *raw_saved_screen = NULL;
181 Bitmap *dynamicallyCreatedSurfaces[MAX_DYNAMIC_SURFACES];
182
183
SpriteListEntry()184 SpriteListEntry::SpriteListEntry()
185 : bmp(NULL)
186 , pic(NULL)
187 , baseline(0), x(0), y(0)
188 , transparent(0)
189 , takesPriorityIfEqual(false), hasAlphaChannel(false)
190 {
191 }
192
193
194 // TODO: move to test unit
195 extern Bitmap *test_allegro_bitmap;
196 extern IDriverDependantBitmap *test_allegro_ddb;
allegro_bitmap_test_draw()197 void allegro_bitmap_test_draw()
198 {
199 if (test_allegro_bitmap)
200 {
201 test_allegro_bitmap->FillTransparent();
202 test_allegro_bitmap->FillRect(Rect(50,50,150,150), 15);
203
204 if (test_allegro_ddb == NULL)
205 {
206 test_allegro_ddb = gfxDriver->CreateDDBFromBitmap(test_allegro_bitmap, false, true);
207 }
208 else
209 {
210 gfxDriver->UpdateDDBFromBitmap(test_allegro_ddb, test_allegro_bitmap, false);
211 }
212 gfxDriver->DrawSprite(-offsetx, -offsety, test_allegro_ddb);
213 }
214 }
215
setpal()216 void setpal() {
217 set_palette_range(palette, 0, 255, 0);
218 }
219
220 int _places_r = 3, _places_g = 2, _places_b = 3;
221
222 // convert RGB to BGR for strange graphics cards
convert_16_to_16bgr(Bitmap * tempbl)223 Bitmap *convert_16_to_16bgr(Bitmap *tempbl) {
224
225 int x,y;
226 unsigned short c,r,ds,b;
227
228 for (y=0; y < tempbl->GetHeight(); y++) {
229 unsigned short*p16 = (unsigned short *)tempbl->GetScanLine(y);
230
231 for (x=0; x < tempbl->GetWidth(); x++) {
232 c = p16[x];
233 if (c != MASK_COLOR_16) {
234 b = _rgb_scale_5[c & 0x1F];
235 ds = _rgb_scale_6[(c >> 5) & 0x3F];
236 r = _rgb_scale_5[(c >> 11) & 0x1F];
237 // allegro assumes 5-6-5 for 16-bit
238 p16[x] = (((r >> _places_r) << _rgb_r_shift_16) |
239 ((ds >> _places_g) << _rgb_g_shift_16) |
240 ((b >> _places_b) << _rgb_b_shift_16));
241
242 }
243 }
244 }
245
246 return tempbl;
247 }
248
249 // PSP: convert 32 bit RGB to BGR.
convert_32_to_32bgr(Bitmap * tempbl)250 Bitmap *convert_32_to_32bgr(Bitmap *tempbl) {
251
252 int i = 0;
253 int j = 0;
254 unsigned char* current;
255 while (i < tempbl->GetHeight())
256 {
257 current = tempbl->GetScanLineForWriting(i);
258 while (j < tempbl->GetWidth())
259 {
260 current[0] ^= current[2];
261 current[2] ^= current[0];
262 current[0] ^= current[2];
263 current += 4;
264 j++;
265 }
266 i++;
267 j = 0;
268 }
269
270 return tempbl;
271 }
272
273 // NOTE: Some of these conversions are required even when using
274 // D3D and OpenGL rendering, for two reasons:
275 // 1) certain raw drawing operations are still performed by software
276 // Allegro methods, hence bitmaps should be kept compatible to any native
277 // software operations, such as blitting two bitmaps of different formats.
278 // 2) mobile ports feature an OpenGL renderer built in Allegro library,
279 // that assumes native bitmaps are in OpenGL-compatible format, so that it
280 // could copy them to texture without additional changes.
281 // AGS own OpenGL renderer tries to sync its behavior with the former one.
282 //
283 // TODO: make gfxDriver->GetCompatibleBitmapFormat describe all necessary
284 // conversions, so that we did not have to guess.
285 //
AdjustBitmapForUseWithDisplayMode(Bitmap * bitmap,bool has_alpha)286 Bitmap *AdjustBitmapForUseWithDisplayMode(Bitmap* bitmap, bool has_alpha)
287 {
288 const int bmp_col_depth = bitmap->GetColorDepth();
289 const int sys_col_depth = System_GetColorDepth();
290 const int game_col_depth = game.GetColorDepth();
291 Bitmap *new_bitmap = bitmap;
292
293 //
294 // The only special case when bitmap needs to be prepared for graphics driver
295 //
296 // In 32-bit display mode, 32-bit bitmaps may require component conversion
297 // to match graphics driver expectation about pixel format.
298 // TODO: make GetCompatibleBitmapFormat tell this somehow
299 #if defined (AGS_INVERTED_COLOR_ORDER)
300 if (sys_col_depth > 16 && bmp_col_depth == 32)
301 {
302 // Convert RGB to BGR.
303 new_bitmap = convert_32_to_32bgr(bitmap);
304 }
305 #endif
306
307 //
308 // The rest is about bringing bitmaps to the native game's format
309 // (has no dependency on display mode).
310 //
311 // In 32-bit game 32-bit bitmaps should have transparent pixels marked
312 // (this adjustment is probably needed for DrawingSurface ops)
313 if (game_col_depth == 32 && bmp_col_depth == 32)
314 {
315 if (has_alpha)
316 set_rgb_mask_using_alpha_channel(new_bitmap);
317 }
318 // In 32-bit game hicolor bitmaps must be converted to the true color
319 else if (game_col_depth == 32 && (bmp_col_depth > 8 && bmp_col_depth <= 16))
320 {
321 new_bitmap = BitmapHelper::CreateBitmapCopy(bitmap, game_col_depth);
322 }
323 // In non-32-bit game truecolor bitmaps must be downgraded
324 else if (game_col_depth <= 16 && bmp_col_depth > 16)
325 {
326 if (has_alpha) // if has valid alpha channel, convert it to regular transparency mask
327 new_bitmap = remove_alpha_channel(bitmap);
328 else // else simply convert bitmap
329 new_bitmap = BitmapHelper::CreateBitmapCopy(bitmap, game_col_depth);
330 }
331 // Special case when we must convert 16-bit RGB to BGR
332 else if (convert_16bit_bgr == 1 && bmp_col_depth == 16)
333 {
334 new_bitmap = convert_16_to_16bgr(bitmap);
335 }
336 return new_bitmap;
337 }
338
ReplaceBitmapWithSupportedFormat(Bitmap * bitmap)339 Bitmap *ReplaceBitmapWithSupportedFormat(Bitmap *bitmap)
340 {
341 Bitmap *new_bitmap = GfxUtil::ConvertBitmap(bitmap, gfxDriver->GetCompatibleBitmapFormat(bitmap->GetColorDepth()));
342 if (new_bitmap != bitmap)
343 delete bitmap;
344 return new_bitmap;
345 }
346
PrepareSpriteForUse(Bitmap * bitmap,bool has_alpha)347 Bitmap *PrepareSpriteForUse(Bitmap* bitmap, bool has_alpha)
348 {
349 bool must_switch_palette = bitmap->GetColorDepth() == 8 && game.GetColorDepth() > 8;
350 if (must_switch_palette)
351 select_palette(palette);
352
353 Bitmap *new_bitmap = AdjustBitmapForUseWithDisplayMode(bitmap, has_alpha);
354 if (new_bitmap != bitmap)
355 delete bitmap;
356 new_bitmap = ReplaceBitmapWithSupportedFormat(new_bitmap);
357
358 if (must_switch_palette)
359 unselect_palette();
360 return new_bitmap;
361 }
362
CopyScreenIntoBitmap(int width,int height,bool at_native_res)363 Bitmap *CopyScreenIntoBitmap(int width, int height, bool at_native_res)
364 {
365 Bitmap *dst = new Bitmap(width, height, game.GetColorDepth());
366 Size want_size;
367 // If the size and color depth are supported we may copy right into our bitmap
368 if (gfxDriver->GetCopyOfScreenIntoBitmap(dst, at_native_res, &want_size))
369 return dst;
370 // Otherwise we might need to copy between few bitmaps...
371 Bitmap *buf_screenfmt = new Bitmap(want_size.Width, want_size.Height, gfxDriver->GetDisplayMode().ColorDepth);
372 gfxDriver->GetCopyOfScreenIntoBitmap(buf_screenfmt, at_native_res);
373 // If at least size matches then we may blit
374 if (dst->GetSize() == buf_screenfmt->GetSize())
375 {
376 dst->Blit(buf_screenfmt);
377 }
378 // Otherwise we need to go through another bitmap of the matching format
379 else
380 {
381 Bitmap *buf_dstfmt = new Bitmap(buf_screenfmt->GetWidth(), buf_screenfmt->GetHeight(), dst->GetColorDepth());
382 buf_dstfmt->Blit(buf_screenfmt);
383 dst->StretchBlt(buf_dstfmt, RectWH(dst->GetSize()));
384 delete buf_dstfmt;
385 }
386 delete buf_screenfmt;
387 return dst;
388 }
389
390
391 // Begin resolution system functions
392
393 // Multiplies up the number of pixels depending on the current
394 // resolution, to give a relatively fixed size at any game res
get_fixed_pixel_size(int pixels)395 AGS_INLINE int get_fixed_pixel_size(int pixels)
396 {
397 return pixels * current_screen_resolution_multiplier;
398 }
399
convert_to_low_res(int coord)400 AGS_INLINE int convert_to_low_res(int coord)
401 {
402 if (game.options[OPT_NATIVECOORDINATES] == 0)
403 return coord;
404 else
405 return coord / current_screen_resolution_multiplier;
406 }
407
convert_back_to_high_res(int coord)408 AGS_INLINE int convert_back_to_high_res(int coord)
409 {
410 if (game.options[OPT_NATIVECOORDINATES] == 0)
411 return coord;
412 else
413 return coord * current_screen_resolution_multiplier;
414 }
415
multiply_up_coordinate(int coord)416 AGS_INLINE int multiply_up_coordinate(int coord)
417 {
418 if (game.options[OPT_NATIVECOORDINATES] == 0)
419 return coord * current_screen_resolution_multiplier;
420 else
421 return coord;
422 }
423
multiply_up_coordinates(int * x,int * y)424 AGS_INLINE void multiply_up_coordinates(int *x, int *y)
425 {
426 if (game.options[OPT_NATIVECOORDINATES] == 0)
427 {
428 x[0] *= current_screen_resolution_multiplier;
429 y[0] *= current_screen_resolution_multiplier;
430 }
431 }
432
multiply_up_coordinates_round_up(int * x,int * y)433 AGS_INLINE void multiply_up_coordinates_round_up(int *x, int *y)
434 {
435 if (game.options[OPT_NATIVECOORDINATES] == 0)
436 {
437 x[0] = x[0] * current_screen_resolution_multiplier + (current_screen_resolution_multiplier - 1);
438 y[0] = y[0] * current_screen_resolution_multiplier + (current_screen_resolution_multiplier - 1);
439 }
440 }
441
divide_down_coordinate(int coord)442 AGS_INLINE int divide_down_coordinate(int coord)
443 {
444 if (game.options[OPT_NATIVECOORDINATES] == 0)
445 return coord / current_screen_resolution_multiplier;
446 else
447 return coord;
448 }
449
divide_down_coordinate_round_up(int coord)450 AGS_INLINE int divide_down_coordinate_round_up(int coord)
451 {
452 if (game.options[OPT_NATIVECOORDINATES] == 0)
453 return (coord / current_screen_resolution_multiplier) + (current_screen_resolution_multiplier - 1);
454 else
455 return coord;
456 }
457
458 // End resolution system functions
459
460
461
462 // ** dirty rectangle system **
463
464 #define MAXDIRTYREGIONS 25
465 #define WHOLESCREENDIRTY (MAXDIRTYREGIONS + 5)
466 #define MAX_SPANS_PER_ROW 4
467 struct InvalidRect {
468 int x1, y1, x2, y2;
469 };
470 struct IRSpan {
471 int x1, x2;
472 int mergeSpan(int tx1, int tx2);
473 };
474 struct IRRow {
475 IRSpan span[MAX_SPANS_PER_ROW];
476 int numSpans;
477 };
478 IRRow *dirtyRow = NULL;
479 int _dirtyRowSize;
480 InvalidRect dirtyRegions[MAXDIRTYREGIONS];
481 int numDirtyRegions = 0;
482 int numDirtyBytes = 0;
483
mergeSpan(int tx1,int tx2)484 int IRSpan::mergeSpan(int tx1, int tx2) {
485 if ((tx1 > x2) || (tx2 < x1))
486 return 0;
487 // overlapping, increase the span
488 if (tx1 < x1)
489 x1 = tx1;
490 if (tx2 > x2)
491 x2 = tx2;
492 return 1;
493 }
494
init_invalid_regions(int scrnHit)495 void init_invalid_regions(int scrnHit)
496 {
497 if (_dirtyRowSize != scrnHit)
498 {
499 destroy_invalid_regions();
500 dirtyRow = new IRRow[scrnHit];
501 }
502
503 numDirtyRegions = WHOLESCREENDIRTY;
504 memset(dirtyRow, 0, sizeof(IRRow) * scrnHit);
505
506 for (int e = 0; e < scrnHit; e++)
507 dirtyRow[e].numSpans = 0;
508 _dirtyRowSize = scrnHit;
509 }
510
destroy_invalid_regions()511 void destroy_invalid_regions()
512 {
513 delete [] dirtyRow;
514 dirtyRow = 0;
515 _dirtyRowSize = 0;
516 numDirtyRegions = 0;
517 }
518
update_invalid_region(Bitmap * ds,int x,int y,Bitmap * src)519 void update_invalid_region(Bitmap *ds, int x, int y, Bitmap *src) {
520 int i;
521
522 // convert the offsets for the destination into
523 // offsets into the source
524 x = -x;
525 y = -y;
526
527 if (numDirtyRegions == WHOLESCREENDIRTY) {
528 ds->Blit(src, x, y, 0, 0, ds->GetWidth(), ds->GetHeight());
529 }
530 else {
531 int k, tx1, tx2, srcdepth = src->GetColorDepth();
532 if ((srcdepth == ds->GetColorDepth()) && (ds->IsMemoryBitmap())) {
533 int bypp = src->GetBPP();
534 // do the fast copy
535 for (i = 0; i < play.viewport.GetHeight(); i++) {
536 const uint8_t *src_scanline = src->GetScanLine(i + y);
537 uint8_t *dst_scanline = ds->GetScanLineForWriting(i);
538 const IRRow &dirty_row = dirtyRow[i];
539 for (k = 0; k < dirty_row.numSpans; k++) {
540 tx1 = dirty_row.span[k].x1;
541 tx2 = dirty_row.span[k].x2;
542 memcpy(&dst_scanline[tx1 * bypp], &src_scanline[(tx1 + x) * bypp], ((tx2 - tx1) + 1) * bypp);
543 }
544 }
545 }
546 else {
547 // do the fast copy
548 int rowsInOne;
549 for (i = 0; i < play.viewport.GetHeight(); i++) {
550 rowsInOne = 1;
551
552 // if there are rows with identical masks, do them all in one go
553 while ((i+rowsInOne < play.viewport.GetHeight()) && (memcmp(&dirtyRow[i], &dirtyRow[i+rowsInOne], sizeof(IRRow)) == 0))
554 rowsInOne++;
555
556 const IRRow &dirty_row = dirtyRow[i];
557 for (k = 0; k < dirty_row.numSpans; k++) {
558 tx1 = dirty_row.span[k].x1;
559 tx2 = dirty_row.span[k].x2;
560 ds->Blit(src, tx1 + x, i + y, tx1, i, (tx2 - tx1) + 1, rowsInOne);
561 }
562
563 i += (rowsInOne - 1);
564 }
565 }
566 /* else {
567 // update the dirty regions
568 for (i = 0; i < numDirtyRegions; i++) {
569 ->Blit(src, dest, x + dirtyRegions[i].x1, y + dirtyRegions[i].y1,
570 dirtyRegions[i].x1, dirtyRegions[i].y1,
571 (dirtyRegions[i].x2 - dirtyRegions[i].x1) + 1,
572 (dirtyRegions[i].y2 - dirtyRegions[i].y1) + 1);
573 }
574 }*/
575 }
576 }
577
578
update_invalid_region_and_reset(Bitmap * ds,int x,int y,Bitmap * src)579 void update_invalid_region_and_reset(Bitmap *ds, int x, int y, Bitmap *src) {
580
581 int i;
582
583 update_invalid_region(ds, x, y, src);
584
585 // screen has been updated, no longer dirty
586 numDirtyRegions = 0;
587 numDirtyBytes = 0;
588
589 for (i = 0; i < _dirtyRowSize; i++)
590 dirtyRow[i].numSpans = 0;
591
592 }
593
combine_new_rect(InvalidRect * r1,InvalidRect * r2)594 int combine_new_rect(InvalidRect *r1, InvalidRect *r2) {
595
596 // check if new rect is within old rect X-wise
597 if ((r2->x1 >= r1->x1) && (r2->x2 <= r1->x2)) {
598 if ((r2->y1 >= r1->y1) && (r2->y2 <= r1->y2)) {
599 // Y is also within the old one - scrap the new rect
600 return 1;
601 }
602 }
603
604 return 0;
605 }
606
invalidate_rect(int x1,int y1,int x2,int y2)607 void invalidate_rect(int x1, int y1, int x2, int y2) {
608 if (numDirtyRegions >= MAXDIRTYREGIONS) {
609 // too many invalid rectangles, just mark the whole thing dirty
610 numDirtyRegions = WHOLESCREENDIRTY;
611 return;
612 }
613
614 int a;
615
616 if (x1 >= play.viewport.GetWidth()) x1 = play.viewport.GetWidth()-1;
617 if (y1 >= play.viewport.GetHeight()) y1 = play.viewport.GetHeight()-1;
618 if (x2 >= play.viewport.GetWidth()) x2 = play.viewport.GetWidth()-1;
619 if (y2 >= play.viewport.GetHeight()) y2 = play.viewport.GetHeight()-1;
620 if (x1 < 0) x1 = 0;
621 if (y1 < 0) y1 = 0;
622 if (x2 < 0) x2 = 0;
623 if (y2 < 0) y2 = 0;
624 /*
625 dirtyRegions[numDirtyRegions].x1 = x1;
626 dirtyRegions[numDirtyRegions].y1 = y1;
627 dirtyRegions[numDirtyRegions].x2 = x2;
628 dirtyRegions[numDirtyRegions].y2 = y2;
629
630 for (a = 0; a < numDirtyRegions; a++) {
631 // see if we can merge it into any other regions
632 if (combine_new_rect(&dirtyRegions[a], &dirtyRegions[numDirtyRegions]))
633 return;
634 }
635
636 numDirtyBytes += (x2 - x1) * (y2 - y1);
637
638 if (numDirtyBytes > (play.viewport.GetWidth() * play.viewport.GetHeight()) / 2)
639 numDirtyRegions = WHOLESCREENDIRTY;
640 else {*/
641 numDirtyRegions++;
642
643 // ** Span code
644 int s, foundOne;
645 // add this rect to the list for this row
646 for (a = y1; a <= y2; a++) {
647 foundOne = 0;
648 for (s = 0; s < dirtyRow[a].numSpans; s++) {
649 if (dirtyRow[a].span[s].mergeSpan(x1, x2)) {
650 foundOne = 1;
651 break;
652 }
653 }
654 if (foundOne) {
655 // we were merged into a span, so we're ok
656 int t;
657 // check whether now two of the spans overlap each other
658 // in which case merge them
659 for (s = 0; s < dirtyRow[a].numSpans; s++) {
660 for (t = s + 1; t < dirtyRow[a].numSpans; t++) {
661 if (dirtyRow[a].span[s].mergeSpan(dirtyRow[a].span[t].x1, dirtyRow[a].span[t].x2)) {
662 dirtyRow[a].numSpans--;
663 for (int u = t; u < dirtyRow[a].numSpans; u++)
664 dirtyRow[a].span[u] = dirtyRow[a].span[u + 1];
665 break;
666 }
667 }
668 }
669 }
670 else if (dirtyRow[a].numSpans < MAX_SPANS_PER_ROW) {
671 dirtyRow[a].span[dirtyRow[a].numSpans].x1 = x1;
672 dirtyRow[a].span[dirtyRow[a].numSpans].x2 = x2;
673 dirtyRow[a].numSpans++;
674 }
675 else {
676 // didn't fit in an existing span, and there are none spare
677 int nearestDist = 99999, nearestWas = -1, extendLeft;
678 int tleft, tright;
679 // find the nearest span, and enlarge that to include this rect
680 for (s = 0; s < dirtyRow[a].numSpans; s++) {
681 tleft = dirtyRow[a].span[s].x1 - x2;
682 if ((tleft > 0) && (tleft < nearestDist)) {
683 nearestDist = tleft;
684 nearestWas = s;
685 extendLeft = 1;
686 }
687 tright = x1 - dirtyRow[a].span[s].x2;
688 if ((tright > 0) && (tright < nearestDist)) {
689 nearestDist = tright;
690 nearestWas = s;
691 extendLeft = 0;
692 }
693 }
694 if (extendLeft)
695 dirtyRow[a].span[nearestWas].x1 = x1;
696 else
697 dirtyRow[a].span[nearestWas].x2 = x2;
698 }
699 }
700 // ** End span code
701 //}
702 }
703
704
705
invalidate_sprite(int x1,int y1,IDriverDependantBitmap * pic)706 void invalidate_sprite(int x1, int y1, IDriverDependantBitmap *pic) {
707 invalidate_rect(x1, y1, x1 + pic->GetWidth(), y1 + pic->GetHeight());
708 }
709
draw_and_invalidate_text(Bitmap * ds,int x1,int y1,int font,color_t text_color,const char * text)710 void draw_and_invalidate_text(Bitmap *ds, int x1, int y1, int font, color_t text_color, const char *text) {
711 wouttext_outline(ds, x1, y1, font, text_color, (char*)text);
712 invalidate_rect(x1, y1, x1 + wgettextwidth_compensate(text, font), y1 + getfontheight_outlined(font) + get_fixed_pixel_size(1));
713 }
714
invalidate_screen()715 void invalidate_screen() {
716 // mark the whole screen dirty
717 numDirtyRegions = WHOLESCREENDIRTY;
718 }
719
720 // ** dirty rectangle system end **
721
mark_current_background_dirty()722 void mark_current_background_dirty()
723 {
724 current_background_is_dirty = true;
725 }
726
render_black_borders(int atx,int aty)727 void render_black_borders(int atx, int aty)
728 {
729 if (!gfxDriver->UsesMemoryBackBuffer())
730 {
731 if (aty > 0)
732 {
733 // letterbox borders
734 blankImage->SetStretch(game.size.Width, aty, false);
735 gfxDriver->DrawSprite(-atx, -aty, blankImage);
736 gfxDriver->DrawSprite(0, play.viewport.GetHeight(), blankImage);
737 }
738 if (atx > 0)
739 {
740 // sidebar borders for widescreen
741 blankSidebarImage->SetStretch(atx, play.viewport.GetHeight(), false);
742 gfxDriver->DrawSprite(-atx, 0, blankSidebarImage);
743 gfxDriver->DrawSprite(play.viewport.GetWidth(), 0, blankSidebarImage);
744 }
745 }
746 }
747
748
render_to_screen(Bitmap * toRender,int atx,int aty)749 void render_to_screen(Bitmap *toRender, int atx, int aty) {
750
751 atx += play.viewport.Left;
752 aty += play.viewport.Top;
753 gfxDriver->SetRenderOffset(atx, aty);
754
755 // For software renderer, need to blacken upper part of the game frame when shaking screen moves image down
756 if (aty > 0 && wasShakingScreen && gfxDriver->UsesMemoryBackBuffer())
757 gfxDriver->ClearRectangle(play.viewport.Left, play.viewport.Top, play.viewport.GetWidth() - 1, aty, NULL);
758 render_black_borders(atx, aty);
759
760 gfxDriver->DrawSprite(AGSE_FINALSCREENDRAW, 0, NULL);
761
762 if (play.screen_is_faded_out)
763 {
764 if (gfxDriver->UsesMemoryBackBuffer())
765 gfxDriver->RenderToBackBuffer();
766 gfxDriver->ClearDrawList();
767 return;
768 }
769
770 // only vsync in full screen mode, it makes things worse
771 // in a window
772 gfxDriver->EnableVsyncBeforeRender((scsystem.vsync > 0) && (!scsystem.windowed));
773
774 bool succeeded = false;
775 while (!succeeded)
776 {
777 try
778 {
779 gfxDriver->Render((GlobalFlipType)play.screen_flipped);
780
781 #if defined(ANDROID_VERSION)
782 if (game.color_depth == 1)
783 android_render();
784 #elif defined(IOS_VERSION)
785 if (game.color_depth == 1)
786 ios_render();
787 #endif
788
789 succeeded = true;
790 }
791 catch (Ali3DFullscreenLostException)
792 {
793 platform->Delay(500);
794 }
795 }
796 }
797
798
clear_letterbox_borders()799 void clear_letterbox_borders() {
800
801 if (multiply_up_coordinate(thisroom.height) < game.size.Height) {
802 // blank out any traces in borders left by a full-screen room
803 gfxDriver->ClearRectangle(0, 0, _old_screen->GetWidth() - 1, play.viewport.Top - 1, NULL);
804 gfxDriver->ClearRectangle(0, play.viewport.Bottom + 1, _old_screen->GetWidth() - 1, game.size.Height - 1, NULL);
805 }
806
807 }
808
809 // writes the virtual screen to the screen, converting colours if
810 // necessary
write_screen()811 void write_screen() {
812
813 if (play.fast_forward)
814 return;
815
816 int at_yp = 0;
817
818 if (play.shakesc_length > 0) {
819 wasShakingScreen = 1;
820 if ( (loopcounter % play.shakesc_delay) < (play.shakesc_delay / 2) )
821 at_yp = multiply_up_coordinate(play.shakesc_amount);
822 invalidate_screen();
823 }
824 else if (wasShakingScreen) {
825 wasShakingScreen = 0;
826
827 if (!gfxDriver->RequiresFullRedrawEachFrame())
828 {
829 clear_letterbox_borders();
830 }
831 }
832
833 if (play.screen_tint < 1)
834 gfxDriver->SetScreenTint(0, 0, 0);
835 else
836 gfxDriver->SetScreenTint(play.screen_tint & 0xff, (play.screen_tint >> 8) & 0xff, (play.screen_tint >> 16) & 0xff);
837
838 render_to_screen(virtual_screen, 0, at_yp);
839 }
840
841
842
draw_screen_callback()843 void draw_screen_callback()
844 {
845 construct_virtual_screen(false);
846
847 render_black_borders(play.viewport.Left, play.viewport.Top);
848 }
849
850
851
putpixel_compensate(Bitmap * ds,int xx,int yy,int col)852 void putpixel_compensate (Bitmap *ds, int xx,int yy, int col) {
853 if ((ds->GetColorDepth() == 32) && (col != 0)) {
854 // ensure the alpha channel is preserved if it has one
855 int alphaval = geta32(ds->GetPixel(xx, yy));
856 col = makeacol32(getr32(col), getg32(col), getb32(col), alphaval);
857 }
858 ds->FillRect(Rect(xx, yy, xx + get_fixed_pixel_size(1) - 1, yy + get_fixed_pixel_size(1) - 1), col);
859 }
860
861
862
863
draw_sprite_support_alpha(Bitmap * ds,bool ds_has_alpha,int xpos,int ypos,Bitmap * image,bool src_has_alpha,BlendMode blend_mode,int alpha)864 void draw_sprite_support_alpha(Bitmap *ds, bool ds_has_alpha, int xpos, int ypos, Bitmap *image, bool src_has_alpha,
865 BlendMode blend_mode, int alpha)
866 {
867 if (alpha <= 0)
868 return;
869
870 if (game.options[OPT_SPRITEALPHA] == kSpriteAlphaRender_Proper)
871 {
872 GfxUtil::DrawSpriteBlend(ds, Point(xpos, ypos), image, blend_mode, ds_has_alpha, src_has_alpha, alpha);
873 }
874 // Backwards-compatible drawing
875 else if (src_has_alpha && alpha == 0xFF)
876 {
877 set_alpha_blender();
878 ds->TransBlendBlt(image, xpos, ypos);
879 }
880 else
881 {
882 GfxUtil::DrawSpriteWithTransparency(ds, image, xpos, ypos, alpha);
883 }
884 }
885
draw_sprite_slot_support_alpha(Bitmap * ds,bool ds_has_alpha,int xpos,int ypos,int src_slot,BlendMode blend_mode,int alpha)886 void draw_sprite_slot_support_alpha(Bitmap *ds, bool ds_has_alpha, int xpos, int ypos, int src_slot,
887 BlendMode blend_mode, int alpha)
888 {
889 draw_sprite_support_alpha(ds, ds_has_alpha, xpos, ypos, spriteset[src_slot], (game.spriteflags[src_slot] & SPF_ALPHACHANNEL) != 0,
890 blend_mode, alpha);
891 }
892
893
recycle_ddb_bitmap(IDriverDependantBitmap * bimp,Bitmap * source,bool hasAlpha,bool opaque)894 IDriverDependantBitmap* recycle_ddb_bitmap(IDriverDependantBitmap *bimp, Bitmap *source, bool hasAlpha, bool opaque) {
895 if (bimp != NULL) {
896 // same colour depth, width and height -> reuse
897 if (((bimp->GetColorDepth() + 1) / 8 == source->GetBPP()) &&
898 (bimp->GetWidth() == source->GetWidth()) && (bimp->GetHeight() == source->GetHeight()))
899 {
900 gfxDriver->UpdateDDBFromBitmap(bimp, source, hasAlpha);
901 return bimp;
902 }
903
904 gfxDriver->DestroyDDB(bimp);
905 }
906 bimp = gfxDriver->CreateDDBFromBitmap(source, hasAlpha, opaque);
907 return bimp;
908 }
909
invalidate_cached_walkbehinds()910 void invalidate_cached_walkbehinds()
911 {
912 memset(&actspswbcache[0], 0, sizeof(CachedActSpsData) * actSpsCount);
913 }
914
915 // sort_out_walk_behinds: modifies the supplied sprite by overwriting parts
916 // of it with transparent pixels where there are walk-behind areas
917 // Returns whether any pixels were updated
sort_out_walk_behinds(Bitmap * sprit,int xx,int yy,int basel,Bitmap * copyPixelsFrom=NULL,Bitmap * checkPixelsFrom=NULL,int zoom=100)918 int sort_out_walk_behinds(Bitmap *sprit,int xx,int yy,int basel, Bitmap *copyPixelsFrom = NULL, Bitmap *checkPixelsFrom = NULL, int zoom=100) {
919 if (noWalkBehindsAtAll)
920 return 0;
921
922 if ((!thisroom.object->IsMemoryBitmap()) ||
923 (!sprit->IsMemoryBitmap()))
924 quit("!sort_out_walk_behinds: wb bitmap not linear");
925
926 int rr,tmm, toheight;//,tcol;
927 // precalculate this to try and shave some time off
928 int maskcol = sprit->GetMaskColor();
929 int spcoldep = sprit->GetColorDepth();
930 int screenhit = thisroom.object->GetHeight();
931 short *shptr, *shptr2;
932 int *loptr, *loptr2;
933 int pixelsChanged = 0;
934 int ee = 0;
935 if (xx < 0)
936 ee = 0 - xx;
937
938 if ((checkPixelsFrom != NULL) && (checkPixelsFrom->GetColorDepth() != spcoldep))
939 quit("sprite colour depth does not match background colour depth");
940
941 for ( ; ee < sprit->GetWidth(); ee++) {
942 if (ee + xx >= thisroom.object->GetWidth())
943 break;
944
945 if ((!walkBehindExists[ee+xx]) ||
946 (walkBehindEndY[ee+xx] <= yy) ||
947 (walkBehindStartY[ee+xx] > yy+sprit->GetHeight()))
948 continue;
949
950 toheight = sprit->GetHeight();
951
952 if (walkBehindStartY[ee+xx] < yy)
953 rr = 0;
954 else
955 rr = (walkBehindStartY[ee+xx] - yy);
956
957 // Since we will use _getpixel, ensure we only check within the screen
958 if (rr + yy < 0)
959 rr = 0 - yy;
960 if (toheight + yy > screenhit)
961 toheight = screenhit - yy;
962 if (toheight + yy > walkBehindEndY[ee+xx])
963 toheight = walkBehindEndY[ee+xx] - yy;
964 if (rr < 0)
965 rr = 0;
966
967 for ( ; rr < toheight;rr++) {
968
969 // we're ok with _getpixel because we've checked the screen edges
970 //tmm = _getpixel(thisroom.object,ee+xx,rr+yy);
971 // actually, _getpixel is well inefficient, do it ourselves
972 // since we know it's 8-bit bitmap
973 tmm = thisroom.object->GetScanLine(rr+yy)[ee+xx];
974 if (tmm<1) continue;
975 if (croom->walkbehind_base[tmm] <= basel) continue;
976
977 if (copyPixelsFrom != NULL)
978 {
979 if (spcoldep <= 8)
980 {
981 if (checkPixelsFrom->GetScanLine((rr * 100) / zoom)[(ee * 100) / zoom] != maskcol) {
982 sprit->GetScanLineForWriting(rr)[ee] = copyPixelsFrom->GetScanLine(rr + yy)[ee + xx];
983 pixelsChanged = 1;
984 }
985 }
986 else if (spcoldep <= 16) {
987 shptr = (short*)&sprit->GetScanLine(rr)[0];
988 shptr2 = (short*)&checkPixelsFrom->GetScanLine((rr * 100) / zoom)[0];
989 if (shptr2[(ee * 100) / zoom] != maskcol) {
990 shptr[ee] = ((short*)(©PixelsFrom->GetScanLine(rr + yy)[0]))[ee + xx];
991 pixelsChanged = 1;
992 }
993 }
994 else if (spcoldep == 24) {
995 char *chptr = (char*)&sprit->GetScanLine(rr)[0];
996 char *chptr2 = (char*)&checkPixelsFrom->GetScanLine((rr * 100) / zoom)[0];
997 if (memcmp(&chptr2[((ee * 100) / zoom) * 3], &maskcol, 3) != 0) {
998 memcpy(&chptr[ee * 3], ©PixelsFrom->GetScanLine(rr + yy)[(ee + xx) * 3], 3);
999 pixelsChanged = 1;
1000 }
1001 }
1002 else if (spcoldep <= 32) {
1003 loptr = (int*)&sprit->GetScanLine(rr)[0];
1004 loptr2 = (int*)&checkPixelsFrom->GetScanLine((rr * 100) / zoom)[0];
1005 if (loptr2[(ee * 100) / zoom] != maskcol) {
1006 loptr[ee] = ((int*)(©PixelsFrom->GetScanLine(rr + yy)[0]))[ee + xx];
1007 pixelsChanged = 1;
1008 }
1009 }
1010 }
1011 else
1012 {
1013 pixelsChanged = 1;
1014 if (spcoldep <= 8)
1015 sprit->GetScanLineForWriting(rr)[ee] = maskcol;
1016 else if (spcoldep <= 16) {
1017 shptr = (short*)&sprit->GetScanLine(rr)[0];
1018 shptr[ee] = maskcol;
1019 }
1020 else if (spcoldep == 24) {
1021 char *chptr = (char*)&sprit->GetScanLine(rr)[0];
1022 memcpy(&chptr[ee * 3], &maskcol, 3);
1023 }
1024 else if (spcoldep <= 32) {
1025 loptr = (int*)&sprit->GetScanLine(rr)[0];
1026 loptr[ee] = maskcol;
1027 }
1028 else
1029 quit("!Sprite colour depth >32 ??");
1030 }
1031 }
1032 }
1033 return pixelsChanged;
1034 }
1035
sort_out_char_sprite_walk_behind(int actspsIndex,int xx,int yy,int basel,int zoom,int width,int height)1036 void sort_out_char_sprite_walk_behind(int actspsIndex, int xx, int yy, int basel, int zoom, int width, int height)
1037 {
1038 if (noWalkBehindsAtAll)
1039 return;
1040
1041 if ((!actspswbcache[actspsIndex].valid) ||
1042 (actspswbcache[actspsIndex].xWas != xx) ||
1043 (actspswbcache[actspsIndex].yWas != yy) ||
1044 (actspswbcache[actspsIndex].baselineWas != basel))
1045 {
1046 actspswb[actspsIndex] = recycle_bitmap(actspswb[actspsIndex], thisroom.ebscene[play.bg_frame]->GetColorDepth(), width, height, true);
1047 Bitmap *wbSprite = actspswb[actspsIndex];
1048
1049 actspswbcache[actspsIndex].isWalkBehindHere = sort_out_walk_behinds(wbSprite, xx, yy, basel, thisroom.ebscene[play.bg_frame], actsps[actspsIndex], zoom);
1050 actspswbcache[actspsIndex].xWas = xx;
1051 actspswbcache[actspsIndex].yWas = yy;
1052 actspswbcache[actspsIndex].baselineWas = basel;
1053 actspswbcache[actspsIndex].valid = 1;
1054
1055 if (actspswbcache[actspsIndex].isWalkBehindHere)
1056 {
1057 actspswbbmp[actspsIndex] = recycle_ddb_bitmap(actspswbbmp[actspsIndex], actspswb[actspsIndex], false);
1058 }
1059 }
1060
1061 if (actspswbcache[actspsIndex].isWalkBehindHere)
1062 {
1063 add_to_sprite_list(actspswbbmp[actspsIndex], xx - offsetx, yy - offsety, basel, 0, -1, true);
1064 }
1065 }
1066
clear_draw_list()1067 void clear_draw_list() {
1068 thingsToDrawList.clear();
1069 }
add_thing_to_draw(IDriverDependantBitmap * bmp,int x,int y,int trans,bool alphaChannel)1070 void add_thing_to_draw(IDriverDependantBitmap* bmp, int x, int y, int trans, bool alphaChannel) {
1071 SpriteListEntry sprite;
1072 sprite.pic = NULL;
1073 sprite.bmp = bmp;
1074 sprite.x = x;
1075 sprite.y = y;
1076 sprite.transparent = trans;
1077 sprite.hasAlphaChannel = alphaChannel;
1078 thingsToDrawList.push_back(sprite);
1079 }
1080
1081 // the sprite list is an intermediate list used to order
1082 // objects and characters by their baselines before everything
1083 // is added to the Thing To Draw List
clear_sprite_list()1084 void clear_sprite_list() {
1085 sprlist.clear();
1086 }
add_to_sprite_list(IDriverDependantBitmap * spp,int xx,int yy,int baseline,int trans,int sprNum,bool isWalkBehind)1087 void add_to_sprite_list(IDriverDependantBitmap* spp, int xx, int yy, int baseline, int trans, int sprNum, bool isWalkBehind) {
1088
1089 if (spp == NULL)
1090 quit("add_to_sprite_list: attempted to draw NULL sprite");
1091 // completely invisible, so don't draw it at all
1092 if (trans == 255)
1093 return;
1094
1095 SpriteListEntry sprite;
1096 if ((sprNum >= 0) && ((game.spriteflags[sprNum] & SPF_ALPHACHANNEL) != 0))
1097 sprite.hasAlphaChannel = true;
1098 else
1099 sprite.hasAlphaChannel = false;
1100
1101 sprite.bmp = spp;
1102 sprite.baseline = baseline;
1103 sprite.x=xx;
1104 sprite.y=yy;
1105 sprite.transparent=trans;
1106
1107 if (walkBehindMethod == DrawAsSeparateSprite)
1108 sprite.takesPriorityIfEqual = !isWalkBehind;
1109 else
1110 sprite.takesPriorityIfEqual = isWalkBehind;
1111
1112 sprlist.push_back(sprite);
1113 }
1114
repair_alpha_channel(Bitmap * dest,Bitmap * bgpic)1115 void repair_alpha_channel(Bitmap *dest, Bitmap *bgpic)
1116 {
1117 // Repair the alpha channel, because sprites may have been drawn
1118 // over it by the buttons, etc
1119 int theWid = (dest->GetWidth() < bgpic->GetWidth()) ? dest->GetWidth() : bgpic->GetWidth();
1120 int theHit = (dest->GetHeight() < bgpic->GetHeight()) ? dest->GetHeight() : bgpic->GetHeight();
1121 for (int y = 0; y < theHit; y++)
1122 {
1123 unsigned int *destination = ((unsigned int*)dest->GetScanLineForWriting(y));
1124 unsigned int *source = ((unsigned int*)bgpic->GetScanLineForWriting(y));
1125 for (int x = 0; x < theWid; x++)
1126 {
1127 destination[x] |= (source[x] & 0xff000000);
1128 }
1129 }
1130 }
1131
1132
1133 // used by GUI renderer to draw images
draw_gui_sprite(Bitmap * ds,int pic,int x,int y,bool use_alpha,BlendMode blend_mode)1134 void draw_gui_sprite(Bitmap *ds, int pic, int x, int y, bool use_alpha, BlendMode blend_mode)
1135 {
1136 Bitmap *sprite = spriteset[pic];
1137 const bool ds_has_alpha = ds->GetColorDepth() == 32;
1138 const bool src_has_alpha = (game.spriteflags[pic] & SPF_ALPHACHANNEL) != 0;
1139
1140 if (use_alpha && game.options[OPT_NEWGUIALPHA] == kGuiAlphaRender_Proper)
1141 {
1142 GfxUtil::DrawSpriteBlend(ds, Point(x, y), sprite, blend_mode, ds_has_alpha, src_has_alpha);
1143 }
1144 // Backwards-compatible drawing
1145 else if (use_alpha && ds_has_alpha && game.options[OPT_NEWGUIALPHA] == kGuiAlphaRender_AdditiveAlpha)
1146 {
1147 if (src_has_alpha)
1148 set_additive_alpha_blender();
1149 else
1150 set_opaque_alpha_blender();
1151 ds->TransBlendBlt(sprite, x, y);
1152 }
1153 else
1154 {
1155 GfxUtil::DrawSpriteWithTransparency(ds, sprite, x, y);
1156 }
1157 }
1158
draw_gui_sprite_v330(Bitmap * ds,int pic,int x,int y,bool use_alpha,BlendMode blend_mode)1159 void draw_gui_sprite_v330(Bitmap *ds, int pic, int x, int y, bool use_alpha, BlendMode blend_mode)
1160 {
1161 draw_gui_sprite(ds, pic, x, y, use_alpha && (loaded_game_file_version >= kGameVersion_330), blend_mode);
1162 }
1163
1164 // function to sort the sprites into baseline order
spritelistentry_less(const SpriteListEntry & e1,const SpriteListEntry & e2)1165 bool spritelistentry_less(const SpriteListEntry &e1, const SpriteListEntry &e2)
1166 {
1167 if (e1.baseline == e2.baseline)
1168 {
1169 if (e1.takesPriorityIfEqual)
1170 return false;
1171 if (e2.takesPriorityIfEqual)
1172 return true;
1173 }
1174 return e1.baseline < e2.baseline;
1175 }
1176
1177
1178
1179
draw_sprite_list()1180 void draw_sprite_list() {
1181
1182 if (walkBehindMethod == DrawAsSeparateSprite)
1183 {
1184 for (int ee = 1; ee < MAX_OBJ; ee++)
1185 {
1186 if (walkBehindBitmap[ee] != NULL)
1187 {
1188 add_to_sprite_list(walkBehindBitmap[ee], walkBehindLeft[ee] - offsetx, walkBehindTop[ee] - offsety,
1189 croom->walkbehind_base[ee], 0, -1, true);
1190 }
1191 }
1192 }
1193
1194 std::sort(sprlist.begin(), sprlist.end(), spritelistentry_less);
1195
1196 clear_draw_list();
1197
1198 add_thing_to_draw(NULL, AGSE_PRESCREENDRAW, 0, TRANS_RUN_PLUGIN, false);
1199
1200 // copy the sorted sprites into the Things To Draw list
1201 thingsToDrawList.insert(thingsToDrawList.end(), sprlist.begin(), sprlist.end());
1202 }
1203
1204 // Avoid freeing and reallocating the memory if possible
recycle_bitmap(Bitmap * bimp,int coldep,int wid,int hit,bool make_transparent)1205 Bitmap *recycle_bitmap(Bitmap *bimp, int coldep, int wid, int hit, bool make_transparent) {
1206 if (bimp != NULL) {
1207 // same colour depth, width and height -> reuse
1208 if ((bimp->GetColorDepth() == coldep) && (bimp->GetWidth() == wid)
1209 && (bimp->GetHeight() == hit))
1210 {
1211 if (make_transparent)
1212 {
1213 bimp->ClearTransparent();
1214 }
1215 return bimp;
1216 }
1217
1218 delete bimp;
1219 }
1220 bimp = make_transparent ? BitmapHelper::CreateTransparentBitmap(wid, hit,coldep) :
1221 BitmapHelper::CreateBitmap(wid, hit,coldep);
1222 return bimp;
1223 }
1224
1225
1226 // Get the local tint at the specified X & Y co-ordinates, based on
1227 // room regions and SetAmbientTint
1228 // tint_amnt will be set to 0 if there is no tint enabled
1229 // if this is the case, then light_lev holds the light level (0=none)
get_local_tint(int xpp,int ypp,int nolight,int * tint_amnt,int * tint_r,int * tint_g,int * tint_b,int * tint_lit,int * light_lev)1230 void get_local_tint(int xpp, int ypp, int nolight,
1231 int *tint_amnt, int *tint_r, int *tint_g,
1232 int *tint_b, int *tint_lit,
1233 int *light_lev) {
1234
1235 int tint_level = 0, light_level = 0;
1236 int tint_amount = 0;
1237 int tint_red = 0;
1238 int tint_green = 0;
1239 int tint_blue = 0;
1240 int tint_light = 255;
1241
1242 if (nolight == 0) {
1243
1244 int onRegion = 0;
1245
1246 if ((play.ground_level_areas_disabled & GLED_EFFECTS) == 0) {
1247 // check if the player is on a region, to find its
1248 // light/tint level
1249 onRegion = GetRegionAt (xpp, ypp);
1250 if (onRegion == 0) {
1251 // when walking, he might just be off a walkable area
1252 onRegion = GetRegionAt (xpp - 3, ypp);
1253 if (onRegion == 0)
1254 onRegion = GetRegionAt (xpp + 3, ypp);
1255 if (onRegion == 0)
1256 onRegion = GetRegionAt (xpp, ypp - 3);
1257 if (onRegion == 0)
1258 onRegion = GetRegionAt (xpp, ypp + 3);
1259 }
1260 }
1261
1262 if ((onRegion > 0) && (onRegion < MAX_REGIONS)) {
1263 light_level = thisroom.regionLightLevel[onRegion];
1264 tint_level = thisroom.regionTintLevel[onRegion];
1265 }
1266 else if (onRegion <= 0) {
1267 light_level = thisroom.regionLightLevel[0];
1268 tint_level = thisroom.regionTintLevel[0];
1269 }
1270
1271 int tint_sat = (tint_level >> 24) & 0xFF;
1272 if ((game.color_depth == 1) || ((tint_level & 0x00ffffff) == 0) ||
1273 (tint_sat == 0))
1274 tint_level = 0;
1275
1276 if (tint_level) {
1277 tint_red = (unsigned char)(tint_level & 0x000ff);
1278 tint_green = (unsigned char)((tint_level >> 8) & 0x000ff);
1279 tint_blue = (unsigned char)((tint_level >> 16) & 0x000ff);
1280 tint_amount = tint_sat;
1281 tint_light = light_level;
1282 }
1283
1284 if (play.rtint_enabled)
1285 {
1286 if (play.rtint_level > 0)
1287 {
1288 // override with room tint
1289 tint_red = play.rtint_red;
1290 tint_green = play.rtint_green;
1291 tint_blue = play.rtint_blue;
1292 tint_amount = play.rtint_level;
1293 tint_light = play.rtint_light;
1294 }
1295 else
1296 {
1297 // override with room light level
1298 tint_amount = 0;
1299 light_level = play.rtint_light;
1300 }
1301 }
1302 }
1303
1304 // copy to output parameters
1305 *tint_amnt = tint_amount;
1306 *tint_r = tint_red;
1307 *tint_g = tint_green;
1308 *tint_b = tint_blue;
1309 *tint_lit = tint_light;
1310 if (light_lev)
1311 *light_lev = light_level;
1312 }
1313
1314
1315
1316
1317 // Applies the specified RGB Tint or Light Level to the actsps
1318 // sprite indexed with actspsindex
apply_tint_or_light(int actspsindex,int light_level,int tint_amount,int tint_red,int tint_green,int tint_blue,int tint_light,int coldept,Bitmap * blitFrom)1319 void apply_tint_or_light(int actspsindex, int light_level,
1320 int tint_amount, int tint_red, int tint_green,
1321 int tint_blue, int tint_light, int coldept,
1322 Bitmap *blitFrom) {
1323
1324 // In a 256-colour game, we cannot do tinting or lightening
1325 // (but we can do darkening, if light_level < 0)
1326 if (game.color_depth == 1) {
1327 if ((light_level > 0) || (tint_amount != 0))
1328 return;
1329 }
1330
1331 // we can only do tint/light if the colour depths match
1332 if (game.GetColorDepth() == actsps[actspsindex]->GetColorDepth()) {
1333 Bitmap *oldwas;
1334 // if the caller supplied a source bitmap, ->Blit from it
1335 // (used as a speed optimisation where possible)
1336 if (blitFrom)
1337 oldwas = blitFrom;
1338 // otherwise, make a new target bmp
1339 else {
1340 oldwas = actsps[actspsindex];
1341 actsps[actspsindex] = BitmapHelper::CreateBitmap(oldwas->GetWidth(), oldwas->GetHeight(), coldept);
1342 }
1343 Bitmap *active_spr = actsps[actspsindex];
1344
1345 if (tint_amount) {
1346 // It is an RGB tint
1347 tint_image (active_spr, oldwas, tint_red, tint_green, tint_blue, tint_amount, tint_light);
1348 }
1349 else {
1350 // the RGB values passed to set_trans_blender decide whether it will darken
1351 // or lighten sprites ( <128=darken, >128=lighten). The parameter passed
1352 // to LitBlendBlt defines how much it will be darkened/lightened by.
1353
1354 int lit_amnt;
1355 active_spr->FillTransparent();
1356 // It's a light level, not a tint
1357 if (game.color_depth == 1) {
1358 // 256-col
1359 lit_amnt = (250 - ((-light_level) * 5)/2);
1360 }
1361 else {
1362 // hi-color
1363 if (light_level < 0)
1364 set_my_trans_blender(8,8,8,0);
1365 else
1366 set_my_trans_blender(248,248,248,0);
1367 lit_amnt = abs(light_level) * 2;
1368 }
1369
1370 active_spr->LitBlendBlt(oldwas, 0, 0, lit_amnt);
1371 }
1372
1373 if (oldwas != blitFrom)
1374 delete oldwas;
1375
1376 }
1377 else if (blitFrom) {
1378 // sprite colour depth != game colour depth, so don't try and tint
1379 // but we do need to do something, so copy the source
1380 Bitmap *active_spr = actsps[actspsindex];
1381 active_spr->Blit(blitFrom, 0, 0, 0, 0, active_spr->GetWidth(), active_spr->GetHeight());
1382 }
1383
1384 }
1385
1386 // Draws the specified 'sppic' sprite onto actsps[useindx] at the
1387 // specified width and height, and flips the sprite if necessary.
1388 // Returns 1 if something was drawn to actsps; returns 0 if no
1389 // scaling or stretching was required, in which case nothing was done
scale_and_flip_sprite(int useindx,int coldept,int zoom_level,int sppic,int newwidth,int newheight,int isMirrored)1390 int scale_and_flip_sprite(int useindx, int coldept, int zoom_level,
1391 int sppic, int newwidth, int newheight,
1392 int isMirrored) {
1393
1394 int actsps_used = 1;
1395
1396 // create and blank out the new sprite
1397 actsps[useindx] = recycle_bitmap(actsps[useindx], coldept, newwidth, newheight, true);
1398 Bitmap *active_spr = actsps[useindx];
1399
1400 if (zoom_level != 100) {
1401 // Scaled character
1402
1403 our_eip = 334;
1404
1405 // Ensure that anti-aliasing routines have a palette to
1406 // use for mapping while faded out
1407 if (in_new_room)
1408 select_palette (palette);
1409
1410
1411 if (isMirrored) {
1412 Bitmap *tempspr = BitmapHelper::CreateBitmap(newwidth, newheight,coldept);
1413 tempspr->Fill (actsps[useindx]->GetMaskColor());
1414 if ((IS_ANTIALIAS_SPRITES) && ((game.spriteflags[sppic] & SPF_ALPHACHANNEL) == 0))
1415 tempspr->AAStretchBlt (spriteset[sppic], RectWH(0, 0, newwidth, newheight), Common::kBitmap_Transparency);
1416 else
1417 tempspr->StretchBlt (spriteset[sppic], RectWH(0, 0, newwidth, newheight), Common::kBitmap_Transparency);
1418 active_spr->FlipBlt(tempspr, 0, 0, Common::kBitmap_HFlip);
1419 delete tempspr;
1420 }
1421 else if ((IS_ANTIALIAS_SPRITES) && ((game.spriteflags[sppic] & SPF_ALPHACHANNEL) == 0))
1422 active_spr->AAStretchBlt(spriteset[sppic],RectWH(0,0,newwidth,newheight), Common::kBitmap_Transparency);
1423 else
1424 active_spr->StretchBlt(spriteset[sppic],RectWH(0,0,newwidth,newheight), Common::kBitmap_Transparency);
1425
1426 /* AASTR2 version of code (doesn't work properly, gives black borders)
1427 if (IS_ANTIALIAS_SPRITES) {
1428 int aa_mode = AA_MASKED;
1429 if (game.spriteflags[sppic] & SPF_ALPHACHANNEL)
1430 aa_mode |= AA_ALPHA | AA_RAW_ALPHA;
1431 if (isMirrored)
1432 aa_mode |= AA_HFLIP;
1433
1434 aa_set_mode(aa_mode);
1435 ->AAStretchBlt(actsps[useindx],spriteset[sppic],0,0,newwidth,newheight);
1436 }
1437 else if (isMirrored) {
1438 Bitmap *tempspr = BitmapHelper::CreateBitmap_ (coldept, newwidth, newheight);
1439 ->Clear (tempspr, ->GetMaskColor(actsps[useindx]));
1440 ->StretchBlt (tempspr, spriteset[sppic], 0, 0, newwidth, newheight);
1441 ->FlipBlt(Common::kBitmap_HFlip, (actsps[useindx], tempspr, 0, 0);
1442 wfreeblock (tempspr);
1443 }
1444 else
1445 ->StretchBlt(actsps[useindx],spriteset[sppic],0,0,newwidth,newheight);
1446 */
1447 if (in_new_room)
1448 unselect_palette();
1449
1450 }
1451 else {
1452 // Not a scaled character, draw at normal size
1453
1454 our_eip = 339;
1455
1456 if (isMirrored)
1457 active_spr->FlipBlt(spriteset[sppic], 0, 0, Common::kBitmap_HFlip);
1458 else
1459 actsps_used = 0;
1460 //->Blit (spriteset[sppic], actsps[useindx], 0, 0, 0, 0, actsps[useindx]->GetWidth(), actsps[useindx]->GetHeight());
1461 }
1462
1463 return actsps_used;
1464 }
1465
1466
1467
1468 // create the actsps[aa] image with the object drawn correctly
1469 // returns 1 if nothing at all has changed and actsps is still
1470 // intact from last time; 0 otherwise
construct_object_gfx(int aa,int * drawnWidth,int * drawnHeight,bool alwaysUseSoftware)1471 int construct_object_gfx(int aa, int *drawnWidth, int *drawnHeight, bool alwaysUseSoftware) {
1472 int useindx = aa;
1473 bool hardwareAccelerated = gfxDriver->HasAcceleratedStretchAndFlip();
1474
1475 if (alwaysUseSoftware)
1476 hardwareAccelerated = false;
1477
1478 if (spriteset[objs[aa].num] == NULL)
1479 quitprintf("There was an error drawing object %d. Its current sprite, %d, is invalid.", aa, objs[aa].num);
1480
1481 int coldept = spriteset[objs[aa].num]->GetColorDepth();
1482 int sprwidth = spritewidth[objs[aa].num];
1483 int sprheight = spriteheight[objs[aa].num];
1484
1485 int tint_red, tint_green, tint_blue;
1486 int tint_level, tint_light, light_level;
1487 int zoom_level = 100;
1488
1489 // calculate the zoom level
1490 if (objs[aa].flags & OBJF_USEROOMSCALING) {
1491 int onarea = get_walkable_area_at_location(objs[aa].x, objs[aa].y);
1492
1493 if ((onarea <= 0) && (thisroom.walk_area_zoom[0] == 0)) {
1494 // just off the edge of an area -- use the scaling we had
1495 // while on the area
1496 zoom_level = objs[aa].last_zoom;
1497 }
1498 else
1499 zoom_level = get_area_scaling(onarea, objs[aa].x, objs[aa].y);
1500
1501 if (zoom_level != 100)
1502 scale_sprite_size(objs[aa].num, zoom_level, &sprwidth, &sprheight);
1503
1504 }
1505 // save the zoom level for next time
1506 objs[aa].last_zoom = zoom_level;
1507
1508 // save width/height into parameters if requested
1509 if (drawnWidth)
1510 *drawnWidth = sprwidth;
1511 if (drawnHeight)
1512 *drawnHeight = sprheight;
1513
1514 objs[aa].last_width = sprwidth;
1515 objs[aa].last_height = sprheight;
1516
1517 tint_red = tint_green = tint_blue = tint_level = tint_light = light_level = 0;
1518
1519 if (objs[aa].flags & OBJF_HASTINT) {
1520 // object specific tint, use it
1521 tint_red = objs[aa].tint_r;
1522 tint_green = objs[aa].tint_g;
1523 tint_blue = objs[aa].tint_b;
1524 tint_level = objs[aa].tint_level;
1525 tint_light = objs[aa].tint_light;
1526 light_level = 0;
1527 }
1528 else if (objs[aa].flags & OBJF_HASLIGHT)
1529 {
1530 light_level = objs[aa].tint_light;
1531 }
1532 else {
1533 // get the ambient or region tint
1534 int ignoreRegionTints = 1;
1535 if (objs[aa].flags & OBJF_USEREGIONTINTS)
1536 ignoreRegionTints = 0;
1537
1538 get_local_tint(objs[aa].x, objs[aa].y, ignoreRegionTints,
1539 &tint_level, &tint_red, &tint_green, &tint_blue,
1540 &tint_light, &light_level);
1541 }
1542
1543 // check whether the image should be flipped
1544 int isMirrored = 0;
1545 if ( (objs[aa].view >= 0) &&
1546 (views[objs[aa].view].loops[objs[aa].loop].frames[objs[aa].frame].pic == objs[aa].num) &&
1547 ((views[objs[aa].view].loops[objs[aa].loop].frames[objs[aa].frame].flags & VFLG_FLIPSPRITE) != 0)) {
1548 isMirrored = 1;
1549 }
1550
1551 if ((objcache[aa].image != NULL) &&
1552 (objcache[aa].sppic == objs[aa].num) &&
1553 (walkBehindMethod != DrawOverCharSprite) &&
1554 (actsps[useindx] != NULL) &&
1555 (hardwareAccelerated))
1556 {
1557 // HW acceleration
1558 objcache[aa].tintamntwas = tint_level;
1559 objcache[aa].tintredwas = tint_red;
1560 objcache[aa].tintgrnwas = tint_green;
1561 objcache[aa].tintbluwas = tint_blue;
1562 objcache[aa].tintlightwas = tint_light;
1563 objcache[aa].lightlevwas = light_level;
1564 objcache[aa].zoomWas = zoom_level;
1565 objcache[aa].mirroredWas = isMirrored;
1566
1567 return 1;
1568 }
1569
1570 if ((!hardwareAccelerated) && (gfxDriver->HasAcceleratedStretchAndFlip()))
1571 {
1572 // They want to draw it in software mode with the D3D driver,
1573 // so force a redraw
1574 objcache[aa].sppic = -389538;
1575 }
1576
1577 // If we have the image cached, use it
1578 if ((objcache[aa].image != NULL) &&
1579 (objcache[aa].sppic == objs[aa].num) &&
1580 (objcache[aa].tintamntwas == tint_level) &&
1581 (objcache[aa].tintlightwas == tint_light) &&
1582 (objcache[aa].tintredwas == tint_red) &&
1583 (objcache[aa].tintgrnwas == tint_green) &&
1584 (objcache[aa].tintbluwas == tint_blue) &&
1585 (objcache[aa].lightlevwas == light_level) &&
1586 (objcache[aa].zoomWas == zoom_level) &&
1587 (objcache[aa].mirroredWas == isMirrored)) {
1588 // the image is the same, we can use it cached!
1589 if ((walkBehindMethod != DrawOverCharSprite) &&
1590 (actsps[useindx] != NULL))
1591 return 1;
1592 // Check if the X & Y co-ords are the same, too -- if so, there
1593 // is scope for further optimisations
1594 if ((objcache[aa].xwas == objs[aa].x) &&
1595 (objcache[aa].ywas == objs[aa].y) &&
1596 (actsps[useindx] != NULL) &&
1597 (walk_behind_baselines_changed == 0))
1598 return 1;
1599 actsps[useindx] = recycle_bitmap(actsps[useindx], coldept, sprwidth, sprheight);
1600 actsps[useindx]->Blit(objcache[aa].image, 0, 0, 0, 0, objcache[aa].image->GetWidth(), objcache[aa].image->GetHeight());
1601 return 0;
1602 }
1603
1604 // Not cached, so draw the image
1605
1606 int actspsUsed = 0;
1607 if (!hardwareAccelerated)
1608 {
1609 // draw the base sprite, scaled and flipped as appropriate
1610 actspsUsed = scale_and_flip_sprite(useindx, coldept, zoom_level,
1611 objs[aa].num, sprwidth, sprheight, isMirrored);
1612 }
1613 else
1614 {
1615 // ensure actsps exists
1616 actsps[useindx] = recycle_bitmap(actsps[useindx], coldept, spritewidth[objs[aa].num], spriteheight[objs[aa].num]);
1617 }
1618
1619 // direct read from source bitmap, where possible
1620 Bitmap *comeFrom = NULL;
1621 if (!actspsUsed)
1622 comeFrom = spriteset[objs[aa].num];
1623
1624 // apply tints or lightenings where appropriate, else just copy
1625 // the source bitmap
1626 if (((tint_level > 0) || (light_level != 0)) &&
1627 (!hardwareAccelerated))
1628 {
1629 apply_tint_or_light(useindx, light_level, tint_level, tint_red,
1630 tint_green, tint_blue, tint_light, coldept,
1631 comeFrom);
1632 }
1633 else if (!actspsUsed) {
1634 actsps[useindx]->Blit(spriteset[objs[aa].num],0,0,0,0,spritewidth[objs[aa].num],spriteheight[objs[aa].num]);
1635 }
1636
1637 // Re-use the bitmap if it's the same size
1638 objcache[aa].image = recycle_bitmap(objcache[aa].image, coldept, sprwidth, sprheight);
1639
1640 // Create the cached image and store it
1641 objcache[aa].image->Blit(actsps[useindx], 0, 0, 0, 0, sprwidth, sprheight);
1642
1643 objcache[aa].sppic = objs[aa].num;
1644 objcache[aa].tintamntwas = tint_level;
1645 objcache[aa].tintredwas = tint_red;
1646 objcache[aa].tintgrnwas = tint_green;
1647 objcache[aa].tintbluwas = tint_blue;
1648 objcache[aa].tintlightwas = tint_light;
1649 objcache[aa].lightlevwas = light_level;
1650 objcache[aa].zoomWas = zoom_level;
1651 objcache[aa].mirroredWas = isMirrored;
1652 return 0;
1653 }
1654
1655
1656
1657
1658 // This is only called from draw_screen_background, but it's seperated
1659 // to help with profiling the program
prepare_objects_for_drawing()1660 void prepare_objects_for_drawing() {
1661 int aa,atxp,atyp,useindx;
1662 our_eip=32;
1663
1664 for (aa=0;aa<croom->numobj;aa++) {
1665 if (objs[aa].on != 1) continue;
1666 // offscreen, don't draw
1667 if ((objs[aa].x >= thisroom.width) || (objs[aa].y < 1))
1668 continue;
1669
1670 useindx = aa;
1671 int tehHeight;
1672
1673 int actspsIntact = construct_object_gfx(aa, NULL, &tehHeight, false);
1674
1675 // update the cache for next time
1676 objcache[aa].xwas = objs[aa].x;
1677 objcache[aa].ywas = objs[aa].y;
1678
1679 atxp = multiply_up_coordinate(objs[aa].x) - offsetx;
1680 atyp = (multiply_up_coordinate(objs[aa].y) - tehHeight) - offsety;
1681
1682 int usebasel = objs[aa].get_baseline();
1683
1684 if (objs[aa].flags & OBJF_NOWALKBEHINDS) {
1685 // ignore walk-behinds, do nothing
1686 if (walkBehindMethod == DrawAsSeparateSprite)
1687 {
1688 usebasel += thisroom.height;
1689 }
1690 }
1691 else if (walkBehindMethod == DrawAsSeparateCharSprite)
1692 {
1693 sort_out_char_sprite_walk_behind(useindx, atxp+offsetx, atyp+offsety, usebasel, objs[aa].last_zoom, objs[aa].last_width, objs[aa].last_height);
1694 }
1695 else if ((!actspsIntact) && (walkBehindMethod == DrawOverCharSprite))
1696 {
1697 sort_out_walk_behinds(actsps[useindx],atxp+offsetx,atyp+offsety,usebasel);
1698 }
1699
1700 if ((!actspsIntact) || (actspsbmp[useindx] == NULL))
1701 {
1702 bool hasAlpha = (game.spriteflags[objs[aa].num] & SPF_ALPHACHANNEL) != 0;
1703
1704 if (actspsbmp[useindx] != NULL)
1705 gfxDriver->DestroyDDB(actspsbmp[useindx]);
1706 actspsbmp[useindx] = gfxDriver->CreateDDBFromBitmap(actsps[useindx], hasAlpha);
1707 }
1708
1709 if (gfxDriver->HasAcceleratedStretchAndFlip())
1710 {
1711 actspsbmp[useindx]->SetFlippedLeftRight(objcache[aa].mirroredWas != 0);
1712 actspsbmp[useindx]->SetStretch(objs[aa].last_width, objs[aa].last_height);
1713 actspsbmp[useindx]->SetTint(objcache[aa].tintredwas, objcache[aa].tintgrnwas, objcache[aa].tintbluwas, (objcache[aa].tintamntwas * 256) / 100);
1714
1715 if (objcache[aa].tintamntwas > 0)
1716 {
1717 if (objcache[aa].tintlightwas == 0) // luminance of 0 -- pass 1 to enable
1718 actspsbmp[useindx]->SetLightLevel(1);
1719 else if (objcache[aa].tintlightwas < 250)
1720 actspsbmp[useindx]->SetLightLevel(objcache[aa].tintlightwas);
1721 else
1722 actspsbmp[useindx]->SetLightLevel(0);
1723 }
1724 else if (objcache[aa].lightlevwas != 0)
1725 actspsbmp[useindx]->SetLightLevel((objcache[aa].lightlevwas * 25) / 10 + 256);
1726 else
1727 actspsbmp[useindx]->SetLightLevel(0);
1728 }
1729
1730 add_to_sprite_list(actspsbmp[useindx],atxp,atyp,usebasel,objs[aa].transparent,objs[aa].num);
1731 }
1732
1733 }
1734
1735
1736
1737 // Draws srcimg onto destimg, tinting to the specified level
1738 // Totally overwrites the contents of the destination image
tint_image(Bitmap * ds,Bitmap * srcimg,int red,int grn,int blu,int light_level,int luminance)1739 void tint_image (Bitmap *ds, Bitmap *srcimg, int red, int grn, int blu, int light_level, int luminance) {
1740
1741 if ((srcimg->GetColorDepth() != ds->GetColorDepth()) ||
1742 (srcimg->GetColorDepth() <= 8)) {
1743 debug_script_warn("Image tint failed - images must both be hi-color");
1744 // the caller expects something to have been copied
1745 ds->Blit(srcimg, 0, 0, 0, 0, srcimg->GetWidth(), srcimg->GetHeight());
1746 return;
1747 }
1748
1749 // For performance reasons, we have a seperate blender for
1750 // when light is being adjusted and when it is not.
1751 // If luminance >= 250, then normal brightness, otherwise darken
1752 if (luminance >= 250)
1753 set_blender_mode (_myblender_color15, _myblender_color16, _myblender_color32, red, grn, blu, 0);
1754 else
1755 set_blender_mode (_myblender_color15_light, _myblender_color16_light, _myblender_color32_light, red, grn, blu, 0);
1756
1757 if (light_level >= 100) {
1758 // fully colourised
1759 ds->FillTransparent();
1760 ds->LitBlendBlt(srcimg, 0, 0, luminance);
1761 }
1762 else {
1763 // light_level is between -100 and 100 normally; 0-100 in
1764 // this case when it's a RGB tint
1765 light_level = (light_level * 25) / 10;
1766
1767 // Copy the image to the new bitmap
1768 ds->Blit(srcimg, 0, 0, 0, 0, srcimg->GetWidth(), srcimg->GetHeight());
1769 // Render the colourised image to a temporary bitmap,
1770 // then transparently draw it over the original image
1771 Bitmap *finaltarget = BitmapHelper::CreateTransparentBitmap(srcimg->GetWidth(), srcimg->GetHeight(), srcimg->GetColorDepth());
1772 finaltarget->LitBlendBlt(srcimg, 0, 0, luminance);
1773
1774 // customized trans blender to preserve alpha channel
1775 set_my_trans_blender (0, 0, 0, light_level);
1776 ds->TransBlendBlt (finaltarget, 0, 0);
1777 delete finaltarget;
1778 }
1779 }
1780
1781
1782
1783
prepare_characters_for_drawing()1784 void prepare_characters_for_drawing() {
1785 int zoom_level,newwidth,newheight,onarea,sppic,atxp,atyp,useindx;
1786 int light_level,coldept,aa;
1787 int tint_red, tint_green, tint_blue, tint_amount, tint_light = 255;
1788
1789 our_eip=33;
1790 // draw characters
1791 for (aa=0;aa<game.numcharacters;aa++) {
1792 if (game.chars[aa].on==0) continue;
1793 if (game.chars[aa].room!=displayed_room) continue;
1794 eip_guinum = aa;
1795 useindx = aa + MAX_INIT_SPR;
1796
1797 CharacterInfo*chin=&game.chars[aa];
1798 our_eip = 330;
1799 // if it's on but set to view -1, they're being silly
1800 if (chin->view < 0) {
1801 quitprintf("!The character '%s' was turned on in the current room (room %d) but has not been assigned a view number.",
1802 chin->name, displayed_room);
1803 }
1804
1805 if (chin->frame >= views[chin->view].loops[chin->loop].numFrames)
1806 chin->frame = 0;
1807
1808 if ((chin->loop >= views[chin->view].numLoops) ||
1809 (views[chin->view].loops[chin->loop].numFrames < 1)) {
1810 quitprintf("!The character '%s' could not be displayed because there were no frames in loop %d of view %d.",
1811 chin->name, chin->loop, chin->view + 1);
1812 }
1813
1814 sppic=views[chin->view].loops[chin->loop].frames[chin->frame].pic;
1815 if ((sppic < 0) || (sppic >= MAX_SPRITES))
1816 sppic = 0; // in case it's screwed up somehow
1817 our_eip = 331;
1818 // sort out the stretching if required
1819 onarea = get_walkable_area_at_character (aa);
1820 our_eip = 332;
1821
1822 if (chin->flags & CHF_MANUALSCALING) // character ignores scaling
1823 zoom_level = charextra[aa].zoom;
1824 else if ((onarea <= 0) && (thisroom.walk_area_zoom[0] == 0)) {
1825 zoom_level = charextra[aa].zoom;
1826 if (zoom_level == 0)
1827 zoom_level = 100;
1828 }
1829 else
1830 zoom_level = get_area_scaling (onarea, chin->x, chin->y);
1831
1832 charextra[aa].zoom = zoom_level;
1833
1834 tint_red = tint_green = tint_blue = tint_amount = tint_light = light_level = 0;
1835
1836 if (chin->flags & CHF_HASTINT) {
1837 // object specific tint, use it
1838 tint_red = charextra[aa].tint_r;
1839 tint_green = charextra[aa].tint_g;
1840 tint_blue = charextra[aa].tint_b;
1841 tint_amount = charextra[aa].tint_level;
1842 tint_light = charextra[aa].tint_light;
1843 light_level = 0;
1844 }
1845 else if (chin->flags & CHF_HASLIGHT)
1846 {
1847 light_level = charextra[aa].tint_light;
1848 }
1849 else {
1850 get_local_tint(chin->x, chin->y, chin->flags & CHF_NOLIGHTING,
1851 &tint_amount, &tint_red, &tint_green, &tint_blue,
1852 &tint_light, &light_level);
1853 }
1854
1855 /*if (actsps[useindx]!=NULL) {
1856 wfreeblock(actsps[useindx]);
1857 actsps[useindx] = NULL;
1858 }*/
1859
1860 our_eip = 3330;
1861 int isMirrored = 0, specialpic = sppic;
1862 bool usingCachedImage = false;
1863
1864 coldept = spriteset[sppic]->GetColorDepth();
1865
1866 // adjust the sppic if mirrored, so it doesn't accidentally
1867 // cache the mirrored frame as the real one
1868 if (views[chin->view].loops[chin->loop].frames[chin->frame].flags & VFLG_FLIPSPRITE) {
1869 isMirrored = 1;
1870 specialpic = -sppic;
1871 }
1872
1873 our_eip = 3331;
1874
1875 // if the character was the same sprite and scaling last time,
1876 // just use the cached image
1877 if ((charcache[aa].inUse) &&
1878 (charcache[aa].sppic == specialpic) &&
1879 (charcache[aa].scaling == zoom_level) &&
1880 (charcache[aa].tintredwas == tint_red) &&
1881 (charcache[aa].tintgrnwas == tint_green) &&
1882 (charcache[aa].tintbluwas == tint_blue) &&
1883 (charcache[aa].tintamntwas == tint_amount) &&
1884 (charcache[aa].tintlightwas == tint_light) &&
1885 (charcache[aa].lightlevwas == light_level))
1886 {
1887 if (walkBehindMethod == DrawOverCharSprite)
1888 {
1889 actsps[useindx] = recycle_bitmap(actsps[useindx], charcache[aa].image->GetColorDepth(), charcache[aa].image->GetWidth(), charcache[aa].image->GetHeight());
1890 actsps[useindx]->Blit (charcache[aa].image, 0, 0, 0, 0, actsps[useindx]->GetWidth(), actsps[useindx]->GetHeight());
1891 }
1892 else
1893 {
1894 usingCachedImage = true;
1895 }
1896 }
1897 else if ((charcache[aa].inUse) &&
1898 (charcache[aa].sppic == specialpic) &&
1899 (gfxDriver->HasAcceleratedStretchAndFlip()))
1900 {
1901 usingCachedImage = true;
1902 }
1903 else if (charcache[aa].inUse) {
1904 //destroy_bitmap (charcache[aa].image);
1905 charcache[aa].inUse = 0;
1906 }
1907
1908 our_eip = 3332;
1909
1910 if (zoom_level != 100) {
1911 // it needs to be stretched, so calculate the new dimensions
1912
1913 scale_sprite_size(sppic, zoom_level, &newwidth, &newheight);
1914 charextra[aa].width=newwidth;
1915 charextra[aa].height=newheight;
1916 }
1917 else {
1918 // draw at original size, so just use the sprite width and height
1919 charextra[aa].width=0;
1920 charextra[aa].height=0;
1921 newwidth = spritewidth[sppic];
1922 newheight = spriteheight[sppic];
1923 }
1924
1925 our_eip = 3336;
1926
1927 // Calculate the X & Y co-ordinates of where the sprite will be
1928 atxp=(multiply_up_coordinate(chin->x) - offsetx) - newwidth/2;
1929 atyp=(multiply_up_coordinate(chin->y) - newheight) - offsety;
1930
1931 charcache[aa].scaling = zoom_level;
1932 charcache[aa].sppic = specialpic;
1933 charcache[aa].tintredwas = tint_red;
1934 charcache[aa].tintgrnwas = tint_green;
1935 charcache[aa].tintbluwas = tint_blue;
1936 charcache[aa].tintamntwas = tint_amount;
1937 charcache[aa].tintlightwas = tint_light;
1938 charcache[aa].lightlevwas = light_level;
1939
1940 // If cache needs to be re-drawn
1941 if (!charcache[aa].inUse) {
1942
1943 // create the base sprite in actsps[useindx], which will
1944 // be scaled and/or flipped, as appropriate
1945 int actspsUsed = 0;
1946 if (!gfxDriver->HasAcceleratedStretchAndFlip())
1947 {
1948 actspsUsed = scale_and_flip_sprite(
1949 useindx, coldept, zoom_level, sppic,
1950 newwidth, newheight, isMirrored);
1951 }
1952 else
1953 {
1954 // ensure actsps exists
1955 actsps[useindx] = recycle_bitmap(actsps[useindx], coldept, spritewidth[sppic], spriteheight[sppic]);
1956 }
1957
1958 our_eip = 335;
1959
1960 if (((light_level != 0) || (tint_amount != 0)) &&
1961 (!gfxDriver->HasAcceleratedStretchAndFlip())) {
1962 // apply the lightening or tinting
1963 Bitmap *comeFrom = NULL;
1964 // if possible, direct read from the source image
1965 if (!actspsUsed)
1966 comeFrom = spriteset[sppic];
1967
1968 apply_tint_or_light(useindx, light_level, tint_amount, tint_red,
1969 tint_green, tint_blue, tint_light, coldept,
1970 comeFrom);
1971 }
1972 else if (!actspsUsed) {
1973 // no scaling, flipping or tinting was done, so just blit it normally
1974 actsps[useindx]->Blit (spriteset[sppic], 0, 0, 0, 0, actsps[useindx]->GetWidth(), actsps[useindx]->GetHeight());
1975 }
1976
1977 // update the character cache with the new image
1978 charcache[aa].inUse = 1;
1979 //charcache[aa].image = BitmapHelper::CreateBitmap_ (coldept, actsps[useindx]->GetWidth(), actsps[useindx]->GetHeight());
1980 charcache[aa].image = recycle_bitmap(charcache[aa].image, coldept, actsps[useindx]->GetWidth(), actsps[useindx]->GetHeight());
1981 charcache[aa].image->Blit (actsps[useindx], 0, 0, 0, 0, actsps[useindx]->GetWidth(), actsps[useindx]->GetHeight());
1982
1983 } // end if !cache.inUse
1984
1985 int usebasel = chin->get_baseline();
1986
1987 // adjust the Y positioning for the character's Z co-ord
1988 atyp -= multiply_up_coordinate(chin->z);
1989
1990 our_eip = 336;
1991
1992 int bgX = atxp + offsetx + chin->pic_xoffs;
1993 int bgY = atyp + offsety + chin->pic_yoffs;
1994
1995 if (chin->flags & CHF_NOWALKBEHINDS) {
1996 // ignore walk-behinds, do nothing
1997 if (walkBehindMethod == DrawAsSeparateSprite)
1998 {
1999 usebasel += thisroom.height;
2000 }
2001 }
2002 else if (walkBehindMethod == DrawAsSeparateCharSprite)
2003 {
2004 sort_out_char_sprite_walk_behind(useindx, bgX, bgY, usebasel, charextra[aa].zoom, newwidth, newheight);
2005 }
2006 else if (walkBehindMethod == DrawOverCharSprite)
2007 {
2008 sort_out_walk_behinds(actsps[useindx], bgX, bgY, usebasel);
2009 }
2010
2011 if ((!usingCachedImage) || (actspsbmp[useindx] == NULL))
2012 {
2013 bool hasAlpha = (game.spriteflags[sppic] & SPF_ALPHACHANNEL) != 0;
2014
2015 actspsbmp[useindx] = recycle_ddb_bitmap(actspsbmp[useindx], actsps[useindx], hasAlpha);
2016 }
2017
2018 if (gfxDriver->HasAcceleratedStretchAndFlip())
2019 {
2020 actspsbmp[useindx]->SetStretch(newwidth, newheight);
2021 actspsbmp[useindx]->SetFlippedLeftRight(isMirrored != 0);
2022 actspsbmp[useindx]->SetTint(tint_red, tint_green, tint_blue, (tint_amount * 256) / 100);
2023
2024 if (tint_amount != 0)
2025 {
2026 if (tint_light == 0) // tint with 0 luminance, pass as 1 instead
2027 actspsbmp[useindx]->SetLightLevel(1);
2028 else if (tint_light < 250)
2029 actspsbmp[useindx]->SetLightLevel(tint_light);
2030 else
2031 actspsbmp[useindx]->SetLightLevel(0);
2032 }
2033 else if (light_level != 0)
2034 actspsbmp[useindx]->SetLightLevel((light_level * 25) / 10 + 256);
2035 else
2036 actspsbmp[useindx]->SetLightLevel(0);
2037
2038 }
2039
2040 our_eip = 337;
2041 // disable alpha blending with tinted sprites (because the
2042 // alpha channel was lost in the tinting process)
2043 //if (((tint_level) && (tint_amount < 100)) || (light_level))
2044 //sppic = -1;
2045 add_to_sprite_list(actspsbmp[useindx], atxp + chin->pic_xoffs, atyp + chin->pic_yoffs, usebasel, chin->transparency, sppic);
2046
2047 chin->actx=atxp+offsetx;
2048 chin->acty=atyp+offsety;
2049 }
2050 }
2051
2052
2053
2054 // draw_screen_background: draws the background scene, all the interfaces
2055 // and objects; basically, the entire screen
draw_screen_background(Bitmap * ds)2056 void draw_screen_background(Bitmap *ds) {
2057
2058 static int offsetxWas = -100, offsetyWas = -100;
2059
2060 screen_reset = 1;
2061
2062 if (is_complete_overlay) {
2063 // this is normally called as part of drawing sprites - clear it
2064 // here instead
2065 clear_draw_list();
2066 return;
2067 }
2068
2069 // don't draw it before the room fades in
2070 /* if ((in_new_room > 0) & (game.color_depth > 1)) {
2071 clear(ds);
2072 return;
2073 }*/
2074 our_eip=30;
2075 update_viewport();
2076
2077 our_eip=31;
2078
2079 if ((offsetx != offsetxWas) || (offsety != offsetyWas)) {
2080 invalidate_screen();
2081
2082 offsetxWas = offsetx;
2083 offsetyWas = offsety;
2084 }
2085
2086 if (play.screen_tint >= 0)
2087 invalidate_screen();
2088
2089 if (gfxDriver->RequiresFullRedrawEachFrame())
2090 {
2091 if (roomBackgroundBmp == NULL)
2092 {
2093 update_polled_stuff_if_runtime();
2094 roomBackgroundBmp = gfxDriver->CreateDDBFromBitmap(thisroom.ebscene[play.bg_frame], false, true);
2095
2096 if ((walkBehindMethod == DrawAsSeparateSprite) && (walkBehindsCachedForBgNum != play.bg_frame))
2097 {
2098 update_walk_behind_images();
2099 }
2100 }
2101 else if (current_background_is_dirty)
2102 {
2103 update_polled_stuff_if_runtime();
2104 gfxDriver->UpdateDDBFromBitmap(roomBackgroundBmp, thisroom.ebscene[play.bg_frame], false);
2105 current_background_is_dirty = false;
2106 if (walkBehindMethod == DrawAsSeparateSprite)
2107 {
2108 update_walk_behind_images();
2109 }
2110 }
2111 gfxDriver->DrawSprite(-offsetx, -offsety, roomBackgroundBmp);
2112 }
2113 else
2114 {
2115 // the following line takes up to 50% of the game CPU time at
2116 // high resolutions and colour depths - if we can optimise it
2117 // somehow, significant performance gains to be had
2118 update_invalid_region_and_reset(ds, -offsetx, -offsety, thisroom.ebscene[play.bg_frame]);
2119 }
2120
2121 clear_sprite_list();
2122
2123 if ((debug_flags & DBG_NOOBJECTS)==0) {
2124
2125 prepare_objects_for_drawing();
2126
2127 prepare_characters_for_drawing ();
2128
2129 if ((debug_flags & DBG_NODRAWSPRITES)==0) {
2130 our_eip=34;
2131 draw_sprite_list();
2132 }
2133 }
2134 our_eip=36;
2135
2136 allegro_bitmap_test_draw();
2137 }
2138
2139
draw_fps()2140 void draw_fps()
2141 {
2142 static IDriverDependantBitmap* ddb = NULL;
2143 static Bitmap *fpsDisplay = NULL;
2144
2145 if (fpsDisplay == NULL)
2146 {
2147 fpsDisplay = BitmapHelper::CreateBitmap(get_fixed_pixel_size(100), (getfontheight_outlined(FONT_SPEECH) + get_fixed_pixel_size(5)), game.GetColorDepth());
2148 fpsDisplay = ReplaceBitmapWithSupportedFormat(fpsDisplay);
2149 }
2150 fpsDisplay->ClearTransparent();
2151 //Bitmap *oldAbuf = ds;
2152 //ds = fpsDisplay;
2153 char tbuffer[60];
2154 sprintf(tbuffer,"FPS: %d",fps);
2155 color_t text_color = fpsDisplay->GetCompatibleColor(14);
2156 wouttext_outline(fpsDisplay, 1, 1, FONT_SPEECH, text_color, tbuffer);
2157 //ds = oldAbuf;
2158
2159 Bitmap *ds = GetVirtualScreen();
2160
2161 if (ddb == NULL)
2162 ddb = gfxDriver->CreateDDBFromBitmap(fpsDisplay, false);
2163 else
2164 gfxDriver->UpdateDDBFromBitmap(ddb, fpsDisplay, false);
2165
2166 int yp = play.viewport.GetHeight() - fpsDisplay->GetHeight();
2167
2168 gfxDriver->DrawSprite(1, yp, ddb);
2169 invalidate_sprite(1, yp, ddb);
2170
2171 sprintf(tbuffer,"Loop %u", loopcounter);
2172 draw_and_invalidate_text(ds, get_fixed_pixel_size(250), yp, FONT_SPEECH, text_color, tbuffer);
2173 }
2174
2175 // draw_screen_overlay: draws any stuff currently on top of the background,
2176 // like a message box or popup interface
draw_screen_overlay()2177 void draw_screen_overlay() {
2178 int gg;
2179
2180 add_thing_to_draw(NULL, AGSE_PREGUIDRAW, 0, TRANS_RUN_PLUGIN, false);
2181
2182 // draw overlays, except text boxes and portraits
2183 for (gg=0;gg<numscreenover;gg++) {
2184 // complete overlay draw in non-transparent mode
2185 if (screenover[gg].type == OVER_COMPLETE)
2186 add_thing_to_draw(screenover[gg].bmp, screenover[gg].x, screenover[gg].y, TRANS_OPAQUE, false);
2187 else if (screenover[gg].type != OVER_TEXTMSG && screenover[gg].type != OVER_PICTURE) {
2188 int tdxp, tdyp;
2189 get_overlay_position(gg, &tdxp, &tdyp);
2190 add_thing_to_draw(screenover[gg].bmp, tdxp, tdyp, 0, screenover[gg].hasAlphaChannel);
2191 }
2192 }
2193
2194 // Draw GUIs - they should always be on top of overlays like
2195 // speech background text
2196 our_eip=35;
2197 mouse_on_iface_button=-1;
2198 if (((debug_flags & DBG_NOIFACE)==0) && (displayed_room >= 0)) {
2199 int aa;
2200
2201 if (playerchar->activeinv >= MAX_INV) {
2202 quit("!The player.activeinv variable has been corrupted, probably as a result\n"
2203 "of an incorrect assignment in the game script.");
2204 }
2205 if (playerchar->activeinv < 1) gui_inv_pic=-1;
2206 else gui_inv_pic=game.invinfo[playerchar->activeinv].pic;
2207 /* for (aa=0;aa<game.numgui;aa++) {
2208 if (guis[aa].on<1) continue;
2209 guis[aa].draw();
2210 guis[aa].poll();
2211 }*/
2212 our_eip = 37;
2213 if (guis_need_update) {
2214 //Bitmap *abufwas = ds;
2215 guis_need_update = 0;
2216 for (aa=0;aa<game.numgui;aa++) {
2217 if (!guis[aa].IsVisible()) continue;
2218
2219 if (guibg[aa] == NULL)
2220 recreate_guibg_image(&guis[aa]);
2221
2222 eip_guinum = aa;
2223 our_eip = 370;
2224 guibg[aa]->ClearTransparent();
2225 //ds = guibg[aa];
2226 our_eip = 372;
2227 guis[aa].DrawAt(guibg[aa], 0,0);
2228 our_eip = 373;
2229
2230 bool isAlpha = false;
2231 if (guis[aa].HasAlphaChannel())
2232 {
2233 isAlpha = true;
2234
2235 if ((game.options[OPT_NEWGUIALPHA] == kGuiAlphaRender_Legacy) && (guis[aa].BgImage > 0))
2236 {
2237 // old-style (pre-3.0.2) GUI alpha rendering
2238 repair_alpha_channel(guibg[aa], spriteset[guis[aa].BgImage]);
2239 }
2240 }
2241
2242 if (guibgbmp[aa] != NULL)
2243 {
2244 gfxDriver->UpdateDDBFromBitmap(guibgbmp[aa], guibg[aa], isAlpha);
2245 }
2246 else
2247 {
2248 guibgbmp[aa] = gfxDriver->CreateDDBFromBitmap(guibg[aa], isAlpha);
2249 }
2250 our_eip = 374;
2251 }
2252 //ds = abufwas;
2253 }
2254 our_eip = 38;
2255 // Draw the GUIs
2256 for (gg = 0; gg < game.numgui; gg++) {
2257 aa = play.gui_draw_order[gg];
2258 if (!guis[aa].IsVisible()) continue;
2259
2260 // Don't draw GUI if "GUIs Turn Off When Disabled"
2261 if ((game.options[OPT_DISABLEOFF] == 3) &&
2262 (all_buttons_disabled > 0) &&
2263 (guis[aa].PopupStyle != kGUIPopupNoAutoRemove))
2264 continue;
2265
2266 add_thing_to_draw(guibgbmp[aa], guis[aa].X, guis[aa].Y, guis[aa].Transparency, guis[aa].HasAlphaChannel());
2267
2268 // only poll if the interface is enabled (mouseovers should not
2269 // work while in Wait state)
2270 if (IsInterfaceEnabled())
2271 guis[aa].Poll();
2272 }
2273 }
2274
2275 // draw speech and portraits (so that they appear over GUIs)
2276 for (gg=0;gg<numscreenover;gg++)
2277 {
2278 if (screenover[gg].type == OVER_TEXTMSG || screenover[gg].type == OVER_PICTURE)
2279 {
2280 int tdxp, tdyp;
2281 get_overlay_position(gg, &tdxp, &tdyp);
2282 add_thing_to_draw(screenover[gg].bmp, tdxp, tdyp, 0, false);
2283 }
2284 }
2285
2286 our_eip = 1099;
2287 }
2288
put_sprite_list_on_screen()2289 void put_sprite_list_on_screen()
2290 {
2291 // *** Draw the Things To Draw List ***
2292
2293 SpriteListEntry *thisThing;
2294
2295 for (size_t i = 0; i < thingsToDrawList.size(); ++i)
2296 {
2297 thisThing = &thingsToDrawList[i];
2298
2299 if (thisThing->bmp != NULL) {
2300 // mark the image's region as dirty
2301 invalidate_sprite(thisThing->x, thisThing->y, thisThing->bmp);
2302 }
2303 else if ((thisThing->transparent != TRANS_RUN_PLUGIN) &&
2304 (thisThing->bmp == NULL))
2305 {
2306 quit("Null pointer added to draw list");
2307 }
2308
2309 if (thisThing->bmp != NULL)
2310 {
2311 if (thisThing->transparent <= 255)
2312 {
2313 thisThing->bmp->SetTransparency(thisThing->transparent);
2314 }
2315
2316 gfxDriver->DrawSprite(thisThing->x, thisThing->y, thisThing->bmp);
2317 }
2318 else if (thisThing->transparent == TRANS_RUN_PLUGIN)
2319 {
2320 // meta entry to run the plugin hook
2321 gfxDriver->DrawSprite(thisThing->x, thisThing->y, NULL);
2322 }
2323 else
2324 quit("Unknown entry in draw list");
2325 }
2326
2327 clear_draw_list();
2328
2329 our_eip = 1100;
2330 }
2331
draw_misc_info()2332 void draw_misc_info()
2333 {
2334 if (display_fps)
2335 {
2336 draw_fps();
2337 }
2338
2339 Bitmap *ds = GetVirtualScreen();
2340
2341 color_t text_color;
2342 if (play.recording) {
2343 // Flash "REC" while recording
2344 text_color = ds->GetCompatibleColor (12);
2345 //if ((loopcounter % (frames_per_second * 2)) > frames_per_second/2) {
2346 char tformat[30];
2347 sprintf (tformat, "REC %02d:%02d:%02d", replay_time / 3600, (replay_time % 3600) / 60, replay_time % 60);
2348 draw_and_invalidate_text(ds, get_fixed_pixel_size(5), get_fixed_pixel_size(10), FONT_SPEECH, text_color, tformat);
2349 //}
2350 }
2351 else if (play.playback) {
2352 text_color = ds->GetCompatibleColor (10);
2353 char tformat[30];
2354 sprintf (tformat, "PLAY %02d:%02d:%02d", replay_time / 3600, (replay_time % 3600) / 60, replay_time % 60);
2355
2356 draw_and_invalidate_text(ds, get_fixed_pixel_size(5), get_fixed_pixel_size(10), FONT_SPEECH, text_color, tformat);
2357 }
2358
2359 our_eip = 1101;
2360 }
2361
GfxDriverNullSpriteCallback(int x,int y)2362 bool GfxDriverNullSpriteCallback(int x, int y)
2363 {
2364 if (displayed_room < 0)
2365 {
2366 // if no room loaded, various stuff won't be initialized yet
2367 return 1;
2368 }
2369 return (pl_run_plugin_hooks(x, y) != 0);
2370 }
2371
GfxDriverOnInitCallback(void * data)2372 void GfxDriverOnInitCallback(void *data)
2373 {
2374 pl_run_plugin_init_gfx_hooks(gfxDriver->GetDriverID(), data);
2375 }
2376
2377
2378
2379
2380 int numOnStack = 0;
2381 Bitmap *screenstack[10];
push_screen(Bitmap * ds)2382 void push_screen (Bitmap *ds) {
2383 if (numOnStack >= 10)
2384 quit("!Too many push screen calls");
2385
2386 screenstack[numOnStack] = ds;
2387 numOnStack++;
2388 }
pop_screen()2389 Bitmap *pop_screen() {
2390 if (numOnStack <= 0)
2391 quit("!Too many pop screen calls");
2392 numOnStack--;
2393 return SetVirtualScreen(screenstack[numOnStack]);
2394 }
2395
2396 // update_screen: copies the contents of the virtual screen to the actual
2397 // screen, and draws the mouse cursor on.
update_screen()2398 void update_screen() {
2399 // cos hi-color doesn't fade in, don't draw it the first time
2400 if ((in_new_room > 0) & (game.color_depth > 1))
2401 return;
2402 gfxDriver->DrawSprite(AGSE_POSTSCREENDRAW, 0, NULL);
2403
2404 // update animating mouse cursor
2405 if (game.mcurs[cur_cursor].view>=0) {
2406 domouse (DOMOUSE_NOCURSOR);
2407 // only on mousemove, and it's not moving
2408 if (((game.mcurs[cur_cursor].flags & MCF_ANIMMOVE)!=0) &&
2409 (mousex==lastmx) && (mousey==lastmy)) ;
2410 // only on hotspot, and it's not on one
2411 else if (((game.mcurs[cur_cursor].flags & MCF_HOTSPOT)!=0) &&
2412 (GetLocationType(divide_down_coordinate(mousex), divide_down_coordinate(mousey)) == 0))
2413 set_new_cursor_graphic(game.mcurs[cur_cursor].pic);
2414 else if (mouse_delay>0) mouse_delay--;
2415 else {
2416 int viewnum=game.mcurs[cur_cursor].view;
2417 int loopnum=0;
2418 if (loopnum >= views[viewnum].numLoops)
2419 quitprintf("An animating mouse cursor is using view %d which has no loops", viewnum + 1);
2420 if (views[viewnum].loops[loopnum].numFrames < 1)
2421 quitprintf("An animating mouse cursor is using view %d which has no frames in loop %d", viewnum + 1, loopnum);
2422
2423 mouse_frame++;
2424 if (mouse_frame >= views[viewnum].loops[loopnum].numFrames)
2425 mouse_frame=0;
2426 set_new_cursor_graphic(views[viewnum].loops[loopnum].frames[mouse_frame].pic);
2427 mouse_delay = views[viewnum].loops[loopnum].frames[mouse_frame].speed + 5;
2428 CheckViewFrame (viewnum, loopnum, mouse_frame);
2429 }
2430 lastmx=mousex; lastmy=mousey;
2431 }
2432
2433 // draw the debug console, if appropriate
2434 if ((play.debug_mode > 0) && (display_console != 0))
2435 {
2436 //int otextc = ds->GetTextColor();
2437 int ypp = 1;
2438 int txtspacing= getfontspacing_outlined(0);
2439 int barheight = getheightoflines(0, DEBUG_CONSOLE_NUMLINES - 1) + 4;
2440
2441 if (debugConsoleBuffer == NULL)
2442 {
2443 debugConsoleBuffer = BitmapHelper::CreateBitmap(play.viewport.GetWidth(), barheight,game.GetColorDepth());
2444 debugConsoleBuffer = ReplaceBitmapWithSupportedFormat(debugConsoleBuffer);
2445 }
2446
2447 //Bitmap *ds = GetVirtualScreen();
2448 //push_screen(ds);
2449 //ds = debugConsoleBuffer;
2450 color_t draw_color = debugConsoleBuffer->GetCompatibleColor(15);
2451 debugConsoleBuffer->FillRect(Rect (0, 0, play.viewport.GetWidth() - 1, barheight), draw_color);
2452 color_t text_color = debugConsoleBuffer->GetCompatibleColor(16);
2453 for (int jj = first_debug_line; jj != last_debug_line; jj = (jj + 1) % DEBUG_CONSOLE_NUMLINES) {
2454 wouttextxy(debugConsoleBuffer, 1, ypp, 0, text_color, debug_line[jj]);
2455 ypp += txtspacing;
2456 }
2457 //buf_graphics.text_color = otextc;
2458 //ds = pop_screen();
2459
2460 if (debugConsole == NULL)
2461 debugConsole = gfxDriver->CreateDDBFromBitmap(debugConsoleBuffer, false, true);
2462 else
2463 gfxDriver->UpdateDDBFromBitmap(debugConsole, debugConsoleBuffer, false);
2464
2465 gfxDriver->DrawSprite(0, 0, debugConsole);
2466 invalidate_sprite(0, 0, debugConsole);
2467 }
2468
2469 domouse(DOMOUSE_NOCURSOR);
2470
2471 if (!play.mouse_cursor_hidden)
2472 {
2473 gfxDriver->DrawSprite(mousex - hotx, mousey - hoty, mouseCursor);
2474 invalidate_sprite(mousex - hotx, mousey - hoty, mouseCursor);
2475 }
2476
2477 /*
2478 domouse(1);
2479 // if the cursor is hidden, remove it again. However, it needs
2480 // to go on-off in order to update the stored mouse coordinates
2481 if (play.mouse_cursor_hidden)
2482 domouse(2);*/
2483
2484 write_screen();
2485
2486 SetVirtualScreen(virtual_screen);
2487
2488 if (!play.screen_is_faded_out) {
2489 // always update the palette, regardless of whether the plugin
2490 // vetos the screen update
2491 if (bg_just_changed) {
2492 setpal ();
2493 bg_just_changed = 0;
2494 }
2495 }
2496
2497 //if (!play.mouse_cursor_hidden)
2498 // domouse(2);
2499
2500 screen_is_dirty = 0;
2501 }
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516 extern volatile int psp_audio_multithreaded; // in ac_audio
2517
2518
construct_virtual_screen(bool fullRedraw)2519 void construct_virtual_screen(bool fullRedraw)
2520 {
2521 gfxDriver->ClearDrawList();
2522
2523 if (play.fast_forward)
2524 return;
2525
2526 our_eip=3;
2527
2528 Bitmap *ds = GetVirtualScreen();
2529
2530 gfxDriver->UseSmoothScaling(IS_ANTIALIAS_SPRITES);
2531 gfxDriver->RenderSpritesAtScreenResolution(usetup.RenderAtScreenRes, usetup.Supersampling);
2532
2533 pl_run_plugin_hooks(AGSE_PRERENDER, 0);
2534
2535 if (displayed_room >= 0) {
2536
2537 if (fullRedraw)
2538 invalidate_screen();
2539
2540 draw_screen_background(ds);
2541 }
2542 else if (!gfxDriver->RequiresFullRedrawEachFrame())
2543 {
2544 // if the driver is not going to redraw the screen,
2545 // black it out so we don't get cursor trails
2546 ds->Fill(0);
2547 }
2548
2549 // reset the Baselines Changed flag now that we've drawn stuff
2550 walk_behind_baselines_changed = 0;
2551
2552 // make sure that the mp3 is always playing smoothly
2553 update_mp3();
2554 our_eip=4;
2555 draw_screen_overlay();
2556 put_sprite_list_on_screen();
2557 draw_misc_info();
2558
2559 if (fullRedraw)
2560 {
2561 // ensure the virtual screen is reconstructed
2562 // in case we want to take any screenshots before
2563 // the next game loop
2564 if (gfxDriver->UsesMemoryBackBuffer())
2565 gfxDriver->RenderToBackBuffer();
2566 }
2567 }
2568
2569 // Draw everything
render_graphics(IDriverDependantBitmap * extraBitmap,int extraX,int extraY)2570 void render_graphics(IDriverDependantBitmap *extraBitmap, int extraX, int extraY) {
2571
2572 construct_virtual_screen(false);
2573 our_eip=5;
2574
2575 if (extraBitmap != NULL) {
2576 invalidate_sprite(extraX, extraY, extraBitmap);
2577 gfxDriver->DrawSprite(extraX, extraY, extraBitmap);
2578 }
2579
2580 update_screen();
2581 }
2582