Initial commit
This commit is contained in:
431
engines/ultima/ultima4/gfx/image.cpp
Normal file
431
engines/ultima/ultima4/gfx/image.cpp
Normal file
@@ -0,0 +1,431 @@
|
||||
/* 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/ultima4/gfx/image.h"
|
||||
#include "ultima/ultima4/gfx/screen.h"
|
||||
#include "ultima/ultima4/core/settings.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
#include "common/system.h"
|
||||
#include "common/list.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/screen.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Image::Image() : _surface(nullptr), _disposeAfterUse(DisposeAfterUse::NO),
|
||||
_paletted(false) {
|
||||
}
|
||||
|
||||
Image *Image::create(int w, int h) {
|
||||
Image *im = new Image();
|
||||
im->createInternal(w, h, Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
return im;
|
||||
}
|
||||
|
||||
Image *Image::create(int w, int h, const Graphics::PixelFormat &format) {
|
||||
Image *im = new Image();
|
||||
im->createInternal(w, h, format);
|
||||
|
||||
return im;
|
||||
}
|
||||
|
||||
void Image::createInternal(int w, int h, const Graphics::PixelFormat &format) {
|
||||
_paletted = format.isCLUT8();
|
||||
_surface = new Graphics::ManagedSurface(w, h, format);
|
||||
_disposeAfterUse = DisposeAfterUse::YES;
|
||||
}
|
||||
|
||||
void Image::blitFrom(const Graphics::Surface &src) {
|
||||
_surface->blitFrom(src);
|
||||
}
|
||||
|
||||
Image *Image::createScreenImage() {
|
||||
Image *screen = new Image();
|
||||
screen->_surface = g_screen;
|
||||
screen->_disposeAfterUse = DisposeAfterUse::NO;
|
||||
screen->_paletted = false;
|
||||
|
||||
return screen;
|
||||
}
|
||||
|
||||
Image *Image::duplicate(Image *image, const Graphics::PixelFormat &format) {
|
||||
bool alphaOn = image->isAlphaOn();
|
||||
Image *im = create(image->width(), image->height(), format);
|
||||
|
||||
if (im->isIndexed())
|
||||
im->setPaletteFromImage(image);
|
||||
|
||||
/* Turn alpha off before blitting to non-screen surfaces */
|
||||
if (alphaOn)
|
||||
image->alphaOff();
|
||||
|
||||
image->drawOn(im, 0, 0);
|
||||
|
||||
if (alphaOn)
|
||||
image->alphaOn();
|
||||
|
||||
im->_backgroundColor = image->_backgroundColor;
|
||||
|
||||
return im;
|
||||
}
|
||||
|
||||
Image::~Image() {
|
||||
if (_disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
void Image::setPalette(const byte *colors, unsigned n_colors) {
|
||||
assertMsg(_paletted, "imageSetPalette called on non-paletted image");
|
||||
_surface->setPalette(colors, 0, n_colors);
|
||||
}
|
||||
|
||||
void Image::setPaletteFromImage(const Image *src) {
|
||||
assertMsg(_paletted && src->_paletted, "imageSetPaletteFromImage called on non-indexed image");
|
||||
|
||||
uint8 srcPal[Graphics::PALETTE_COUNT * 3];
|
||||
src->_surface->grabPalette(srcPal, 0, Graphics::PALETTE_COUNT);
|
||||
_surface->setPalette(srcPal, 0, Graphics::PALETTE_COUNT);
|
||||
}
|
||||
|
||||
RGBA Image::getPaletteColor(int index) {
|
||||
RGBA color = RGBA(0, 0, 0, 0);
|
||||
|
||||
if (_paletted) {
|
||||
uint8 pal[1 * 3];
|
||||
_surface->grabPalette(pal, index, 1);
|
||||
color.r = pal[0];
|
||||
color.g = pal[1];
|
||||
color.b = pal[2];
|
||||
color.a = IM_OPAQUE;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
RGBA Image::setColor(uint8 r, uint8 g, uint8 b, uint8 a) {
|
||||
RGBA color = RGBA(r, g, b, a);
|
||||
return color;
|
||||
}
|
||||
|
||||
bool Image::setFontColor(ColorFG fg, ColorBG bg) {
|
||||
if (!setFontColorFG(fg)) return false;
|
||||
if (!setFontColorBG(bg)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Image::setFontColorFG(ColorFG fg) {
|
||||
switch (fg) {
|
||||
case FG_GREY:
|
||||
if (!setPaletteIndex(TEXT_FG_PRIMARY_INDEX, 153, 153, 153)) return false;
|
||||
if (!setPaletteIndex(TEXT_FG_SECONDARY_INDEX, 102, 102, 102)) return false;
|
||||
if (!setPaletteIndex(TEXT_FG_SHADOW_INDEX, 51, 51, 51)) return false;
|
||||
break;
|
||||
case FG_BLUE:
|
||||
if (!setPaletteIndex(TEXT_FG_PRIMARY_INDEX, 102, 102, 255)) return false;
|
||||
if (!setPaletteIndex(TEXT_FG_SECONDARY_INDEX, 51, 51, 204)) return false;
|
||||
if (!setPaletteIndex(TEXT_FG_SHADOW_INDEX, 51, 51, 51)) return false;
|
||||
break;
|
||||
case FG_PURPLE:
|
||||
if (!setPaletteIndex(TEXT_FG_PRIMARY_INDEX, 255, 102, 255)) return false;
|
||||
if (!setPaletteIndex(TEXT_FG_SECONDARY_INDEX, 204, 51, 204)) return false;
|
||||
if (!setPaletteIndex(TEXT_FG_SHADOW_INDEX, 51, 51, 51)) return false;
|
||||
break;
|
||||
case FG_GREEN:
|
||||
if (!setPaletteIndex(TEXT_FG_PRIMARY_INDEX, 102, 255, 102)) return false;
|
||||
if (!setPaletteIndex(TEXT_FG_SECONDARY_INDEX, 0, 153, 0)) return false;
|
||||
if (!setPaletteIndex(TEXT_FG_SHADOW_INDEX, 51, 51, 51)) return false;
|
||||
break;
|
||||
case FG_RED:
|
||||
if (!setPaletteIndex(TEXT_FG_PRIMARY_INDEX, 255, 102, 102)) return false;
|
||||
if (!setPaletteIndex(TEXT_FG_SECONDARY_INDEX, 204, 51, 51)) return false;
|
||||
if (!setPaletteIndex(TEXT_FG_SHADOW_INDEX, 51, 51, 51)) return false;
|
||||
break;
|
||||
case FG_YELLOW:
|
||||
if (!setPaletteIndex(TEXT_FG_PRIMARY_INDEX, 255, 255, 51)) return false;
|
||||
if (!setPaletteIndex(TEXT_FG_SECONDARY_INDEX, 204, 153, 51)) return false;
|
||||
if (!setPaletteIndex(TEXT_FG_SHADOW_INDEX, 51, 51, 51)) return false;
|
||||
break;
|
||||
default:
|
||||
if (!setPaletteIndex(TEXT_FG_PRIMARY_INDEX, 255, 255, 255)) return false;
|
||||
if (!setPaletteIndex(TEXT_FG_SECONDARY_INDEX, 204, 204, 204)) return false;
|
||||
if (!setPaletteIndex(TEXT_FG_SHADOW_INDEX, 68, 68, 68)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Image::setFontColorBG(ColorBG bg) {
|
||||
switch (bg) {
|
||||
case BG_BRIGHT:
|
||||
if (!setPaletteIndex(TEXT_BG_INDEX, 0, 0, 102))
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
if (!setPaletteIndex(TEXT_BG_INDEX, 0, 0, 0))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Image::setPaletteIndex(uint index, uint8 r, uint8 g, uint8 b) {
|
||||
if (!_paletted)
|
||||
return false;
|
||||
|
||||
const uint8 palette[1 * 3] = {
|
||||
r, g, b
|
||||
};
|
||||
_surface->setPalette(palette, index, 1);
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Image::getTransparentIndex(uint &index) const {
|
||||
if (!_paletted || !_surface->hasTransparentColor())
|
||||
return false;
|
||||
|
||||
index = _surface->getTransparentColor();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Image::initializeToBackgroundColor(RGBA backgroundColor) {
|
||||
if (_paletted)
|
||||
error("initializeToBackgroundColor: Not supported");
|
||||
|
||||
_backgroundColor = backgroundColor;
|
||||
fillRect(0, 0, _surface->w, _surface->h, backgroundColor.r,
|
||||
backgroundColor.g, backgroundColor.b, backgroundColor.a);
|
||||
}
|
||||
|
||||
bool Image::isAlphaOn() const {
|
||||
return !_paletted;
|
||||
}
|
||||
|
||||
void Image::alphaOn() {
|
||||
}
|
||||
|
||||
void Image::alphaOff() {
|
||||
}
|
||||
|
||||
void Image::putPixel(int x, int y, int r, int g, int b, int a) {
|
||||
uint32 color = getColor(r, g, b, a);
|
||||
_surface->setPixel(x, y, color);
|
||||
}
|
||||
|
||||
uint Image::getColor(byte r, byte g, byte b, byte a) {
|
||||
uint color;
|
||||
|
||||
if (_surface->format.bytesPerPixel == 1) {
|
||||
uint8 pal[256 * 3];
|
||||
_surface->grabPalette(pal, 0, 256);
|
||||
for (color = 0; color <= 0xfe; ++color) {
|
||||
byte rv = pal[(color * 3) + 0];
|
||||
byte gv = pal[(color * 3) + 1] & 0xff;
|
||||
byte bv = pal[(color * 3) + 2] & 0xff;
|
||||
if (r == rv && g == gv && b == bv)
|
||||
break;
|
||||
}
|
||||
|
||||
return color;
|
||||
} else {
|
||||
return _surface->format.ARGBToColor(a, r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
void Image::makeBackgroundColorTransparent(int haloSize, int shadowOpacity) {
|
||||
uint32 bgColor = _surface->format.ARGBToColor(
|
||||
static_cast<byte>(_backgroundColor.a),
|
||||
static_cast<byte>(_backgroundColor.r),
|
||||
static_cast<byte>(_backgroundColor.g),
|
||||
static_cast<byte>(_backgroundColor.b)
|
||||
);
|
||||
|
||||
performTransparencyHack(bgColor, 1, 0, haloSize, shadowOpacity);
|
||||
}
|
||||
|
||||
void Image::performTransparencyHack(uint colorValue, uint numFrames,
|
||||
uint currentFrameIndex, uint haloWidth,
|
||||
uint haloOpacityIncrementByPixelDistance) {
|
||||
Common::List<Common::Pair<uint, uint> > opaqueXYs;
|
||||
int x, y;
|
||||
byte t_r, t_g, t_b;
|
||||
|
||||
_surface->format.colorToRGB(colorValue, t_r, t_g, t_b);
|
||||
|
||||
int frameHeight = _surface->h / numFrames;
|
||||
//Min'd so that they never go out of range (>=h)
|
||||
int top = MIN(_surface->h, (int16)(currentFrameIndex * frameHeight));
|
||||
int bottom = MIN(_surface->h, (int16)(top + frameHeight));
|
||||
|
||||
for (y = top; y < bottom; y++) {
|
||||
|
||||
for (x = 0; x < _surface->w; x++) {
|
||||
uint r, g, b, a;
|
||||
getPixel(x, y, r, g, b, a);
|
||||
if (r == t_r &&
|
||||
g == t_g &&
|
||||
b == t_b) {
|
||||
putPixel(x, y, r, g, b, IM_TRANSPARENT);
|
||||
} else {
|
||||
putPixel(x, y, r, g, b, a);
|
||||
if (haloWidth)
|
||||
opaqueXYs.push_back(Common::Pair<uint, uint>(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
int ox, oy;
|
||||
for (const auto &xy : opaqueXYs) {
|
||||
ox = xy.first;
|
||||
oy = xy.second;
|
||||
int span = int(haloWidth);
|
||||
int x_start = MAX(0, ox - span);
|
||||
int x_finish = MIN(int(_surface->w), ox + span + 1);
|
||||
for (x = x_start; x < x_finish; ++x) {
|
||||
int y_start = MAX(int(top), oy - span);
|
||||
int y_finish = MIN(int(bottom), oy + span + 1);
|
||||
for (y = y_start; y < y_finish; ++y) {
|
||||
|
||||
int divisor = 1 + span * 2 - abs(int(ox - x)) - abs(int(oy - y));
|
||||
|
||||
uint r, g, b, a;
|
||||
getPixel(x, y, r, g, b, a);
|
||||
if (a != IM_OPAQUE) {
|
||||
putPixel(x, y, r, g, b, MIN(IM_OPAQUE, a + haloOpacityIncrementByPixelDistance / divisor));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Image::setTransparentIndex(uint index) {
|
||||
if (_paletted)
|
||||
_surface->setTransparentColor(index);
|
||||
}
|
||||
|
||||
void Image::putPixelIndex(int x, int y, uint index) {
|
||||
_surface->setPixel(x, y, index);
|
||||
}
|
||||
|
||||
void Image::fillRect(int x, int y, int w, int h, int r, int g, int b, int a) {
|
||||
uint32 color = getColor(r, g, b, a);
|
||||
_surface->fillRect(Common::Rect(x, y, x + w, y + h), color);
|
||||
}
|
||||
|
||||
void Image::getPixel(int x, int y, uint &r, uint &g, uint &b, uint &a) const {
|
||||
uint index;
|
||||
byte r1, g1, b1, a1;
|
||||
|
||||
index = _surface->getPixel(x, y);
|
||||
|
||||
if (_surface->format.bytesPerPixel == 1) {
|
||||
uint8 pal[1 * 3];
|
||||
_surface->grabPalette(pal, index, 1);
|
||||
r = pal[0];
|
||||
g = pal[1];
|
||||
b = pal[2];
|
||||
a = IM_OPAQUE;
|
||||
} else {
|
||||
_surface->format.colorToARGB(index, a1, r1, g1, b1);
|
||||
r = r1;
|
||||
g = g1;
|
||||
b = b1;
|
||||
a = a1;
|
||||
}
|
||||
}
|
||||
|
||||
void Image::getPixelIndex(int x, int y, uint &index) const {
|
||||
index = _surface->getPixel(x, y);
|
||||
}
|
||||
|
||||
Graphics::ManagedSurface *Image::getSurface(Image *d) const {
|
||||
if (d)
|
||||
return d->_surface;
|
||||
|
||||
return g_screen;
|
||||
}
|
||||
|
||||
void Image::drawOn(Image *d, int x, int y) const {
|
||||
Graphics::ManagedSurface *destSurface = getSurface(d);
|
||||
destSurface->blitFrom(*_surface, Common::Point(x, y));
|
||||
}
|
||||
|
||||
void Image::drawSubRectOn(Image *d, int x, int y, int rx, int ry, int rw, int rh) const {
|
||||
Graphics::ManagedSurface *destSurface = getSurface(d);
|
||||
|
||||
Common::Rect srcRect(rx, ry, MIN(rx + rw, (int)_surface->w), MIN(ry + rh, (int)_surface->h));
|
||||
Common::Point destPos(x, y);
|
||||
|
||||
// Handle when the source rect is off the surface
|
||||
if (srcRect.left < 0) {
|
||||
destPos.x += -srcRect.left;
|
||||
srcRect.left = 0;
|
||||
}
|
||||
|
||||
if (srcRect.top < 0) {
|
||||
destPos.y += -srcRect.top;
|
||||
srcRect.top = 0;
|
||||
}
|
||||
|
||||
if (srcRect.isValidRect())
|
||||
// Blit the surface
|
||||
destSurface->blitFrom(*_surface, srcRect, destPos);
|
||||
}
|
||||
|
||||
void Image::drawSubRectInvertedOn(Image *d, int x, int y, int rx, int ry, int rw, int rh) const {
|
||||
Graphics::ManagedSurface *destSurface = getSurface(d);
|
||||
int i;
|
||||
Common::Rect src;
|
||||
Common::Point destPos;
|
||||
|
||||
for (i = 0; i < rh; i++) {
|
||||
src.left = rx;
|
||||
src.top = ry + i;
|
||||
src.right = rx + rw;
|
||||
src.bottom = ry + i + 1;
|
||||
|
||||
destPos.x = x;
|
||||
destPos.y = y + rh - i - 1;
|
||||
|
||||
destSurface->blitFrom(*_surface, src, destPos);
|
||||
}
|
||||
}
|
||||
|
||||
void Image::dump() {
|
||||
g_screen->blitFrom(*_surface, Common::Point(0, 0));
|
||||
g_screen->update();
|
||||
}
|
||||
|
||||
void Image::drawHighlighted() {
|
||||
RGBA c;
|
||||
for (int i = 0; i < _surface->h; i++) {
|
||||
for (int j = 0; j < _surface->w; j++) {
|
||||
getPixel(j, i, c.r, c.g, c.b, c.a);
|
||||
putPixel(j, i, 0xff - c.r, 0xff - c.g, 0xff - c.b, c.a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
270
engines/ultima/ultima4/gfx/image.h
Normal file
270
engines/ultima/ultima4/gfx/image.h
Normal file
@@ -0,0 +1,270 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_GFX_IMAGE_H
|
||||
#define ULTIMA4_GFX_IMAGE_H
|
||||
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
#include "ultima/ultima4/gfx/textcolor.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
typedef Graphics::ManagedSurface *BackendSurface;
|
||||
|
||||
#define DARK_GRAY_HALO RGBA(14,15,16,255)
|
||||
|
||||
struct RGBA {
|
||||
RGBA(int red, int green, int blue, int alpha) : r(red), g(green), b(blue), a(alpha) {}
|
||||
RGBA() : r(0), g(0), b(0), a(255) {}
|
||||
uint r, g, b, a;
|
||||
|
||||
operator uint32() const {
|
||||
return r | (g << 8) | (b << 16) | (0xff << 24);
|
||||
}
|
||||
};
|
||||
bool operator==(const RGBA &lhs, const RGBA &rhs);
|
||||
|
||||
class Image;
|
||||
|
||||
struct SubImage : public Common::Rect {
|
||||
Common::String _name;
|
||||
Common::String _srcImageName;
|
||||
};
|
||||
|
||||
#define IM_OPAQUE (uint) 255
|
||||
#define IM_TRANSPARENT 0
|
||||
|
||||
/**
|
||||
* A simple image object that can be drawn and read/written to at the
|
||||
* pixel level.
|
||||
* @todo
|
||||
* <ul>
|
||||
* <li>drawing methods should be pushed to Drawable subclass</li>
|
||||
* </ul>
|
||||
*/
|
||||
class Image {
|
||||
private:
|
||||
Graphics::ManagedSurface *_surface;
|
||||
DisposeAfterUse::Flag _disposeAfterUse;
|
||||
bool _paletted;
|
||||
RGBA _backgroundColor;
|
||||
Image(); /* use create method to construct images */
|
||||
|
||||
// disallow assignments, copy construction
|
||||
Image(const Image &);
|
||||
const Image &operator=(const Image &);
|
||||
|
||||
Graphics::ManagedSurface *getSurface(Image *d) const;
|
||||
|
||||
uint getColor(byte r, byte g, byte b, byte a);
|
||||
public:
|
||||
/**
|
||||
* Creates a new palette based image. Scale is stored to allow drawing
|
||||
* using U4 (320x200) coordinates, regardless of the actual image scale.
|
||||
*/
|
||||
static Image *create(int w, int h);
|
||||
|
||||
/**
|
||||
* Creates a new RGB image. Scale is stored to allow drawing using U4
|
||||
* (320x200) coordinates, regardless of the actual image scale.
|
||||
*/
|
||||
static Image *create(int w, int h, const Graphics::PixelFormat &format);
|
||||
|
||||
/**
|
||||
* Create a special purpose image the represents the whole screen.
|
||||
*/
|
||||
static Image *createScreenImage();
|
||||
|
||||
/**
|
||||
* Creates a duplicate of another image
|
||||
*/
|
||||
static Image *duplicate(Image *image, const Graphics::PixelFormat &format);
|
||||
|
||||
/**
|
||||
* Frees the image.
|
||||
*/
|
||||
~Image();
|
||||
|
||||
void createInternal(int w, int h, const Graphics::PixelFormat &format);
|
||||
|
||||
/* palette handling */
|
||||
/**
|
||||
* Sets the palette
|
||||
*/
|
||||
void setPalette(const byte *colors, unsigned n_colors);
|
||||
|
||||
/**
|
||||
* Copies the palette from another image.
|
||||
*/
|
||||
void setPaletteFromImage(const Image *src);
|
||||
bool getTransparentIndex(uint &index) const;
|
||||
void performTransparencyHack(uint colorValue, uint numFrames, uint currentFrameIndex, uint haloWidth, uint haloOpacityIncrementByPixelDistance);
|
||||
void setTransparentIndex(uint index);
|
||||
|
||||
/**
|
||||
* Sets the specified font colors
|
||||
*/
|
||||
bool setFontColor(ColorFG fg, ColorBG bg);
|
||||
|
||||
/**
|
||||
* Sets the specified font colors
|
||||
*/
|
||||
bool setFontColorFG(ColorFG fg);
|
||||
|
||||
/**
|
||||
* Sets the specified font colors
|
||||
*/
|
||||
bool setFontColorBG(ColorBG bg);
|
||||
|
||||
/**
|
||||
* Returns the color of the specified palette index
|
||||
*/
|
||||
RGBA getPaletteColor(int index); // returns the color of the specified palette index
|
||||
|
||||
/**
|
||||
* Sets the specified palette index to the specified RGB color
|
||||
*/
|
||||
bool setPaletteIndex(uint index, uint8 r, uint8 g, uint8 b);
|
||||
|
||||
RGBA setColor(uint8 r, uint8 g, uint8 b, uint8 a = IM_OPAQUE);
|
||||
|
||||
|
||||
/* alpha handling */
|
||||
bool isAlphaOn() const;
|
||||
void alphaOn();
|
||||
void alphaOff();
|
||||
|
||||
|
||||
/* Will clear the image to the background color, and set the internal backgroundColor variable */
|
||||
void initializeToBackgroundColor(RGBA backgroundColor = DARK_GRAY_HALO);
|
||||
/* Will make the pixels that match the background color disappear, with a blur halo */
|
||||
void makeBackgroundColorTransparent(int haloSize = 0, int shadowOpacity = 255);
|
||||
|
||||
|
||||
//void finalizeAlphaSurface(RGBA * key = nullptr);
|
||||
|
||||
/* writing to image */
|
||||
|
||||
/**
|
||||
* Sets the color of a single pixel.
|
||||
*/
|
||||
void putPixel(int x, int y, int r, int g, int b, int a); //TODO Consider using &
|
||||
|
||||
/**
|
||||
* Sets the palette index of a single pixel. If the image is in
|
||||
* indexed mode, then the index is simply the palette entry number.
|
||||
* If the image is RGB, it is a packed RGB triplet.
|
||||
*/
|
||||
void putPixelIndex(int x, int y, uint index);
|
||||
|
||||
/**
|
||||
* Fills a rectangle in the image with a given color.
|
||||
*/
|
||||
void fillRect(int x, int y, int w, int h, int r, int g, int b, int a = IM_OPAQUE);
|
||||
|
||||
void blitFrom(const Graphics::Surface &src);
|
||||
|
||||
/* reading from image */
|
||||
/**
|
||||
* Gets the color of a single pixel.
|
||||
*/
|
||||
void getPixel(int x, int y, uint &r, uint &g, uint &b, uint &a) const;
|
||||
|
||||
/**
|
||||
* Gets the palette index of a single pixel. If the image is in
|
||||
* indexed mode, then the index is simply the palette entry number.
|
||||
* If the image is RGB, it is a packed RGB triplet.
|
||||
*/
|
||||
void getPixelIndex(int x, int y, uint &index) const;
|
||||
|
||||
/* image drawing methods */
|
||||
/**
|
||||
* Draws the entire image onto the screen at the given offset.
|
||||
*/
|
||||
void draw(int x, int y) const {
|
||||
drawOn(nullptr, x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a piece of the image onto the screen at the given offset.
|
||||
* The area of the image to draw is defined by the rectangle rx, ry,
|
||||
* rw, rh.
|
||||
*/
|
||||
void drawSubRect(int x, int y, int rx, int ry, int rw, int rh) const {
|
||||
drawSubRectOn(nullptr, x, y, rx, ry, rw, rh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a piece of the image onto the screen at the given offset, inverted.
|
||||
* The area of the image to draw is defined by the rectangle rx, ry,
|
||||
* rw, rh.
|
||||
*/
|
||||
void drawSubRectInverted(int x, int y, int rx, int ry, int rw, int rh) const {
|
||||
drawSubRectInvertedOn(nullptr, x, y, rx, ry, rw, rh);
|
||||
}
|
||||
|
||||
/* image drawing methods for drawing onto another image instead of the screen */
|
||||
/**
|
||||
* Draws the image onto another image.
|
||||
*/
|
||||
void drawOn(Image *d, int x, int y) const;
|
||||
|
||||
/**
|
||||
* Draws a piece of the image onto another image.
|
||||
*/
|
||||
void drawSubRectOn(Image *d, int x, int y, int rx, int ry, int rw, int rh) const;
|
||||
|
||||
/**
|
||||
* Draws a piece of the image onto another image, inverted.
|
||||
*/
|
||||
void drawSubRectInvertedOn(Image *d, int x, int y, int rx, int ry, int rw, int rh) const;
|
||||
|
||||
int width() const {
|
||||
return _surface->w;
|
||||
}
|
||||
int height() const {
|
||||
return _surface->h;
|
||||
}
|
||||
Graphics::PixelFormat format() const {
|
||||
return _surface->format;
|
||||
}
|
||||
bool isIndexed() const {
|
||||
return _paletted;
|
||||
}
|
||||
BackendSurface getSurface() {
|
||||
return _surface;
|
||||
}
|
||||
|
||||
void drawHighlighted();
|
||||
|
||||
/**
|
||||
* Debug method that dumps a given image directly to the screen
|
||||
* and does a screen update. Useful for validating images are correct
|
||||
*/
|
||||
void dump();
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
107
engines/ultima/ultima4/gfx/imageloader.cpp
Normal file
107
engines/ultima/ultima4/gfx/imageloader.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/* 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/ultima4/gfx/imageloader.h"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
U4ImageDecoder::U4ImageDecoder(int width, int height, int bpp) : _palette(0) {
|
||||
_width = width;
|
||||
_height = height;
|
||||
_bpp = bpp;
|
||||
_surface = nullptr;
|
||||
}
|
||||
|
||||
U4ImageDecoder::~U4ImageDecoder() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
void U4ImageDecoder::destroy() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
_surface = nullptr;
|
||||
}
|
||||
|
||||
_palette.clear();
|
||||
}
|
||||
|
||||
void U4ImageDecoder::setFromRawData(const byte *rawData) {
|
||||
int x, y;
|
||||
|
||||
switch (_bpp) {
|
||||
case 32:
|
||||
case 24:
|
||||
case 8:
|
||||
_surface->copyRectToSurface(rawData, (_width * _bpp) / 8, 0, 0, _width, _height);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
for (y = 0; y < _height; y++) {
|
||||
for (x = 0; x < _width; x += 2) {
|
||||
_surface->setPixel(x, y, rawData[(y * _width + x) / 2] >> 4);
|
||||
_surface->setPixel(x + 1, y, rawData[(y * _width + x) / 2] & 0x0f);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
for (y = 0; y < _height; y++) {
|
||||
for (x = 0; x < _width; x += 8) {
|
||||
_surface->setPixel(x + 0, y, (rawData[(y * _width + x) / 8] >> 7) & 0x01);
|
||||
_surface->setPixel(x + 1, y, (rawData[(y * _width + x) / 8] >> 6) & 0x01);
|
||||
_surface->setPixel(x + 2, y, (rawData[(y * _width + x) / 8] >> 5) & 0x01);
|
||||
_surface->setPixel(x + 3, y, (rawData[(y * _width + x) / 8] >> 4) & 0x01);
|
||||
_surface->setPixel(x + 4, y, (rawData[(y * _width + x) / 8] >> 3) & 0x01);
|
||||
_surface->setPixel(x + 5, y, (rawData[(y * _width + x) / 8] >> 2) & 0x01);
|
||||
_surface->setPixel(x + 6, y, (rawData[(y * _width + x) / 8] >> 1) & 0x01);
|
||||
_surface->setPixel(x + 7, y, (rawData[(y * _width + x) / 8] >> 0) & 0x01);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
error("invalid bits-per-pixel (bpp): %d", _bpp);
|
||||
}
|
||||
}
|
||||
|
||||
Graphics::PixelFormat U4ImageDecoder::getPixelFormatForBpp() const {
|
||||
switch (_bpp) {
|
||||
case 1:
|
||||
case 4:
|
||||
case 8:
|
||||
return Graphics::PixelFormat::createFormatCLUT8();
|
||||
case 24:
|
||||
return Graphics::PixelFormat::createFormatRGB24();
|
||||
case 32:
|
||||
return Graphics::PixelFormat::createFormatRGBA32();
|
||||
default:
|
||||
error("invalid bits-per-pixel (bpp): %d", _bpp);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
68
engines/ultima/ultima4/gfx/imageloader.h
Normal file
68
engines/ultima/ultima4/gfx/imageloader.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_GFX_IMAGELOADER_H
|
||||
#define ULTIMA4_GFX_IMAGELOADER_H
|
||||
|
||||
#include "graphics/palette.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "image/image_decoder.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* A common base for all Ultima 4 image loaders.
|
||||
*/
|
||||
class U4ImageDecoder : public ::Image::ImageDecoder {
|
||||
public:
|
||||
U4ImageDecoder(int width, int height, int bpp);
|
||||
virtual ~U4ImageDecoder();
|
||||
|
||||
// ImageDecoder API
|
||||
void destroy() override;
|
||||
const Graphics::Surface *getSurface() const override { return _surface; }
|
||||
const Graphics::Palette &getPalette() const override { return _palette; }
|
||||
|
||||
protected:
|
||||
Graphics::Surface *_surface;
|
||||
Graphics::Palette _palette;
|
||||
int _width, _height, _bpp;
|
||||
|
||||
/**
|
||||
* Fill in the image pixel data from an uncompressed string of bytes.
|
||||
*/
|
||||
void setFromRawData(const byte *rawData);
|
||||
|
||||
/**
|
||||
* Get the expected pixel format based on the value of _bpp.
|
||||
*/
|
||||
Graphics::PixelFormat getPixelFormatForBpp() const;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
87
engines/ultima/ultima4/gfx/imageloader_fmtowns.cpp
Normal file
87
engines/ultima/ultima4/gfx/imageloader_fmtowns.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
/* 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/ultima4/core/utils.h"
|
||||
#include "ultima/ultima4/gfx/imageloader_fmtowns.h"
|
||||
#include "ultima/ultima4/gfx/imageloader_u4.h"
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
bool FMTOWNSImageDecoder::loadStream(Common::SeekableReadStream &stream) {
|
||||
destroy();
|
||||
|
||||
if (_width == -1 || _height == -1 || _bpp == -1) {
|
||||
error("dimensions not set for fmtowns image");
|
||||
}
|
||||
|
||||
assertMsg((_bpp == 16) | (_bpp == 4), "invalid bpp: %d", _bpp);
|
||||
|
||||
int rawLen = stream.size() - _offset;
|
||||
int requiredLength = (_width * _height * _bpp / 8);
|
||||
if (rawLen < requiredLength) {
|
||||
warning("FMTOWNS Image of size %d does not fit anticipated size %d", rawLen, requiredLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.seek(_offset, 0);
|
||||
byte *raw = (byte *) malloc(rawLen);
|
||||
stream.read(raw, rawLen);
|
||||
|
||||
|
||||
if (_bpp == 4) {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
setFromRawData(raw);
|
||||
|
||||
U4PaletteLoader pal;
|
||||
_palette.resize(16, false);
|
||||
_palette.set(pal.loadEgaPalette(), 0, 16);
|
||||
}
|
||||
|
||||
|
||||
if (_bpp == 16) {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(_width, _height, Graphics::PixelFormat(2, 5, 5, 5, 1, 5, 10, 0, 15));
|
||||
|
||||
uint16 *dst = (uint16 *)_surface->getPixels();
|
||||
const uint16 *src = (const uint16 *)raw;
|
||||
|
||||
for (int y = 0; y < _height; y++) {
|
||||
for (int x = 0; x < _width; x++) {
|
||||
dst[x] = FROM_LE_16(*src++) ^ 0x8000;
|
||||
}
|
||||
|
||||
dst = (uint16 *)((uint8 *)dst + _surface->pitch);
|
||||
}
|
||||
}
|
||||
|
||||
free(raw);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
43
engines/ultima/ultima4/gfx/imageloader_fmtowns.h
Normal file
43
engines/ultima/ultima4/gfx/imageloader_fmtowns.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_GFX_IMAGELOADER_FMTOWNS_H_
|
||||
#define ULTIMA4_GFX_IMAGELOADER_FMTOWNS_H_
|
||||
|
||||
#include "ultima/ultima4/gfx/imageloader.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class FMTOWNSImageDecoder : public U4ImageDecoder {
|
||||
protected:
|
||||
int _offset;
|
||||
public:
|
||||
FMTOWNSImageDecoder(int width, int height, int bpp, int offset)
|
||||
: U4ImageDecoder(width, height, bpp), _offset(offset) {}
|
||||
|
||||
bool loadStream(Common::SeekableReadStream &stream) override;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
230
engines/ultima/ultima4/gfx/imageloader_u4.cpp
Normal file
230
engines/ultima/ultima4/gfx/imageloader_u4.cpp
Normal file
@@ -0,0 +1,230 @@
|
||||
/* 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/ultima4/core/config.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "ultima/ultima4/core/lzw/u4decode.h"
|
||||
#include "ultima/ultima4/gfx/imageloader_u4.h"
|
||||
#include "ultima/ultima4/filesys/rle.h"
|
||||
|
||||
#include "common/file.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "graphics/palette.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
const byte U4PaletteLoader::_bwPalette[2 * 3] = {
|
||||
0, 0, 0,
|
||||
255, 255, 255
|
||||
};
|
||||
byte *U4PaletteLoader::_egaPalette = nullptr;
|
||||
byte *U4PaletteLoader::_vgaPalette = nullptr;
|
||||
|
||||
bool U4RawImageDecoder::loadStream(Common::SeekableReadStream &stream) {
|
||||
if (_width == -1 || _height == -1 || _bpp == -1) {
|
||||
error("dimensions not set for u4raw image");
|
||||
}
|
||||
|
||||
assertMsg(_bpp == 1 || _bpp == 4 || _bpp == 8 || _bpp == 24 || _bpp == 32, "invalid bpp: %d", _bpp);
|
||||
|
||||
long rawLen = stream.size();
|
||||
byte *raw = (byte *)malloc(rawLen);
|
||||
stream.read(raw, rawLen);
|
||||
|
||||
long requiredLength = (_width * _height * _bpp / 8);
|
||||
if (rawLen < requiredLength) {
|
||||
if (raw)
|
||||
free(raw);
|
||||
warning("u4Raw Image of size %ld does not fit anticipated size %ld", rawLen, requiredLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(_width, _height, getPixelFormatForBpp());
|
||||
|
||||
U4PaletteLoader paletteLoader;
|
||||
if (_bpp == 8) {
|
||||
_palette.resize(256, false);
|
||||
_palette.set(paletteLoader.loadVgaPalette(), 0, 256);
|
||||
} else if (_bpp == 4) {
|
||||
_palette.resize(16, false);
|
||||
_palette.set(paletteLoader.loadEgaPalette(), 0, 16);
|
||||
} else if (_bpp == 1) {
|
||||
_palette.resize(2, false);
|
||||
_palette.set(paletteLoader.loadBWPalette(), 0, 2);
|
||||
}
|
||||
|
||||
setFromRawData(raw);
|
||||
|
||||
free(raw);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads in the rle-compressed image and apply the standard U4 16 or
|
||||
* 256 color palette.
|
||||
*/
|
||||
bool U4RleImageDecoder::loadStream(Common::SeekableReadStream &stream) {
|
||||
if (_width == -1 || _height == -1 || _bpp == -1) {
|
||||
error("dimensions not set for u4rle image");
|
||||
}
|
||||
|
||||
assertMsg(_bpp == 1 || _bpp == 4 || _bpp == 8 || _bpp == 24 || _bpp == 32, "invalid bpp: %d", _bpp);
|
||||
|
||||
long compressedLen = stream.size();
|
||||
byte *compressed = (byte *) malloc(compressedLen);
|
||||
stream.read(compressed, compressedLen);
|
||||
|
||||
byte *raw = nullptr;
|
||||
long rawLen = rleDecompressMemory(compressed, compressedLen, (void **) &raw);
|
||||
free(compressed);
|
||||
|
||||
if (rawLen != (_width * _height * _bpp / 8)) {
|
||||
if (raw)
|
||||
free(raw);
|
||||
return false;
|
||||
}
|
||||
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(_width, _height, getPixelFormatForBpp());
|
||||
|
||||
U4PaletteLoader paletteLoader;
|
||||
if (_bpp == 8) {
|
||||
_palette.resize(256, false);
|
||||
_palette.set(paletteLoader.loadVgaPalette(), 0, 256);
|
||||
} else if (_bpp == 4) {
|
||||
_palette.resize(16, false);
|
||||
_palette.set(paletteLoader.loadEgaPalette(), 0, 16);
|
||||
} else if (_bpp == 1) {
|
||||
_palette.resize(2, false);
|
||||
_palette.set(paletteLoader.loadBWPalette(), 0, 2);
|
||||
}
|
||||
|
||||
setFromRawData(raw);
|
||||
|
||||
free(raw);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads in the lzw-compressed image and apply the standard U4 16 or
|
||||
* 256 color palette.
|
||||
*/
|
||||
bool U4LzwImageDecoder::loadStream(Common::SeekableReadStream &stream) {
|
||||
if (_width == -1 || _height == -1 || _bpp == -1) {
|
||||
error("dimensions not set for u4lzw image");
|
||||
}
|
||||
|
||||
assertMsg(_bpp == 1 || _bpp == 4 || _bpp == 8 || _bpp == 24 || _bpp == 32, "invalid bpp: %d", _bpp);
|
||||
|
||||
long compressedLen = stream.size();
|
||||
byte *compressed = (byte *) malloc(compressedLen);
|
||||
stream.read(compressed, compressedLen);
|
||||
|
||||
byte *raw = nullptr;
|
||||
long rawLen = LZW::decompress_u4_memory(compressed, compressedLen, (void **) &raw);
|
||||
free(compressed);
|
||||
|
||||
if (rawLen != (_width * _height * _bpp / 8)) {
|
||||
if (raw)
|
||||
free(raw);
|
||||
return false;
|
||||
}
|
||||
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->create(_width, _height, getPixelFormatForBpp());
|
||||
|
||||
U4PaletteLoader paletteLoader;
|
||||
if (_bpp == 8) {
|
||||
_palette.resize(256, false);
|
||||
_palette.set(paletteLoader.loadVgaPalette(), 0, 256);
|
||||
} else if (_bpp == 4) {
|
||||
_palette.resize(16, false);
|
||||
_palette.set(paletteLoader.loadEgaPalette(), 0, 16);
|
||||
} else if (_bpp == 1) {
|
||||
_palette.resize(2, false);
|
||||
_palette.set(paletteLoader.loadBWPalette(), 0, 2);
|
||||
}
|
||||
|
||||
setFromRawData(raw);
|
||||
|
||||
free(raw);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Loads a simple black & white palette
|
||||
*/
|
||||
const byte *U4PaletteLoader::loadBWPalette() {
|
||||
return _bwPalette;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the basic EGA palette from egaPalette.xml
|
||||
*/
|
||||
const byte *U4PaletteLoader::loadEgaPalette() {
|
||||
if (_egaPalette == nullptr) {
|
||||
int index = 0;
|
||||
const Config *config = Config::getInstance();
|
||||
|
||||
_egaPalette = new byte[16 * 3];
|
||||
|
||||
Std::vector<ConfigElement> paletteConf = config->getElement("egaPalette").getChildren();
|
||||
for (const auto &i : paletteConf) {
|
||||
|
||||
if (i.getName() != "color")
|
||||
continue;
|
||||
|
||||
_egaPalette[index++] = i.getInt("red");
|
||||
_egaPalette[index++] = i.getInt("green");
|
||||
_egaPalette[index++] = i.getInt("blue");
|
||||
}
|
||||
}
|
||||
return _egaPalette;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the 256 color VGA palette from a file.
|
||||
*/
|
||||
const byte *U4PaletteLoader::loadVgaPalette() {
|
||||
if (_vgaPalette == nullptr) {
|
||||
Common::File pal;
|
||||
if (!pal.open("u4vga.pal"))
|
||||
return nullptr;
|
||||
|
||||
_vgaPalette = new byte[256 * 3];
|
||||
pal.read(_vgaPalette, 256 * 3);
|
||||
|
||||
for (int i = 0; i < 256 * 3; i++) {
|
||||
_vgaPalette[i] = PALETTE_6BIT_TO_8BIT(_vgaPalette[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return _vgaPalette;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
86
engines/ultima/ultima4/gfx/imageloader_u4.h
Normal file
86
engines/ultima/ultima4/gfx/imageloader_u4.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_GFX_IMAGELOADER_U4_H
|
||||
#define ULTIMA4_GFX_IMAGELOADER_U4_H
|
||||
|
||||
#include "ultima/ultima4/gfx/imageloader.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* Decoder for U4 raw images. Raw images are just an uncompressed
|
||||
* stream of pixel data with no palette information (e.g. shapes.ega,
|
||||
* charset.ega). This decoder handles the original 4-bit images, as
|
||||
* well as the 8-bit VGA upgrade images.
|
||||
*/
|
||||
class U4RawImageDecoder : public U4ImageDecoder {
|
||||
public:
|
||||
U4RawImageDecoder(int width, int height, int bpp)
|
||||
: U4ImageDecoder(width, height, bpp) {}
|
||||
|
||||
bool loadStream(Common::SeekableReadStream &stream) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decoder for U4 images with RLE compression. Like raw images, the
|
||||
* data is just a stream of pixel data with no palette information
|
||||
* (e.g. start.ega, rune_*.ega). This decoder handles the original
|
||||
* 4-bit images, as well as the 8-bit VGA upgrade images.
|
||||
*/
|
||||
class U4RleImageDecoder : public U4ImageDecoder {
|
||||
public:
|
||||
U4RleImageDecoder(int width, int height, int bpp)
|
||||
: U4ImageDecoder(width, height, bpp) {}
|
||||
|
||||
bool loadStream(Common::SeekableReadStream &stream) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decoder for U4 images with LZW compression. Like raw images, the
|
||||
* data is just a stream of pixel data with no palette information
|
||||
* (e.g. title.ega, tree.ega). This decoder handles the original 4-bit
|
||||
* images, as well as the 8-bit VGA upgrade images.
|
||||
*/
|
||||
class U4LzwImageDecoder : public U4ImageDecoder {
|
||||
public:
|
||||
U4LzwImageDecoder(int width, int height, int bpp)
|
||||
: U4ImageDecoder(width, height, bpp) {}
|
||||
|
||||
bool loadStream(Common::SeekableReadStream &stream) override;
|
||||
};
|
||||
|
||||
class U4PaletteLoader {
|
||||
static const byte _bwPalette[2 * 3];
|
||||
static byte *_egaPalette;
|
||||
static byte *_vgaPalette;
|
||||
|
||||
public:
|
||||
const byte *loadBWPalette();
|
||||
const byte *loadEgaPalette();
|
||||
const byte *loadVgaPalette();
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
733
engines/ultima/ultima4/gfx/imagemgr.cpp
Normal file
733
engines/ultima/ultima4/gfx/imagemgr.cpp
Normal file
@@ -0,0 +1,733 @@
|
||||
/* 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/ultima4/gfx/image.h"
|
||||
#include "ultima/ultima4/gfx/imageloader_u4.h"
|
||||
#include "ultima/ultima4/gfx/imageloader_fmtowns.h"
|
||||
#include "ultima/ultima4/gfx/imagemgr.h"
|
||||
#include "ultima/ultima4/controllers/intro_controller.h"
|
||||
#include "ultima/ultima4/core/config.h"
|
||||
#include "ultima/ultima4/core/settings.h"
|
||||
#include "ultima/ultima4/ultima4.h"
|
||||
|
||||
#include "image/png.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
bool ImageInfo::hasBlackBackground() {
|
||||
return this->_filetype == "image/x-u4raw";
|
||||
}
|
||||
|
||||
|
||||
class ImageSet {
|
||||
public:
|
||||
~ImageSet();
|
||||
|
||||
Common::String _name;
|
||||
Common::String _location;
|
||||
Common::String _extends;
|
||||
Common::HashMap<Common::String, ImageInfo *> _info;
|
||||
};
|
||||
|
||||
ImageMgr *ImageMgr::_instance = nullptr;
|
||||
|
||||
ImageMgr *ImageMgr::getInstance() {
|
||||
if (_instance == nullptr) {
|
||||
_instance = new ImageMgr();
|
||||
_instance->init();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
|
||||
void ImageMgr::destroy() {
|
||||
if (_instance != nullptr) {
|
||||
delete _instance;
|
||||
_instance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ImageMgr::ImageMgr() : _baseSet(nullptr), _abyssData(nullptr) {
|
||||
settings.addObserver(this);
|
||||
}
|
||||
|
||||
ImageMgr::~ImageMgr() {
|
||||
settings.deleteObserver(this);
|
||||
|
||||
for (auto &i : _imageSets)
|
||||
delete i._value;
|
||||
|
||||
delete[] _abyssData;
|
||||
}
|
||||
|
||||
void ImageMgr::init() {
|
||||
/*
|
||||
* register the "screen" image representing the entire screen
|
||||
*/
|
||||
Image *screen = Image::createScreenImage();
|
||||
|
||||
_screenInfo._name = "screen";
|
||||
_screenInfo._filename = "";
|
||||
_screenInfo._width = screen->width();
|
||||
_screenInfo._height = screen->height();
|
||||
_screenInfo._depth = 0;
|
||||
_screenInfo._prescale = 0;
|
||||
_screenInfo._filetype = "";
|
||||
_screenInfo._tiles = 0;
|
||||
_screenInfo._introOnly = false;
|
||||
_screenInfo._transparentIndex = -1;
|
||||
_screenInfo._xu4Graphic = false;
|
||||
_screenInfo._fixup = FIXUP_NONE;
|
||||
_screenInfo._image = screen;
|
||||
|
||||
/*
|
||||
* register all the images declared in the config files
|
||||
*/
|
||||
const Config *config = Config::getInstance();
|
||||
Std::vector<ConfigElement> graphicsConf = config->getElement("graphics").getChildren();
|
||||
for (const auto &conf : graphicsConf) {
|
||||
if (conf.getName() == "imageset") {
|
||||
ImageSet *set = loadImageSetFromConf(conf);
|
||||
_imageSets[set->_name] = set;
|
||||
|
||||
// all image sets include the "screen" image
|
||||
set->_info[_screenInfo._name] = &_screenInfo;
|
||||
}
|
||||
}
|
||||
|
||||
_imageSetNames.clear();
|
||||
for (const auto &set : _imageSets)
|
||||
_imageSetNames.push_back(set._key);
|
||||
|
||||
update(&settings);
|
||||
}
|
||||
|
||||
ImageSet *ImageMgr::loadImageSetFromConf(const ConfigElement &conf) {
|
||||
ImageSet *set;
|
||||
|
||||
set = new ImageSet();
|
||||
set->_name = conf.getString("name");
|
||||
set->_location = conf.getString("location");
|
||||
set->_extends = conf.getString("extends");
|
||||
|
||||
Std::vector<ConfigElement> children = conf.getChildren();
|
||||
for (const auto &i : children) {
|
||||
if (i.getName() == "image") {
|
||||
ImageInfo *info = loadImageInfoFromConf(i);
|
||||
if (set->_info.contains(info->_name))
|
||||
delete set->_info[info->_name];
|
||||
set->_info[info->_name] = info;
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
ImageInfo *ImageMgr::loadImageInfoFromConf(const ConfigElement &conf) {
|
||||
ImageInfo *info;
|
||||
static const char *const fixupEnumStrings[] = { "none", "intro", "abyss", "abacus", "dungns", "blackTransparencyHack", "fmtownsscreen", nullptr };
|
||||
|
||||
info = new ImageInfo();
|
||||
info->_name = conf.getString("name");
|
||||
info->_filename = conf.getString("filename");
|
||||
info->_width = conf.getInt("width", -1);
|
||||
info->_height = conf.getInt("height", -1);
|
||||
info->_depth = conf.getInt("depth", -1);
|
||||
info->_prescale = conf.getInt("prescale");
|
||||
info->_filetype = conf.getString("filetype");
|
||||
info->_tiles = conf.getInt("tiles");
|
||||
info->_introOnly = conf.getBool("introOnly");
|
||||
info->_transparentIndex = conf.getInt("transparentIndex", -1);
|
||||
|
||||
info->_xu4Graphic = conf.getBool("xu4Graphic");
|
||||
info->_fixup = static_cast<ImageFixup>(conf.getEnum("fixup", fixupEnumStrings));
|
||||
info->_image = nullptr;
|
||||
|
||||
Std::vector<ConfigElement> children = conf.getChildren();
|
||||
for (const auto &i : children) {
|
||||
if (i.getName() == "subimage") {
|
||||
SubImage *subimage = loadSubImageFromConf(info, i);
|
||||
info->_subImages[subimage->_name] = subimage;
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
SubImage *ImageMgr::loadSubImageFromConf(const ImageInfo *info, const ConfigElement &conf) {
|
||||
SubImage *subimage;
|
||||
static int x = 0,
|
||||
y = 0,
|
||||
last_width = 0,
|
||||
last_height = 0;
|
||||
|
||||
subimage = new SubImage();
|
||||
subimage->_name = conf.getString("name");
|
||||
subimage->setWidth(conf.getInt("width"));
|
||||
subimage->setHeight(conf.getInt("height"));
|
||||
subimage->_srcImageName = info->_name;
|
||||
if (conf.exists("x") && conf.exists("y")) {
|
||||
x = conf.getInt("x");
|
||||
y = conf.getInt("y");
|
||||
subimage->moveTo(x, y);
|
||||
} else {
|
||||
// Automatically increment our position through the base image
|
||||
x += last_width;
|
||||
if (x >= last_width) {
|
||||
x = 0;
|
||||
y += last_height;
|
||||
}
|
||||
|
||||
subimage->moveTo(x, y);
|
||||
}
|
||||
|
||||
// "remember" the width and height of this subimage
|
||||
last_width = subimage->width();
|
||||
last_height = subimage->height();
|
||||
|
||||
return subimage;
|
||||
}
|
||||
|
||||
void ImageMgr::fixupIntro(Image *im, int prescale) {
|
||||
const byte *sigData;
|
||||
int i, x, y;
|
||||
bool alpha = im->isAlphaOn();
|
||||
RGBA color;
|
||||
|
||||
sigData = g_intro->getSigData();
|
||||
im->alphaOff();
|
||||
if (settings._videoType != "VGA-ALLPNG" && settings._videoType != "new") {
|
||||
/* ----------------------------
|
||||
* update the position of "and"
|
||||
* ---------------------------- */
|
||||
im->drawSubRectOn(im, 148 * prescale, 17 * prescale,
|
||||
153 * prescale,
|
||||
17 * prescale,
|
||||
11 * prescale,
|
||||
4 * prescale);
|
||||
im->drawSubRectOn(im, 159 * prescale, 17 * prescale,
|
||||
165 * prescale,
|
||||
18 * prescale,
|
||||
1 * prescale,
|
||||
4 * prescale);
|
||||
im->drawSubRectOn(im, 160 * prescale, 17 * prescale,
|
||||
164 * prescale,
|
||||
17 * prescale,
|
||||
16 * prescale,
|
||||
4 * prescale);
|
||||
/* ---------------------------------------------
|
||||
* update the position of "Origin Systems, Inc."
|
||||
* --------------------------------------------- */
|
||||
im->drawSubRectOn(im, 86 * prescale, 21 * prescale,
|
||||
88 * prescale,
|
||||
21 * prescale,
|
||||
114 * prescale,
|
||||
9 * prescale);
|
||||
im->drawSubRectOn(im, 199 * prescale, 21 * prescale,
|
||||
202 * prescale,
|
||||
21 * prescale,
|
||||
6 * prescale,
|
||||
9 * prescale);
|
||||
im->drawSubRectOn(im, 207 * prescale, 21 * prescale,
|
||||
208 * prescale,
|
||||
21 * prescale,
|
||||
28 * prescale,
|
||||
9 * prescale);
|
||||
/* ---------------------------------------------
|
||||
* update the position of "Ultima IV"
|
||||
* --------------------------------------------- */
|
||||
// move this *prior* to moving "present"
|
||||
im->drawSubRectOn(im, 59 * prescale, 33 * prescale,
|
||||
61 * prescale,
|
||||
33 * prescale,
|
||||
204 * prescale,
|
||||
46 * prescale);
|
||||
/* ---------------------------------------------
|
||||
* update the position of "Quest of the Avatar"
|
||||
* --------------------------------------------- */
|
||||
im->drawSubRectOn(im, 69 * prescale, 80 * prescale, // quEst
|
||||
70 * prescale,
|
||||
80 * prescale,
|
||||
11 * prescale,
|
||||
13 * prescale);
|
||||
im->drawSubRectOn(im, 82 * prescale, 80 * prescale, // queST
|
||||
84 * prescale,
|
||||
80 * prescale,
|
||||
27 * prescale,
|
||||
13 * prescale);
|
||||
im->drawSubRectOn(im, 131 * prescale, 80 * prescale, // oF
|
||||
132 * prescale,
|
||||
80 * prescale,
|
||||
11 * prescale,
|
||||
13 * prescale);
|
||||
im->drawSubRectOn(im, 150 * prescale, 80 * prescale, // THE
|
||||
149 * prescale,
|
||||
80 * prescale,
|
||||
40 * prescale,
|
||||
13 * prescale);
|
||||
im->drawSubRectOn(im, 166 * prescale, 80 * prescale, // tHe
|
||||
165 * prescale,
|
||||
80 * prescale,
|
||||
11 * prescale,
|
||||
13 * prescale);
|
||||
im->drawSubRectOn(im, 200 * prescale, 80 * prescale, // AVATAR
|
||||
201 * prescale,
|
||||
80 * prescale,
|
||||
81 * prescale,
|
||||
13 * prescale);
|
||||
im->drawSubRectOn(im, 227 * prescale, 80 * prescale, // avAtar
|
||||
228 * prescale,
|
||||
80 * prescale,
|
||||
11 * prescale,
|
||||
13 * prescale);
|
||||
}
|
||||
/* -----------------------------------------------------------------------------
|
||||
* copy "present" to new location between "Origin Systems, Inc." and "Ultima IV"
|
||||
* ----------------------------------------------------------------------------- */
|
||||
// do this *after* moving "Ultima IV"
|
||||
im->drawSubRectOn(im, 132 * prescale, 33 * prescale,
|
||||
135 * prescale,
|
||||
0 * prescale,
|
||||
56 * prescale,
|
||||
5 * prescale);
|
||||
|
||||
if (alpha) {
|
||||
im->alphaOn();
|
||||
}
|
||||
|
||||
/* ----------------------------
|
||||
* erase the original "present"
|
||||
* ---------------------------- */
|
||||
im->fillRect(135 * prescale, 0 * prescale, 56 * prescale, 5 * prescale, 0, 0, 0);
|
||||
|
||||
/* -------------------------
|
||||
* update the colors for VGA
|
||||
* ------------------------- */
|
||||
if (settings._videoType == "VGA") {
|
||||
ImageInfo *borderInfo = imageMgr->get(BKGD_BORDERS, true);
|
||||
// ImageInfo *charsetInfo = imageMgr->get(BKGD_CHARSET);
|
||||
if (!borderInfo)
|
||||
error("ERROR 1001: Unable to load the \"%s\" data file", BKGD_BORDERS);
|
||||
|
||||
delete borderInfo->_image;
|
||||
borderInfo->_image = nullptr;
|
||||
borderInfo = imageMgr->get(BKGD_BORDERS, true);
|
||||
|
||||
im->setPaletteFromImage(borderInfo->_image);
|
||||
|
||||
// update the color of "and" and "present"
|
||||
(void)im->setPaletteIndex(15, 226, 226, 255);
|
||||
|
||||
// update the color of "Origin Systems, Inc."
|
||||
(void)im->setPaletteIndex(9, 129, 129, 255);
|
||||
|
||||
#ifdef TODO
|
||||
borderInfo->_image->save("border.png");
|
||||
#endif
|
||||
// update the border appearance
|
||||
borderInfo->_image->alphaOff();
|
||||
borderInfo->_image->drawSubRectOn(im, 0, 96, 0, 0, 16, 56);
|
||||
for (i = 0; i < 9; i++) {
|
||||
borderInfo->_image->drawSubRectOn(im, 16 + (i * 32), 96, 144, 0, 48, 48);
|
||||
}
|
||||
im->drawSubRectInvertedOn(im, 0, 144, 0, 104, 320, 40);
|
||||
im->drawSubRectOn(im, 0, 184, 0, 96, 320, 8);
|
||||
borderInfo->_image->alphaOn();
|
||||
|
||||
delete borderInfo->_image;
|
||||
borderInfo->_image = nullptr;
|
||||
}
|
||||
|
||||
/* -----------------------------
|
||||
* draw "Lord British" signature
|
||||
* ----------------------------- */
|
||||
color = im->setColor(0, 255, 255); // cyan for EGA
|
||||
int blue[16] = {255, 250, 226, 226, 210, 194, 161, 161,
|
||||
129, 97, 97, 64, 64, 32, 32, 0
|
||||
};
|
||||
i = 0;
|
||||
while (sigData[i] != 0) {
|
||||
/* (x/y) are unscaled coordinates, i.e. in 320x200 */
|
||||
x = sigData[i] + 0x14;
|
||||
y = 0xBF - sigData[i + 1];
|
||||
|
||||
if (settings._videoType != "EGA") {
|
||||
// yellow gradient
|
||||
color = im->setColor(255, (y == 1 ? 250 : 255), blue[y]);
|
||||
}
|
||||
|
||||
im->fillRect(x * prescale, y * prescale,
|
||||
2 * prescale, prescale,
|
||||
color.r, color.g, color.b);
|
||||
i += 2;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------
|
||||
* draw the red line between "Origin Systems, Inc." and "present"
|
||||
* -------------------------------------------------------------- */
|
||||
/* we're still working with an unscaled surface */
|
||||
if (settings._videoType != "EGA") {
|
||||
color = im->setColor(0, 0, 161); // dark blue
|
||||
} else {
|
||||
color = im->setColor(128, 0, 0); // dark red for EGA
|
||||
}
|
||||
for (i = 84; i < 236; i++) // 152 px wide
|
||||
im->fillRect(i * prescale, 31 * prescale,
|
||||
prescale, prescale,
|
||||
color.r, color.g, color.b);
|
||||
}
|
||||
|
||||
void ImageMgr::fixupAbyssVision(Image *im, int prescale) {
|
||||
// Ignore fixups for xu4 PNG images - they're already correct
|
||||
if (im->isIndexed())
|
||||
return;
|
||||
|
||||
/*
|
||||
* Each VGA vision components must be XORed with all the previous
|
||||
* vision components to get the actual image.
|
||||
*/
|
||||
if (_abyssData) {
|
||||
for (int y = 0; y < im->height(); y++) {
|
||||
for (int x = 0; x < im->width(); x++) {
|
||||
uint index;
|
||||
im->getPixelIndex(x, y, index);
|
||||
index ^= _abyssData[y * im->width() + x];
|
||||
im->putPixelIndex(x, y, index);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_abyssData = new uint[im->width() * im->height()];
|
||||
}
|
||||
|
||||
for (int y = 0; y < im->height(); y++) {
|
||||
for (int x = 0; x < im->width(); x++) {
|
||||
uint index;
|
||||
im->getPixelIndex(x, y, index);
|
||||
_abyssData[y * im->width() + x] = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImageMgr::fixupAbacus(Image *im, int prescale) {
|
||||
|
||||
/*
|
||||
* surround each bead with a row green pixels to avoid artifacts
|
||||
* when scaling
|
||||
*/
|
||||
|
||||
im->fillRect(7 * prescale, 186 * prescale, prescale, 14 * prescale, 0, 255, 80); /* green */
|
||||
im->fillRect(16 * prescale, 186 * prescale, prescale, 14 * prescale, 0, 255, 80); /* green */
|
||||
im->fillRect(8 * prescale, 186 * prescale, prescale * 8, prescale, 0, 255, 80); /* green */
|
||||
im->fillRect(8 * prescale, 199 * prescale, prescale * 8, prescale, 0, 255, 80); /* green */
|
||||
|
||||
im->fillRect(23 * prescale, 186 * prescale, prescale, 14 * prescale, 0, 255, 80); /* green */
|
||||
im->fillRect(32 * prescale, 186 * prescale, prescale, 14 * prescale, 0, 255, 80); /* green */
|
||||
im->fillRect(24 * prescale, 186 * prescale, prescale * 8, prescale, 0, 255, 80); /* green */
|
||||
im->fillRect(24 * prescale, 199 * prescale, prescale * 8, prescale, 0, 255, 80); /* green */
|
||||
}
|
||||
|
||||
void ImageMgr::fixupDungNS(Image *im, int prescale) {
|
||||
for (int y = 0; y < im->height(); y++) {
|
||||
for (int x = 0; x < im->width(); x++) {
|
||||
uint index;
|
||||
im->getPixelIndex(x, y, index);
|
||||
if (index == 1)
|
||||
im->putPixelIndex(x, y, 2);
|
||||
else if (index == 2)
|
||||
im->putPixelIndex(x, y, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImageMgr::fixupFMTowns(Image *im, int prescale) {
|
||||
for (int y = 20; y < im->height(); y++) {
|
||||
for (int x = 0; x < im->width(); x++) {
|
||||
uint index;
|
||||
im->getPixelIndex(x, y, index);
|
||||
im->putPixelIndex(x, y - 20, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageSet *ImageMgr::getSet(const Common::String &setname) {
|
||||
Common::HashMap<Common::String, ImageSet *>::iterator i = _imageSets.find(setname);
|
||||
if (i != _imageSets.end())
|
||||
return i->_value;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ImageInfo *ImageMgr::getInfo(const Common::String &name) {
|
||||
return getInfoFromSet(name, _baseSet);
|
||||
}
|
||||
|
||||
ImageInfo *ImageMgr::getInfoFromSet(const Common::String &name, ImageSet *imageset) {
|
||||
if (!imageset)
|
||||
return nullptr;
|
||||
|
||||
/* if the image set contains the image we want, AND IT EXISTS we are done */
|
||||
Common::HashMap<Common::String, ImageInfo *>::iterator i = imageset->_info.find(name);
|
||||
if (i != imageset->_info.end())
|
||||
if (imageExists(i->_value))
|
||||
return i->_value;
|
||||
|
||||
/* otherwise if this image set extends another, check the base image set */
|
||||
while (imageset->_extends != "") {
|
||||
imageset = getSet(imageset->_extends);
|
||||
return getInfoFromSet(name, imageset);
|
||||
}
|
||||
|
||||
//warning("Searched recursively from imageset %s through to %s and couldn't find %s", baseSet->name.c_str(), imageset->name.c_str(), name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::String ImageMgr::guessFileType(const Common::String &filename) {
|
||||
if (filename.size() >= 4 && filename.hasSuffixIgnoreCase(".png")) {
|
||||
return "image/png";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
bool ImageMgr::imageExists(ImageInfo *info) {
|
||||
if (info->_filename.empty()) //If it is an abstract image like "screen"
|
||||
return true;
|
||||
Common::File *file = getImageFile(info);
|
||||
if (file) {
|
||||
delete file;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Common::File *ImageMgr::getImageFile(ImageInfo *info) {
|
||||
Common::String filename = info->_filename;
|
||||
|
||||
if (filename.empty())
|
||||
return nullptr;
|
||||
|
||||
Common::File *file = new Common::File();
|
||||
if (!info->_xu4Graphic) {
|
||||
// It's a file in the game folder
|
||||
if (file->open(Common::Path(filename)))
|
||||
return file;
|
||||
}
|
||||
|
||||
if (file->open(Common::Path("data/graphics/").joinInPlace(filename)))
|
||||
return file;
|
||||
|
||||
delete file;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ImageInfo *ImageMgr::get(const Common::String &name, bool returnUnscaled) {
|
||||
ImageInfo *info = getInfo(name);
|
||||
if (!info)
|
||||
return nullptr;
|
||||
|
||||
/* return if already loaded */
|
||||
if (info->_image != nullptr)
|
||||
return info;
|
||||
|
||||
Common::File *file = getImageFile(info);
|
||||
Image *unscaled = nullptr;
|
||||
if (file) {
|
||||
if (info->_filetype.empty())
|
||||
info->_filetype = guessFileType(info->_filename);
|
||||
Common::String filetype = info->_filetype;
|
||||
::Image::ImageDecoder *decoder = createDecoder(filetype, info->_width, info->_height, info->_depth);
|
||||
if (decoder == nullptr) {
|
||||
warning("can't find loader to load image \"%s\" with type \"%s\"", info->_filename.c_str(), filetype.c_str());
|
||||
} else {
|
||||
if (!decoder->loadStream(*file)) {
|
||||
warning("can't load image \"%s\" with type \"%s\"", info->_filename.c_str(), filetype.c_str());
|
||||
} else {
|
||||
const Graphics::Surface *surface = decoder->getSurface();
|
||||
unscaled = Image::create(surface->w, surface->h, surface->format);
|
||||
unscaled->blitFrom(*surface);
|
||||
|
||||
const Graphics::Palette &pal = decoder->getPalette();
|
||||
if (!pal.empty()) {
|
||||
unscaled->setPalette(pal.data(), pal.size());
|
||||
}
|
||||
|
||||
if (info->_width == -1) {
|
||||
// Write in the values for later use.
|
||||
info->_width = unscaled->width();
|
||||
info->_height = unscaled->height();
|
||||
// ### info->depth = ???
|
||||
}
|
||||
}
|
||||
|
||||
delete decoder;
|
||||
}
|
||||
|
||||
delete file;
|
||||
} else {
|
||||
warning("Failed to open file %s for reading.", info->_filename.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (unscaled == nullptr)
|
||||
return nullptr;
|
||||
|
||||
if (info->_transparentIndex != -1)
|
||||
unscaled->setTransparentIndex(info->_transparentIndex);
|
||||
|
||||
if (info->_prescale == 0)
|
||||
info->_prescale = 1;
|
||||
|
||||
/*
|
||||
* fixup the image before scaling it
|
||||
*/
|
||||
switch (info->_fixup) {
|
||||
case FIXUP_NONE:
|
||||
break;
|
||||
case FIXUP_INTRO:
|
||||
fixupIntro(unscaled, info->_prescale);
|
||||
break;
|
||||
case FIXUP_ABYSS:
|
||||
fixupAbyssVision(unscaled, info->_prescale);
|
||||
break;
|
||||
case FIXUP_ABACUS:
|
||||
fixupAbacus(unscaled, info->_prescale);
|
||||
break;
|
||||
case FIXUP_DUNGNS:
|
||||
fixupDungNS(unscaled, info->_prescale);
|
||||
break;
|
||||
case FIXUP_FMTOWNSSCREEN:
|
||||
fixupFMTowns(unscaled, info->_prescale);
|
||||
break;
|
||||
case FIXUP_BLACKTRANSPARENCYHACK:
|
||||
//Apply transparency shadow hack to ultima4 ega and vga upgrade classic graphics.
|
||||
if (Settings::getInstance()._enhancements && Settings::getInstance()._enhancementsOptions._u4TileTransparencyHack) {
|
||||
// TODO: Pick a more optimal pixel format?
|
||||
Image *unscaled_original = unscaled;
|
||||
unscaled = Image::duplicate(unscaled, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
|
||||
delete unscaled_original;
|
||||
|
||||
int transparency_shadow_size = Settings::getInstance()._enhancementsOptions._u4TrileTransparencyHackShadowBreadth;
|
||||
int black_index = 0;
|
||||
int opacity = Settings::getInstance()._enhancementsOptions._u4TileTransparencyHackPixelShadowOpacity;
|
||||
|
||||
int frames = info->_tiles;
|
||||
for (int f = 0; f < frames; ++f)
|
||||
unscaled->performTransparencyHack(black_index, frames, f, transparency_shadow_size, opacity);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (returnUnscaled) {
|
||||
info->_image = unscaled;
|
||||
return info;
|
||||
}
|
||||
|
||||
int imageScale = settings._scale;
|
||||
if ((settings._scale % info->_prescale) != 0) {
|
||||
int orig_scale = settings._scale;
|
||||
settings._scale = info->_prescale;
|
||||
settings.write();
|
||||
error("image %s is prescaled to an incompatible size: %d\nResetting the scale to %d. Sorry about the inconvenience, please restart.", info->_filename.c_str(), orig_scale, settings._scale);
|
||||
}
|
||||
imageScale /= info->_prescale;
|
||||
|
||||
info->_image = g_screen->screenScale(unscaled, imageScale, info->_tiles, 1);
|
||||
|
||||
delete unscaled;
|
||||
return info;
|
||||
}
|
||||
|
||||
SubImage *ImageMgr::getSubImage(const Common::String &name) {
|
||||
Common::String setname;
|
||||
|
||||
ImageSet *set = _baseSet;
|
||||
|
||||
while (set != nullptr) {
|
||||
for (auto &i : set->_info) {
|
||||
ImageInfo *info = (ImageInfo *) i._value;
|
||||
Common::HashMap<Common::String, SubImage *>::iterator j = info->_subImages.find(name);
|
||||
if (j != info->_subImages.end())
|
||||
return j->_value;
|
||||
}
|
||||
|
||||
set = getSet(set->_extends);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ImageMgr::freeIntroBackgrounds() {
|
||||
for (const auto &i : _imageSets) {
|
||||
ImageSet *set = i._value;
|
||||
for (auto &j : set->_info) {
|
||||
ImageInfo *info = j._value;
|
||||
if (info->_image != nullptr && info->_introOnly) {
|
||||
delete info->_image;
|
||||
info->_image = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Std::vector<Common::String> &ImageMgr::getSetNames() {
|
||||
return _imageSetNames;
|
||||
}
|
||||
|
||||
void ImageMgr::update(Settings *newSettings) {
|
||||
Common::String setname;
|
||||
|
||||
setname = newSettings->_videoType;
|
||||
|
||||
_baseSet = getSet(setname);
|
||||
}
|
||||
|
||||
::Image::ImageDecoder *ImageMgr::createDecoder(const Common::String &fileType, int width, int height, int bpp) {
|
||||
if (fileType == "image/png")
|
||||
return new ::Image::PNGDecoder();
|
||||
if (fileType == "image/x-u4raw")
|
||||
return new U4RawImageDecoder(width, height, bpp);
|
||||
if (fileType == "image/x-u4rle")
|
||||
return new U4RleImageDecoder(width, height, bpp);
|
||||
if (fileType == "image/x-u4lzw")
|
||||
return new U4LzwImageDecoder(width, height, bpp);
|
||||
if (fileType == "image/fmtowns-tif")
|
||||
return new FMTOWNSImageDecoder(width, height, bpp, 510);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ImageSet::~ImageSet() {
|
||||
for (const auto &i : _info) {
|
||||
ImageInfo *imageInfo = i._value;
|
||||
if (imageInfo->_name != "screen")
|
||||
delete imageInfo;
|
||||
}
|
||||
}
|
||||
|
||||
ImageInfo::~ImageInfo() {
|
||||
for (auto &i : _subImages)
|
||||
delete i._value;
|
||||
if (_image != nullptr)
|
||||
delete _image;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
216
engines/ultima/ultima4/gfx/imagemgr.h
Normal file
216
engines/ultima/ultima4/gfx/imagemgr.h
Normal file
@@ -0,0 +1,216 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_GFX_IMAGEMGR_H
|
||||
#define ULTIMA4_GFX_IMAGEMGR_H
|
||||
|
||||
#include "ultima/ultima4/gfx/image.h"
|
||||
#include "ultima/ultima4/core/observer.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
|
||||
namespace Common {
|
||||
class File;
|
||||
}
|
||||
|
||||
namespace Image {
|
||||
class ImageDecoder;
|
||||
}
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class ConfigElement;
|
||||
class Debug;
|
||||
class ImageSet;
|
||||
class Settings;
|
||||
|
||||
/*
|
||||
* The image manager is responsible for loading and keeping track of
|
||||
* the various images.
|
||||
*/
|
||||
|
||||
#define BKGD_SHAPES "tiles"
|
||||
#define BKGD_CHARSET "charset"
|
||||
#define BKGD_BORDERS "borders"
|
||||
#define BKGD_INTRO "title"
|
||||
#define BKGD_OPTIONS_TOP "options_top"
|
||||
#define BKGD_OPTIONS_BTM "options_btm"
|
||||
#define BKGD_TREE "tree"
|
||||
#define BKGD_PORTAL "portal"
|
||||
#define BKGD_OUTSIDE "outside"
|
||||
#define BKGD_INSIDE "inside"
|
||||
#define BKGD_WAGON "wagon"
|
||||
#define BKGD_GYPSY "gypsy"
|
||||
#define BKGD_ABACUS "abacus"
|
||||
#define BKGD_HONCOM "honcom"
|
||||
#define BKGD_VALJUS "valjus"
|
||||
#define BKGD_SACHONOR "sachonor"
|
||||
#define BKGD_SPIRHUM "spirhum"
|
||||
#define BKGD_ANIMATE "beasties"
|
||||
#define BKGD_KEY "key"
|
||||
#define BKGD_HONESTY "honesty"
|
||||
#define BKGD_COMPASSN "compassn"
|
||||
#define BKGD_VALOR "valor"
|
||||
#define BKGD_JUSTICE "justice"
|
||||
#define BKGD_SACRIFIC "sacrific"
|
||||
#define BKGD_HONOR "honor"
|
||||
#define BKGD_SPIRIT "spirit"
|
||||
#define BKGD_HUMILITY "humility"
|
||||
#define BKGD_TRUTH "truth"
|
||||
#define BKGD_LOVE "love"
|
||||
#define BKGD_COURAGE "courage"
|
||||
#define BKGD_STONCRCL "stoncrcl"
|
||||
#define BKGD_RUNE_INF "rune0"
|
||||
#define BKGD_SHRINE_HON "rune1"
|
||||
#define BKGD_SHRINE_COM "rune2"
|
||||
#define BKGD_SHRINE_VAL "rune3"
|
||||
#define BKGD_SHRINE_JUS "rune4"
|
||||
#define BKGD_SHRINE_SAC "rune5"
|
||||
#define BKGD_SHRINE_HNR "rune6"
|
||||
#define BKGD_SHRINE_SPI "rune7"
|
||||
#define BKGD_SHRINE_HUM "rune8"
|
||||
#define BKGD_GEMTILES "gemtiles"
|
||||
|
||||
enum ImageFixup {
|
||||
FIXUP_NONE,
|
||||
FIXUP_INTRO,
|
||||
FIXUP_ABYSS,
|
||||
FIXUP_ABACUS,
|
||||
FIXUP_DUNGNS,
|
||||
FIXUP_BLACKTRANSPARENCYHACK,
|
||||
FIXUP_FMTOWNSSCREEN
|
||||
};
|
||||
|
||||
/**
|
||||
* Image meta info.
|
||||
*/
|
||||
class ImageInfo {
|
||||
public:
|
||||
~ImageInfo();
|
||||
|
||||
Common::String _name;
|
||||
Common::String _filename;
|
||||
int _width, _height, _depth;
|
||||
int _prescale;
|
||||
Common::String _filetype;
|
||||
int _tiles; /**< used to scale the without bleeding colors between adjacent tiles */
|
||||
bool _introOnly; /**< whether can be freed after the intro */
|
||||
int _transparentIndex; /**< color index to consider transparent */
|
||||
bool _xu4Graphic; /**< an original xu4 graphic not part of u4dos or the VGA upgrade */
|
||||
ImageFixup _fixup; /**< a routine to do miscellaneous fixes to the image */
|
||||
Image *_image; /**< the image we're describing */
|
||||
Common::HashMap<Common::String, SubImage *> _subImages;
|
||||
|
||||
bool hasBlackBackground();
|
||||
};
|
||||
|
||||
/**
|
||||
* The image manager singleton that keeps track of all the images.
|
||||
*/
|
||||
class ImageMgr : Observer<Settings *> {
|
||||
public:
|
||||
static ImageMgr *getInstance();
|
||||
static void destroy();
|
||||
|
||||
/**
|
||||
* Load in a background image from a ".ega" file.
|
||||
*/
|
||||
ImageInfo *get(const Common::String &name, bool returnUnscaled = false);
|
||||
|
||||
/**
|
||||
* Returns information for the given image set.
|
||||
*/
|
||||
SubImage *getSubImage(const Common::String &name);
|
||||
|
||||
/**
|
||||
* Free up any background images used only in the animations.
|
||||
*/
|
||||
void freeIntroBackgrounds();
|
||||
const Std::vector<Common::String> &getSetNames();
|
||||
Common::File *getImageFile(ImageInfo *info);
|
||||
bool imageExists(ImageInfo *info);
|
||||
|
||||
private:
|
||||
ImageMgr();
|
||||
~ImageMgr();
|
||||
void init();
|
||||
|
||||
ImageSet *loadImageSetFromConf(const ConfigElement &conf);
|
||||
ImageInfo *loadImageInfoFromConf(const ConfigElement &conf);
|
||||
SubImage *loadSubImageFromConf(const ImageInfo *info, const ConfigElement &conf);
|
||||
|
||||
/**
|
||||
* Returns information for the given image set.
|
||||
*/
|
||||
ImageSet *getSet(const Common::String &setname);
|
||||
|
||||
/**
|
||||
* Returns image information for the current image set.
|
||||
*/
|
||||
ImageInfo *getInfo(const Common::String &name);
|
||||
|
||||
/**
|
||||
* Returns information for the given image set.
|
||||
*/
|
||||
ImageInfo *getInfoFromSet(const Common::String &name, ImageSet *set);
|
||||
|
||||
Common::String guessFileType(const Common::String &filename);
|
||||
|
||||
void fixupIntro(Image *im, int prescale);
|
||||
void fixupAbyssVision(Image *im, int prescale);
|
||||
void fixupAbacus(Image *im, int prescale);
|
||||
|
||||
/**
|
||||
* Swap blue and green for the dungeon walls when facing north or
|
||||
* south.
|
||||
*/
|
||||
void fixupDungNS(Image *im, int prescale);
|
||||
|
||||
/**
|
||||
* The FMTowns images have a different screen dimension. This moves them
|
||||
* up to what xu4 is accustomed to. south.
|
||||
*/
|
||||
void fixupFMTowns(Image *im, int prescale);
|
||||
|
||||
/**
|
||||
* Find the new base image set when settings have changed.
|
||||
*/
|
||||
void update(Settings *newSettings);
|
||||
|
||||
/**
|
||||
* Create an image decoder for the specified file type.
|
||||
*/
|
||||
::Image::ImageDecoder *createDecoder(const Common::String &fileType, int width, int height, int bpp);
|
||||
|
||||
static ImageMgr *_instance;
|
||||
Common::HashMap<Common::String, ImageSet *> _imageSets;
|
||||
Std::vector<Common::String> _imageSetNames;
|
||||
ImageSet *_baseSet;
|
||||
ImageInfo _screenInfo;
|
||||
uint *_abyssData;
|
||||
};
|
||||
|
||||
#define imageMgr (ImageMgr::getInstance())
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
458
engines/ultima/ultima4/gfx/scale.cpp
Normal file
458
engines/ultima/ultima4/gfx/scale.cpp
Normal file
@@ -0,0 +1,458 @@
|
||||
/* 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/ultima4/gfx/image.h"
|
||||
#include "ultima/ultima4/gfx/scale.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Image *scalePoint(Image *src, int scale, int n);
|
||||
Image *scale2xBilinear(Image *src, int scale, int n);
|
||||
Image *scale2xSaI(Image *src, int scale, int N);
|
||||
Image *scaleScale2x(Image *src, int scale, int N);
|
||||
|
||||
Scaler scalerGet(const Common::String &filter) {
|
||||
if (filter == "point")
|
||||
return &scalePoint;
|
||||
else if (filter == "2xBi")
|
||||
return &scale2xBilinear;
|
||||
else if (filter == "2xSaI")
|
||||
return &scale2xSaI;
|
||||
else if (filter == "Scale2x")
|
||||
return &scaleScale2x;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given scaler can scale by 3 (as well as by 2).
|
||||
*/
|
||||
int scaler3x(const Common::String &filter) {
|
||||
return filter == "Scale2x";
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple row and column duplicating scaler.
|
||||
*/
|
||||
Image *scalePoint(Image *src, int scale, int n) {
|
||||
int x, y, i, j;
|
||||
Image *dest;
|
||||
|
||||
dest = Image::create(src->width() * scale, src->height() * scale, src->format());
|
||||
if (!dest)
|
||||
return nullptr;
|
||||
|
||||
if (dest->isIndexed())
|
||||
dest->setPaletteFromImage(src);
|
||||
|
||||
for (y = 0; y < src->height(); y++) {
|
||||
for (x = 0; x < src->width(); x++) {
|
||||
for (i = 0; i < scale; i++) {
|
||||
for (j = 0; j < scale; j++) {
|
||||
uint index;
|
||||
src->getPixelIndex(x, y, index);
|
||||
dest->putPixelIndex(x * scale + j, y * scale + i, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* A scaler that interpolates each intervening pixel from it's two
|
||||
* neighbors.
|
||||
*/
|
||||
Image *scale2xBilinear(Image *src, int scale, int n) {
|
||||
int i, x, y, xoff, yoff;
|
||||
RGBA a, b, c, d;
|
||||
Image *dest;
|
||||
|
||||
/* this scaler works only with images scaled by 2x */
|
||||
assertMsg(scale == 2, "invalid scale: %d", scale);
|
||||
|
||||
Graphics::PixelFormat format = src->isIndexed() ? g_system->getScreenFormat() : src->format();
|
||||
dest = Image::create(src->width() * scale, src->height() * scale, format);
|
||||
if (!dest)
|
||||
return nullptr;
|
||||
|
||||
/*
|
||||
* Each pixel in the source image is translated into four in the
|
||||
* destination. The destination pixels are dependant on the pixel
|
||||
* itself, and the three surrounding pixels (A is the original
|
||||
* pixel):
|
||||
* A B
|
||||
* C D
|
||||
* The four destination pixels mapping to A are calculated as
|
||||
* follows:
|
||||
* [ A ] [ (A+B)/2 ]
|
||||
* [(A+C)/2] [(A+B+C+D)/4]
|
||||
*/
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
for (y = (src->height() / n) * i; y < (src->height() / n) * (i + 1); y++) {
|
||||
if (y == (src->height() / n) * (i + 1) - 1)
|
||||
yoff = 0;
|
||||
else
|
||||
yoff = 1;
|
||||
|
||||
for (x = 0; x < src->width(); x++) {
|
||||
if (x == src->width() - 1)
|
||||
xoff = 0;
|
||||
else
|
||||
xoff = 1;
|
||||
|
||||
src->getPixel(x, y, a.r, a.g, a.b, a.a);
|
||||
src->getPixel(x + xoff, y, b.r, b.g, b.b, b.a);
|
||||
src->getPixel(x, y + yoff, c.r, c.g, c.b, c.a);
|
||||
src->getPixel(x + xoff, y + yoff, d.r, d.g, d.b, d.a);
|
||||
|
||||
dest->putPixel(x * 2, y * 2, a.r, a.g, a.b, a.a);
|
||||
dest->putPixel(x * 2 + 1, y * 2, (a.r + b.r) >> 1, (a.g + b.g) >> 1, (a.b + b.b) >> 1, (a.a + b.a) >> 1);
|
||||
dest->putPixel(x * 2, y * 2 + 1, (a.r + c.r) >> 1, (a.g + c.g) >> 1, (a.b + c.b) >> 1, (a.a + c.a) >> 1);
|
||||
dest->putPixel(x * 2 + 1, y * 2 + 1, (a.r + b.r + c.r + d.r) >> 2, (a.g + b.g + c.g + d.g) >> 2, (a.b + b.b + c.b + d.b) >> 2, (a.a + b.a + c.a + d.a) >> 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
int colorEqual(RGBA a, RGBA b) {
|
||||
return
|
||||
a.r == b.r &&
|
||||
a.g == b.g &&
|
||||
a.b == b.b &&
|
||||
a.a == b.a;
|
||||
}
|
||||
|
||||
RGBA colorAverage(RGBA a, RGBA b) {
|
||||
RGBA result;
|
||||
result.r = (a.r + b.r) >> 1;
|
||||
result.g = (a.g + b.g) >> 1;
|
||||
result.b = (a.b + b.b) >> 1;
|
||||
result.a = (a.a + b.a) >> 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
int _2xSaI_GetResult1(RGBA a, RGBA b, RGBA c, RGBA d) {
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int r = 0;
|
||||
if (colorEqual(a, c)) x++;
|
||||
else if (colorEqual(b, c)) y++;
|
||||
if (colorEqual(a, d)) x++;
|
||||
else if (colorEqual(b, d)) y++;
|
||||
if (x <= 1) r++;
|
||||
if (y <= 1) r--;
|
||||
return r;
|
||||
}
|
||||
|
||||
int _2xSaI_GetResult2(RGBA a, RGBA b, RGBA c, RGBA d) {
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int r = 0;
|
||||
if (colorEqual(a, c)) x++;
|
||||
else if (colorEqual(b, c)) y++;
|
||||
if (colorEqual(a, d)) x++;
|
||||
else if (colorEqual(b, d)) y++;
|
||||
if (x <= 1) r--;
|
||||
if (y <= 1) r++;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* A more sophisticated scaler that interpolates each new pixel the
|
||||
* surrounding pixels.
|
||||
*/
|
||||
Image *scale2xSaI(Image *src, int scale, int N) {
|
||||
int ii, x, y, xoff0, xoff1, xoff2, yoff0, yoff1, yoff2;
|
||||
RGBA a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p;
|
||||
RGBA prod0, prod1, prod2;
|
||||
Image *dest;
|
||||
|
||||
/* this scaler works only with images scaled by 2x */
|
||||
assertMsg(scale == 2, "invalid scale: %d", scale);
|
||||
|
||||
Graphics::PixelFormat format = src->isIndexed() ? g_system->getScreenFormat() : src->format();
|
||||
dest = Image::create(src->width() * scale, src->height() * scale, format);
|
||||
if (!dest)
|
||||
return nullptr;
|
||||
|
||||
/*
|
||||
* Each pixel in the source image is translated into four in the
|
||||
* destination. The destination pixels are dependant on the pixel
|
||||
* itself, and the surrounding pixels as shown below (A is the
|
||||
* original pixel):
|
||||
* I E F J
|
||||
* G A B K
|
||||
* H C D L
|
||||
* M N O P
|
||||
*/
|
||||
|
||||
for (ii = 0; ii < N; ii++) {
|
||||
for (y = (src->height() / N) * ii; y < (src->height() / N) * (ii + 1); y++) {
|
||||
if (y == 0)
|
||||
yoff0 = 0;
|
||||
else
|
||||
yoff0 = -1;
|
||||
if (y == (src->height() / N) * (ii + 1) - 1) {
|
||||
yoff1 = 0;
|
||||
yoff2 = 0;
|
||||
} else if (y == (src->height() / N) * (ii + 1) - 2) {
|
||||
yoff1 = 1;
|
||||
yoff2 = 1;
|
||||
} else {
|
||||
yoff1 = 1;
|
||||
yoff2 = 2;
|
||||
}
|
||||
|
||||
|
||||
for (x = 0; x < src->width(); x++) {
|
||||
if (x == 0)
|
||||
xoff0 = 0;
|
||||
else
|
||||
xoff0 = -1;
|
||||
if (x == src->width() - 1) {
|
||||
xoff1 = 0;
|
||||
xoff2 = 0;
|
||||
} else if (x == src->width() - 2) {
|
||||
xoff1 = 1;
|
||||
xoff2 = 1;
|
||||
} else {
|
||||
xoff1 = 1;
|
||||
xoff2 = 2;
|
||||
}
|
||||
|
||||
src->getPixel(x, y, a.r, a.g, a.b, a.a);
|
||||
src->getPixel(x + xoff1, y, b.r, b.g, b.b, b.a);
|
||||
src->getPixel(x, y + yoff1, c.r, c.g, c.b, c.a);
|
||||
src->getPixel(x + xoff1, y + yoff1, d.r, d.g, d.b, d.a);
|
||||
|
||||
src->getPixel(x, y + yoff0, e.r, e.g, e.b, e.a);
|
||||
src->getPixel(x + xoff1, y + yoff0, f.r, f.g, f.b, f.a);
|
||||
src->getPixel(x + xoff0, y, g.r, g.g, g.b, g.a);
|
||||
src->getPixel(x + xoff0, y + yoff1, h.r, h.g, h.b, h.a);
|
||||
|
||||
src->getPixel(x + xoff0, y + yoff0, i.r, i.g, i.b, i.a);
|
||||
src->getPixel(x + xoff2, y + yoff0, j.r, j.g, j.b, j.a);
|
||||
src->getPixel(x + xoff0, y, k.r, k.g, k.b, k.a);
|
||||
src->getPixel(x + xoff0, y + yoff1, l.r, l.g, l.b, l.a);
|
||||
|
||||
src->getPixel(x + xoff0, y + yoff2, m.r, m.g, m.b, m.a);
|
||||
src->getPixel(x, y + yoff2, n.r, n.g, n.b, n.a);
|
||||
src->getPixel(x + xoff1, y + yoff2, o.r, o.g, o.b, o.a);
|
||||
src->getPixel(x + xoff2, y + yoff2, p.r, p.g, p.b, p.a);
|
||||
|
||||
if (colorEqual(a, d) && !colorEqual(b, c)) {
|
||||
if ((colorEqual(a, e) && colorEqual(b, l)) ||
|
||||
(colorEqual(a, c) && colorEqual(a, f) && !colorEqual(b, e) && colorEqual(b, j)))
|
||||
prod0 = a;
|
||||
else
|
||||
prod0 = colorAverage(a, b);
|
||||
|
||||
if ((colorEqual(a, g) && colorEqual(c, o)) ||
|
||||
(colorEqual(a, b) && colorEqual(a, h) && !colorEqual(g, c) && colorEqual(c, m)))
|
||||
prod1 = a;
|
||||
else
|
||||
prod1 = colorAverage(a, c);
|
||||
|
||||
prod2 = a;
|
||||
} else if (colorEqual(b, c) && !colorEqual(a, d)) {
|
||||
if ((colorEqual(b, f) && colorEqual(a, h)) ||
|
||||
(colorEqual(b, e) && colorEqual(b, d) && !colorEqual(a, f) && colorEqual(a, i)))
|
||||
prod0 = b;
|
||||
else
|
||||
prod0 = colorAverage(a, b);
|
||||
|
||||
if ((colorEqual(c, h) && colorEqual(a, f)) ||
|
||||
(colorEqual(c, g) && colorEqual(c, d) && !colorEqual(a, h) && colorEqual(a, i)))
|
||||
prod1 = c;
|
||||
else
|
||||
prod1 = colorAverage(a, c);
|
||||
|
||||
prod2 = b;
|
||||
} else if (colorEqual(a, d) && colorEqual(b, c)) {
|
||||
if (colorEqual(a, b))
|
||||
prod0 = prod1 = prod2 = a;
|
||||
else {
|
||||
int r = 0;
|
||||
prod0 = colorAverage(a, b);
|
||||
prod1 = colorAverage(a, c);
|
||||
|
||||
r += _2xSaI_GetResult1(a, b, g, e);
|
||||
r += _2xSaI_GetResult2(b, a, k, f);
|
||||
r += _2xSaI_GetResult2(b, a, h, n);
|
||||
r += _2xSaI_GetResult1(a, b, l, o);
|
||||
|
||||
if (r > 0)
|
||||
prod2 = a;
|
||||
else if (r < 0)
|
||||
prod2 = b;
|
||||
else {
|
||||
prod2.r = (a.r + b.r + c.r + d.r) >> 2;
|
||||
prod2.g = (a.g + b.g + c.g + d.g) >> 2;
|
||||
prod2.b = (a.b + b.b + c.b + d.b) >> 2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (colorEqual(a, c) && colorEqual(a, f) && !colorEqual(b, e) && colorEqual(b, j))
|
||||
prod0 = a;
|
||||
else if (colorEqual(b, e) && colorEqual(b, d) && !colorEqual(a, f) && colorEqual(a, i))
|
||||
prod0 = b;
|
||||
else
|
||||
prod0 = colorAverage(a, b);
|
||||
|
||||
if (colorEqual(a, b) && colorEqual(a, h) && !colorEqual(g, c) && colorEqual(c, m))
|
||||
prod1 = a;
|
||||
else if (colorEqual(c, g) && colorEqual(c, d) && !colorEqual(a, h) && colorEqual(a, i))
|
||||
prod1 = c;
|
||||
else
|
||||
prod1 = colorAverage(a, c);
|
||||
|
||||
prod2.r = (a.r + b.r + c.r + d.r) >> 2;
|
||||
prod2.g = (a.g + b.g + c.g + d.g) >> 2;
|
||||
prod2.b = (a.b + b.b + c.b + d.b) >> 2;
|
||||
prod2.a = 255;
|
||||
}
|
||||
|
||||
dest->putPixel((x << 1), (y << 1), a.r, a.g, a.b, a.a);
|
||||
dest->putPixel((x << 1) + 1, (y << 1), prod0.r, prod0.g, prod0.b, prod0.a);
|
||||
dest->putPixel((x << 1), (y << 1) + 1, prod1.r, prod1.g, prod1.b, prod1.a);
|
||||
dest->putPixel((x << 1) + 1, (y << 1) + 1, prod2.r, prod2.g, prod2.b, prod2.a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* A more sophisticated scaler that doesn't interpolate, but avoids
|
||||
* the stair step effect by detecting angles.
|
||||
*/
|
||||
Image *scaleScale2x(Image *src, int scale, int n) {
|
||||
int ii, x, y, xoff0, xoff1, yoff0, yoff1;
|
||||
RGBA a, b, c, d, e, f, g, h, i;
|
||||
RGBA e0, e1, e2, e3;
|
||||
RGBA e4, e5, e6, e7;
|
||||
Image *dest;
|
||||
|
||||
/* this scaler works only with images scaled by 2x or 3x */
|
||||
assertMsg(scale == 2 || scale == 3, "invalid scale: %d", scale);
|
||||
|
||||
dest = Image::create(src->width() * scale, src->height() * scale, src->format());
|
||||
if (!dest)
|
||||
return nullptr;
|
||||
|
||||
if (dest->isIndexed())
|
||||
dest->setPaletteFromImage(src);
|
||||
|
||||
/*
|
||||
* Each pixel in the source image is translated into four (or
|
||||
* nine) in the destination. The destination pixels are dependant
|
||||
* on the pixel itself, and the eight surrounding pixels (E is the
|
||||
* original pixel):
|
||||
*
|
||||
* A B C
|
||||
* D E F
|
||||
* G H I
|
||||
*/
|
||||
|
||||
for (ii = 0; ii < n; ii++) {
|
||||
for (y = (src->height() / n) * ii; y < (src->height() / n) * (ii + 1); y++) {
|
||||
if (y == 0)
|
||||
yoff0 = 0;
|
||||
else
|
||||
yoff0 = -1;
|
||||
if (y == (src->height() / n) * (ii + 1) - 1)
|
||||
yoff1 = 0;
|
||||
else
|
||||
yoff1 = 1;
|
||||
|
||||
for (x = 0; x < src->width(); x++) {
|
||||
if (x == 0)
|
||||
xoff0 = 0;
|
||||
else
|
||||
xoff0 = -1;
|
||||
if (x == src->width() - 1)
|
||||
xoff1 = 0;
|
||||
else
|
||||
xoff1 = 1;
|
||||
|
||||
src->getPixel(x + xoff0, y + yoff0, a.r, a.g, a.b, a.a);
|
||||
src->getPixel(x, y + yoff0, b.r, b.g, b.b, b.a);
|
||||
src->getPixel(x + xoff1, y + yoff0, c.r, c.g, c.b, c.a);
|
||||
|
||||
src->getPixel(x + xoff0, y, d.r, d.g, d.b, d.a);
|
||||
src->getPixel(x, y, e.r, e.g, e.b, e.a);
|
||||
src->getPixel(x + xoff1, y, f.r, f.g, f.b, f.a);
|
||||
|
||||
src->getPixel(x + xoff0, y + yoff1, g.r, g.g, g.b, g.a);
|
||||
src->getPixel(x, y + yoff1, h.r, h.g, h.b, h.a);
|
||||
src->getPixel(x + xoff1, y + yoff1, i.r, i.g, i.b, i.a);
|
||||
|
||||
// lissen diagonals (45°,135°,225°,315°)
|
||||
// corner : if there is gradient towards a diagonal direction,
|
||||
// take the color of surrounding points in this direction
|
||||
e0 = colorEqual(d, b) && (!colorEqual(b, f)) && (!colorEqual(d, h)) ? d : e;
|
||||
e1 = colorEqual(b, f) && (!colorEqual(b, d)) && (!colorEqual(f, h)) ? f : e;
|
||||
e2 = colorEqual(d, h) && (!colorEqual(d, b)) && (!colorEqual(h, f)) ? d : e;
|
||||
e3 = colorEqual(h, f) && (!colorEqual(d, h)) && (!colorEqual(b, f)) ? f : e;
|
||||
|
||||
// lissen eight more directions (22° or 67°, 112° or 157°...)
|
||||
// middle of side : if there is a gradient towards one of these directions (middle of side direction and of direction of either diagonal around this side),
|
||||
// take the color of surrounding points in this direction
|
||||
e4 = colorEqual(e0, c) ? e0 : colorEqual(e1, a) ? e1 : e;
|
||||
e5 = colorEqual(e2, a) ? e2 : colorEqual(e0, g) ? e0 : e;
|
||||
e6 = colorEqual(e1, i) ? e1 : colorEqual(e3, c) ? e3 : e;
|
||||
e7 = colorEqual(e3, g) ? e3 : colorEqual(e2, i) ? e2 : e;
|
||||
|
||||
if (scale == 2) {
|
||||
dest->putPixel(x * 2, y * 2, e0.r, e0.g, e0.b, e0.a);
|
||||
dest->putPixel(x * 2 + 1, y * 2, e1.r, e1.g, e1.b, e1.a);
|
||||
dest->putPixel(x * 2, y * 2 + 1, e2.r, e2.g, e2.b, e2.a);
|
||||
dest->putPixel(x * 2 + 1, y * 2 + 1, e3.r, e3.g, e3.b, e3.a);
|
||||
} else if (scale == 3) {
|
||||
dest->putPixel(x * 3, y * 3, e0.r, e0.g, e0.b, e0.a);
|
||||
dest->putPixel(x * 3 + 1, y * 3, e4.r, e4.g, e4.b, e4.a);
|
||||
dest->putPixel(x * 3 + 2, y * 3, e1.r, e1.g, e1.b, e1.a);
|
||||
dest->putPixel(x * 3, y * 3 + 1, e5.r, e5.g, e5.b, e5.a);
|
||||
dest->putPixel(x * 3 + 1, y * 3 + 1, e.r, e.g, e.b, e.a);
|
||||
dest->putPixel(x * 3 + 2, y * 3 + 1, e6.r, e6.g, e6.b, e6.a);
|
||||
dest->putPixel(x * 3, y * 3 + 2, e2.r, e2.g, e2.b, e2.a);
|
||||
dest->putPixel(x * 3 + 1, y * 3 + 2, e7.r, e7.g, e7.b, e7.a);
|
||||
dest->putPixel(x * 3 + 2, y * 3 + 2, e3.r, e3.g, e3.b, e3.a);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
41
engines/ultima/ultima4/gfx/scale.h
Normal file
41
engines/ultima/ultima4/gfx/scale.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_GFX_SCALE_H
|
||||
#define ULTIMA4_GFX_SCALE_H
|
||||
|
||||
#include "ultima/ultima4/core/settings.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class Image;
|
||||
|
||||
typedef Image *(*Scaler)(Image *src, int scale, int n);
|
||||
|
||||
Scaler scalerGet(const Common::String &filter);
|
||||
int scaler3x(const Common::String &filter);
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
1405
engines/ultima/ultima4/gfx/screen.cpp
Normal file
1405
engines/ultima/ultima4/gfx/screen.cpp
Normal file
File diff suppressed because it is too large
Load Diff
308
engines/ultima/ultima4/gfx/screen.h
Normal file
308
engines/ultima/ultima4/gfx/screen.h
Normal file
@@ -0,0 +1,308 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_GFX_SCREEN_H
|
||||
#define ULTIMA4_GFX_SCREEN_H
|
||||
|
||||
#include "common/file.h"
|
||||
#include "graphics/screen.h"
|
||||
#include "ultima/ultima4/core/config.h"
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
#include "ultima/ultima4/gfx/scale.h"
|
||||
#include "ultima/ultima4/map/direction.h"
|
||||
#include "ultima/ultima4/map/map_tile.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
#define SCR_CYCLE_PER_SECOND 4
|
||||
#define SCR_CYCLE_MAX 16
|
||||
#define SCREEN_FRAME_TIME 50
|
||||
#define SCREEN_WIDTH 320
|
||||
#define SCREEN_HEIGHT 200
|
||||
|
||||
/*
|
||||
* bitmasks for LOS shadows
|
||||
*/
|
||||
#define ____H 0x01 // obscured along the horizontal face
|
||||
#define ___C_ 0x02 // obscured at the center
|
||||
#define __V__ 0x04 // obscured along the vertical face
|
||||
#define _N___ 0x80 // start of new raster
|
||||
|
||||
#define ___CH 0x03
|
||||
#define __VCH 0x07
|
||||
#define __VC_ 0x06
|
||||
|
||||
#define _N__H 0x81
|
||||
#define _N_CH 0x83
|
||||
#define _NVCH 0x87
|
||||
#define _NVC_ 0x86
|
||||
#define _NV__ 0x84
|
||||
|
||||
|
||||
enum LayoutType {
|
||||
LAYOUT_STANDARD,
|
||||
LAYOUT_GEM,
|
||||
LAYOUT_DUNGEONGEM
|
||||
};
|
||||
|
||||
enum MouseCursor {
|
||||
MC_DEFAULT,
|
||||
MC_WEST,
|
||||
MC_NORTH,
|
||||
MC_EAST,
|
||||
MC_SOUTH
|
||||
};
|
||||
|
||||
class Image;
|
||||
class Map;
|
||||
class Tile;
|
||||
class TileView;
|
||||
class Coords;
|
||||
class TileAnimSet;
|
||||
class ImageInfo;
|
||||
|
||||
struct MouseCursorSurface : public Graphics::ManagedSurface {
|
||||
Common::Point _hotspot;
|
||||
};
|
||||
|
||||
struct MouseArea {
|
||||
int _nPoints;
|
||||
struct {
|
||||
int x, y;
|
||||
} _point[3];
|
||||
MouseCursor _cursor;
|
||||
Direction _direction;
|
||||
};
|
||||
|
||||
struct Layout {
|
||||
Common::String _name;
|
||||
LayoutType _type;
|
||||
Common::Point _tileShape;
|
||||
Common::Rect _viewport;
|
||||
};
|
||||
|
||||
class Screen : public Graphics::Screen {
|
||||
private:
|
||||
MouseCursorSurface *_mouseCursors[5];
|
||||
int _currentMouseCursor;
|
||||
|
||||
Std::vector<TileAnimSet *> _tileAnimSets;
|
||||
Layout *_gemLayout;
|
||||
Common::HashMap<Common::String, int> _dungeonTileChars;
|
||||
ImageInfo *_charSetInfo;
|
||||
ImageInfo *_gemTilesInfo;
|
||||
|
||||
int _needPrompt;
|
||||
Common::Point _cursorPos;
|
||||
int _cursorStatus;
|
||||
int _cursorEnabled;
|
||||
int _los[VIEWPORT_W][VIEWPORT_H];
|
||||
//bool _continueScreenRefresh;
|
||||
uint32 _priorFrameTime;
|
||||
|
||||
public:
|
||||
Std::vector<Common::String> _gemLayoutNames;
|
||||
Std::vector<Common::String> _filterNames;
|
||||
Std::vector<Common::String> _lineOfSightStyles;
|
||||
int _currentCycle;
|
||||
TileAnimSet *_tileAnims;
|
||||
private:
|
||||
/**
|
||||
* Load the cursors
|
||||
*/
|
||||
void loadMouseCursors();
|
||||
|
||||
/**
|
||||
* Loads the data for a single cursor from the passed file
|
||||
*/
|
||||
MouseCursorSurface *loadMouseCursor(Common::File &src);
|
||||
|
||||
void screenLoadGraphicsFromConf();
|
||||
Layout *screenLoadLayoutFromConf(const ConfigElement &conf);
|
||||
|
||||
/**
|
||||
* Draw a tile graphic on the screen.
|
||||
*/
|
||||
void screenShowGemTile(Layout *layout, Map *map, MapTile &t, bool focus, int x, int y);
|
||||
|
||||
/**
|
||||
* Finds which tiles in the viewport are visible from the avatars
|
||||
* location in the middle. (original DOS algorithm)
|
||||
*/
|
||||
void screenFindLineOfSight(Std::vector<MapTile> viewportTiles[VIEWPORT_W][VIEWPORT_H]);
|
||||
|
||||
/**
|
||||
* Finds which tiles in the viewport are visible from the avatars
|
||||
* location in the middle. (original DOS algorithm)
|
||||
*/
|
||||
void screenFindLineOfSightDOS(Std::vector<MapTile> viewportTiles[VIEWPORT_W][VIEWPORT_H]);
|
||||
|
||||
/**
|
||||
* Finds which tiles in the viewport are visible from the avatars
|
||||
* location in the middle.
|
||||
*
|
||||
* A new, more accurate LOS function
|
||||
*
|
||||
* Based somewhat off Andy McFadden's 1994 article,
|
||||
* "Improvements to a Fast Algorithm for Calculating Shading
|
||||
* and Visibility in a Two-Dimensional Field"
|
||||
* -----
|
||||
* https://fadden.com/tech/fast-los.html
|
||||
*
|
||||
* This function uses a lookup table to get the correct shadowmap,
|
||||
* therefore, the table will need to be updated if the viewport
|
||||
* dimensions increase. Also, the function assumes that the
|
||||
* viewport width and height are odd values and that the player
|
||||
* is always at the center of the screen.
|
||||
*/
|
||||
void screenFindLineOfSightEnhanced(Std::vector<MapTile> viewportTiles[VIEWPORT_W][VIEWPORT_H]);
|
||||
|
||||
/**
|
||||
* Generates terms a and b for equation "ax + b = y" that defines the
|
||||
* line containing the two given points. Vertical lines are special
|
||||
* cased to return DBL_MAX for a and the x coordinate as b since they
|
||||
* cannot be represented with the above formula.
|
||||
*/
|
||||
void screenGetLineTerms(int x1, int y1, int x2, int y2, double *a, double *b);
|
||||
|
||||
/**
|
||||
* Determine if two points are on the same side of a line (or both on
|
||||
* the line). The line is defined by the terms a and b of the
|
||||
* equation "ax + b = y".
|
||||
*/
|
||||
int screenPointsOnSameSideOfLine(int x1, int y1, int x2, int y2, double a, double b);
|
||||
|
||||
int screenPointInTriangle(int x, int y, int tx1, int ty1, int tx2, int ty2, int tx3, int ty3);
|
||||
Layout *screenGetGemLayout(const Map *map);
|
||||
public:
|
||||
Std::vector<Layout *> _layouts;
|
||||
Scaler _filterScaler;
|
||||
|
||||
public:
|
||||
Screen();
|
||||
~Screen();
|
||||
|
||||
void init();
|
||||
|
||||
/**
|
||||
* Reset the screen
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Sets a given mouse cursor
|
||||
*/
|
||||
void setMouseCursor(MouseCursor cursor);
|
||||
|
||||
/**
|
||||
* Re-initializes the screen and implements any changes made in settings
|
||||
*/
|
||||
void screenReInit();
|
||||
void screenWait(int numberOfAnimationFrames);
|
||||
|
||||
/**
|
||||
* Draw an image or subimage on the screen.
|
||||
*/
|
||||
void screenDrawImage(const Common::String &name, int x = 0, int y = 0);
|
||||
void screenDrawImageInMapArea(const Common::String &bkgd);
|
||||
|
||||
void screenFrame();
|
||||
void screenCycle();
|
||||
void screenEraseMapArea();
|
||||
void screenEraseTextArea(int x, int y, int width, int height);
|
||||
void screenGemUpdate();
|
||||
|
||||
void screenMessage(const char *fmt, ...);
|
||||
void screenPrompt();
|
||||
void screenRedrawMapArea();
|
||||
void screenRedrawTextArea(int x, int y, int width, int height);
|
||||
|
||||
/**
|
||||
* Scroll the text in the message area up one position.
|
||||
*/
|
||||
void screenScrollMessageArea();
|
||||
|
||||
/**
|
||||
* Do the tremor spell effect where the screen shakes.
|
||||
*/
|
||||
void screenShake(int iterations);
|
||||
|
||||
/**
|
||||
* Draw a character from the charset onto the screen.
|
||||
*/
|
||||
void screenShowChar(int chr, int x, int y);
|
||||
void screenTextAt(int x, int y, const char *fmt, ...);
|
||||
|
||||
/**
|
||||
* Change the current text color
|
||||
*/
|
||||
void screenTextColor(int color);
|
||||
bool screenTileUpdate(TileView *view, const Coords &coords, bool redraw = true); //Returns true if the screen was affected
|
||||
|
||||
/**
|
||||
* Redraw the screen. If showmap is set, the normal map is drawn in
|
||||
* the map area. If blackout is set, the map area is blacked out. If
|
||||
* neither is set, the map area is left untouched.
|
||||
*/
|
||||
void screenUpdate(TileView *view, bool showmap, bool blackout);
|
||||
void screenUpdateCursor();
|
||||
void screenUpdateMoons();
|
||||
void screenUpdateWind();
|
||||
Std::vector<MapTile> screenViewportTile(uint width, uint height, int x, int y, bool &focus);
|
||||
|
||||
void screenShowCursor();
|
||||
void screenHideCursor();
|
||||
void screenEnableCursor();
|
||||
void screenDisableCursor();
|
||||
void screenSetCursorPos(int x, int y);
|
||||
|
||||
/**
|
||||
* Determine if the given point is within a mouse area.
|
||||
*/
|
||||
int screenPointInMouseArea(int x, int y, const MouseArea *area);
|
||||
|
||||
/**
|
||||
* Scale an image up. The resulting image will be scale * the
|
||||
* original dimensions. The original image is no longer deleted.
|
||||
* n is the number of tiles in the image; each tile is filtered
|
||||
* separately. filter determines whether or not to filter the
|
||||
* resulting image.
|
||||
*/
|
||||
Image *screenScale(Image *src, int scale, int n, int filter);
|
||||
|
||||
/**
|
||||
* Scale an image down. The resulting image will be 1/scale * the
|
||||
* original dimensions. The original image is no longer deleted.
|
||||
*/
|
||||
Image *screenScaleDown(Image *src, int scale);
|
||||
};
|
||||
|
||||
extern Screen *g_screen;
|
||||
|
||||
extern const Std::vector<Common::String> &screenGetGemLayoutNames();
|
||||
extern const Std::vector<Common::String> &screenGetFilterNames();
|
||||
extern const Std::vector<Common::String> &screenGetLineOfSightStyles();
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
58
engines/ultima/ultima4/gfx/textcolor.h
Normal file
58
engines/ultima/ultima4/gfx/textcolor.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_GFX_TEXTCOLOR_H
|
||||
#define ULTIMA4_GFX_TEXTCOLOR_H
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
#define TEXT_BG_INDEX 0
|
||||
#define TEXT_FG_PRIMARY_INDEX 15
|
||||
#define TEXT_FG_SECONDARY_INDEX 7
|
||||
#define TEXT_FG_SHADOW_INDEX 80
|
||||
|
||||
|
||||
//
|
||||
// text foreground colors
|
||||
//
|
||||
enum ColorFG {
|
||||
FG_GREY = '\023',
|
||||
FG_BLUE = '\024',
|
||||
FG_PURPLE = '\025',
|
||||
FG_GREEN = '\026',
|
||||
FG_RED = '\027',
|
||||
FG_YELLOW = '\030',
|
||||
FG_WHITE = '\031'
|
||||
};
|
||||
|
||||
//
|
||||
// text background colors
|
||||
//
|
||||
enum ColorBG {
|
||||
BG_NORMAL = '\032',
|
||||
BG_BRIGHT = '\033'
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user