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 ¤t = *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