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 "image/bmp.h"
24 
25 #include "engines/util.h"
26 
27 #include "engines/nancy/nancy.h"
28 #include "engines/nancy/graphics.h"
29 #include "engines/nancy/renderobject.h"
30 #include "engines/nancy/resource.h"
31 
32 namespace Nancy {
33 
GraphicsManager()34 GraphicsManager::GraphicsManager() :
35 	_objects(objectComparator),
36 	_inputPixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0),
37 	_screenPixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0),
38 	_clut8Format(Graphics::PixelFormat::createFormatCLUT8()) {}
39 
init()40 void GraphicsManager::init() {
41 	initGraphics(640, 480, &_screenPixelFormat);
42 	_screen.create(640, 480, _screenPixelFormat);
43 	_screen.setTransparentColor(getTransColor());
44 	_screen.clear();
45 
46 	Common::SeekableReadStream *ob = g_nancy->getBootChunkStream("OB0");
47 	ob->seek(0);
48 
49 	g_nancy->_resource->loadImage(ob->readString(), _object0);
50 
51 	loadFonts();
52 }
53 
draw()54 void GraphicsManager::draw() {
55 	for (auto it : _objects) {
56 		RenderObject &current = *it;
57 
58 		current.updateGraphics();
59 
60 		if (current._isVisible && current._needsRedraw) {
61 			// object is visible and updated
62 
63 			if (current._redrawFrom) {
64 				if (current.hasMoved() && !current.getPreviousScreenPosition().isEmpty()) {
65 					// Redraw previous location if moved
66 					blitToScreen(*current._redrawFrom, current.getPreviousScreenPosition());
67 				}
68 
69 				if (current._drawSurface.hasTransparentColor()) {
70 					// Redraw below if transparent
71 					blitToScreen(*current._redrawFrom, current.getScreenPosition());
72 				}
73 			}
74 
75 			// Draw the object itself
76 			blitToScreen(current, current.getScreenPosition());
77 		} else if (!current._isVisible && current._needsRedraw && current._redrawFrom && !current.getPreviousScreenPosition().isEmpty()) {
78 			// Object just turned invisible, redraw below
79 			blitToScreen(*current._redrawFrom, current.getPreviousScreenPosition());
80 		}
81 
82 		current._needsRedraw = false;
83 		current._previousScreenPosition = current._screenPosition;
84 	}
85 
86 	// Draw the screen
87 	_screen.update();
88 }
89 
addObject(RenderObject * object)90 void GraphicsManager::addObject(RenderObject *object) {
91 	for (const auto &r : _objects) {
92 		if (r == object) {
93 			return;
94 		}
95 
96 		if (r->getZOrder() > object->getZOrder()) {
97 			break;
98 		}
99 	}
100 
101 	_objects.insert(object);
102 }
103 
removeObject(RenderObject * object)104 void GraphicsManager::removeObject(RenderObject *object) {
105 	for (auto &r : _objects) {
106 		if (r == object) {
107 			_objects.erase(&r);
108 			break;
109 		}
110 	}
111 }
112 
clearObjects()113 void GraphicsManager::clearObjects() {
114 	_objects.clear();
115 }
116 
redrawAll()117 void GraphicsManager::redrawAll() {
118 	for (auto &obj : _objects) {
119 		obj->_needsRedraw = true;
120 	}
121 }
122 
loadSurfacePalette(Graphics::ManagedSurface & inSurf,const Common::String paletteFilename)123 void GraphicsManager::loadSurfacePalette(Graphics::ManagedSurface &inSurf, const Common::String paletteFilename) {
124 	Common::File f;
125 	if (f.open(paletteFilename + ".bmp")) {
126 		Image::BitmapDecoder dec;
127 		if (dec.loadStream(f)) {
128 			inSurf.setPalette(dec.getPalette(), dec.getPaletteStartIndex(), MIN<uint>(256, dec.getPaletteColorCount()));
129 		}
130 	}
131 }
132 
loadSurfacePalette(Graphics::ManagedSurface & inSurf,const Common::String paletteFilename,uint paletteStart,uint paletteSize)133 void GraphicsManager::loadSurfacePalette(Graphics::ManagedSurface &inSurf, const Common::String paletteFilename, uint paletteStart, uint paletteSize) {
134 	Common::File f;
135 	if (f.open(paletteFilename + ".bmp")) {
136 		Image::BitmapDecoder dec;
137 		if (dec.loadStream(f)) {
138 			inSurf.setPalette(dec.getPalette(), paletteStart, paletteSize);
139 		}
140 	}
141 }
142 
copyToManaged(const Graphics::Surface & src,Graphics::ManagedSurface & dst,bool verticalFlip,bool doubleSize)143 void GraphicsManager::copyToManaged(const Graphics::Surface &src, Graphics::ManagedSurface &dst, bool verticalFlip, bool doubleSize) {
144 	if (dst.w != (doubleSize ? src.w * 2 : src.w) || dst.h != (doubleSize ? src.h * 2 : src.h)) {
145 		const uint32 *palette = dst.getPalette();
146 		bool hasTransColor = dst.hasTransparentColor();
147 		dst.create(doubleSize ? src.w * 2 : src.w, doubleSize ? src.h * 2 : src.h, src.format);
148 
149 		if (palette && g_nancy->getGameType() == kGameTypeVampire) {
150 			// free() clears the _hasPalette flag but doesn't clear the palette itself, so
151 			// we just set it to itself; hopefully this doesn't cause any issues
152 			dst.setPalette(palette, 0, 256);
153 		}
154 
155 		if (hasTransColor) {
156 			// Do the same trick with the transparent color
157 			dst.setTransparentColor(dst.getTransparentColor());
158 		}
159 	}
160 
161 	if (!verticalFlip && !doubleSize) {
162 		dst.copyRectToSurface(src, 0, 0, Common::Rect(0, 0, src.w, src.h));
163 		return;
164 	}
165 
166 	for (int y = 0; y < src.h; ++y) {
167 		if (!doubleSize) {
168 			// Copy single line bottom to top
169 			memcpy(dst.getBasePtr(0, y), src.getBasePtr(0, src.h - y - 1), src.w * src.format.bytesPerPixel);
170 		} else {
171 			// Make four copies of each source pixel
172 			for (int x = 0; x < src.w; ++x) {
173 				switch (src.format.bytesPerPixel) {
174 				case 1: {
175 					const byte *srcP = (const byte *)src.getBasePtr(x, y);
176 					uint dstX = x * 2;
177 					uint dstY = verticalFlip ? (src.h - y - 1) * 2 : src.h - y - 1;
178 					byte *dstP = ((byte *)dst.getBasePtr(dstX, dstY));
179 					*dstP = *srcP;
180 					*(dstP + 1) = *srcP;
181 					dstP += dst.w;
182 					*dstP = *srcP;
183 					*(dstP + 1) = *srcP;
184 					break;
185 				}
186 				case 2: {
187 					const uint16 *srcP = (const uint16 *)src.getBasePtr(x, y);
188 					uint dstX = x * 2;
189 					uint dstY = verticalFlip ? (src.h - y - 1) * 2 : src.h - y - 1;
190 					uint16 *dstP = ((uint16 *)dst.getBasePtr(dstX, dstY));
191 					*dstP = *srcP;
192 					*(dstP + 1) = *srcP;
193 					dstP += dst.w;
194 					*dstP = *srcP;
195 					*(dstP + 1) = *srcP;
196 					break;
197 				}
198 				case 4: {
199 					const uint32 *srcP = (const uint32 *)src.getBasePtr(x, y);
200 					uint dstX = x * 2;
201 					uint dstY = verticalFlip ? (src.h - y - 1) * 2 : src.h - y - 1;
202 					uint32 *dstP = ((uint32 *)dst.getBasePtr(dstX, dstY));
203 					*dstP = *srcP;
204 					*(dstP + 1) = *srcP;
205 					dstP += dst.w;
206 					*dstP = *srcP;
207 					*(dstP + 1) = *srcP;
208 					break;
209 				}
210 				default:
211 					return;
212 				}
213 			}
214 		}
215 	}
216 }
217 
copyToManaged(void * src,Graphics::ManagedSurface & dst,uint srcW,uint srcH,const Graphics::PixelFormat & format,bool verticalFlip,bool doubleSize)218 void GraphicsManager::copyToManaged(void *src, Graphics::ManagedSurface &dst, uint srcW, uint srcH, const Graphics::PixelFormat &format, bool verticalFlip, bool doubleSize) {
219 	// Do things the lazy way and simply create a Surface and pass it to the other overload
220 	// We do NOT free the surface since it's a temporary object and does not own the pixels
221 	Graphics::Surface surf;
222 	surf.w = srcW;
223 	surf.h = srcH;
224 	surf.format = format;
225 	surf.pitch = srcW * format.bytesPerPixel;
226 	surf.setPixels(src);
227 
228 	copyToManaged(surf, dst, verticalFlip, doubleSize);
229 }
230 
debugDrawToScreen(const Graphics::Surface & surf)231 void GraphicsManager::debugDrawToScreen(const Graphics::Surface &surf) {
232 	_screen.blitFrom(surf, Common::Point());
233 	_screen.update();
234 }
235 
getInputPixelFormat()236 const Graphics::PixelFormat &GraphicsManager::getInputPixelFormat() {
237 	if (g_nancy->getGameType() == kGameTypeVampire) {
238 		return _clut8Format;
239 	} else {
240 		return _inputPixelFormat;
241 	}
242 }
243 
getScreenPixelFormat()244 const Graphics::PixelFormat &GraphicsManager::getScreenPixelFormat() {
245 	return _screenPixelFormat;
246 }
247 
getTransColor()248 uint GraphicsManager::getTransColor() {
249 	if (g_nancy->getGameType() == kGameTypeVampire) {
250 		return 1; // If this isn't correct, try picking the pixel at [0, 0] inside the palette bitmap
251 	} else {
252 		return _inputPixelFormat.ARGBToColor(0, 0, 255, 0);
253 	}
254 }
255 
loadFonts()256 void GraphicsManager::loadFonts() {
257 	Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("FONT");
258 
259 	chunk->seek(0);
260 	while (chunk->pos() < chunk->size() - 1) {
261 		_fonts.push_back(Font());
262 		_fonts.back().read(*chunk);
263 	}
264 }
265 
266 // Draw a given screen-space rectangle to the screen
blitToScreen(const RenderObject & src,Common::Rect screenRect)267 void GraphicsManager::blitToScreen(const RenderObject &src, Common::Rect screenRect) {
268 	Common::Point pointDest(screenRect.left, screenRect.top);
269 	_screen.blitFrom(src._drawSurface, src.convertToLocal(screenRect), pointDest);
270 }
271 
objectComparator(const void * a,const void * b)272 int GraphicsManager::objectComparator(const void *a, const void *b) {
273 	if (((const RenderObject*)a)->getZOrder() < ((const RenderObject*)b)->getZOrder()) {
274 		return -1;
275 	} else if (((const RenderObject*)a)->getZOrder() > ((const RenderObject*)b)->getZOrder()) {
276 		return 1;
277 	} else {
278 		return 0;
279 	}
280 }
281 
282 } // End of namespace Nancy
283