mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-16 00:34:19 +00:00
Fixed win32 horizontal scroll wheel direction. Big internal refactoring of win32 typeface implementation. Added some functionality to SortedSet. Fixed a mac main menu bug.
This commit is contained in:
parent
5b7a1d6311
commit
9226bfba49
11 changed files with 552 additions and 403 deletions
|
|
@ -93112,7 +93112,7 @@ END_JUCE_NAMESPACE
|
|||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
Typeface::Typeface (const String& name_) noexcept
|
||||
: name (name_), isFallbackFont (false)
|
||||
: name (name_)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -93123,9 +93123,18 @@ Typeface::~Typeface()
|
|||
const Typeface::Ptr Typeface::getFallbackTypeface()
|
||||
{
|
||||
const Font fallbackFont (Font::getFallbackFontName(), 10, 0);
|
||||
Typeface* t = fallbackFont.getTypeface();
|
||||
t->isFallbackFont = true;
|
||||
return t;
|
||||
return fallbackFont.getTypeface();
|
||||
}
|
||||
|
||||
EdgeTable* Typeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform)
|
||||
{
|
||||
Path path;
|
||||
|
||||
if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty())
|
||||
return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0),
|
||||
path, transform);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
|
|
@ -93319,34 +93328,6 @@ CustomTypeface::GlyphInfo* CustomTypeface::findGlyph (const juce_wchar character
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
CustomTypeface::GlyphInfo* CustomTypeface::findGlyphSubstituting (const juce_wchar character) noexcept
|
||||
{
|
||||
GlyphInfo* glyph = findGlyph (character, true);
|
||||
|
||||
if (glyph == nullptr)
|
||||
{
|
||||
if (CharacterFunctions::isWhitespace (character) && character != L' ')
|
||||
glyph = findGlyph (L' ', true);
|
||||
|
||||
if (glyph == nullptr)
|
||||
{
|
||||
const Font fallbackFont (Font::getFallbackFontName(), 10, 0);
|
||||
Typeface* const fallbackTypeface = fallbackFont.getTypeface();
|
||||
if (fallbackTypeface != nullptr && fallbackTypeface != this)
|
||||
{
|
||||
Path path;
|
||||
fallbackTypeface->getOutlineForGlyph (character, path);
|
||||
addGlyph (character, path, fallbackTypeface->getStringWidth (String::charToString (character)));
|
||||
}
|
||||
|
||||
if (glyph == nullptr)
|
||||
glyph = findGlyph (defaultCharacter, true);
|
||||
}
|
||||
}
|
||||
|
||||
return glyph;
|
||||
}
|
||||
|
||||
bool CustomTypeface::loadGlyphIfPossible (const juce_wchar /*characterNeeded*/)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -93448,13 +93429,13 @@ float CustomTypeface::getStringWidth (const String& text)
|
|||
while (! t.isEmpty())
|
||||
{
|
||||
const juce_wchar c = t.getAndAdvance();
|
||||
const GlyphInfo* const glyph = findGlyphSubstituting (c);
|
||||
const GlyphInfo* const glyph = findGlyph (c, true);
|
||||
|
||||
if (glyph == nullptr && ! isFallbackFont)
|
||||
if (glyph == nullptr)
|
||||
{
|
||||
const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface());
|
||||
|
||||
if (fallbackTypeface != nullptr)
|
||||
if (fallbackTypeface != nullptr && fallbackTypeface != this)
|
||||
x += fallbackTypeface->getStringWidth (String::charToString (c));
|
||||
}
|
||||
|
||||
|
|
@ -93476,11 +93457,11 @@ void CustomTypeface::getGlyphPositions (const String& text, Array <int>& resultG
|
|||
const juce_wchar c = t.getAndAdvance();
|
||||
const GlyphInfo* const glyph = findGlyph (c, true);
|
||||
|
||||
if (glyph == nullptr && ! isFallbackFont)
|
||||
if (glyph == nullptr)
|
||||
{
|
||||
const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface());
|
||||
|
||||
if (fallbackTypeface != nullptr)
|
||||
if (fallbackTypeface != nullptr && fallbackTypeface != this)
|
||||
{
|
||||
Array <int> subGlyphs;
|
||||
Array <float> subOffsets;
|
||||
|
|
@ -93508,11 +93489,11 @@ bool CustomTypeface::getOutlineForGlyph (int glyphNumber, Path& path)
|
|||
{
|
||||
const GlyphInfo* const glyph = findGlyph ((juce_wchar) glyphNumber, true);
|
||||
|
||||
if (glyph == nullptr && ! isFallbackFont)
|
||||
if (glyph == nullptr)
|
||||
{
|
||||
const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface());
|
||||
|
||||
if (fallbackTypeface != nullptr)
|
||||
if (fallbackTypeface != nullptr && fallbackTypeface != this)
|
||||
fallbackTypeface->getOutlineForGlyph (glyphNumber, path);
|
||||
}
|
||||
|
||||
|
|
@ -93529,11 +93510,11 @@ EdgeTable* CustomTypeface::getEdgeTableForGlyph (int glyphNumber, const AffineTr
|
|||
{
|
||||
const GlyphInfo* const glyph = findGlyph ((juce_wchar) glyphNumber, true);
|
||||
|
||||
if (glyph == nullptr && ! isFallbackFont)
|
||||
if (glyph == nullptr)
|
||||
{
|
||||
const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface());
|
||||
|
||||
if (fallbackTypeface != nullptr)
|
||||
if (fallbackTypeface != nullptr && fallbackTypeface != this)
|
||||
return fallbackTypeface->getEdgeTableForGlyph (glyphNumber, transform);
|
||||
}
|
||||
|
||||
|
|
@ -246567,19 +246548,6 @@ public:
|
|||
return dc;
|
||||
}
|
||||
|
||||
KERNINGPAIR* getKerningPairs (int& numKPs_)
|
||||
{
|
||||
if (kps == nullptr)
|
||||
{
|
||||
numKPs = GetKerningPairs (dc, 0, 0);
|
||||
kps.calloc (numKPs);
|
||||
GetKerningPairs (dc, numKPs, kps);
|
||||
}
|
||||
|
||||
numKPs_ = numKPs;
|
||||
return kps;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
HFONT fontH;
|
||||
|
|
@ -246613,70 +246581,99 @@ private:
|
|||
|
||||
juce_ImplementSingleton_SingleThreaded (FontDCHolder);
|
||||
|
||||
class WindowsTypeface : public CustomTypeface
|
||||
class WindowsTypeface : public Typeface
|
||||
{
|
||||
public:
|
||||
WindowsTypeface (const Font& font)
|
||||
: Typeface (font.getTypefaceName()),
|
||||
ascent (1.0f),
|
||||
defaultGlyph (-1),
|
||||
bold (font.isBold()),
|
||||
italic (font.isItalic())
|
||||
{
|
||||
HDC dc = FontDCHolder::getInstance()->loadFont (font.getTypefaceName(),
|
||||
font.isBold(), font.isItalic(), 0);
|
||||
HDC dc = getDC();
|
||||
|
||||
TEXTMETRIC tm;
|
||||
tm.tmAscent = tm.tmHeight = 1;
|
||||
tm.tmDefaultChar = 0;
|
||||
GetTextMetrics (dc, &tm);
|
||||
|
||||
setCharacteristics (font.getTypefaceName(),
|
||||
tm.tmAscent / (float) tm.tmHeight,
|
||||
font.isBold(), font.isItalic(),
|
||||
tm.tmDefaultChar);
|
||||
if (GetTextMetrics (dc, &tm))
|
||||
{
|
||||
ascent = tm.tmAscent / (float) tm.tmHeight;
|
||||
defaultGlyph = getGlyphForChar (dc, tm.tmDefaultChar);
|
||||
createKerningPairs (dc, (float) tm.tmHeight);
|
||||
}
|
||||
}
|
||||
|
||||
bool loadGlyphIfPossible (juce_wchar character)
|
||||
float getAscent() const { return ascent; }
|
||||
float getDescent() const { return 1.0f - ascent; }
|
||||
|
||||
float getStringWidth (const String& text)
|
||||
{
|
||||
HDC dc = FontDCHolder::getInstance()->loadFont (name, isBold, isItalic, 0);
|
||||
HDC dc = getDC();
|
||||
const CharPointer_UTF16 utf16 (text.toUTF16());
|
||||
const int numChars = utf16.length();
|
||||
HeapBlock<int16> results (numChars + 1);
|
||||
results[numChars] = -1;
|
||||
float x = 0;
|
||||
|
||||
GLYPHMETRICS gm;
|
||||
|
||||
// if this is the fallback font, skip checking for the glyph's existence. This is because
|
||||
// with fonts like Tahoma, GetGlyphIndices can say that a glyph doesn't exist, but it still
|
||||
// gets correctly created later on.
|
||||
if (! isFallbackFont)
|
||||
if (GetGlyphIndices (dc, utf16, numChars, reinterpret_cast <WORD*> (results.getData()),
|
||||
GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
|
||||
{
|
||||
const WCHAR charToTest[] = { (WCHAR) character, 0 };
|
||||
WORD index = 0;
|
||||
for (int i = 0; i < numChars; ++i)
|
||||
x += getKerning (dc, results[i], results[i + 1]);
|
||||
}
|
||||
|
||||
if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR
|
||||
&& index == 0xffff)
|
||||
return x;
|
||||
}
|
||||
|
||||
void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets)
|
||||
{
|
||||
HDC dc = getDC();
|
||||
const CharPointer_UTF16 utf16 (text.toUTF16());
|
||||
const int numChars = utf16.length();
|
||||
HeapBlock<int16> results (numChars + 1);
|
||||
results[numChars] = -1;
|
||||
float x = 0;
|
||||
|
||||
if (GetGlyphIndices (dc, utf16, numChars, reinterpret_cast <WORD*> (results.getData()),
|
||||
GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
|
||||
{
|
||||
resultGlyphs.ensureStorageAllocated (numChars);
|
||||
xOffsets.ensureStorageAllocated (numChars + 1);
|
||||
|
||||
for (int i = 0; i < numChars; ++i)
|
||||
{
|
||||
return false;
|
||||
resultGlyphs.add (results[i]);
|
||||
xOffsets.add (x);
|
||||
x += getKerning (dc, results[i], results[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
Path glyphPath;
|
||||
xOffsets.add (x);
|
||||
}
|
||||
|
||||
bool getOutlineForGlyph (int glyphNumber, Path& glyphPath)
|
||||
{
|
||||
HDC dc = getDC();
|
||||
|
||||
TEXTMETRIC tm;
|
||||
if (! GetTextMetrics (dc, &tm))
|
||||
{
|
||||
addGlyph (character, glyphPath, 0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
const float height = (float) tm.tmHeight;
|
||||
static const MAT2 identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } };
|
||||
if (glyphNumber < 0)
|
||||
glyphNumber = defaultGlyph;
|
||||
|
||||
const int bufSize = GetGlyphOutline (dc, character, GGO_NATIVE,
|
||||
GLYPHMETRICS gm;
|
||||
const int bufSize = GetGlyphOutline (dc, glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX,
|
||||
&gm, 0, 0, &identityMatrix);
|
||||
|
||||
if (bufSize > 0)
|
||||
{
|
||||
HeapBlock<char> data (bufSize);
|
||||
|
||||
GetGlyphOutline (dc, character, GGO_NATIVE, &gm,
|
||||
GetGlyphOutline (dc, glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm,
|
||||
bufSize, data, &identityMatrix);
|
||||
|
||||
const TTPOLYGONHEADER* pheader = reinterpret_cast<TTPOLYGONHEADER*> (data.getData());
|
||||
|
||||
const float height = (float) tm.tmHeight;
|
||||
const float scaleX = 1.0f / height;
|
||||
const float scaleY = -1.0f / height;
|
||||
|
||||
|
|
@ -246737,25 +246734,117 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
addGlyph (character, glyphPath, gm.gmCellIncX / height);
|
||||
|
||||
int numKPs;
|
||||
const KERNINGPAIR* const kps = FontDCHolder::getInstance()->getKerningPairs (numKPs);
|
||||
|
||||
for (int i = 0; i < numKPs; ++i)
|
||||
{
|
||||
if (kps[i].wFirst == character)
|
||||
addKerningPair (kps[i].wFirst, kps[i].wSecond,
|
||||
kps[i].iKernAmount / height);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static const MAT2 identityMatrix;
|
||||
float ascent;
|
||||
int defaultGlyph;
|
||||
bool bold, italic;
|
||||
|
||||
struct KerningPair
|
||||
{
|
||||
int glyph1, glyph2;
|
||||
float kerning;
|
||||
|
||||
bool operator== (const KerningPair& other) const noexcept
|
||||
{
|
||||
return glyph1 == other.glyph1 && glyph2 == other.glyph2;
|
||||
}
|
||||
|
||||
bool operator< (const KerningPair& other) const noexcept
|
||||
{
|
||||
return glyph1 < other.glyph1
|
||||
|| (glyph1 == other.glyph1 && glyph2 < other.glyph2);
|
||||
}
|
||||
};
|
||||
|
||||
SortedSet<KerningPair> kerningPairs;
|
||||
|
||||
void createKerningPairs (HDC dc, const float height)
|
||||
{
|
||||
HeapBlock<KERNINGPAIR> rawKerning;
|
||||
const int numKPs = GetKerningPairs (dc, 0, 0);
|
||||
rawKerning.calloc (numKPs);
|
||||
GetKerningPairs (dc, numKPs, rawKerning);
|
||||
|
||||
kerningPairs.ensureStorageAllocated (numKPs);
|
||||
|
||||
for (int i = 0; i < numKPs; ++i)
|
||||
{
|
||||
KerningPair kp;
|
||||
kp.glyph1 = getGlyphForChar (dc, rawKerning[i].wFirst);
|
||||
kp.glyph2 = getGlyphForChar (dc, rawKerning[i].wSecond);
|
||||
|
||||
const int standardWidth = getGlyphWidth (dc, kp.glyph1);
|
||||
kp.kerning = (standardWidth + rawKerning[i].iKernAmount) / height;
|
||||
kerningPairs.add (kp);
|
||||
|
||||
kp.glyph2 = -1; // add another entry for the standard width version..
|
||||
kp.kerning = standardWidth / height;
|
||||
kerningPairs.add (kp);
|
||||
}
|
||||
}
|
||||
|
||||
static int getGlyphForChar (HDC dc, juce_wchar character)
|
||||
{
|
||||
const WCHAR charToTest[] = { (WCHAR) character, 0 };
|
||||
WORD index = 0;
|
||||
|
||||
if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR
|
||||
|| index == 0xffff)
|
||||
return -1;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static int getGlyphWidth (HDC dc, int glyphNumber)
|
||||
{
|
||||
GLYPHMETRICS gm;
|
||||
gm.gmCellIncX = 0;
|
||||
GetGlyphOutline (dc, glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, 0, &identityMatrix);
|
||||
return gm.gmCellIncX;
|
||||
}
|
||||
|
||||
float getKerning (HDC dc, const int glyph1, const int glyph2)
|
||||
{
|
||||
KerningPair kp;
|
||||
kp.glyph1 = glyph1;
|
||||
kp.glyph2 = glyph2;
|
||||
int index = kerningPairs.indexOf (kp);
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
kp.glyph2 = -1;
|
||||
index = kerningPairs.indexOf (kp);
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
TEXTMETRIC tm;
|
||||
if (! GetTextMetrics (dc, &tm))
|
||||
return 0;
|
||||
|
||||
kp.glyph2 = -1;
|
||||
kp.kerning = getGlyphWidth (dc, kp.glyph1) / (float) tm.tmHeight;
|
||||
kerningPairs.add (kp);
|
||||
return kp.kerning;
|
||||
}
|
||||
}
|
||||
|
||||
return kerningPairs.getReference (index).kerning;
|
||||
}
|
||||
|
||||
HDC getDC() const
|
||||
{
|
||||
return FontDCHolder::getInstance()->loadFont (name, bold, italic, 0);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTypeface);
|
||||
};
|
||||
|
||||
const MAT2 WindowsTypeface::identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } };
|
||||
|
||||
const Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
|
||||
{
|
||||
return new WindowsTypeface (font);
|
||||
|
|
@ -249131,7 +249220,7 @@ private:
|
|||
peer = this;
|
||||
|
||||
peer->handleMouseWheel (0, peer->globalToLocal (globalPos), getMouseEventTime(),
|
||||
isVertical ? 0.0f : amount,
|
||||
isVertical ? 0.0f : -amount,
|
||||
isVertical ? amount : 0.0f);
|
||||
}
|
||||
|
||||
|
|
@ -281677,7 +281766,10 @@ public:
|
|||
[menu setAutoenablesItems: false];
|
||||
[menu update];
|
||||
[parentItem setTag: tag];
|
||||
[parentItem setSubmenu: menu];
|
||||
|
||||
if (! [[parentItem submenu] equals: menu]) // NB this comparison is needed to avoid a strange
|
||||
[parentItem setSubmenu: menu]; // crash deep inside Apple code when no windows are focused..
|
||||
|
||||
[menu release];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ namespace JuceDummyNamespace {}
|
|||
*/
|
||||
#define JUCE_MAJOR_VERSION 1
|
||||
#define JUCE_MINOR_VERSION 53
|
||||
#define JUCE_BUILDNUMBER 96
|
||||
#define JUCE_BUILDNUMBER 97
|
||||
|
||||
/** Current Juce version number.
|
||||
|
||||
|
|
@ -14735,7 +14735,7 @@ public:
|
|||
return false;
|
||||
|
||||
for (int i = numUsed; --i >= 0;)
|
||||
if (data.elements[i] != other.data.elements[i])
|
||||
if (! (data.elements[i] == other.data.elements[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
@ -14818,6 +14818,21 @@ public:
|
|||
return data.elements [index];
|
||||
}
|
||||
|
||||
/** Returns a direct reference to one of the elements in the set, without checking the index passed in.
|
||||
|
||||
This is like getUnchecked, but returns a direct reference to the element, so that
|
||||
you can alter it directly. Obviously this can be dangerous, so only use it when
|
||||
absolutely necessary.
|
||||
|
||||
@param index the index of the element being requested (0 is the first element in the array)
|
||||
*/
|
||||
inline ElementType& getReference (const int index) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
jassert (isPositiveAndBelow (index, numUsed));
|
||||
return data.elements [index];
|
||||
}
|
||||
|
||||
/** Returns the first element in the set, or 0 if the set is empty.
|
||||
|
||||
@see operator[], getUnchecked, getLast
|
||||
|
|
@ -14885,10 +14900,10 @@ public:
|
|||
|
||||
if (halfway == start)
|
||||
return -1;
|
||||
else if (elementToLookFor >= data.elements [halfway])
|
||||
start = halfway;
|
||||
else
|
||||
else if (elementToLookFor < data.elements [halfway])
|
||||
end = halfway;
|
||||
else
|
||||
start = halfway;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14921,10 +14936,10 @@ public:
|
|||
|
||||
if (halfway == start)
|
||||
return false;
|
||||
else if (elementToLookFor >= data.elements [halfway])
|
||||
start = halfway;
|
||||
else
|
||||
else if (elementToLookFor < data.elements [halfway])
|
||||
end = halfway;
|
||||
else
|
||||
start = halfway;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14959,17 +14974,17 @@ public:
|
|||
|
||||
if (halfway == start)
|
||||
{
|
||||
if (newElement >= data.elements [halfway])
|
||||
insertInternal (start + 1, newElement);
|
||||
else
|
||||
if (newElement < data.elements [halfway])
|
||||
insertInternal (start, newElement);
|
||||
else
|
||||
insertInternal (start + 1, newElement);
|
||||
|
||||
break;
|
||||
}
|
||||
else if (newElement >= data.elements [halfway])
|
||||
start = halfway;
|
||||
else
|
||||
else if (newElement < data.elements [halfway])
|
||||
end = halfway;
|
||||
else
|
||||
start = halfway;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15137,6 +15152,18 @@ public:
|
|||
data.shrinkToNoMoreThan (numUsed);
|
||||
}
|
||||
|
||||
/** Increases the set's internal storage to hold a minimum number of elements.
|
||||
|
||||
Calling this before adding a large known number of elements means that
|
||||
the set won't have to keep dynamically resizing itself as the elements
|
||||
are added, and it'll therefore be more efficient.
|
||||
*/
|
||||
void ensureStorageAllocated (const int minNumElements)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (minNumElements);
|
||||
}
|
||||
|
||||
/** Returns the CriticalSection that locks this array.
|
||||
To lock, you can call getLock().enter() and getLock().exit(), or preferably use
|
||||
an object of ScopedLockType as an RAII lock for it.
|
||||
|
|
@ -25132,7 +25159,7 @@ public:
|
|||
virtual bool getOutlineForGlyph (int glyphNumber, Path& path) = 0;
|
||||
|
||||
/** Returns a new EdgeTable that contains the path for the givem glyph, with the specified transform applied. */
|
||||
virtual EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform) = 0;
|
||||
virtual EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform);
|
||||
|
||||
/** Returns true if the typeface uses hinting. */
|
||||
virtual bool isHinted() const { return false; }
|
||||
|
|
@ -25143,7 +25170,6 @@ public:
|
|||
protected:
|
||||
|
||||
String name;
|
||||
bool isFallbackFont;
|
||||
|
||||
explicit Typeface (const String& name) noexcept;
|
||||
|
||||
|
|
@ -67847,7 +67873,6 @@ public:
|
|||
void getGlyphPositions (const String& text, Array <int>& glyphs, Array<float>& xOffsets);
|
||||
bool getOutlineForGlyph (int glyphNumber, Path& path);
|
||||
EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform);
|
||||
int getGlyphForCharacter (juce_wchar character);
|
||||
|
||||
protected:
|
||||
|
||||
|
|
@ -67871,7 +67896,6 @@ private:
|
|||
short lookupTable [128];
|
||||
|
||||
GlyphInfo* findGlyph (const juce_wchar character, bool loadIfNeeded) noexcept;
|
||||
GlyphInfo* findGlyphSubstituting (juce_wchar character) noexcept;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomTypeface);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ public:
|
|||
return false;
|
||||
|
||||
for (int i = numUsed; --i >= 0;)
|
||||
if (data.elements[i] != other.data.elements[i])
|
||||
if (! (data.elements[i] == other.data.elements[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
@ -204,6 +204,21 @@ public:
|
|||
return data.elements [index];
|
||||
}
|
||||
|
||||
/** Returns a direct reference to one of the elements in the set, without checking the index passed in.
|
||||
|
||||
This is like getUnchecked, but returns a direct reference to the element, so that
|
||||
you can alter it directly. Obviously this can be dangerous, so only use it when
|
||||
absolutely necessary.
|
||||
|
||||
@param index the index of the element being requested (0 is the first element in the array)
|
||||
*/
|
||||
inline ElementType& getReference (const int index) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
jassert (isPositiveAndBelow (index, numUsed));
|
||||
return data.elements [index];
|
||||
}
|
||||
|
||||
/** Returns the first element in the set, or 0 if the set is empty.
|
||||
|
||||
@see operator[], getUnchecked, getLast
|
||||
|
|
@ -273,10 +288,10 @@ public:
|
|||
|
||||
if (halfway == start)
|
||||
return -1;
|
||||
else if (elementToLookFor >= data.elements [halfway])
|
||||
start = halfway;
|
||||
else
|
||||
else if (elementToLookFor < data.elements [halfway])
|
||||
end = halfway;
|
||||
else
|
||||
start = halfway;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -309,10 +324,10 @@ public:
|
|||
|
||||
if (halfway == start)
|
||||
return false;
|
||||
else if (elementToLookFor >= data.elements [halfway])
|
||||
start = halfway;
|
||||
else
|
||||
else if (elementToLookFor < data.elements [halfway])
|
||||
end = halfway;
|
||||
else
|
||||
start = halfway;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -348,17 +363,17 @@ public:
|
|||
|
||||
if (halfway == start)
|
||||
{
|
||||
if (newElement >= data.elements [halfway])
|
||||
insertInternal (start + 1, newElement);
|
||||
else
|
||||
if (newElement < data.elements [halfway])
|
||||
insertInternal (start, newElement);
|
||||
else
|
||||
insertInternal (start + 1, newElement);
|
||||
|
||||
break;
|
||||
}
|
||||
else if (newElement >= data.elements [halfway])
|
||||
start = halfway;
|
||||
else
|
||||
else if (newElement < data.elements [halfway])
|
||||
end = halfway;
|
||||
else
|
||||
start = halfway;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -529,6 +544,18 @@ public:
|
|||
data.shrinkToNoMoreThan (numUsed);
|
||||
}
|
||||
|
||||
/** Increases the set's internal storage to hold a minimum number of elements.
|
||||
|
||||
Calling this before adding a large known number of elements means that
|
||||
the set won't have to keep dynamically resizing itself as the elements
|
||||
are added, and it'll therefore be more efficient.
|
||||
*/
|
||||
void ensureStorageAllocated (const int minNumElements)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (minNumElements);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the CriticalSection that locks this array.
|
||||
To lock, you can call getLock().enter() and getLock().exit(), or preferably use
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
*/
|
||||
#define JUCE_MAJOR_VERSION 1
|
||||
#define JUCE_MINOR_VERSION 53
|
||||
#define JUCE_BUILDNUMBER 96
|
||||
#define JUCE_BUILDNUMBER 97
|
||||
|
||||
/** Current Juce version number.
|
||||
|
||||
|
|
|
|||
|
|
@ -222,34 +222,6 @@ CustomTypeface::GlyphInfo* CustomTypeface::findGlyph (const juce_wchar character
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
CustomTypeface::GlyphInfo* CustomTypeface::findGlyphSubstituting (const juce_wchar character) noexcept
|
||||
{
|
||||
GlyphInfo* glyph = findGlyph (character, true);
|
||||
|
||||
if (glyph == nullptr)
|
||||
{
|
||||
if (CharacterFunctions::isWhitespace (character) && character != L' ')
|
||||
glyph = findGlyph (L' ', true);
|
||||
|
||||
if (glyph == nullptr)
|
||||
{
|
||||
const Font fallbackFont (Font::getFallbackFontName(), 10, 0);
|
||||
Typeface* const fallbackTypeface = fallbackFont.getTypeface();
|
||||
if (fallbackTypeface != nullptr && fallbackTypeface != this)
|
||||
{
|
||||
Path path;
|
||||
fallbackTypeface->getOutlineForGlyph (character, path);
|
||||
addGlyph (character, path, fallbackTypeface->getStringWidth (String::charToString (character)));
|
||||
}
|
||||
|
||||
if (glyph == nullptr)
|
||||
glyph = findGlyph (defaultCharacter, true);
|
||||
}
|
||||
}
|
||||
|
||||
return glyph;
|
||||
}
|
||||
|
||||
bool CustomTypeface::loadGlyphIfPossible (const juce_wchar /*characterNeeded*/)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -352,13 +324,13 @@ float CustomTypeface::getStringWidth (const String& text)
|
|||
while (! t.isEmpty())
|
||||
{
|
||||
const juce_wchar c = t.getAndAdvance();
|
||||
const GlyphInfo* const glyph = findGlyphSubstituting (c);
|
||||
const GlyphInfo* const glyph = findGlyph (c, true);
|
||||
|
||||
if (glyph == nullptr && ! isFallbackFont)
|
||||
if (glyph == nullptr)
|
||||
{
|
||||
const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface());
|
||||
|
||||
if (fallbackTypeface != nullptr)
|
||||
if (fallbackTypeface != nullptr && fallbackTypeface != this)
|
||||
x += fallbackTypeface->getStringWidth (String::charToString (c));
|
||||
}
|
||||
|
||||
|
|
@ -380,11 +352,11 @@ void CustomTypeface::getGlyphPositions (const String& text, Array <int>& resultG
|
|||
const juce_wchar c = t.getAndAdvance();
|
||||
const GlyphInfo* const glyph = findGlyph (c, true);
|
||||
|
||||
if (glyph == nullptr && ! isFallbackFont)
|
||||
if (glyph == nullptr)
|
||||
{
|
||||
const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface());
|
||||
|
||||
if (fallbackTypeface != nullptr)
|
||||
if (fallbackTypeface != nullptr && fallbackTypeface != this)
|
||||
{
|
||||
Array <int> subGlyphs;
|
||||
Array <float> subOffsets;
|
||||
|
|
@ -412,11 +384,11 @@ bool CustomTypeface::getOutlineForGlyph (int glyphNumber, Path& path)
|
|||
{
|
||||
const GlyphInfo* const glyph = findGlyph ((juce_wchar) glyphNumber, true);
|
||||
|
||||
if (glyph == nullptr && ! isFallbackFont)
|
||||
if (glyph == nullptr)
|
||||
{
|
||||
const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface());
|
||||
|
||||
if (fallbackTypeface != nullptr)
|
||||
if (fallbackTypeface != nullptr && fallbackTypeface != this)
|
||||
fallbackTypeface->getOutlineForGlyph (glyphNumber, path);
|
||||
}
|
||||
|
||||
|
|
@ -433,11 +405,11 @@ EdgeTable* CustomTypeface::getEdgeTableForGlyph (int glyphNumber, const AffineTr
|
|||
{
|
||||
const GlyphInfo* const glyph = findGlyph ((juce_wchar) glyphNumber, true);
|
||||
|
||||
if (glyph == nullptr && ! isFallbackFont)
|
||||
if (glyph == nullptr)
|
||||
{
|
||||
const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface());
|
||||
|
||||
if (fallbackTypeface != nullptr)
|
||||
if (fallbackTypeface != nullptr && fallbackTypeface != this)
|
||||
return fallbackTypeface->getEdgeTableForGlyph (glyphNumber, transform);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -113,7 +113,6 @@ public:
|
|||
void getGlyphPositions (const String& text, Array <int>& glyphs, Array<float>& xOffsets);
|
||||
bool getOutlineForGlyph (int glyphNumber, Path& path);
|
||||
EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform);
|
||||
int getGlyphForCharacter (juce_wchar character);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
|
|
@ -138,7 +137,6 @@ private:
|
|||
short lookupTable [128];
|
||||
|
||||
GlyphInfo* findGlyph (const juce_wchar character, bool loadIfNeeded) noexcept;
|
||||
GlyphInfo* findGlyphSubstituting (juce_wchar character) noexcept;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomTypeface);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,11 +29,12 @@ BEGIN_JUCE_NAMESPACE
|
|||
|
||||
#include "juce_Typeface.h"
|
||||
#include "juce_Font.h"
|
||||
#include "../contexts/juce_EdgeTable.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
Typeface::Typeface (const String& name_) noexcept
|
||||
: name (name_), isFallbackFont (false)
|
||||
: name (name_)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -44,10 +45,20 @@ Typeface::~Typeface()
|
|||
const Typeface::Ptr Typeface::getFallbackTypeface()
|
||||
{
|
||||
const Font fallbackFont (Font::getFallbackFontName(), 10, 0);
|
||||
Typeface* t = fallbackFont.getTypeface();
|
||||
t->isFallbackFont = true;
|
||||
return t;
|
||||
return fallbackFont.getTypeface();
|
||||
}
|
||||
|
||||
EdgeTable* Typeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform)
|
||||
{
|
||||
Path path;
|
||||
|
||||
if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty())
|
||||
return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0),
|
||||
path, transform);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ public:
|
|||
virtual bool getOutlineForGlyph (int glyphNumber, Path& path) = 0;
|
||||
|
||||
/** Returns a new EdgeTable that contains the path for the givem glyph, with the specified transform applied. */
|
||||
virtual EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform) = 0;
|
||||
virtual EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform);
|
||||
|
||||
/** Returns true if the typeface uses hinting. */
|
||||
virtual bool isHinted() const { return false; }
|
||||
|
|
@ -125,7 +125,6 @@ public:
|
|||
protected:
|
||||
//==============================================================================
|
||||
String name;
|
||||
bool isFallbackFont;
|
||||
|
||||
explicit Typeface (const String& name) noexcept;
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,10 @@ public:
|
|||
[menu setAutoenablesItems: false];
|
||||
[menu update];
|
||||
[parentItem setTag: tag];
|
||||
[parentItem setSubmenu: menu];
|
||||
|
||||
if (! [[parentItem submenu] equals: menu]) // NB this comparison is needed to avoid a strange
|
||||
[parentItem setSubmenu: menu]; // crash deep inside Apple code when no windows are focused..
|
||||
|
||||
[menu release];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,41 +29,44 @@
|
|||
|
||||
|
||||
//==============================================================================
|
||||
static int CALLBACK wfontEnum2 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam)
|
||||
namespace FontEnumerators
|
||||
{
|
||||
if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0)
|
||||
int CALLBACK fontEnum2 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam)
|
||||
{
|
||||
const String fontName (lpelfe->elfLogFont.lfFaceName);
|
||||
if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0)
|
||||
{
|
||||
const String fontName (lpelfe->elfLogFont.lfFaceName);
|
||||
|
||||
((StringArray*) lParam)->addIfNotAlreadyThere (fontName.removeCharacters ("@"));
|
||||
((StringArray*) lParam)->addIfNotAlreadyThere (fontName.removeCharacters ("@"));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int CALLBACK wfontEnum1 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam)
|
||||
{
|
||||
if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0)
|
||||
int CALLBACK fontEnum1 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam)
|
||||
{
|
||||
LOGFONTW lf = { 0 };
|
||||
lf.lfWeight = FW_DONTCARE;
|
||||
lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
|
||||
lf.lfQuality = DEFAULT_QUALITY;
|
||||
lf.lfCharSet = DEFAULT_CHARSET;
|
||||
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
||||
lf.lfPitchAndFamily = FF_DONTCARE;
|
||||
if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0)
|
||||
{
|
||||
LOGFONTW lf = { 0 };
|
||||
lf.lfWeight = FW_DONTCARE;
|
||||
lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
|
||||
lf.lfQuality = DEFAULT_QUALITY;
|
||||
lf.lfCharSet = DEFAULT_CHARSET;
|
||||
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
||||
lf.lfPitchAndFamily = FF_DONTCARE;
|
||||
|
||||
const String fontName (lpelfe->elfLogFont.lfFaceName);
|
||||
fontName.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
|
||||
const String fontName (lpelfe->elfLogFont.lfFaceName);
|
||||
fontName.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
|
||||
|
||||
HDC dc = CreateCompatibleDC (0);
|
||||
EnumFontFamiliesEx (dc, &lf,
|
||||
(FONTENUMPROCW) &wfontEnum2,
|
||||
lParam, 0);
|
||||
DeleteDC (dc);
|
||||
HDC dc = CreateCompatibleDC (0);
|
||||
EnumFontFamiliesEx (dc, &lf,
|
||||
(FONTENUMPROCW) &fontEnum2,
|
||||
lParam, 0);
|
||||
DeleteDC (dc);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
StringArray Font::findAllTypefaceNames()
|
||||
|
|
@ -81,7 +84,7 @@ StringArray Font::findAllTypefaceNames()
|
|||
lf.lfPitchAndFamily = FF_DONTCARE;
|
||||
|
||||
EnumFontFamiliesEx (dc, &lf,
|
||||
(FONTENUMPROCW) &wfontEnum1,
|
||||
(FONTENUMPROCW) &FontEnumerators::fontEnum1,
|
||||
(LPARAM) &results, 0);
|
||||
}
|
||||
|
||||
|
|
@ -111,203 +114,109 @@ void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSeri
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class FontDCHolder : private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
FontDCHolder()
|
||||
: fontH (0), previousFontH (0), dc (0), numKPs (0), size (0),
|
||||
bold (false), italic (false)
|
||||
{
|
||||
}
|
||||
|
||||
~FontDCHolder()
|
||||
{
|
||||
deleteDCAndFont();
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
juce_DeclareSingleton_SingleThreaded_Minimal (FontDCHolder);
|
||||
|
||||
//==============================================================================
|
||||
HDC loadFont (const String& fontName_, const bool bold_, const bool italic_, const int size_)
|
||||
{
|
||||
if (fontName != fontName_ || bold != bold_ || italic != italic_ || size != size_)
|
||||
{
|
||||
fontName = fontName_;
|
||||
bold = bold_;
|
||||
italic = italic_;
|
||||
size = size_;
|
||||
|
||||
deleteDCAndFont();
|
||||
|
||||
dc = CreateCompatibleDC (0);
|
||||
SetMapperFlags (dc, 0);
|
||||
SetMapMode (dc, MM_TEXT);
|
||||
|
||||
LOGFONTW lf = { 0 };
|
||||
lf.lfCharSet = DEFAULT_CHARSET;
|
||||
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
||||
lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
|
||||
lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
|
||||
lf.lfQuality = PROOF_QUALITY;
|
||||
lf.lfItalic = (BYTE) (italic ? TRUE : FALSE);
|
||||
lf.lfWeight = bold ? FW_BOLD : FW_NORMAL;
|
||||
lf.lfHeight = size > 0 ? size : -256;
|
||||
fontName.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
|
||||
|
||||
HFONT standardSizedFont = CreateFontIndirect (&lf);
|
||||
|
||||
if (standardSizedFont != 0)
|
||||
{
|
||||
if ((previousFontH = SelectObject (dc, standardSizedFont)) != 0)
|
||||
{
|
||||
fontH = standardSizedFont;
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
OUTLINETEXTMETRIC otm;
|
||||
if (GetOutlineTextMetrics (dc, sizeof (otm), &otm) != 0)
|
||||
{
|
||||
lf.lfHeight = -(int) otm.otmEMSquare;
|
||||
fontH = CreateFontIndirect (&lf);
|
||||
|
||||
SelectObject (dc, fontH);
|
||||
DeleteObject (standardSizedFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dc;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
KERNINGPAIR* getKerningPairs (int& numKPs_)
|
||||
{
|
||||
if (kps == nullptr)
|
||||
{
|
||||
numKPs = GetKerningPairs (dc, 0, 0);
|
||||
kps.calloc (numKPs);
|
||||
GetKerningPairs (dc, numKPs, kps);
|
||||
}
|
||||
|
||||
numKPs_ = numKPs;
|
||||
return kps;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
HFONT fontH;
|
||||
HGDIOBJ previousFontH;
|
||||
HDC dc;
|
||||
String fontName;
|
||||
HeapBlock <KERNINGPAIR> kps;
|
||||
int numKPs, size;
|
||||
bool bold, italic;
|
||||
|
||||
void deleteDCAndFont()
|
||||
{
|
||||
if (dc != 0)
|
||||
{
|
||||
SelectObject (dc, previousFontH); // Replacing the previous font before deleting the DC avoids a warning in BoundsChecker
|
||||
DeleteDC (dc);
|
||||
dc = 0;
|
||||
}
|
||||
|
||||
if (fontH != 0)
|
||||
{
|
||||
DeleteObject (fontH);
|
||||
fontH = 0;
|
||||
}
|
||||
|
||||
kps.free();
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (FontDCHolder);
|
||||
};
|
||||
|
||||
juce_ImplementSingleton_SingleThreaded (FontDCHolder);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class WindowsTypeface : public CustomTypeface
|
||||
class WindowsTypeface : public Typeface
|
||||
{
|
||||
public:
|
||||
WindowsTypeface (const Font& font)
|
||||
: Typeface (font.getTypefaceName()),
|
||||
fontH (0),
|
||||
previousFontH (0),
|
||||
dc (CreateCompatibleDC (0)),
|
||||
ascent (1.0f),
|
||||
defaultGlyph (-1),
|
||||
bold (font.isBold()),
|
||||
italic (font.isItalic())
|
||||
{
|
||||
HDC dc = FontDCHolder::getInstance()->loadFont (font.getTypefaceName(),
|
||||
font.isBold(), font.isItalic(), 0);
|
||||
loadFont();
|
||||
|
||||
TEXTMETRIC tm;
|
||||
tm.tmAscent = tm.tmHeight = 1;
|
||||
tm.tmDefaultChar = 0;
|
||||
GetTextMetrics (dc, &tm);
|
||||
|
||||
setCharacteristics (font.getTypefaceName(),
|
||||
tm.tmAscent / (float) tm.tmHeight,
|
||||
font.isBold(), font.isItalic(),
|
||||
tm.tmDefaultChar);
|
||||
if (GetTextMetrics (dc, &tm))
|
||||
{
|
||||
ascent = tm.tmAscent / (float) tm.tmHeight;
|
||||
defaultGlyph = getGlyphForChar (dc, tm.tmDefaultChar);
|
||||
createKerningPairs (dc, (float) tm.tmHeight);
|
||||
}
|
||||
}
|
||||
|
||||
bool loadGlyphIfPossible (juce_wchar character)
|
||||
~WindowsTypeface()
|
||||
{
|
||||
HDC dc = FontDCHolder::getInstance()->loadFont (name, isBold, isItalic, 0);
|
||||
SelectObject (dc, previousFontH); // Replacing the previous font before deleting the DC avoids a warning in BoundsChecker
|
||||
DeleteDC (dc);
|
||||
|
||||
GLYPHMETRICS gm;
|
||||
if (fontH != 0)
|
||||
DeleteObject (fontH);
|
||||
}
|
||||
|
||||
// if this is the fallback font, skip checking for the glyph's existence. This is because
|
||||
// with fonts like Tahoma, GetGlyphIndices can say that a glyph doesn't exist, but it still
|
||||
// gets correctly created later on.
|
||||
if (! isFallbackFont)
|
||||
float getAscent() const { return ascent; }
|
||||
float getDescent() const { return 1.0f - ascent; }
|
||||
|
||||
float getStringWidth (const String& text)
|
||||
{
|
||||
const CharPointer_UTF16 utf16 (text.toUTF16());
|
||||
const int numChars = utf16.length();
|
||||
HeapBlock<int16> results (numChars + 1);
|
||||
results[numChars] = -1;
|
||||
float x = 0;
|
||||
|
||||
if (GetGlyphIndices (dc, utf16, numChars, reinterpret_cast <WORD*> (results.getData()),
|
||||
GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
|
||||
{
|
||||
const WCHAR charToTest[] = { (WCHAR) character, 0 };
|
||||
WORD index = 0;
|
||||
for (int i = 0; i < numChars; ++i)
|
||||
x += getKerning (dc, results[i], results[i + 1]);
|
||||
}
|
||||
|
||||
if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR
|
||||
&& index == 0xffff)
|
||||
return x;
|
||||
}
|
||||
|
||||
void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets)
|
||||
{
|
||||
const CharPointer_UTF16 utf16 (text.toUTF16());
|
||||
const int numChars = utf16.length();
|
||||
HeapBlock<int16> results (numChars + 1);
|
||||
results[numChars] = -1;
|
||||
float x = 0;
|
||||
|
||||
if (GetGlyphIndices (dc, utf16, numChars, reinterpret_cast <WORD*> (results.getData()),
|
||||
GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
|
||||
{
|
||||
resultGlyphs.ensureStorageAllocated (numChars);
|
||||
xOffsets.ensureStorageAllocated (numChars + 1);
|
||||
|
||||
for (int i = 0; i < numChars; ++i)
|
||||
{
|
||||
return false;
|
||||
resultGlyphs.add (results[i]);
|
||||
xOffsets.add (x);
|
||||
x += getKerning (dc, results[i], results[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
Path glyphPath;
|
||||
xOffsets.add (x);
|
||||
}
|
||||
|
||||
TEXTMETRIC tm;
|
||||
if (! GetTextMetrics (dc, &tm))
|
||||
{
|
||||
addGlyph (character, glyphPath, 0);
|
||||
return true;
|
||||
}
|
||||
bool getOutlineForGlyph (int glyphNumber, Path& glyphPath)
|
||||
{
|
||||
if (glyphNumber < 0)
|
||||
glyphNumber = defaultGlyph;
|
||||
|
||||
const float height = (float) tm.tmHeight;
|
||||
static const MAT2 identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } };
|
||||
|
||||
const int bufSize = GetGlyphOutline (dc, character, GGO_NATIVE,
|
||||
GLYPHMETRICS gm;
|
||||
const int bufSize = GetGlyphOutline (dc, glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX,
|
||||
&gm, 0, 0, &identityMatrix);
|
||||
|
||||
if (bufSize > 0)
|
||||
{
|
||||
HeapBlock<char> data (bufSize);
|
||||
|
||||
GetGlyphOutline (dc, character, GGO_NATIVE, &gm,
|
||||
GetGlyphOutline (dc, glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm,
|
||||
bufSize, data, &identityMatrix);
|
||||
|
||||
const TTPOLYGONHEADER* pheader = reinterpret_cast<TTPOLYGONHEADER*> (data.getData());
|
||||
|
||||
const float scaleX = 1.0f / height;
|
||||
const float scaleY = -1.0f / height;
|
||||
const float scaleX = 1.0f / tm.tmHeight;
|
||||
const float scaleY = -scaleX;
|
||||
|
||||
while ((char*) pheader < data + bufSize)
|
||||
{
|
||||
float x = scaleX * pheader->pfxStart.x.value;
|
||||
float y = scaleY * pheader->pfxStart.y.value;
|
||||
|
||||
glyphPath.startNewSubPath (x, y);
|
||||
glyphPath.startNewSubPath (scaleX * pheader->pfxStart.x.value,
|
||||
scaleY * pheader->pfxStart.y.value);
|
||||
|
||||
const TTPOLYCURVE* curve = (const TTPOLYCURVE*) ((const char*) pheader + sizeof (TTPOLYGONHEADER));
|
||||
const char* const curveEnd = ((const char*) pheader) + pheader->cb;
|
||||
|
|
@ -317,12 +226,8 @@ public:
|
|||
if (curve->wType == TT_PRIM_LINE)
|
||||
{
|
||||
for (int i = 0; i < curve->cpfx; ++i)
|
||||
{
|
||||
x = scaleX * curve->apfx[i].x.value;
|
||||
y = scaleY * curve->apfx[i].y.value;
|
||||
|
||||
glyphPath.lineTo (x, y);
|
||||
}
|
||||
glyphPath.lineTo (scaleX * curve->apfx[i].x.value,
|
||||
scaleY * curve->apfx[i].y.value);
|
||||
}
|
||||
else if (curve->wType == TT_PRIM_QSPLINE)
|
||||
{
|
||||
|
|
@ -330,23 +235,16 @@ public:
|
|||
{
|
||||
const float x2 = scaleX * curve->apfx[i].x.value;
|
||||
const float y2 = scaleY * curve->apfx[i].y.value;
|
||||
float x3, y3;
|
||||
float x3 = scaleX * curve->apfx[i + 1].x.value;
|
||||
float y3 = scaleY * curve->apfx[i + 1].y.value;
|
||||
|
||||
if (i < curve->cpfx - 2)
|
||||
{
|
||||
x3 = 0.5f * (x2 + scaleX * curve->apfx[i + 1].x.value);
|
||||
y3 = 0.5f * (y2 + scaleY * curve->apfx[i + 1].y.value);
|
||||
}
|
||||
else
|
||||
{
|
||||
x3 = scaleX * curve->apfx[i + 1].x.value;
|
||||
y3 = scaleY * curve->apfx[i + 1].y.value;
|
||||
x3 = 0.5f * (x2 + x3);
|
||||
y3 = 0.5f * (y2 + y3);
|
||||
}
|
||||
|
||||
glyphPath.quadraticTo (x2, y2, x3, y3);
|
||||
|
||||
x = x3;
|
||||
y = y3;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -359,25 +257,150 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
addGlyph (character, glyphPath, gm.gmCellIncX / height);
|
||||
|
||||
int numKPs;
|
||||
const KERNINGPAIR* const kps = FontDCHolder::getInstance()->getKerningPairs (numKPs);
|
||||
|
||||
for (int i = 0; i < numKPs; ++i)
|
||||
{
|
||||
if (kps[i].wFirst == character)
|
||||
addKerningPair (kps[i].wFirst, kps[i].wSecond,
|
||||
kps[i].iKernAmount / height);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static const MAT2 identityMatrix;
|
||||
HFONT fontH;
|
||||
HGDIOBJ previousFontH;
|
||||
HDC dc;
|
||||
TEXTMETRIC tm;
|
||||
float ascent;
|
||||
int defaultGlyph;
|
||||
bool bold, italic;
|
||||
|
||||
struct KerningPair
|
||||
{
|
||||
int glyph1, glyph2;
|
||||
float kerning;
|
||||
|
||||
bool operator== (const KerningPair& other) const noexcept
|
||||
{
|
||||
return glyph1 == other.glyph1 && glyph2 == other.glyph2;
|
||||
}
|
||||
|
||||
bool operator< (const KerningPair& other) const noexcept
|
||||
{
|
||||
return glyph1 < other.glyph1
|
||||
|| (glyph1 == other.glyph1 && glyph2 < other.glyph2);
|
||||
}
|
||||
};
|
||||
|
||||
SortedSet<KerningPair> kerningPairs;
|
||||
|
||||
void loadFont()
|
||||
{
|
||||
SetMapperFlags (dc, 0);
|
||||
SetMapMode (dc, MM_TEXT);
|
||||
|
||||
LOGFONTW lf = { 0 };
|
||||
lf.lfCharSet = DEFAULT_CHARSET;
|
||||
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
||||
lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
|
||||
lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
|
||||
lf.lfQuality = PROOF_QUALITY;
|
||||
lf.lfItalic = (BYTE) (italic ? TRUE : FALSE);
|
||||
lf.lfWeight = bold ? FW_BOLD : FW_NORMAL;
|
||||
lf.lfHeight = -256;
|
||||
name.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
|
||||
|
||||
HFONT standardSizedFont = CreateFontIndirect (&lf);
|
||||
|
||||
if (standardSizedFont != 0)
|
||||
{
|
||||
if ((previousFontH = SelectObject (dc, standardSizedFont)) != 0)
|
||||
{
|
||||
fontH = standardSizedFont;
|
||||
|
||||
OUTLINETEXTMETRIC otm;
|
||||
if (GetOutlineTextMetrics (dc, sizeof (otm), &otm) != 0)
|
||||
{
|
||||
lf.lfHeight = -(int) otm.otmEMSquare;
|
||||
fontH = CreateFontIndirect (&lf);
|
||||
|
||||
SelectObject (dc, fontH);
|
||||
DeleteObject (standardSizedFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void createKerningPairs (HDC dc, const float height)
|
||||
{
|
||||
HeapBlock<KERNINGPAIR> rawKerning;
|
||||
const int numKPs = GetKerningPairs (dc, 0, 0);
|
||||
rawKerning.calloc (numKPs);
|
||||
GetKerningPairs (dc, numKPs, rawKerning);
|
||||
|
||||
kerningPairs.ensureStorageAllocated (numKPs);
|
||||
|
||||
for (int i = 0; i < numKPs; ++i)
|
||||
{
|
||||
KerningPair kp;
|
||||
kp.glyph1 = getGlyphForChar (dc, rawKerning[i].wFirst);
|
||||
kp.glyph2 = getGlyphForChar (dc, rawKerning[i].wSecond);
|
||||
|
||||
const int standardWidth = getGlyphWidth (dc, kp.glyph1);
|
||||
kp.kerning = (standardWidth + rawKerning[i].iKernAmount) / height;
|
||||
kerningPairs.add (kp);
|
||||
|
||||
kp.glyph2 = -1; // add another entry for the standard width version..
|
||||
kp.kerning = standardWidth / height;
|
||||
kerningPairs.add (kp);
|
||||
}
|
||||
}
|
||||
|
||||
static int getGlyphForChar (HDC dc, juce_wchar character)
|
||||
{
|
||||
const WCHAR charToTest[] = { (WCHAR) character, 0 };
|
||||
WORD index = 0;
|
||||
|
||||
if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR
|
||||
|| index == 0xffff)
|
||||
return -1;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static int getGlyphWidth (HDC dc, int glyphNumber)
|
||||
{
|
||||
GLYPHMETRICS gm;
|
||||
gm.gmCellIncX = 0;
|
||||
GetGlyphOutline (dc, glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, 0, &identityMatrix);
|
||||
return gm.gmCellIncX;
|
||||
}
|
||||
|
||||
float getKerning (HDC dc, const int glyph1, const int glyph2)
|
||||
{
|
||||
KerningPair kp;
|
||||
kp.glyph1 = glyph1;
|
||||
kp.glyph2 = glyph2;
|
||||
int index = kerningPairs.indexOf (kp);
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
kp.glyph2 = -1;
|
||||
index = kerningPairs.indexOf (kp);
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
kp.glyph2 = -1;
|
||||
kp.kerning = getGlyphWidth (dc, kp.glyph1) / (float) tm.tmHeight;
|
||||
kerningPairs.add (kp);
|
||||
return kp.kerning;
|
||||
}
|
||||
}
|
||||
|
||||
return kerningPairs.getReference (index).kerning;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTypeface);
|
||||
};
|
||||
|
||||
const MAT2 WindowsTypeface::identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } };
|
||||
|
||||
|
||||
const Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
|
||||
{
|
||||
return new WindowsTypeface (font);
|
||||
|
|
|
|||
|
|
@ -1522,7 +1522,7 @@ private:
|
|||
peer = this;
|
||||
|
||||
peer->handleMouseWheel (0, peer->globalToLocal (globalPos), getMouseEventTime(),
|
||||
isVertical ? 0.0f : amount,
|
||||
isVertical ? 0.0f : -amount,
|
||||
isVertical ? amount : 0.0f);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue