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 #include "common/system.h"
23 #include "graphics/macgui/macwindowmanager.h"
24 
25 #include "director/director.h"
26 
27 namespace Director {
28 
29 #include "director/graphics-data.h"
30 
31 /**
32  * The sprites colors are in reverse order with respect to the ids in director.
33  * The palette is in reverse order, this eases the code for loading files.
34  * All other color ids can be converted with: 255 - colorId.
35  **/
transformColor(uint32 color)36 uint32 DirectorEngine::transformColor(uint32 color) {
37 	if (_pixelformat.bytesPerPixel == 1)
38 		return 255 - color;
39 
40 	color = 255 - color;
41 
42 	return _wm->findBestColor(_currentPalette[color * 3], _currentPalette[color * 3 + 1], _currentPalette[color * 3 + 2]);
43 }
44 
loadPatterns()45 void DirectorEngine::loadPatterns() {
46 	for (int i = 0; i < ARRAYSIZE(director3Patterns); i++)
47 		_director3Patterns.push_back(director3Patterns[i]);
48 
49 	for (int i = 0; i < ARRAYSIZE(director3QuickDrawPatterns); i++)
50 		_director3QuickDrawPatterns.push_back(director3QuickDrawPatterns[i]);
51 }
52 
getPatterns()53 Graphics::MacPatterns &DirectorEngine::getPatterns() {
54 	// TOOD: implement switch and other version patterns. (use getVersion());
55 	return _director3QuickDrawPatterns;
56 }
57 
loadDefaultPalettes()58 void DirectorEngine::loadDefaultPalettes() {
59 	_loadedPalettes[kClutSystemMac] = PaletteV4(kClutSystemMac, macPalette, 256);
60 	_loadedPalettes[kClutRainbow] = PaletteV4(kClutRainbow, rainbowPalette, 256);
61 	_loadedPalettes[kClutGrayscale] = PaletteV4(kClutGrayscale, grayscalePalette, 256);
62 	_loadedPalettes[kClutPastels] = PaletteV4(kClutPastels, pastelsPalette, 256);
63 	_loadedPalettes[kClutVivid] = PaletteV4(kClutVivid, vividPalette, 256);
64 	_loadedPalettes[kClutNTSC] = PaletteV4(kClutNTSC, ntscPalette, 256);
65 	_loadedPalettes[kClutMetallic] = PaletteV4(kClutMetallic, metallicPalette, 256);
66 	_loadedPalettes[kClutSystemWin] = PaletteV4(kClutSystemWin, winPalette, 256);
67 }
68 
getPalette(int id)69 PaletteV4 *DirectorEngine::getPalette(int id) {
70 	if (!_loadedPalettes.contains(id)) {
71 		warning("DirectorEngine::addPalette(): Palette %d not found", id);
72 		return nullptr;
73 	}
74 
75 	return &_loadedPalettes[id];
76 }
77 
addPalette(int id,byte * palette,int length)78 void DirectorEngine::addPalette(int id, byte *palette, int length) {
79 	if (id < 0) {
80 		warning("DirectorEngine::addPalette(): Negative palette ids reserved for default palettes");
81 		return;
82 	} else if (_loadedPalettes.contains(id)) {
83 		delete[] _loadedPalettes[id].palette;
84 	}
85 
86 	_loadedPalettes[id] = PaletteV4(id, palette, length);
87 }
88 
setPalette(int id)89 bool DirectorEngine::setPalette(int id) {
90 	if (id == 0) {
91 		// Palette id of 0 is unused
92 		return false;
93 	} else if (!_loadedPalettes.contains(id)) {
94 		warning("setPalette(): no palette with matching id %d", id);
95 		return false;
96 	}
97 
98 	PaletteV4 pal = _loadedPalettes[id];
99 	setPalette(pal.palette, pal.length);
100 
101 	return true;
102 }
103 
setPalette(byte * palette,uint16 count)104 void DirectorEngine::setPalette(byte *palette, uint16 count) {
105 	// Pass the palette to OSystem only for 8bpp mode
106 	if (_pixelformat.bytesPerPixel == 1)
107 		_system->getPaletteManager()->setPalette(palette, 0, count);
108 
109 	_currentPalette = palette;
110 	_currentPaletteLength = count;
111 
112 	_wm->passPalette(palette, count);
113 }
114 
clearPalettes()115 void DirectorEngine::clearPalettes() {
116 	for (Common::HashMap<int, PaletteV4>::iterator it = _loadedPalettes.begin(); it != _loadedPalettes.end(); ++it) {
117 		if (it->_value.id > 0)
118 			delete[] it->_value.palette;
119 	}
120 }
121 
setCursor(DirectorCursor type)122 void DirectorEngine::setCursor(DirectorCursor type) {
123 	switch (type) {
124 	case kCursorMouseDown:
125 		_wm->replaceCustomCursor(mouseDown, 16, 16, 0, 0, 3);
126 		break;
127 	case kCursorMouseUp:
128 		_wm->replaceCustomCursor(mouseUp, 16, 16, 0, 0, 3);
129 		break;
130 	}
131 }
132 
draw()133 void DirectorEngine::draw() {
134 	_wm->renderZoomBox(true);
135 	_wm->draw();
136 	g_system->updateScreen();
137 }
138 
139 template <typename T>
inkDrawPixel(int x,int y,int src,void * data)140 void inkDrawPixel(int x, int y, int src, void *data) {
141 	DirectorPlotData *p = (DirectorPlotData *)data;
142 
143 	if (!p->destRect.contains(x, y))
144 		return;
145 
146 	T dst;
147 	uint32 tmpDst;
148 
149 	dst = (T)p->dst->getBasePtr(x, y);
150 
151 	if (p->ms) {
152 		// Get the pixel that macDrawPixel will give us, but store it to apply the
153 		// ink later
154 		tmpDst = *dst;
155 		(p->_wm->getDrawPixel())(x, y, src, p->ms->pd);
156 		src = *dst;
157 
158 		*dst = tmpDst;
159 	} else if (p->alpha) {
160 		// Sprite blend does not respect colourization; defaults to matte ink
161 		byte rSrc, gSrc, bSrc;
162 		byte rDst, gDst, bDst;
163 
164 		g_director->_wm->decomposeColor(src, rSrc, gSrc, bSrc);
165 		g_director->_wm->decomposeColor(*dst, rDst, gDst, bDst);
166 
167 		double alpha = (double)p->alpha / 100.0;
168 		rDst = static_cast<byte>((rSrc * alpha) + (rDst * (1.0 - alpha)));
169 		gDst = static_cast<byte>((gSrc * alpha) + (gDst * (1.0 - alpha)));
170 		bDst = static_cast<byte>((bSrc * alpha) + (bDst * (1.0 - alpha)));
171 
172 		*dst = p->_wm->findBestColor(rDst, gDst, bDst);
173 		return;
174 	}
175 
176  	switch (p->ink) {
177 	case kInkTypeBackgndTrans:
178 		if ((uint32)src == p->backColor)
179 			break;
180 		// fall through
181 	case kInkTypeMatte:
182 	case kInkTypeMask:
183 		// Only unmasked pixels make it here, so copy them straight
184 	case kInkTypeCopy: {
185 		if (p->applyColor) {
186 			// TODO: Improve the efficiency of this composition
187 			byte rSrc, gSrc, bSrc;
188 			byte rDst, gDst, bDst;
189 			byte rFor, gFor, bFor;
190 			byte rBak, gBak, bBak;
191 
192 			g_director->_wm->decomposeColor(src, rSrc, gSrc, bSrc);
193 			g_director->_wm->decomposeColor(*dst, rDst, gDst, bDst);
194 			g_director->_wm->decomposeColor(p->foreColor, rFor, gFor, bFor);
195 			g_director->_wm->decomposeColor(p->backColor, rBak, gBak, bBak);
196 
197 			*dst = p->_wm->findBestColor((rSrc | rFor) & (~rSrc | rBak),
198 																	 (gSrc | gFor) & (~gSrc | gBak),
199 																	 (bSrc | bFor) & (~bSrc | bBak));
200 		} else {
201 			*dst = src;
202 		}
203 		break;
204 	}
205 	case kInkTypeNotCopy:
206 		if (p->applyColor) {
207 			// TODO: Improve the efficiency of this composition
208 			byte rSrc, gSrc, bSrc;
209 			byte rDst, gDst, bDst;
210 			byte rFor, gFor, bFor;
211 			byte rBak, gBak, bBak;
212 
213 			g_director->_wm->decomposeColor(src, rSrc, gSrc, bSrc);
214 			g_director->_wm->decomposeColor(*dst, rDst, gDst, bDst);
215 			g_director->_wm->decomposeColor(p->foreColor, rFor, gFor, bFor);
216 			g_director->_wm->decomposeColor(p->backColor, rBak, gBak, bBak);
217 
218 			*dst = p->_wm->findBestColor((~rSrc | rFor) & (rSrc | rBak),
219 																	 (~gSrc | gFor) & (gSrc | gBak),
220 																	 (~bSrc | bFor) & (bSrc | bBak));
221 		} else {
222 			*dst = src;
223 		}
224 		break;
225 	case kInkTypeTransparent:
226 		*dst = p->applyColor ? (~src & p->foreColor) | (*dst & src) : (*dst & src);
227 		break;
228 	case kInkTypeNotTrans:
229 		*dst = p->applyColor ? (src & p->foreColor) | (*dst & ~src) : (*dst & ~src);
230 		break;
231 	case kInkTypeReverse:
232 		*dst ^= ~(src);
233 		break;
234 	case kInkTypeNotReverse:
235 		*dst ^= src;
236 		break;
237 	case kInkTypeGhost:
238 		*dst = p->applyColor ? (src | p->backColor) & (*dst | ~src) : (*dst | ~src);
239 		break;
240 	case kInkTypeNotGhost:
241 		*dst = p->applyColor ? (~src | p->backColor) & (*dst | src) : *dst | src;
242 		break;
243 		// Arithmetic ink types
244 	default: {
245 		byte rSrc, gSrc, bSrc;
246 		byte rDst, gDst, bDst;
247 
248 		g_director->_wm->decomposeColor(src, rSrc, gSrc, bSrc);
249 		g_director->_wm->decomposeColor(*dst, rDst, gDst, bDst);
250 
251 		switch (p->ink) {
252 		case kInkTypeBlend:
253 				*dst = p->_wm->findBestColor((rSrc + rDst) / 2, (gSrc + gDst) / 2, (bSrc + bDst) / 2);
254 			break;
255 		case kInkTypeAddPin:
256 				*dst = p->_wm->findBestColor(MIN((rSrc + rDst), 0xff), MIN((gSrc + gDst), 0xff), MIN((bSrc + bDst), 0xff));
257 			break;
258 		case kInkTypeAdd:
259 			// in basilisk, D3.1 is exactly using this method, adding color directly without preventing the overflow.
260 			// but i think min(src + dst, 255) will give us a better visual effect
261 				*dst = p->_wm->findBestColor(rSrc + rDst, gSrc + gDst, bSrc + bDst);
262 			break;
263 		case kInkTypeSubPin:
264 				*dst = p->_wm->findBestColor(MAX(rSrc - rDst, 0), MAX(gSrc - gDst, 0), MAX(bSrc - bDst, 0));
265 			break;
266 		case kInkTypeLight:
267 				*dst = p->_wm->findBestColor(MAX(rSrc, rDst), MAX(gSrc, gDst), MAX(bSrc, bDst));
268 			break;
269 		case kInkTypeSub:
270 				*dst = p->_wm->findBestColor(abs(rSrc - rDst) % 0xff + 1, abs(gSrc - gDst) % 0xff + 1, abs(bSrc - bDst) % 0xff + 1);
271 			break;
272 		case kInkTypeDark:
273 				*dst = p->_wm->findBestColor(MIN(rSrc, rDst), MIN(gSrc, gDst), MIN(bSrc, bDst));
274 			break;
275 		default:
276 			break;
277 		}
278 	}
279 	}
280 }
281 
getInkDrawPixel()282 Graphics::MacDrawPixPtr DirectorEngine::getInkDrawPixel() {
283 	if (_pixelformat.bytesPerPixel == 1)
284 		return &inkDrawPixel<byte *>;
285 	else
286 		return &inkDrawPixel<uint32 *>;
287 }
288 
setApplyColor()289 void DirectorPlotData::setApplyColor() {
290 	applyColor = false;
291 
292 	if (foreColor != colorBlack) {
293 		if (ink != kInkTypeGhost && ink != kInkTypeNotGhost)
294 			applyColor = true;
295 	}
296 
297 	if (backColor != colorWhite) {
298 		if (ink != kInkTypeTransparent && ink != kInkTypeNotTrans && ink != kInkTypeBackgndTrans)
299 			applyColor = true;
300 	}
301 }
302 
303 } // End of namespace Director
304