Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/vcl/source/gdi/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 25 kB image not shown  

Quelle  impglyphitem.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <impglyphitem.hxx>
#include <utility>
#include <vcl/glyphitemcache.hxx>
#include <vcl/vcllayout.hxx>
#include <tools/lazydelete.hxx>
#include <tools/stream.hxx>
#include <unotools/configmgr.hxx>
#include <TextLayoutCache.hxx>
#include <officecfg/Office/Common.hxx>
#include <o3tl/string_view.hxx>

#include <unicode/ubidi.h>
#include <unicode/uchar.h>

// These need being explicit because of SalLayoutGlyphsImpl being private in vcl.
SalLayoutGlyphs::SalLayoutGlyphs() {}

SalLayoutGlyphs::~SalLayoutGlyphs() {}

SalLayoutGlyphs::SalLayoutGlyphs(SalLayoutGlyphs&& rOther) noexcept
{
    std::swap(m_pImpl, rOther.m_pImpl);
    std::swap(m_pExtraImpls, rOther.m_pExtraImpls);
}

SalLayoutGlyphs& SalLayoutGlyphs::operator=(SalLayoutGlyphs&& rOther) noexcept
{
    if (this != &rOther)
    {
        std::swap(m_pImpl, rOther.m_pImpl);
        std::swap(m_pExtraImpls, rOther.m_pExtraImpls);
    }
    return *this;
}

bool SalLayoutGlyphs::IsValid() const
{
    if (m_pImpl == nullptr)
        return false;
    if (!m_pImpl->IsValid())
        return false;
    if (m_pExtraImpls)
        for (std::unique_ptr<SalLayoutGlyphsImpl> const& impl : *m_pExtraImpls)
            if (!impl->IsValid())
                return false;
    return true;
}

void SalLayoutGlyphs::Invalidate()
{
    // Invalidating is in fact simply clearing.
    m_pImpl.reset();
    m_pExtraImpls.reset();
}

SalLayoutGlyphsImpl* SalLayoutGlyphs::Impl(unsigned int nLevel) const
{
    if (nLevel == 0)
        return m_pImpl.get();
    if (m_pExtraImpls != nullptr && nLevel - 1 < m_pExtraImpls->size())
        return (*m_pExtraImpls)[nLevel - 1].get();
    return nullptr;
}

void SalLayoutGlyphs::AppendImpl(SalLayoutGlyphsImpl* pImpl)
{
    if (!m_pImpl)
        m_pImpl.reset(pImpl);
    else
    {
        if (!m_pExtraImpls)
            m_pExtraImpls.reset(new std::vector<std::unique_ptr<SalLayoutGlyphsImpl>>);
        m_pExtraImpls->emplace_back(pImpl);
    }
}

SalLayoutGlyphsImpl* SalLayoutGlyphsImpl::clone() const { return new SalLayoutGlyphsImpl(*this); }

// Clone, but only glyphs in the given range in the original text string.
// It is possible the given range may not be cloned, in which case this returns nullptr.
SalLayoutGlyphsImpl* SalLayoutGlyphsImpl::cloneCharRange(sal_Int32 index, sal_Int32 length) const
{
    std::unique_ptr<SalLayoutGlyphsImpl> copy(new SalLayoutGlyphsImpl(*GetFont()));
    copy->SetFlags(GetFlags());
    if (empty())
        return copy.release();
    bool rtl = front().IsRTLGlyph();
    // Avoid mixing LTR/RTL or layouts that do not have it set explicitly (BiDiStrong). Otherwise
    // the subset may not quite match what would a real layout call give (e.g. some characters with neutral
    // direction such as space might have different LTR/RTL flag). It seems bailing out here mostly
    // avoid relatively rare corner cases and doesn't matter for performance.
    // This is also checked in SalLayoutGlyphsCache::GetLayoutGlyphs() below.
    if (!(GetFlags() & SalLayoutFlags::BiDiStrong)
        || rtl != bool(GetFlags() & SalLayoutFlags::BiDiRtl))
        return nullptr;
    copy->reserve(std::min<size_t>(size(), length));
    sal_Int32 beginPos = index;
    sal_Int32 endPos = index + length;
    const_iterator pos;
    if (rtl)
    {
        // Glyphs are in reverse order for RTL.
        beginPos = index + length - 1;
        endPos = index - 1;
        // Skip glyphs that are in the string after the given index, i.e. are before the glyphs
        // we want.
        pos = std::partition_point(
            begin(), end(), [beginPos](const GlyphItem& it) { return it.charPos() > beginPos; });
    }
    else
    {
        // Skip glyphs that are in the string before the given index (glyphs are sorted by charPos()).
        pos = std::partition_point(
            begin(), end(), [beginPos](const GlyphItem& it) { return it.charPos() < beginPos; });
    }
    if (pos == end())
        return nullptr;
    // Require a start at the exact position given, otherwise bail out.
    if (pos->charPos() != beginPos)
        return nullptr;
    // For RTL make sure we're not cutting in the middle of a multi-character glyph,
    // or in the middle of a cluster
    // (for non-RTL charPos is always the start of a multi-character glyph).
    if (rtl && (pos->charPos() + pos->charCount() > beginPos + 1 || pos->IsInCluster()))
        return nullptr;
    if (!isSafeToBreak(pos, rtl))
        return nullptr;
    // LinearPos needs adjusting to start at xOffset/yOffset for the first item,
    // that's how it's computed in GenericSalLayout::LayoutText().
    basegfx::B2DPoint zeroPoint
        = pos->linearPos() - basegfx::B2DPoint(pos->xOffset(), pos->yOffset());
    // Add and adjust all glyphs until the given length.
    // The check is written as 'charPos + charCount <= endPos' rather than 'charPos < endPos'
    // (or similarly for RTL) to make sure we include complete glyphs. If a glyph is composed
    // from several characters, we should not cut in the middle of those characters, so this
    // checks the glyph is entirely in the given character range. If it is not, this will end
    // the loop and the later 'pos->charPos() != endPos' check will fail and bail out.
    // CppunitTest_sw_layoutwriter's testCombiningCharacterCursorPosition would fail without this.
    while (pos != end()
           && (rtl ? pos->charPos() - pos->charCount() >= endPos
                   : pos->charPos() + pos->charCount() <= endPos))
    {
        if (pos->IsRTLGlyph() != rtl)
            return nullptr; // Don't mix RTL and non-RTL runs.
        // HACK: When running CppunitTest_sw_uiwriter3's testTdf104649 on Mac there's glyph
        // with id 1232 that has 0 charCount, 0 origWidth and inconsistent xOffset (sometimes 0,
        // but sometimes not). Possibly font or Harfbuzz bug? It's extremely rare, so simply bail out.
        if (pos->charCount() == 0 && pos->origWidth() == 0)
            return nullptr;
        copy->push_back(*pos);
        copy->back().setLinearPos(copy->back().linearPos() - zeroPoint);
        ++pos;
    }
    if (pos != end())
    {
        // Fail if the next character is not at the expected past-end position. For RTL check
        // that we're not cutting in the middle of a multi-character glyph.
        if (rtl ? pos->charPos() + pos->charCount() != endPos + 1 : pos->charPos() != endPos)
            return nullptr;
        if (!isSafeToBreak(pos, rtl))
            return nullptr;
    }
    return copy.release();
}

bool SalLayoutGlyphsImpl::isSafeToBreak(const_iterator pos, bool rtl) const
{
    if (rtl)
    {
        // RTL is more complicated, because HB_GLYPH_FLAG_UNSAFE_TO_BREAK talks about beginning
        // of a cluster, which refers to the text, not glyphs. This function is called
        // for the first glyph of the subset and the first glyph after the subset, but since
        // the glyphs are backwards, and we need the beginning of cluster at the start of the text
        // and beginning of the cluster after the text, we need to check glyphs before this position.
        if (pos == begin())
            return true;
        --pos;
    }
    // Don't create a subset if it's not safe to break at the beginning or end of the sequence
    // (https://harfbuzz.github.io/harfbuzz-hb-buffer.html#hb-glyph-flags-t).
    if (pos->IsUnsafeToBreak() || (pos->IsInCluster() && !pos->IsClusterStart()))
        return false;
    return true;
}

#ifdef DBG_UTIL
bool SalLayoutGlyphsImpl::isLayoutEquivalent(const SalLayoutGlyphsImpl* other) const
{
    if (!GetFont()->mxFontMetric->CompareDeviceIndependentFontAttributes(
            *other->GetFont()->mxFontMetric))
        return false;
    if (GetFlags() != other->GetFlags())
        return false;
    if (empty() || other->empty())
        return empty() == other->empty();
    if (size() != other->size())
        return false;
    for (size_t pos = 0; pos < size(); ++pos)
    {
        if (!(*this)[pos].isLayoutEquivalent((*other)[pos]))
            return false;
    }
    return true;
}
#endif

bool SalLayoutGlyphsImpl::IsValid() const
{
    if (!m_rFontInstance.is())
        return false;
    return true;
}

void SalLayoutGlyphsCache::clear() { mCachedGlyphs.clear(); }

SalLayoutGlyphsCache* SalLayoutGlyphsCache::self()
{
    static tools::DeleteOnDeinit<SalLayoutGlyphsCache> cache(
        !comphelper::IsFuzzing() ? officecfg::Office::Common::Cache::Font::GlyphsCacheSize::get()
                                 : 20000000);
    return cache.get();
}

static UBiDiDirection getBiDiDirection(std::u16string_view text, sal_Int32 index, sal_Int32 len)
{
    // Return whether all character are LTR, RTL, neutral or whether it's mixed.
    // This is sort of ubidi_getBaseDirection() and ubidi_getDirection(),
    // but it's meant to be fast but also check all characters.
    sal_Int32 end = index + len;
    UBiDiDirection direction = UBIDI_NEUTRAL;
    while (index < end)
    {
        switch (u_charDirection(o3tl::iterateCodePoints(text, &index)))
        {
            // Only characters with strong direction.
            case U_LEFT_TO_RIGHT:
                if (direction == UBIDI_RTL)
                    return UBIDI_MIXED;
                direction = UBIDI_LTR;
                break;
            case U_RIGHT_TO_LEFT:
            case U_RIGHT_TO_LEFT_ARABIC:
                if (direction == UBIDI_LTR)
                    return UBIDI_MIXED;
                direction = UBIDI_RTL;
                break;
            default:
                break;
        }
    }
    return direction;
}

static SalLayoutGlyphs makeGlyphsSubset(const SalLayoutGlyphs& source,
                                        const OutputDevice* outputDevice, std::u16string_view text,
                                        sal_Int32 index, sal_Int32 len)
{
    // tdf#149264: We need to check if the text is LTR, RTL or mixed. Apparently
    // harfbuzz doesn't give reproducible results (or possibly HB_GLYPH_FLAG_UNSAFE_TO_BREAK
    // is not reliable?) when asked to lay out RTL text as LTR. So require that the whole
    // subset ir either LTR or RTL.
    UBiDiDirection direction = getBiDiDirection(text, index, len);
    if (direction == UBIDI_MIXED)
        return SalLayoutGlyphs();
    SalLayoutGlyphs ret;
    for (int level = 0;; ++level)
    {
        const SalLayoutGlyphsImpl* sourceLevel = source.Impl(level);
        if (sourceLevel == nullptr)
            break;
        bool sourceRtl = bool(sourceLevel->GetFlags() & SalLayoutFlags::BiDiRtl);
        if ((direction == UBIDI_LTR && sourceRtl) || (direction == UBIDI_RTL && !sourceRtl))
            return SalLayoutGlyphs();
        SalLayoutGlyphsImpl* cloned = sourceLevel->cloneCharRange(index, len);
        // If the glyphs range cannot be cloned, bail out.
        if (cloned == nullptr)
            return SalLayoutGlyphs();
        // If the entire string is mixed LTR/RTL but the subset is only LTR,
        // then make sure the flags match that, otherwise checkGlyphsEqual()
        // would assert on flags being different.
        cloned->SetFlags(cloned->GetFlags()
                         | outputDevice->GetBiDiLayoutFlags(text, index, index + len));
        ret.AppendImpl(cloned);
    }
    return ret;
}

#ifdef DBG_UTIL
static void checkGlyphsEqual(const SalLayoutGlyphs& g1, const SalLayoutGlyphs& g2)
{
    for (int level = 0;; ++level)
    {
        const SalLayoutGlyphsImpl* l1 = g1.Impl(level);
        const SalLayoutGlyphsImpl* l2 = g2.Impl(level);
        if (l1 == nullptr || l2 == nullptr)
        {
            assert(l1 == l2);
            break;
        }
        assert(l1->isLayoutEquivalent(l2));
    }
}
#endif

const SalLayoutGlyphs* SalLayoutGlyphsCache::GetLayoutGlyphs(
    const VclPtr<const OutputDevice>& outputDevice, const OUString& text, sal_Int32 nIndex,
    sal_Int32 nLen, tools::Long nLogicWidth, const vcl::text::TextLayoutCache* layoutCache)
{
    if (nLen == 0)
        return nullptr;
    const CachedGlyphsKey key(outputDevice, text, nIndex, nLen, nLogicWidth);
    GlyphsCache::const_iterator it = mCachedGlyphs.find(key);
    if (it != mCachedGlyphs.end())
    {
        if (it->second.IsValid())
            return &it->second;
        // Do not try to create the layout here. If a cache item exists, it's already
        // been attempted and the layout was invalid (this happens with MultiSalLayout).
        // So in that case this is a cached failure.
        return nullptr;
    }
    bool resetLastSubstringKey = true;
    const sal_Unicode nbSpace = 0xa0; // non-breaking space
    // SalLayoutGlyphsImpl::cloneCharRange() requires BiDiStrong, so if not set, do not even try.
    bool skipGlyphSubsets
        = !(outputDevice->GetLayoutMode() & vcl::text::ComplexTextLayoutFlags::BiDiStrong);
    if ((nIndex != 0 || nLen != text.getLength()) && !skipGlyphSubsets)
    {
        // The glyphs functions are often called first for an entire string
        // and then with an increasing starting index until the end of the string.
        // Which means it's possible to get the glyphs faster by just copying
        // a subset of the full glyphs and adjusting as necessary.
        if (mLastTemporaryKey.has_value() && mLastTemporaryKey == key)
            return &mLastTemporaryGlyphs;
        const CachedGlyphsKey keyWhole(outputDevice, text, 0, text.getLength(), nLogicWidth);
        GlyphsCache::const_iterator itWhole = mCachedGlyphs.find(keyWhole);
        if (itWhole == mCachedGlyphs.end())
        {
            // This function may often be called repeatedly for segments of the same string,
            // in which case it is more efficient to cache glyphs for the entire string
            // and then return subsets of them. So if a second call either starts at the same
            // position or starts at the end of the previous call, cache the entire string.
            // This used to do this only for the first two segments of the string,
            // but that missed the case when the font slightly changed e.g. because of the first
            // part being underlined. Doing this for any two segments allows this optimization
            // even when the prefix of the string would use a different font.
            // TODO: Can those font differences be ignored?

            // Shaping performance seems to scale poorly with respect to string length. Certain
            // writing systems involve extremely long strings (for example, Tibetan: tdf#92064).
            // In such cases, this optimization would be a net loss, and must be disabled.
            constexpr sal_Int32 nOptLengthThreshold = 20000;
            bool bEnableOptimization = (text.getLength() < nOptLengthThreshold);

            // Writer layouts tests enable SAL_NON_APPLICATION_FONT_USE=abort in order
            // to make PrintFontManager::Substitute() abort if font fallback happens. When
            // laying out the entire string the chance this happens increases (e.g. testAbi11870
            // normally calls this function only for a part of a string, but this optimization
            // lays out the entire string and causes a fallback). Since this optimization
            // does not change result of this function, simply disable it for those tests.
            static const bool bAbortOnFontSubstitute = [] {
                const char* pEnv = getenv("SAL_NON_APPLICATION_FONT_USE");
                return pEnv && strcmp(pEnv, "abort") == 0;
            }();
            if (bEnableOptimization && mLastSubstringKey.has_value() && !bAbortOnFontSubstitute)
            {
                sal_Int32 pos = nIndex;
                if (mLastSubstringKey->len < pos && text[pos - 1] == nbSpace)
                    --pos; // Writer skips a non-breaking space, so skip that character too.
                if ((mLastSubstringKey->len == pos || mLastSubstringKey->index == nIndex)
                    && mLastSubstringKey
                           == CachedGlyphsKey(outputDevice, text, mLastSubstringKey->index,
                                              mLastSubstringKey->len, nLogicWidth))
                {
                    GetLayoutGlyphs(outputDevice, text, 0, text.getLength(), nLogicWidth,
                                    layoutCache);
                    itWhole = mCachedGlyphs.find(keyWhole);
                }
                else
                    mLastSubstringKey.reset();
            }
            if (!mLastSubstringKey.has_value())
            {
                mLastSubstringKey = key;
                resetLastSubstringKey = false;
            }
        }
        if (itWhole != mCachedGlyphs.end() && itWhole->second.IsValid())
        {
            mLastSubstringKey.reset();
            mLastTemporaryGlyphs
                = makeGlyphsSubset(itWhole->second, outputDevice, text, nIndex, nLen);
            if (mLastTemporaryGlyphs.IsValid())
            {
                mLastTemporaryKey = key;
#ifdef DBG_UTIL
                std::shared_ptr<const vcl::text::TextLayoutCache> tmpLayoutCache;
                if (layoutCache == nullptr)
                {
                    tmpLayoutCache = vcl::text::TextLayoutCache::Create(text);
                    layoutCache = tmpLayoutCache.get();
                }
                // Check if the subset result really matches what we would get normally,
                // to make sure corner cases are handled well (see SalLayoutGlyphsImpl::cloneCharRange()).
                std::unique_ptr<SalLayout> layout
                    = outputDevice->ImplLayout(text, nIndex, nLen, Point(0, 0), nLogicWidth, {}, {},
                                               SalLayoutFlags::GlyphItemsOnly, layoutCache);
                assert(layout);
                checkGlyphsEqual(mLastTemporaryGlyphs, layout->GetGlyphs());
#endif
                return &mLastTemporaryGlyphs;
            }
        }
    }
    if (resetLastSubstringKey)
    {
        // Writer does non-breaking space differently (not as part of the string), so in that
        // case ignore that call and still allow finding two adjacent substrings that have
        // the non-breaking space between them.
        if (nLen != 1 || text[nIndex] != nbSpace)
            mLastSubstringKey.reset();
    }

    std::shared_ptr<const vcl::text::TextLayoutCache> tmpLayoutCache;
    if (layoutCache == nullptr)
    {
        tmpLayoutCache = vcl::text::TextLayoutCache::Create(text);
        layoutCache = tmpLayoutCache.get();
    }
    std::unique_ptr<SalLayout> layout
        = outputDevice->ImplLayout(text, nIndex, nLen, Point(0, 0), nLogicWidth, {}, {},
                                   SalLayoutFlags::GlyphItemsOnly, layoutCache);
    if (layout)
    {
        SalLayoutGlyphs glyphs = layout->GetGlyphs();
        if (glyphs.IsValid())
        {
            // TODO: Fallbacks do not work reliably (fallback font not included in the key),
            // so do not cache (but still return once, using the temporary without a key set).
            if (!mbCacheGlyphsWhenDoingFallbackFonts && glyphs.Impl(1) != nullptr)
            {
                mLastTemporaryGlyphs = std::move(glyphs);
                mLastTemporaryKey.reset();
                return &mLastTemporaryGlyphs;
            }
            mCachedGlyphs.insert(std::make_pair(key, std::move(glyphs)));
            assert(mCachedGlyphs.find(key)
                   == mCachedGlyphs.begin()); // newly inserted item is first
            return &mCachedGlyphs.begin()->second;
        }
    }
    // Failure, cache it too as invalid glyphs.
    mCachedGlyphs.insert(std::make_pair(key, SalLayoutGlyphs()));
    return nullptr;
}

const SalLayoutGlyphs* SalLayoutGlyphsCache::GetLayoutGlyphs(
    const VclPtr<const OutputDevice>& outputDevice, const OUString& text, sal_Int32 nIndex,
    sal_Int32 nLen, sal_Int32 nDrawMinCharPos, sal_Int32 nDrawEndCharPos, tools::Long nLogicWidth,
    const vcl::text::TextLayoutCache* layoutCache)
{
    // This version is used by callers that need to draw a subset of a layout. In all ordinary uses
    // this function will be called for successive glyph subsets, so should optimize for that case.
    auto* pWholeGlyphs
        = GetLayoutGlyphs(outputDevice, text, nIndex, nLen, nLogicWidth, layoutCache);
    if (nDrawMinCharPos <= nIndex && nDrawEndCharPos >= (nIndex + nLen))
    {
        return pWholeGlyphs;
    }

    if (pWholeGlyphs && pWholeGlyphs->IsValid())
    {
        mLastTemporaryKey.reset();
        mLastTemporaryGlyphs = makeGlyphsSubset(*pWholeGlyphs, outputDevice, text, nDrawMinCharPos,
                                                nDrawEndCharPos - nDrawMinCharPos);
        if (mLastTemporaryGlyphs.IsValid())
        {
            return &mLastTemporaryGlyphs;
        }
    }

    return nullptr;
}

void SalLayoutGlyphsCache::SetCacheGlyphsWhenDoingFallbackFonts(bool bOK)
{
    mbCacheGlyphsWhenDoingFallbackFonts = bOK;
    if (!bOK)
        clear();
}

SalLayoutGlyphsCache::CachedGlyphsKey::CachedGlyphsKey(
    const VclPtr<const OutputDevice>& outputDevice, OUString t, sal_Int32 i, sal_Int32 l,
    tools::Long w)
    : text(std::move(t))
    , index(i)
    , len(l)
    , logicWidth(w)
    // we also need to save things used in OutputDevice::ImplPrepareLayoutArgs(), in case they
    // change in the output device, plus mapMode affects the sizes.
    , fontMetric(outputDevice->GetFontMetric())
    // TODO It would be possible to get a better hit ratio if mapMode wasn't part of the key
    // and results that differ only in mapmode would have coordinates adjusted based on that.
    // That would occasionally lead to rounding errors (at least differences that would
    // make checkGlyphsEqual() fail).
    , mapMode(outputDevice->GetMapMode())
    , digitLanguage(outputDevice->GetDigitLanguage())
    , layoutMode(outputDevice->GetLayoutMode())
    , rtl(outputDevice->IsRTLEnabled())
{
    const LogicalFontInstance* fi = outputDevice->GetFontInstance();
    fi->GetScale(&fontScaleX, &fontScaleY);

    const vcl::font::FontSelectPattern& rFSD = fi->GetFontSelectPattern();
    disabledLigatures = rFSD.GetPitch() == PITCH_FIXED;
    artificialItalic = fi->NeedsArtificialItalic();
    artificialBold = fi->NeedsArtificialBold();

    hashValue = 0;
    o3tl::hash_combine(hashValue, vcl::text::FirstCharsStringHash()(text));
    o3tl::hash_combine(hashValue, index);
    o3tl::hash_combine(hashValue, len);
    o3tl::hash_combine(hashValue, logicWidth);
    o3tl::hash_combine(hashValue, outputDevice.get());
    // Need to use IgnoreColor, because sometimes the color changes, but it's irrelevant
    // for text layout (and also obsolete in vcl::Font).
    o3tl::hash_combine(hashValue, fontMetric.GetHashValueIgnoreColor());
    // For some reason font scale may differ even if vcl::Font is the same,
    // so explicitly check it too.
    o3tl::hash_combine(hashValue, fontScaleX);
    o3tl::hash_combine(hashValue, fontScaleY);
    o3tl::hash_combine(hashValue, mapMode.GetHashValue());
    o3tl::hash_combine(hashValue, rtl);
    o3tl::hash_combine(hashValue, disabledLigatures);
    o3tl::hash_combine(hashValue, artificialItalic);
    o3tl::hash_combine(hashValue, artificialBold);
    o3tl::hash_combine(hashValue, layoutMode);
    o3tl::hash_combine(hashValue, digitLanguage.get());

    // In case the font name is the same, but the font family differs, then the font metric won't
    // contain that custom font family, so explicitly include the font family from the output device
    // font.
    o3tl::hash_combine(hashValue, outputDevice->GetFont().GetFamilyType());
}

bool SalLayoutGlyphsCache::CachedGlyphsKey::operator==(const CachedGlyphsKey& other) const
{
    return hashValue == other.hashValue && index == other.index && len == other.len
           && logicWidth == other.logicWidth && mapMode == other.mapMode && rtl == other.rtl
           && disabledLigatures == other.disabledLigatures
           && artificialItalic == other.artificialItalic && artificialBold == other.artificialBold
           && layoutMode == other.layoutMode && digitLanguage == other.digitLanguage
           && fontScaleX == other.fontScaleX && fontScaleY == other.fontScaleY
           && fontMetric.EqualIgnoreColor(other.fontMetric)
           && vcl::text::FastStringCompareEqual()(text, other.text);
    // Slower things last in the comparison.
}

size_t SalLayoutGlyphsCache::GlyphsCost::operator()(const SalLayoutGlyphs& glyphs) const
{
    size_t cost = 0;
    for (int level = 0;; ++level)
    {
        const SalLayoutGlyphsImpl* impl = glyphs.Impl(level);
        if (impl == nullptr)
            break;
        // Count size in bytes, both the SalLayoutGlyphsImpl instance and contained GlyphItem's.
        cost += sizeof(*impl);
        cost += impl->size() * sizeof(impl->front());
    }
    return cost;
}

void SalLayoutGlyphsCache::dropCaches() { clear(); }

void SalLayoutGlyphsCache::dumpState(rtl::OStringBuffer& rState)
{
    rState.append("\nSalLayoutGlyphsCache:\t");
    rState.append(static_cast<sal_Int32>(mCachedGlyphs.size()));
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=90 H=91 G=90

¤ Dauer der Verarbeitung: 0.9 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.