Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/sd/source/ui/slidesorter/cache/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 16 kB image not shown  

Quelle  SlsBitmapCache.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 <officecfg/Office/Impress.hxx>

#include <memory>
#include <unordered_map>
#include "SlsBitmapCache.hxx"
#include "SlsCacheCompactor.hxx"
#include "SlsBitmapCompressor.hxx"

#include <sal/log.hxx>

// Define the default value for the maximal cache size that is used for
// previews that are currently not visible.  The visible previews are all
// held in memory at all times.  This default is used only when the
// configuration does not have a value.
const sal_Int32 MAXIMAL_CACHE_SIZE = 4*1024L*1024L;

using namespace ::com::sun::star::uno;

namespace sd::slidesorter::cache {

class BitmapCache::CacheEntry
{
public:
    CacheEntry(const BitmapEx& rBitmap, sal_Int32 nLastAccessTime, bool bIsPrecious);
    CacheEntry(sal_Int32 nLastAccessTime, bool bIsPrecious);
    inline void Recycle (const CacheEntry& rEntry);
    inline sal_Int32 GetMemorySize() const;
    void Compress (const std::shared_ptr<BitmapCompressor>& rpCompressor);
    inline void Decompress();

    bool IsUpToDate() const { return mbIsUpToDate; }
    void SetUpToDate (bool bIsUpToDate) { mbIsUpToDate = bIsUpToDate; }
    sal_Int32 GetAccessTime() const { return mnLastAccessTime; }
    void SetAccessTime (sal_Int32 nAccessTime) { mnLastAccessTime = nAccessTime; }

    const BitmapEx& GetPreview() const { return maPreview; }
    inline void SetPreview (const BitmapEx& rPreview);
    bool HasPreview() const;

    const BitmapEx& GetMarkedPreview() const { return maMarkedPreview; }
    inline void SetMarkedPreview (const BitmapEx& rMarkePreview);

    bool HasReplacement() const { return (mpReplacement != nullptr); }
    inline bool HasLosslessReplacement() const;
    void Invalidate() { mpReplacement.reset(); mpCompressor.reset(); mbIsUpToDate = false; }
    bool IsPrecious() const { return mbIsPrecious; }
    void SetPrecious (bool bIsPrecious) { mbIsPrecious = bIsPrecious; }

private:
    BitmapEx maPreview;
    BitmapEx maMarkedPreview;
    std::shared_ptr<BitmapReplacement> mpReplacement;
    std::shared_ptr<BitmapCompressor> mpCompressor;
    bool mbIsUpToDate;
    sal_Int32 mnLastAccessTime;
    // When this flag is set then the bitmap is not modified by a cache
    // compactor.
    bool mbIsPrecious;
};

namespace {

class CacheHash {
public:
    size_t operator()(const BitmapCache::CacheKey& p) const
    { return reinterpret_cast<size_t>(p); }
};

}

class BitmapCache::CacheBitmapContainer
    : public std::unordered_map<CacheKey, CacheEntry, CacheHash>
{
public:
    CacheBitmapContainer() {}
};

namespace {

typedef ::std::vector<
    ::std::pair< ::sd::slidesorter::cache::BitmapCache::CacheKey,
      ::sd::slidesorter::cache::BitmapCache::CacheEntry>
    > SortableBitmapContainer;

    /** Compare elements of the bitmap cache according to their last access
        time.
    */

    class AccessTimeComparator
    {
    public:
        bool operator () (
            const SortableBitmapContainer::value_type& e1,
            const SortableBitmapContainer::value_type& e2)
        {
            return e1.second.GetAccessTime() < e2.second.GetAccessTime();
        }
    };

// end of anonymous namespace

//=====  BitmapCache  =========================================================

BitmapCache::BitmapCache ()
    : mpBitmapContainer(new CacheBitmapContainer()),
      mnNormalCacheSize(0),
      mnPreciousCacheSize(0),
      mnCurrentAccessTime(0),
      mnMaximalNormalCacheSize(MAXIMAL_CACHE_SIZE),
      mbIsFull(false)
{
    mnMaximalNormalCacheSize = officecfg::Office::Impress::MultiPaneGUI::SlideSorterBar::PreviewCache::CacheSize::get();

    mpCacheCompactor = CacheCompactor::Create(*this,mnMaximalNormalCacheSize);
}

BitmapCache::~BitmapCache()
{
    Clear();
}

void BitmapCache::Clear()
{
    std::unique_lock aGuard (maMutex);

    mpBitmapContainer->clear();
    mnNormalCacheSize = 0;
    mnPreciousCacheSize = 0;
    mnCurrentAccessTime = 0;
}

bool BitmapCache::HasBitmap (const CacheKey& rKey)
{
    std::unique_lock aGuard (maMutex);

    CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
    return (iEntry != mpBitmapContainer->end()
        && (iEntry->second.HasPreview() || iEntry->second.HasReplacement()));
}

bool BitmapCache::BitmapIsUpToDate (const CacheKey& rKey)
{
    std::unique_lock aGuard (maMutex);

    bool bIsUpToDate = false;
    CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey));
    if (aIterator != mpBitmapContainer->end())
        bIsUpToDate = aIterator->second.IsUpToDate();

    return bIsUpToDate;
}

BitmapEx BitmapCache::GetBitmap (const CacheKey& rKey)
{
    std::unique_lock aGuard (maMutex);

    CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
    if (iEntry == mpBitmapContainer->end())
    {
        // Create an empty bitmap for the given key that acts as placeholder
        // until we are given the real one.  Mark it as not being up to date.
        SetBitmap(aGuard, rKey, BitmapEx(), false);
        iEntry = mpBitmapContainer->find(rKey);
        iEntry->second.SetUpToDate(false);
    }
    else
    {
        iEntry->second.SetAccessTime(mnCurrentAccessTime++);

        // Maybe we have to decompress the preview.
        if ( ! iEntry->second.HasPreview() && iEntry->second.HasReplacement())
        {
            UpdateCacheSize(aGuard, iEntry->second, REMOVE);
            iEntry->second.Decompress();
            UpdateCacheSize(aGuard, iEntry->second, ADD);
        }
    }
    return iEntry->second.GetPreview();
}

BitmapEx BitmapCache::GetMarkedBitmap (const CacheKey& rKey)
{
    std::unique_lock aGuard (maMutex);

    CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
    if (iEntry != mpBitmapContainer->end())
    {
        iEntry->second.SetAccessTime(mnCurrentAccessTime++);
        return iEntry->second.GetMarkedPreview();
    }
    else
        return BitmapEx();
}

void BitmapCache::ReleaseBitmap (const CacheKey& rKey)
{
    std::unique_lock aGuard (maMutex);

    CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey));
    if (aIterator != mpBitmapContainer->end())
    {
        UpdateCacheSize(aGuard, aIterator->second, REMOVE);
        mpBitmapContainer->erase(aIterator);
    }
}

bool BitmapCache::InvalidateBitmap (const CacheKey& rKey)
{
    std::unique_lock aGuard (maMutex);

    CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
    if (iEntry != mpBitmapContainer->end())
    {
        iEntry->second.SetUpToDate(false);

        // When there is a preview then we release the replacement.  The
        // preview itself is kept until a new one is created.
        if (iEntry->second.HasPreview())
        {
            UpdateCacheSize(aGuard, iEntry->second, REMOVE);
            iEntry->second.Invalidate();
            UpdateCacheSize(aGuard, iEntry->second, ADD);
        }
        return true;
    }
    else
        return false;
}

void BitmapCache::InvalidateCache()
{
    std::unique_lock aGuard (maMutex);

    for (auto& rEntry : *mpBitmapContainer)
    {
        rEntry.second.Invalidate();
    }
    ReCalculateTotalCacheSize(aGuard);
}

void BitmapCache::SetBitmap (
    const CacheKey& rKey,
    const BitmapEx& rPreview,
    bool bIsPrecious)
{
    std::unique_lock aGuard (maMutex);
    SetBitmap(aGuard, rKey, rPreview, bIsPrecious);
}

void BitmapCache::SetBitmap (
    std::unique_lock<std::mutex>& rGuard,
    const CacheKey& rKey,
    const BitmapEx& rPreview,
    bool bIsPrecious)
{
    CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
    if (iEntry != mpBitmapContainer->end())
    {
        UpdateCacheSize(rGuard, iEntry->second, REMOVE);
        iEntry->second.SetPreview(rPreview);
        iEntry->second.SetUpToDate(true);
        iEntry->second.SetAccessTime(mnCurrentAccessTime++);
    }
    else
    {
        iEntry = mpBitmapContainer->emplace(
            rKey,
            CacheEntry(rPreview, mnCurrentAccessTime++, bIsPrecious)
            ).first;
    }

    if (iEntry != mpBitmapContainer->end())
        UpdateCacheSize(rGuard, iEntry->second, ADD);
}

void BitmapCache::SetMarkedBitmap (
    const CacheKey& rKey,
    const BitmapEx& rPreview)
{
    std::unique_lock aGuard (maMutex);

    CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
    if (iEntry != mpBitmapContainer->end())
    {
        UpdateCacheSize(aGuard, iEntry->second, REMOVE);
        iEntry->second.SetMarkedPreview(rPreview);
        iEntry->second.SetAccessTime(mnCurrentAccessTime++);
        UpdateCacheSize(aGuard, iEntry->second, ADD);
    }
}

void BitmapCache::SetPrecious (const CacheKey& rKey, bool bIsPrecious)
{
    std::unique_lock aGuard (maMutex);

    CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
    if (iEntry != mpBitmapContainer->end())
    {
        if (iEntry->second.IsPrecious() != bIsPrecious)
        {
            UpdateCacheSize(aGuard, iEntry->second, REMOVE);
            iEntry->second.SetPrecious(bIsPrecious);
            UpdateCacheSize(aGuard, iEntry->second, ADD);
        }
    }
    else if (bIsPrecious)
    {
        iEntry = mpBitmapContainer->emplace(
            rKey,
            CacheEntry(BitmapEx(), mnCurrentAccessTime++, bIsPrecious)
            ).first;
        UpdateCacheSize(aGuard, iEntry->second, ADD);
    }
}

void BitmapCache::ReCalculateTotalCacheSize()
{
    std::unique_lock aGuard (maMutex);
    ReCalculateTotalCacheSize(aGuard);
}

void BitmapCache::ReCalculateTotalCacheSize(std::unique_lock<std::mutex>& /*rGuard*/)
{
    mnNormalCacheSize = 0;
    mnPreciousCacheSize = 0;
    for (const auto& rEntry : *mpBitmapContainer)
    {
        if (rEntry.second.IsPrecious())
            mnPreciousCacheSize += rEntry.second.GetMemorySize();
        else
            mnNormalCacheSize += rEntry.second.GetMemorySize();
    }
    mbIsFull = (mnNormalCacheSize  >= mnMaximalNormalCacheSize);

    SAL_INFO("sd.sls", __func__ << ": cache size is " << mnNormalCacheSize << "/" << mnPreciousCacheSize);
}

void BitmapCache::Recycle (const BitmapCache& rCache)
{
    std::unique_lock aGuard (maMutex);

    for (const auto& rOtherEntry : *rCache.mpBitmapContainer)
    {
        CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rOtherEntry.first));
        if (iEntry == mpBitmapContainer->end())
        {
            iEntry = mpBitmapContainer->emplace(
                rOtherEntry.first,
                CacheEntry(mnCurrentAccessTime++, true)
                ).first;
            UpdateCacheSize(aGuard, iEntry->second, ADD);
        }
        if (iEntry != mpBitmapContainer->end())
        {
            UpdateCacheSize(aGuard, iEntry->second, REMOVE);
            iEntry->second.Recycle(rOtherEntry.second);
            UpdateCacheSize(aGuard, iEntry->second, ADD);
        }
    }
}

BitmapCache::CacheIndex BitmapCache::GetCacheIndex() const
{
    std::unique_lock aGuard (maMutex);

    // Create a copy of the bitmap container.
    SortableBitmapContainer aSortedContainer;
    aSortedContainer.reserve(mpBitmapContainer->size());

    // Copy the relevant entries.
    for (const auto& rEntry : *mpBitmapContainer)
    {
        if ( rEntry.second.IsPrecious())
            continue;

        if ( ! rEntry.second.HasPreview())
            continue;

        aSortedContainer.emplace_back(rEntry.first, rEntry.second);
    }

    // Sort the remaining entries.
    ::std::sort(aSortedContainer.begin(), aSortedContainer.end(), AccessTimeComparator());

    // Return a list with the keys of the sorted entries.
    CacheIndex aIndex;
    aIndex.reserve(aSortedContainer.size());
    for (const auto& rIndexEntry : aSortedContainer)
        aIndex.push_back(rIndexEntry.first);
    return aIndex;
}

void BitmapCache::Compress (
    const CacheKey& rKey,
    const std::shared_ptr<BitmapCompressor>& rpCompressor)
{
    std::unique_lock aGuard (maMutex);

    CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey));
    if (iEntry != mpBitmapContainer->end() && iEntry->second.HasPreview())
    {
        UpdateCacheSize(aGuard, iEntry->second, REMOVE);
        iEntry->second.Compress(rpCompressor);
        UpdateCacheSize(aGuard, iEntry->second, ADD);
    }
}

void BitmapCache::UpdateCacheSize (std::unique_lock<std::mutex>& /*rGuard*/, const CacheEntry& rEntry, CacheOperation eOperation)
{
    sal_Int32 nEntrySize (rEntry.GetMemorySize());
    sal_Int32& rCacheSize (rEntry.IsPrecious() ? mnPreciousCacheSize : mnNormalCacheSize);
    switch (eOperation)
    {
        case ADD:
            rCacheSize += nEntrySize;
            if ( ! rEntry.IsPrecious() && mnNormalCacheSize>mnMaximalNormalCacheSize)
            {
                mbIsFull = true;
                SAL_INFO("sd.sls", __func__ << ": cache size is " << mnNormalCacheSize << " > " << mnMaximalNormalCacheSize);
                mpCacheCompactor->RequestCompaction();
            }
            break;

        case REMOVE:
            rCacheSize -= nEntrySize;
            if (mnNormalCacheSize < mnMaximalNormalCacheSize)
                mbIsFull = false;
            break;

        default:
            assert(false);
            break;
    }
}

//===== CacheEntry ============================================================

BitmapCache::CacheEntry::CacheEntry(
    sal_Int32 nLastAccessTime,
    bool bIsPrecious)
    : mbIsUpToDate(true),
      mnLastAccessTime(nLastAccessTime),
      mbIsPrecious(bIsPrecious)
{
}

BitmapCache::CacheEntry::CacheEntry(
    const BitmapEx& rPreview,
    sal_Int32 nLastAccessTime,
    bool bIsPrecious)
    : maPreview(rPreview),
      mbIsUpToDate(true),
      mnLastAccessTime(nLastAccessTime),
      mbIsPrecious(bIsPrecious)
{
}

inline void BitmapCache::CacheEntry::Recycle (const CacheEntry& rEntry)
{
    if ((rEntry.HasPreview() || rEntry.HasLosslessReplacement())
        && ! (HasPreview() || HasLosslessReplacement()))
    {
        maPreview = rEntry.maPreview;
        maMarkedPreview = rEntry.maMarkedPreview;
        mpReplacement = rEntry.mpReplacement;
        mpCompressor = rEntry.mpCompressor;
        mnLastAccessTime = rEntry.mnLastAccessTime;
        mbIsUpToDate = rEntry.mbIsUpToDate;
    }
}

inline sal_Int32 BitmapCache::CacheEntry::GetMemorySize() const
{
    sal_Int32 nSize (0);
    nSize += maPreview.GetSizeBytes();
    nSize += maMarkedPreview.GetSizeBytes();
    if (mpReplacement != nullptr)
        nSize += mpReplacement->GetMemorySize();
    return nSize;
}

void BitmapCache::CacheEntry::Compress (const std::shared_ptr<BitmapCompressor>& rpCompressor)
{
    if (  maPreview.IsEmpty())
        return;

    if (mpReplacement == nullptr)
    {
        mpReplacement = rpCompressor->Compress(maPreview);

#ifdef DEBUG_SD_SLSBITMAPCACHE
        sal_uInt32 nOldSize (maPreview.GetSizeBytes());
        sal_uInt32 nNewSize (mpReplacement.get()!=NULL ? mpReplacement->GetMemorySize() : 0);
        if (nOldSize == 0)
            nOldSize = 1;
        sal_Int32 nRatio (100L * nNewSize / nOldSize);
        SAL_INFO("sd.sls", __func__ << ": compressing bitmap for " << %x << " from " << nOldSize << " to " << nNewSize << " bytes (" << nRatio << "%)");
#endif

        mpCompressor = rpCompressor;
    }

    maPreview.SetEmpty();
    maMarkedPreview.SetEmpty();
}

inline void BitmapCache::CacheEntry::Decompress()
{
    if (mpReplacement != nullptr && mpCompressor != nullptr && maPreview.IsEmpty())
    {
        maPreview = mpCompressor->Decompress(*mpReplacement);
        maMarkedPreview.SetEmpty();
        if ( ! mpCompressor->IsLossless())
            mbIsUpToDate = false;
    }
}

inline void BitmapCache::CacheEntry::SetPreview (const BitmapEx& rPreview)
{
    maPreview = rPreview;
    maMarkedPreview.SetEmpty();
    mpReplacement.reset();
    mpCompressor.reset();
}

bool BitmapCache::CacheEntry::HasPreview() const
{
    return ! maPreview.IsEmpty();
}

inline void BitmapCache::CacheEntry::SetMarkedPreview (const BitmapEx& rMarkedPreview)
{
    maMarkedPreview = rMarkedPreview;
}

inline bool BitmapCache::CacheEntry::HasLosslessReplacement() const
{
    return mpReplacement != nullptr && mpCompressor != nullptr && mpCompressor->IsLossless();
}

// end of namespace ::sd::slidesorter::cache

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

Messung V0.5
C=96 H=95 G=95

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet)  ¤

*© 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.