538 lines
13 KiB
C++
538 lines
13 KiB
C++
/* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "ultima/nuvie/core/nuvie_defs.h"
|
|
#include "ultima/nuvie/screen/surface.h"
|
|
#include "common/algorithm.h"
|
|
|
|
namespace Ultima {
|
|
namespace Nuvie {
|
|
|
|
// Colour shifting values
|
|
uint8 RenderSurface::Rloss;
|
|
uint8 RenderSurface::Gloss;
|
|
uint8 RenderSurface::Bloss;
|
|
uint8 RenderSurface::Rloss16;
|
|
uint8 RenderSurface::Gloss16;
|
|
uint8 RenderSurface::Bloss16;
|
|
uint8 RenderSurface::Rshift;
|
|
uint8 RenderSurface::Gshift;
|
|
uint8 RenderSurface::Bshift;
|
|
uint32 RenderSurface::Rmask;
|
|
uint32 RenderSurface::Gmask;
|
|
uint32 RenderSurface::Bmask;
|
|
|
|
// Default constructor for no created surface
|
|
RenderSurface::RenderSurface() : buffer(0), zbuffer_priv(0), _rawSurface(nullptr),
|
|
_disposeSurface(DisposeAfterUse::YES), opengl(0), bytes_per_pixel(0),
|
|
bits_per_pixel(0), format_type(0), pixels(0), zbuffer(0), w(0), h(0),
|
|
pitch(0), gl(0), gr(0), gt(0), gb(0), lock_count(0) {
|
|
}
|
|
|
|
// Constructor for custom buffer
|
|
RenderSurface::RenderSurface(uint32 width, uint32 height, uint32 bpp, byte *p) :
|
|
buffer(0), zbuffer_priv(0), _rawSurface(nullptr), _disposeSurface(DisposeAfterUse::YES),
|
|
opengl(0), bytes_per_pixel(bpp / 8), bits_per_pixel(bpp), pixels(p), zbuffer(0),
|
|
w(width), h(height), pitch(width), gl(0), gr(width), gt(0), gb(height), lock_count(0) {
|
|
// Set default formats for the buffer
|
|
if (bpp == 32) set_format888();
|
|
else set_format565();
|
|
}
|
|
|
|
// Constructor for generic surface (with optional guardband)
|
|
RenderSurface::RenderSurface(uint32 width, uint32 height, uint32 bpp, sint32 guard) :
|
|
buffer(0), zbuffer_priv(0), _rawSurface(nullptr), _disposeSurface(DisposeAfterUse::YES),
|
|
opengl(0), bytes_per_pixel(bpp / 8), bits_per_pixel(bpp), pixels(0), zbuffer(0),
|
|
w(width), h(height), pitch(width * (bpp / 8) + 2 * guard * (bpp / 8)),
|
|
gl(-guard), gr(guard + width), gt(-guard), gb(guard + height), lock_count(0) {
|
|
// Set default formats for the buffer
|
|
if (bpp == 32) set_format888();
|
|
else set_format565();
|
|
|
|
buffer = new uint8[pitch * (height + 2 * gb)];
|
|
pixels = buffer + (pitch * gb) + gb;
|
|
}
|
|
|
|
// Constructor for sdl surface
|
|
RenderSurface::RenderSurface(Graphics::ManagedSurface *surf) :
|
|
_rawSurface(surf), _disposeSurface(DisposeAfterUse::NO), w(surf->w), h(surf->h),
|
|
pitch(surf->pitch), gr(surf->w), pixels((byte*)surf->getPixels()), buffer(0),
|
|
zbuffer_priv(0), opengl(0), bytes_per_pixel(0), bits_per_pixel(0), zbuffer(0),
|
|
gl(0), gt(0), gb(surf->h), lock_count(0) {
|
|
set_format(&surf->format);
|
|
}
|
|
|
|
// Constructor for opengl surface
|
|
RenderSurface::RenderSurface(OpenGL *ogl) : buffer(0), zbuffer_priv(0), _rawSurface(nullptr),
|
|
_disposeSurface(DisposeAfterUse::NO), opengl(ogl), bytes_per_pixel(0),
|
|
bits_per_pixel(0), format_type(0), pixels(0), zbuffer(0), w(0), h(0), pitch(0),
|
|
gl(0), gr(0), gt(0), gb(0), lock_count(0) {
|
|
}
|
|
|
|
RenderSurface::~RenderSurface() {
|
|
delete[] buffer;
|
|
delete[] zbuffer_priv;
|
|
if (_rawSurface && _disposeSurface == DisposeAfterUse::YES)
|
|
delete _rawSurface;
|
|
}
|
|
|
|
//
|
|
// Set the buffer format from Graphics::PixelFormat
|
|
//
|
|
void RenderSurface::set_format(const Graphics::PixelFormat *fmt) {
|
|
bits_per_pixel = fmt->bytesPerPixel * 8;
|
|
bytes_per_pixel = fmt->bytesPerPixel;
|
|
|
|
Rloss = fmt->rLoss;
|
|
Gloss = fmt->gLoss;
|
|
Bloss = fmt->bLoss;
|
|
Rloss16 = Rloss + 8;
|
|
Gloss16 = Gloss + 8;
|
|
Bloss16 = Bloss + 8;
|
|
Rshift = fmt->rShift;
|
|
Gshift = fmt->gShift;
|
|
Bshift = fmt->bShift;
|
|
Rmask = fmt->rMax() << fmt->rShift;
|
|
Gmask = fmt->gMax() << fmt->gShift;
|
|
Bmask = fmt->bMax() << fmt->bShift;
|
|
|
|
// RGB 565
|
|
if (Rmask == 0xf800 && Gmask == 0x7e0 && Bmask == 0x1f)
|
|
format_type = 565;
|
|
// RGB 555
|
|
else if (Rmask == 0x7c00 && Gmask == 0x3e0 && Bmask == 0x1f)
|
|
format_type = 555;
|
|
// RGB 888
|
|
else if (Rmask == 0xFF0000 && Gmask == 0x00FF00 && Bmask == 0x0000FF)
|
|
format_type = 888;
|
|
// RGB 16 Bit Generic
|
|
else if (bits_per_pixel == 16)
|
|
format_type = 16;
|
|
// RGB 32 Bit Generic
|
|
else
|
|
format_type = 32;
|
|
}
|
|
|
|
//
|
|
// Set a custom 565 format
|
|
//
|
|
void RenderSurface::set_format565(int rsft, int gsft, int bsft) {
|
|
bits_per_pixel = 16;
|
|
bytes_per_pixel = 2;
|
|
|
|
// Static Colour shifting values
|
|
Rloss = 3;
|
|
Gloss = 2;
|
|
Bloss = 3;
|
|
Rloss16 = Rloss + 8;
|
|
Gloss16 = Gloss + 8;
|
|
Bloss16 = Bloss + 8;
|
|
Rshift = rsft;
|
|
Gshift = gsft;
|
|
Bshift = bsft;
|
|
Rmask = 0x1f << rsft;
|
|
Gmask = 0x2f << rsft;
|
|
Bmask = 0x1f << rsft;
|
|
|
|
// RGB 565
|
|
if (Rmask == 0xf800 && Gmask == 0x7e0 && Bmask == 0x1f)
|
|
format_type = 565;
|
|
// RGB 16 Bit Generic
|
|
else if (bits_per_pixel == 16)
|
|
format_type = 16;
|
|
}
|
|
|
|
//
|
|
// Set a custom 555 format
|
|
//
|
|
void RenderSurface::set_format555(int rsft, int gsft, int bsft) {
|
|
bits_per_pixel = 16;
|
|
bytes_per_pixel = 2;
|
|
|
|
// Static Colour shifting values
|
|
Rloss = 3;
|
|
Gloss = 3;
|
|
Bloss = 3;
|
|
Rloss16 = Rloss + 8;
|
|
Gloss16 = Gloss + 8;
|
|
Bloss16 = Bloss + 8;
|
|
Rshift = rsft;
|
|
Gshift = gsft;
|
|
Bshift = bsft;
|
|
Rmask = 0x1f << rsft;
|
|
Gmask = 0x1f << rsft;
|
|
Bmask = 0x1f << rsft;
|
|
|
|
// RGB 555
|
|
if (Rmask == 0x7c00 && Gmask == 0x3e0 && Bmask == 0x1f)
|
|
format_type = 555;
|
|
// RGB 16 Bit Generic
|
|
else if (bits_per_pixel == 16)
|
|
format_type = 16;
|
|
}
|
|
|
|
//
|
|
// Set a custom 888 format
|
|
//
|
|
void RenderSurface::set_format888(int rsft, int gsft, int bsft) {
|
|
bits_per_pixel = 32;
|
|
bytes_per_pixel = 4;
|
|
|
|
Rloss = 0;
|
|
Gloss = 0;
|
|
Bloss = 0;
|
|
Rloss16 = Rloss + 8;
|
|
Gloss16 = Gloss + 8;
|
|
Bloss16 = Bloss + 8;
|
|
Rshift = rsft;
|
|
Gshift = gsft;
|
|
Bshift = bsft;
|
|
Rmask = 0xFF << rsft;
|
|
Gmask = 0xFF << rsft;
|
|
Bmask = 0xFF << rsft;
|
|
|
|
// RGB 888
|
|
if (Rmask == 0xFF0000 && Gmask == 0x00FF00 && Bmask == 0x0000FF)
|
|
format_type = 888;
|
|
// RGB 32 Bit Generic
|
|
else
|
|
format_type = 32;
|
|
}
|
|
|
|
void RenderSurface::draw_line(int sx, int sy, int ex, int ey, unsigned char col) {
|
|
if (bytes_per_pixel == 4) draw_line32(sx, sy, ex, ey, col);
|
|
else draw_line16(sx, sy, ex, ey, col);
|
|
}
|
|
|
|
#define LINE_FRACTION 65536L
|
|
|
|
void RenderSurface::draw_line16(int sx, int sy, int ex, int ey, unsigned char col) {
|
|
#ifdef WANT_OPENGL
|
|
if (opengl) {
|
|
opengl->draw_line(sx, sy + 1, 0, ex, ey + 1, 0, col);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
int xinc = 1;
|
|
int yinc = 1;
|
|
|
|
if (sx == ex) {
|
|
sx --;
|
|
if (sy > ey) {
|
|
yinc = -1;
|
|
sy--;
|
|
}
|
|
} else {
|
|
if (sx > ex) {
|
|
sx--;
|
|
xinc = -1;
|
|
} else {
|
|
ex--;
|
|
}
|
|
|
|
if (sy > ey) {
|
|
yinc = -1;
|
|
sy--;
|
|
ey--;
|
|
}
|
|
}
|
|
|
|
uint16 *pixptr = (uint16 *)(pixels + pitch * sy + sx * 2);
|
|
uint16 *pixend = (uint16 *)(pixels + pitch * ey + ex * 2);
|
|
int pitch_ = this->pitch * yinc / 2;
|
|
|
|
int cury = sy;
|
|
int curx = sx;
|
|
int width = w;
|
|
int height = h;
|
|
bool no_clip = true;
|
|
|
|
if (sx >= width && ex >= width) return;
|
|
if (sy >= height && ey >= height) return;
|
|
if (sx < 0 && ex < 0) return;
|
|
if (sy < 0 && ey < 0) return;
|
|
|
|
if (sy < 0 || sy >= height || sx < 0 || sx >= width) no_clip = false;
|
|
if (ey < 0 || ey >= height || ex < 0 || ex >= width) no_clip = false;
|
|
|
|
int col32 = colour32[col];
|
|
|
|
// vertical
|
|
if (sx == ex) {
|
|
//Std::cout << "Vertical" << Std::endl;
|
|
// start is below end
|
|
while (pixptr != pixend) {
|
|
if (no_clip || (cury >= 0 && cury < height)) *pixptr = col32;
|
|
pixptr += pitch_;
|
|
cury += yinc;
|
|
}
|
|
}
|
|
// Horizontal
|
|
else if (sy == ey) {
|
|
//Std::cout << "Horizontal" << Std::endl;
|
|
while (pixptr != pixend) {
|
|
if (no_clip || (curx >= 0 && curx < width)) *pixptr = col32;
|
|
pixptr += xinc;
|
|
curx += xinc;
|
|
}
|
|
}
|
|
// Diagonal xdiff >= ydiff
|
|
else if (ABS(sx - ex) >= ABS(sy - ey)) {
|
|
//Std::cout << "Diagonal 1" << Std::endl;
|
|
uint32 fraction = ABS((LINE_FRACTION * (sy - ey)) / (sx - ex));
|
|
uint32 ycounter = 0;
|
|
|
|
for (; ;) {
|
|
if ((no_clip || (cury >= 0 && cury < height && curx >= 0 && curx < width)))
|
|
*pixptr = col32;
|
|
pixptr += xinc;
|
|
if (curx == ex) break;
|
|
curx += xinc;
|
|
ycounter += fraction;
|
|
|
|
// Need to work out if we need to change line
|
|
if (ycounter > LINE_FRACTION) {
|
|
ycounter -= LINE_FRACTION;
|
|
pixptr += pitch_;
|
|
cury += yinc;
|
|
}
|
|
}
|
|
}
|
|
// Diagonal ydiff > xdiff
|
|
else {
|
|
//Std::cout << "Diagonal 2" << Std::endl;
|
|
uint32 fraction = ABS((LINE_FRACTION * (sx - ex)) / (sy - ey));
|
|
uint32 xcounter = 0;
|
|
|
|
for (; ;) {
|
|
if ((no_clip || (cury >= 0 && cury < height && curx >= 0 && curx < width)))
|
|
*pixptr = col32;
|
|
pixptr += pitch_;
|
|
if (cury == ey) break;
|
|
cury += yinc;
|
|
xcounter += fraction;
|
|
|
|
// Need to work out if we need to change line
|
|
if (xcounter > LINE_FRACTION) {
|
|
xcounter -= LINE_FRACTION;
|
|
pixptr += xinc;
|
|
curx += xinc;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void RenderSurface::draw_line32(int sx, int sy, int ex, int ey, unsigned char col) {
|
|
#ifdef WANT_OPENGL
|
|
if (opengl) {
|
|
opengl->draw_line(sx, sy + 1, 0, ex, ey + 1, 0, col);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
int xinc = 1;
|
|
int yinc = 1;
|
|
|
|
if (sx == ex) {
|
|
sx --;
|
|
if (sy > ey) {
|
|
yinc = -1;
|
|
sy--;
|
|
}
|
|
} else {
|
|
if (sx > ex) {
|
|
sx--;
|
|
xinc = -1;
|
|
} else {
|
|
ex--;
|
|
}
|
|
|
|
if (sy > ey) {
|
|
yinc = -1;
|
|
sy--;
|
|
ey--;
|
|
}
|
|
}
|
|
|
|
uint32 *pixptr = (uint32 *)(pixels + pitch * sy + sx * 4);
|
|
uint32 *pixend = (uint32 *)(pixels + pitch * ey + ex * 4);
|
|
int pitch_ = this->pitch * yinc / 4;
|
|
|
|
int cury = sy;
|
|
int curx = sx;
|
|
int width = w;
|
|
int height = h;
|
|
bool no_clip = true;
|
|
|
|
if (sx >= width && ex >= width) return;
|
|
if (sy >= height && ey >= height) return;
|
|
if (sx < 0 && ex < 0) return;
|
|
if (sy < 0 && ey < 0) return;
|
|
|
|
if (sy < 0 || sy >= height || sx < 0 || sx >= width) no_clip = false;
|
|
if (ey < 0 || ey >= height || ex < 0 || ex >= width) no_clip = false;
|
|
|
|
int col32 = colour32[col];
|
|
|
|
// vertical
|
|
if (sx == ex) {
|
|
//Std::cout << "Vertical" << Std::endl;
|
|
// start is below end
|
|
while (pixptr != pixend) {
|
|
if (no_clip || (cury >= 0 && cury < height)) *pixptr = col32;
|
|
pixptr += pitch_;
|
|
cury += yinc;
|
|
}
|
|
}
|
|
// Horizontal
|
|
else if (sy == ey) {
|
|
//Std::cout << "Horizontal" << Std::endl;
|
|
while (pixptr != pixend) {
|
|
if (no_clip || (curx >= 0 && curx < width)) *pixptr = col32;
|
|
pixptr += xinc;
|
|
curx += xinc;
|
|
}
|
|
}
|
|
// Diagonal xdiff >= ydiff
|
|
else if (ABS(sx - ex) >= ABS(sy - ey)) {
|
|
//Std::cout << "Diagonal 1" << Std::endl;
|
|
uint32 fraction = ABS((LINE_FRACTION * (sy - ey)) / (sx - ex));
|
|
uint32 ycounter = 0;
|
|
|
|
for (; ;) {
|
|
if ((no_clip || (cury >= 0 && cury < height && curx >= 0 && curx < width)))
|
|
*pixptr = col32;
|
|
pixptr += xinc;
|
|
if (curx == ex) break;
|
|
curx += xinc;
|
|
ycounter += fraction;
|
|
|
|
// Need to work out if we need to change line
|
|
if (ycounter > LINE_FRACTION) {
|
|
ycounter -= LINE_FRACTION;
|
|
pixptr += pitch_;
|
|
cury += yinc;
|
|
}
|
|
}
|
|
}
|
|
// Diagonal ydiff > xdiff
|
|
else {
|
|
//Std::cout << "Diagonal 2" << Std::endl;
|
|
uint32 fraction = ABS((LINE_FRACTION * (sx - ex)) / (sy - ey));
|
|
uint32 xcounter = 0;
|
|
|
|
for (; ;) {
|
|
if ((no_clip || (cury >= 0 && cury < height && curx >= 0 && curx < width)))
|
|
*pixptr = col32;
|
|
pixptr += pitch_;
|
|
if (cury == ey) break;
|
|
cury += yinc;
|
|
xcounter += fraction;
|
|
|
|
// Need to work out if we need to change line
|
|
if (xcounter > LINE_FRACTION) {
|
|
xcounter -= LINE_FRACTION;
|
|
pixptr += xinc;
|
|
curx += xinc;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
void RenderSurface::draw_3d_line(int x, int y, int sx, int sy, int sz, int ex, int ey, int ez, unsigned char col) {
|
|
int dispsx = x + (sx - sy) / 4;
|
|
int dispsy = y + (sx + sy) / 8 - sz;
|
|
int dispex = x + (ex - ey) / 4;
|
|
int dispey = y + (ex + ey) / 8 - ez;
|
|
|
|
#ifdef WANT_OPENGL
|
|
if (opengl) opengl->draw_line(dispsx, dispsy + 1, 0, dispex, dispey + 1, 0, col);
|
|
else
|
|
#endif
|
|
draw_line(dispsx, dispsy + 1, dispex, dispey + 1, col);
|
|
//draw_line (0, 0, 800, 600, col);
|
|
}
|
|
|
|
void RenderSurface::create_zbuffer() {
|
|
#ifdef ENABLE_SOFTREND_EMU
|
|
SoftRend::SetupInitialState();
|
|
#endif
|
|
|
|
// Not in opengl, or if we alraedy have one
|
|
if (opengl || zbuffer_priv) return;
|
|
|
|
zbuffer = zbuffer_priv = new uint16[pitch * h];
|
|
}
|
|
|
|
RenderSurface *CreateRenderSurface(uint32 width, uint32 height, uint32 bpp, byte *p) {
|
|
return new RenderSurface(width, height, bpp, p);
|
|
}
|
|
|
|
RenderSurface *CreateRenderSurface(uint32 width, uint32 height, uint32 bpp, sint32 gb) {
|
|
return new RenderSurface(width, height, bpp, gb);
|
|
}
|
|
|
|
RenderSurface *CreateRenderSurface(Graphics::ManagedSurface *surf) {
|
|
return new RenderSurface(surf);
|
|
}
|
|
|
|
RenderSurface *CreateRenderSurface(OpenGL *ogl) {
|
|
return new RenderSurface(ogl);
|
|
}
|
|
|
|
static int getBits(uint mask) {
|
|
int count = 0;
|
|
for (; mask; mask >>= 1)
|
|
++count;
|
|
return count;
|
|
}
|
|
|
|
Graphics::ManagedSurface *RenderSurface::createSurface(int w, int h,
|
|
const Graphics::PixelFormat &format) {
|
|
return new Graphics::ManagedSurface(w, h, format);
|
|
}
|
|
|
|
Graphics::ManagedSurface *RenderSurface::get_sdl_surface() {
|
|
if (_rawSurface == nullptr) {
|
|
_rawSurface = new Graphics::ManagedSurface(w, h,
|
|
Graphics::PixelFormat(bytes_per_pixel, getBits(Rmask), getBits(Gmask),
|
|
getBits(Bmask), 0, Rshift, Gshift, Bshift, 0));
|
|
|
|
byte *dest = (byte *)_rawSurface->getPixels();
|
|
Common::copy(pixels, pixels + (_rawSurface->pitch * _rawSurface->h), dest);
|
|
}
|
|
|
|
return _rawSurface;
|
|
}
|
|
|
|
const unsigned char *RenderSurface::get_pixels() {
|
|
return ((const unsigned char *)pixels);
|
|
}
|
|
|
|
} // End of namespace Nuvie
|
|
} // End of namespace Ultima
|