Compare commits

...

9 commits

Author SHA1 Message Date
Cameron Cawley e5bc220045
Merge 31011c5b37 into c78780bca0 2024-05-14 20:44:05 +02:00
Torbjörn Andersson c78780bca0 AGI: Clarified text delay comment at m-kiewitz's request 2024-05-14 20:25:48 +02:00
Strangerke e35ad1e428 BAGEL: Work on fatal errors in CBofDataFile and CBofStringTable 2024-05-14 18:59:26 +01:00
Eugene Sandulenko f21878353f I18N: Add translation (Chinese (Traditional)) 2024-05-14 16:29:15 +00:00
Eugene Sandulenko 126cba16cf
DIRECTOR: Factored out XPFloat reading 2024-05-14 18:27:58 +02:00
复予 a9e5a2fa01 I18N: Update translation (Chinese (zh))
Currently translated at 23.3% (499 of 2135 strings)
2024-05-14 12:42:55 +00:00
Cameron Cawley 31011c5b37 GUI: Make use of simple blitting routines where possible 2024-05-08 22:38:38 +01:00
Cameron Cawley 6f36512f77 GRAPHICS: Add a common function for detecting transparent surfaces 2024-05-08 22:38:38 +01:00
Cameron Cawley 3964255a36 GRAPHICS: Add simplified blitting routines to ManagedSurface 2024-05-08 19:39:04 +01:00
26 changed files with 11216 additions and 229 deletions

View file

@ -387,7 +387,9 @@ bool TextMgr::messageBox(const char *textPtr) {
uint32 windowTimer = _vm->getVar(VM_VAR_WINDOW_AUTO_CLOSE_TIMER);
debugC(3, kDebugLevelText, "blocking window v21=%d", windowTimer);
windowTimer = windowTimer * 20; // 1 = 0.5 seconds (20 cycles)
// 1 = 0.5 seconds. NB: ScummVM runs at 40 fps, not 20, so we have
// to multiply by 20, not 10, to get the number of cycles.
windowTimer = windowTimer * 20;
_messageBoxCancelled = false;
_vm->inGameTimerResetPassedCycles();

View file

@ -174,20 +174,18 @@ ErrorCode CBofDataFile::open() {
assert(isValidObject(this));
// Only continue if there is no current error
if (_errCode == ERR_NONE) {
if (_stream == nullptr) {
if (!(_lFlags & CDF_READONLY)) {
if (_lFlags & CDF_SAVEFILE) {
if (_lFlags & CDF_CREATE)
create();
} else if (!fileExists(_szFileName))
if (_errCode == ERR_NONE && _stream == nullptr) {
if (!(_lFlags & CDF_READONLY)) {
if (_lFlags & CDF_SAVEFILE) {
if (_lFlags & CDF_CREATE)
create();
}
} else if (!fileExists(_szFileName))
create();
}
if (_stream == nullptr) {
// Open data file
CBofFile::open(_szFileName, _lFlags);
}
if (_stream == nullptr) {
// Open data file
CBofFile::open(_szFileName, _lFlags);
}
}
@ -242,32 +240,30 @@ ErrorCode CBofDataFile::readHeader() {
if (_lHeaderLength != 0) {
// Allocate buffer to hold header
if ((_pHeader = new HEADER_REC[(int)_lNumRecs]) != nullptr) {
// Seek to start of header
seek(_lHeaderStart);
_pHeader = new HEADER_REC[(int)_lNumRecs];
if (_pHeader == nullptr)
fatalError(ERR_MEMORY, buildString("Could not allocate footer for file '%s'", _szFileName));
// Read header
ErrorCode errCode = ERR_NONE;
for (int i = 0; i < _lNumRecs && errCode == ERR_NONE; ++i) {
errCode = read(_pHeader[i]);
}
// Seek to start of header
seek(_lHeaderStart);
if (errCode == ERR_NONE) {
uint32 lCrc = calculateCRC(&_pHeader->_lOffset, 4 * _lNumRecs);
// Read header
ErrorCode errCode = ERR_NONE;
for (int i = 0; i < _lNumRecs && errCode == ERR_NONE; ++i) {
errCode = read(_pHeader[i]);
}
if (lCrc != stHeaderInfo._lFootCrc) {
logError(buildString("Error: '%s' has invalid footer", _szFileName));
_errCode = ERR_CRC;
}
if (errCode == ERR_NONE) {
uint32 lCrc = calculateCRC(&_pHeader->_lOffset, 4 * _lNumRecs);
} else {
logError(buildString("Error: Could not read footer in file '%s'", _szFileName));
_errCode = ERR_FREAD;
if (lCrc != stHeaderInfo._lFootCrc) {
logError(buildString("Error: '%s' has invalid footer", _szFileName));
_errCode = ERR_CRC;
}
} else {
logError(buildString("Error: Could not allocate footer for file '%s'", _szFileName));
_errCode = ERR_MEMORY;
logError(buildString("Error: Could not read footer in file '%s'", _szFileName));
_errCode = ERR_FREAD;
}
}
@ -505,35 +501,33 @@ ErrorCode CBofDataFile::writeRecord(int32 lRecNum, void *pBuf, int32 lSize, bool
// Allocate a buffer big enough for one chunk
byte *pTmpBuf = (byte *)bofAlloc(lChunkSize);
if (pTmpBuf != nullptr) {
// While there is data to move
while (lBufLength > 0) {
// Seek to beginning of the source for this chunk
setPosition(pRecInfo->_lOffset + pRecInfo->_lLength + lBufLength - lChunkSize);
if (pTmpBuf == nullptr)
fatalError(ERR_MEMORY, "Unable to allocate %ld bytes to expand record size in writeRecord()", lBufLength);
// Read the chunk
read(pTmpBuf, lChunkSize);
// While there is data to move
while (lBufLength > 0) {
// Seek to beginning of the source for this chunk
setPosition(pRecInfo->_lOffset + pRecInfo->_lLength + lBufLength - lChunkSize);
// Seek to this chunks new positon (offset by 'lDiff' bytes)
setPosition(pRecInfo->_lOffset + pRecInfo->_lLength + lBufLength - lChunkSize + lDiff);
// Read the chunk
read(pTmpBuf, lChunkSize);
// Write chunk to new position
write(pTmpBuf, lChunkSize);
// Seek to this chunks new position (offset by 'lDiff' bytes)
setPosition(pRecInfo->_lOffset + pRecInfo->_lLength + lBufLength - lChunkSize + lDiff);
// That much less to do next time thru
lBufLength -= lChunkSize;
// Write chunk to new position
write(pTmpBuf, lChunkSize);
// Last chunk is lBufLength
lChunkSize = MIN(lBufLength, lChunkSize);
}
// That much less to do next time thru
lBufLength -= lChunkSize;
// Don't need that temp buffer anymore
bofFree(pTmpBuf);
} else {
reportError(ERR_MEMORY, "Unable to allocate %ld bytes to expand record size in writeRecord()", lBufLength);
// Last chunk is lBufLength
lChunkSize = MIN(lBufLength, lChunkSize);
}
// Don't need that temp buffer anymore
bofFree(pTmpBuf);
// Tell the rest of the records that they moved
for (int i = lRecNum + 1; i < getNumberOfRecs(); i++) {
_pHeader[i]._lOffset += lDiff;
@ -563,22 +557,20 @@ ErrorCode CBofDataFile::writeRecord(int32 lRecNum, void *pBuf, int32 lSize, bool
// Allocate a buffer that could hold the largest record
byte *pTmpBuf = (byte *)bofAlloc((int)getMaxRecSize());
if (pTmpBuf != nullptr) {
for (int i = (int)lRecNum + 1; i < (int)_lNumRecs - 1; i++) {
_errCode = readRecord(i, pTmpBuf);
if (_errCode != ERR_NONE)
break;
if (pTmpBuf == nullptr)
fatalError(ERR_MEMORY, "unable to allocate a buffer of %ld bytes", getMaxRecSize());
_errCode = writeRecord(i + 1, pTmpBuf);
if (_errCode != ERR_NONE)
break;
}
for (int i = (int)lRecNum + 1; i < (int)_lNumRecs - 1; i++) {
_errCode = readRecord(i, pTmpBuf);
if (_errCode != ERR_NONE)
break;
bofFree(pTmpBuf);
} else {
_errCode = ERR_MEMORY;
_errCode = writeRecord(i + 1, pTmpBuf);
if (_errCode != ERR_NONE)
break;
}
bofFree(pTmpBuf);
}
// If we are to update the header now
@ -609,13 +601,11 @@ ErrorCode CBofDataFile::verifyRecord(int32 lRecNum) {
// Allocate space to hold this record
void *pBuf = bofAlloc((int)getRecSize(lRecNum));
if (pBuf != nullptr) {
_errCode = readRecord(lRecNum, pBuf);
bofFree(pBuf);
if (pBuf == nullptr)
fatalError(ERR_MEMORY, "Unable to allocate a buffer of %ld bytes", getRecSize(lRecNum));
} else {
_errCode = ERR_MEMORY;
}
_errCode = readRecord(lRecNum, pBuf);
bofFree(pBuf);
}
return _errCode;
@ -656,33 +646,30 @@ ErrorCode CBofDataFile::addRecord(void *pBuf, int32 lLength, bool bUpdateHeader,
_lNumRecs++;
HEADER_REC *pTmpHeader = new HEADER_REC[(int)_lNumRecs];
if (pTmpHeader != nullptr) {
if (_pHeader != nullptr) {
memcpy(pTmpHeader, _pHeader, (size_t)(HEADER_REC::size() * (_lNumRecs - 1)));
if (pTmpHeader == nullptr)
fatalError(ERR_MEMORY, "Could not allocate a data file header");
delete[] _pHeader;
}
_pHeader = pTmpHeader;
int32 lRecNum = _lNumRecs - 1;
HEADER_REC *pCurRec = &_pHeader[lRecNum];
int32 lPrevLength = HEAD_INFO::size();
int32 lPrevOffset = 0;
if (lRecNum != 0) {
lPrevLength = _pHeader[lRecNum - 1]._lLength;
lPrevOffset = _pHeader[lRecNum - 1]._lOffset;
}
pCurRec->_lLength = lLength;
pCurRec->_lOffset = lPrevOffset + lPrevLength;
writeRecord(lRecNum, pBuf, lLength, bUpdateHeader, lKey);
} else {
reportError(ERR_MEMORY, "Could not allocate a data file header");
if (_pHeader != nullptr) {
memcpy(pTmpHeader, _pHeader, (size_t)(HEADER_REC::size() * (_lNumRecs - 1)));
delete[] _pHeader;
}
_pHeader = pTmpHeader;
int32 lRecNum = _lNumRecs - 1;
HEADER_REC *pCurRec = &_pHeader[lRecNum];
int32 lPrevLength = HEAD_INFO::size();
int32 lPrevOffset = 0;
if (lRecNum != 0) {
lPrevLength = _pHeader[lRecNum - 1]._lLength;
lPrevOffset = _pHeader[lRecNum - 1]._lOffset;
}
pCurRec->_lLength = lLength;
pCurRec->_lOffset = lPrevOffset + lPrevLength;
writeRecord(lRecNum, pBuf, lLength, bUpdateHeader, lKey);
}
}
}

View file

@ -54,17 +54,14 @@ ErrorCode CBofStringTable::load(const char *pszFileName) {
// Allocate a buffer to hold entire file
_pBuf = (byte *)bofAlloc(_lBufSize + 1);
if (_pBuf != nullptr) {
memset(_pBuf, 0, _lBufSize + 1);
if (_pBuf == nullptr)
fatalError(ERR_MEMORY, "Unable to allocate %u bytes for String Table", _lBufSize);
// Read in entire file
read(_pBuf, _lBufSize);
memset(_pBuf, 0, _lBufSize + 1);
buildTable();
} else {
reportError(ERR_MEMORY, "Unable to allocate %ld bytes for String Table", _lBufSize);
}
// Read in entire file
read(_pBuf, _lBufSize);
buildTable();
// Don't need this file open anymore
close();
@ -116,16 +113,14 @@ ErrorCode CBofStringTable::buildTable() {
pBuf++;
CResString *pString = new CResString(nId, (const char *)pBuf);
if (pString != nullptr) {
// Add this string to the table
if (_pStringTable == nullptr) {
_pStringTable = pString;
} else {
_pStringTable->addToTail(pString);
}
if (pString == nullptr)
fatalError(ERR_MEMORY, "Unable to allocate a CResString");
// Add this string to the table
if (_pStringTable == nullptr) {
_pStringTable = pString;
} else {
reportError(ERR_MEMORY, "Unable to allocate a CResString");
break;
_pStringTable->addToTail(pString);
}
while (*pBuf++ != '\0') {

View file

@ -22,7 +22,6 @@
#include "common/config-manager.h"
#include "common/file.h"
#include "common/substream.h"
#include "common/xpfloat.h"
#include "director/director.h"
#include "director/cast.h"
@ -1242,10 +1241,7 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
// Floats are stored as an "80 bit IEEE Standard 754 floating
// point number (Standard Apple Numeric Environment [SANE] data type
// Extended).
uint16 signAndExponent = READ_BE_UINT16(&constsStore[pointer]);
uint64 mantissa = READ_BE_UINT64(&constsStore[pointer+2]);
constant.u.f = Common::XPFloat(signAndExponent, mantissa).toDouble(Common::XPFloat::kSemanticsSANE);
constant.u.f = readAppleFloat80(&constsStore[pointer]);
} else if (length == 8) {
constant.u.f = READ_BE_FLOAT64(&constsStore[pointer]);
} else {

View file

@ -26,6 +26,7 @@
#include "common/punycode.h"
#include "common/str-array.h"
#include "common/tokenizer.h"
#include "common/xpfloat.h"
#include "common/compression/deflate.h"
#include "director/types.h"
@ -1650,4 +1651,15 @@ void DirectorEngine::delayMillis(uint32 delay) {
_system->delayMillis(delay);
}
double readAppleFloat80(byte *ptr) {
// Floats in an "80 bit IEEE Standard 754 floating
// point number (Standard Apple Numeric Environment [SANE] data type
// Extended).
uint16 signAndExponent = READ_BE_UINT16(&ptr[0]);
uint64 mantissa = READ_BE_UINT64(&ptr[2]);
return Common::XPFloat(signAndExponent, mantissa).toDouble(Common::XPFloat::kSemanticsSANE);
}
} // End of namespace Director

View file

@ -70,6 +70,8 @@ Common::Path dumpFactoryName(const char *prefix, const char *name, const char *e
bool isButtonSprite(SpriteType spriteType);
double readAppleFloat80(byte *ptr);
class RandomState {
public:
uint32 _seed;

View file

@ -140,7 +140,7 @@ RenderedImage::RenderedImage(const Common::String &filename, bool &result) :
delete[] pFileData;
_doCleanup = true;
_alphaType = checkForTransparency();
_alphaType = _surface.detectAlpha();
return;
}
@ -260,24 +260,4 @@ void RenderedImage::copyDirectly(int posX, int posY) {
g_system->copyRectToScreen(data, _backSurface->pitch, posX, posY, w, h);
}
Graphics::AlphaType RenderedImage::checkForTransparency() const {
// Check if the source bitmap has any transparent pixels at all
Graphics::AlphaType alphaType = Graphics::ALPHA_OPAQUE;
uint32 mask = _surface.format.ARGBToColor(0xff, 0, 0, 0);
const uint32 *data = (const uint32 *)_surface.getPixels();
for (int i = 0; i < _surface.h; i++) {
for (int j = 0; j < _surface.w; j++) {
if ((*data & mask) != mask) {
if ((*data & mask) != 0)
return Graphics::ALPHA_FULL;
else
alphaType = Graphics::ALPHA_BINARY;
}
data++;
}
}
return alphaType;
}
} // End of namespace Sword25

View file

@ -113,8 +113,6 @@ private:
bool _doCleanup;
Graphics::ManagedSurface *_backSurface;
Graphics::AlphaType checkForTransparency() const;
};
} // End of namespace Sword25

View file

@ -73,39 +73,6 @@ BaseSurfaceOSystem::~BaseSurfaceOSystem() {
renderer->invalidateTicketsFromSurface(this);
}
Graphics::AlphaType hasTransparencyType(const Graphics::Surface *surf) {
if (surf->format.bytesPerPixel != 4) {
warning("hasTransparencyType:: non 32 bpp surface passed as argument");
return Graphics::ALPHA_OPAQUE;
}
uint8 r, g, b, a;
bool seenAlpha = false;
bool seenFullAlpha = false;
for (int i = 0; i < surf->h; i++) {
if (seenFullAlpha) {
break;
}
for (int j = 0; j < surf->w; j++) {
uint32 pix = *(const uint32 *)surf->getBasePtr(j, i);
surf->format.colorToARGB(pix, a, r, g, b);
if (a != 255) {
seenAlpha = true;
if (a != 0) {
seenFullAlpha = true;
break;
}
}
}
}
if (seenFullAlpha) {
return Graphics::ALPHA_FULL;
} else if (seenAlpha) {
return Graphics::ALPHA_BINARY;
} else {
return Graphics::ALPHA_OPAQUE;
}
}
//////////////////////////////////////////////////////////////////////////
bool BaseSurfaceOSystem::create(const Common::String &filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime, bool keepLoaded) {
/* BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); */
@ -188,7 +155,7 @@ bool BaseSurfaceOSystem::finishLoad() {
_surface->applyColorKey(_ckRed, _ckGreen, _ckBlue, replaceAlpha);
}
_alphaType = hasTransparencyType(_surface);
_alphaType = _surface->detectAlpha();
_valid = true;
_gameRef->addMem(_width * _height * 4);

View file

@ -51,6 +51,7 @@ typedef void (VectorRenderer::*DrawingFunctionCallback)(const Common::Rect &, co
struct DrawStep {
DrawingFunctionCallback drawingCall; /**< Pointer to drawing function */
Graphics::ManagedSurface *blitSrc;
Graphics::AlphaType alphaType;
struct Color {
uint8 r, g, b;
@ -99,6 +100,7 @@ struct DrawStep {
DrawStep() {
drawingCall = nullptr;
blitSrc = nullptr;
alphaType = Graphics::ALPHA_OPAQUE;
// fgColor, bgColor, gradColor1, gradColor2, bevelColor initialized by Color default constructor
autoWidth = autoHeight = false;
x = y = w = h = 0;
@ -472,7 +474,7 @@ public:
void drawCallback_BITMAP(const Common::Rect &area, const DrawStep &step) {
uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h);
blitManagedSurface(step.blitSrc, Common::Point(x, y));
blitManagedSurface(step.blitSrc, Common::Point(x, y), step.alphaType);
}
void drawCallback_CROSS(const Common::Rect &area, const DrawStep &step) {
@ -523,7 +525,7 @@ public:
*/
virtual void blitSurface(const Graphics::ManagedSurface *source, const Common::Rect &r) = 0;
virtual void blitManagedSurface(const Graphics::ManagedSurface *source, const Common::Point &p) = 0;
virtual void blitManagedSurface(const Graphics::ManagedSurface *source, const Common::Point &p, Graphics::AlphaType alphaType) = 0;
/**
* Draws a string into the screen. Wrapper for the Graphics::Font string drawing

View file

@ -785,7 +785,7 @@ blitSurface(const Graphics::ManagedSurface *source, const Common::Rect &r) {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
blitManagedSurface(const Graphics::ManagedSurface *source, const Common::Point &p) {
blitManagedSurface(const Graphics::ManagedSurface *source, const Common::Point &p, Graphics::AlphaType alphaType) {
Common::Rect drawRect(p.x, p.y, p.x + source->w, p.y + source->h);
drawRect.clip(_clippingArea);
drawRect.translate(-p.x, -p.y);
@ -803,7 +803,11 @@ blitManagedSurface(const Graphics::ManagedSurface *source, const Common::Point &
np = p;
}
_activeSurface->blitFrom(*source, drawRect, np);
if (alphaType != Graphics::ALPHA_OPAQUE) {
_activeSurface->transBlitFrom(*source, drawRect, np);
} else {
_activeSurface->simpleBlitFrom(*source, drawRect, np);
}
}
template<typename PixelType>

View file

@ -88,7 +88,7 @@ public:
void fillSurface() override;
void blitSurface(const Graphics::ManagedSurface *source, const Common::Rect &r) override;
void blitManagedSurface(const Graphics::ManagedSurface *source, const Common::Point &p) override;
void blitManagedSurface(const Graphics::ManagedSurface *source, const Common::Point &p, Graphics::AlphaType alphaType) override;
void applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle) override;

View file

@ -228,6 +228,73 @@ void ManagedSurface::copyFrom(const Surface &surf) {
}
}
void ManagedSurface::simpleBlitFrom(const Surface &src, const Palette *srcPalette) {
simpleBlitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Point(0, 0), srcPalette);
}
void ManagedSurface::simpleBlitFrom(const Surface &src, const Common::Point &destPos, const Palette *srcPalette) {
simpleBlitFrom(src, Common::Rect(0, 0, src.w, src.h), destPos, srcPalette);
}
void ManagedSurface::simpleBlitFrom(const Surface &src, const Common::Rect &srcRect,
const Common::Point &destPos, const Palette *srcPalette) {
simpleBlitFromInner(src, srcRect, destPos, srcPalette, false, 0);
}
void ManagedSurface::simpleBlitFrom(const ManagedSurface &src) {
simpleBlitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Point(0, 0));
}
void ManagedSurface::simpleBlitFrom(const ManagedSurface &src, const Common::Point &destPos) {
simpleBlitFrom(src, Common::Rect(0, 0, src.w, src.h), destPos);
}
void ManagedSurface::simpleBlitFrom(const ManagedSurface &src, const Common::Rect &srcRect,
const Common::Point &destPos) {
simpleBlitFromInner(src._innerSurface, srcRect, destPos, src._palette,
src._transparentColorSet, src._transparentColor);
}
void ManagedSurface::simpleBlitFromInner(const Surface &src, const Common::Rect &srcRect,
const Common::Point &destPos, const Palette *srcPalette,
bool transparentColorSet, uint transparentColor) {
Common::Rect srcRectC = srcRect;
Common::Rect dstRectC = srcRect;
dstRectC.moveTo(destPos.x, destPos.y);
clip(srcRectC, dstRectC);
const byte *srcPtr = (const byte *)src.getBasePtr(srcRectC.left, srcRectC.top);
byte *dstPtr = (byte *)getBasePtr(dstRectC.left, dstRectC.top);
if (src.format.isCLUT8()) {
assert(srcPalette);
assert(format.isCLUT8());
uint32 map[256];
convertPaletteToMap(map, srcPalette->data(), srcPalette->size(), format);
if (transparentColorSet) {
crossKeyBlitMap(dstPtr, srcPtr, pitch, src.pitch, srcRectC.width(), srcRectC.height(),
format.bytesPerPixel, map, transparentColor);
} else {
crossBlitMap(dstPtr, srcPtr, pitch, src.pitch, srcRectC.width(), srcRectC.height(),
format.bytesPerPixel, map);
}
} else {
if (transparentColorSet) {
crossKeyBlit(dstPtr, srcPtr, pitch, src.pitch, srcRectC.width(), srcRectC.height(),
format, src.format, transparentColor);
} else {
crossBlit(dstPtr, srcPtr, pitch, src.pitch, srcRectC.width(), srcRectC.height(),
format, src.format);
}
}
addDirtyRect(dstRectC);
}
void ManagedSurface::blitFrom(const Surface &src, const Palette *srcPalette) {
blitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Point(0, 0), srcPalette);
}

View file

@ -85,6 +85,13 @@ private:
*/
Palette *_palette;
protected:
/**
* Inner method for blitting.
*/
void simpleBlitFromInner(const Surface &src, const Common::Rect &srcRect,
const Common::Point &destPos, const Palette *srcPalette,
bool transparentColorSet, uint transparentColor);
/**
* Inner method for blitting.
*/
@ -306,6 +313,38 @@ public:
return Common::Rect(0, 0, this->w, this->h);
}
/**
* Copy another surface into this one.
*/
void simpleBlitFrom(const Surface &src, const Palette *srcPalette = nullptr);
/**
* Copy another surface into this one at a given destination position.
*/
void simpleBlitFrom(const Surface &src, const Common::Point &destPos, const Palette *srcPalette = nullptr);
/**
* Copy another surface into this one at a given destination position.
*/
void simpleBlitFrom(const Surface &src, const Common::Rect &srcRect,
const Common::Point &destPos, const Palette *srcPalette = nullptr);
/**
* Copy another surface into this one.
*/
void simpleBlitFrom(const ManagedSurface &src);
/**
* Copy another surface into this one at a given destination position.
*/
void simpleBlitFrom(const ManagedSurface &src, const Common::Point &destPos);
/**
* Copy another surface into this one at a given destination position.
*/
void simpleBlitFrom(const ManagedSurface &src, const Common::Rect &srcRect,
const Common::Point &destPos);
/**
* Copy another surface into this one.
*/
@ -674,6 +713,13 @@ public:
addDirtyRect(r);
}
/**
* Checks if the given surface contains alpha transparency
*/
AlphaType detectAlpha() const {
return _innerSurface.detectAlpha();
}
/**
* Return a sub-area of the screen, but only add a single initial dirty rect
* for the retrieved area.

View file

@ -443,6 +443,28 @@ bool Surface::setAlpha(uint8 alpha, bool skipTransparent) {
skipTransparent, alpha);
}
AlphaType Surface::detectAlpha() const {
if (format.isCLUT8() || format.aBits() == 0)
return ALPHA_OPAQUE;
const uint32 mask = format.ARGBToColor(0xff, 0, 0, 0);
AlphaType alphaType = ALPHA_OPAQUE;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
uint32 pixel = getPixel(x, y);
if ((pixel & mask) != mask) {
if ((pixel & mask) == 0)
alphaType = ALPHA_BINARY;
else
return ALPHA_FULL;
}
}
}
return alphaType;
}
Graphics::Surface *Surface::scale(int16 newWidth, int16 newHeight, bool filtering) const {
Graphics::Surface *target = new Graphics::Surface();

View file

@ -32,6 +32,7 @@ struct Point;
}
#include "graphics/pixelformat.h"
#include "graphics/transform_struct.h"
namespace Graphics {
@ -512,6 +513,11 @@ public:
*/
bool setAlpha(uint8 alpha, bool skipTransparent = false);
/**
* Checks if the given surface contains alpha transparency
*/
AlphaType detectAlpha() const;
/**
* Scale the data to the given size.
*

View file

@ -1240,7 +1240,7 @@ void ThemeEngine::drawPopUpWidget(const Common::Rect &r, const Common::U32String
}
}
void ThemeEngine::drawManagedSurface(const Common::Point &p, const Graphics::ManagedSurface &surface) {
void ThemeEngine::drawManagedSurface(const Common::Point &p, const Graphics::ManagedSurface &surface, Graphics::AlphaType alphaType) {
if (!ready())
return;
@ -1248,7 +1248,7 @@ void ThemeEngine::drawManagedSurface(const Common::Point &p, const Graphics::Man
return;
_vectorRenderer->setClippingRect(_clip);
_vectorRenderer->blitManagedSurface(&surface, p);
_vectorRenderer->blitManagedSurface(&surface, p, alphaType);
Common::Rect dirtyRect = Common::Rect(p.x, p.y, p.x + surface.w, p.y + surface.h);
dirtyRect.clip(_clip);

View file

@ -469,7 +469,7 @@ public:
void drawDropDownButton(const Common::Rect &r, uint32 dropdownWidth, const Common::U32String &str,
WidgetStateInfo buttonState, bool inButton, bool inDropdown, bool rtl = false);
void drawManagedSurface(const Common::Point &p, const Graphics::ManagedSurface &surface);
void drawManagedSurface(const Common::Point &p, const Graphics::ManagedSurface &surface, Graphics::AlphaType alphaType);
void drawSlider(const Common::Rect &r, int width, WidgetStateInfo state = kStateEnabled, bool rtl = false);

View file

@ -537,6 +537,8 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
if (!drawstep->blitSrc)
return parserError("The given filename hasn't been loaded into the GUI.");
drawstep->alphaType = drawstep->blitSrc->detectAlpha();
}
if (functionName == "roundedsq" || functionName == "circle" || functionName == "tab") {

View file

@ -646,6 +646,8 @@ void PicButtonWidget::setGfx(const Graphics::ManagedSurface *gfx, int statenum,
if (!isVisible() || !_boss->isVisible())
return;
_alphaType[statenum] = gfx->detectAlpha();
float sf = g_gui.getScaleFactor();
if (scale && sf != 1.0) {
Graphics::Surface *tmp2 = gfx->rawSurface().scale(gfx->w * sf, gfx->h * sf, false);
@ -686,6 +688,7 @@ void PicButtonWidget::setGfx(int w, int h, int r, int g, int b, int statenum) {
_gfx[statenum].create(w, h, requiredFormat);
_gfx[statenum].fillRect(Common::Rect(0, 0, w, h), _gfx[statenum].format.RGBToColor(r, g, b));
_alphaType[statenum] = Graphics::ALPHA_OPAQUE;
}
void PicButtonWidget::drawWidget() {
@ -693,24 +696,31 @@ void PicButtonWidget::drawWidget() {
g_gui.theme()->drawButton(Common::Rect(_x, _y, _x + _w, _y + _h), Common::U32String(), _state, getFlags());
Graphics::ManagedSurface *gfx;
Graphics::AlphaType alphaType;
if (_state == ThemeEngine::kStateHighlight)
if (_state == ThemeEngine::kStateHighlight) {
gfx = &_gfx[kPicButtonHighlight];
else if (_state == ThemeEngine::kStateDisabled)
alphaType = _alphaType[kPicButtonHighlight];
} else if (_state == ThemeEngine::kStateDisabled) {
gfx = &_gfx[kPicButtonStateDisabled];
else if (_state == ThemeEngine::kStatePressed)
alphaType = _alphaType[kPicButtonStateDisabled];
} else if (_state == ThemeEngine::kStatePressed) {
gfx = &_gfx[kPicButtonStatePressed];
else
alphaType = _alphaType[kPicButtonStatePressed];
} else {
gfx = &_gfx[kPicButtonStateEnabled];
if (!gfx->getPixels())
alphaType = _alphaType[kPicButtonStateEnabled];
}
if (!gfx->getPixels()) {
gfx = &_gfx[kPicButtonStateEnabled];
alphaType= _alphaType[kPicButtonStateEnabled];
}
if (gfx->getPixels()) {
const int x = _x + (_w - gfx->w) / 2;
const int y = _y + (_h - gfx->h) / 2;
g_gui.theme()->drawManagedSurface(Common::Point(x, y), *gfx);
g_gui.theme()->drawManagedSurface(Common::Point(x, y), *gfx, alphaType);
}
}
@ -928,7 +938,7 @@ int SliderWidget::posToValue(int pos) {
#pragma mark -
GraphicsWidget::GraphicsWidget(GuiObject *boss, int x, int y, int w, int h, bool scale, const Common::U32String &tooltip)
: Widget(boss, x, y, w, h, scale, tooltip), _gfx() {
: Widget(boss, x, y, w, h, scale, tooltip), _gfx(), _alphaType(Graphics::ALPHA_OPAQUE) {
setFlags(WIDGET_ENABLED | WIDGET_CLEARBG);
_type = kGraphicsWidget;
}
@ -938,7 +948,7 @@ GraphicsWidget::GraphicsWidget(GuiObject *boss, int x, int y, int w, int h, cons
}
GraphicsWidget::GraphicsWidget(GuiObject *boss, const Common::String &name, const Common::U32String &tooltip)
: Widget(boss, name, tooltip), _gfx() {
: Widget(boss, name, tooltip), _gfx(), _alphaType(Graphics::ALPHA_OPAQUE) {
setFlags(WIDGET_ENABLED | WIDGET_CLEARBG);
_type = kGraphicsWidget;
}
@ -970,6 +980,8 @@ void GraphicsWidget::setGfx(const Graphics::ManagedSurface *gfx, bool scale) {
_h = gfx->h;
}
_alphaType = gfx->detectAlpha();
if ((_w != gfx->w || _h != gfx->h) && _w && _h) {
Graphics::Surface *tmp2 = gfx->rawSurface().scale(_w, _h, false);
_gfx.copyFrom(*tmp2);
@ -1001,6 +1013,7 @@ void GraphicsWidget::setGfx(int w, int h, int r, int g, int b) {
_gfx.create(w, h, requiredFormat);
_gfx.fillRect(Common::Rect(0, 0, w, h), _gfx.format.RGBToColor(r, g, b));
_alphaType = Graphics::ALPHA_OPAQUE;
}
void GraphicsWidget::setGfxFromTheme(const char *name) {
@ -1014,7 +1027,7 @@ void GraphicsWidget::drawWidget() {
const int x = _x + (_w - _gfx.w) / 2;
const int y = _y + (_h - _gfx.h) / 2;
g_gui.theme()->drawManagedSurface(Common::Point(x, y), _gfx);
g_gui.theme()->drawManagedSurface(Common::Point(x, y), _gfx, _alphaType);
}
}

View file

@ -319,6 +319,7 @@ protected:
void drawWidget() override;
Graphics::ManagedSurface _gfx[kPicButtonStateMax + 1];
Graphics::AlphaType _alphaType[kPicButtonStateMax + 1];
bool _showButton;
};
@ -454,6 +455,7 @@ protected:
void drawWidget() override;
Graphics::ManagedSurface _gfx;
Graphics::AlphaType _alphaType;
};
class PathWidget : public StaticTextWidget {

View file

@ -49,8 +49,10 @@ void GridItemWidget::setActiveEntry(GridItemInfo &entry) {
void GridItemWidget::updateThumb() {
const Graphics::ManagedSurface *gfx = _grid->filenameToSurface(_activeEntry->thumbPath);
_thumbGfx.free();
if (gfx)
if (gfx) {
_thumbGfx.copyFrom(*gfx);
_thumbAlpha = _thumbGfx.detectAlpha();
}
}
void GridItemWidget::update() {
@ -119,30 +121,32 @@ void GridItemWidget::drawWidget() {
r.translate(0, kLineHeight);
}
} else {
g_gui.theme()->drawManagedSurface(Common::Point(_x + _grid->_thumbnailMargin, _y + _grid->_thumbnailMargin), _thumbGfx);
g_gui.theme()->drawManagedSurface(Common::Point(_x + _grid->_thumbnailMargin, _y + _grid->_thumbnailMargin), _thumbGfx, _thumbAlpha);
}
Graphics::AlphaType alphaType;
// Draw Platform Icon
const Graphics::ManagedSurface *platGfx = _grid->platformToSurface(_activeEntry->platform);
const Graphics::ManagedSurface *platGfx = _grid->platformToSurface(_activeEntry->platform, alphaType);
if (platGfx) {
Common::Point p(_x + thumbWidth - platGfx->w, _y + thumbHeight - platGfx->h);
g_gui.theme()->drawManagedSurface(p, *platGfx);
g_gui.theme()->drawManagedSurface(p, *platGfx, alphaType);
}
// Draw Flag
const Graphics::ManagedSurface *flagGfx = _grid->languageToSurface(_activeEntry->language);
const Graphics::ManagedSurface *flagGfx = _grid->languageToSurface(_activeEntry->language, alphaType);
if (flagGfx) {
// SVG and PNG can resize differently so it's better to use thumbWidth as reference to
// ensure all flags are aligned
Common::Point p(_x + thumbWidth - (thumbWidth / 5), _y + 5);
g_gui.theme()->drawManagedSurface(p, *flagGfx);
g_gui.theme()->drawManagedSurface(p, *flagGfx, alphaType);
}
// Draw Demo Overlay
const Graphics::ManagedSurface *demoGfx = _grid->demoToSurface(_activeEntry->extra);
const Graphics::ManagedSurface *demoGfx = _grid->demoToSurface(_activeEntry->extra, alphaType);
if (demoGfx) {
Common::Point p(_x, _y);
g_gui.theme()->drawManagedSurface(p, *demoGfx);
g_gui.theme()->drawManagedSurface(p, *demoGfx, alphaType);
}
bool validEntry = _activeEntry->validEntry;
@ -152,7 +156,7 @@ void GridItemWidget::drawWidget() {
const Graphics::ManagedSurface *darkenGfx = _grid->disabledThumbnail();
if (darkenGfx) {
Common::Point p(_x, _y);
g_gui.theme()->drawManagedSurface(p, *darkenGfx);
g_gui.theme()->drawManagedSurface(p, *darkenGfx, Graphics::ALPHA_FULL);
}
}
@ -452,6 +456,9 @@ GridWidget::~GridWidget() {
_headerEntryList.clear();
_sortedEntryList.clear();
_visibleEntryList.clear();
_platformIconsAlpha.clear();
_languageIconsAlpha.clear();
_extraIconsAlpha.clear();
}
template<typename T>
@ -468,21 +475,24 @@ const Graphics::ManagedSurface *GridWidget::filenameToSurface(const Common::Stri
return _loadedSurfaces[name];
}
const Graphics::ManagedSurface *GridWidget::languageToSurface(Common::Language languageCode) {
const Graphics::ManagedSurface *GridWidget::languageToSurface(Common::Language languageCode, Graphics::AlphaType &alphaType) {
if (languageCode == Common::UNK_LANG)
return nullptr;
alphaType = _languageIconsAlpha[languageCode];
return _languageIcons[languageCode];
}
const Graphics::ManagedSurface *GridWidget::platformToSurface(Common::Platform platformCode) {
const Graphics::ManagedSurface *GridWidget::platformToSurface(Common::Platform platformCode, Graphics::AlphaType &alphaType) {
if (platformCode == Common::kPlatformUnknown)
return nullptr;
alphaType = _platformIconsAlpha[platformCode];
return _platformIcons[platformCode];
}
const Graphics::ManagedSurface *GridWidget::demoToSurface(const Common::String extraString) {
const Graphics::ManagedSurface *GridWidget::demoToSurface(const Common::String extraString, Graphics::AlphaType &alphaType) {
if (! extraString.contains("Demo") )
return nullptr;
alphaType = _extraIconsAlpha[0];
return _extraIcons[0];
}
@ -731,6 +741,7 @@ void GridWidget::loadFlagIcons() {
Graphics::ManagedSurface *gfx = loadSurfaceFromFile(path, _flagIconWidth, _flagIconHeight);
if (gfx) {
_languageIcons[l->id] = gfx;
_languageIconsAlpha[l->id] = gfx->detectAlpha();
continue;
} // if no .svg flag is available, search for a .png
path = Common::String::format("icons/flags/%s.png", l->code);
@ -738,6 +749,7 @@ void GridWidget::loadFlagIcons() {
if (gfx) {
const Graphics::ManagedSurface *scGfx = scaleGfx(gfx, _flagIconWidth, _flagIconHeight, true);
_languageIcons[l->id] = scGfx;
_languageIconsAlpha[l->id] = gfx->detectAlpha();
if (gfx != scGfx) {
gfx->free();
delete gfx;
@ -756,6 +768,7 @@ void GridWidget::loadPlatformIcons() {
if (gfx) {
const Graphics::ManagedSurface *scGfx = scaleGfx(gfx, _platformIconWidth, _platformIconHeight, true);
_platformIcons[l->id] = scGfx;
_platformIconsAlpha[l->id] = scGfx->detectAlpha();
if (gfx != scGfx) {
gfx->free();
delete gfx;
@ -770,12 +783,14 @@ void GridWidget::loadExtraIcons() { // for now only the demo icon is available
Graphics::ManagedSurface *gfx = loadSurfaceFromFile("icons/extra/demo.svg", _extraIconWidth, _extraIconHeight);
if (gfx) {
_extraIcons[0] = gfx;
_extraIconsAlpha[0] = gfx->detectAlpha();
return;
} // if no .svg file is available, search for a .png
gfx = loadSurfaceFromFile("icons/extra/demo.png");
if (gfx) {
const Graphics::ManagedSurface *scGfx = scaleGfx(gfx, _extraIconWidth, _extraIconHeight, true);
_extraIcons[0] = scGfx;
_extraIconsAlpha[0] = scGfx->detectAlpha();
if (gfx != scGfx) {
gfx->free();
delete gfx;
@ -1051,6 +1066,9 @@ void GridWidget::reflowLayout() {
unloadSurfaces(_platformIcons);
unloadSurfaces(_languageIcons);
unloadSurfaces(_loadedSurfaces);
_platformIconsAlpha.clear();
_languageIconsAlpha.clear();
_extraIconsAlpha.clear();
if (_disabledIconOverlay)
_disabledIconOverlay->free();
reloadThumbnails();

View file

@ -102,6 +102,9 @@ protected:
Common::HashMap<int, const Graphics::ManagedSurface *> _platformIcons;
Common::HashMap<int, const Graphics::ManagedSurface *> _languageIcons;
Common::HashMap<int, const Graphics::ManagedSurface *> _extraIcons;
Common::HashMap<int, Graphics::AlphaType> _platformIconsAlpha;
Common::HashMap<int, Graphics::AlphaType> _languageIconsAlpha;
Common::HashMap<int, Graphics::AlphaType> _extraIconsAlpha;
Graphics::ManagedSurface *_disabledIconOverlay;
// Images are mapped by filename -> surface.
Common::HashMap<Common::String, const Graphics::ManagedSurface *> _loadedSurfaces;
@ -173,9 +176,9 @@ public:
void unloadSurfaces(Common::HashMap<T, const Graphics::ManagedSurface *> &surfaces);
const Graphics::ManagedSurface *filenameToSurface(const Common::String &name);
const Graphics::ManagedSurface *languageToSurface(Common::Language languageCode);
const Graphics::ManagedSurface *platformToSurface(Common::Platform platformCode);
const Graphics::ManagedSurface *demoToSurface(const Common::String extraString);
const Graphics::ManagedSurface *languageToSurface(Common::Language languageCode, Graphics::AlphaType &alphaType);
const Graphics::ManagedSurface *platformToSurface(Common::Platform platformCode, Graphics::AlphaType &alphaType);
const Graphics::ManagedSurface *demoToSurface(const Common::String extraString, Graphics::AlphaType &alphaType);
const Graphics::ManagedSurface *disabledThumbnail();
/// Update _visibleEntries from _allEntries and returns true if reload is required.
@ -232,6 +235,7 @@ public:
class GridItemWidget : public ContainerWidget, public CommandSender {
protected:
Graphics::ManagedSurface _thumbGfx;
Graphics::AlphaType _thumbAlpha;
GridItemInfo *_activeEntry;
GridWidget *_grid;

View file

@ -237,7 +237,7 @@ void RichTextWidget::drawWidget() {
_txtWnd->draw(_surface, 0, _scrolledY, _textWidth, _textHeight, 0, 0);
g_gui.theme()->drawManagedSurface(Common::Point(_x + _innerMargin, _y + _innerMargin), *_surface);
g_gui.theme()->drawManagedSurface(Common::Point(_x + _innerMargin, _y + _innerMargin), *_surface, Graphics::ALPHA_FULL);
}
void RichTextWidget::draw() {

View file

@ -11,7 +11,7 @@ msgstr ""
"Project-Id-Version: ScummVM 2.3.0git\n"
"Report-Msgid-Bugs-To: scummvm-devel@lists.scummvm.org\n"
"POT-Creation-Date: 2024-05-04 22:58+0000\n"
"PO-Revision-Date: 2024-03-21 21:40+0000\n"
"PO-Revision-Date: 2024-05-14 12:42+0000\n"
"Last-Translator: 复予 <clonewith@qq.com>\n"
"Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
"scummvm/zh/>\n"
@ -251,31 +251,31 @@ msgstr "载入"
#: gui/cloudconnectionwizard.cpp:362
msgid "Load code from file"
msgstr ""
msgstr "从文件载入代码"
#: gui/cloudconnectionwizard.cpp:379
msgid "Manual Mode: Something went wrong"
msgstr ""
msgstr "手动模式:出错了"
#: gui/cloudconnectionwizard.cpp:383
msgid "Cloud storage was not connected."
msgstr ""
msgstr "未连接云存储。"
#: gui/cloudconnectionwizard.cpp:384
msgid "Make sure the JSON code was copied correctly and retry."
msgstr ""
msgstr "请确保 JSON 代码复制正确并重试。"
#: gui/cloudconnectionwizard.cpp:385
msgid "If that doesn't work, try again from the beginning."
msgstr ""
msgstr "如果没有用,请从头再试。"
#: gui/cloudconnectionwizard.cpp:386
msgid "Error message: "
msgstr ""
msgstr "错误消息: "
#: gui/cloudconnectionwizard.cpp:400
msgid "Manual Mode: Success"
msgstr ""
msgstr "手动模式:成功"
#: gui/cloudconnectionwizard.cpp:428 gui/dlcsdialog.cpp:59
#: gui/downloaddlcsdialog.cpp:60 engines/crab/input/input.cpp:175
@ -285,11 +285,11 @@ msgstr "返回"
#: gui/cloudconnectionwizard.cpp:436 gui/imagealbum-dialog.cpp:104
#: gui/saveload-dialog.cpp:829 engines/crab/input/input.cpp:181
msgid "Next"
msgstr ""
msgstr "下一步"
#: gui/cloudconnectionwizard.cpp:499
msgid "Another Storage is working right now. Do you want to interrupt it?"
msgstr ""
msgstr "另一存储正工作中。您要中断它吗?"
#: gui/cloudconnectionwizard.cpp:499 gui/downloaddialog.cpp:118
#: gui/downloaddialog.cpp:152 gui/filebrowser-dialog.cpp:135
@ -320,12 +320,12 @@ msgstr "否"
#: gui/cloudconnectionwizard.cpp:508
msgid "Wait until current Storage finishes and try again."
msgstr ""
msgstr "请等待当前存储工作完成后再试。"
#. I18N: JSON is name of the format, this message is displayed if user entered something incorrect to the text field
#: gui/cloudconnectionwizard.cpp:536
msgid "JSON code contents are malformed."
msgstr ""
msgstr "JSON 代码内容有误。"
#: gui/cloudconnectionwizard.cpp:557 gui/downloaddialog.cpp:146
#: gui/editgamedialog.cpp:363 gui/editrecorddialog.cpp:67
@ -355,7 +355,7 @@ msgstr "确认"
#: gui/cloudconnectionwizard.cpp:558
msgid "Incorrect JSON."
msgstr ""
msgstr "JSON 不正确。"
#. I18N: error message displayed on 'Manual Mode: Failure' step of 'Cloud Connection Wizard', describing that storage connection process was interrupted
#: gui/cloudconnectionwizard.cpp:565
@ -373,20 +373,20 @@ msgstr ""
#. I18N: JSON is a file format name
#: gui/cloudconnectionwizard.cpp:631
msgid "Select JSON file copied from scummvm.org site"
msgstr ""
msgstr "选择从 scummvm.org 网站复制的 JSON 文件"
#. I18N: JSON is a file format name
#: gui/cloudconnectionwizard.cpp:637
msgid "Failed to load JSON file"
msgstr ""
msgstr "无法载入 JSON 文件"
#: gui/dlcsdialog.cpp:41
msgid "Download Freeware Games and Demos"
msgstr ""
msgstr "下载免费游戏与演示游戏"
#: gui/dlcsdialog.cpp:52
msgid "Fetching DLCs..."
msgstr ""
msgstr "正在获取 DLC..."
#: gui/dlcsdialog.cpp:60
msgid "All Downloads"
@ -536,7 +536,7 @@ msgstr "開始執行"
#: gui/dump-all-dialogs.cpp:103 gui/options.cpp:3275
msgid "icon packs"
msgstr ""
msgstr "图标包"
#: gui/dump-all-dialogs.cpp:107 gui/options.cpp:3286
msgid "shader packs"

10862
po/zh_Hant.po Normal file

File diff suppressed because it is too large Load diff