[Scummvm-git-logs] scummvm master -> 36cfb5f6f932fb5ac259a43eb1c8256531e7d1ae
neuromancer
noreply at scummvm.org
Sat Jun 13 06:14:05 UTC 2026
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
36cfb5f6f9 PRIVATE: added option to enhance contrast of reading material
Commit: 36cfb5f6f932fb5ac259a43eb1c8256531e7d1ae
https://github.com/scummvm/scummvm/commit/36cfb5f6f932fb5ac259a43eb1c8256531e7d1ae
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-13T08:10:25+02:00
Commit Message:
PRIVATE: added option to enhance contrast of reading material
Changed paths:
A engines/private/paper.cpp
A engines/private/paper.h
engines/private/detection.cpp
engines/private/detection.h
engines/private/metaengine.cpp
engines/private/module.mk
engines/private/private.cpp
engines/private/private.h
diff --git a/engines/private/detection.cpp b/engines/private/detection.cpp
index 29334ed0885..6650c63afd5 100644
--- a/engines/private/detection.cpp
+++ b/engines/private/detection.cpp
@@ -48,7 +48,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::EN_USA,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
- GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // Demo from the US release v1.0.0.23
@@ -58,7 +58,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::EN_USA,
Common::kPlatformWindows,
ADGF_DEMO,
- GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // EU release (UK)
@@ -68,7 +68,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::EN_GRB,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
- GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // Demo from the EU release
@@ -78,7 +78,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::EN_GRB,
Common::kPlatformWindows,
ADGF_DEMO,
- GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // Demo from PCGamer Disc 2.6 JULY 1996 v1.0.0.12
@@ -88,7 +88,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::EN_USA,
Common::kPlatformWindows,
ADGF_DEMO,
- GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // Another demo
@@ -98,7 +98,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::EN_USA,
Common::kPlatformWindows,
ADGF_DEMO,
- GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // EU release (ES)
@@ -108,7 +108,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::ES_ESP,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
- GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // Demo from the EU release (ES)
@@ -118,7 +118,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::ES_ESP,
Common::kPlatformWindows,
ADGF_DEMO,
- GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // EU release (FR)
@@ -128,7 +128,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::FR_FRA,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
- GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO3(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // EU release (DE)
@@ -138,7 +138,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
- GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO3(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // Promotional demo disc
@@ -148,7 +148,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::EN_USA,
Common::kPlatformWindows,
ADGF_DEMO,
- GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO3(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // Demo from the EU release (DE)
@@ -158,7 +158,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_DEMO,
- GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO3(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // Demo from the EU release (FR)
@@ -168,7 +168,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::FR_FRA,
Common::kPlatformWindows,
ADGF_DEMO,
- GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO3(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // RU release
@@ -178,7 +178,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::RU_RUS,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
- GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO3(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // KO release
@@ -188,7 +188,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::KO_KOR,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
- GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO3(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // JP release
@@ -198,7 +198,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::JA_JPN,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
- GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO3(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // MacOS release (US)
@@ -208,7 +208,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::EN_USA,
Common::kPlatformMacintosh,
ADGF_NO_FLAGS,
- GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // MacOS release (US) uninstalled
@@ -217,7 +217,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::EN_USA,
Common::kPlatformMacintosh,
ADGF_NO_FLAGS,
- GUIO1(GUIO_NOMIDI)
+ GUIO2(GUIO_NOMIDI, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // MacOS release (JP) uninstalled
@@ -226,7 +226,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::JA_JPN,
Common::kPlatformMacintosh,
ADGF_NO_FLAGS,
- GUIO1(GUIO_NOMIDI)
+ GUIO2(GUIO_NOMIDI, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // MacOS demo (US)
@@ -236,7 +236,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::EN_USA,
Common::kPlatformMacintosh,
ADGF_DEMO,
- GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+ GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
{
"private-eye", // MacOS demo (US) uninstalled
@@ -245,7 +245,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::EN_USA,
Common::kPlatformMacintosh,
ADGF_DEMO,
- GUIO1(GUIO_NOMIDI)
+ GUIO2(GUIO_NOMIDI, GAMEOPTION_READING_MATERIAL_CONTRAST)
},
AD_TABLE_END_MARKER
};
diff --git a/engines/private/detection.h b/engines/private/detection.h
index 7e0d803276d..42e899394ab 100644
--- a/engines/private/detection.h
+++ b/engines/private/detection.h
@@ -3,5 +3,6 @@
#define GAMEOPTION_SFX_SUBTITLES GUIO_GAMEOPTIONS1
#define GAMEOPTION_HIGHLIGHT_MASKS GUIO_GAMEOPTIONS2
+#define GAMEOPTION_READING_MATERIAL_CONTRAST GUIO_GAMEOPTIONS3
#endif // PRIVATE_DETECTION_H
diff --git a/engines/private/metaengine.cpp b/engines/private/metaengine.cpp
index bcf2aa24882..bdcab09986f 100644
--- a/engines/private/metaengine.cpp
+++ b/engines/private/metaengine.cpp
@@ -55,6 +55,17 @@ static const ADExtraGuiOptionsMap optionsList[] = {
0
}
},
+ {
+ GAMEOPTION_READING_MATERIAL_CONTRAST,
+ {
+ _s("Increased contrast for small print"),
+ _s("Enhance small printed paper close-ups for improved readability."),
+ "readingMaterialContrast",
+ false,
+ 0,
+ 0
+ }
+ },
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
diff --git a/engines/private/module.mk b/engines/private/module.mk
index 44de2aabf26..300b695fbd5 100644
--- a/engines/private/module.mk
+++ b/engines/private/module.mk
@@ -8,6 +8,7 @@ MODULE_OBJS := \
grammar.o \
lexer.o \
metaengine.o \
+ paper.o \
private.o \
savegame.o \
symbol.o
diff --git a/engines/private/paper.cpp b/engines/private/paper.cpp
new file mode 100644
index 00000000000..cb7776cc896
--- /dev/null
+++ b/engines/private/paper.cpp
@@ -0,0 +1,274 @@
+/* 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 "common/array.h"
+#include "common/path.h"
+#include "common/str.h"
+#include "common/util.h"
+#include "graphics/palette.h"
+#include "graphics/surface.h"
+
+#include "private/paper.h"
+
+namespace Private {
+
+const int kPaperScanTransparentBackgroundLuma = 180;
+
+// Nearest palette color in YCbCr space among the allowed entries. Chroma
+// errors are weighted heavier than luminance ones: a wrong brightness just
+// looks like paper grain, while a wrong hue stands out as colored speckle.
+byte findNearestUsedColor(const byte *luma, const byte *cb, const byte *cr,
+ const Common::Array<byte> &allowedColors, int targetY, int targetCb, int targetCr) {
+ byte bestColor = allowedColors[0];
+ int bestDistance = 0x7fffffff;
+
+ for (uint i = 0; i < allowedColors.size(); i++) {
+ byte color = allowedColors[i];
+ int dy = luma[color] - targetY;
+ int dcb = cb[color] - targetCb;
+ int dcr = cr[color] - targetCr;
+ int distance = 2 * dy * dy + 5 * (dcb * dcb + dcr * dcr);
+
+ if (distance < bestDistance) {
+ bestDistance = distance;
+ bestColor = color;
+ }
+ }
+
+ return bestColor;
+}
+
+void blurPlane(const Graphics::Surface &image, byte transparentColor, const byte *src, byte *dst, byte *tmp) {
+ const int kernel[5] = {1, 14, 34, 14, 1};
+ const int width = image.w;
+ const int height = image.h;
+
+ for (int y = 0; y < height; y++) {
+ const byte *row = (const byte *)image.getBasePtr(0, y);
+ const byte *srcRow = src + y * width;
+ byte *tmpRow = tmp + y * width;
+ for (int x = 0; x < width; x++) {
+ int center = srcRow[x];
+ int sum = 0;
+ for (int k = -2; k <= 2; k++) {
+ int xx = x + k;
+ bool outside = xx < 0 || xx >= width || row[xx] == transparentColor;
+ sum += kernel[k + 2] * (outside ? center : srcRow[xx]);
+ }
+ tmpRow[x] = (sum + 32) >> 6;
+ }
+ }
+
+ for (int y = 0; y < height; y++) {
+ const byte *tmpRow = tmp + y * width;
+ byte *dstRow = dst + y * width;
+ for (int x = 0; x < width; x++) {
+ int center = tmpRow[x];
+ int sum = 0;
+ for (int k = -2; k <= 2; k++) {
+ int yy = y + k;
+ bool outside = yy < 0 || yy >= height;
+ if (!outside) {
+ const byte *row = (const byte *)image.getBasePtr(0, yy);
+ outside = row[x] == transparentColor;
+ }
+ sum += kernel[k + 2] * (outside ? center : tmp[yy * width + x]);
+ }
+ dstRow[x] = (sum + 32) >> 6;
+ }
+ }
+}
+
+bool isPaperScanImage(const Common::Path &path) {
+ Common::String pathString = path.toString('/');
+ pathString.toLowercase();
+
+ size_t fileNamePos = pathString.findLastOf('/');
+ Common::String fileName = (fileNamePos == Common::String::npos) ?
+ pathString : pathString.substr(fileNamePos + 1);
+
+ if (!fileName.hasSuffix(".bmp"))
+ return false;
+
+ if (fileName.contains("mask") || fileName.contains("msk") || fileName.contains("mk"))
+ return false;
+
+ if (pathString.contains("inface/dossiers/") && fileName.hasPrefix("filec"))
+ return true;
+
+ if (!pathString.contains("/search_s/"))
+ return false;
+
+ return fileName.contains("nwsc") ||
+ fileName.contains("newsc") ||
+ fileName.contains("magc") ||
+ fileName.contains("formcu") ||
+ fileName.contains("ltrcu") ||
+ fileName.contains("liccu") ||
+ fileName.contains("billcu") ||
+ fileName.contains("bilcu");
+}
+
+// Readability enhancement for document close-ups (newspapers, magazines,
+// letters): stretch the luminance histogram so it spans the full range
+// (clipping 1% of pixels at each end) and apply a gentle unsharp mask.
+//
+// The paper in these scans is dithered with alternately tinted entries
+// that read as colored speckle once the contrast is raised, so the
+// chroma is smoothed over a small neighborhood and every pixel is mapped
+// to the palette entry nearest to (new luminance, smoothed chroma),
+// which collapses the dither into a uniform paper tint.
+//
+// The palette itself is never modified, and pixels are only remapped to
+// palette indices the image already uses: the palette installed when the
+// image is drawn is not necessarily this image's own (scene images share
+// index conventions), so an index this image never referenced may show
+// up as an arbitrary color on screen even though it looks correct in the
+// image's own palette.
+bool enhancePaperScanImage(Graphics::Surface *image, const byte *palette, byte transparentColor) {
+ if (image->format.bytesPerPixel != 1)
+ return false;
+
+ const int width = image->w;
+ const int height = image->h;
+
+ byte luma[Graphics::PALETTE_COUNT], chromaB[Graphics::PALETTE_COUNT], chromaR[Graphics::PALETTE_COUNT];
+ for (uint32 color = 0; color < Graphics::PALETTE_COUNT; color++) {
+ int r = palette[3 * color + 0];
+ int g = palette[3 * color + 1];
+ int b = palette[3 * color + 2];
+ luma[color] = (77 * r + 150 * g + 29 * b) >> 8;
+ chromaB[color] = (byte)(128 + (-43 * r - 85 * g + 128 * b) / 256);
+ chromaR[color] = (byte)(128 + (128 * r - 107 * g - 21 * b) / 256);
+ }
+
+ uint32 histogram[Graphics::PALETTE_COUNT] = {};
+ bool used[Graphics::PALETTE_COUNT] = {};
+ uint32 opaquePixelCount = 0;
+ uint32 histogramPixelCount = 0;
+
+ for (int y = 0; y < height; y++) {
+ const byte *row = (const byte *)image->getBasePtr(0, y);
+ for (int x = 0; x < width; x++) {
+ byte color = row[x];
+ if (color == transparentColor) {
+ histogram[kPaperScanTransparentBackgroundLuma]++;
+ histogramPixelCount++;
+ continue;
+ }
+
+ histogram[luma[color]]++;
+ used[color] = true;
+ opaquePixelCount++;
+ histogramPixelCount++;
+ }
+ }
+
+ if (opaquePixelCount < (uint32)(width * height / 30))
+ return false;
+
+ Common::Array<byte> allowedColors;
+ allowedColors.reserve(Graphics::PALETTE_COUNT);
+ for (uint32 color = 0; color < Graphics::PALETTE_COUNT; color++) {
+ if (used[color] && color != transparentColor)
+ allowedColors.push_back(color);
+ }
+ if (allowedColors.empty())
+ return false;
+
+ uint32 cutoff = histogramPixelCount / 100;
+ uint32 accumulated = 0;
+ int lowCut = 0;
+ while (lowCut < 255 && accumulated + histogram[lowCut] <= cutoff)
+ accumulated += histogram[lowCut++];
+
+ accumulated = 0;
+ int highCut = 255;
+ while (highCut > lowCut && accumulated + histogram[highCut] <= cutoff)
+ accumulated += histogram[highCut--];
+
+ if (highCut <= lowCut) {
+ lowCut = 0;
+ highCut = 255;
+ }
+
+ byte stretched[Graphics::PALETTE_COUNT];
+ for (int level = 0; level < Graphics::PALETTE_COUNT; level++)
+ stretched[level] = (byte)CLIP((level - lowCut) * 255 / (highCut - lowCut), 0, 255);
+
+ const uint32 planeSize = width * height;
+ Common::Array<byte> yPlane(planeSize), yBlur(planeSize);
+ Common::Array<byte> cbPlane(planeSize), crPlane(planeSize);
+ Common::Array<byte> scratch(planeSize);
+ const uint32 cacheSize = Graphics::PALETTE_COUNT * 64 * 64;
+ Common::Array<int16> nearestCache(cacheSize, -1);
+
+ for (int y = 0; y < height; y++) {
+ const byte *row = (const byte *)image->getBasePtr(0, y);
+ byte *yRow = yPlane.data() + y * width;
+ byte *cbRow = cbPlane.data() + y * width;
+ byte *crRow = crPlane.data() + y * width;
+ for (int x = 0; x < width; x++) {
+ byte color = row[x];
+ yRow[x] = stretched[luma[color]];
+ cbRow[x] = chromaB[color];
+ crRow[x] = chromaR[color];
+ }
+ }
+
+ blurPlane(*image, transparentColor, yPlane.data(), yBlur.data(), scratch.data());
+ blurPlane(*image, transparentColor, cbPlane.data(), cbPlane.data(), scratch.data());
+ blurPlane(*image, transparentColor, cbPlane.data(), cbPlane.data(), scratch.data());
+ blurPlane(*image, transparentColor, crPlane.data(), crPlane.data(), scratch.data());
+ blurPlane(*image, transparentColor, crPlane.data(), crPlane.data(), scratch.data());
+
+ for (int y = 0; y < height; y++) {
+ byte *row = (byte *)image->getBasePtr(0, y);
+ const byte *yRow = yPlane.data() + y * width;
+ const byte *blurRow = yBlur.data() + y * width;
+ const byte *cbRow = cbPlane.data() + y * width;
+ const byte *crRow = crPlane.data() + y * width;
+ for (int x = 0; x < width; x++) {
+ byte color = row[x];
+ if (color == transparentColor)
+ continue;
+
+ int value = yRow[x];
+ int diff = value - blurRow[x];
+ if (diff > 2 || diff < -2)
+ value += diff * 120 / 100;
+ value = CLIP(value, 0, 255);
+
+ int targetCb = (cbRow[x] & ~3) + 2;
+ int targetCr = (crRow[x] & ~3) + 2;
+
+ uint32 cacheKey = (value << 12) | ((cbRow[x] >> 2) << 6) | (crRow[x] >> 2);
+ if (nearestCache[cacheKey] < 0)
+ nearestCache[cacheKey] = findNearestUsedColor(luma, chromaB, chromaR,
+ allowedColors, value, targetCb, targetCr);
+ row[x] = (byte)nearestCache[cacheKey];
+ }
+ }
+
+ return true;
+}
+
+} // End of namespace Private
diff --git a/engines/private/paper.h b/engines/private/paper.h
new file mode 100644
index 00000000000..c7b3d07bc03
--- /dev/null
+++ b/engines/private/paper.h
@@ -0,0 +1,42 @@
+/* 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 PRIVATE_PAPER_H
+#define PRIVATE_PAPER_H
+
+#include "common/scummsys.h"
+
+namespace Common {
+class Path;
+}
+
+namespace Graphics {
+struct Surface;
+}
+
+namespace Private {
+
+bool isPaperScanImage(const Common::Path &path);
+bool enhancePaperScanImage(Graphics::Surface *image, const byte *palette, byte transparentColor);
+
+} // End of namespace Private
+
+#endif // PRIVATE_PAPER_H
diff --git a/engines/private/private.cpp b/engines/private/private.cpp
index 45c82bcee1c..f833cd17e73 100644
--- a/engines/private/private.cpp
+++ b/engines/private/private.cpp
@@ -42,6 +42,7 @@
#include "private/decompiler.h"
#include "private/grammar.h"
+#include "private/paper.h"
#include "private/private.h"
#include "private/savegame.h"
#include "private/tokens.h"
@@ -59,6 +60,9 @@ PrivateEngine::PrivateEngine(OSystem *syst, const ADGameDescription *gd)
_defaultCursor(nullptr),
_screenW(640), _screenH(480) {
_highlightMasks = false;
+ _readingMaterialContrast = false;
+ _paperScanFilteringActive = false;
+ _paperScanPreviousFiltering = false;
_rnd = new Common::RandomSource("private");
// Global object for external reference
@@ -130,6 +134,7 @@ PrivateEngine::PrivateEngine(OSystem *syst, const ADGameDescription *gd)
}
PrivateEngine::~PrivateEngine() {
+ setPaperScanFiltering(false);
destroyVideo();
destroySubtitles();
@@ -273,6 +278,9 @@ Common::Error PrivateEngine::run() {
if (!Common::parseBool(ConfMan.get("highlightMasks"), _shouldHighlightMasks))
warning("Failed to parse bool from highlightMasks options");
+ if (!Common::parseBool(ConfMan.get("readingMaterialContrast"), _readingMaterialContrast))
+ warning("Failed to parse bool from readingMaterialContrast options");
+
if (!_useSubtitles && _sfxSubtitles) {
warning("SFX subtitles are enabled, but no subtitles will be shown");
}
@@ -515,6 +523,8 @@ void PrivateEngine::initFuncs() {
}
void PrivateEngine::clearAreas() {
+ setPaperScanFiltering(false);
+
for (MaskList::const_iterator it = _masks.begin(); it != _masks.end(); ++it) {
const MaskInfo &m = *it;
if (m.surf != nullptr) {
@@ -2766,6 +2776,11 @@ Graphics::Surface *PrivateEngine::decodeImage(const Common::String &name, byte *
swapImageColors(newImage, currentPalette, maskTransparentColor, _transparentColor);
}
+ if (_readingMaterialContrast && isPaperScanImage(path)) {
+ if (enhancePaperScanImage(newImage, currentPalette, _transparentColor))
+ setPaperScanFiltering(true);
+ }
+
return newImage;
}
@@ -2854,6 +2869,31 @@ void PrivateEngine::swapImageColors(Graphics::Surface *image, byte *palette, uin
}
}
+void PrivateEngine::setPaperScanFiltering(bool enabled) {
+ if (!_system->hasFeature(OSystem::kFeatureFilteringMode))
+ return;
+
+ if (enabled) {
+ if (_paperScanFilteringActive)
+ return;
+
+ _paperScanPreviousFiltering = _system->getFeatureState(OSystem::kFeatureFilteringMode);
+ if (!_paperScanPreviousFiltering) {
+ _system->beginGFXTransaction();
+ _system->setFeatureState(OSystem::kFeatureFilteringMode, true);
+ _system->endGFXTransaction();
+ }
+ _paperScanFilteringActive = true;
+ } else if (_paperScanFilteringActive) {
+ if (_system->getFeatureState(OSystem::kFeatureFilteringMode) != _paperScanPreviousFiltering) {
+ _system->beginGFXTransaction();
+ _system->setFeatureState(OSystem::kFeatureFilteringMode, _paperScanPreviousFiltering);
+ _system->endGFXTransaction();
+ }
+ _paperScanFilteringActive = false;
+ }
+}
+
void PrivateEngine::loadImage(const Common::String &name, int x, int y) {
debugC(1, kPrivateDebugFunction, "%s(%s,%d,%d)", __FUNCTION__, name.c_str(), x, y);
byte *palette;
diff --git a/engines/private/private.h b/engines/private/private.h
index d9f25a3c236..ee13e68f453 100644
--- a/engines/private/private.h
+++ b/engines/private/private.h
@@ -257,6 +257,7 @@ private:
public:
bool _shouldHighlightMasks;
bool _highlightMasks;
+ bool _readingMaterialContrast;
PrivateEngine(OSystem *syst, const ADGameDescription *gd);
~PrivateEngine();
@@ -335,6 +336,7 @@ public:
void remapImage(uint16 ncolors, const Graphics::Surface *oldImage, const byte *oldPalette, Graphics::Surface *newImage, const byte *currentPalette);
static uint32 findMaskTransparentColor(const byte *palette, uint32 defaultColor);
static void swapImageColors(Graphics::Surface *image, byte *palette, uint32 a, uint32 b);
+ void setPaperScanFiltering(bool enabled);
void loadImage(const Common::String &file, int x, int y);
void drawScreenFrame(const byte *videoPalette);
@@ -365,6 +367,8 @@ public:
Common::Point _origin;
void drawScreen();
bool _needToDrawScreenFrame;
+ bool _paperScanFilteringActive;
+ bool _paperScanPreviousFiltering;
// settings
Common::String _nextSetting;
More information about the Scummvm-git-logs
mailing list