ScummVM API documentation
graphics.h
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 modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (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, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #ifndef ADL_GRAPHICS_H
23 #define ADL_GRAPHICS_H
24 
25 #include "common/rect.h"
26 #include "common/stream.h"
27 
28 #include "adl/display.h"
29 
30 namespace Adl {
31 
32 class GraphicsMan {
33 public:
34  virtual ~GraphicsMan() { }
35 
36  // Applesoft BASIC HLINE
37  virtual void drawLine(const Common::Point &p1, const Common::Point &p2, byte color) const = 0;
38  // Applesoft BASIC DRAW
39  virtual void drawShape(Common::ReadStream &shape, Common::Point &pos, byte rotation = 0, byte scaling = 1, byte color = 0x7f) const = 0;
40  virtual void drawPic(Common::SeekableReadStream &pic, const Common::Point &pos) = 0;
41  virtual void clearScreen() const = 0;
42  void setBounds(const Common::Rect &r) { _bounds = r; }
43 
44 protected:
45  Common::Rect _bounds;
46 };
47 
48 // Used in hires1
49 template <class T>
50 class GraphicsMan_v1 : public GraphicsMan {
51 public:
52  GraphicsMan_v1<T>(T &display) : _display(display) { this->setBounds(Common::Rect(280, 160)); }
53 
54  void drawLine(const Common::Point &p1, const Common::Point &p2, byte color) const override;
55  void drawShape(Common::ReadStream &shape, Common::Point &pos, byte rotation = 0, byte scaling = 1, byte color = 0x7f) const override;
56  void drawPic(Common::SeekableReadStream &pic, const Common::Point &pos) override;
57  void clearScreen() const override;
58 
59 protected:
60  T &_display;
61  void putPixel(const Common::Point &p, byte color) const;
62 
63 private:
64  void drawShapePixel(Common::Point &p, byte color, byte bits, byte quadrant) const;
65  virtual byte getClearColor() const { return 0x00; }
66 };
67 
68 // Used in hires0 and hires2-hires4
69 template <class T>
70 class GraphicsMan_v2 : public GraphicsMan_v1<T> {
71 public:
72  GraphicsMan_v2<T>(T &display) : GraphicsMan_v1<T>(display), _color(0) { }
73  void drawPic(Common::SeekableReadStream &pic, const Common::Point &pos) override;
74 
75 protected:
76  bool canFillAt(const Common::Point &p, const bool stopBit = false);
77  void fillRow(Common::Point p, const byte pattern, const bool stopBit = false);
78  byte getPatternColor(const Common::Point &p, byte pattern);
79 
80 private:
81  static bool readByte(Common::SeekableReadStream &pic, byte &b);
82  bool readPoint(Common::SeekableReadStream &pic, Common::Point &p);
83  void drawCorners(Common::SeekableReadStream &pic, bool yFirst);
84  void drawRelativeLines(Common::SeekableReadStream &pic);
85  void drawAbsoluteLines(Common::SeekableReadStream &pic);
87  virtual void fillRowLeft(Common::Point p, const byte pattern, const bool stopBit);
88  virtual void fillAt(Common::Point p, const byte pattern);
89  byte getClearColor() const override { return 0xff; }
90 
91  byte _color;
92  Common::Point _offset;
93 };
94 
95 // Used in hires5, hires6 and gelfling (possibly others as well)
96 template <class T>
97 class GraphicsMan_v3 : public GraphicsMan_v2<T> {
98 public:
99  GraphicsMan_v3<T>(T &display) : GraphicsMan_v2<T>(display) { }
100 
101 private:
102  void fillRowLeft(Common::Point p, const byte pattern, const bool stopBit) override;
103  void fillAt(Common::Point p, const byte pattern) override;
104 };
105 
106 template <class T>
107 void GraphicsMan_v1<T>::clearScreen() const {
108  _display.setMode(Display::kModeMixed);
109  _display.clear(getClearColor());
110 }
111 
112 // Draws a four-connected line
113 template <class T>
114 void GraphicsMan_v1<T>::drawLine(const Common::Point &p1, const Common::Point &p2, byte color) const {
115  int16 deltaX = p2.x - p1.x;
116  int8 xStep = 1;
117 
118  if (deltaX < 0) {
119  deltaX = -deltaX;
120  xStep = -1;
121  }
122 
123  int16 deltaY = p2.y - p1.y;
124  int8 yStep = -1;
125 
126  if (deltaY > 0) {
127  deltaY = -deltaY;
128  yStep = 1;
129  }
130 
131  Common::Point p(p1);
132  int16 steps = deltaX - deltaY + 1;
133  int16 err = deltaX + deltaY;
134 
135  while (true) {
136  putPixel(p, color);
137 
138  if (--steps == 0)
139  return;
140 
141  if (err < 0) {
142  p.y += yStep;
143  err += deltaX;
144  } else {
145  p.x += xStep;
146  err += deltaY;
147  }
148  }
149 }
150 
151 template <class T>
152 void GraphicsMan_v1<T>::putPixel(const Common::Point &p, byte color) const {
153  if (this->_bounds.contains(p))
154  _display.putPixel(p, color);
155 }
156 
157 template <class T>
158 void GraphicsMan_v1<T>::drawShapePixel(Common::Point &p, byte color, byte bits, byte quadrant) const {
159  if (bits & 4)
160  putPixel(p, color);
161 
162  bits += quadrant;
163 
164  if (bits & 1)
165  p.x += (bits & 2 ? -1 : 1);
166  else
167  p.y += (bits & 2 ? 1 : -1);
168 }
169 
170 template <class T>
171 void GraphicsMan_v1<T>::drawShape(Common::ReadStream &corners, Common::Point &pos, byte rotation, byte scaling, byte color) const {
172  const byte stepping[] = {
173  0xff, 0xfe, 0xfa, 0xf4, 0xec, 0xe1, 0xd4, 0xc5,
174  0xb4, 0xa1, 0x8d, 0x78, 0x61, 0x49, 0x31, 0x18,
175  0xff
176  };
177 
178  byte quadrant = rotation >> 4;
179  rotation &= 0xf;
180  byte xStep = stepping[rotation];
181  byte yStep = stepping[(rotation ^ 0xf) + 1] + 1;
182 
183  while (true) {
184  byte b = corners.readByte();
185 
186  if (corners.eos() || corners.err())
187  error("Error reading corners");
188 
189  if (b == 0)
190  return;
191 
192  do {
193  byte xFrac = 0x80;
194  byte yFrac = 0x80;
195  for (uint j = 0; j < scaling; ++j) {
196  if (xFrac + xStep + 1 > 255)
197  drawShapePixel(pos, color, b, quadrant);
198  xFrac += xStep + 1;
199  if (yFrac + yStep > 255)
200  drawShapePixel(pos, color, b, quadrant + 1);
201  yFrac += yStep;
202  }
203  b >>= 3;
204  } while (b != 0);
205  }
206 }
207 
208 template <class T>
210  byte x, y;
211  bool bNewLine = false;
212  byte oldX = 0, oldY = 0;
213  while (1) {
214  x = pic.readByte();
215  y = pic.readByte();
216 
217  if (pic.err() || pic.eos())
218  error("Error reading picture");
219 
220  if (x == 0xff && y == 0xff)
221  return;
222 
223  if (x == 0 && y == 0) {
224  bNewLine = true;
225  continue;
226  }
227 
228  x += pos.x;
229  y += pos.y;
230 
231  if (y > 160)
232  y = 160;
233 
234  if (bNewLine) {
235  putPixel(Common::Point(x, y), 0x7f);
236  bNewLine = false;
237  } else {
238  drawLine(Common::Point(oldX, oldY), Common::Point(x, y), 0x7f);
239  }
240 
241  oldX = x;
242  oldY = y;
243  }
244 }
245 
246 template <class T>
248  b = pic.readByte();
249 
250  if (pic.eos() || pic.err())
251  error("Error reading picture");
252 
253  if (b >= 0xe0) {
254  pic.seek(-1, SEEK_CUR);
255  return false;
256  }
257 
258  return true;
259 }
260 
261 template <class T>
263  byte b;
264 
265  if (!readByte(pic, b))
266  return false;
267 
268  p.x = b + _offset.x;
269  p.x <<= 1;
270 
271  if (!readByte(pic, b))
272  return false;
273 
274  p.y = b + _offset.y;
275 
276  return true;
277 }
278 
279 template <class T>
280 byte GraphicsMan_v2<T>::getPatternColor(const Common::Point &p, byte pattern) {
281  const byte fillPatterns[][4] = {
282  { 0x00, 0x00, 0x00, 0x00 },
283  { 0x80, 0x80, 0x80, 0x80 },
284  { 0xff, 0xff, 0xff, 0xff },
285  { 0x7f, 0x7f, 0x7f, 0x7f },
286  { 0x2a, 0x55, 0x2a, 0x55 },
287  { 0xaa, 0xd5, 0xaa, 0xd5 },
288  { 0x55, 0x2a, 0x55, 0x2a },
289  { 0xd5, 0xaa, 0xd5, 0xaa },
290  { 0x33, 0x66, 0x4c, 0x19 },
291  { 0xb3, 0xe6, 0xcc, 0x99 },
292  { 0x22, 0x44, 0x08, 0x11 },
293  { 0xa2, 0xc4, 0x88, 0x91 },
294  { 0x11, 0x22, 0x44, 0x08 },
295  { 0x91, 0xa2, 0xc4, 0x88 },
296  { 0x6e, 0x5d, 0x3b, 0x77 },
297  { 0xee, 0xdd, 0xbb, 0xf7 },
298  { 0x5d, 0x3b, 0x77, 0x6e },
299  { 0xdd, 0xbb, 0xf7, 0xee },
300  { 0x66, 0x4c, 0x19, 0x33 },
301  { 0xe6, 0xcc, 0x99, 0xb3 },
302  { 0x33, 0x66, 0x4c, 0x19 },
303  { 0xb3, 0xe6, 0xcc, 0x99 }
304  };
305 
306  if (pattern >= ARRAYSIZE(fillPatterns))
307  error("Invalid fill pattern %i encountered in picture", pattern);
308 
309  byte offset = (p.y & 1) << 1;
310  offset += (p.x / 7) & 3;
311 
312  return fillPatterns[pattern][offset % sizeof(fillPatterns[0])];
313 }
314 
315 template <class T>
317  Common::Point p;
318 
319  if (!readPoint(pic, p))
320  return;
321 
322  if (yFirst)
323  goto doYStep;
324 
325  while (true) {
326  byte b;
327  int16 n;
328 
329  if (!readByte(pic, b))
330  return;
331 
332  n = b + _offset.x;
333 
334  this->putPixel(p, _color);
335 
336  n <<= 1;
337  this->drawLine(p, Common::Point(n, p.y), _color);
338  p.x = n;
339 
340 doYStep:
341  if (!readByte(pic, b))
342  return;
343 
344  n = b + _offset.y;
345 
346  this->putPixel(p, _color);
347  this->drawLine(p, Common::Point(p.x, n), _color);
348 
349  this->putPixel(Common::Point(p.x + 1, p.y), _color);
350  this->drawLine(Common::Point(p.x + 1, p.y), Common::Point(p.x + 1, n), _color);
351 
352  p.y = n;
353  }
354 }
355 
356 template <class T>
358  Common::Point p1;
359 
360  if (!readPoint(pic, p1))
361  return;
362 
363  this->putPixel(p1, _color);
364 
365  while (true) {
366  Common::Point p2(p1);
367 
368  byte n;
369 
370  if (!readByte(pic, n))
371  return;
372 
373  byte h = (n & 0x70) >> 4;
374  byte l = n & 7;
375 
376  if (n & 0x80)
377  p2.x -= (h << 1);
378  else
379  p2.x += (h << 1);
380 
381  if (n & 8)
382  p2.y -= l;
383  else
384  p2.y += l;
385 
386  this->drawLine(p1, p2, _color);
387  p1 = p2;
388  }
389 }
390 
391 template <class T>
393  Common::Point p1;
394 
395  if (!readPoint(pic, p1))
396  return;
397 
398  this->putPixel(p1, _color);
399 
400  while (true) {
401  Common::Point p2;
402 
403  if (!readPoint(pic, p2))
404  return;
405 
406  this->drawLine(p1, p2, _color);
407  p1 = p2;
408  }
409 }
410 
411 template <class T>
412 bool GraphicsMan_v2<T>::canFillAt(const Common::Point &p, const bool stopBit) {
413  return this->_display.getPixelBit(p) != stopBit && this->_display.getPixelBit(Common::Point(p.x + 1, p.y)) != stopBit;
414 }
415 
416 template <class T>
417 void GraphicsMan_v2<T>::fillRowLeft(Common::Point p, const byte pattern, const bool stopBit) {
418  byte color = getPatternColor(p, pattern);
419 
420  while (--p.x >= this->_bounds.left) {
421  if ((p.x % 7) == 6) {
422  color = getPatternColor(p, pattern);
423  this->_display.setPixelPalette(p, color);
424  }
425  if (this->_display.getPixelBit(p) == stopBit)
426  break;
427  this->_display.setPixelBit(p, color);
428  }
429 }
430 
431 template <class T>
432 void GraphicsMan_v2<T>::fillRow(Common::Point p, const byte pattern, const bool stopBit) {
433  // Set pixel at p and palette
434  byte color = getPatternColor(p, pattern);
435  this->_display.setPixelPalette(p, color);
436  this->_display.setPixelBit(p, color);
437 
438  // Fill left of p
439  fillRowLeft(p, pattern, stopBit);
440 
441  // Fill right of p
442  while (++p.x < this->_bounds.right) {
443  if ((p.x % 7) == 0) {
444  color = getPatternColor(p, pattern);
445  // Palette is set before the first bit is tested
446  this->_display.setPixelPalette(p, color);
447  }
448  if (this->_display.getPixelBit(p) == stopBit)
449  break;
450  this->_display.setPixelBit(p, color);
451  }
452 }
453 
454 template <class T>
455 void GraphicsMan_v2<T>::fillAt(Common::Point p, const byte pattern) {
456  const bool stopBit = !this->_display.getPixelBit(p);
457 
458  // Move up into the open space above p
459  while (--p.y >= this->_bounds.top && canFillAt(p, stopBit)) {}
460 
461  // Then fill by moving down
462  while (++p.y < this->_bounds.bottom && canFillAt(p, stopBit))
463  fillRow(p, pattern, stopBit);
464 }
465 
466 template <class T>
468  byte pattern;
469 
470  if (!readByte(pic, pattern))
471  return;
472 
473  while (true) {
474  Common::Point p;
475 
476  if (!readPoint(pic, p))
477  return;
478 
479  if (this->_bounds.contains(p))
480  fillAt(p, pattern);
481  }
482 }
483 
484 template <class T>
486  // NOTE: The original engine only resets the color for overlays. As a result, room
487  // pictures that draw without setting a color or clearing the screen, will use the
488  // last color set by the previous picture. We assume this is unintentional and do
489  // not copy this behavior.
490  _color = 0;
491  _offset = pos;
492 
493  while (true) {
494  byte opcode = pic.readByte();
495 
496  if (pic.eos() || pic.err())
497  error("Error reading picture");
498 
499  switch (opcode) {
500  case 0xe0:
501  drawCorners(pic, false);
502  break;
503  case 0xe1:
504  drawCorners(pic, true);
505  break;
506  case 0xe2:
507  drawRelativeLines(pic);
508  break;
509  case 0xe3:
510  drawAbsoluteLines(pic);
511  break;
512  case 0xe4:
513  fill(pic);
514  break;
515  case 0xe5:
516  this->clearScreen();
517  _color = 0x00;
518  break;
519  case 0xf0:
520  _color = 0x00;
521  break;
522  case 0xf1:
523  _color = 0x2a;
524  break;
525  case 0xf2:
526  _color = 0x55;
527  break;
528  case 0xf3:
529  _color = 0x7f;
530  break;
531  case 0xf4:
532  _color = 0x80;
533  break;
534  case 0xf5:
535  _color = 0xaa;
536  break;
537  case 0xf6:
538  _color = 0xd5;
539  break;
540  case 0xf7:
541  _color = 0xff;
542  break;
543  case 0xff:
544  return;
545  default:
546  if (opcode >= 0xe0)
547  error("Invalid pic opcode %02x", opcode);
548  else
549  warning("Expected pic opcode, but found data byte %02x", opcode);
550  }
551  }
552 }
553 
554 template <class T>
555 void GraphicsMan_v3<T>::fillRowLeft(Common::Point p, const byte pattern, const bool stopBit) {
556  byte color = this->getPatternColor(p, pattern);
557 
558  while (--p.x >= this->_bounds.left) {
559  // In this version, when moving left, it no longer sets the palette first
560  if (!this->_display.getPixelBit(p))
561  return;
562  if ((p.x % 7) == 6) {
563  color = this->getPatternColor(p, pattern);
564  this->_display.setPixelPalette(p, color);
565  }
566  this->_display.setPixelBit(p, color);
567  }
568 }
569 
570 template <class T>
571 void GraphicsMan_v3<T>::fillAt(Common::Point p, const byte pattern) {
572  // If the row at p cannot be filled, we do nothing
573  if (!this->canFillAt(p))
574  return;
575 
576  this->fillRow(p, pattern);
577 
578  Common::Point q(p);
579 
580  // Fill up from p
581  while (--q.y >= this->_bounds.top && this->canFillAt(q))
582  this->fillRow(q, pattern);
583 
584  // Fill down from p
585  while (++p.y < this->_bounds.bottom && this->canFillAt(p))
586  this->fillRow(p, pattern);
587 }
588 
589 } // End of namespace Adl
590 
591 #endif
#define ARRAYSIZE(x)
Definition: util.h:91
virtual bool err() const
Definition: stream.h:61
void warning(MSVC_PRINTF const char *s,...) GCC_PRINTF(1
virtual bool seek(int64 offset, int whence=SEEK_SET)=0
virtual bool eos() const =0
Definition: rect.h:144
Definition: adl.h:55
Definition: graphics.h:32
Definition: stream.h:745
Definition: graphics.h:70
byte readByte()
Definition: stream.h:434
Definition: graphics.h:97
Definition: rect.h:45
Definition: graphics.h:50
void NORETURN_PRE error(MSVC_PRINTF const char *s,...) GCC_PRINTF(1
int16 x
Definition: rect.h:46
signed char * fill(signed char *first, signed char *last, Value val)
Definition: algorithm.h:111
int16 y
Definition: rect.h:47
Definition: stream.h:385