1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "ags/shared/ac/common.h"
24 #include "ags/engine/ac/draw.h"
25 #include "ags/shared/ac/game_setup_struct.h"
26 #include "ags/engine/ac/sprite.h"
27 #include "ags/engine/ac/system.h"
28 #include "ags/engine/platform/base/ags_platform_driver.h"
29 #include "ags/plugins/ags_plugin.h"
30 #include "ags/plugins/plugin_engine.h"
31 #include "ags/shared/ac/sprite_cache.h"
32 #include "ags/shared/gfx/bitmap.h"
33 #include "ags/engine/gfx/graphics_driver.h"
34 
35 namespace AGS3 {
36 
37 using namespace AGS::Shared;
38 using namespace AGS::Engine;
39 
get_new_size_for_sprite(int ee,int ww,int hh,int & newwid,int & newhit)40 void get_new_size_for_sprite(int ee, int ww, int hh, int &newwid, int &newhit) {
41 	newwid = ww;
42 	newhit = hh;
43 	const SpriteInfo &spinfo = _GP(game).SpriteInfos[ee];
44 	if (!_GP(game).AllowRelativeRes() || !spinfo.IsRelativeRes())
45 		return;
46 	ctx_data_to_game_size(newwid, newhit, spinfo.IsLegacyHiRes());
47 }
48 
49 // set any alpha-transparent pixels in the image to the appropriate
50 // RGB mask value so that the blit calls work correctly
set_rgb_mask_using_alpha_channel(Bitmap * image)51 void set_rgb_mask_using_alpha_channel(Bitmap *image) {
52 	int x, y;
53 
54 	for (y = 0; y < image->GetHeight(); y++) {
55 		unsigned int *psrc = (unsigned int *)image->GetScanLine(y);
56 
57 		for (x = 0; x < image->GetWidth(); x++) {
58 			if ((psrc[x] & 0xff000000) == 0x00000000)
59 				psrc[x] = MASK_COLOR_32;
60 		}
61 	}
62 }
63 
64 // from is a 32-bit RGBA image, to is a 15/16/24-bit destination image
remove_alpha_channel(Bitmap * from)65 Bitmap *remove_alpha_channel(Bitmap *from) {
66 	const int game_cd = _GP(game).GetColorDepth();
67 	Bitmap *to = BitmapHelper::CreateBitmap(from->GetWidth(), from->GetHeight(), game_cd);
68 	const int maskcol = to->GetMaskColor();
69 	int y, x;
70 	unsigned int c, b, g, r;
71 
72 	if (game_cd == 24) { // 32-to-24
73 		for (y = 0; y < from->GetHeight(); y++) {
74 			unsigned int *psrc = (unsigned int *)from->GetScanLine(y);
75 			unsigned char *pdest = (unsigned char *)to->GetScanLine(y);
76 
77 			for (x = 0; x < from->GetWidth(); x++) {
78 				c = psrc[x];
79 				// less than 50% opaque, remove the pixel
80 				if (((c >> 24) & 0x00ff) < 128)
81 					c = maskcol;
82 
83 				// copy the RGB values across
84 				memcpy(&pdest[x * 3], &c, 3);
85 			}
86 		}
87 	} else if (game_cd > 8) { // 32 to 15 or 16
88 		for (y = 0; y < from->GetHeight(); y++) {
89 			unsigned int *psrc = (unsigned int *)from->GetScanLine(y);
90 			unsigned short *pdest = (unsigned short *)to->GetScanLine(y);
91 
92 			for (x = 0; x < from->GetWidth(); x++) {
93 				c = psrc[x];
94 				// less than 50% opaque, remove the pixel
95 				if (((c >> 24) & 0x00ff) < 128)
96 					pdest[x] = maskcol;
97 				else {
98 					// otherwise, copy it across
99 					r = (c >> 16) & 0x00ff;
100 					g = (c >> 8) & 0x00ff;
101 					b = c & 0x00ff;
102 					pdest[x] = makecol_depth(game_cd, r, g, b);
103 				}
104 			}
105 		}
106 	} else { // 32 to 8-bit game
107 		// TODO: consider similar to above approach if this becomes a wanted feature
108 		to->Blit(from);
109 	}
110 	return to;
111 }
112 
pre_save_sprite(Bitmap * image)113 void pre_save_sprite(Bitmap *image) {
114 	// not used, we don't save
115 }
116 
117 // these vars are global to help with debugging
118 Bitmap *tmpdbl, *curspr;
119 int newwid, newhit;
initialize_sprite(int ee)120 void initialize_sprite(int ee) {
121 
122 	if ((ee < 0) || ((size_t)ee > _GP(spriteset).GetSpriteSlotCount()))
123 		quit("initialize_sprite: invalid sprite number");
124 
125 	if ((_GP(spriteset)[ee] == nullptr) && (ee > 0)) {
126 		// replace empty sprites with blue cups, to avoid crashes
127 		_GP(spriteset).RemapSpriteToSprite0(ee);
128 	} else if (_GP(spriteset)[ee] == nullptr) {
129 		_GP(game).SpriteInfos[ee].Width = 0;
130 		_GP(game).SpriteInfos[ee].Height = 0;
131 	} else {
132 		// stretch sprites to correct resolution
133 		int oldeip = _G(our_eip);
134 		_G(our_eip) = 4300;
135 
136 		if (_GP(game).SpriteInfos[ee].Flags & SPF_HADALPHACHANNEL) {
137 			// we stripped the alpha channel out last time, put
138 			// it back so that we can remove it properly again
139 			_GP(game).SpriteInfos[ee].Flags |= SPF_ALPHACHANNEL;
140 		}
141 
142 		curspr = _GP(spriteset)[ee];
143 		get_new_size_for_sprite(ee, curspr->GetWidth(), curspr->GetHeight(), newwid, newhit);
144 
145 		_G(eip_guinum) = ee;
146 		_G(eip_guiobj) = newwid;
147 
148 		if ((newwid != curspr->GetWidth()) || (newhit != curspr->GetHeight())) {
149 			tmpdbl = BitmapHelper::CreateTransparentBitmap(newwid, newhit, curspr->GetColorDepth());
150 			if (tmpdbl == nullptr)
151 				quit("Not enough memory to load sprite graphics");
152 			tmpdbl->StretchBlt(curspr, RectWH(0, 0, tmpdbl->GetWidth(), tmpdbl->GetHeight()), Shared::kBitmap_Transparency);
153 			delete curspr;
154 			_GP(spriteset).SubstituteBitmap(ee, tmpdbl);
155 		}
156 
157 		_GP(game).SpriteInfos[ee].Width = _GP(spriteset)[ee]->GetWidth();
158 		_GP(game).SpriteInfos[ee].Height = _GP(spriteset)[ee]->GetHeight();
159 
160 		_GP(spriteset).SubstituteBitmap(ee, PrepareSpriteForUse(_GP(spriteset)[ee], (_GP(game).SpriteInfos[ee].Flags & SPF_ALPHACHANNEL) != 0));
161 
162 		if (_GP(game).GetColorDepth() < 32) {
163 			_GP(game).SpriteInfos[ee].Flags &= ~SPF_ALPHACHANNEL;
164 			// save the fact that it had one for the next time this
165 			// is re-loaded from disk
166 			_GP(game).SpriteInfos[ee].Flags |= SPF_HADALPHACHANNEL;
167 		}
168 
169 		pl_run_plugin_hooks(AGSE_SPRITELOAD, ee);
170 		update_polled_stuff_if_runtime();
171 
172 		_G(our_eip) = oldeip;
173 	}
174 }
175 
176 } // namespace AGS3
177