/* * Copyright (c) 2026, Aliaksandr Kalenik * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include namespace Gfx { static constexpr u64 image_cache_max_unused_generations = 120; struct DecodedImageFrameSkiaImageCache::Impl { Impl() = default; explicit Impl(RefPtr skia_backend_context) : skia_backend_context(move(skia_backend_context)) { } struct DecodedImageFrameKeyTraits : public Traits { static unsigned hash(DecodedImageFrame const& frame) { return pair_int_hash( ptr_hash(&frame.bitmap()), ptr_hash(color_space_pointer(frame))); } static bool equals(DecodedImageFrame const& a, DecodedImageFrame const& b) { return &a.bitmap() == &b.bitmap() && color_space_pointer(a) == color_space_pointer(b); } static constexpr bool may_have_slow_equality_check() { return false; } private: static SkColorSpace const* color_space_pointer(DecodedImageFrame const& frame) { return frame.color_space().color_space>().get(); } }; struct CachedImage { sk_sp image; u64 last_used_generation { 0 }; }; RefPtr skia_backend_context; HashMap images; u64 generation { 0 }; }; DecodedImageFrameSkiaImageCache::DecodedImageFrameSkiaImageCache() : m_impl(make()) { } DecodedImageFrameSkiaImageCache::DecodedImageFrameSkiaImageCache(RefPtr skia_backend_context) : m_impl(make(move(skia_backend_context))) { } DecodedImageFrameSkiaImageCache::~DecodedImageFrameSkiaImageCache() = default; sk_sp DecodedImageFrameSkiaImageCache::image_for_frame(DecodedImageFrame const& frame) { auto const& bitmap = frame.bitmap(); if (auto it = m_impl->images.find(frame); it != m_impl->images.end()) { it->value.last_used_generation = m_impl->generation; return it->value.image; } auto raster_image = sk_image_from_bitmap(bitmap, frame.color_space()); sk_sp image; auto* gr_context = m_impl->skia_backend_context ? m_impl->skia_backend_context->sk_context() : nullptr; if (gr_context) { image = SkImages::TextureFromImage(gr_context, raster_image.get(), skgpu::Mipmapped::kNo, skgpu::Budgeted::kYes); if (!image) image = move(raster_image); } else { image = move(raster_image); } if (!image) return nullptr; Impl::CachedImage cached_image { .image = image, .last_used_generation = m_impl->generation, }; m_impl->images.set(frame, move(cached_image)); return image; } void DecodedImageFrameSkiaImageCache::prune() { m_impl->images.remove_all_matching([this](auto const&, auto const& cached_image) { return m_impl->generation - cached_image.last_used_generation > image_cache_max_unused_generations; }); ++m_impl->generation; } }