upgrade to JUCE 5.4.3. Remove (probably) unused JUCE modules. Remove VST2 target (it's been end-of-life'd by Steinberg and by JUCE)
This commit is contained in:
@ -43,14 +43,10 @@ namespace RenderingHelpers
|
||||
class TranslationOrTransform
|
||||
{
|
||||
public:
|
||||
TranslationOrTransform() noexcept {}
|
||||
TranslationOrTransform() = default;
|
||||
TranslationOrTransform (Point<int> origin) noexcept : offset (origin) {}
|
||||
|
||||
TranslationOrTransform (const TranslationOrTransform& other) noexcept
|
||||
: complexTransform (other.complexTransform), offset (other.offset),
|
||||
isOnlyTranslated (other.isOnlyTranslated), isRotated (other.isRotated)
|
||||
{
|
||||
}
|
||||
TranslationOrTransform (const TranslationOrTransform& other) = default;
|
||||
|
||||
AffineTransform getTransform() const noexcept
|
||||
{
|
||||
@ -156,7 +152,7 @@ public:
|
||||
reset();
|
||||
}
|
||||
|
||||
~GlyphCache()
|
||||
~GlyphCache() override
|
||||
{
|
||||
getSingletonPointer() = nullptr;
|
||||
}
|
||||
@ -194,35 +190,34 @@ public:
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (auto* g = findExistingGlyph (font, glyphNumber))
|
||||
if (auto g = findExistingGlyph (font, glyphNumber))
|
||||
{
|
||||
++hits;
|
||||
return g;
|
||||
}
|
||||
|
||||
++misses;
|
||||
auto* g = getGlyphForReuse();
|
||||
auto g = getGlyphForReuse();
|
||||
jassert (g != nullptr);
|
||||
g->generate (font, glyphNumber);
|
||||
return g;
|
||||
}
|
||||
|
||||
private:
|
||||
friend struct ContainerDeletePolicy<CachedGlyphType>;
|
||||
ReferenceCountedArray<CachedGlyphType> glyphs;
|
||||
Atomic<int> accessCounter, hits, misses;
|
||||
CriticalSection lock;
|
||||
|
||||
CachedGlyphType* findExistingGlyph (const Font& font, int glyphNumber) const noexcept
|
||||
ReferenceCountedObjectPtr<CachedGlyphType> findExistingGlyph (const Font& font, int glyphNumber) const noexcept
|
||||
{
|
||||
for (auto* g : glyphs)
|
||||
for (auto g : glyphs)
|
||||
if (g->glyph == glyphNumber && g->font == font)
|
||||
return g;
|
||||
return *g;
|
||||
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
CachedGlyphType* getGlyphForReuse()
|
||||
ReferenceCountedObjectPtr<CachedGlyphType> getGlyphForReuse()
|
||||
{
|
||||
if (hits.get() + misses.get() > glyphs.size() * 16)
|
||||
{
|
||||
@ -233,8 +228,8 @@ private:
|
||||
misses = 0;
|
||||
}
|
||||
|
||||
if (auto* g = findLeastRecentlyUsedGlyph())
|
||||
return g;
|
||||
if (auto g = findLeastRecentlyUsedGlyph())
|
||||
return *g;
|
||||
|
||||
addNewGlyphSlots (32);
|
||||
return glyphs.getLast();
|
||||
@ -284,7 +279,7 @@ template <class RendererType>
|
||||
class CachedGlyphEdgeTable : public ReferenceCountedObject
|
||||
{
|
||||
public:
|
||||
CachedGlyphEdgeTable() {}
|
||||
CachedGlyphEdgeTable() = default;
|
||||
|
||||
void draw (RendererType& state, Point<float> pos) const
|
||||
{
|
||||
@ -581,7 +576,7 @@ namespace EdgeTableFillers
|
||||
SolidColour (const Image::BitmapData& image, PixelARGB colour)
|
||||
: destData (image), sourceColour (colour)
|
||||
{
|
||||
if (sizeof (PixelType) == 3 && destData.pixelStride == sizeof (PixelType))
|
||||
if (sizeof (PixelType) == 3 && (size_t) destData.pixelStride == sizeof (PixelType))
|
||||
{
|
||||
areRGBComponentsEqual = sourceColour.getRed() == sourceColour.getGreen()
|
||||
&& sourceColour.getGreen() == sourceColour.getBlue();
|
||||
@ -690,11 +685,11 @@ namespace EdgeTableFillers
|
||||
|
||||
forcedinline void replaceLine (PixelRGB* dest, PixelARGB colour, int width) const noexcept
|
||||
{
|
||||
if (destData.pixelStride == sizeof (*dest))
|
||||
if ((size_t) destData.pixelStride == sizeof (*dest))
|
||||
{
|
||||
if (areRGBComponentsEqual) // if all the component values are the same, we can cheat..
|
||||
{
|
||||
memset (dest, colour.getRed(), (size_t) width * 3);
|
||||
memset ((void*) dest, colour.getRed(), (size_t) width * 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -735,8 +730,8 @@ namespace EdgeTableFillers
|
||||
|
||||
forcedinline void replaceLine (PixelAlpha* dest, const PixelARGB colour, int width) const noexcept
|
||||
{
|
||||
if (destData.pixelStride == sizeof (*dest))
|
||||
memset (dest, colour.getAlpha(), (size_t) width);
|
||||
if ((size_t) destData.pixelStride == sizeof (*dest))
|
||||
memset ((void*) dest, colour.getAlpha(), (size_t) width);
|
||||
else
|
||||
JUCE_PERFORM_PIXEL_OP_LOOP (setAlpha (colour.getAlpha()))
|
||||
}
|
||||
@ -966,7 +961,7 @@ namespace EdgeTableFillers
|
||||
&& srcData.pixelFormat == Image::RGB
|
||||
&& destData.pixelFormat == Image::RGB)
|
||||
{
|
||||
memcpy (dest, src, (size_t) (width * srcStride));
|
||||
memcpy ((void*) dest, src, (size_t) (width * srcStride));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1398,7 +1393,7 @@ namespace EdgeTableFillers
|
||||
private:
|
||||
struct BresenhamInterpolator
|
||||
{
|
||||
BresenhamInterpolator() noexcept {}
|
||||
BresenhamInterpolator() = default;
|
||||
|
||||
void set (int n1, int n2, int steps, int offsetInt) noexcept
|
||||
{
|
||||
@ -1629,8 +1624,8 @@ struct ClipRegions
|
||||
{
|
||||
struct Base : public SingleThreadedReferenceCountedObject
|
||||
{
|
||||
Base() {}
|
||||
virtual ~Base() {}
|
||||
Base() = default;
|
||||
~Base() override = default;
|
||||
|
||||
using Ptr = ReferenceCountedObjectPtr<Base>;
|
||||
|
||||
@ -1671,13 +1666,13 @@ struct ClipRegions
|
||||
|
||||
using Ptr = typename Base::Ptr;
|
||||
|
||||
Ptr clone() const override { return new EdgeTableRegion (*this); }
|
||||
Ptr clone() const override { return *new EdgeTableRegion (*this); }
|
||||
Ptr applyClipTo (const Ptr& target) const override { return target->clipToEdgeTable (edgeTable); }
|
||||
|
||||
Ptr clipToRectangle (Rectangle<int> r) override
|
||||
{
|
||||
edgeTable.clipToRectangle (r);
|
||||
return edgeTable.isEmpty() ? nullptr : this;
|
||||
return edgeTable.isEmpty() ? Ptr() : Ptr (*this);
|
||||
}
|
||||
|
||||
Ptr clipToRectangleList (const RectangleList<int>& r) override
|
||||
@ -1688,26 +1683,26 @@ struct ClipRegions
|
||||
for (auto& i : inverse)
|
||||
edgeTable.excludeRectangle (i);
|
||||
|
||||
return edgeTable.isEmpty() ? nullptr : this;
|
||||
return edgeTable.isEmpty() ? Ptr() : Ptr (*this);
|
||||
}
|
||||
|
||||
Ptr excludeClipRectangle (Rectangle<int> r) override
|
||||
{
|
||||
edgeTable.excludeRectangle (r);
|
||||
return edgeTable.isEmpty() ? nullptr : this;
|
||||
return edgeTable.isEmpty() ? Ptr() : Ptr (*this);
|
||||
}
|
||||
|
||||
Ptr clipToPath (const Path& p, const AffineTransform& transform) override
|
||||
{
|
||||
EdgeTable et (edgeTable.getMaximumBounds(), p, transform);
|
||||
edgeTable.clipToEdgeTable (et);
|
||||
return edgeTable.isEmpty() ? nullptr : this;
|
||||
return edgeTable.isEmpty() ? Ptr() : Ptr (*this);
|
||||
}
|
||||
|
||||
Ptr clipToEdgeTable (const EdgeTable& et) override
|
||||
{
|
||||
edgeTable.clipToEdgeTable (et);
|
||||
return edgeTable.isEmpty() ? nullptr : this;
|
||||
return edgeTable.isEmpty() ? Ptr() : Ptr (*this);
|
||||
}
|
||||
|
||||
Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, Graphics::ResamplingQuality quality) override
|
||||
@ -1726,11 +1721,11 @@ struct ClipRegions
|
||||
auto imageY = ((ty + 128) >> 8);
|
||||
|
||||
if (image.getFormat() == Image::ARGB)
|
||||
straightClipImage (srcData, imageX, imageY, (PixelARGB*) 0);
|
||||
straightClipImage (srcData, imageX, imageY, (PixelARGB*) nullptr);
|
||||
else
|
||||
straightClipImage (srcData, imageX, imageY, (PixelAlpha*) 0);
|
||||
straightClipImage (srcData, imageX, imageY, (PixelAlpha*) nullptr);
|
||||
|
||||
return edgeTable.isEmpty() ? nullptr : this;
|
||||
return edgeTable.isEmpty() ? Ptr() : Ptr (*this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1747,12 +1742,12 @@ struct ClipRegions
|
||||
if (! edgeTable.isEmpty())
|
||||
{
|
||||
if (image.getFormat() == Image::ARGB)
|
||||
transformedClipImage (srcData, transform, quality, (PixelARGB*) 0);
|
||||
transformedClipImage (srcData, transform, quality, (PixelARGB*) nullptr);
|
||||
else
|
||||
transformedClipImage (srcData, transform, quality, (PixelAlpha*) 0);
|
||||
transformedClipImage (srcData, transform, quality, (PixelAlpha*) nullptr);
|
||||
}
|
||||
|
||||
return edgeTable.isEmpty() ? nullptr : this;
|
||||
return edgeTable.isEmpty() ? Ptr() : Ptr (*this);
|
||||
}
|
||||
|
||||
void translate (Point<int> delta) override
|
||||
@ -1852,25 +1847,25 @@ struct ClipRegions
|
||||
|
||||
using Ptr = typename Base::Ptr;
|
||||
|
||||
Ptr clone() const override { return new RectangleListRegion (*this); }
|
||||
Ptr clone() const override { return *new RectangleListRegion (*this); }
|
||||
Ptr applyClipTo (const Ptr& target) const override { return target->clipToRectangleList (clip); }
|
||||
|
||||
Ptr clipToRectangle (Rectangle<int> r) override
|
||||
{
|
||||
clip.clipTo (r);
|
||||
return clip.isEmpty() ? nullptr : this;
|
||||
return clip.isEmpty() ? Ptr() : Ptr (*this);
|
||||
}
|
||||
|
||||
Ptr clipToRectangleList (const RectangleList<int>& r) override
|
||||
{
|
||||
clip.clipTo (r);
|
||||
return clip.isEmpty() ? nullptr : this;
|
||||
return clip.isEmpty() ? Ptr() : Ptr (*this);
|
||||
}
|
||||
|
||||
Ptr excludeClipRectangle (Rectangle<int> r) override
|
||||
{
|
||||
clip.subtract (r);
|
||||
return clip.isEmpty() ? nullptr : this;
|
||||
return clip.isEmpty() ? Ptr() : Ptr (*this);
|
||||
}
|
||||
|
||||
Ptr clipToPath (const Path& p, const AffineTransform& transform) override { return toEdgeTable()->clipToPath (p, transform); }
|
||||
@ -2069,9 +2064,9 @@ struct ClipRegions
|
||||
JUCE_DECLARE_NON_COPYABLE (SubRectangleIteratorFloat)
|
||||
};
|
||||
|
||||
Ptr toEdgeTable() const { return new EdgeTableRegion (clip); }
|
||||
Ptr toEdgeTable() const { return *new EdgeTableRegion (clip); }
|
||||
|
||||
RectangleListRegion& operator= (const RectangleListRegion&);
|
||||
RectangleListRegion& operator= (const RectangleListRegion&) = delete;
|
||||
};
|
||||
};
|
||||
|
||||
@ -2268,7 +2263,7 @@ public:
|
||||
auto clipped = clip->getClipBounds().getIntersection (r);
|
||||
|
||||
if (! clipped.isEmpty())
|
||||
fillShape (new RectangleListRegionType (clipped), false);
|
||||
fillShape (*new RectangleListRegionType (clipped), false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2283,7 +2278,7 @@ public:
|
||||
auto clipped = clip->getClipBounds().toFloat().getIntersection (r);
|
||||
|
||||
if (! clipped.isEmpty())
|
||||
fillShape (new EdgeTableRegionType (clipped), false);
|
||||
fillShape (*new EdgeTableRegionType (clipped), false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2337,7 +2332,7 @@ public:
|
||||
|
||||
if (transform.isIdentity())
|
||||
{
|
||||
fillShape (new EdgeTableRegionType (list), false);
|
||||
fillShape (*new EdgeTableRegionType (list), false);
|
||||
}
|
||||
else if (! transform.isRotated)
|
||||
{
|
||||
@ -2348,7 +2343,7 @@ public:
|
||||
else
|
||||
transformed.transformAll (transform.getTransform());
|
||||
|
||||
fillShape (new EdgeTableRegionType (transformed), false);
|
||||
fillShape (*new EdgeTableRegionType (transformed), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2365,7 +2360,7 @@ public:
|
||||
auto clipRect = clip->getClipBounds();
|
||||
|
||||
if (path.getBoundsTransformed (trans).getSmallestIntegerContainer().intersects (clipRect))
|
||||
fillShape (new EdgeTableRegionType (clipRect, path, trans), false);
|
||||
fillShape (*new EdgeTableRegionType (clipRect, path, trans), false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2384,7 +2379,7 @@ public:
|
||||
edgeTableClip->edgeTable.multiplyLevels (1.0f + 1.6f * brightness);
|
||||
}
|
||||
|
||||
fillShape (edgeTableClip, false);
|
||||
fillShape (*edgeTableClip, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2398,15 +2393,15 @@ public:
|
||||
void drawImage (const Image& sourceImage, const AffineTransform& trans)
|
||||
{
|
||||
if (clip != nullptr && ! fillType.colour.isTransparent())
|
||||
renderImage (sourceImage, trans, nullptr);
|
||||
renderImage (sourceImage, trans, {});
|
||||
}
|
||||
|
||||
static bool isOnlyTranslationAllowingError (const AffineTransform& t, float tolerence) noexcept
|
||||
static bool isOnlyTranslationAllowingError (const AffineTransform& t, float tolerance) noexcept
|
||||
{
|
||||
return std::abs (t.mat01) < tolerence
|
||||
&& std::abs (t.mat10) < tolerence
|
||||
&& std::abs (t.mat00 - 1.0f) < tolerence
|
||||
&& std::abs (t.mat11 - 1.0f) < tolerence;
|
||||
return std::abs (t.mat01) < tolerance
|
||||
&& std::abs (t.mat10) < tolerance
|
||||
&& std::abs (t.mat00 - 1.0f) < tolerance
|
||||
&& std::abs (t.mat11 - 1.0f) < tolerance;
|
||||
}
|
||||
|
||||
void renderImage (const Image& sourceImage, const AffineTransform& trans, const BaseRegionType* tiledFillClipRegion)
|
||||
@ -2435,7 +2430,7 @@ public:
|
||||
area = area.getIntersection (getThis().getMaximumBounds());
|
||||
|
||||
if (! area.isEmpty())
|
||||
if (auto c = clip->applyClipTo (new EdgeTableRegionType (area)))
|
||||
if (auto c = clip->applyClipTo (*new EdgeTableRegionType (area)))
|
||||
c->renderImageUntransformed (getThis(), sourceImage, alpha, tx, ty, false);
|
||||
}
|
||||
|
||||
@ -2491,7 +2486,7 @@ public:
|
||||
}
|
||||
else if (fillType.isTiledImage())
|
||||
{
|
||||
renderImage (fillType.image, fillType.transform, shapeToFill);
|
||||
renderImage (fillType.image, fillType.transform, shapeToFill.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2529,10 +2524,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
SoftwareRendererSavedState (const SoftwareRendererSavedState& other)
|
||||
: BaseClass (other), image (other.image), font (other.font)
|
||||
{
|
||||
}
|
||||
SoftwareRendererSavedState (const SoftwareRendererSavedState& other) = default;
|
||||
|
||||
SoftwareRendererSavedState* beginTransparencyLayer (float opacity)
|
||||
{
|
||||
@ -2610,7 +2602,7 @@ public:
|
||||
std::unique_ptr<EdgeTable> et (font.getTypeface()->getEdgeTableForGlyph (glyphNumber, t, fontHeight));
|
||||
|
||||
if (et != nullptr)
|
||||
fillShape (new EdgeTableRegionType (*et), false);
|
||||
fillShape (*new EdgeTableRegionType (*et), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2641,9 +2633,9 @@ public:
|
||||
|
||||
switch (destData.pixelFormat)
|
||||
{
|
||||
case Image::ARGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelARGB*) 0); break;
|
||||
case Image::RGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelRGB*) 0); break;
|
||||
default: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelAlpha*) 0); break;
|
||||
case Image::ARGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelARGB*) nullptr); break;
|
||||
case Image::RGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelRGB*) nullptr); break;
|
||||
default: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelAlpha*) nullptr); break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2658,9 +2650,9 @@ public:
|
||||
|
||||
switch (destData.pixelFormat)
|
||||
{
|
||||
case Image::ARGB: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break;
|
||||
case Image::RGB: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break;
|
||||
default: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break;
|
||||
case Image::ARGB: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) nullptr); break;
|
||||
case Image::RGB: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) nullptr); break;
|
||||
default: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) nullptr); break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2669,7 +2661,7 @@ public:
|
||||
Font font;
|
||||
|
||||
private:
|
||||
SoftwareRendererSavedState& operator= (const SoftwareRendererSavedState&);
|
||||
SoftwareRendererSavedState& operator= (const SoftwareRendererSavedState&) = delete;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
@ -2681,7 +2673,7 @@ public:
|
||||
: currentState (initialState)
|
||||
{}
|
||||
|
||||
SavedStateStack() noexcept {}
|
||||
SavedStateStack() = default;
|
||||
|
||||
void initialise (StateObjectType* state)
|
||||
{
|
||||
@ -2765,7 +2757,7 @@ public:
|
||||
|
||||
protected:
|
||||
StackBasedLowLevelGraphicsContext (SavedStateType* initialState) : stack (initialState) {}
|
||||
StackBasedLowLevelGraphicsContext() {}
|
||||
StackBasedLowLevelGraphicsContext() = default;
|
||||
|
||||
RenderingHelpers::SavedStateStack<SavedStateType> stack;
|
||||
};
|
||||
|
@ -94,11 +94,37 @@ bool TextLayout::createNativeLayout (const AttributedString&)
|
||||
#else
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (create, "create", "(Ljava/lang/String;I)Landroid/graphics/Typeface;") \
|
||||
STATICMETHOD (createFromFile, "createFromFile", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \
|
||||
STATICMETHOD (createFromAsset, "createFromAsset", "(Landroid/content/res/AssetManager;Ljava/lang/String;)Landroid/graphics/Typeface;")
|
||||
|
||||
DECLARE_JNI_CLASS (TypefaceClass, "android/graphics/Typeface");
|
||||
DECLARE_JNI_CLASS (TypefaceClass, "android/graphics/Typeface")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (constructor, "<init>", "()V") \
|
||||
METHOD (computeBounds, "computeBounds", "(Landroid/graphics/RectF;Z)V")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidPath, "android/graphics/Path")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (constructor, "<init>", "()V") \
|
||||
FIELD (left, "left", "F") \
|
||||
FIELD (right, "right", "F") \
|
||||
FIELD (top, "top", "F") \
|
||||
FIELD (bottom, "bottom", "F") \
|
||||
METHOD (roundOut, "roundOut", "(Landroid/graphics/Rect;)V")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidRectF, "android/graphics/RectF")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (getInstance, "getInstance", "(Ljava/lang/String;)Ljava/security/MessageDigest;") \
|
||||
METHOD (update, "update", "([B)V") \
|
||||
METHOD (digest, "digest", "()[B")
|
||||
DECLARE_JNI_CLASS (JavaMessageDigest, "java/security/MessageDigest")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
@ -136,8 +162,7 @@ public:
|
||||
JNIEnv* const env = getEnv();
|
||||
|
||||
// First check whether there's an embedded asset with this font name:
|
||||
typeface = GlobalRef (android.activity.callObjectMethod (JuceAppActivity.getTypeFaceFromAsset,
|
||||
javaString ("fonts/" + name).get()));
|
||||
typeface = GlobalRef (getTypefaceFromAsset (name));
|
||||
|
||||
if (typeface.get() == nullptr)
|
||||
{
|
||||
@ -150,12 +175,12 @@ public:
|
||||
fontFile = findFontFile (name, isBold, isItalic);
|
||||
|
||||
if (fontFile.exists())
|
||||
typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile,
|
||||
javaString (fontFile.getFullPathName()).get()));
|
||||
typeface = GlobalRef (LocalRef<jobject>(env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile,
|
||||
javaString (fontFile.getFullPathName()).get())));
|
||||
else
|
||||
typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.create,
|
||||
javaString (getName()).get(),
|
||||
(isBold ? 1 : 0) + (isItalic ? 2 : 0)));
|
||||
typeface = GlobalRef (LocalRef<jobject>(env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.create,
|
||||
javaString (getName()).get(),
|
||||
(isBold ? 1 : 0) + (isItalic ? 2 : 0))));
|
||||
}
|
||||
|
||||
initialise (env);
|
||||
@ -164,23 +189,24 @@ public:
|
||||
AndroidTypeface (const void* data, size_t size)
|
||||
: Typeface (String (static_cast<uint64> (reinterpret_cast<uintptr_t> (data))), String())
|
||||
{
|
||||
JNIEnv* const env = getEnv();
|
||||
auto* env = getEnv();
|
||||
auto cacheFile = getCacheFileForData (data, size);
|
||||
|
||||
LocalRef<jbyteArray> bytes (env->NewByteArray ((jsize) size));
|
||||
env->SetByteArrayRegion (bytes, 0, (jsize) size, (const jbyte*) data);
|
||||
|
||||
typeface = GlobalRef (android.activity.callObjectMethod (JuceAppActivity.getTypeFaceFromByteArray, bytes.get()));
|
||||
typeface = GlobalRef (LocalRef<jobject>(env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile,
|
||||
javaString (cacheFile.getFullPathName()).get())));
|
||||
|
||||
initialise (env);
|
||||
}
|
||||
|
||||
void initialise (JNIEnv* const env)
|
||||
{
|
||||
rect = GlobalRef (env->NewObject (AndroidRect, AndroidRect.constructor, 0, 0, 0, 0));
|
||||
rect = GlobalRef (LocalRef<jobject>(env->NewObject (AndroidRect, AndroidRect.constructor, 0, 0, 0, 0)));
|
||||
|
||||
paint = GlobalRef (GraphicsHelpers::createPaint (Graphics::highResamplingQuality));
|
||||
const LocalRef<jobject> ignored (paint.callObjectMethod (AndroidPaint.setTypeface, typeface.get()));
|
||||
|
||||
charArray = GlobalRef (LocalRef<jobject>((jobject) env->NewCharArray (2)));
|
||||
|
||||
paint.callVoidMethod (AndroidPaint.setTextSize, referenceFontSize);
|
||||
|
||||
const float fullAscent = std::abs (paint.callFloatMethod (AndroidPaint.ascent));
|
||||
@ -199,8 +225,8 @@ public:
|
||||
float getStringWidth (const String& text) override
|
||||
{
|
||||
JNIEnv* env = getEnv();
|
||||
const int numChars = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer());
|
||||
jfloatArray widths = env->NewFloatArray (numChars);
|
||||
auto numChars = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer());
|
||||
jfloatArray widths = env->NewFloatArray ((int) numChars);
|
||||
|
||||
const int numDone = paint.callIntMethod (AndroidPaint.getTextWidths, javaString (text).get(), widths);
|
||||
|
||||
@ -209,6 +235,7 @@ public:
|
||||
env->DeleteLocalRef (widths);
|
||||
|
||||
float x = 0;
|
||||
|
||||
for (int i = 0; i < numDone; ++i)
|
||||
x += localWidths[i];
|
||||
|
||||
@ -218,8 +245,8 @@ public:
|
||||
void getGlyphPositions (const String& text, Array<int>& glyphs, Array<float>& xOffsets) override
|
||||
{
|
||||
JNIEnv* env = getEnv();
|
||||
const int numChars = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer());
|
||||
jfloatArray widths = env->NewFloatArray (numChars);
|
||||
auto numChars = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer());
|
||||
jfloatArray widths = env->NewFloatArray ((int) numChars);
|
||||
|
||||
const int numDone = paint.callIntMethod (AndroidPaint.getTextWidths, javaString (text).get(), widths);
|
||||
|
||||
@ -230,8 +257,8 @@ public:
|
||||
auto s = text.getCharPointer();
|
||||
|
||||
xOffsets.add (0);
|
||||
|
||||
float x = 0;
|
||||
|
||||
for (int i = 0; i < numDone; ++i)
|
||||
{
|
||||
const float local = localWidths[i];
|
||||
@ -290,20 +317,56 @@ public:
|
||||
#else
|
||||
jchar ch1 = glyphNumber, ch2 = 0;
|
||||
#endif
|
||||
Rectangle<int> bounds;
|
||||
auto* env = getEnv();
|
||||
|
||||
JNIEnv* env = getEnv();
|
||||
{
|
||||
LocalRef<jobject> matrix (GraphicsHelpers::createMatrix (env, AffineTransform::scale (referenceFontToUnits).followedBy (t)));
|
||||
|
||||
jobject matrix = GraphicsHelpers::createMatrix (env, AffineTransform::scale (referenceFontToUnits).followedBy (t));
|
||||
jintArray maskData = (jintArray) android.activity.callObjectMethod (JuceAppActivity.renderGlyph, ch1, ch2, paint.get(), matrix, rect.get());
|
||||
jboolean isCopy;
|
||||
auto* buffer = env->GetCharArrayElements ((jcharArray) charArray.get(), &isCopy);
|
||||
|
||||
env->DeleteLocalRef (matrix);
|
||||
buffer[0] = ch1; buffer[1] = ch2;
|
||||
env->ReleaseCharArrayElements ((jcharArray) charArray.get(), buffer, 0);
|
||||
|
||||
const int left = env->GetIntField (rect.get(), AndroidRect.left);
|
||||
const int top = env->GetIntField (rect.get(), AndroidRect.top);
|
||||
const int right = env->GetIntField (rect.get(), AndroidRect.right);
|
||||
const int bottom = env->GetIntField (rect.get(), AndroidRect.bottom);
|
||||
LocalRef<jobject> path (env->NewObject (AndroidPath, AndroidPath.constructor));
|
||||
LocalRef<jobject> boundsF (env->NewObject (AndroidRectF, AndroidRectF.constructor));
|
||||
|
||||
const Rectangle<int> bounds (left, top, right - left, bottom - top);
|
||||
|
||||
env->CallVoidMethod (paint.get(), AndroidPaint.getCharsPath, charArray.get(), 0, (ch2 != 0 ? 2 : 1), 0.0f, 0.0f, path.get());
|
||||
|
||||
env->CallVoidMethod (path.get(), AndroidPath.computeBounds, boundsF.get(), 1);
|
||||
|
||||
env->CallBooleanMethod (matrix.get(), AndroidMatrix.mapRect, boundsF.get());
|
||||
|
||||
env->CallVoidMethod (boundsF.get(), AndroidRectF.roundOut, rect.get());
|
||||
|
||||
bounds = Rectangle<int>::leftTopRightBottom (env->GetIntField (rect.get(), AndroidRect.left) - 1,
|
||||
env->GetIntField (rect.get(), AndroidRect.top),
|
||||
env->GetIntField (rect.get(), AndroidRect.right) + 1,
|
||||
env->GetIntField (rect.get(), AndroidRect.bottom));
|
||||
|
||||
auto w = bounds.getWidth();
|
||||
auto h = jmax (1, bounds.getHeight());
|
||||
|
||||
LocalRef<jobject> bitmapConfig (env->CallStaticObjectMethod (AndroidBitmapConfig, AndroidBitmapConfig.valueOf, javaString ("ARGB_8888").get()));
|
||||
LocalRef<jobject> bitmap (env->CallStaticObjectMethod (AndroidBitmap, AndroidBitmap.createBitmap, w, h, bitmapConfig.get()));
|
||||
LocalRef<jobject> canvas (env->NewObject (AndroidCanvas, AndroidCanvas.create, bitmap.get()));
|
||||
|
||||
env->CallBooleanMethod (matrix.get(), AndroidMatrix.postTranslate, bounds.getX() * -1.0f, bounds.getY() * -1.0f);
|
||||
env->CallVoidMethod (canvas.get(), AndroidCanvas.setMatrix, matrix.get());
|
||||
env->CallVoidMethod (canvas.get(), AndroidCanvas.drawPath, path.get(), paint.get());
|
||||
|
||||
int requiredRenderArraySize = w * h;
|
||||
if (requiredRenderArraySize > lastCachedRenderArraySize)
|
||||
{
|
||||
cachedRenderArray = GlobalRef (LocalRef<jobject> ((jobject) env->NewIntArray (requiredRenderArraySize)));
|
||||
lastCachedRenderArraySize = requiredRenderArraySize;
|
||||
}
|
||||
|
||||
env->CallVoidMethod (bitmap.get(), AndroidBitmap.getPixels, cachedRenderArray.get(), 0, w, 0, 0, w, h);
|
||||
env->CallVoidMethod (bitmap.get(), AndroidBitmap.recycle);
|
||||
}
|
||||
|
||||
EdgeTable* et = nullptr;
|
||||
|
||||
@ -311,10 +374,10 @@ public:
|
||||
{
|
||||
et = new EdgeTable (bounds);
|
||||
|
||||
jint* const maskDataElements = env->GetIntArrayElements (maskData, 0);
|
||||
jint* const maskDataElements = env->GetIntArrayElements ((jintArray) cachedRenderArray.get(), 0);
|
||||
const jint* mask = maskDataElements;
|
||||
|
||||
for (int y = top; y < bottom; ++y)
|
||||
for (int y = bounds.getY(); y < bounds.getBottom(); ++y)
|
||||
{
|
||||
#if JUCE_LITTLE_ENDIAN
|
||||
const uint8* const lineBytes = ((const uint8*) mask) + 3;
|
||||
@ -322,19 +385,19 @@ public:
|
||||
const uint8* const lineBytes = (const uint8*) mask;
|
||||
#endif
|
||||
|
||||
et->clipLineToMask (left, y, lineBytes, 4, bounds.getWidth());
|
||||
et->clipLineToMask (bounds.getX(), y, lineBytes, 4, bounds.getWidth());
|
||||
mask += bounds.getWidth();
|
||||
}
|
||||
|
||||
env->ReleaseIntArrayElements (maskData, maskDataElements, 0);
|
||||
env->ReleaseIntArrayElements ((jintArray) cachedRenderArray.get(), maskDataElements, 0);
|
||||
}
|
||||
|
||||
env->DeleteLocalRef (maskData);
|
||||
return et;
|
||||
}
|
||||
|
||||
GlobalRef typeface, paint, rect;
|
||||
GlobalRef typeface, paint, rect, charArray, cachedRenderArray;
|
||||
float ascent, descent, heightToPointsFactor;
|
||||
int lastCachedRenderArraySize = -1;
|
||||
|
||||
private:
|
||||
static File findFontFile (const String& family,
|
||||
@ -372,6 +435,92 @@ private:
|
||||
return File (path + ".ttf");
|
||||
}
|
||||
|
||||
static LocalRef<jobject> getTypefaceFromAsset (const String& typefaceName)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
LocalRef<jobject> assetManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getAssets));
|
||||
|
||||
if (assetManager == nullptr)
|
||||
return LocalRef<jobject>();
|
||||
|
||||
auto assetTypeface = env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromAsset, assetManager.get(),
|
||||
javaString ("fonts/" + typefaceName).get());
|
||||
|
||||
// this may throw
|
||||
if (env->ExceptionCheck() != 0)
|
||||
{
|
||||
env->ExceptionClear();
|
||||
return LocalRef<jobject>();
|
||||
}
|
||||
|
||||
return LocalRef<jobject> (assetTypeface);
|
||||
}
|
||||
|
||||
static File getCacheDirectory()
|
||||
{
|
||||
static File result = [] ()
|
||||
{
|
||||
auto appContext = getAppContext();
|
||||
|
||||
if (appContext != nullptr)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
LocalRef<jobject> cacheFile (env->CallObjectMethod (appContext.get(), AndroidContext.getCacheDir));
|
||||
LocalRef<jstring> jPath ((jstring) env->CallObjectMethod (cacheFile.get(), JavaFile.getAbsolutePath));
|
||||
|
||||
return File (juceString (env, jPath.get()));
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return File();
|
||||
} ();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static HashMap<String, File>& getInMemoryFontCache()
|
||||
{
|
||||
static HashMap<String, File> cache;
|
||||
return cache;
|
||||
}
|
||||
|
||||
static File getCacheFileForData (const void* data, size_t size)
|
||||
{
|
||||
static CriticalSection cs;
|
||||
JNIEnv* const env = getEnv();
|
||||
|
||||
String key;
|
||||
{
|
||||
LocalRef<jobject> digest (env->CallStaticObjectMethod (JavaMessageDigest, JavaMessageDigest.getInstance, javaString("MD5").get()));
|
||||
LocalRef<jbyteArray> bytes(env->NewByteArray(size));
|
||||
|
||||
jboolean ignore;
|
||||
auto* jbytes = env->GetByteArrayElements(bytes.get(), &ignore);
|
||||
memcpy(jbytes, data, size);
|
||||
env->ReleaseByteArrayElements(bytes.get(), jbytes, 0);
|
||||
|
||||
env->CallVoidMethod(digest.get(), JavaMessageDigest.update, bytes.get());
|
||||
LocalRef<jbyteArray> result((jbyteArray) env->CallObjectMethod(digest.get(), JavaMessageDigest.digest));
|
||||
|
||||
auto* md5Bytes = env->GetByteArrayElements(result.get(), &ignore);
|
||||
key = String::toHexString(md5Bytes, env->GetArrayLength(result.get()), 0);
|
||||
env->ReleaseByteArrayElements(result.get(), md5Bytes, 0);
|
||||
}
|
||||
|
||||
ScopedLock lock (cs);
|
||||
auto& mapEntry = getInMemoryFontCache().getReference (key);
|
||||
|
||||
if (mapEntry == File())
|
||||
{
|
||||
mapEntry = getCacheDirectory().getChildFile ("bindata_" + key);
|
||||
mapEntry.replaceWithData (data, size);
|
||||
}
|
||||
|
||||
return mapEntry;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidTypeface)
|
||||
};
|
||||
|
||||
|
@ -29,7 +29,7 @@ namespace juce
|
||||
|
||||
namespace GraphicsHelpers
|
||||
{
|
||||
jobject createPaint (Graphics::ResamplingQuality quality)
|
||||
LocalRef<jobject> createPaint (Graphics::ResamplingQuality quality)
|
||||
{
|
||||
jint constructorFlags = 1 /*ANTI_ALIAS_FLAG*/
|
||||
| 4 /*DITHER_FLAG*/
|
||||
@ -38,12 +38,12 @@ namespace GraphicsHelpers
|
||||
if (quality > Graphics::lowResamplingQuality)
|
||||
constructorFlags |= 2; /*FILTER_BITMAP_FLAG*/
|
||||
|
||||
return getEnv()->NewObject (AndroidPaint, AndroidPaint.constructor, constructorFlags);
|
||||
return LocalRef<jobject>(getEnv()->NewObject (AndroidPaint, AndroidPaint.constructor, constructorFlags));
|
||||
}
|
||||
|
||||
const jobject createMatrix (JNIEnv* env, const AffineTransform& t)
|
||||
const LocalRef<jobject> createMatrix (JNIEnv* env, const AffineTransform& t)
|
||||
{
|
||||
jobject m = env->NewObject (AndroidMatrix, AndroidMatrix.constructor);
|
||||
auto m = LocalRef<jobject>(env->NewObject (AndroidMatrix, AndroidMatrix.constructor));
|
||||
|
||||
jfloat values[9] = { t.mat00, t.mat01, t.mat02,
|
||||
t.mat10, t.mat11, t.mat12,
|
||||
|
@ -27,13 +27,13 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct FTLibWrapper : public ReferenceCountedObject
|
||||
struct FTLibWrapper : public ReferenceCountedObject
|
||||
{
|
||||
FTLibWrapper() : library (0)
|
||||
FTLibWrapper()
|
||||
{
|
||||
if (FT_Init_FreeType (&library) != 0)
|
||||
{
|
||||
library = 0;
|
||||
library = {};
|
||||
DBG ("Failed to initialize FreeType");
|
||||
}
|
||||
}
|
||||
@ -44,7 +44,7 @@ struct FTLibWrapper : public ReferenceCountedObject
|
||||
FT_Done_FreeType (library);
|
||||
}
|
||||
|
||||
FT_Library library;
|
||||
FT_Library library = {};
|
||||
|
||||
using Ptr = ReferenceCountedObjectPtr<FTLibWrapper>;
|
||||
|
||||
@ -55,18 +55,18 @@ struct FTLibWrapper : public ReferenceCountedObject
|
||||
struct FTFaceWrapper : public ReferenceCountedObject
|
||||
{
|
||||
FTFaceWrapper (const FTLibWrapper::Ptr& ftLib, const File& file, int faceIndex)
|
||||
: face (0), library (ftLib)
|
||||
: library (ftLib)
|
||||
{
|
||||
if (FT_New_Face (ftLib->library, file.getFullPathName().toUTF8(), faceIndex, &face) != 0)
|
||||
face = 0;
|
||||
face = {};
|
||||
}
|
||||
|
||||
FTFaceWrapper (const FTLibWrapper::Ptr& ftLib, const void* data, size_t dataSize, int faceIndex)
|
||||
: face (0), library (ftLib), savedFaceData (data, dataSize)
|
||||
: library (ftLib), savedFaceData (data, dataSize)
|
||||
{
|
||||
if (FT_New_Memory_Face (ftLib->library, (const FT_Byte*) savedFaceData.getData(),
|
||||
(FT_Long) savedFaceData.getSize(), faceIndex, &face) != 0)
|
||||
face = 0;
|
||||
face = {};
|
||||
}
|
||||
|
||||
~FTFaceWrapper()
|
||||
@ -75,7 +75,7 @@ struct FTFaceWrapper : public ReferenceCountedObject
|
||||
FT_Done_Face (face);
|
||||
}
|
||||
|
||||
FT_Face face;
|
||||
FT_Face face = {};
|
||||
FTLibWrapper::Ptr library;
|
||||
MemoryBlock savedFaceData;
|
||||
|
||||
@ -101,7 +101,7 @@ public:
|
||||
//==============================================================================
|
||||
struct KnownTypeface
|
||||
{
|
||||
KnownTypeface (const File& f, const int index, const FTFaceWrapper& face)
|
||||
KnownTypeface (const File& f, int index, const FTFaceWrapper& face)
|
||||
: file (f),
|
||||
family (face.face->family_name),
|
||||
style (face.face->style_name),
|
||||
@ -141,10 +141,10 @@ public:
|
||||
|
||||
FTFaceWrapper::Ptr createFace (const String& fontName, const String& fontStyle)
|
||||
{
|
||||
const KnownTypeface* ftFace = matchTypeface (fontName, fontStyle);
|
||||
auto ftFace = matchTypeface (fontName, fontStyle);
|
||||
|
||||
if (ftFace == nullptr) ftFace = matchTypeface (fontName, "Regular");
|
||||
if (ftFace == nullptr) ftFace = matchTypeface (fontName, String());
|
||||
if (ftFace == nullptr) ftFace = matchTypeface (fontName, {});
|
||||
|
||||
if (ftFace != nullptr)
|
||||
return createFace (ftFace->file, ftFace->faceIndex);
|
||||
@ -157,8 +157,8 @@ public:
|
||||
{
|
||||
StringArray s;
|
||||
|
||||
for (int i = 0; i < faces.size(); ++i)
|
||||
s.addIfNotAlreadyThere (faces.getUnchecked(i)->family);
|
||||
for (auto* face : faces)
|
||||
s.addIfNotAlreadyThere (face->family);
|
||||
|
||||
return s;
|
||||
}
|
||||
@ -167,28 +167,27 @@ public:
|
||||
{
|
||||
int i = styles.indexOf ("Regular", true);
|
||||
|
||||
if (i < 0)
|
||||
for (i = 0; i < styles.size(); ++i)
|
||||
if (! (styles[i].containsIgnoreCase ("Bold") || styles[i].containsIgnoreCase ("Italic")))
|
||||
break;
|
||||
if (i >= 0)
|
||||
return i;
|
||||
|
||||
return i;
|
||||
for (i = 0; i < styles.size(); ++i)
|
||||
if (! (styles[i].containsIgnoreCase ("Bold") || styles[i].containsIgnoreCase ("Italic")))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
StringArray findAllTypefaceStyles (const String& family) const
|
||||
{
|
||||
StringArray s;
|
||||
|
||||
for (int i = 0; i < faces.size(); ++i)
|
||||
{
|
||||
const KnownTypeface* const face = faces.getUnchecked(i);
|
||||
|
||||
for (auto* face : faces)
|
||||
if (face->family == family)
|
||||
s.addIfNotAlreadyThere (face->style);
|
||||
}
|
||||
|
||||
// try to get a regular style to be first in the list
|
||||
const int regular = indexOfRegularStyle (s);
|
||||
auto regular = indexOfRegularStyle (s);
|
||||
|
||||
if (regular > 0)
|
||||
s.strings.swap (0, regular);
|
||||
|
||||
@ -197,10 +196,9 @@ public:
|
||||
|
||||
void scanFontPaths (const StringArray& paths)
|
||||
{
|
||||
for (int i = 0; i < paths.size(); ++i)
|
||||
for (auto& path : paths)
|
||||
{
|
||||
DirectoryIterator iter (File::getCurrentWorkingDirectory()
|
||||
.getChildFile (paths[i]), true);
|
||||
DirectoryIterator iter (File::getCurrentWorkingDirectory().getChildFile (path), true);
|
||||
|
||||
while (iter.next())
|
||||
if (iter.getFile().hasFileExtension ("ttf;pfb;pcf;otf"))
|
||||
@ -210,23 +208,23 @@ public:
|
||||
|
||||
void getMonospacedNames (StringArray& monoSpaced) const
|
||||
{
|
||||
for (int i = 0; i < faces.size(); ++i)
|
||||
if (faces.getUnchecked(i)->isMonospaced)
|
||||
monoSpaced.addIfNotAlreadyThere (faces.getUnchecked(i)->family);
|
||||
for (auto* face : faces)
|
||||
if (face->isMonospaced)
|
||||
monoSpaced.addIfNotAlreadyThere (face->family);
|
||||
}
|
||||
|
||||
void getSerifNames (StringArray& serif) const
|
||||
{
|
||||
for (int i = 0; i < faces.size(); ++i)
|
||||
if (! faces.getUnchecked(i)->isSansSerif)
|
||||
serif.addIfNotAlreadyThere (faces.getUnchecked(i)->family);
|
||||
for (auto* face : faces)
|
||||
if (! (face->isSansSerif || face->isMonospaced))
|
||||
serif.addIfNotAlreadyThere (face->family);
|
||||
}
|
||||
|
||||
void getSansSerifNames (StringArray& sansSerif) const
|
||||
{
|
||||
for (int i = 0; i < faces.size(); ++i)
|
||||
if (faces.getUnchecked(i)->isSansSerif)
|
||||
sansSerif.addIfNotAlreadyThere (faces.getUnchecked(i)->family);
|
||||
for (auto* face : faces)
|
||||
if (face->isSansSerif)
|
||||
sansSerif.addIfNotAlreadyThere (face->family);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (FTTypefaceList)
|
||||
@ -262,14 +260,10 @@ private:
|
||||
|
||||
const KnownTypeface* matchTypeface (const String& familyName, const String& style) const noexcept
|
||||
{
|
||||
for (int i = 0; i < faces.size(); ++i)
|
||||
{
|
||||
const KnownTypeface* const face = faces.getUnchecked(i);
|
||||
|
||||
for (auto* face : faces)
|
||||
if (face->family == familyName
|
||||
&& (face->style.equalsIgnoreCase (style) || style.isEmpty()))
|
||||
return face;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@ -278,8 +272,8 @@ private:
|
||||
{
|
||||
static const char* sansNames[] = { "Sans", "Verdana", "Arial", "Ubuntu" };
|
||||
|
||||
for (int i = 0; i < numElementsInArray (sansNames); ++i)
|
||||
if (family.containsIgnoreCase (sansNames[i]))
|
||||
for (auto* name : sansNames)
|
||||
if (family.containsIgnoreCase (name))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@ -323,13 +317,13 @@ public:
|
||||
{
|
||||
if (faceWrapper != nullptr)
|
||||
{
|
||||
FT_Face face = faceWrapper->face;
|
||||
const unsigned int glyphIndex = FT_Get_Char_Index (face, (FT_ULong) character);
|
||||
auto face = faceWrapper->face;
|
||||
auto glyphIndex = FT_Get_Char_Index (face, (FT_ULong) character);
|
||||
|
||||
if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM | FT_LOAD_NO_HINTING) == 0
|
||||
&& face->glyph->format == ft_glyph_format_outline)
|
||||
{
|
||||
const float scale = 1.0f / (float) (face->ascender - face->descender);
|
||||
auto scale = 1.0f / (float) (face->ascender - face->descender);
|
||||
Path destShape;
|
||||
|
||||
if (getGlyphShape (destShape, face->glyph->outline, scale))
|
||||
@ -350,12 +344,12 @@ public:
|
||||
private:
|
||||
FTFaceWrapper::Ptr faceWrapper;
|
||||
|
||||
bool getGlyphShape (Path& destShape, const FT_Outline& outline, const float scaleX)
|
||||
bool getGlyphShape (Path& destShape, const FT_Outline& outline, float scaleX)
|
||||
{
|
||||
const float scaleY = -scaleX;
|
||||
const short* const contours = outline.contours;
|
||||
const char* const tags = outline.tags;
|
||||
const FT_Vector* const points = outline.points;
|
||||
auto scaleY = -scaleX;
|
||||
auto* contours = outline.contours;
|
||||
auto* tags = outline.tags;
|
||||
auto* points = outline.points;
|
||||
|
||||
for (int c = 0; c < outline.n_contours; ++c)
|
||||
{
|
||||
@ -364,15 +358,15 @@ private:
|
||||
|
||||
for (int p = startPoint; p <= endPoint; ++p)
|
||||
{
|
||||
const float x = scaleX * points[p].x;
|
||||
const float y = scaleY * points[p].y;
|
||||
auto x = scaleX * points[p].x;
|
||||
auto y = scaleY * points[p].y;
|
||||
|
||||
if (p == startPoint)
|
||||
{
|
||||
if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic)
|
||||
{
|
||||
float x2 = scaleX * points [endPoint].x;
|
||||
float y2 = scaleY * points [endPoint].y;
|
||||
auto x2 = scaleX * points [endPoint].x;
|
||||
auto y2 = scaleY * points [endPoint].y;
|
||||
|
||||
if (FT_CURVE_TAG (tags[endPoint]) != FT_Curve_Tag_On)
|
||||
{
|
||||
@ -396,8 +390,8 @@ private:
|
||||
else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic)
|
||||
{
|
||||
const int nextIndex = (p == endPoint) ? startPoint : p + 1;
|
||||
float x2 = scaleX * points [nextIndex].x;
|
||||
float y2 = scaleY * points [nextIndex].y;
|
||||
auto x2 = scaleX * points [nextIndex].x;
|
||||
auto y2 = scaleY * points [nextIndex].y;
|
||||
|
||||
if (FT_CURVE_TAG (tags [nextIndex]) == FT_Curve_Tag_Conic)
|
||||
{
|
||||
@ -421,10 +415,10 @@ private:
|
||||
|| FT_CURVE_TAG (tags[next2]) != FT_Curve_Tag_On)
|
||||
return false;
|
||||
|
||||
const float x2 = scaleX * points [next1].x;
|
||||
const float y2 = scaleY * points [next1].y;
|
||||
const float x3 = scaleX * points [next2].x;
|
||||
const float y3 = scaleY * points [next2].y;
|
||||
auto x2 = scaleX * points [next1].x;
|
||||
auto y2 = scaleY * points [next1].y;
|
||||
auto x3 = scaleX * points [next2].x;
|
||||
auto y3 = scaleY * points [next2].y;
|
||||
|
||||
destShape.cubicTo (x, y, x2, y2, x3, y3);
|
||||
p += 2;
|
||||
@ -439,10 +433,10 @@ private:
|
||||
|
||||
void addKerning (FT_Face face, const uint32 character, const uint32 glyphIndex)
|
||||
{
|
||||
const float height = (float) (face->ascender - face->descender);
|
||||
auto height = (float) (face->ascender - face->descender);
|
||||
|
||||
uint32 rightGlyphIndex;
|
||||
FT_ULong rightCharCode = FT_Get_First_Char (face, &rightGlyphIndex);
|
||||
auto rightCharCode = FT_Get_First_Char (face, &rightGlyphIndex);
|
||||
|
||||
while (rightGlyphIndex != 0)
|
||||
{
|
||||
|
@ -27,16 +27,16 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
static XmlElement* findFontsConfFile()
|
||||
static std::unique_ptr<XmlElement> findFontsConfFile()
|
||||
{
|
||||
static const char* pathsToSearch[] = { "/etc/fonts/fonts.conf",
|
||||
"/usr/share/fonts/fonts.conf" };
|
||||
|
||||
for (auto* path : pathsToSearch)
|
||||
if (auto* xml = XmlDocument::parse (File (path)))
|
||||
if (auto xml = parseXML (File (path)))
|
||||
return xml;
|
||||
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
StringArray FTTypefaceList::getDefaultFontDirectories()
|
||||
@ -48,9 +48,7 @@ StringArray FTTypefaceList::getDefaultFontDirectories()
|
||||
|
||||
if (fontDirs.isEmpty())
|
||||
{
|
||||
std::unique_ptr<XmlElement> fontsInfo (findFontsConfFile());
|
||||
|
||||
if (fontsInfo != nullptr)
|
||||
if (auto fontsInfo = findFontsConfFile())
|
||||
{
|
||||
forEachXmlChildElementWithTagName (*fontsInfo, e, "dir")
|
||||
{
|
||||
|
@ -32,7 +32,7 @@ class CoreGraphicsContext : public LowLevelGraphicsContext
|
||||
{
|
||||
public:
|
||||
CoreGraphicsContext (CGContextRef context, float flipHeight, float targetScale);
|
||||
~CoreGraphicsContext();
|
||||
~CoreGraphicsContext() override;
|
||||
|
||||
//==============================================================================
|
||||
bool isVectorDevice() const override { return false; }
|
||||
@ -80,7 +80,7 @@ private:
|
||||
float targetScale;
|
||||
CGColorSpaceRef rgbColourSpace, greyColourSpace;
|
||||
mutable Rectangle<int> lastClipRect;
|
||||
mutable bool lastClipRectIsValid;
|
||||
mutable bool lastClipRectIsValid = false;
|
||||
|
||||
struct SavedState
|
||||
{
|
||||
@ -92,9 +92,9 @@ private:
|
||||
|
||||
FillType fillType;
|
||||
Font font;
|
||||
CGFontRef fontRef;
|
||||
CGFontRef fontRef = {};
|
||||
CGAffineTransform fontTransform;
|
||||
CGGradientRef gradient;
|
||||
CGGradientRef gradient = {};
|
||||
};
|
||||
|
||||
std::unique_ptr<SavedState> state;
|
||||
|
@ -27,27 +27,37 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class CoreGraphicsImage : public ImagePixelData
|
||||
{
|
||||
public:
|
||||
CoreGraphicsImage (const Image::PixelFormat format, const int w, const int h, const bool clearImage)
|
||||
: ImagePixelData (format, w, h), cachedImageRef (0)
|
||||
CoreGraphicsImage (const Image::PixelFormat format, int w, int h, bool clearImage)
|
||||
: ImagePixelData (format, w, h)
|
||||
{
|
||||
pixelStride = format == Image::RGB ? 3 : ((format == Image::ARGB) ? 4 : 1);
|
||||
lineStride = (pixelStride * jmax (1, width) + 3) & ~3;
|
||||
|
||||
imageData.allocate ((size_t) lineStride * (size_t) jmax (1, height), clearImage);
|
||||
auto numComponents = (size_t) lineStride * (size_t) jmax (1, height);
|
||||
|
||||
# if JUCE_MAC && defined (__MAC_10_14)
|
||||
// This version of the SDK intermittently requires a bit of extra space
|
||||
// at the end of the image data. This feels like something has gone
|
||||
// wrong in Apple's code.
|
||||
numComponents += (size_t) lineStride;
|
||||
#endif
|
||||
|
||||
imageDataHolder->data.allocate (numComponents, clearImage);
|
||||
|
||||
CGColorSpaceRef colourSpace = (format == Image::SingleChannel) ? CGColorSpaceCreateDeviceGray()
|
||||
: CGColorSpaceCreateDeviceRGB();
|
||||
|
||||
context = CGBitmapContextCreate (imageData, (size_t) width, (size_t) height, 8, (size_t) lineStride,
|
||||
context = CGBitmapContextCreate (imageDataHolder->data, (size_t) width, (size_t) height, 8, (size_t) lineStride,
|
||||
colourSpace, getCGImageFlags (format));
|
||||
|
||||
CGColorSpaceRelease (colourSpace);
|
||||
}
|
||||
|
||||
~CoreGraphicsImage()
|
||||
~CoreGraphicsImage() override
|
||||
{
|
||||
freeCachedImageRef();
|
||||
CGContextRelease (context);
|
||||
@ -62,7 +72,7 @@ public:
|
||||
|
||||
void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
|
||||
{
|
||||
bitmap.data = imageData + x * pixelStride + y * lineStride;
|
||||
bitmap.data = imageDataHolder->data + x * pixelStride + y * lineStride;
|
||||
bitmap.pixelFormat = pixelFormat;
|
||||
bitmap.lineStride = lineStride;
|
||||
bitmap.pixelStride = pixelStride;
|
||||
@ -76,9 +86,9 @@ public:
|
||||
|
||||
ImagePixelData::Ptr clone() override
|
||||
{
|
||||
CoreGraphicsImage* im = new CoreGraphicsImage (pixelFormat, width, height, false);
|
||||
memcpy (im->imageData, imageData, (size_t) (lineStride * height));
|
||||
return im;
|
||||
auto im = new CoreGraphicsImage (pixelFormat, width, height, false);
|
||||
memcpy (im->imageDataHolder->data, imageDataHolder->data, (size_t) (lineStride * height));
|
||||
return *im;
|
||||
}
|
||||
|
||||
ImageType* createType() const override { return new NativeImageType(); }
|
||||
@ -86,9 +96,9 @@ public:
|
||||
//==============================================================================
|
||||
static CGImageRef getCachedImageRef (const Image& juceImage, CGColorSpaceRef colourSpace)
|
||||
{
|
||||
CoreGraphicsImage* const cgim = dynamic_cast<CoreGraphicsImage*> (juceImage.getPixelData());
|
||||
auto cgim = dynamic_cast<CoreGraphicsImage*> (juceImage.getPixelData());
|
||||
|
||||
if (cgim != nullptr && cgim->cachedImageRef != 0)
|
||||
if (cgim != nullptr && cgim->cachedImageRef != nullptr)
|
||||
{
|
||||
CGImageRetain (cgim->cachedImageRef);
|
||||
return cgim->cachedImageRef;
|
||||
@ -97,36 +107,45 @@ public:
|
||||
CGImageRef ref = createImage (juceImage, colourSpace, false);
|
||||
|
||||
if (cgim != nullptr)
|
||||
{
|
||||
CGImageRetain (ref);
|
||||
cgim->cachedImageRef = ref;
|
||||
}
|
||||
cgim->cachedImageRef = CGImageRetain (ref);
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
static CGImageRef createImage (const Image& juceImage, CGColorSpaceRef colourSpace, const bool mustOutliveSource)
|
||||
static CGImageRef createImage (const Image& juceImage, CGColorSpaceRef colourSpace, bool mustOutliveSource)
|
||||
{
|
||||
const Image::BitmapData srcData (juceImage, Image::BitmapData::readOnly);
|
||||
CGDataProviderRef provider;
|
||||
|
||||
if (mustOutliveSource)
|
||||
{
|
||||
CFDataRef data = CFDataCreate (0, (const UInt8*) srcData.data, (CFIndex) ((size_t) srcData.lineStride * (size_t) srcData.height));
|
||||
CFDataRef data = CFDataCreate (nullptr, (const UInt8*) srcData.data, (CFIndex) ((size_t) srcData.lineStride * (size_t) srcData.height));
|
||||
provider = CGDataProviderCreateWithCFData (data);
|
||||
CFRelease (data);
|
||||
}
|
||||
else
|
||||
{
|
||||
provider = CGDataProviderCreateWithData (0, srcData.data, (size_t) srcData.lineStride * (size_t) srcData.height, 0);
|
||||
auto* imageDataContainer = [](const Image& img) -> HeapBlockContainer::Ptr*
|
||||
{
|
||||
if (auto* cgim = dynamic_cast<CoreGraphicsImage*> (img.getPixelData()))
|
||||
return new HeapBlockContainer::Ptr (cgim->imageDataHolder);
|
||||
|
||||
return nullptr;
|
||||
} (juceImage);
|
||||
|
||||
provider = CGDataProviderCreateWithData (imageDataContainer,
|
||||
srcData.data,
|
||||
(size_t) srcData.lineStride * (size_t) srcData.height,
|
||||
[] (void * __nullable info, const void*, size_t) { delete (HeapBlockContainer::Ptr*) info; });
|
||||
}
|
||||
|
||||
CGImageRef imageRef = CGImageCreate ((size_t) srcData.width,
|
||||
(size_t) srcData.height,
|
||||
8, (size_t) srcData.pixelStride * 8,
|
||||
8,
|
||||
(size_t) srcData.pixelStride * 8,
|
||||
(size_t) srcData.lineStride,
|
||||
colourSpace, getCGImageFlags (juceImage.getFormat()), provider,
|
||||
0, true, kCGRenderingIntentDefault);
|
||||
nullptr, true, kCGRenderingIntentDefault);
|
||||
|
||||
CGDataProviderRelease (provider);
|
||||
return imageRef;
|
||||
@ -134,17 +153,24 @@ public:
|
||||
|
||||
//==============================================================================
|
||||
CGContextRef context;
|
||||
CGImageRef cachedImageRef;
|
||||
HeapBlock<uint8> imageData;
|
||||
CGImageRef cachedImageRef = {};
|
||||
|
||||
struct HeapBlockContainer : public ReferenceCountedObject
|
||||
{
|
||||
using Ptr = ReferenceCountedObjectPtr<HeapBlockContainer>;
|
||||
HeapBlock<uint8> data;
|
||||
};
|
||||
|
||||
HeapBlockContainer::Ptr imageDataHolder = new HeapBlockContainer();
|
||||
int pixelStride, lineStride;
|
||||
|
||||
private:
|
||||
void freeCachedImageRef()
|
||||
{
|
||||
if (cachedImageRef != 0)
|
||||
if (cachedImageRef != CGImageRef())
|
||||
{
|
||||
CGImageRelease (cachedImageRef);
|
||||
cachedImageRef = 0;
|
||||
cachedImageRef = {};
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,21 +188,28 @@ private:
|
||||
|
||||
ImagePixelData::Ptr NativeImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const
|
||||
{
|
||||
return new CoreGraphicsImage (format == Image::RGB ? Image::ARGB : format, width, height, clearImage);
|
||||
return *new CoreGraphicsImage (format == Image::RGB ? Image::ARGB : format, width, height, clearImage);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
CoreGraphicsContext::CoreGraphicsContext (CGContextRef c, const float h, const float scale)
|
||||
CoreGraphicsContext::CoreGraphicsContext (CGContextRef c, float h, float scale)
|
||||
: context (c),
|
||||
flipHeight (h),
|
||||
targetScale (scale),
|
||||
lastClipRectIsValid (false),
|
||||
state (new SavedState())
|
||||
{
|
||||
CGContextRetain (context);
|
||||
CGContextSaveGState (context);
|
||||
CGContextSetShouldSmoothFonts (context, true);
|
||||
CGContextSetAllowsFontSmoothing (context, true);
|
||||
|
||||
bool enableFontSmoothing
|
||||
#if JUCE_DISABLE_COREGRAPHICS_FONT_SMOOTHING
|
||||
= false;
|
||||
#else
|
||||
= true;
|
||||
#endif
|
||||
|
||||
CGContextSetShouldSmoothFonts (context, enableFontSmoothing);
|
||||
CGContextSetAllowsFontSmoothing (context, enableFontSmoothing);
|
||||
CGContextSetShouldAntialias (context, true);
|
||||
CGContextSetBlendMode (context, kCGBlendModeNormal);
|
||||
rgbColourSpace = CGColorSpaceCreateDeviceRGB();
|
||||
@ -215,14 +248,15 @@ void CoreGraphicsContext::addTransform (const AffineTransform& transform)
|
||||
|
||||
float CoreGraphicsContext::getPhysicalPixelScaleFactor()
|
||||
{
|
||||
const CGAffineTransform t = CGContextGetCTM (context);
|
||||
auto t = CGContextGetCTM (context);
|
||||
|
||||
return targetScale * (float) (juce_hypot (t.a, t.c) + juce_hypot (t.b, t.d)) / 2.0f;
|
||||
}
|
||||
|
||||
bool CoreGraphicsContext::clipToRectangle (const Rectangle<int>& r)
|
||||
{
|
||||
CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()));
|
||||
CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(),
|
||||
r.getWidth(), r.getHeight()));
|
||||
|
||||
if (lastClipRectIsValid)
|
||||
{
|
||||
@ -295,10 +329,10 @@ void CoreGraphicsContext::clipToImageAlpha (const Image& sourceImage, const Affi
|
||||
CGImageRef image = CoreGraphicsImage::createImage (singleChannelImage, greyColourSpace, true);
|
||||
|
||||
flip();
|
||||
AffineTransform t (AffineTransform::verticalFlip (sourceImage.getHeight()).followedBy (transform));
|
||||
auto t = AffineTransform::verticalFlip (sourceImage.getHeight()).followedBy (transform);
|
||||
applyTransform (t);
|
||||
|
||||
CGRect r = convertToCGRect (sourceImage.getBounds());
|
||||
auto r = convertToCGRect (sourceImage.getBounds());
|
||||
CGContextClipToMask (context, r, image);
|
||||
|
||||
applyTransform (t.inverted());
|
||||
@ -318,7 +352,7 @@ Rectangle<int> CoreGraphicsContext::getClipBounds() const
|
||||
{
|
||||
if (! lastClipRectIsValid)
|
||||
{
|
||||
CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context));
|
||||
auto bounds = CGRectIntegral (CGContextGetClipBoundingBox (context));
|
||||
|
||||
lastClipRectIsValid = true;
|
||||
lastClipRect.setBounds (roundToInt (bounds.origin.x),
|
||||
@ -362,7 +396,7 @@ void CoreGraphicsContext::beginTransparencyLayer (float opacity)
|
||||
{
|
||||
saveState();
|
||||
CGContextSetAlpha (context, opacity);
|
||||
CGContextBeginTransparencyLayer (context, 0);
|
||||
CGContextBeginTransparencyLayer (context, nullptr);
|
||||
}
|
||||
|
||||
void CoreGraphicsContext::endTransparencyLayer()
|
||||
@ -402,7 +436,7 @@ void CoreGraphicsContext::setInterpolationQuality (Graphics::ResamplingQuality q
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void CoreGraphicsContext::fillRect (const Rectangle<int>& r, const bool replaceExistingContents)
|
||||
void CoreGraphicsContext::fillRect (const Rectangle<int>& r, bool replaceExistingContents)
|
||||
{
|
||||
fillCGRect (CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()), replaceExistingContents);
|
||||
}
|
||||
@ -412,7 +446,7 @@ void CoreGraphicsContext::fillRect (const Rectangle<float>& r)
|
||||
fillCGRect (CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()), false);
|
||||
}
|
||||
|
||||
void CoreGraphicsContext::fillCGRect (const CGRect& cgRect, const bool replaceExistingContents)
|
||||
void CoreGraphicsContext::fillCGRect (const CGRect& cgRect, bool replaceExistingContents)
|
||||
{
|
||||
if (replaceExistingContents)
|
||||
{
|
||||
@ -481,19 +515,21 @@ void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTrans
|
||||
drawImage (sourceImage, transform, false);
|
||||
}
|
||||
|
||||
void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTransform& transform, const bool fillEntireClipAsTiles)
|
||||
void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTransform& transform, bool fillEntireClipAsTiles)
|
||||
{
|
||||
const int iw = sourceImage.getWidth();
|
||||
const int ih = sourceImage.getHeight();
|
||||
CGImageRef image = CoreGraphicsImage::getCachedImageRef (sourceImage, sourceImage.getFormat() == Image::PixelFormat::SingleChannel ? greyColourSpace
|
||||
: rgbColourSpace);
|
||||
auto iw = sourceImage.getWidth();
|
||||
auto ih = sourceImage.getHeight();
|
||||
|
||||
auto colourSpace = sourceImage.getFormat() == Image::PixelFormat::SingleChannel ? greyColourSpace
|
||||
: rgbColourSpace;
|
||||
CGImageRef image = CoreGraphicsImage::getCachedImageRef (sourceImage, colourSpace);
|
||||
|
||||
CGContextSaveGState (context);
|
||||
CGContextSetAlpha (context, state->fillType.getOpacity());
|
||||
|
||||
flip();
|
||||
applyTransform (AffineTransform::verticalFlip (ih).followedBy (transform));
|
||||
CGRect imageRect = CGRectMake (0, 0, iw, ih);
|
||||
auto imageRect = CGRectMake (0, 0, iw, ih);
|
||||
|
||||
if (fillEntireClipAsTiles)
|
||||
{
|
||||
@ -502,21 +538,21 @@ void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTrans
|
||||
#else
|
||||
// There's a bug in CGContextDrawTiledImage that makes it incredibly slow
|
||||
// if it's doing a transformation - it's quicker to just draw lots of images manually
|
||||
if (&CGContextDrawTiledImage != 0 && transform.isOnlyTranslation())
|
||||
if (&CGContextDrawTiledImage != nullptr && transform.isOnlyTranslation())
|
||||
{
|
||||
CGContextDrawTiledImage (context, imageRect, image);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback to manually doing a tiled fill
|
||||
CGRect clip = CGRectIntegral (CGContextGetClipBoundingBox (context));
|
||||
auto clip = CGRectIntegral (CGContextGetClipBoundingBox (context));
|
||||
|
||||
int x = 0, y = 0;
|
||||
while (x > clip.origin.x) x -= iw;
|
||||
while (y > clip.origin.y) y -= ih;
|
||||
|
||||
const int right = (int) (clip.origin.x + clip.size.width);
|
||||
const int bottom = (int) (clip.origin.y + clip.size.height);
|
||||
auto right = (int) (clip.origin.x + clip.size.width);
|
||||
auto bottom = (int) (clip.origin.y + clip.size.height);
|
||||
|
||||
while (y < bottom)
|
||||
{
|
||||
@ -540,25 +576,9 @@ void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTrans
|
||||
//==============================================================================
|
||||
void CoreGraphicsContext::drawLine (const Line<float>& line)
|
||||
{
|
||||
if (state->fillType.isColour())
|
||||
{
|
||||
CGContextSetLineCap (context, kCGLineCapSquare);
|
||||
CGContextSetLineWidth (context, 1.0f);
|
||||
CGContextSetRGBStrokeColor (context,
|
||||
state->fillType.colour.getFloatRed(), state->fillType.colour.getFloatGreen(),
|
||||
state->fillType.colour.getFloatBlue(), state->fillType.colour.getFloatAlpha());
|
||||
|
||||
CGPoint cgLine[] = { { (CGFloat) line.getStartX(), flipHeight - (CGFloat) line.getStartY() },
|
||||
{ (CGFloat) line.getEndX(), flipHeight - (CGFloat) line.getEndY() } };
|
||||
|
||||
CGContextStrokeLineSegments (context, cgLine, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Path p;
|
||||
p.addLineSegment (line, 1.0f);
|
||||
fillPath (p, AffineTransform());
|
||||
}
|
||||
Path p;
|
||||
p.addLineSegment (line, 1.0f);
|
||||
fillPath (p, {});
|
||||
}
|
||||
|
||||
void CoreGraphicsContext::fillRectList (const RectangleList<float>& list)
|
||||
@ -594,10 +614,10 @@ void CoreGraphicsContext::setFont (const Font& newFont)
|
||||
{
|
||||
if (state->font != newFont)
|
||||
{
|
||||
state->fontRef = 0;
|
||||
state->fontRef = nullptr;
|
||||
state->font = newFont;
|
||||
|
||||
if (OSXTypeface* osxTypeface = dynamic_cast<OSXTypeface*> (state->font.getTypeface()))
|
||||
if (auto osxTypeface = dynamic_cast<OSXTypeface*> (state->font.getTypeface()))
|
||||
{
|
||||
state->fontRef = osxTypeface->fontRef;
|
||||
CGContextSetFont (context, state->fontRef);
|
||||
@ -617,7 +637,7 @@ const Font& CoreGraphicsContext::getFont()
|
||||
|
||||
void CoreGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& transform)
|
||||
{
|
||||
if (state->fontRef != 0 && state->fillType.isColour())
|
||||
if (state->fontRef != nullptr && state->fillType.isColour())
|
||||
{
|
||||
#if JUCE_CLANG
|
||||
#pragma clang diagnostic push
|
||||
@ -628,7 +648,7 @@ void CoreGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& tra
|
||||
{
|
||||
CGContextSetTextMatrix (context, state->fontTransform); // have to set this each time, as it's not saved as part of the state
|
||||
|
||||
CGGlyph g = (CGGlyph) glyphNumber;
|
||||
auto g = (CGGlyph) glyphNumber;
|
||||
CGContextShowGlyphsAtPoint (context, transform.getTranslationX(),
|
||||
flipHeight - roundToInt (transform.getTranslationY()), &g, 1);
|
||||
}
|
||||
@ -638,11 +658,11 @@ void CoreGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& tra
|
||||
flip();
|
||||
applyTransform (transform);
|
||||
|
||||
CGAffineTransform t = state->fontTransform;
|
||||
auto t = state->fontTransform;
|
||||
t.d = -t.d;
|
||||
CGContextSetTextMatrix (context, t);
|
||||
|
||||
CGGlyph g = (CGGlyph) glyphNumber;
|
||||
auto g = (CGGlyph) glyphNumber;
|
||||
CGContextShowGlyphsAtPoint (context, 0, 0, &g, 1);
|
||||
|
||||
CGContextRestoreGState (context);
|
||||
@ -655,7 +675,7 @@ void CoreGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& tra
|
||||
else
|
||||
{
|
||||
Path p;
|
||||
Font& f = state->font;
|
||||
auto& f = state->font;
|
||||
f.getTypeface()->getOutlineForGlyph (glyphNumber, p);
|
||||
|
||||
fillPath (p, AffineTransform::scale (f.getHeight() * f.getHorizontalScale(), f.getHeight())
|
||||
@ -670,7 +690,7 @@ bool CoreGraphicsContext::drawTextLayout (const AttributedString& text, const Re
|
||||
}
|
||||
|
||||
CoreGraphicsContext::SavedState::SavedState()
|
||||
: font (1.0f), fontRef (0), fontTransform (CGAffineTransformIdentity), gradient (0)
|
||||
: font (1.0f), fontTransform (CGAffineTransformIdentity)
|
||||
{
|
||||
}
|
||||
|
||||
@ -678,13 +698,13 @@ CoreGraphicsContext::SavedState::SavedState (const SavedState& other)
|
||||
: fillType (other.fillType), font (other.font), fontRef (other.fontRef),
|
||||
fontTransform (other.fontTransform), gradient (other.gradient)
|
||||
{
|
||||
if (gradient != 0)
|
||||
if (gradient != nullptr)
|
||||
CGGradientRetain (gradient);
|
||||
}
|
||||
|
||||
CoreGraphicsContext::SavedState::~SavedState()
|
||||
{
|
||||
if (gradient != 0)
|
||||
if (gradient != nullptr)
|
||||
CGGradientRelease (gradient);
|
||||
}
|
||||
|
||||
@ -692,24 +712,24 @@ void CoreGraphicsContext::SavedState::setFill (const FillType& newFill)
|
||||
{
|
||||
fillType = newFill;
|
||||
|
||||
if (gradient != 0)
|
||||
if (gradient != nullptr)
|
||||
{
|
||||
CGGradientRelease (gradient);
|
||||
gradient = 0;
|
||||
gradient = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static CGGradientRef createGradient (const ColourGradient& g, CGColorSpaceRef colourSpace)
|
||||
{
|
||||
const int numColours = g.getNumColours();
|
||||
CGFloat* const data = (CGFloat*) alloca ((size_t) numColours * 5 * sizeof (CGFloat));
|
||||
CGFloat* const locations = data;
|
||||
CGFloat* const components = data + numColours;
|
||||
CGFloat* comps = components;
|
||||
auto numColours = g.getNumColours();
|
||||
auto data = (CGFloat*) alloca ((size_t) numColours * 5 * sizeof (CGFloat));
|
||||
auto locations = data;
|
||||
auto components = data + numColours;
|
||||
auto comps = components;
|
||||
|
||||
for (int i = 0; i < numColours; ++i)
|
||||
{
|
||||
const Colour colour (g.getColour (i));
|
||||
auto colour = g.getColour (i);
|
||||
*comps++ = (CGFloat) colour.getFloatRed();
|
||||
*comps++ = (CGFloat) colour.getFloatGreen();
|
||||
*comps++ = (CGFloat) colour.getFloatBlue();
|
||||
@ -730,14 +750,14 @@ void CoreGraphicsContext::drawGradient()
|
||||
applyTransform (state->fillType.transform);
|
||||
CGContextSetAlpha (context, state->fillType.getOpacity());
|
||||
|
||||
const ColourGradient& g = *state->fillType.gradient;
|
||||
auto& g = *state->fillType.gradient;
|
||||
|
||||
CGPoint p1 (convertToCGPoint (g.point1));
|
||||
CGPoint p2 (convertToCGPoint (g.point2));
|
||||
auto p1 = convertToCGPoint (g.point1);
|
||||
auto p2 = convertToCGPoint (g.point2);
|
||||
|
||||
state->fillType.transform.transformPoints (p1.x, p1.y, p2.x, p2.y);
|
||||
|
||||
if (state->gradient == 0)
|
||||
if (state->gradient == nullptr)
|
||||
state->gradient = createGradient (g, rgbColourSpace);
|
||||
|
||||
if (g.isRadial)
|
||||
@ -820,34 +840,43 @@ void CoreGraphicsContext::applyTransform (const AffineTransform& transform) cons
|
||||
#if USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER
|
||||
Image juce_loadWithCoreImage (InputStream& input)
|
||||
{
|
||||
MemoryBlock data;
|
||||
input.readIntoMemoryBlock (data, -1);
|
||||
struct MemoryBlockHolder : public ReferenceCountedObject
|
||||
{
|
||||
using Ptr = ReferenceCountedObjectPtr<MemoryBlockHolder>;
|
||||
MemoryBlock block;
|
||||
};
|
||||
|
||||
MemoryBlockHolder::Ptr memBlockHolder = new MemoryBlockHolder();
|
||||
input.readIntoMemoryBlock (memBlockHolder->block, -1);
|
||||
|
||||
#if JUCE_IOS
|
||||
JUCE_AUTORELEASEPOOL
|
||||
#endif
|
||||
{
|
||||
#if JUCE_IOS
|
||||
if (UIImage* uiImage = [UIImage imageWithData: [NSData dataWithBytesNoCopy: data.getData()
|
||||
length: data.getSize()
|
||||
if (UIImage* uiImage = [UIImage imageWithData: [NSData dataWithBytesNoCopy: memBlockHolder->block.getData()
|
||||
length: memBlockHolder->block.getSize()
|
||||
freeWhenDone: NO]])
|
||||
{
|
||||
CGImageRef loadedImage = uiImage.CGImage;
|
||||
|
||||
#else
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithData (0, data.getData(), data.getSize(), 0);
|
||||
CGImageSourceRef imageSource = CGImageSourceCreateWithDataProvider (provider, 0);
|
||||
auto provider = CGDataProviderCreateWithData (new MemoryBlockHolder::Ptr (memBlockHolder),
|
||||
memBlockHolder->block.getData(),
|
||||
memBlockHolder->block.getSize(),
|
||||
[] (void * __nullable info, const void*, size_t) { delete (MemoryBlockHolder::Ptr*) info; });
|
||||
auto imageSource = CGImageSourceCreateWithDataProvider (provider, nullptr);
|
||||
CGDataProviderRelease (provider);
|
||||
|
||||
if (imageSource != 0)
|
||||
if (imageSource != nullptr)
|
||||
{
|
||||
CGImageRef loadedImage = CGImageSourceCreateImageAtIndex (imageSource, 0, 0);
|
||||
auto loadedImage = CGImageSourceCreateImageAtIndex (imageSource, 0, nullptr);
|
||||
CFRelease (imageSource);
|
||||
#endif
|
||||
|
||||
if (loadedImage != 0)
|
||||
if (loadedImage != nullptr)
|
||||
{
|
||||
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo (loadedImage);
|
||||
auto alphaInfo = CGImageGetAlphaInfo (loadedImage);
|
||||
const bool hasAlphaChan = (alphaInfo != kCGImageAlphaNone
|
||||
&& alphaInfo != kCGImageAlphaNoneSkipLast
|
||||
&& alphaInfo != kCGImageAlphaNoneSkipFirst);
|
||||
@ -857,7 +886,7 @@ Image juce_loadWithCoreImage (InputStream& input)
|
||||
(int) CGImageGetHeight (loadedImage),
|
||||
hasAlphaChan));
|
||||
|
||||
CoreGraphicsImage* const cgImage = dynamic_cast<CoreGraphicsImage*> (image.getPixelData());
|
||||
auto cgImage = dynamic_cast<CoreGraphicsImage*> (image.getPixelData());
|
||||
jassert (cgImage != nullptr); // if USE_COREGRAPHICS_RENDERING is set, the CoreGraphicsImage class should have been used.
|
||||
|
||||
CGContextDrawImage (cgImage->context, convertToCGRect (image.getBounds()), loadedImage);
|
||||
@ -875,20 +904,20 @@ Image juce_loadWithCoreImage (InputStream& input)
|
||||
}
|
||||
}
|
||||
|
||||
return Image();
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
Image juce_createImageFromCIImage (CIImage*, int, int);
|
||||
Image juce_createImageFromCIImage (CIImage* im, int w, int h)
|
||||
{
|
||||
CoreGraphicsImage* cgImage = new CoreGraphicsImage (Image::ARGB, w, h, false);
|
||||
auto cgImage = new CoreGraphicsImage (Image::ARGB, w, h, false);
|
||||
|
||||
CIContext* cic = [CIContext contextWithCGContext: cgImage->context options: nil];
|
||||
[cic drawImage: im inRect: CGRectMake (0, 0, w, h) fromRect: CGRectMake (0, 0, w, h)];
|
||||
CGContextFlush (cgImage->context);
|
||||
|
||||
return Image (cgImage);
|
||||
return Image (*cgImage);
|
||||
}
|
||||
|
||||
CGImageRef juce_createCoreGraphicsImage (const Image& juceImage, CGColorSpaceRef colourSpace,
|
||||
@ -899,11 +928,11 @@ CGImageRef juce_createCoreGraphicsImage (const Image& juceImage, CGColorSpaceRef
|
||||
|
||||
CGContextRef juce_getImageContext (const Image& image)
|
||||
{
|
||||
if (CoreGraphicsImage* const cgi = dynamic_cast<CoreGraphicsImage*> (image.getPixelData()))
|
||||
if (auto cgi = dynamic_cast<CoreGraphicsImage*> (image.getPixelData()))
|
||||
return cgi->context;
|
||||
|
||||
jassertfalse;
|
||||
return 0;
|
||||
return {};
|
||||
}
|
||||
|
||||
#if JUCE_IOS
|
||||
@ -929,8 +958,8 @@ CGContextRef juce_getImageContext (const Image& image)
|
||||
auto requiredSize = NSMakeSize (image.getWidth() / scaleFactor, image.getHeight() / scaleFactor);
|
||||
|
||||
[im setSize: requiredSize];
|
||||
CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGImageRef imageRef = juce_createCoreGraphicsImage (image, colourSpace, true);
|
||||
auto colourSpace = CGColorSpaceCreateDeviceRGB();
|
||||
auto imageRef = juce_createCoreGraphicsImage (image, colourSpace, true);
|
||||
CGColorSpaceRelease (colourSpace);
|
||||
|
||||
NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc] initWithCGImage: imageRef];
|
||||
|
@ -239,7 +239,7 @@ namespace CoreTextTypeLayout
|
||||
{
|
||||
extraKerning *= attr.font.getHeight();
|
||||
|
||||
auto numberRef = CFNumberCreate (0, kCFNumberFloatType, &extraKerning);
|
||||
auto numberRef = CFNumberCreate (nullptr, kCFNumberFloatType, &extraKerning);
|
||||
CFAttributedStringSetAttribute (attribString, range, kCTKernAttributeName, numberRef);
|
||||
CFRelease (numberRef);
|
||||
}
|
||||
@ -503,7 +503,8 @@ public:
|
||||
if (fontRef != nullptr)
|
||||
{
|
||||
#if JUCE_MAC && defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8
|
||||
canBeUsedForLayout = CTFontManagerRegisterGraphicsFont (fontRef, nullptr);
|
||||
if (SystemStats::getOperatingSystemType() >= SystemStats::OperatingSystemType::MacOSX_10_11)
|
||||
canBeUsedForLayout = CTFontManagerRegisterGraphicsFont (fontRef, nullptr);
|
||||
#endif
|
||||
|
||||
ctFontRef = CTFontCreateWithGraphicsFont (fontRef, referenceFontSize, nullptr, nullptr);
|
||||
@ -540,7 +541,7 @@ public:
|
||||
fontHeightToPointsFactor = referenceFontSize / ctTotalHeight;
|
||||
|
||||
const short zero = 0;
|
||||
auto numberRef = CFNumberCreate (0, kCFNumberShortType, &zero);
|
||||
auto numberRef = CFNumberCreate (nullptr, kCFNumberShortType, &zero);
|
||||
|
||||
CFStringRef keys[] = { kCTFontAttributeName, kCTLigatureAttributeName };
|
||||
CFTypeRef values[] = { ctFontRef, numberRef };
|
||||
@ -550,7 +551,7 @@ public:
|
||||
CFRelease (numberRef);
|
||||
}
|
||||
|
||||
~OSXTypeface()
|
||||
~OSXTypeface() override
|
||||
{
|
||||
if (attributedStringAtts != nullptr)
|
||||
CFRelease (attributedStringAtts);
|
||||
@ -788,8 +789,8 @@ StringArray Font::findAllTypefaceStyles (const String& family)
|
||||
|
||||
|
||||
//==============================================================================
|
||||
Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { return new OSXTypeface (font); }
|
||||
Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t size) { return new OSXTypeface (data, size); }
|
||||
Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { return *new OSXTypeface (font); }
|
||||
Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t size) { return *new OSXTypeface (data, size); }
|
||||
|
||||
void Typeface::scanFolderForFonts (const File&)
|
||||
{
|
||||
|
@ -761,7 +761,7 @@ void Direct2DLowLevelGraphicsContext::drawImage (const Image& image, const Affin
|
||||
pimpl->renderingTarget->SetTransform (D2D1::IdentityMatrix());
|
||||
}
|
||||
|
||||
void Direct2DLowLevelGraphicsContext::drawLine (const Line <float>& line)
|
||||
void Direct2DLowLevelGraphicsContext::drawLine (const Line<float>& line)
|
||||
{
|
||||
// xxx doesn't seem to be correctly aligned, may need nudging by 0.5 to match the software renderer's behaviour
|
||||
pimpl->renderingTarget->SetTransform (transformToMatrix (currentState->transform));
|
||||
|
@ -96,8 +96,6 @@ private:
|
||||
Rectangle<int> bounds;
|
||||
|
||||
struct Pimpl;
|
||||
friend struct Pimpl;
|
||||
friend struct ContainerDeletePolicy<Pimpl>;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Direct2DLowLevelGraphicsContext)
|
||||
|
@ -36,9 +36,7 @@ namespace DirectWriteTypeLayout
|
||||
CustomDirectWriteTextRenderer (IDWriteFontCollection& fonts, const AttributedString& as)
|
||||
: ComBaseClassHelper<IDWriteTextRenderer> (0),
|
||||
attributedString (as),
|
||||
fontCollection (fonts),
|
||||
currentLine (-1),
|
||||
lastOriginY (-10000.0f)
|
||||
fontCollection (fonts)
|
||||
{
|
||||
}
|
||||
|
||||
@ -89,7 +87,7 @@ namespace DirectWriteTypeLayout
|
||||
DWRITE_GLYPH_RUN const* glyphRun, DWRITE_GLYPH_RUN_DESCRIPTION const* runDescription,
|
||||
IUnknown* clientDrawingEffect) override
|
||||
{
|
||||
TextLayout* const layout = static_cast<TextLayout*> (clientDrawingContext);
|
||||
auto layout = static_cast<TextLayout*> (clientDrawingContext);
|
||||
|
||||
if (! (baselineOriginY >= -1.0e10f && baselineOriginY <= 1.0e10f))
|
||||
baselineOriginY = 0; // DirectWrite sometimes sends NaNs in this parameter
|
||||
@ -102,14 +100,14 @@ namespace DirectWriteTypeLayout
|
||||
if (currentLine >= layout->getNumLines())
|
||||
{
|
||||
jassert (currentLine == layout->getNumLines());
|
||||
TextLayout::Line* const line = new TextLayout::Line();
|
||||
auto line = new TextLayout::Line();
|
||||
layout->addLine (line);
|
||||
|
||||
line->lineOrigin = Point<float> (baselineOriginX, baselineOriginY);
|
||||
}
|
||||
}
|
||||
|
||||
TextLayout::Line& glyphLine = layout->getLine (currentLine);
|
||||
auto& glyphLine = layout->getLine (currentLine);
|
||||
|
||||
DWRITE_FONT_METRICS dwFontMetrics;
|
||||
glyphRun->fontFace->GetMetrics (&dwFontMetrics);
|
||||
@ -117,27 +115,27 @@ namespace DirectWriteTypeLayout
|
||||
glyphLine.ascent = jmax (glyphLine.ascent, scaledFontSize (dwFontMetrics.ascent, dwFontMetrics, *glyphRun));
|
||||
glyphLine.descent = jmax (glyphLine.descent, scaledFontSize (dwFontMetrics.descent, dwFontMetrics, *glyphRun));
|
||||
|
||||
TextLayout::Run* const glyphRunLayout = new TextLayout::Run (Range<int> (runDescription->textPosition,
|
||||
runDescription->textPosition + runDescription->stringLength),
|
||||
glyphRun->glyphCount);
|
||||
auto glyphRunLayout = new TextLayout::Run (Range<int> (runDescription->textPosition,
|
||||
runDescription->textPosition + runDescription->stringLength),
|
||||
glyphRun->glyphCount);
|
||||
glyphLine.runs.add (glyphRunLayout);
|
||||
|
||||
glyphRun->fontFace->GetMetrics (&dwFontMetrics);
|
||||
const float totalHeight = std::abs ((float) dwFontMetrics.ascent) + std::abs ((float) dwFontMetrics.descent);
|
||||
const float fontHeightToEmSizeFactor = (float) dwFontMetrics.designUnitsPerEm / totalHeight;
|
||||
auto totalHeight = std::abs ((float) dwFontMetrics.ascent) + std::abs ((float) dwFontMetrics.descent);
|
||||
auto fontHeightToEmSizeFactor = (float) dwFontMetrics.designUnitsPerEm / totalHeight;
|
||||
|
||||
glyphRunLayout->font = getFontForRun (*glyphRun, glyphRun->fontEmSize / fontHeightToEmSizeFactor);
|
||||
glyphRunLayout->colour = getColourOf (static_cast<ID2D1SolidColorBrush*> (clientDrawingEffect));
|
||||
|
||||
const Point<float> lineOrigin (layout->getLine (currentLine).lineOrigin);
|
||||
float x = baselineOriginX - lineOrigin.x;
|
||||
auto lineOrigin = layout->getLine (currentLine).lineOrigin;
|
||||
auto x = baselineOriginX - lineOrigin.x;
|
||||
|
||||
const float extraKerning = glyphRunLayout->font.getExtraKerningFactor()
|
||||
* glyphRunLayout->font.getHeight();
|
||||
auto extraKerning = glyphRunLayout->font.getExtraKerningFactor()
|
||||
* glyphRunLayout->font.getHeight();
|
||||
|
||||
for (UINT32 i = 0; i < glyphRun->glyphCount; ++i)
|
||||
{
|
||||
const float advance = glyphRun->glyphAdvances[i];
|
||||
auto advance = glyphRun->glyphAdvances[i];
|
||||
|
||||
if ((glyphRun->bidiLevel & 1) != 0)
|
||||
x -= advance + extraKerning; // RTL text
|
||||
@ -156,8 +154,8 @@ namespace DirectWriteTypeLayout
|
||||
private:
|
||||
const AttributedString& attributedString;
|
||||
IDWriteFontCollection& fontCollection;
|
||||
int currentLine;
|
||||
float lastOriginY;
|
||||
int currentLine = -1;
|
||||
float lastOriginY = -10000.0f;
|
||||
|
||||
static float scaledFontSize (int n, const DWRITE_FONT_METRICS& metrics, const DWRITE_GLYPH_RUN& glyphRun) noexcept
|
||||
{
|
||||
@ -177,15 +175,15 @@ namespace DirectWriteTypeLayout
|
||||
{
|
||||
for (int i = 0; i < attributedString.getNumAttributes(); ++i)
|
||||
{
|
||||
const Font& font = attributedString.getAttribute(i).font;
|
||||
auto& font = attributedString.getAttribute(i).font;
|
||||
|
||||
if (WindowsDirectWriteTypeface* wt = dynamic_cast<WindowsDirectWriteTypeface*> (font.getTypeface()))
|
||||
if (auto* wt = dynamic_cast<WindowsDirectWriteTypeface*> (font.getTypeface()))
|
||||
if (wt->getIDWriteFontFace() == glyphRun.fontFace)
|
||||
return font.withHeight (fontHeight);
|
||||
}
|
||||
|
||||
ComSmartPtr<IDWriteFont> dwFont;
|
||||
HRESULT hr = fontCollection.GetFontFromFontFace (glyphRun.fontFace, dwFont.resetAndGetPointerAddress());
|
||||
auto hr = fontCollection.GetFontFromFontFace (glyphRun.fontFace, dwFont.resetAndGetPointerAddress());
|
||||
jassert (dwFont != nullptr);
|
||||
|
||||
ComSmartPtr<IDWriteFontFamily> dwFontFamily;
|
||||
@ -261,7 +259,7 @@ namespace DirectWriteTypeLayout
|
||||
range.length = jmin (attr.range.getLength(), textLen - attr.range.getStart());
|
||||
|
||||
{
|
||||
const String familyName (FontStyleHelpers::getConcreteFamilyName (attr.font));
|
||||
auto familyName = FontStyleHelpers::getConcreteFamilyName (attr.font);
|
||||
|
||||
BOOL fontFound = false;
|
||||
uint32 fontIndex;
|
||||
@ -271,7 +269,7 @@ namespace DirectWriteTypeLayout
|
||||
fontIndex = 0;
|
||||
|
||||
ComSmartPtr<IDWriteFontFamily> fontFamily;
|
||||
HRESULT hr = fontCollection.GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress());
|
||||
auto hr = fontCollection.GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress());
|
||||
|
||||
ComSmartPtr<IDWriteFont> dwFont;
|
||||
uint32 fontFacesCount = 0;
|
||||
@ -290,12 +288,12 @@ namespace DirectWriteTypeLayout
|
||||
textLayout.SetFontStretch (dwFont->GetStretch(), range);
|
||||
textLayout.SetFontStyle (dwFont->GetStyle(), range);
|
||||
|
||||
const float fontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (*dwFont);
|
||||
auto fontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (*dwFont);
|
||||
textLayout.SetFontSize (attr.font.getHeight() * fontHeightToEmSizeFactor, range);
|
||||
}
|
||||
|
||||
{
|
||||
const Colour col (attr.colour);
|
||||
auto col = attr.colour;
|
||||
ComSmartPtr<ID2D1SolidColorBrush> d2dBrush;
|
||||
renderTarget.CreateSolidColorBrush (D2D1::ColorF (col.getFloatRed(),
|
||||
col.getFloatGreen(),
|
||||
@ -308,7 +306,7 @@ namespace DirectWriteTypeLayout
|
||||
}
|
||||
}
|
||||
|
||||
bool setupLayout (const AttributedString& text, const float maxWidth, const float maxHeight,
|
||||
bool setupLayout (const AttributedString& text, float maxWidth, float maxHeight,
|
||||
ID2D1RenderTarget& renderTarget, IDWriteFactory& directWriteFactory,
|
||||
IDWriteFontCollection& fontCollection, ComSmartPtr<IDWriteTextLayout>& textLayout)
|
||||
{
|
||||
@ -324,14 +322,14 @@ namespace DirectWriteTypeLayout
|
||||
fontIndex = 0;
|
||||
|
||||
ComSmartPtr<IDWriteFontFamily> dwFontFamily;
|
||||
HRESULT hr = fontCollection.GetFontFamily (fontIndex, dwFontFamily.resetAndGetPointerAddress());
|
||||
auto hr = fontCollection.GetFontFamily (fontIndex, dwFontFamily.resetAndGetPointerAddress());
|
||||
|
||||
ComSmartPtr<IDWriteFont> dwFont;
|
||||
hr = dwFontFamily->GetFirstMatchingFont (DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL,
|
||||
dwFont.resetAndGetPointerAddress());
|
||||
jassert (dwFont != nullptr);
|
||||
|
||||
const float defaultFontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (*dwFont);
|
||||
auto defaultFontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (*dwFont);
|
||||
|
||||
ComSmartPtr<IDWriteTextFormat> dwTextFormat;
|
||||
hr = directWriteFactory.CreateTextFormat (defaultFont.getTypefaceName().toWideCharPointer(), &fontCollection,
|
||||
@ -348,7 +346,7 @@ namespace DirectWriteTypeLayout
|
||||
hr = dwTextFormat->SetTrimming (&trimming, trimmingSign);
|
||||
}
|
||||
|
||||
const int textLen = text.getText().length();
|
||||
auto textLen = text.getText().length();
|
||||
|
||||
hr = directWriteFactory.CreateTextLayout (text.getText().toWideCharPointer(), textLen, dwTextFormat,
|
||||
maxWidth, maxHeight, textLayout.resetAndGetPointerAddress());
|
||||
@ -356,7 +354,7 @@ namespace DirectWriteTypeLayout
|
||||
if (FAILED (hr) || textLayout == nullptr)
|
||||
return false;
|
||||
|
||||
const int numAttributes = text.getNumAttributes();
|
||||
auto numAttributes = text.getNumAttributes();
|
||||
|
||||
for (int i = 0; i < numAttributes; ++i)
|
||||
addAttributedRange (text.getAttribute (i), *textLayout, textLen, renderTarget, fontCollection);
|
||||
@ -376,7 +374,7 @@ namespace DirectWriteTypeLayout
|
||||
return;
|
||||
|
||||
UINT32 actualLineCount = 0;
|
||||
HRESULT hr = dwTextLayout->GetLineMetrics (nullptr, 0, &actualLineCount);
|
||||
auto hr = dwTextLayout->GetLineMetrics (nullptr, 0, &actualLineCount);
|
||||
|
||||
layout.ensureStorageAllocated (actualLineCount);
|
||||
|
||||
@ -388,13 +386,13 @@ namespace DirectWriteTypeLayout
|
||||
HeapBlock<DWRITE_LINE_METRICS> dwLineMetrics (actualLineCount);
|
||||
hr = dwTextLayout->GetLineMetrics (dwLineMetrics, actualLineCount, &actualLineCount);
|
||||
int lastLocation = 0;
|
||||
const int numLines = jmin ((int) actualLineCount, layout.getNumLines());
|
||||
auto numLines = jmin ((int) actualLineCount, layout.getNumLines());
|
||||
float yAdjustment = 0;
|
||||
const float extraLineSpacing = text.getLineSpacing();
|
||||
auto extraLineSpacing = text.getLineSpacing();
|
||||
|
||||
for (int i = 0; i < numLines; ++i)
|
||||
{
|
||||
TextLayout::Line& line = layout.getLine (i);
|
||||
auto& line = layout.getLine (i);
|
||||
line.stringRange = Range<int> (lastLocation, (int) lastLocation + dwLineMetrics[i].length);
|
||||
line.lineOrigin.y += yAdjustment;
|
||||
yAdjustment += extraLineSpacing;
|
||||
@ -422,7 +420,7 @@ namespace DirectWriteTypeLayout
|
||||
|
||||
static bool canAllTypefacesBeUsedInLayout (const AttributedString& text)
|
||||
{
|
||||
const int numCharacterAttributes = text.getNumAttributes();
|
||||
auto numCharacterAttributes = text.getNumAttributes();
|
||||
|
||||
for (int i = 0; i < numCharacterAttributes; ++i)
|
||||
if (dynamic_cast<WindowsDirectWriteTypeface*> (text.getAttribute(i).font.getTypeface()) == nullptr)
|
||||
|
@ -36,7 +36,8 @@ namespace
|
||||
|
||||
uint32 index = 0;
|
||||
BOOL exists = false;
|
||||
HRESULT hr = names->FindLocaleName (L"en-us", &index, &exists);
|
||||
auto hr = names->FindLocaleName (L"en-us", &index, &exists);
|
||||
|
||||
if (! exists)
|
||||
index = 0;
|
||||
|
||||
@ -53,7 +54,7 @@ namespace
|
||||
{
|
||||
jassert (family != nullptr);
|
||||
ComSmartPtr<IDWriteLocalizedStrings> familyNames;
|
||||
HRESULT hr = family->GetFamilyNames (familyNames.resetAndGetPointerAddress());
|
||||
auto hr = family->GetFamilyNames (familyNames.resetAndGetPointerAddress());
|
||||
jassert (SUCCEEDED (hr)); ignoreUnused (hr);
|
||||
return getLocalisedName (familyNames);
|
||||
}
|
||||
@ -62,7 +63,7 @@ namespace
|
||||
{
|
||||
jassert (font != nullptr);
|
||||
ComSmartPtr<IDWriteLocalizedStrings> faceNames;
|
||||
HRESULT hr = font->GetFaceNames (faceNames.resetAndGetPointerAddress());
|
||||
auto hr = font->GetFaceNames (faceNames.resetAndGetPointerAddress());
|
||||
jassert (SUCCEEDED (hr)); ignoreUnused (hr);
|
||||
return getLocalisedName (faceNames);
|
||||
}
|
||||
@ -106,12 +107,12 @@ public:
|
||||
|
||||
if (d2dFactory != nullptr)
|
||||
{
|
||||
D2D1_RENDER_TARGET_PROPERTIES d2dRTProp = D2D1::RenderTargetProperties (D2D1_RENDER_TARGET_TYPE_SOFTWARE,
|
||||
D2D1::PixelFormat (DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
D2D1_ALPHA_MODE_IGNORE),
|
||||
0, 0,
|
||||
D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE,
|
||||
D2D1_FEATURE_LEVEL_DEFAULT);
|
||||
auto d2dRTProp = D2D1::RenderTargetProperties (D2D1_RENDER_TARGET_TYPE_SOFTWARE,
|
||||
D2D1::PixelFormat (DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
D2D1_ALPHA_MODE_IGNORE),
|
||||
0, 0,
|
||||
D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE,
|
||||
D2D1_FEATURE_LEVEL_DEFAULT);
|
||||
|
||||
d2dFactory->CreateDCRenderTarget (&d2dRTProp, directWriteRenderTarget.resetAndGetPointerAddress());
|
||||
}
|
||||
@ -142,14 +143,14 @@ class WindowsDirectWriteTypeface : public Typeface
|
||||
{
|
||||
public:
|
||||
WindowsDirectWriteTypeface (const Font& font, IDWriteFontCollection* fontCollection)
|
||||
: Typeface (font.getTypefaceName(), font.getTypefaceStyle()),
|
||||
unitsToHeightScaleFactor (1.0f), heightToPointsFactor (1.0f), ascent (0.0f)
|
||||
: Typeface (font.getTypefaceName(), font.getTypefaceStyle())
|
||||
{
|
||||
jassert (fontCollection != nullptr);
|
||||
|
||||
BOOL fontFound = false;
|
||||
uint32 fontIndex = 0;
|
||||
HRESULT hr = fontCollection->FindFamilyName (font.getTypefaceName().toWideCharPointer(), &fontIndex, &fontFound);
|
||||
auto hr = fontCollection->FindFamilyName (font.getTypefaceName().toWideCharPointer(), &fontIndex, &fontFound);
|
||||
|
||||
if (! fontFound)
|
||||
fontIndex = 0;
|
||||
|
||||
@ -190,18 +191,18 @@ public:
|
||||
designUnitsPerEm = dwFontMetrics.designUnitsPerEm;
|
||||
|
||||
ascent = std::abs ((float) dwFontMetrics.ascent);
|
||||
const float totalSize = ascent + std::abs ((float) dwFontMetrics.descent);
|
||||
auto totalSize = ascent + std::abs ((float) dwFontMetrics.descent);
|
||||
ascent /= totalSize;
|
||||
unitsToHeightScaleFactor = designUnitsPerEm / totalSize;
|
||||
|
||||
HDC tempDC = GetDC (0);
|
||||
float dpi = (GetDeviceCaps (tempDC, LOGPIXELSX) + GetDeviceCaps (tempDC, LOGPIXELSY)) / 2.0f;
|
||||
auto tempDC = GetDC (0);
|
||||
auto dpi = (GetDeviceCaps (tempDC, LOGPIXELSX) + GetDeviceCaps (tempDC, LOGPIXELSY)) / 2.0f;
|
||||
heightToPointsFactor = (dpi / GetDeviceCaps (tempDC, LOGPIXELSY)) * unitsToHeightScaleFactor;
|
||||
ReleaseDC (0, tempDC);
|
||||
|
||||
const float pathAscent = (1024.0f * dwFontMetrics.ascent) / designUnitsPerEm;
|
||||
const float pathDescent = (1024.0f * dwFontMetrics.descent) / designUnitsPerEm;
|
||||
const float pathScale = 1.0f / (std::abs (pathAscent) + std::abs (pathDescent));
|
||||
auto pathAscent = (1024.0f * dwFontMetrics.ascent) / designUnitsPerEm;
|
||||
auto pathDescent = (1024.0f * dwFontMetrics.descent) / designUnitsPerEm;
|
||||
auto pathScale = 1.0f / (std::abs (pathAscent) + std::abs (pathDescent));
|
||||
pathTransform = AffineTransform::scale (pathScale);
|
||||
}
|
||||
}
|
||||
@ -214,8 +215,8 @@ public:
|
||||
|
||||
float getStringWidth (const String& text)
|
||||
{
|
||||
const CharPointer_UTF32 textUTF32 (text.toUTF32());
|
||||
const size_t len = textUTF32.length();
|
||||
auto textUTF32 = text.toUTF32();
|
||||
auto len = textUTF32.length();
|
||||
|
||||
HeapBlock<UINT16> glyphIndices (len);
|
||||
dwFontFace->GetGlyphIndices (textUTF32, (UINT32) len, glyphIndices);
|
||||
@ -224,6 +225,7 @@ public:
|
||||
dwFontFace->GetDesignGlyphMetrics (glyphIndices, (UINT32) len, dwGlyphMetrics, false);
|
||||
|
||||
float x = 0;
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
x += (float) dwGlyphMetrics[i].advanceWidth / designUnitsPerEm;
|
||||
|
||||
@ -234,8 +236,8 @@ public:
|
||||
{
|
||||
xOffsets.add (0);
|
||||
|
||||
const CharPointer_UTF32 textUTF32 (text.toUTF32());
|
||||
const size_t len = textUTF32.length();
|
||||
auto textUTF32 = text.toUTF32();
|
||||
auto len = textUTF32.length();
|
||||
|
||||
HeapBlock<UINT16> glyphIndices (len);
|
||||
dwFontFace->GetGlyphIndices (textUTF32, (UINT32) len, glyphIndices);
|
||||
@ -243,6 +245,7 @@ public:
|
||||
dwFontFace->GetDesignGlyphMetrics (glyphIndices, (UINT32) len, dwGlyphMetrics, false);
|
||||
|
||||
float x = 0;
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
{
|
||||
x += (float) dwGlyphMetrics[i].advanceWidth / designUnitsPerEm;
|
||||
@ -254,10 +257,11 @@ public:
|
||||
bool getOutlineForGlyph (int glyphNumber, Path& path)
|
||||
{
|
||||
jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty
|
||||
UINT16 glyphIndex = (UINT16) glyphNumber;
|
||||
auto glyphIndex = (UINT16) glyphNumber;
|
||||
ComSmartPtr<PathGeometrySink> pathGeometrySink (new PathGeometrySink());
|
||||
|
||||
dwFontFace->GetGlyphRunOutline (1024.0f, &glyphIndex, nullptr, nullptr, 1, false, false, pathGeometrySink);
|
||||
dwFontFace->GetGlyphRunOutline (1024.0f, &glyphIndex, nullptr, nullptr,
|
||||
1, false, false, pathGeometrySink);
|
||||
path = pathGeometrySink->path;
|
||||
|
||||
if (! pathTransform.isIdentity())
|
||||
@ -273,8 +277,8 @@ public:
|
||||
private:
|
||||
SharedResourcePointer<Direct2DFactories> factories;
|
||||
ComSmartPtr<IDWriteFontFace> dwFontFace;
|
||||
float unitsToHeightScaleFactor, heightToPointsFactor, ascent;
|
||||
int designUnitsPerEm;
|
||||
float unitsToHeightScaleFactor = 1.0f, heightToPointsFactor = 1.0f, ascent = 0;
|
||||
int designUnitsPerEm = 0;
|
||||
AffineTransform pathTransform;
|
||||
|
||||
struct PathGeometrySink : public ComBaseClassHelper<IDWriteGeometrySink>
|
||||
|
@ -63,14 +63,14 @@ namespace TTFNameExtractor
|
||||
const int64 directoryOffset, const int64 offsetOfStringStorage)
|
||||
{
|
||||
String result;
|
||||
const int64 oldPos = input.getPosition();
|
||||
auto oldPos = input.getPosition();
|
||||
input.setPosition (directoryOffset + offsetOfStringStorage + ByteOrder::swapIfLittleEndian (nameRecord.offsetFromStorageArea));
|
||||
const int stringLength = (int) ByteOrder::swapIfLittleEndian (nameRecord.stringLength);
|
||||
const int platformID = ByteOrder::swapIfLittleEndian (nameRecord.platformID);
|
||||
auto stringLength = (int) ByteOrder::swapIfLittleEndian (nameRecord.stringLength);
|
||||
auto platformID = ByteOrder::swapIfLittleEndian (nameRecord.platformID);
|
||||
|
||||
if (platformID == 0 || platformID == 3)
|
||||
{
|
||||
const int numChars = stringLength / 2 + 1;
|
||||
auto numChars = stringLength / 2 + 1;
|
||||
HeapBlock<uint16> buffer;
|
||||
buffer.calloc (numChars + 1);
|
||||
input.read (buffer, stringLength);
|
||||
@ -165,10 +165,8 @@ namespace FontEnumerators
|
||||
const String fontName (lpelfe->elfLogFont.lfFaceName);
|
||||
fontName.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
|
||||
|
||||
HDC dc = CreateCompatibleDC (0);
|
||||
EnumFontFamiliesEx (dc, &lf,
|
||||
(FONTENUMPROCW) &fontEnum2,
|
||||
lParam, 0);
|
||||
auto dc = CreateCompatibleDC (0);
|
||||
EnumFontFamiliesEx (dc, &lf, (FONTENUMPROCW) &fontEnum2, lParam, 0);
|
||||
DeleteDC (dc);
|
||||
}
|
||||
|
||||
@ -191,7 +189,7 @@ StringArray Font::findAllTypefaceNames()
|
||||
|
||||
for (uint32 i = 0; i < fontFamilyCount; ++i)
|
||||
{
|
||||
HRESULT hr = factories->systemFonts->GetFontFamily (i, fontFamily.resetAndGetPointerAddress());
|
||||
auto hr = factories->systemFonts->GetFontFamily (i, fontFamily.resetAndGetPointerAddress());
|
||||
|
||||
if (SUCCEEDED (hr))
|
||||
results.addIfNotAlreadyThere (getFontFamilyName (fontFamily));
|
||||
@ -200,7 +198,7 @@ StringArray Font::findAllTypefaceNames()
|
||||
else
|
||||
#endif
|
||||
{
|
||||
HDC dc = CreateCompatibleDC (0);
|
||||
auto dc = CreateCompatibleDC (0);
|
||||
|
||||
{
|
||||
LOGFONTW lf = { 0 };
|
||||
@ -237,7 +235,8 @@ StringArray Font::findAllTypefaceStyles (const String& family)
|
||||
{
|
||||
BOOL fontFound = false;
|
||||
uint32 fontIndex = 0;
|
||||
HRESULT hr = factories->systemFonts->FindFamilyName (family.toWideCharPointer(), &fontIndex, &fontFound);
|
||||
auto hr = factories->systemFonts->FindFamilyName (family.toWideCharPointer(), &fontIndex, &fontFound);
|
||||
|
||||
if (! fontFound)
|
||||
fontIndex = 0;
|
||||
|
||||
@ -302,7 +301,7 @@ Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
|
||||
static DefaultFontNames defaultNames;
|
||||
|
||||
Font newFont (font);
|
||||
const String& faceName = font.getTypefaceName();
|
||||
auto& faceName = font.getTypefaceName();
|
||||
|
||||
if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans);
|
||||
else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif);
|
||||
@ -318,22 +317,14 @@ Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
|
||||
class WindowsTypeface : public Typeface
|
||||
{
|
||||
public:
|
||||
WindowsTypeface (const Font& font)
|
||||
: Typeface (font.getTypefaceName(), font.getTypefaceStyle()),
|
||||
fontH (0), previousFontH (0),
|
||||
dc (CreateCompatibleDC (0)), memoryFont (0),
|
||||
ascent (1.0f), heightToPointsFactor (1.0f),
|
||||
defaultGlyph (-1)
|
||||
WindowsTypeface (const Font& font) : Typeface (font.getTypefaceName(),
|
||||
font.getTypefaceStyle())
|
||||
{
|
||||
loadFont();
|
||||
}
|
||||
|
||||
WindowsTypeface (const void* data, size_t dataSize)
|
||||
: Typeface (String(), String()),
|
||||
fontH (0), previousFontH (0),
|
||||
dc (CreateCompatibleDC (0)), memoryFont (0),
|
||||
ascent (1.0f), heightToPointsFactor (1.0f),
|
||||
defaultGlyph (-1)
|
||||
: Typeface (String(), String())
|
||||
{
|
||||
DWORD numInstalled = 0;
|
||||
memoryFont = AddFontMemResourceEx (const_cast<void*> (data), (DWORD) dataSize,
|
||||
@ -362,8 +353,8 @@ public:
|
||||
|
||||
float getStringWidth (const String& text)
|
||||
{
|
||||
const CharPointer_UTF16 utf16 (text.toUTF16());
|
||||
const size_t numChars = utf16.length();
|
||||
auto utf16 = text.toUTF16();
|
||||
auto numChars = utf16.length();
|
||||
HeapBlock<uint16> results (numChars);
|
||||
float x = 0;
|
||||
|
||||
@ -377,10 +368,10 @@ public:
|
||||
return x;
|
||||
}
|
||||
|
||||
void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets)
|
||||
void getGlyphPositions (const String& text, Array<int>& resultGlyphs, Array<float>& xOffsets)
|
||||
{
|
||||
const CharPointer_UTF16 utf16 (text.toUTF16());
|
||||
const size_t numChars = utf16.length();
|
||||
auto utf16 = text.toUTF16();
|
||||
auto numChars = utf16.length();
|
||||
HeapBlock<uint16> results (numChars);
|
||||
float x = 0;
|
||||
|
||||
@ -408,8 +399,8 @@ public:
|
||||
|
||||
GLYPHMETRICS gm;
|
||||
// (although GetGlyphOutline returns a DWORD, it may be -1 on failure, so treat it as signed int..)
|
||||
const int bufSize = (int) GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX,
|
||||
&gm, 0, 0, &identityMatrix);
|
||||
auto bufSize = (int) GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX,
|
||||
&gm, 0, 0, &identityMatrix);
|
||||
|
||||
if (bufSize > 0)
|
||||
{
|
||||
@ -417,18 +408,18 @@ public:
|
||||
GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm,
|
||||
bufSize, data, &identityMatrix);
|
||||
|
||||
const TTPOLYGONHEADER* pheader = reinterpret_cast<TTPOLYGONHEADER*> (data.getData());
|
||||
auto pheader = reinterpret_cast<const TTPOLYGONHEADER*> (data.getData());
|
||||
|
||||
const float scaleX = 1.0f / tm.tmHeight;
|
||||
const float scaleY = -scaleX;
|
||||
auto scaleX = 1.0f / tm.tmHeight;
|
||||
auto scaleY = -scaleX;
|
||||
|
||||
while ((char*) pheader < data + bufSize)
|
||||
{
|
||||
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;
|
||||
auto curve = (const TTPOLYCURVE*) ((const char*) pheader + sizeof (TTPOLYGONHEADER));
|
||||
auto curveEnd = ((const char*) pheader) + pheader->cb;
|
||||
|
||||
while ((const char*) curve < curveEnd)
|
||||
{
|
||||
@ -442,10 +433,10 @@ public:
|
||||
{
|
||||
for (int i = 0; i < curve->cpfx - 1; ++i)
|
||||
{
|
||||
const float x2 = scaleX * curve->apfx[i].x.value;
|
||||
const float y2 = scaleY * curve->apfx[i].y.value;
|
||||
float x3 = scaleX * curve->apfx[i + 1].x.value;
|
||||
float y3 = scaleY * curve->apfx[i + 1].y.value;
|
||||
auto x2 = scaleX * curve->apfx[i].x.value;
|
||||
auto y2 = scaleY * curve->apfx[i].y.value;
|
||||
auto x3 = scaleX * curve->apfx[i + 1].x.value;
|
||||
auto y3 = scaleY * curve->apfx[i + 1].y.value;
|
||||
|
||||
if (i < curve->cpfx - 2)
|
||||
{
|
||||
@ -471,32 +462,19 @@ public:
|
||||
|
||||
private:
|
||||
static const MAT2 identityMatrix;
|
||||
HFONT fontH;
|
||||
HGDIOBJ previousFontH;
|
||||
HDC dc;
|
||||
HFONT fontH = {};
|
||||
HGDIOBJ previousFontH = {};
|
||||
HDC dc { CreateCompatibleDC (0) };
|
||||
TEXTMETRIC tm;
|
||||
HANDLE memoryFont;
|
||||
float ascent, heightToPointsFactor;
|
||||
int defaultGlyph, heightInPoints;
|
||||
HANDLE memoryFont = {};
|
||||
float ascent = 1.0f, heightToPointsFactor = 1.0f;
|
||||
int defaultGlyph = -1, heightInPoints = 0;
|
||||
std::unordered_map<uint64, float> kerningPairs;
|
||||
|
||||
struct KerningPair
|
||||
static uint64 kerningPairIndex (int glyph1, int glyph2)
|
||||
{
|
||||
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;
|
||||
return (((uint64) (uint32) glyph1) << 32) | (uint64) (uint32) glyph2;
|
||||
}
|
||||
|
||||
void loadFont()
|
||||
{
|
||||
@ -514,15 +492,15 @@ private:
|
||||
lf.lfHeight = -256;
|
||||
name.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
|
||||
|
||||
HFONT standardSizedFont = CreateFontIndirect (&lf);
|
||||
auto standardSizedFont = CreateFontIndirect (&lf);
|
||||
|
||||
if (standardSizedFont != 0)
|
||||
{
|
||||
if ((previousFontH = SelectObject (dc, standardSizedFont)) != 0)
|
||||
{
|
||||
fontH = standardSizedFont;
|
||||
|
||||
OUTLINETEXTMETRIC otm;
|
||||
|
||||
if (GetOutlineTextMetrics (dc, sizeof (otm), &otm) != 0)
|
||||
{
|
||||
heightInPoints = otm.otmEMSquare;
|
||||
@ -537,41 +515,42 @@ private:
|
||||
|
||||
if (GetTextMetrics (dc, &tm))
|
||||
{
|
||||
float dpi = (GetDeviceCaps (dc, LOGPIXELSX) + GetDeviceCaps (dc, LOGPIXELSY)) / 2.0f;
|
||||
auto dpi = (GetDeviceCaps (dc, LOGPIXELSX) + GetDeviceCaps (dc, LOGPIXELSY)) / 2.0f;
|
||||
heightToPointsFactor = (dpi / GetDeviceCaps (dc, LOGPIXELSY)) * heightInPoints / (float) tm.tmHeight;
|
||||
ascent = tm.tmAscent / (float) tm.tmHeight;
|
||||
defaultGlyph = getGlyphForChar (dc, tm.tmDefaultChar);
|
||||
createKerningPairs (dc, (float) tm.tmHeight);
|
||||
std::unordered_map<int, int> glyphsForChars;
|
||||
defaultGlyph = getGlyphForChar (dc, glyphsForChars, tm.tmDefaultChar);
|
||||
createKerningPairs (dc, glyphsForChars, (float) tm.tmHeight);
|
||||
}
|
||||
}
|
||||
|
||||
void createKerningPairs (HDC hdc, const float height)
|
||||
void createKerningPairs (HDC hdc, std::unordered_map<int, int>& glyphsForChars, float height)
|
||||
{
|
||||
HeapBlock<KERNINGPAIR> rawKerning;
|
||||
const DWORD numKPs = GetKerningPairs (hdc, 0, 0);
|
||||
auto numKPs = GetKerningPairs (hdc, 0, 0);
|
||||
rawKerning.calloc (numKPs);
|
||||
GetKerningPairs (hdc, numKPs, rawKerning);
|
||||
|
||||
kerningPairs.ensureStorageAllocated ((int) numKPs);
|
||||
std::unordered_map<int, int> widthsForGlyphs;
|
||||
|
||||
for (DWORD i = 0; i < numKPs; ++i)
|
||||
{
|
||||
KerningPair kp;
|
||||
kp.glyph1 = getGlyphForChar (hdc, rawKerning[i].wFirst);
|
||||
kp.glyph2 = getGlyphForChar (hdc, rawKerning[i].wSecond);
|
||||
auto glyph1 = getGlyphForChar (hdc, glyphsForChars, rawKerning[i].wFirst);
|
||||
auto glyph2 = getGlyphForChar (hdc, glyphsForChars, rawKerning[i].wSecond);
|
||||
auto standardWidth = getGlyphWidth (hdc, widthsForGlyphs, glyph1);
|
||||
|
||||
const int standardWidth = getGlyphWidth (hdc, 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);
|
||||
kerningPairs[kerningPairIndex (glyph1, glyph2)] = (standardWidth + rawKerning[i].iKernAmount) / height;
|
||||
kerningPairs[kerningPairIndex (glyph1, -1)] = standardWidth / height;
|
||||
}
|
||||
}
|
||||
|
||||
static int getGlyphForChar (HDC dc, juce_wchar character)
|
||||
static int getGlyphForChar (HDC dc, std::unordered_map<int, int>& cache, juce_wchar character)
|
||||
{
|
||||
auto existing = cache.find ((int) character);
|
||||
|
||||
if (existing != cache.end())
|
||||
return existing->second;
|
||||
|
||||
const WCHAR charToTest[] = { (WCHAR) character, 0 };
|
||||
WORD index = 0;
|
||||
|
||||
@ -579,9 +558,22 @@ private:
|
||||
|| index == 0xffff)
|
||||
return -1;
|
||||
|
||||
cache[(int) character] = index;
|
||||
return index;
|
||||
}
|
||||
|
||||
static int getGlyphWidth (HDC dc, std::unordered_map<int, int>& cache, int glyphNumber)
|
||||
{
|
||||
auto existing = cache.find (glyphNumber);
|
||||
|
||||
if (existing != cache.end())
|
||||
return existing->second;
|
||||
|
||||
auto width = getGlyphWidth (dc, glyphNumber);
|
||||
cache[glyphNumber] = width;
|
||||
return width;
|
||||
}
|
||||
|
||||
static int getGlyphWidth (HDC dc, int glyphNumber)
|
||||
{
|
||||
GLYPHMETRICS gm;
|
||||
@ -590,28 +582,21 @@ private:
|
||||
return gm.gmCellIncX;
|
||||
}
|
||||
|
||||
float getKerning (HDC hdc, const int glyph1, const int glyph2)
|
||||
float getKerning (HDC hdc, int glyph1, int glyph2)
|
||||
{
|
||||
KerningPair kp;
|
||||
kp.glyph1 = glyph1;
|
||||
kp.glyph2 = glyph2;
|
||||
int index = kerningPairs.indexOf (kp);
|
||||
auto pair = kerningPairs.find (kerningPairIndex (glyph1, glyph2));
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
kp.glyph2 = -1;
|
||||
index = kerningPairs.indexOf (kp);
|
||||
if (pair != kerningPairs.end())
|
||||
return pair->second;
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
kp.glyph2 = -1;
|
||||
kp.kerning = getGlyphWidth (hdc, kp.glyph1) / (float) tm.tmHeight;
|
||||
kerningPairs.add (kp);
|
||||
return kp.kerning;
|
||||
}
|
||||
}
|
||||
auto single = kerningPairs.find (kerningPairIndex (glyph1, -1));
|
||||
|
||||
return kerningPairs.getReference (index).kerning;
|
||||
if (single != kerningPairs.end())
|
||||
return single->second;
|
||||
|
||||
auto width = getGlyphWidth (hdc, glyph1) / (float) tm.tmHeight;
|
||||
kerningPairs[kerningPairIndex (glyph1, -1)] = width;
|
||||
return width;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTypeface)
|
||||
|
Reference in New Issue
Block a user