1 #include "innocent/graphics.h"
6 #include "common/system.h"
7 #include "common/util.h"
8 #include "graphics/cursorman.h"
10 #include "innocent/animation.h"
11 #include "innocent/debug.h"
12 #include "innocent/debugger.h"
13 #include "innocent/exit.h"
14 #include "innocent/innocent.h"
15 #include "innocent/logic.h"
16 #include "innocent/resources.h"
17 #include "innocent/room.h"
18 #include "innocent/util.h"
24 DECLARE_SINGLETON(Graphics
);
26 Common::Point
&operator+=(Common::Point
&p1
, const Common::Point
&p2
) { return p1
= Common::Point(p1
.x
+ p2
.x
, p1
.y
+ p2
.y
); }
28 void Graphics::setEngine(Engine
*engine
) {
30 _framebuffer
.reset(new Surface
);
31 _framebuffer
->create(320, 200, 1);
35 _speechFramesLeft
= 0;
38 void Graphics::init() {
39 _resources
= _engine
->resources();
40 _system
= _engine
->_system
;
44 void Graphics::paint() {
45 debugC(2, kDebugLevelFlow
| kDebugLevelGraphics
, ">>>start paint procedure");
52 debugC(3, kDebugLevelGraphics
, "painting paintables");
53 foreach (Paintable
*, _paintables
)
56 unless (_afterRepaintHooks
.empty()) {
57 debugC(3, kDebugLevelGraphics
| kDebugLevelScript
, "running hooks");
58 foreach (CodePointer
, _afterRepaintHooks
)
60 _afterRepaintHooks
.clear();
63 debugC(2, kDebugLevelFlow
| kDebugLevelGraphics
, "<<<end paint procedure");
66 void Graphics::paintExits() {
67 debugC(3, kDebugLevelFlow
| kDebugLevelGraphics
, "painting exits");
68 foreach(Exit
*, _engine
->logic()->room()->exits())
72 void Graphics::loadInterface() {
73 _resources
->loadInterfaceImage(_interface
, _interfacePalette
);
76 void Graphics::prepareInterfacePalette() {
77 _engine
->_system
->setPalette(_interfacePalette
+ 160 * 4, 160, 96);
80 void Graphics::paintInterface() {
81 // _framebuffer->blit(_interface, Common::Point(152, 48));
84 void Graphics::setBackdrop(uint16 id
) {
86 _backdrop
.reset(_resources
->loadBackdrop(id
, palette
));
87 setPalette(palette
, 0, 256);
91 void Graphics::willFadein(FadeFlags f
) {
95 clearPalette(160, 96);
100 void Graphics::paintBackdrop() {
102 debugC(3, kDebugLevelGraphics
, "painting backdrop");
103 _framebuffer
->blit(_backdrop
.get());
106 void Graphics::paintSpeech() {
107 if (!_speech
) return;
109 if (!_speechFramesLeft
) {
112 CodePointer cb
= _speechDoneCallback
;
113 _speechDoneCallback
.reset();
118 paintText(0, 0, 235, _speech
);
123 void Graphics::paintAnimations() {
124 debugC(3, kDebugLevelGraphics
, "painting animations");
125 Common::List
<Animation
*> animations
= _engine
->logic()->animations();
126 for (Common::List
<Animation
*>::iterator it
= animations
.begin(); it
!= animations
.end(); ++it
)
131 static int _mOption
= 0;
132 static Common::Rect _optionRects
[10];
133 static uint16 _optionValues
[10];
135 uint16
Graphics::ask(uint16 left
, uint16 top
, byte width
, byte height
, byte
*string
) {
139 kFrameTileHeight
= 12,
144 frame
.create(width
* kFrameTileWidth
, height
* kFrameTileHeight
+4, 1);
146 Sprite
**frames
= _resources
->frames();
148 Common::Point
tile(0, 0);
150 paint(frames
[kFrameTopLeft
], tile
, &frame
);
151 tile
.x
+= kFrameTileWidth
;
152 for (int x
= 1; x
< width
- 1; x
++) {
153 paint(frames
[kFrameTop
], tile
, &frame
);
154 tile
.x
+= kFrameTileWidth
;
156 paint(frames
[kFrameTopRight
], tile
, &frame
);
158 tile
.y
+= kFrameTileHeight
;
161 for (int y
= 1; y
< height
- 1; y
++) {
162 paint(frames
[kFrameLeft
], tile
, &frame
);
163 tile
.x
+= kFrameTileWidth
;
164 for (int x
= 1; x
< width
- 1; x
++) {
165 paint(frames
[kFrameFill
], tile
, &frame
);
166 tile
.x
+= kFrameTileWidth
;
168 paint(frames
[kFrameRight
], tile
, &frame
);
169 tile
.y
+= kFrameTileHeight
;
173 paint(frames
[kFrameBottomLeft
], tile
, &frame
);
174 tile
.x
+= kFrameTileWidth
;
175 for (int x
= 1; x
< width
- 1; x
++) {
176 paint(frames
[kFrameBottom
], tile
, &frame
);
177 tile
.x
+= kFrameTileWidth
;
179 paint(frames
[kFrameBottomRight
], tile
, &frame
);
183 // TODO this should use the interpreter's built-in font
184 // (but it does look nicer this way)
185 paintText(10, 16, 254, string
, &frame
);
187 _system
->copyRectToScreen(reinterpret_cast<byte
*>(frame
.pixels
), frame
.pitch
, left
, top
, width
* kFrameTileWidth
, height
* kFrameTileHeight
+4);
191 _system
->updateScreen();
192 _engine
->debugger()->onFrame();
194 while (_engine
->eventMan()->pollEvent(event
)) {
196 case Common::EVENT_LBUTTONUP
:
200 for (int i
= 0; i
< _mOption
; i
++) {
201 Common::Point p
= event
.mouse
;
204 if (_optionRects
[i
].contains(p
))
205 return _optionValues
[i
];
211 _system
->delayMillis(1000/60);
219 kSelectedOptionColour
= 227
222 Common::Rect
Graphics::paintText(uint16 left
, uint16 top
, byte colour
, byte
*string
, Surface
*dest
) {
224 uint16 current_left
= left
;
225 uint16 current_top
= top
;
226 uint16 max_left
= left
;
227 byte current_colour
= colour
;
230 while ((ch
= *(string
++))) {
233 current_left
= READ_LE_UINT16(string
);
235 current_top
= READ_LE_UINT16(string
);
237 debugC(3, kDebugLevelGraphics
, "string move to %d:%d", current_left
, current_top
);
239 case kStringSetColour
:
240 current_colour
= *(string
++);
242 case kStringDefaultColour
:
243 current_colour
= colour
;
246 current_left
+= *(string
++);
249 current_left
= (320 - calculateLineWidth(string
))/2;
254 current_top
+= kLineHeight
;
256 case kStringMenuOption
:
258 _optionRects
[opt
] = paintText(current_left
, current_top
, kOptionColour
, string
, dest
);
260 _optionValues
[opt
] = READ_LE_UINT16(string
);
261 debugC(2, kDebugLevelGraphics
| kDebugLevelScript
, "option value %d: 0x%x", opt
, _optionValues
[opt
]);
265 current_left
+= paintChar(current_left
, current_top
, current_colour
, ch
, dest
);
266 if (current_left
> max_left
)
267 max_left
= current_left
;
271 return Common::Rect(left
, top
, max_left
, current_top
+ kLineHeight
);
274 byte
Graphics::clampChar(byte ch
) {
277 if (ch
< ' ' || ch
> '~')
282 uint16
Graphics::calculateLineWidth(byte
*string
) const {
285 while ((ch
= *(string
++))) {
286 if (ch
== '\n' || ch
== '\r')
288 total
+= getGlyphWidth(ch
);
293 uint16
Graphics::getGlyphWidth(byte ch
) const {
297 return getGlyph(ch
)->w
-1;
300 Sprite
*Graphics::getGlyph(byte ch
) const {
301 // TODO perhaps cache or sth
304 return 0; // space has no glyph, just width 4
305 return _resources
->getGlyph(ch
);
309 * @returns char width
311 uint16
Graphics::paintChar(uint16 left
, uint16 top
, byte colour
, byte ch
, Surface
*dest
) const {
312 Sprite
*glyph
= getGlyph(ch
);
315 glyph
->recolour(colour
);
316 paint(glyph
, Common::Point(left
, top
+glyph
->h
), dest
);
323 void Graphics::paint(const Sprite
*sprite
, Common::Point pos
, Surface
*dest
) const {
324 debugC(4, kDebugLevelGraphics
, "painting sprite at %d:%d (+%d:%d) [%dx%d]", pos
.x
, pos
.y
, sprite
->_hotPoint
.x
, sprite
->_hotPoint
.y
, sprite
->w
, sprite
->h
);
325 pos
+= sprite
->_hotPoint
;
327 Common::Rect
r(sprite
->w
, sprite
->h
);
329 // this is actually bottom
330 r
.translate(0, -sprite
->h
);
333 debugC(4, kDebugLevelGraphics
, "transformed rect: %d:%d %d:%d", r
.left
, r
.top
, r
.right
, r
.bottom
);
335 dest
->blit(sprite
, r
, 0);
338 Common::Point
Graphics::cursorPosition() const {
339 debugC(1, kDebugLevelGraphics
, "cursor position STUB");
340 return Common::Point(160, 100);
343 void Graphics::updateScreen() {
344 _system
->copyRectToScreen(reinterpret_cast<byte
*>(_framebuffer
->pixels
), _framebuffer
->pitch
, 0, 0, 320, 200);
346 if (_willFadein
&& (_fadeFlags
& kPartialFade
)) {
347 debugC(3, kDebugLevelGraphics
, "performing partial fade in");
349 fadeIn(_interfacePalette
+ 160*4, 160, 96);
350 } else if (_willFadein
&& !(_fadeFlags
& kPartialFade
)) {
355 _system
->updateScreen();
358 void Graphics::showCursor() {
359 Sprite
*cursor
= _resources
->getCursor();
360 assert(cursor
->pitch
== cursor
->w
);
361 ::Graphics::CursorManager
&m
= ::Graphics::CursorManager::instance();
362 m
.replaceCursor(reinterpret_cast<byte
*>(cursor
->pixels
), cursor
->w
, cursor
->h
, cursor
->_hotPoint
.x
, cursor
->_hotPoint
.y
, 0);
366 void Graphics::hideCursor() {
367 ::Graphics::CursorManager::instance().showMouse(false);
370 void Graphics::paintRect(const Common::Rect
&r
, byte colour
) {
371 _framebuffer
->frameRect(r
, colour
);
374 void Graphics::push(Paintable
*p
) {
375 debugC(3, kDebugLevelGraphics
, "pushing to paintables");
376 _paintables
.push_back(p
);
379 void Graphics::pop(Paintable
*p
) {
380 debugC(3, kDebugLevelGraphics
, "popping from paintables");
381 _paintables
.remove(p
);
384 void Graphics::hookAfterRepaint(CodePointer
&p
) {
385 _afterRepaintHooks
.push_back(p
);
388 const char Graphics::_charwidths
[] = {
389 #include "charwidths.data"
392 void Graphics::clearPalette(int offset
, int count
) {
394 fill(pal
, pal
+0x400, 0);
395 _system
->setPalette(pal
, offset
, count
);
398 void Graphics::setPalette(const byte
*colours
, uint start
, uint num
) {
399 _system
->setPalette(colours
, start
, num
);
402 struct Tr
: public unary_function
<byte
, byte
> {
403 byte
operator()(const byte
&b
) const { return 0xff & ((b
<< 1) - 63); }
406 void Graphics::fadeIn(const byte
*colours
, uint start
, uint num
) {
409 _system
->grabPalette(buf
, start
, num
);
413 const int bytes
= num
* 4;
416 fill(current
, current
+ bytes
, 0);
419 for (int j
= 0; j
< 63; j
++) {
421 for (int i
= 0; i
< bytes
; i
++)
422 current
[i
] = colours
[i
] - MIN(off
, colours
[i
]);
424 _system
->setPalette((current
), start
, num
);
425 _system
->updateScreen();
428 if (Eng
.escapePressed()) {
429 _system
->setPalette(colours
, start
, num
);
430 _system
->updateScreen();
436 void Graphics::fadeOut(FadeFlags f
) {
442 if (f
== kPartialFade
) {
448 _system
->grabPalette(current
, offset
, colours
);
450 for (int j
= 0; j
< 63; j
++) {
451 for (int i
= 0; i
< bytes
; i
++)
452 current
[i
] -= MIN
<byte
>(4, current
[i
]);
454 _system
->setPalette((current
), offset
, colours
);
455 _system
->updateScreen();
458 if (Eng
.escapePressed()) {
466 void Graphics::say(const byte
*text
, uint16 length
, uint16 frames
) {
468 error("queuing speech not supported yet.");
470 _speech
= new byte
[length
];
471 memcpy(_speech
, text
, length
);
472 _speechFramesLeft
= frames
;
476 void Graphics::runWhenSaid(const CodePointer
&cb
) {
477 unless (_speechDoneCallback
.isEmpty())
478 error("queuing events on speech complete not supported yet");
480 _speechDoneCallback
= cb
;
483 } // End of namespace Innocent