/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * Based on ScottFree interpreter version 1.14 developed by Swansea * University Computer Society without disassembly of any other game * drivers, only of game databases as permitted by EEC law (for purposes * of compatibility). * * Licensed under GPLv2 * * https://github.com/angstsmurf/spatterlight/tree/master/terps/scott */ #include "glk/scott/scott.h" #include "glk/scott/globals.h" #include "glk/scott/ring_buffer.h" #include "glk/scott/saga_draw.h" #include "glk/scott/line_drawing.h" namespace Glk { namespace Scott { void scottLinegraphicsPlotClip(int x, int y, int colour) { /* * Clip the plot if the value is outside the context. Otherwise, plot the * pixel as colour1 if it is currently colour2. */ if (x >= 0 && x <= _G(_scottGraphicsWidth) && y >= 0 && y < _G(_scottGraphicsHeight)) { _G(_pictureBitmap)[y * 255 + x] = colour; PixelToDraw *toDraw = new PixelToDraw; toDraw->_x = x; toDraw->_y = y; toDraw->_colour = colour; _G(_pixelsToDraw)[_G(_totalDrawInstructions)++] = toDraw; } } void scottLinegraphicsDrawLine(int x1, int y1, int x2, int y2, int colour) { int x, y, dx, dy, incx, incy, balance; /* Normalize the line into deltas and increments. */ if (x2 >= x1) { dx = x2 - x1; incx = 1; } else { dx = x1 - x2; incx = -1; } if (y2 >= y1) { dy = y2 - y1; incy = 1; } else { dy = y1 - y2; incy = -1; } /* Start at x1,y1. */ x = x1; y = y1; /* Decide on a direction to progress in. */ if (dx >= dy) { dy <<= 1; balance = dy - dx; dx <<= 1; /* Loop until we reach the end point of the line. */ while (x != x2) { scottLinegraphicsPlotClip(x, y, colour); if (balance >= 0) { y += incy; balance -= dx; } balance += dy; x += incx; } scottLinegraphicsPlotClip(x, y, colour); } else { dx <<= 1; balance = dx - dy; dy <<= 1; /* Loop until we reach the end point of the line. */ while (y != y2) { scottLinegraphicsPlotClip(x, y, colour); if (balance >= 0) { x += incx; balance -= dy; } balance += dx; y += incy; } scottLinegraphicsPlotClip(x, y, colour); } } void freePixels() { for (int i = 0; i < _G(_totalDrawInstructions); i++) if (_G(_pixelsToDraw)[i] != nullptr) delete _G(_pixelsToDraw)[i]; delete[] _G(_pixelsToDraw); } int linegraphicsGetPixel(int x, int y) { return _G(_pictureBitmap)[y * 255 + x]; } void diamondFill(int x, int y, int colour) { uint8_t buffer[2048]; cbuf_handle_t ringbuf = circularBufInit(buffer, 2048); circularBufPut(ringbuf, x, y); while (!circularBufEmpty(ringbuf)) { circularBufGet(ringbuf, &x, &y); if (x >= 0 && x < _G(_scottGraphicsWidth) && y >= 0 && y < _G(_scottGraphicsHeight) && linegraphicsGetPixel(x, y) == _G(_bgColour)) { scottLinegraphicsPlotClip(x, y, colour); circularBufPut(ringbuf, x, y + 1); circularBufPut(ringbuf, x, y - 1); circularBufPut(ringbuf, x + 1, y); circularBufPut(ringbuf, x - 1, y); } } } void drawVectorPicture(int image) { if (image < 0) { return; } if (_G(_vectorImageShown) == image) { if (_G(_vectorState) == SHOWING_VECTOR_IMAGE) { return; } else { if (_G(_gliSlowDraw)) g_scott->glk_request_timer_events(20); drawSomeVectorPixels(1); return; } } g_scott->glk_request_timer_events(0); _G(_vectorImageShown) = image; if (_G(_pixelsToDraw) != nullptr) freePixels(); _G(_pixelsToDraw) = new PixelToDraw *[255 * 97]; _G(_totalDrawInstructions) = 0; _G(_currentDrawInstruction) = 0; if (_G(_palChosen) == NO_PALETTE) { _G(_palChosen) = _G(_game)->_palette; definePalette(); } _G(_pictureBitmap) = new uint8_t[255 * 97]; _G(_bgColour) = _G(_lineImages)[image]._bgColour; memset(_G(_pictureBitmap), _G(_bgColour), 255 * 97); if (_G(_bgColour) == 0) _G(_lineColour) = 7; else _G(_lineColour) = 0; int x = 0, y = 0, y2 = 0; int arg1, arg2, arg3; uint8_t *p = _G(_lineImages)[image]._data; uint8_t opcode = 0; while (((p < _G(_lineImages)[image]._data) || static_cast(p - _G(_lineImages)[image]._data) < _G(_lineImages)[image]._size) && opcode != 0xff) { if (p > _G(_entireFile) + _G(_fileLength)) { error("drawVectorPicture: Out of range! Opcode: %x. Image: %d. LineImages[%d].size: %llu", opcode, image, image, _G(_lineImages)[image]._size); break; } opcode = *(p++); switch (opcode) { case 0xc0: y = 190 - *(p++); x = *(p++); break; case 0xc1: arg1 = *(p++); arg2 = *(p++); arg3 = *(p++); diamondFill(arg3, 190 - arg2, arg1); break; case 0xff: break; default: arg1 = *(p++); y2 = 190 - opcode; scottLinegraphicsDrawLine(x, y, arg1, y2, _G(_lineColour)); x = arg1; y = y2; break; } } if (_G(_pictureBitmap) != nullptr) { delete[] _G(_pictureBitmap); _G(_pictureBitmap) = nullptr; } if (_G(_gliSlowDraw)) g_scott->glk_request_timer_events(20); else drawSomeVectorPixels(1); } void drawSomeVectorPixels(int fromStart) { _G(_vectorState) = DRAWING_VECTOR_IMAGE; int i = _G(_currentDrawInstruction); if (fromStart) i = 0; if (i == 0) rectFill(0, 0, _G(_scottGraphicsWidth), _G(_scottGraphicsHeight), remap(_G(_bgColour))); for (; i < _G(_totalDrawInstructions) && (!_G(_gliSlowDraw) || i < _G(_currentDrawInstruction) + 50); i++) { PixelToDraw toDraw = *_G(_pixelsToDraw)[i]; putPixel(toDraw._x, toDraw._y, remap(toDraw._colour)); } _G(_currentDrawInstruction) = i; if (_G(_currentDrawInstruction) >= _G(_totalDrawInstructions)) { g_scott->glk_request_timer_events(0); _G(_vectorState) = SHOWING_VECTOR_IMAGE; } } int drawingVector() { return (_G(_totalDrawInstructions) > _G(_currentDrawInstruction)); } } // End of namespace Scott } // End of namespace Glk