Files
ppsspp/Core/Font/PGF.h
T
James0124andClaude Opus 5 8afa18c21c sceFont: match hardware's glyph blending and destination combine
sceFontGetCharGlyphImage_Clip stored the glyph over the destination buffer.
Hardware adds it with saturation instead, so on hardware a glyph never erases
what was already in the buffer - the transparent parts contribute zero - while
we cleared them to 0. Text drawn as overlapping glyphs lost the earlier ones.

Measured on a PSP-1000 against a dumped ltn0.pgf, sweeping the pen position's
fractional part, both 4bpp and 8bpp, and several destination pre-fill values so
that "wrote a 0 here" could be told apart from "did not write here":

- The result is min(dst + blend, max), not blend.
- The two horizontal weights are rounded in opposite directions, down for the
  left neighbour and up for the pixel itself. That is not a free choice: it is
  what makes the pair sum to exactly the format's full scale whenever both
  samples are full ink, for every fraction. Rounding the combined sum once, as
  before, disagrees with hardware on about a fifth of the blended pixels.
- 4bpp blends the raw nibble and 8bpp the value swizzled to 8 bit. Blending at
  8 bit and narrowing afterwards is not the same thing and misses ~0.6%.
- The fractional part of yPos64 is discarded entirely. There is no vertical
  blending and the rectangle never grows downwards, so renderY2 loses its +1
  and the two render loops collapse into one - xFrac == 0 needs no special
  case, the first term vanishes and the second collapses to exactly b.

Only PSP_FONT_PIXELFORMAT_4 and _8 render at all. _4_REV, _24 and _32 return 0
and leave the buffer completely untouched, which is also the answer to the
"not sure how to make these produce an image" note in pspautotests'
charglyphimageclip.cpp. They are skipped now, with a report so that a game
relying on them can be found - only one buffer configuration was measured.

Verified with pspautotests: the new charglyphimagexfrac goes from 388 of 601
differing lines to passing, and charglyphimage and charglyphimageclip improve
from 57 differing lines to 3. No other font test changes. The remaining 3 are
a separate, pre-existing issue with bytesPerLine of 0 and 1.

Not covered: clip rectangles that actually clip, negative pen positions, and
the shadow glyph entry points.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 19:49:55 +09:00

309 lines
6.9 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// 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, version 2.0 or later versions.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
// Thanks to the JPCSP project! This sceFont implementation is basically a C++ take on JPCSP's font code.
#pragma once
#include <string>
#include <vector>
#include "Common/CommonTypes.h"
class PointerWrap;
enum {
FONT_FILETYPE_PGF = 0x00,
FONT_FILETYPE_BWFON = 0x01,
};
enum {
FONT_PGF_BMP_H_ROWS = 0x01,
FONT_PGF_BMP_V_ROWS = 0x02,
FONT_PGF_BMP_OVERLAY = 0x03,
// Metric names according to JPCSP findings
FONT_PGF_METRIC_DIMENSION_INDEX = 0x04,
FONT_PGF_METRIC_BEARING_X_INDEX = 0x08,
FONT_PGF_METRIC_BEARING_Y_INDEX = 0x10,
FONT_PGF_METRIC_ADVANCE_INDEX = 0x20,
FONT_PGF_CHARGLYPH = 0x20,
FONT_PGF_SHADOWGLYPH = 0x40,
};
enum Family {
FONT_FAMILY_SANS_SERIF = 1,
FONT_FAMILY_SERIF = 2,
};
enum Style {
FONT_STYLE_REGULAR = 1,
FONT_STYLE_ITALIC = 2,
FONT_STYLE_BOLD = 5,
FONT_STYLE_BOLD_ITALIC = 6,
FONT_STYLE_DB = 103, // Demi-Bold / semi-bold
};
enum Language {
FONT_LANGUAGE_JAPANESE = 1,
FONT_LANGUAGE_LATIN = 2,
FONT_LANGUAGE_KOREAN = 3,
FONT_LANGUAGE_CHINESE = 4,
};
enum FontPixelFormat {
PSP_FONT_PIXELFORMAT_4 = 0, // 2 pixels packed in 1 byte (natural order)
PSP_FONT_PIXELFORMAT_4_REV = 1, // 2 pixels packed in 1 byte (reversed order)
PSP_FONT_PIXELFORMAT_8 = 2, // 1 pixel in 1 byte
PSP_FONT_PIXELFORMAT_24 = 3, // 1 pixel in 3 bytes (RGB)
PSP_FONT_PIXELFORMAT_32 = 4, // 1 pixel in 4 bytes (RGBA)
};
struct PGFFontStyle {
float_le fontH;
float_le fontV;
float_le fontHRes;
float_le fontVRes;
float_le fontWeight;
u16_le fontFamily;
u16_le fontStyle;
// Check.
u16_le fontStyleSub;
u16_le fontLanguage;
u16_le fontRegion;
u16_le fontCountry;
char fontName[64];
char fontFileName[64];
u32_le fontAttributes;
u32_le fontExpire;
};
struct Glyph {
int w;
int h;
int left;
int top;
int flags;
int shadowFlags;
int shadowID;
int advanceH;
int advanceV;
int dimensionWidth, dimensionHeight;
int xAdjustH, xAdjustV;
int yAdjustH, yAdjustV;
u32 ptr;
};
#if COMMON_LITTLE_ENDIAN
typedef FontPixelFormat FontPixelFormat_le;
#else
typedef swap_struct_t<FontPixelFormat, swap_32_t<FontPixelFormat> > FontPixelFormat_le;
#endif
struct GlyphImage {
FontPixelFormat_le pixelFormat;
s32_le xPos64;
s32_le yPos64;
u16_le bufWidth;
u16_le bufHeight;
u16_le bytesPerLine;
u16_le pad;
u32_le bufferPtr;
};
#pragma pack(push,1)
struct PGFHeader
{
u16_le headerOffset;
u16_le headerSize;
char PGFMagic[4];
s32_le revision;
s32_le version;
s32_le charMapLength;
s32_le charPointerLength;
s32_le charMapBpe;
s32_le charPointerBpe;
// TODO: This has values in it (0404)...
u8 pad1[2];
u8 bpp;
u8 pad2[1];
s32_le hSize;
s32_le vSize;
s32_le hResolution;
s32_le vResolution;
u8 pad3[1];
char fontName[64];
char fontType[64];
u8 pad4[1];
u16_le firstGlyph;
u16_le lastGlyph;
// TODO: This has a few 01s in it in the official fonts.
u8 pad5[26];
s32_le maxAscender;
s32_le maxDescender;
s32_le maxLeftXAdjust;
s32_le maxBaseYAdjust;
s32_le minCenterXAdjust;
s32_le maxTopYAdjust;
s32_le maxAdvance[2];
s32_le maxSize[2];
u16_le maxGlyphWidth;
u16_le maxGlyphHeight;
u8 pad6[2];
u8 dimTableLength;
u8 xAdjustTableLength;
u8 yAdjustTableLength;
u8 advanceTableLength;
u8 pad7[102];
s32_le shadowMapLength;
s32_le shadowMapBpe;
float_le unknown1;
s32_le shadowScale[2];
u8 pad8[8];
};
struct PGFHeaderRev3Extra {
s32_le compCharMapBpe1;
s32_le compCharMapLength1;
s32_le compCharMapBpe2;
s32_le compCharMapLength2;
u32_le unknown;
};
struct PGFCharInfo {
u32_le bitmapWidth;
u32_le bitmapHeight;
u32_le bitmapLeft;
u32_le bitmapTop;
// Glyph metrics (in 26.6 signed fixed-point).
u32_le sfp26Width;
u32_le sfp26Height;
s32_le sfp26Ascender;
s32_le sfp26Descender;
s32_le sfp26BearingHX;
s32_le sfp26BearingHY;
s32_le sfp26BearingVX;
s32_le sfp26BearingVY;
s32_le sfp26AdvanceH;
s32_le sfp26AdvanceV;
s16_le shadowFlags;
s16_le shadowId;
};
struct PGFFontInfo {
// Glyph metrics (in 26.6 signed fixed-point).
s32_le maxGlyphWidthI;
s32_le maxGlyphHeightI;
s32_le maxGlyphAscenderI;
s32_le maxGlyphDescenderI;
s32_le maxGlyphLeftXI;
s32_le maxGlyphBaseYI;
s32_le minGlyphCenterXI;
s32_le maxGlyphTopYI;
s32_le maxGlyphAdvanceXI;
s32_le maxGlyphAdvanceYI;
// Glyph metrics (replicated as float).
float_le maxGlyphWidthF;
float_le maxGlyphHeightF;
float_le maxGlyphAscenderF;
float_le maxGlyphDescenderF;
float_le maxGlyphLeftXF;
float_le maxGlyphBaseYF;
float_le minGlyphCenterXF;
float_le maxGlyphTopYF;
float_le maxGlyphAdvanceXF;
float_le maxGlyphAdvanceYF;
// Bitmap dimensions.
s16_le maxGlyphWidth;
s16_le maxGlyphHeight;
s32_le numGlyphs;
s32_le shadowMapLength; // Number of elements in the font's shadow charmap.
// Font style (used by font comparison functions).
PGFFontStyle fontStyle;
u8 BPP; // Font's BPP.
u8 pad[3];
};
#pragma pack(pop)
class PGF {
public:
PGF();
~PGF();
bool ReadPtr(const u8 *ptr, size_t dataSize);
bool GetCharInfo(int charCode, PGFCharInfo *ci, int altCharCode, int glyphType = FONT_PGF_CHARGLYPH) const;
void GetFontInfo(PGFFontInfo *fi) const;
void DrawCharacter(const GlyphImage *image, int clipX, int clipY, int clipWidth, int clipHeight, int charCode, int altCharCode, int glyphType) const;
void DoState(PointerWrap &p);
PGFHeader header;
private:
bool ReadCharGlyph(const u8 *fontdata, size_t charPtr, Glyph &glyph);
bool ReadShadowGlyph(const u8 *fontdata, size_t charPtr, Glyph &glyph);
bool GetCharGlyph(int charCode, int glyphType, Glyph &glyph) const;
// Unused
int GetCharIndex(int charCode, const std::vector<int> &charmapCompressed);
void SetFontPixel(u32 base, int bpl, int bufWidth, int bufHeight, int x, int y, int pixelColor, FontPixelFormat pixelformat) const;
PGFHeaderRev3Extra rev3extra;
// Font character image data
u8 *fontData;
size_t fontDataSize;
std::string fileName;
std::vector<int> dimensionTable[2];
std::vector<int> xAdjustTable[2];
std::vector<int> yAdjustTable[2];
std::vector<int> advanceTable[2];
// Unused
std::vector<int> charmapCompressionTable1[2];
std::vector<int> charmapCompressionTable2[2];
std::vector<int> charmap_compr;
std::vector<int> charmap;
std::vector<Glyph> glyphs;
std::vector<Glyph> shadowGlyphs;
int firstGlyph;
};