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*)(&copyPixelsFrom->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], &copyPixelsFrom->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*)(&copyPixelsFrom->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