[Scummvm-git-logs] scummvm master -> 44eb2540cb5119fcdc4eedffbf7046ed6d307f5d
sev-
noreply at scummvm.org
Sat Jul 8 19:07:51 UTC 2023
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
44eb2540cb GRAPHICS: Improve BDF scaling quality.
Commit: 44eb2540cb5119fcdc4eedffbf7046ed6d307f5d
https://github.com/scummvm/scummvm/commit/44eb2540cb5119fcdc4eedffbf7046ed6d307f5d
Author: elasota (ejlasota at gmail.com)
Date: 2023-07-08T22:07:48+03:00
Commit Message:
GRAPHICS: Improve BDF scaling quality.
Instead of doing bbox-to-bbox scales, fill pixels based on distance to a reference coordinate, which ensures the same rounding behavior at each scanline.
Changed paths:
graphics/fonts/bdf.cpp
diff --git a/graphics/fonts/bdf.cpp b/graphics/fonts/bdf.cpp
index aa64354e543..6caf60e153e 100644
--- a/graphics/fonts/bdf.cpp
+++ b/graphics/fonts/bdf.cpp
@@ -726,6 +726,37 @@ BdfFont *BdfFont::loadFromCache(Common::SeekableReadStream &stream) {
return new BdfFont(data, DisposeAfterUse::YES);
}
+static Common::Rect bdfBoxToRect(int ascent, const BdfBoundingBox &bbox) {
+ int top = ascent - bbox.yOffset - bbox.height;
+ int left = bbox.xOffset;
+ int bottom = top + bbox.height;
+ int right = left + bbox.width;
+
+ return Common::Rect(left, top, right, bottom);
+}
+
+static BdfBoundingBox rectToBdfBox(int ascent, const Common::Rect &rect) {
+ BdfBoundingBox bbox;
+
+ bbox.yOffset = ascent - rect.top - rect.height();
+ bbox.xOffset = rect.left;
+ bbox.height = rect.height();
+ bbox.width = rect.width();
+
+ return bbox;
+}
+
+static BdfBoundingBox scaleBdfBoundingBox(int srcAscent, float srcReferencePointX, float srcReferencePointY, int destAscent, float destReferencePointX, float destReferencePointY, const BdfBoundingBox &srcBBox, float scale) {
+ Common::Rect srcRect = bdfBoxToRect(srcAscent, srcBBox);
+
+ int destLeft = static_cast<int>(floorf((static_cast<float>(srcRect.left) - srcReferencePointX) * scale + destReferencePointX));
+ int destRight = static_cast<int>(ceilf((static_cast<float>(srcRect.right) - srcReferencePointX) * scale + destReferencePointX));
+ int destTop = static_cast<int>(floorf((static_cast<float>(srcRect.top) - srcReferencePointY) * scale + destReferencePointY));
+ int destBottom = static_cast<int>(ceilf((static_cast<float>(srcRect.bottom) - srcReferencePointY) * scale + destReferencePointY));
+
+ return rectToBdfBox(destAscent, Common::Rect(destLeft, destTop, destRight, destBottom));
+}
+
BdfFont *BdfFont::scaleFont(const BdfFont *src, int newSize) {
if (!src) {
warning("BdfFont::scaleFont(): Empty font reference in scale font");
@@ -742,6 +773,9 @@ BdfFont *BdfFont::scaleFont(const BdfFont *src, int newSize) {
return nullptr;
}
+ // This is approximate and may not be ideal for all fonts!
+ const int scaleCenterYRelativeToBaseline = -1;
+
Graphics::Surface srcSurf;
srcSurf.create(MAX(src->getFontSize() * 2, newSize * 2), MAX(src->getFontSize() * 2, newSize * 2), PixelFormat::createFormatCLUT8());
int dstGraySize = newSize * 20 * newSize;
@@ -749,16 +783,31 @@ BdfFont *BdfFont::scaleFont(const BdfFont *src, int newSize) {
float scale = (float)newSize / (float)src->getFontSize();
+ int scaledAscent = (int)(roundf((float)src->_data.ascent * scale));
+
+ // The reference point is the center point of scaling in the old and new glyph.
+ // The coordinate values are relative to the top-left corner of the draw coordinate pixel.
+ // Since the baseline is located at the top edge of the pixel that is +ascent from the draw
+ // coordinate, we use the intersection of that edge and the left edge of the draw coordinate
+ // as the reference point. This seems to produce good results.
+ float srcReferencePointX = 0.0f;
+ float srcReferencePointY = static_cast<float>(src->_data.ascent);
+
+ float destReferencePointX = 0.0f;
+ float destReferencePointY = static_cast<float>(scaledAscent);
+
BdfFontData data;
data.maxAdvance = (int)(roundf((float)src->_data.maxAdvance * scale));
data.height = (int)(roundf((float)src->_data.height * scale));
data.size = (int)(roundf((float)src->_data.size * scale));
+ data.ascent = scaledAscent;
+ data.defaultBox = scaleBdfBoundingBox(src->_data.ascent, srcReferencePointX, srcReferencePointY, data.ascent, destReferencePointX, destReferencePointY, src->_data.defaultBox, scale);
+
data.defaultBox.width = (int)(roundf((float)src->_data.defaultBox.width * scale));
data.defaultBox.height = (int)(roundf((float)src->_data.defaultBox.height * scale));
data.defaultBox.xOffset = (int)(roundf((float)src->_data.defaultBox.xOffset * scale));
data.defaultBox.yOffset = (int)(roundf((float)src->_data.defaultBox.yOffset * scale));
- data.ascent = (int)(roundf((float)src->_data.ascent * scale));
data.firstCharacter = src->_data.firstCharacter;
data.defaultCharacter = src->_data.defaultCharacter;
data.numCharacters = src->_data.numCharacters;
@@ -771,14 +820,14 @@ BdfFont *BdfFont::scaleFont(const BdfFont *src, int newSize) {
Common::strcpy_s(slant, sz, src->_data.slant);
data.slant = slant;
+ Common::Array<Common::Rect> srcRects;
+
if (src->_data.boxes) {
+ srcRects.resize(data.numCharacters);
+
BdfBoundingBox *boxes = new BdfBoundingBox[data.numCharacters];
- for (int i = 0; i < data.numCharacters; ++i) {
- boxes[i].width = (int)(roundf((float)src->_data.boxes[i].width * scale));
- boxes[i].height = (int)(roundf((float)src->_data.height * scale));
- boxes[i].xOffset = (int)(roundf((float)src->_data.boxes[i].xOffset * scale));
- boxes[i].yOffset = (int)(roundf((float)src->_data.boxes[i].yOffset * scale));
- }
+ for (int i = 0; i < data.numCharacters; ++i)
+ boxes[i] = scaleBdfBoundingBox(src->_data.ascent, srcReferencePointX, srcReferencePointY, data.ascent, destReferencePointX, destReferencePointY, src->_data.boxes[i], scale);
data.boxes = boxes;
} else {
// if the sources have null boxes
@@ -802,7 +851,7 @@ BdfFont *BdfFont::scaleFont(const BdfFont *src, int newSize) {
const BdfBoundingBox &srcBox = data.boxes ? src->_data.boxes[i] : src->_data.defaultBox;
#if DRAWDEBUG
- int ccc = 'd';
+ int ccc = 'L';
#endif
if (src->_data.bitmaps[i]) {
int grayLevel = 10; //box.height * box.width / 3;
@@ -810,43 +859,82 @@ BdfFont *BdfFont::scaleFont(const BdfFont *src, int newSize) {
const int bytes = dstPitch * box.height;
bitmaps[i] = new byte[bytes + 1];
- int srcBoxWidth = 0;
- if (src->_data.boxes) {
- srcBoxWidth = srcBox.width;
- } else {
- srcBoxWidth = src->_data.defaultBox.width;
+#if DRAWDEBUG
+ if (i == ccc) {
+ Common::Rect srcRect = bdfBoxToRect(src->_data.ascent, srcBox);
+ debugN("Ascent: %i\n", static_cast<int>(src->_data.ascent));
+ debugN("Source box: (%i,%i) - (%i,%i)\n", static_cast<int>(srcRect.left), static_cast<int>(srcRect.top), static_cast<int>(srcRect.right), static_cast<int>(srcRect.bottom));
+ debugN("Source bitmap:\n");
+
+ int srcPitch = (srcBox.width + 7) / 8;
+ for (int y = 0; y < srcBox.height; y++) {
+ for (int x = 0; x < srcBox.width; x++) {
+ byte b = src->_data.bitmaps[i][y * srcPitch + x / 8];
+ b >>= 7 - (x % 8);
+ b &= 1;
+
+ debugN("%c", b ? '@' : '_');
+ }
+ debugN(" %i\n", static_cast<int>(y + srcRect.top));
+ }
}
- src->scaleSingleGlyph(&srcSurf, dstGray, dstGraySize, box.width, box.height, 0, 0, grayLevel, i + src->_data.firstCharacter,
- src->_data.height, srcBoxWidth, scale);
+#endif
+
+ Common::Rect rect = bdfBoxToRect(data.ascent, box);
+ Common::Rect srcRect = bdfBoxToRect(src->_data.ascent, srcBox);
byte *ptr = bitmaps[i];
- for (int y = 0; y < box.height; y++) {
- byte *srcd = (byte *)srcSurf.getBasePtr(0, y);
- byte *dst = ptr;
- byte b = 0;
- for (int x = 0; x < dstPitch * 8; x++, srcd++) {
- b <<= 1;
- if (*srcd == 1) {
- b |= 1;
- }
- if (x % 8 == 7) {
- *dst++ = b;
- b = 0;
- }
- }
- ptr += dstPitch;
+ const byte *srcPtr = src->_data.bitmaps[i];
+ int destPitch = (rect.width() + 7) / 8;
+ int srcPitch = (srcRect.width() + 7) / 8;
+
+ memset(ptr, 0, destPitch * rect.height());
+
+ float rcpScale = 1.0f / scale;
+
#if DRAWDEBUG
- if (i == ccc) {
- int *grayPtr = dstGray;
- debugN("--> %d ", grayLevel);
- grayPtr = &dstGray[y * box.width];
- for (int x = 0; x < box.width; x++, grayPtr++)
- debugN("%c", *grayPtr > grayLevel ? '@' : '_');
- debugN("\n");
- debugN("***");
- }
+ if (i == ccc) {
+ debugN("New ascent: %i\n", static_cast<int>(data.ascent));
+ debugN("Dest box: (%i,%i) - (%i,%i)\n", static_cast<int>(rect.left), static_cast<int>(rect.top), static_cast<int>(rect.right), static_cast<int>(rect.bottom));
+ }
#endif
+
+ for (int destRelY = 0; destRelY < rect.height(); destRelY++) {
+
+ int destY = destRelY + rect.top;
+ float destPixelCenterY = destY + 0.5f;
+
+ float srcPixelCenterY = (destPixelCenterY - destReferencePointY) * rcpScale + srcReferencePointY;
+
+ int srcY = static_cast<int>(floorf(srcPixelCenterY));
+
+ if (srcY < srcRect.top || srcY >= srcRect.bottom)
+ continue;
+
+ int srcRelY = srcY - srcRect.top;
+
+ byte *rowPtr = ptr + destPitch * destRelY;
+ const byte *srcRowPtr = srcPtr + srcPitch * srcRelY;
+
+ for (int destRelX = 0; destRelX < rect.width(); destRelX++) {
+
+ int destX = destRelX + rect.left;
+ float destPixelCenterX = destX + 0.5f;
+
+ float srcPixelCenterX = (destPixelCenterX - destReferencePointX) * rcpScale + srcReferencePointX;
+
+ int srcX = static_cast<int>(floorf(srcPixelCenterX));
+
+ if (srcX < srcRect.left || srcX >= srcRect.right)
+ continue;
+
+ int srcRelX = srcX - srcRect.left;
+
+ if ((srcRowPtr[srcRelX / 8] << (srcRelX % 8)) & 0x80)
+ rowPtr[destRelX / 8] |= (0x80 >> (destRelX % 8));
+ }
}
+
#if DRAWDEBUG
if (i == ccc) {
for (int y = 0; y < box.height; y++) {
@@ -855,12 +943,12 @@ BdfFont *BdfFont::scaleFont(const BdfFont *src, int newSize) {
int sx = x;
debugN("%c", (srcRow[sx / 8] & (0x80 >> (sx % 8))) ? '#' : '_');
}
- debugN("\n");
+ debugN(" %i\n", static_cast<int>(y + rect.top));
}
}
#endif
} else {
- bitmaps[i] = 0;
+ bitmaps[i] = nullptr;
}
}
data.bitmaps = bitmaps;
More information about the Scummvm-git-logs
mailing list