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
|
||||
Reference in New Issue
Block a user