diff --git a/.agent/plans/resource_ownership_followup_plan.md b/.agent/plans/resource_ownership_followup_plan.md new file mode 100644 index 000000000..bd6a37abd --- /dev/null +++ b/.agent/plans/resource_ownership_followup_plan.md @@ -0,0 +1,61 @@ +# Resource ownership follow-up plan + +Status: proposed follow-up after Stage 7 completion, 2026-07-24. + +The shared-resource ownership migration is complete. This plan is intentionally limited to +validation, documentation, diagnostics, and compatibility-era naming cleanup. It must not reopen +the established `ResourceScope` / `ResourceCatalog` ownership model without a concrete defect. + +## 1. Scene lifetime coverage + +- Add focused tests proving resources published only into a scene scope are released when its + `UISceneNode` and `ResourceScope` are destroyed. +- Cover textures, drawables, fonts, themes, icons, and shader programs where practical. +- Verify explicitly imported catalogs remain alive only through their actual external owners. +- Assert against registry/catalog contents and retained handles, not only destructor side effects. + +## 2. Public ownership documentation + +- Audit public resource APIs and consistently document whether parameters and return values are: + owning, retaining, borrowing, or observing. +- Document the required lifetime for borrowed raw pointers returned by UI and GPU APIs. +- Keep hot-path raw pointers where ownership is established elsewhere; do not imply ownership by + converting those APIs to shared handles. +- Add short ownership examples to `ResourceScope`, `ResourceCatalog`, and family-specific services. + +## 3. Compatibility-era naming cleanup + +- Rename non-owning `ShaderProgramManager`, `VertexBufferManager`, and `FrameBufferManager` + concepts to `ShaderProgramRegistry`, `VertexBufferRegistry`, and `FrameBufferRegistry` throughout + filenames, includes, build files, and documentation. +- Review other `*Manager` names only when their current role is genuinely a registry or service. +- Keep this as an isolated public API cleanup so downstream include breakage is easy to review. + +## 4. GPU borrowed-lifetime diagnostics + +- Add debug-only validation that borrowed frame buffers, vertex buffers, shaders, and programs are + not used after their owning OpenGL context or renderer has been destroyed. +- Prefer cheap generation/context identity checks at API boundaries over reference counting in hot + rendering paths. +- Do not add OpenGL context-loss recreation support; current supported platforms do not require it. + +## 5. Static initialization audit + +- Build a Clang diagnostic configuration with `-Wglobal-constructors` and + `-Wexit-time-destructors` to identify C++ work performed before `main()` and after its return. +- Produce a linker-level inventory of `.init_array` entries for representative executables to find + constructors hidden in libraries or translation units excluded from Clang diagnostics. +- Prioritize globals that allocate memory, register callbacks/resources, depend on singleton order, + or retain graphics objects. Constant-initialized POD data is not a migration target. +- Move executable state into `main()` scopes and callback lambdas. Replace necessary library + globals with function-local statics only when process lifetime is intentional and documented. +- Track the baseline count and prevent new non-trivial global constructors in CI once existing + cases have been classified. + +## Validation + +- Run the complete unit-test suite and all normal platform CI jobs after each focused change. +- Keep `git diff --check` clean and run examples affected by shutdown-order changes under ASan. +- Confirm GPU/resource handles are destroyed before `Engine::destroySingleton()` in examples and + tools that own them locally. + diff --git a/.agent/plans/resource_shared_ownership_architecture.md b/.agent/plans/resource_shared_ownership_architecture.md index bdb69aca7..b42218b4c 100644 --- a/.agent/plans/resource_shared_ownership_architecture.md +++ b/.agent/plans/resource_shared_ownership_architecture.md @@ -1,7 +1,6 @@ # eepp shared-resource ownership architecture -Status: active implementation baseline; Stage 0, prerequisite fixes, and Stage 1 complete; -Stage 2 is next, 2026-07-15. +Status: implementation complete through Stage 7, 2026-07-24. This document freezes the contracts that must be true before the public texture API is changed. The implementation may refine names and small mechanics, but changing an invariant below requires an @@ -19,9 +18,9 @@ The final model is: reporting. It is never searched for semantic names. - Catalogs define names and persistence. - Scopes define which catalogs and typed caches are visible. -- GPU resources remain graphics-thread-affine. The project contract requires final owning releases - and destruction to run through the graphics/update lifecycle rather than supporting arbitrary - last-release threads. +- GPU resources remain graphics-thread-affine. A final owning release may happen on a worker, but + the texture deleter only performs a thread-safe handoff to TextureFactory. Actual destruction runs + through the graphics/display lifecycle. - UI drawable resolution is layered over Graphics resource lookup; browser caching and navigation remain outside Graphics. - A UISceneNode can own a scope and resolver, but neither texture lifetime nor pure Graphics usage @@ -105,10 +104,10 @@ Factories use an equivalent private helper for protected constructors. `std::mak used for tracked eepp resources unless the memory manager is redesigned to understand its combined allocation. No second control block may be created from `handle.get()`. -Texture is the deliberate exception to immediate `eeDelete`: its factory-controlled deleter queues -the final raw object for graphics-thread destruction. `TextureFactory::collectReleasedTextures()` -performs the eventual `eeDelete` after queued rendering has been flushed. This is the same deferred -destruction contract used by scene nodes; it is not a general arbitrary-thread GPU disposal system. +Texture is the deliberate exception to immediate `eeDelete`: its factory-controlled deleter may +queue the final raw object from any thread. `TextureFactory::collectReleasedTextures()` performs the +eventual `eeDelete` on the graphics thread after queued rendering has been flushed. This is the same +deferred destruction contract used by scene nodes; it is not a general GPU disposal system. ### 3.3 Identity, keys, and labels @@ -234,9 +233,11 @@ Frozen lookup rules: - Never search the live registry. - Never implicitly search a parent, host scene, sibling scene, or every live resource. - The default Graphics scope imports the global catalog explicitly. -- A UI/application scene receives only the catalogs deliberately imported into it. -- A Web document does not inherit host/global resources unless the host exports and imports them - intentionally. +- A UI/application scene imports the default resource catalog automatically for the common case; + callers can disable this at construction for strict isolation and then import only the catalogs + they deliberately expose. +- A Web document receives the same default-catalog baseline unless created with automatic import + disabled. It never implicitly inherits host, sibling, or other document-local catalogs. - Scopes import catalogs, not arbitrary scopes. This avoids recursive lookup and import cycles. Pure `EE::Graphics` users may use TextureFactory for unpinned creation or Engine's default Graphics @@ -270,14 +271,14 @@ performs the same collection explicitly because no later display is guaranteed. ### 5.1 Graphics-thread lifetime contract -GPU resources are graphics-thread-affine. Creating, mutating and finally releasing owning handles -must follow the engine's graphics/update lifecycle. `std::shared_ptr` provides ownership safety; it -does not expand eepp's supported threading contract. Async CPU decoding may run elsewhere, but -ownership handoff and final release are marshalled to the main/scene update path unless an existing -API explicitly acquires a shared GL context. +GPU operations remain graphics-context-affine and follow the existing graphics/update or explicitly +shared-context rules. Releasing the final `TexturePtr` is different: it performs no GPU operation and +may enqueue the raw texture from any thread. Only collection and actual destruction require the +graphics thread and a current context. -Debug builds should assert this contract at factory release and collection boundaries. The design -does not add a generic device state, epoch or arbitrary-thread disposal queue for unsupported usage. +Debug builds assert the collection boundary. The design does not add a generic device state, epoch, +or disposal mechanism for other GPU resource families; TextureFactory's small deferred-release queue +is the texture-specific lifetime boundary already required by batched rendering. ### 5.2 Texture deferred destruction @@ -344,34 +345,75 @@ fixed: Shared lifetime and shareable instance state are separate concerns. `isStateful()` is not a sharing contract and will not be used as one. -### 7.1 Frozen source/instance split +### 7.1 Source/instance split Resource resolution caches immutable source data. UI consumers own per-consumer drawable instances: ```cpp -using DrawableSourcePtr = ResourcePtr; using DrawablePtr = ResourcePtr; -DrawableSourcePtr DrawableResolver::findSource( const DrawableRequest& request ); +DrawablePtr Drawable::clone() const; DrawablePtr DrawableResolver::createDrawable( const DrawableRequest& request ); ``` +Stage 4 established this contract without introducing a parallel `DrawableSource` class hierarchy. +Existing drawable resource types serve as source prototypes while retained by an atlas, theme, +icon, catalog, or resolver. A prototype is never handed directly to an unrelated consumer: +`clone()` returns independently mutable presentation state while sharing underlying +texture/resource handles. This is simpler than duplicating every drawable type into source and +instance classes and remains compatible with introducing immutable source-only types later when a +concrete resource requires one. + +eepp continues to use `Drawable::Type` for runtime drawable dispatch. Generic handle conversion +checks that tag and then uses `static_pointer_cast`; cloning code for a statically known concrete +type also uses `static_pointer_cast`. The ownership migration does not introduce RTTI casts. + Representative split: - `Texture` is shared GPU/resource data, not a globally shared mutable drawable instance. -- `TextureRegionSource` contains a TexturePtr, immutable source rectangle, offset, and intrinsic size. -- `NinePatchSource` contains immutable region and border data. -- `TextureDrawable`/`TextureRegionDrawable` hold per-consumer destination size, tint, alpha, position, - and other presentation state while retaining their source. +- `TextureRegion` prototypes and instances retain a TexturePtr; instances copy rectangle, offset, + intrinsic size, destination size, tint, and position. +- `NinePatch` instances clone their nine mutable region children while sharing the textures. +- `TextureDrawable` holds per-consumer destination size, tint, alpha, and position while retaining + the shared TexturePtr. - `StateListDrawable`, `DrawableGroup`, and `Sprite` are per-consumer state machines/instances that refer to source handles or private child instances. `DrawableImageParser::createDrawable()` always returns a fresh consumer instance for CSS-generated or resolved content, even when its immutable source came from a cache. -The migration will remove draw-time mutation of shared child/source objects. Rendering APIs may use -external draw parameters where that simplifies an implementation, but no shared source can be -temporarily recolored, resized, repositioned, or advanced by a consumer. +`UIIcon`, `UIGlyphIcon`, and `UISVGIcon` expose the split directly: + +```cpp +const DrawablePtr& UIIcon::getSource( int size ) const; +DrawablePtr UIIcon::createDrawable( int size ) const; +``` + +`getSource()` supports lookup, measurement, and immediate rendering without cloning an existing +prototype. Glyph and SVG icons may materialize and cache a missing size source once. +`createDrawable()` is the explicit consumer-instance boundary for callers that retain the drawable +or need persistent independent state. + +Immediate, single-threaded render paths may borrow an icon source and temporarily change +presentation state when they restore every changed value before returning and never retain the raw +pointer. Retained widget, menu, model, animated, or otherwise independently stateful consumers must +create and own an instance. Shared child mutation remains forbidden where drawing can be reentrant +or where the complete state cannot be restored locally. + +No rendering callback may call `clone()`, `UIIcon::createDrawable()`, or an API that performs either +operation internally. It must render either a previously retained instance or a borrowed source +under the temporary-state contract above. The Stage 4 call-site audit classifies all remaining +direct `clone()` calls as: + +- implementations recursively cloning their private child state; +- constructors and setters adopting a private region/sprite/map instance; +- theme, skin, icon, CSS, and name-resolution source-to-instance boundaries; +- widget deserialization and one-time assignment; or +- focused ownership tests. + +The code editor lock icon and ecode debugger, linter, LSP breadcrumb, and autocomplete icon paths +borrow their per-size icon sources at the point of immediate rendering and restore temporary color +changes before returning. Icons assigned to widgets, menus, or models still use owned instances. ### 7.2 Consumer API @@ -526,7 +568,7 @@ focused regression coverage while preserving current raw factory ownership: - Externally executed HTTP tasks cannot retain a dangling raw Http after Pool destruction. - TextureAtlasLoader joins/stops ResourceLoader work before callback-visible loader state is destroyed. -- Engine destroys ShaderProgramManager before Renderer and clears TextLayout before FontManager. +- Engine clears TextLayout before destroying the default ResourceScope and its FontService. - Engine stops asynchronous resource producers before resource consumers and GPU managers. - UISceneNode's static async delivery queue has an explicit shutdown purge/rejection boundary. - Obsolete FrameBuffer context-loss reload APIs were removed. @@ -571,11 +613,22 @@ Exit tests: - Pending batches flush before texture collection. - Engine teardown leaves no pending released textures or unexpected live registry entries. - Repeated test-only Engine create/destroy cycles start with empty resource state. -- Wrong-thread final release triggers the documented debug contract assertion. +- Worker-thread final release only queues the texture; it does not run GL or destruction work. - `EE_MEMORY_MANAGER` accurately removes texture allocations through the factory-controlled deleter. ### Stage 2: one complete TexturePtr ownership cut +Status: complete, 2026-07-19. TextureFactory creation and acquisition APIs now return TexturePtr, +and TextureLoader exposes handle-based state with `reset()` replacing destructive `unload()` +semantics. TextureRegion, atlases/loaders, framebuffers, font pages and glyphs, nine-patches, +sprites, particle systems, SVG caches, UI image/background paths, maps, tools, tests, ecode, and +eeiv now retain texture handles. Atlas worker loads store their returned handles directly instead +of depending on later global lookup. BatchRenderer retains handle-aware submissions until flush; +its raw overload is limited to Texture's immediate draw path, whose queued object lifetime is +protected by display-time deferred destruction. Sprite's obsolete texture-owner flag and public +factory texture-removal APIs are removed. Factory-wide strong retention remains only as the +planned temporary bridge to Stage 3. + Change creation/acquisition APIs to return TexturePtr and migrate every required holder in the same repository-wide cut. During conversion, TextureFactory temporarily retains strong handles so an unclassified ignored result cannot silently expire. @@ -608,6 +661,15 @@ Exit criteria: ### Stage 3: catalog and scope ownership cutover +Status: complete, 2026-07-19. Engine now owns the global catalog and default Graphics scope; +UISceneNode owns an isolated scope that can be shared explicitly. TextureFactory is an unpinned +creator and weak live registry with no semantic name/hash lookup. Atlas, map, UI image/background, +DrawableSearcher, ecode, tests, and other name-based consumers publish to and resolve through their +explicit scope. Catalog aliases and imports provide intentional persistence and deterministic +sharing. Worker-thread final TexturePtr release is handed to the factory's thread-safe queue and +actual deletion remains display/shutdown-bound. The full cut also corrected TextureLoader's decoder +pixel allocator provenance, which asynchronous scoped loading exposed. + Implement the global catalog, default Graphics scope, application/scene catalogs, explicit imports, immutable keys, and aliases. Move intended persistent resources from temporary factory retention into catalogs/caches. Remove factory-wide strong retention and activate final unpinned creation. @@ -625,6 +687,31 @@ Exit criteria: ### Stage 4: drawable source/instance conversion +Status: complete, 2026-07-20. Drawable ownership +now uses `DrawablePtr`; textures create private +`TextureDrawable` wrappers; mutable prototypes implement `clone()`; sprites, state lists, +skins, groups, nine-patches, regions, glyphs, gradients, and primitive drawables clone their +presentation state. UIImage, UINodeDrawable, menus/icons/themes, parsers, editor/tool consumers, +maps, physics, ecode, and eeiv were migrated in the same API cut. + +`DrawableResource::Unload` and callback IDs were replaced by Change-only RAII connections. +Callback state is allocated lazily on the first connection, so ordinary drawable resources carry +no callback allocation. Callback storage and notification snapshots use small inline buffers; +snapshotting preserves safe self-disconnection and reentrant mutation during notification without +allocating in the common case. +`Variant` stores DrawablePtr outside its scalar union. UITextureRegion and ScrollParallax render +with local geometry rather than temporarily resizing shared source regions; region-based map +objects retain private instances. `DrawableSearcher` already returns fresh instances as a safe +bridge, but its replacement by the layered UI resolver remains Stage 5. + +`UIIcon::getSource()` now returns a cached source/prototype for lookup, measurement, and immediate +single-threaded rendering under the temporary-state restoration contract, while +`UIIcon::createDrawable()` explicitly creates one private consumer instance. `UIGlyphIcon` and +`UISVGIcon` cache their lazily materialized sources under the same contract. The complete `clone()` +call-site audit found no remaining render-loop cloning. The code editor, debugger, linter, LSP +breadcrumb, and autocomplete draw-only paths borrow sources directly; retained widget and menu +icons continue to own instances. + Introduce source types and per-consumer instances, remove shared draw-state mutation, replace manual ownership with DrawablePtr, remove Unload lifetime callbacks, add RAII change connections, and migrate Variant's storage. @@ -642,6 +729,18 @@ Exit criteria: ### Stage 5: layered UI resolution +Status: complete, 2026-07-21. `DrawableSearcher` was removed from Graphics. `ResourceScope` now +provides scoped drawable lookup for textures, atlas regions, nine-patches, and sprites, preserving a +pure Graphics entry point with no UI dependency. Each `UISceneNode` owns an allocation-free +`UI::DrawableResolver` that reads the scene's current scope and referer when resolving file, data, +HTTP, and named drawable references. UI images, sprites, menus, CSS parsing, and tests use the scene +resolver; callers without a scene explicitly construct one over `defaultResourceScope()`. + +CSS icon resolution now uses the requesting node's scene instead of the process-global scene. +Texture names remain isolated by local/imported catalogs and become visible across scenes only when +their scopes or catalogs are shared intentionally. Atlas, nine-patch, and sprite manager migration +to scoped catalogs remains Stage 7 work for those resource families. + Implement UI::DrawableResolver and replace DrawableSearcher. Scene resolvers delegate Graphics work to their explicit scope. Keep CSS/icon/glyph interpretation in UI and cookie/navigation concerns in Web services. @@ -655,6 +754,29 @@ Exit criteria: ### Stage 6: WebResourceCache and document leases +Status: complete, 2026-07-21. Each UISceneNode now owns a WebResourceCache document session with +an explicit cache partition and navigation generation. UIWebView advances that session when the +replacement document is installed, after the previous document has been detached. Document, +stylesheet, remote font, image, and CSS background image +requests share canonical fragment-free keys that include the partition, request method/body and +headers, resource kind, and image decode options. + +Concurrent requests coalesce into one fetch and one image decode/upload. Subscribers retain their +own document generation, so navigation or destruction removes stale delivery without cancelling a +request needed by another session. Applications can install one cache and explicit partition into +multiple WebViews to share eligible public resources; distinct partitions and content-affecting +headers remain isolated. Scene ResourceScope entries remain an explicit override but fetched Web +resources are retained only by consumers, document leases, and the cache. + +Completed entries use monotonic TTL and LRU timestamps plus a configurable byte budget. Active +document leases are not evicted; the TTL starts when the final document lease is released, allowing +Back/Forward navigation to reuse resources regardless of how long the previous document remained +open. Navigation releases only that session's previous leases, and UIWebView performs throttled +cache maintenance so expired unleased entries are collected even when no new requests complete. Failed loads +retry, redirect/final cookies are delivered only to current subscribers, and image upload is +dispatched through the scene's guarded main-thread resource queue. Common lease lists use inline +small vectors, and completed entries release starter request bodies, headers, and dispatchers. + Implement cache partitions, canonical keys/origins, per-document sessions and leases, in-flight coalescing, per-subscriber generation guards, retries, TTL/LRU, and byte budgets. Integrate WebView navigation at its existing document replacement boundary. @@ -670,12 +792,44 @@ Exit criteria: ### Stage 7: remaining resource families -Migrate fonts, font faces/fallback caches, themes, shader programs/shaders, nine-patch catalogs, -atlas managers, and every remaining raw-owning ResourceManager subclass one family at a time. Their -self-contained GPU objects retain the established graphics-thread destruction contract unless a -concrete migration requires otherwise. +Status: complete, 2026-07-24. Nine-patches and texture atlases are migrated. `NinePatch::New()`, +`TextureRegion::New()`, and `TextureAtlas::New()` return handles. Atlases retain region and texture +handles; loaders retain and publish atlas handles; theme-owned catalogs retain their named atlas +sources; and scene `ResourceScope` imports make those sources visible intentionally. +`NinePatchManager`, `TextureAtlasManager`, and `GlobalTextureAtlas` were removed. Removing a catalog +entry releases only catalog ownership and leaves separately retained consumers valid. -Remove raw-owning `ResourceManager` only when no subclass or consumer depends on it. +This is the required pattern for the remaining process-wide singleton resource managers. A +singleton must not be modernized into another process-global semantic namespace. Each family moves +to ordinary catalogs owned by its application, scene, theme, document, or other natural lifetime +boundary. `globalResourceCatalog()` is reserved for resources deliberately published process-wide; +scene scopes see non-global resources only through their local catalog or explicit imports. + +Migrate fonts, font faces/fallback caches, themes/icons, shader programs/shaders, and every remaining +raw-owning ResourceManager subclass one family at a time. Their self-contained GPU objects retain +the established graphics-thread destruction contract unless a concrete migration requires +otherwise. + +For fonts, ownership is separate from rendering policy. Font handles and semantic lookup live in +naturally owned catalogs: application defaults use the default scope, while author `@font-face` +resources are owned by their document scene. Every `ResourceScope` owns an inline `FontService` for +its rendering configuration, emoji fonts, configured fallbacks, and system fallback cache. Fonts +retain only a borrowed service pointer while published in that scope and are detached when removed +or when the scope is destroyed. Raw `Font*` values in text/layout/style structures remain borrowed +views whose enclosing application, scene, theme, or fallback service retains the corresponding +handle. The former process-wide `FontManager` singleton and compatibility namespace were removed. +Publishing a font with an existing local key replaces that catalog binding; the legacy manager +behavior that silently suffixed duplicate font names is intentionally not preserved. + +Themes, skins, icon themes, and icons now use shared resource handles. Each scene's +`UIThemeManager` owns its themes and imports their catalogs into that scene's scope; it is not a +process-wide semantic namespace. Shader and shader-program factories return handles, programs own +their constituent shaders, and catalogs/scopes can publish shader programs under semantic keys. +`ShaderProgramRegistry`, `VertexBufferRegistry`, and `FrameBufferRegistry` remain process-wide only +as non-owning inventories of live OpenGL-context objects used for reload and diagnostics. + +The raw-owning `ResourceManager` and `ResourceManagerMulti` templates were removed after their +last consumers migrated. ## 11. Required validation matrix @@ -692,7 +846,7 @@ Remove raw-owning `ResourceManager` only when no subclass or consumer depends ### GPU/thread lifetime - Final texture release on the graphics thread queues rather than immediately deleting. -- Wrong-thread final release is detected as a project-contract violation in debug builds. +- Worker-thread final release safely queues; display performs the destruction with a current context. - Display flushes batches before collecting released textures under the current context. - Engine shutdown performs a final collection before TextureFactory/Renderer/context destruction. - No deletion/callback occurs while registry/cache locks are held. @@ -740,8 +894,8 @@ Remove raw-owning `ResourceManager` only when no subclass or consumer depends ## 12. Next implementation deliverable -Stage 1 is complete, including removal of the obsolete context-recovery APIs and TextureLoader -callback registry, weak UITextureViewer observation, and display/shutdown texture collection. The -next coding deliverable is the complete Stage 2 TexturePtr ownership cut described above: change -the public APIs and migrate every texture holder while temporary factory retention keeps the -repository behavior stable. +The ownership migration is complete through Stage 7. Follow-up work is validation and API polish: +expand scene-lifetime and repeated-Engine coverage, audit public ownership documentation, add +debug-only borrowed GPU lifetime checks, audit non-trivial static initialization, and rename +compatibility-era manager filenames when the public include transition is scheduled. OpenGL +context-loss recreation is explicitly outside the supported lifecycle contract. diff --git a/.ecode/project_build.json b/.ecode/project_build.json index 1a5edc961..7369a4c91 100644 --- a/.ecode/project_build.json +++ b/.ecode/project_build.json @@ -387,6 +387,18 @@ "command": "${project_root}/bin/eepp-ui-font-picker-debug", "name": "eepp-ui-font-picker-debug", "working_dir": "${project_root}/bin" + }, + { + "args": "", + "command": "${project_root}/bin/eepp-TextureAtlasEditor-debug", + "name": "eepp-TextureAtlasEditor-debug", + "working_dir": "${project_root}/bin" + }, + { + "args": "", + "command": "${project_root}/bin/eepp-sprites-debug", + "name": "eepp-sprites-debug", + "working_dir": "${project_root}/bin" } ], "var": { diff --git a/.github/workflows/eepp-emscripten-build-check.yml b/.github/workflows/eepp-emscripten-build-check.yml new file mode 100644 index 000000000..8f45fdb48 --- /dev/null +++ b/.github/workflows/eepp-emscripten-build-check.yml @@ -0,0 +1,37 @@ +name: Emscripten + +on: [push, pull_request] + +env: + EM_VERSION: 4.0.1 + EM_CACHE_FOLDER: emsdk-cache + +jobs: + Emscripten: + runs-on: ubuntu-24.04 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + ref: ${{ github.ref }} + fetch-depth: 2 + - name: Checkout submodules + run: | + git submodule update --init --recursive + - name: Install Premake + run: | + wget https://cdn.ensoft.dev/eepp-assets/premake-5.0.0-beta6-linux.tar.gz + tar xvzf premake-5.0.0-beta6-linux.tar.gz + - name: Cache Emscripten system libraries + uses: actions/cache@v5 + with: + path: ${{ env.EM_CACHE_FOLDER }} + key: emscripten-${{ env.EM_VERSION }}-${{ runner.os }} + - name: Setup Emscripten + uses: emscripten-core/setup-emsdk@v15 + with: + version: ${{ env.EM_VERSION }} + actions-cache-folder: ${{ env.EM_CACHE_FOLDER }} + - name: Build + run: | + projects/emscripten/make.sh config=release_wasm32 diff --git a/README.md b/README.md index 579cfe5da..23dd5c0de 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ framework heavily focused on the development of rich graphical user interfaces. [![macOS status](https://img.shields.io/github/actions/workflow/status/SpartanJ/eepp/eepp-macos-build-check.yml?branch=develop&label=macOS)](https://github.com/SpartanJ/eepp/actions?query=workflow%3AmacOS) [![iOS status](https://img.shields.io/github/actions/workflow/status/SpartanJ/eepp/eepp-ios-build-check.yml?branch=develop&label=iOS)](https://github.com/SpartanJ/eepp/actions?query=workflow%3AiOS) [![Android status](https://img.shields.io/github/actions/workflow/status/SpartanJ/eepp/eepp-android-build-check.yml?branch=develop&label=Android)](https://github.com/SpartanJ/eepp/actions?query=workflow%3AAndroid) +[![emscripten status](https://img.shields.io/github/actions/workflow/status/SpartanJ/eepp/eepp-emscripten-build-check.yml?branch=develop&label=emscripten)](https://github.com/SpartanJ/eepp/actions?query=workflow%3Aemscripten) ## Features diff --git a/bin/assets/ui/breeze.css b/bin/assets/ui/breeze.css index 0e03ea909..93cad5519 100644 --- a/bin/assets/ui/breeze.css +++ b/bin/assets/ui/breeze.css @@ -56,14 +56,11 @@ TabWidget { } MarkdownView { + color: var(--font); background-color: var(--list-back); padding: 4dp; } -MarkdownView body { - color: var(--font); -} - MarkdownView p, MarkdownView ol, MarkdownView ul, diff --git a/include/eepp/core/lrucache.hpp b/include/eepp/core/lrucache.hpp index 8f0153bff..64bbd1ea0 100644 --- a/include/eepp/core/lrucache.hpp +++ b/include/eepp/core/lrucache.hpp @@ -68,6 +68,17 @@ class DynamicLRU { mCacheMap.clear(); } + template void eraseIf( Predicate predicate ) { + for ( auto it = mCacheList.begin(); it != mCacheList.end(); ) { + if ( predicate( it->first, it->second ) ) { + mCacheMap.erase( it->first ); + it = mCacheList.erase( it ); + } else { + ++it; + } + } + } + [[nodiscard]] std::size_t size() const { return mCacheList.size(); } }; @@ -166,6 +177,19 @@ class StaticLRU { used_ = 0; } + template void eraseIf( Predicate predicate ) { + std::vector> retained; + retained.reserve( used_ ); + for ( std::uint16_t idx = head_; idx < N; idx = next_[idx] ) { + if ( !predicate( keys_[idx], vals_[idx] ) ) + retained.emplace_back( std::move( keys_[idx] ), std::move( vals_[idx] ) ); + } + + clear(); + for ( auto it = retained.rbegin(); it != retained.rend(); ++it ) + put( std::move( it->first ), std::move( it->second ) ); + } + [[nodiscard]] std::size_t size() const noexcept { return used_; } private: @@ -283,6 +307,9 @@ class LRUCache { std::optional get( KeyParamT key ) { return impl_.get( key ); } void put( KeyT key, ValueT value ) { impl_.put( std::move( key ), std::move( value ) ); } void clear() { impl_.clear(); } + template void eraseIf( Predicate predicate ) { + impl_.eraseIf( std::move( predicate ) ); + } static constexpr std::size_t capacity() { return Capacity; } [[nodiscard]] std::size_t size() const { return impl_.size(); } diff --git a/include/eepp/graphics.hpp b/include/eepp/graphics.hpp index 4cd5df98a..9558f5751 100644 --- a/include/eepp/graphics.hpp +++ b/include/eepp/graphics.hpp @@ -10,23 +10,20 @@ #include #include #include -#include #include #include #include -#include +#include #include #include #include #include #include #include -#include #include #include #include #include -#include #include #include #include @@ -46,6 +43,8 @@ #include #include #include +#include +#include #include #include #include @@ -65,7 +64,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/eepp/graphics/arcdrawable.hpp b/include/eepp/graphics/arcdrawable.hpp index 470c0103b..6d339b4c6 100644 --- a/include/eepp/graphics/arcdrawable.hpp +++ b/include/eepp/graphics/arcdrawable.hpp @@ -30,6 +30,8 @@ class EE_API ArcDrawable : public PrimitiveDrawable { virtual bool isStateful() { return false; } + DrawablePtr clone() const; + Float getRadius() const; void setRadius( const Float& radius ); diff --git a/include/eepp/graphics/batchrenderer.hpp b/include/eepp/graphics/batchrenderer.hpp index b799f7b63..469b62a55 100644 --- a/include/eepp/graphics/batchrenderer.hpp +++ b/include/eepp/graphics/batchrenderer.hpp @@ -43,6 +43,10 @@ class EE_API BatchRenderer { void setTexture( const Texture* texture, Texture::CoordinateType coordinateType = Texture::CoordinateType::Normalized ); + /** Retains the texture until the currently queued batch has been rendered. */ + void setTexture( const TexturePtr& texture, + Texture::CoordinateType coordinateType = Texture::CoordinateType::Normalized ); + /** Set the predefined blending function to use on the batch */ void setBlendMode( const BlendMode& blend ); @@ -315,7 +319,10 @@ class EE_API BatchRenderer { VertexData* mTVertex{ nullptr }; unsigned int mNumVertex{ 0 }; + // Borrowed draw view. Handle-aware submissions retain mTextureOwner; Texture::draw() borrows + // under the contract that released textures are collected only after this batch is flushed. const Texture* mTexture{ nullptr }; + TexturePtr mTextureOwner; BlendMode mBlend{ BlendMode::Alpha() }; Vector2f mTexCoord[4]{ Vector2f::Zero, Vector2f::Zero, Vector2f::Zero, Vector2f::Zero }; diff --git a/include/eepp/graphics/circledrawable.hpp b/include/eepp/graphics/circledrawable.hpp index a85071ed9..a2dca7fde 100644 --- a/include/eepp/graphics/circledrawable.hpp +++ b/include/eepp/graphics/circledrawable.hpp @@ -14,6 +14,8 @@ class EE_API CircleDrawable : public ArcDrawable { CircleDrawable(); CircleDrawable( const Float& radius, const Uint32& segmentsCount ); + + DrawablePtr clone() const; }; }} // namespace EE::Graphics diff --git a/include/eepp/graphics/convexshapedrawable.hpp b/include/eepp/graphics/convexshapedrawable.hpp index fc945faff..fc6c665f7 100644 --- a/include/eepp/graphics/convexshapedrawable.hpp +++ b/include/eepp/graphics/convexshapedrawable.hpp @@ -24,6 +24,8 @@ class EE_API ConvexShapeDrawable : public PrimitiveDrawable { virtual bool isStateful() { return false; } + DrawablePtr clone() const; + void setPolygon( const Polygon2f& polygon ); void addPoint( const Vector2f& point ); diff --git a/include/eepp/graphics/drawable.hpp b/include/eepp/graphics/drawable.hpp index 2b5e4cfd8..27c623166 100644 --- a/include/eepp/graphics/drawable.hpp +++ b/include/eepp/graphics/drawable.hpp @@ -2,6 +2,7 @@ #define EE_GRAPHICS_DRAWABLE_HPP #include +#include #include #include #include @@ -10,12 +11,17 @@ using namespace EE::System; namespace EE { namespace Graphics { +class Drawable; class StatefulDrawable; +using DrawablePtr = ResourcePtr; +using DrawableWeakPtr = ResourceWeakPtr; + class EE_API Drawable { public: enum Type { TEXTURE, + TEXTUREDRAWABLE, TEXTUREREGION, SPRITE, ARC, @@ -57,6 +63,10 @@ class EE_API Drawable { virtual bool isStateful() = 0; + /** Creates an independently mutable instance backed by the same immutable resource data. + * This is an ownership/setup operation; rendering loops must retain and reuse the result. */ + virtual DrawablePtr clone() const; + void setAlpha( Uint8 alpha ); const Uint8& getAlpha(); diff --git a/include/eepp/graphics/drawablegroup.hpp b/include/eepp/graphics/drawablegroup.hpp index cbdc45d01..e1ba8c80c 100644 --- a/include/eepp/graphics/drawablegroup.hpp +++ b/include/eepp/graphics/drawablegroup.hpp @@ -8,7 +8,7 @@ namespace EE { namespace Graphics { class EE_API DrawableGroup : public Drawable { public: - static DrawableGroup* New(); + static ResourcePtr New(); DrawableGroup(); @@ -26,9 +26,11 @@ class EE_API DrawableGroup : public Drawable { virtual bool isStateful() { return false; } + DrawablePtr clone() const; + void clearDrawables(); - Drawable* addDrawable( Drawable* drawable ); + DrawablePtr addDrawable( DrawablePtr drawable ); Uint32 getDrawableCount() const; @@ -36,19 +38,14 @@ class EE_API DrawableGroup : public Drawable { void setClipEnabled( bool clipEnabled ); - bool isDrawableOwner() const; - - void setDrawableOwner( bool drawableOwner ); - - std::vector& getGroup(); + std::vector& getGroup(); protected: - std::vector mGroup; + std::vector mGroup; std::vector mPos; Sizef mSize; bool mNeedsUpdate; bool mClipEnabled; - bool mDrawableOwner; virtual void onPositionChange(); diff --git a/include/eepp/graphics/drawableresource.hpp b/include/eepp/graphics/drawableresource.hpp index e68a48194..9a2754284 100644 --- a/include/eepp/graphics/drawableresource.hpp +++ b/include/eepp/graphics/drawableresource.hpp @@ -2,17 +2,45 @@ #define EE_GRAPHICS_DRAWABLERESOURCE_HPP #include +#include #include +#include namespace EE { namespace Graphics { +class DrawableResource; + +struct DrawableResourceCallbackState { + using Callback = std::function; + Uint32 nextId{ 0 }; + SmallVector, 2> callbacks; +}; + +class EE_API DrawableResourceConnection { + public: + DrawableResourceConnection() = default; + ~DrawableResourceConnection(); + DrawableResourceConnection( DrawableResourceConnection&& other ) noexcept; + DrawableResourceConnection& operator=( DrawableResourceConnection&& other ) noexcept; + DrawableResourceConnection( const DrawableResourceConnection& ) = delete; + DrawableResourceConnection& operator=( const DrawableResourceConnection& ) = delete; + + void disconnect(); + explicit operator bool() const; + + private: + friend class DrawableResource; + DrawableResourceConnection( std::weak_ptr state, Uint32 id ); + + std::weak_ptr mState; + Uint32 mId{ 0 }; +}; + class EE_API DrawableResource : public Drawable { public: - enum Event { Change, Unload }; - virtual ~DrawableResource(); - typedef std::function OnResourceChangeCallback; + using OnResourceChangeCallback = DrawableResourceCallbackState::Callback; /** @return The DrawableResource Id. The Id is the String::hash of the name. */ const String::HashType& getId() const; @@ -26,19 +54,13 @@ class EE_API DrawableResource : public Drawable { /** Always true */ bool isDrawableResource() const; - /** Push a new on resource change callback. - * @return The Callback Id - */ - Uint32 pushResourceChangeCallback( const OnResourceChangeCallback& cb ); - - /** Pop the on resource change callback id indicated. */ - bool popResourceChangeCallback( const Uint32& callbackId ); + /** Connects a callback for mutable resource data changes. */ + DrawableResourceConnection connectResourceChange( OnResourceChangeCallback cb ); protected: std::string mName; String::HashType mId; - Uint32 mNumCallBacks; - UnorderedMap mCallbacks; + std::shared_ptr mCallbackState; explicit DrawableResource( Type drawableType ); @@ -48,7 +70,7 @@ class EE_API DrawableResource : public Drawable { virtual void onResourceChange(); - void sendEvent( const Event& event ); + void sendResourceChanged(); }; }} // namespace EE::Graphics diff --git a/include/eepp/graphics/drawablesearcher.hpp b/include/eepp/graphics/drawablesearcher.hpp deleted file mode 100644 index 81efb4da8..000000000 --- a/include/eepp/graphics/drawablesearcher.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef EE_GRAPHICS_DRAWABLEMANAGER_HPP -#define EE_GRAPHICS_DRAWABLEMANAGER_HPP - -#include -#include -#include - -namespace EE { namespace Graphics { - -class EE_API DrawableSearcher { - public: - static Drawable* searchByName( const std::string& name, bool firstSearchSprite = false, - Network::URI referer = "" ); - - static Drawable* searchById( const Uint32& id ); - - static void setPrintWarnings( const bool& print ); - - static bool getPrintWarnings(); - - protected: - static bool sPrintWarnings; -}; - -}} // namespace EE::Graphics - -#endif diff --git a/include/eepp/graphics/font.hpp b/include/eepp/graphics/font.hpp index 92bd6b478..4283e1a21 100644 --- a/include/eepp/graphics/font.hpp +++ b/include/eepp/graphics/font.hpp @@ -11,6 +11,8 @@ using namespace std::literals; namespace EE { namespace Graphics { class Font; +using FontPtr = ResourcePtr; +using FontWeakPtr = ResourceWeakPtr; struct EE_API Glyph { Float advance{ 0 }; ///< Offset to move horizontally to the next character @@ -135,6 +137,10 @@ class EE_API Font { virtual Glyph getGlyph( Uint32 codePoint, unsigned int characterSize, bool bold, bool italic, Float outlineThickness = 0 ) const = 0; + /** Returns the horizontal advance without requiring a renderable glyph texture. */ + virtual Float getGlyphAdvance( Uint32 codePoint, unsigned int characterSize, bool bold = false, + bool italic = false, Float outlineThickness = 0 ) const; + /** @return The glyph drawable that represents the glyph in a texture. The glyph drawable * allocation is managed by the font. */ virtual GlyphDrawable* getGlyphDrawable( Uint32 codePoint, unsigned int characterSize, @@ -156,7 +162,7 @@ class EE_API Font { virtual Float getUnderlineThickness( unsigned int characterSize ) const = 0; - virtual Texture* getTexture( unsigned int characterSize ) const = 0; + virtual const TexturePtr& getTexture( unsigned int characterSize ) const = 0; virtual Uint32 getFontStyle() const; diff --git a/include/eepp/graphics/fontbmfont.hpp b/include/eepp/graphics/fontbmfont.hpp index 36489185c..b2e5c355a 100644 --- a/include/eepp/graphics/fontbmfont.hpp +++ b/include/eepp/graphics/fontbmfont.hpp @@ -12,12 +12,20 @@ class IOStream; namespace EE { namespace Graphics { +class FontBMFont; +class ResourceScope; +using FontBMFontPtr = ResourcePtr; +using FontBMFontWeakPtr = ResourceWeakPtr; + /** @brief Implementation of AngelCode BMFont fonts. */ class EE_API FontBMFont : public Font { public: - static FontBMFont* New( const std::string fontName ); + static FontBMFontPtr New( const std::string fontName ); + static FontBMFontPtr New( const std::string fontName, ResourceScope& resourceScope ); - static FontBMFont* New( const std::string fontName, const std::string& filename ); + static FontBMFontPtr New( const std::string fontName, const std::string& filename ); + static FontBMFontPtr New( const std::string fontName, const std::string& filename, + ResourceScope& resourceScope ); ~FontBMFont(); @@ -37,7 +45,10 @@ class EE_API FontBMFont : public Font { const Font::Info& getInfo() const; Glyph getGlyph( Uint32 codePoint, unsigned int characterSize, bool bold, bool italic, - Float outlineThickness = 0 ) const; + Float outlineThickness = 0 ) const; + + Float getGlyphAdvance( Uint32 codePoint, unsigned int characterSize, bool bold = false, + bool italic = false, Float outlineThickness = 0 ) const; GlyphDrawable* getGlyphDrawable( Uint32 codePoint, unsigned int characterSize, bool bold = false, bool italic = false, @@ -54,7 +65,7 @@ class EE_API FontBMFont : public Font { Float getUnderlineThickness( unsigned int characterSize ) const; - Texture* getTexture( unsigned int characterSize ) const; + const TexturePtr& getTexture( unsigned int characterSize ) const; bool loaded() const; @@ -71,8 +82,8 @@ class EE_API FontBMFont : public Font { GlyphTable glyphs; ///< Table mapping code points to their corresponding glyph GlyphDrawableTable - drawables; ///> Table mapping code points to their corresponding glyph drawables. - Texture* texture; ///< Texture containing the pixels of the glyphs + drawables; ///> Table mapping code points to their corresponding glyph drawables. + TexturePtr texture; ///< Texture containing the pixels of the glyphs }; void cleanup(); diff --git a/include/eepp/graphics/fontfamily.hpp b/include/eepp/graphics/fontfamily.hpp index 734296210..9539d37b5 100644 --- a/include/eepp/graphics/fontfamily.hpp +++ b/include/eepp/graphics/fontfamily.hpp @@ -14,8 +14,8 @@ class EE_API FontFamily { const std::string& ext, const std::vector& names ); - static FontTrueType* setFont( FontTrueType* font, const std::string& fontpath, - const std::string_view& fontType ); + static void setFont( FontTrueType* font, const std::string& fontpath, + const std::string_view& fontType ); }; }} // namespace EE::Graphics diff --git a/include/eepp/graphics/fontmanager.hpp b/include/eepp/graphics/fontmanager.hpp deleted file mode 100644 index 59624f305..000000000 --- a/include/eepp/graphics/fontmanager.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef EE_GRAPHICSCFONTMANAGER_HPP -#define EE_GRAPHICSCFONTMANAGER_HPP - -#include -#include - -#include -#include -using namespace EE::System; - -namespace EE { namespace Graphics { - -class FontTrueType; -struct FontDesc; - -/** @brief The Font Manager is a singleton class that manages all the instance of fonts - instantiated. And releases the font instances automatically. So the user doesn't need to release - any font instance. -*/ -class EE_API FontManager : public ResourceManager { - SINGLETON_DECLARE_HEADERS( FontManager ) - - public: - virtual ~FontManager(); - - /** @brief Adds a new font to the manager */ - Graphics::Font* add( Graphics::Font* Font ); - - Font* getColorEmojiFont() const; - - void setColorEmojiFont( Graphics::Font* font ); - - Font* getEmojiFont() const; - - void setEmojiFont( Font* newEmojiFont ); - - const std::vector& getFallbackFonts() const; - - bool hasFallbackFonts() const; - - bool addFallbackFont( Font* fallbackFont ); - - bool removeFallbackFont( Font* fallbackFont ); - - FontHinting getHinting() const; - - void setHinting( FontHinting hinting ); - - FontAntialiasing getAntialiasing() const; - - void setAntialiasing( FontAntialiasing antialiasing ); - - Font* getByInternalId( Uint32 internalId ) const; - - FontTrueType* getOrLoadSystemFallbackFont( const FontDesc& desc ); - - protected: - Font* mColorEmojiFont{ nullptr }; - Font* mEmojiFont{ nullptr }; - std::vector mFallbackFonts; - std::vector mSystemFallbackFonts; - FontHinting mHinting{ FontHinting::Full }; - FontAntialiasing mAntialiasing{ FontAntialiasing::Grayscale }; - - FontManager(); -}; - -}} // namespace EE::Graphics - -#endif diff --git a/include/eepp/graphics/fontservice.hpp b/include/eepp/graphics/fontservice.hpp new file mode 100644 index 000000000..d211aa614 --- /dev/null +++ b/include/eepp/graphics/fontservice.hpp @@ -0,0 +1,139 @@ +#ifndef EE_GRAPHICS_FONTSERVICE_HPP +#define EE_GRAPHICS_FONTSERVICE_HPP + +#include +#include + +namespace EE { namespace Graphics { + +class FontTrueType; +class ResourceScope; +struct FontDesc; + +/** + * @brief Owns font fallback state and rendering policy for a ResourceScope. + * + * Every ResourceScope has one FontService. Fonts published locally by that scope are associated + * with its service, allowing glyph lookup to resolve configured emoji, explicit fallback, and + * system fallback fonts without consulting global state. + * + * The service strongly owns configured emoji and fallback fonts. Raw-pointer setters only accept + * fonts that can be resolved through the associated scope; passing an unrelated pointer clears or + * ignores the corresponding configuration. Removing a locally published font from the scope also + * removes any service references to it. + * + * Rendering policy changes are applied to TrueType fonts associated with this service. Imported + * fonts remain associated with the service of their owning scope and are therefore not mutated. + * A FontTrueType stores one borrowed FontService pointer, so the same FontTrueType instance must + * not be published locally into multiple scopes: the latest publication would replace its service + * association. Share such a font by importing its owning catalog instead. A future design may move + * fallback resolution entirely out of the shared font object and remove this restriction. + * + * System fonts can be loaded with two different lifetime contracts: + * - loadSystemFont() returns an independently owned, uncached font. + * - getOrLoadSystemFallbackFont() retains the font in this service for repeated fallback lookup. + */ +class EE_API FontService { + friend class ResourceScope; + + public: + /** Creates a service associated with @p resourceScope. ResourceScope owns its service. */ + explicit FontService( ResourceScope& resourceScope ); + + /** @return The resource scope whose fonts and policy are managed by this service. */ + ResourceScope& getResourceScope() const; + + /** @return The configured color emoji font, or nullptr when none is configured. */ + Font* getColorEmojiFont() const; + + /** + * Sets the color emoji font. The font must be resolvable through the associated scope. Passing + * nullptr or an unrelated font clears the current value. + */ + void setColorEmojiFont( Font* font ); + + /** @return The configured monochrome emoji font, or nullptr when none is configured. */ + Font* getEmojiFont() const; + + /** + * Sets the monochrome emoji font. The font must be resolvable through the associated scope. + * Passing nullptr or an unrelated font clears the current value. + */ + void setEmojiFont( Font* font ); + + /** @return The strongly owned explicit fallback fonts in lookup order. */ + const std::vector& getFallbackFonts() const; + + /** @return Whether at least one explicit fallback font is configured. */ + bool hasFallbackFonts() const; + + /** Adds an owning fallback handle unless the same font is already configured. */ + bool addFallbackFont( FontPtr fallbackFont ); + + /** + * Adds a fallback font resolved through the associated scope. + * @return True when the font was found and added; false otherwise. + */ + bool addFallbackFont( Font* fallbackFont ); + + /** Removes the matching explicit fallback font without removing it from its resource scope. */ + bool removeFallbackFont( Font* fallbackFont ); + + /** @return The policy used when loading and updating associated TrueType fonts. */ + FontHinting getHinting() const; + + /** Updates the hinting policy and applies it to associated non-emoji TrueType fonts. */ + void setHinting( FontHinting hinting ); + + /** @return The antialiasing policy used by associated TrueType fonts. */ + FontAntialiasing getAntialiasing() const; + + /** Updates the antialiasing policy and applies it to associated non-emoji TrueType fonts. */ + void setAntialiasing( FontAntialiasing antialiasing ); + + /** + * Finds a TrueType font visible through the associated scope by its runtime internal ID. + * @return A borrowed pointer, or nullptr when no matching font is visible. + */ + Font* getByInternalId( Uint32 internalId ) const; + + /** + * Loads a standalone system font described by @p desc. + * + * The font is not published into the associated scope and is not retained by this service. The + * returned handle is its sole owner and can be released independently, immediately freeing its + * glyph pages when no other handle exists. Current hinting and antialiasing policy is applied + * at load time. The standalone font does not use this service for subsequent fallback + * resolution. + * + * @return An owning handle, or an empty handle when the descriptor cannot be loaded. + */ + ResourcePtr loadSystemFont( const FontDesc& desc ); + + /** + * Finds or loads a system fallback font described by @p desc. + * + * Successfully loaded fonts are published into the associated scope and strongly retained by + * the service for future glyph fallback requests. The returned pointer is borrowed and remains + * valid until the font is removed from the scope or the service is destroyed. + * + * @return A borrowed pointer to the cached font, or nullptr when loading fails. + */ + FontTrueType* getOrLoadSystemFallbackFont( const FontDesc& desc ); + + private: + FontPtr findHandle( Font* font ) const; + void onFontRemoved( Font* font ); + + ResourceScope& mResourceScope; + FontPtr mColorEmojiFont; + FontPtr mEmojiFont; + std::vector mFallbackFonts; + std::vector mSystemFallbackFonts; + FontHinting mHinting{ FontHinting::Full }; + FontAntialiasing mAntialiasing{ FontAntialiasing::Grayscale }; +}; + +}} // namespace EE::Graphics + +#endif diff --git a/include/eepp/graphics/fontsprite.hpp b/include/eepp/graphics/fontsprite.hpp index 39cb8b06d..4200b9098 100644 --- a/include/eepp/graphics/fontsprite.hpp +++ b/include/eepp/graphics/fontsprite.hpp @@ -13,12 +13,20 @@ class IOStream; namespace EE { namespace Graphics { +class FontSprite; +class ResourceScope; +using FontSpritePtr = ResourcePtr; +using FontSpriteWeakPtr = ResourceWeakPtr; + /** @brief Implementation of XNA Font Sprites */ class EE_API FontSprite : public Font { public: - static FontSprite* New( const std::string fontName ); + static FontSpritePtr New( const std::string fontName ); + static FontSpritePtr New( const std::string fontName, ResourceScope& resourceScope ); - static FontSprite* New( const std::string fontName, const std::string& filename ); + static FontSpritePtr New( const std::string fontName, const std::string& filename ); + static FontSpritePtr New( const std::string fontName, const std::string& filename, + ResourceScope& resourceScope ); ~FontSprite(); @@ -58,7 +66,7 @@ class EE_API FontSprite : public Font { Float getUnderlineThickness( unsigned int characterSize ) const; - Texture* getTexture( unsigned int characterSize ) const; + const TexturePtr& getTexture( unsigned int characterSize ) const; bool loaded() const; @@ -75,8 +83,8 @@ class EE_API FontSprite : public Font { GlyphTable glyphs; ///< Table mapping code points to their corresponding glyph GlyphDrawableTable - drawables; ///> Table mapping code points to their corresponding glyph drawables. - Texture* texture; ///< Texture containing the pixels of the glyphs + drawables; ///> Table mapping code points to their corresponding glyph drawables. + TexturePtr texture; ///< Texture containing the pixels of the glyphs }; void cleanup(); diff --git a/include/eepp/graphics/fonttruetype.hpp b/include/eepp/graphics/fonttruetype.hpp index fc2f21f8e..028c908f6 100644 --- a/include/eepp/graphics/fonttruetype.hpp +++ b/include/eepp/graphics/fonttruetype.hpp @@ -15,15 +15,26 @@ namespace EE { namespace Graphics { enum class FontWeight : Uint16; struct FontDesc; +class ResourceScope; + +class FontTrueType; +class FontService; +using FontTrueTypePtr = ResourcePtr; +using FontTrueTypeWeakPtr = ResourceWeakPtr; class EE_API FontTrueType : public Font { public: - static FontTrueType* New( const std::string& FontName ); + static FontTrueTypePtr New( const std::string& FontName ); + static FontTrueTypePtr New( const std::string& FontName, ResourceScope& resourceScope ); - static FontTrueType* New( const std::string& FontName, const std::string& filename ); + static FontTrueTypePtr New( const std::string& FontName, const std::string& filename ); + static FontTrueTypePtr New( const std::string& FontName, const std::string& filename, + ResourceScope& resourceScope ); - static FontTrueType* New( const std::string& FontName, const std::string& filename, - Uint32 faceIndex ); + static FontTrueTypePtr New( const std::string& FontName, const std::string& filename, + Uint32 faceIndex ); + static FontTrueTypePtr New( const std::string& FontName, const std::string& filename, + Uint32 faceIndex, ResourceScope& resourceScope ); ~FontTrueType(); @@ -48,6 +59,9 @@ class EE_API FontTrueType : public Font { Glyph getGlyph( Uint32 codePoint, unsigned int characterSize, bool bold, bool italic, Float outlineThickness = 0 ) const; + Float getGlyphAdvance( Uint32 codePoint, unsigned int characterSize, bool bold = false, + bool italic = false, Float outlineThickness = 0 ) const; + Glyph getGlyphByIndex( Uint32 index, unsigned int characterSize, bool bold, bool italic, Float outlineThickness = 0 ) const; @@ -77,9 +91,10 @@ class EE_API FontTrueType : public Font { Float getUnderlineThickness( unsigned int characterSize ) const; - Texture* getTexture( unsigned int characterSize ) const; + const TexturePtr& getTexture( unsigned int characterSize ) const; bool loaded() const; + FontService* getFontService() const; FontTrueType( const FontTrueType& ) = delete; FontTrueType& operator=( const FontTrueType& ) = delete; @@ -151,19 +166,19 @@ class EE_API FontTrueType : public Font { virtual bool hasItalic() const { return mIsItalic || mFontItalic != nullptr; } - virtual bool hasBoldItalic() const { return isBoldItalic() || mFontBoldItalic; } + virtual bool hasBoldItalic() const { return isBoldItalic() || mFontBoldItalic != nullptr; } - FontTrueType* getBoldFont() const { return mFontBold; } + const FontTrueTypePtr& getBoldFont() const { return mFontBold; } - FontTrueType* getItalicFont() const { return mFontItalic; } + const FontTrueTypePtr& getItalicFont() const { return mFontItalic; } - FontTrueType* getBoldItalicFont() const { return mFontBoldItalic; } + const FontTrueTypePtr& getBoldItalicFont() const { return mFontBoldItalic; } - void setBoldFont( FontTrueType* fontBold ); + void setBoldFont( const FontTrueTypePtr& fontBold ); - void setItalicFont( FontTrueType* fontItalic ); + void setItalicFont( const FontTrueTypePtr& fontItalic ); - void setBoldItalicFont( FontTrueType* fontBoldItalic ); + void setBoldItalicFont( const FontTrueTypePtr& fontBoldItalic ); void* face() const { return mFace; } @@ -178,8 +193,11 @@ class EE_API FontTrueType : public Font { protected: friend class Text; friend class TextLayout; + friend class FontService; + friend class ResourceScope; - explicit FontTrueType( const std::string& FontName ); + explicit FontTrueType( const std::string& FontName, FontService& fontService ); + void setFontService( FontService* fontService ); struct Row { Row( unsigned int rowTop, unsigned int rowHeight ) : @@ -201,11 +219,11 @@ class EE_API FontTrueType : public Font { GlyphTable glyphs; ///< Table mapping code points to their corresponding glyph GlyphDrawableTable - drawables; ///> Table mapping code points to their corresponding glyph drawables. - Texture* texture{ nullptr }; ///< Texture containing the pixels of the glyphs - std::vector rows; ///< List containing the position of all the existing rows - Uint32 fontInternalId{ 0 }; // The font internal id - unsigned int nextRow; ///< Y position of the next new row in the texture + drawables; ///> Table mapping code points to their corresponding glyph drawables. + TexturePtr texture; ///< Texture containing the pixels of the glyphs + std::vector rows; ///< List containing the position of all the existing rows + Uint32 fontInternalId{ 0 }; // The font internal id + unsigned int nextRow; ///< Y position of the next new row in the texture const FontTrueType* font{ nullptr }; }; @@ -267,15 +285,14 @@ class EE_API FontTrueType : public Font { mutable UnorderedMap> mKeyCache; mutable UnorderedMap mKerningCache; // For codepoints (getKerning) mutable UnorderedMap mKerningGlyphCache; // For glyph indices + mutable UnorderedMap> mGlyphAdvanceCache; FontHinting mHinting{ FontHinting::Full }; FontAntialiasing mAntialiasing{ FontAntialiasing::Grayscale }; + FontService* mFontService{ nullptr }; Uint32 mFaceIndex{ 0 }; - FontTrueType* mFontBold{ nullptr }; - FontTrueType* mFontItalic{ nullptr }; - FontTrueType* mFontBoldItalic{ nullptr }; - Uint32 mFontBoldCb{ 0 }; - Uint32 mFontItalicCb{ 0 }; - Uint32 mFontBoldItalicCb{ 0 }; + FontTrueTypePtr mFontBold; + FontTrueTypePtr mFontItalic; + FontTrueTypePtr mFontBoldItalic; Float getGlyphTopOffset( unsigned int characterSize ) const; @@ -284,12 +301,6 @@ class EE_API FontTrueType : public Font { bool setFontFace( void* face ); void updateMonospaceState() const; - - void disconnectBoldFont(); - - void disconnectItalicFont(); - - void disconnectBoldItalicFont(); }; }} // namespace EE::Graphics diff --git a/include/eepp/graphics/framebuffer.hpp b/include/eepp/graphics/framebuffer.hpp index b2613c4f3..7bf2b66ca 100644 --- a/include/eepp/graphics/framebuffer.hpp +++ b/include/eepp/graphics/framebuffer.hpp @@ -2,6 +2,7 @@ #define EE_GRAPHICSCFRAMEBUFFER_HPP #include +#include #include #include @@ -13,6 +14,9 @@ using namespace EE::Window; namespace EE { namespace Graphics { +class FrameBuffer; +using FrameBufferUniquePtr = std::unique_ptr>; + /** @brief A frame buffer allows rendering to a off-screen 2D texture */ class EE_API FrameBuffer { public: @@ -26,9 +30,10 @@ class EE_API FrameBuffer { ** @param window In case that the application is using more than one window, the user can *indicate which one to use ( by default uses the current active window ) */ - static FrameBuffer* New( const Uint32& Width, const Uint32& Height, bool StencilBuffer = true, - bool DepthBuffer = false, bool useColorBuffer = false, - const Uint32& channels = 4, EE::Window::Window* window = NULL ); + static FrameBufferUniquePtr New( const Uint32& Width, const Uint32& Height, + bool StencilBuffer = true, bool DepthBuffer = false, + bool useColorBuffer = false, const Uint32& channels = 4, + EE::Window::Window* window = NULL ); virtual ~FrameBuffer(); @@ -62,7 +67,7 @@ class EE_API FrameBuffer { ** The frame buffer must be unbinded before any rendering is done outside the frame buffer. ** For example MyFrameBufferPtr->getTexture()->Draw(0,0); */ - Texture* getTexture() const; + const TexturePtr& getTexture() const; /** @brief Sets the frame buffer clear color. */ void setClearColor( const ColorAf& color ); @@ -123,7 +128,7 @@ class EE_API FrameBuffer { bool mHasStencilBuffer{ false }; bool mAdjustCurrentClipping{ true }; bool mNeedsToRestoreScissorsClipping{ false }; - Texture* mTexture{ nullptr }; + TexturePtr mTexture; ColorAf mClearColor; View mView; float mProjMat[16]; diff --git a/include/eepp/graphics/framebuffermanager.hpp b/include/eepp/graphics/framebuffermanager.hpp index d854c2b68..2464fdb07 100644 --- a/include/eepp/graphics/framebuffermanager.hpp +++ b/include/eepp/graphics/framebuffermanager.hpp @@ -10,11 +10,12 @@ using namespace EE::System; namespace EE { namespace Graphics { namespace Private { -class EE_API FrameBufferManager : public Container { - SINGLETON_DECLARE_HEADERS( FrameBufferManager ) +/** Non-owning registry of framebuffers visible to the active graphics context. */ +class EE_API FrameBufferRegistry : public Container { + SINGLETON_DECLARE_HEADERS( FrameBufferRegistry ) public: - virtual ~FrameBufferManager(); + virtual ~FrameBufferRegistry(); FrameBuffer* getCurrentlyBound(); @@ -23,7 +24,7 @@ class EE_API FrameBufferManager : public Container { FrameBuffer* getFromId( const String::HashType& id ); protected: - FrameBufferManager(); + FrameBufferRegistry(); }; }}} // namespace EE::Graphics::Private diff --git a/include/eepp/graphics/globaltextureatlas.hpp b/include/eepp/graphics/globaltextureatlas.hpp deleted file mode 100644 index c1c76be86..000000000 --- a/include/eepp/graphics/globaltextureatlas.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef EE_GRAPHICSCGLOBALTEXTUREATLAS_HPP -#define EE_GRAPHICSCGLOBALTEXTUREATLAS_HPP - -#include -#include - -#include -using namespace EE::System; - -namespace EE { namespace Graphics { - -/** @brief Any TextureRegion that doesn't belong to an specific TextureAtlas ( a real texture atlas - texture ), goes here. This is useful to auto release the TextureRegions. -*/ -class EE_API GlobalTextureAtlas : public TextureAtlas { - SINGLETON_DECLARE_HEADERS( GlobalTextureAtlas ) - - public: - ~GlobalTextureAtlas(); - - protected: - GlobalTextureAtlas(); -}; - -}} // namespace EE::Graphics - -#endif diff --git a/include/eepp/graphics/glyphdrawable.hpp b/include/eepp/graphics/glyphdrawable.hpp index 6c72bcbce..94f8efc4d 100644 --- a/include/eepp/graphics/glyphdrawable.hpp +++ b/include/eepp/graphics/glyphdrawable.hpp @@ -11,7 +11,7 @@ class VertexBuffer; class EE_API GlyphDrawable : public DrawableResource { public: - static GlyphDrawable* New( Texture* texture, const Rect& srcRect, const Sizef& destSize = {}, + static GlyphDrawable* New( TexturePtr texture, const Rect& srcRect, const Sizef& destSize = {}, const std::string& resourceName = "" ); enum class DrawMode { @@ -21,7 +21,7 @@ class EE_API GlyphDrawable : public DrawableResource { ///< italic skew }; - GlyphDrawable( Texture* texture, const Rect& srcRect, const Sizef& destSize = {}, + GlyphDrawable( TexturePtr texture, const Rect& srcRect, const Sizef& destSize = {}, const std::string& resourceName = "" ); virtual void draw(); @@ -35,8 +35,10 @@ class EE_API GlyphDrawable : public DrawableResource { virtual bool isStateful(); + DrawablePtr clone() const; + /** @return The texture instance used by the GlyphDrawable. */ - Texture* getTexture(); + const TexturePtr& getTexture() const; /** @return The Texture sector that represents the GlyphDrawable */ const Rectf& getSrcRect() const; @@ -69,7 +71,7 @@ class EE_API GlyphDrawable : public DrawableResource { void setAdvance( Float advance ); protected: - Texture* mTexture; + TexturePtr mTexture; Rectf mSrcRect; Sizef mDestSize; Float mPixelDensity; diff --git a/include/eepp/graphics/ninepatch.hpp b/include/eepp/graphics/ninepatch.hpp index dd2ba9915..6c7102b1d 100644 --- a/include/eepp/graphics/ninepatch.hpp +++ b/include/eepp/graphics/ninepatch.hpp @@ -7,6 +7,10 @@ namespace EE { namespace Graphics { +class NinePatch; +using NinePatchPtr = ResourcePtr; +using NinePatchWeakPtr = ResourceWeakPtr; + class EE_API NinePatch : public DrawableResource { public: enum NinePatchSides { @@ -22,16 +26,16 @@ class EE_API NinePatch : public DrawableResource { SideCount }; - static NinePatch* New( ResourceId textureId, int left, int top, int right, int bottom, - const Float& pixelDensity = 1, const std::string& name = "" ); + static NinePatchPtr New( ResourceId textureId, int left, int top, int right, int bottom, + const Float& pixelDensity = 1, const std::string& name = "" ); - static NinePatch* New( Texture* tex, int left, int top, int right, int bottom, - const Float& pixelDensity = 1, const std::string& name = "" ); + static NinePatchPtr New( TexturePtr tex, int left, int top, int right, int bottom, + const Float& pixelDensity = 1, const std::string& name = "" ); - static NinePatch* New( TextureRegion* textureRegion, int left, int top, int right, int bottom, - const std::string& name = "" ); + static NinePatchPtr New( TextureRegion* textureRegion, int left, int top, int right, int bottom, + const std::string& name = "" ); - NinePatch( Texture* tex, int left, int top, int right, int bottom, + NinePatch( TexturePtr tex, int left, int top, int right, int bottom, const Float& pixelDensity = 1, const std::string& name = "" ); NinePatch( TextureRegion* textureRegion, int left, int top, int right, int bottom, @@ -51,17 +55,19 @@ class EE_API NinePatch : public DrawableResource { virtual bool isStateful() { return false; } + DrawablePtr clone() const; + TextureRegion* getTextureRegion( const int& side ); protected: - TextureRegion* mDrawable[SideCount]; + TextureRegionPtr mDrawable[SideCount]; Rect mRect; Rectf mRectf; Sizef mSize; Sizef mDestSize; Float mPixelDensity; - void createFromTexture( Texture* tex, int left, int top, int right, int bottom ); + void createFromTexture( const TexturePtr& tex, int left, int top, int right, int bottom ); virtual void onAlphaChange(); diff --git a/include/eepp/graphics/ninepatchmanager.hpp b/include/eepp/graphics/ninepatchmanager.hpp deleted file mode 100644 index 1dd1e18d1..000000000 --- a/include/eepp/graphics/ninepatchmanager.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef EE_GRAPHICS_NINEPATCHMANAGER_HPP -#define EE_GRAPHICS_NINEPATCHMANAGER_HPP - -#include -#include - -#include -#include -using namespace EE::System; - -namespace EE { namespace Graphics { - -class EE_API NinePatchManager : public ResourceManager { - SINGLETON_DECLARE_HEADERS( NinePatchManager ) - - ~NinePatchManager(); -}; - -}} // namespace EE::Graphics - -#endif diff --git a/include/eepp/graphics/particlesystem.hpp b/include/eepp/graphics/particlesystem.hpp index 3d9dea7ed..9995cd1b5 100644 --- a/include/eepp/graphics/particlesystem.hpp +++ b/include/eepp/graphics/particlesystem.hpp @@ -12,6 +12,7 @@ using namespace EE::System; namespace EE { namespace Graphics { class Texture; +using TexturePtr = ResourcePtr; /** @enum EE::Graphics::ParticleEffect Predefined effects for the particle system. Use Callback when * wan't to create a new effect, o set the parameters using NoFx, but it's much more limited. */ @@ -157,7 +158,7 @@ class EE_API ParticleSystem { private: Particle* mParticle; Uint32 mPCount; - const Texture* mTexture; + TexturePtr mTexture; Uint32 mPLeft; Uint32 mLoops; diff --git a/include/eepp/graphics/primitivedrawable.hpp b/include/eepp/graphics/primitivedrawable.hpp index 33b33d788..3d59e6493 100644 --- a/include/eepp/graphics/primitivedrawable.hpp +++ b/include/eepp/graphics/primitivedrawable.hpp @@ -3,11 +3,10 @@ #include #include +#include namespace EE { namespace Graphics { -class VertexBuffer; - class EE_API PrimitiveDrawable : public Drawable { public: virtual ~PrimitiveDrawable(); @@ -47,7 +46,7 @@ class EE_API PrimitiveDrawable : public Drawable { bool mNeedsUpdate; bool mRecreateVertexBuffer; bool mSmooth{ false }; - VertexBuffer* mVertexBuffer; + VertexBufferUniquePtr mVertexBuffer; virtual void onAlphaChange(); diff --git a/include/eepp/graphics/rectangledrawable.hpp b/include/eepp/graphics/rectangledrawable.hpp index 933b65349..6ecae2dd5 100644 --- a/include/eepp/graphics/rectangledrawable.hpp +++ b/include/eepp/graphics/rectangledrawable.hpp @@ -28,6 +28,8 @@ class EE_API RectangleDrawable : public PrimitiveDrawable { virtual bool isStateful() { return false; } + DrawablePtr clone() const; + Float getRotation() const; void setRotation( const Float& rotation ); diff --git a/include/eepp/graphics/renderer/renderergl3.hpp b/include/eepp/graphics/renderer/renderergl3.hpp index 13c19d215..ab04da4af 100644 --- a/include/eepp/graphics/renderer/renderergl3.hpp +++ b/include/eepp/graphics/renderer/renderergl3.hpp @@ -68,7 +68,7 @@ class EE_API RendererGL3 : public RendererGLShader { void reloadCurrentShader(); protected: - ShaderProgram* mShaders[EEGL3_SHADERS_COUNT]; + ShaderProgramPtr mShaders[EEGL3_SHADERS_COUNT]; int mAttribsLoc[EEGL_ARRAY_STATES_COUNT]; int mAttribsLocStates[EEGL_ARRAY_STATES_COUNT]; int mPlanes[EE_MAX_PLANES]; diff --git a/include/eepp/graphics/renderer/renderergl3cp.hpp b/include/eepp/graphics/renderer/renderergl3cp.hpp index af86d2cd7..86be0c1ff 100644 --- a/include/eepp/graphics/renderer/renderergl3cp.hpp +++ b/include/eepp/graphics/renderer/renderergl3cp.hpp @@ -70,7 +70,7 @@ class EE_API RendererGL3CP : public RendererGLShader { void reloadCurrentShader(); protected: - ShaderProgram* mShaders[EEGL3CP_SHADERS_COUNT]; + ShaderProgramPtr mShaders[EEGL3CP_SHADERS_COUNT]; unsigned int mVAO; unsigned int mVBO[8]; int mAttribsLoc[EEGL_ARRAY_STATES_COUNT]; diff --git a/include/eepp/graphics/renderer/renderergles2.hpp b/include/eepp/graphics/renderer/renderergles2.hpp index 8d763ef71..3760a27e1 100644 --- a/include/eepp/graphics/renderer/renderergles2.hpp +++ b/include/eepp/graphics/renderer/renderergles2.hpp @@ -74,7 +74,7 @@ class EE_API RendererGLES2 : public RendererGLShader { void reloadCurrentShader(); protected: - ShaderProgram* mShaders[EEGLES2_SHADERS_COUNT]; + ShaderProgramPtr mShaders[EEGLES2_SHADERS_COUNT]; int mAttribsLoc[EEGL_ARRAY_STATES_COUNT]; int mAttribsLocStates[EEGL_ARRAY_STATES_COUNT]; int mPlanes[EE_MAX_PLANES]; diff --git a/include/eepp/graphics/resource.hpp b/include/eepp/graphics/resource.hpp index 8158269b3..ac603e37e 100644 --- a/include/eepp/graphics/resource.hpp +++ b/include/eepp/graphics/resource.hpp @@ -1,7 +1,11 @@ #ifndef EE_GRAPHICS_RESOURCE_HPP #define EE_GRAPHICS_RESOURCE_HPP +#include #include +#include +#include +#include #include @@ -25,6 +29,58 @@ class ResourceId { Uint64 mValue{ 0 }; }; +/** + * Strong 64-bit hash of a semantic resource name. + * + * This is a fast, process-local convenience key for trusted resource names. It is deliberately + * distinct from String::HashType so legacy 32-bit hashes cannot enter resource-name APIs through + * an implicit integer conversion. Complete ResourceKey values remain the authoritative identity + * whenever collision safety or persistence is required. + */ +class ResourceNameHash { + public: + constexpr ResourceNameHash() = default; + explicit constexpr ResourceNameHash( Uint64 value ) : mValue( value ) {} + + constexpr Uint64 value() const { return mValue; } + explicit constexpr operator bool() const { return mValue != 0; } + + constexpr bool operator==( const ResourceNameHash& other ) const { + return mValue == other.mValue; + } + constexpr bool operator!=( const ResourceNameHash& other ) const { return !( *this == other ); } + constexpr bool operator<( const ResourceNameHash& other ) const { + return mValue < other.mValue; + } + + private: + Uint64 mValue{ 0 }; +}; + +/** + * Hashes a semantic resource name with the vendored wyhash implementation used by + * UnorderedMap. Do not serialize the result: use the complete ResourceKey in persistent formats. + */ +inline ResourceNameHash resourceNameHash( std::string_view name ) { + return ResourceNameHash( ankerl::unordered_dense::hash{}( name ) ); +} + +/** Immutable semantic lookup key. Catalog equality always compares the complete key value. */ +class ResourceKey { + public: + ResourceKey() = default; + explicit ResourceKey( std::string value ) : mValue( std::move( value ) ) {} + + const std::string& value() const { return mValue; } + bool empty() const { return mValue.empty(); } + + bool operator==( const ResourceKey& other ) const { return mValue == other.mValue; } + bool operator!=( const ResourceKey& other ) const { return !( *this == other ); } + + private: + std::string mValue; +}; + template using ResourcePtr = std::shared_ptr; template using ResourceWeakPtr = std::weak_ptr; @@ -33,6 +89,26 @@ template struct ResourceDeleter { void operator()( T* resource ) const noexcept { eeDelete( resource ); } }; +template ResourcePtr makeResource( Args&&... args ) { + return ResourcePtr( eeNew( T, ( std::forward( args )... ) ), ResourceDeleter() ); +} + }} // namespace EE::Graphics +namespace std { + +template <> struct hash { + std::size_t operator()( const EE::Graphics::ResourceId& id ) const noexcept { + return std::hash{}( id.value() ); + } +}; + +template <> struct hash { + std::size_t operator()( const EE::Graphics::ResourceNameHash& hash ) const noexcept { + return std::hash{}( hash.value() ); + } +}; + +} // namespace std + #endif diff --git a/include/eepp/graphics/resourcecatalog.hpp b/include/eepp/graphics/resourcecatalog.hpp new file mode 100644 index 000000000..25cd36dfd --- /dev/null +++ b/include/eepp/graphics/resourcecatalog.hpp @@ -0,0 +1,174 @@ +#ifndef EE_GRAPHICS_RESOURCECATALOG_HPP +#define EE_GRAPHICS_RESOURCECATALOG_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace EE { namespace Graphics { + +class ResourceCatalog; +using ResourceCatalogPtr = ResourcePtr; + +/** + * @brief Thread-safe, strongly owning collection of named graphics resources. + * + * A catalog maps semantic string keys to textures, drawable sources, texture atlases, and fonts. + * Publishing a resource gives the catalog shared ownership of it. Publishing another resource of + * the same kind under the same key replaces that binding; the key and resource name are never + * changed automatically. Publishing a null handle is equivalent to erasing the corresponding key, + * while an empty key is ignored. + * + * Lookups return owning handles. A resource obtained from a catalog therefore remains alive even + * if its binding is subsequently replaced, erased, or the catalog is cleared. Final handles are + * released after dropping the catalog mutex so resource destruction and callbacks never execute + * while the catalog is locked. + * + * Drawable and font hash lookups use resourceNameHash() as a direct secondary index. The dedicated + * 64-bit hash provides a cheap expected-O(1) convenience lookup for trusted names without changing + * the repository-wide 32-bit String::HashType. A hash is not mathematically collision-free, so + * complete ResourceKey lookup remains authoritative and must be used for attacker-controlled names + * or persistent identity. ResourceNameHash values are process-local conveniences and must not be + * serialized. + * + * ResourceCatalog performs no parent, scene, live-registry, filesystem, or fallback search. A + * ResourceScope defines lookup precedence by searching its local catalog and then explicitly + * imported catalogs. Importing a catalog shares this object and its live contents; it does not copy + * any resource. + * + * All public operations are safe to call concurrently. Enumeration methods return snapshots and + * allocate vectors containing owning handles. + */ +class EE_API ResourceCatalog { + public: + /** @return A new empty catalog using eepp resource allocation and deletion. */ + static ResourceCatalogPtr New(); + + /** @brief Publishes or replaces a texture binding. A null texture erases @p key. */ + void publish( ResourceKey key, TexturePtr texture ); + /** @copydoc publish(ResourceKey,TexturePtr) */ + void publish( std::string key, TexturePtr texture ); + + /** @brief Publishes or replaces a drawable-source binding. A null drawable erases @p key. */ + void publishDrawable( ResourceKey key, DrawablePtr drawable ); + /** @copydoc publishDrawable(ResourceKey,DrawablePtr) */ + void publishDrawable( std::string key, DrawablePtr drawable ); + + /** @brief Publishes or replaces a texture-atlas binding. A null atlas erases @p key. */ + void publishAtlas( ResourceKey key, TextureAtlasPtr atlas ); + /** @copydoc publishAtlas(ResourceKey,TextureAtlasPtr) */ + void publishAtlas( std::string key, TextureAtlasPtr atlas ); + + /** @brief Publishes or replaces a font binding. A null font erases @p key. */ + void publishFont( ResourceKey key, FontPtr font ); + /** @copydoc publishFont(ResourceKey,FontPtr) */ + void publishFont( std::string key, FontPtr font ); + + /** @brief Publishes or replaces a shader-program binding. A null program erases @p key. */ + void publishShaderProgram( ResourceKey key, ShaderProgramPtr program ); + /** @copydoc publishShaderProgram(ResourceKey,ShaderProgramPtr) */ + void publishShaderProgram( std::string key, ShaderProgramPtr program ); + + /** @return The texture bound to @p key, or an empty handle when it is not present. */ + TexturePtr findTexture( const ResourceKey& key ) const; + /** @copydoc findTexture(const ResourceKey&)const */ + TexturePtr findTexture( const std::string& key ) const; + + /** @return The drawable source bound to @p key, or an empty handle when it is not present. */ + DrawablePtr findDrawable( const ResourceKey& key ) const; + /** @copydoc findDrawable(const ResourceKey&)const */ + DrawablePtr findDrawable( const std::string& key ) const; + /** + * @brief Looks up a drawable through the resourceNameHash(key) convenience index. + */ + DrawablePtr findDrawable( ResourceNameHash hash ) const; + /** + * @brief Legacy lookup for persisted 32-bit drawable hashes. + * + * This compatibility path scans full bindings and should not be used by new code. Persistent + * formats should migrate to complete ResourceKey values rather than serializing another hash. + * If several names share @p legacyHash, which matching drawable is returned is unspecified. + */ + DrawablePtr findDrawable( String::HashType legacyHash ) const; + + /** @return The texture atlas bound to @p key, or an empty handle when it is not present. */ + TextureAtlasPtr findAtlas( const ResourceKey& key ) const; + /** @copydoc findAtlas(const ResourceKey&)const */ + TextureAtlasPtr findAtlas( const std::string& key ) const; + /** @return An owning snapshot of all texture atlases currently published in this catalog. */ + std::vector getAtlases() const; + + /** @return The font bound to @p key, or an empty handle when it is not present. */ + FontPtr findFont( const ResourceKey& key ) const; + /** @copydoc findFont(const ResourceKey&)const */ + FontPtr findFont( const std::string& key ) const; + /** + * @brief Looks up a font through the resourceNameHash(key) convenience index. + */ + FontPtr findFont( ResourceNameHash hash ) const; + /** @return An owning snapshot of all fonts currently published in this catalog. */ + std::vector getFonts() const; + + /** @return The shader program bound to @p key, or an empty handle when absent. */ + ShaderProgramPtr findShaderProgram( const ResourceKey& key ) const; + /** @copydoc findShaderProgram(const ResourceKey&)const */ + ShaderProgramPtr findShaderProgram( const std::string& key ) const; + /** @return An owning snapshot of all shader programs published in this catalog. */ + std::vector getShaderPrograms() const; + + /** @brief Removes the texture binding for @p key. @return Whether a binding was removed. */ + bool erase( const ResourceKey& key ); + /** @copydoc erase(const ResourceKey&) */ + bool erase( const std::string& key ); + + /** @brief Removes the drawable binding for @p key. @return Whether a binding was removed. */ + bool eraseDrawable( const ResourceKey& key ); + /** @copydoc eraseDrawable(const ResourceKey&) */ + bool eraseDrawable( const std::string& key ); + + /** @brief Removes the texture-atlas binding for @p key. @return Whether a binding was removed. + */ + bool eraseAtlas( const ResourceKey& key ); + /** @copydoc eraseAtlas(const ResourceKey&) */ + bool eraseAtlas( const std::string& key ); + + /** @brief Removes the font binding for @p key. @return Whether a binding was removed. */ + bool eraseFont( const ResourceKey& key ); + /** @copydoc eraseFont(const ResourceKey&) */ + bool eraseFont( const std::string& key ); + /** + * @brief Removes @p font only when its current name maps to that exact font in this catalog. + * @return Whether the matching binding was removed. + */ + bool eraseFont( Font* font ); + + /** @brief Removes the shader-program binding for @p key. */ + bool eraseShaderProgram( const ResourceKey& key ); + /** @copydoc eraseShaderProgram(const ResourceKey&) */ + bool eraseShaderProgram( const std::string& key ); + + /** @brief Removes every binding while allowing previously returned handles to remain valid. */ + void clear(); + + /** @return The total number of bindings of every supported resource kind. */ + std::size_t size() const; + + private: + mutable System::Mutex mMutex; + UnorderedMap mTextures; + UnorderedMap mDrawables; + UnorderedMap mDrawablesByNameHash; + UnorderedMap mAtlases; + UnorderedMap mFonts; + UnorderedMap mFontsByNameHash; + UnorderedMap mShaderPrograms; +}; + +}} // namespace EE::Graphics + +#endif diff --git a/include/eepp/graphics/resourcescope.hpp b/include/eepp/graphics/resourcescope.hpp new file mode 100644 index 000000000..d94e2df87 --- /dev/null +++ b/include/eepp/graphics/resourcescope.hpp @@ -0,0 +1,127 @@ +#ifndef EE_GRAPHICS_RESOURCESCOPE_HPP +#define EE_GRAPHICS_RESOURCESCOPE_HPP + +#include +#include +#include +#include + +namespace EE { namespace Graphics { + +class ResourceScope; +using ResourceScopePtr = ResourcePtr; + +/** + * @brief Graphics resource lookup boundary with local ownership and explicit catalog visibility. + * + * A scope owns resources published into its local catalog. Lookups search that local catalog first, + * followed by imported catalogs in import order. Importing a catalog retains the catalog and makes + * its resources visible; it does not copy resources and does not transfer them into the local + * catalog. Consequently, removing an import only removes lookup visibility and releases the + * scope's catalog reference. + * + * Use importCatalog() when resources owned by another lifetime boundary must be visible in this + * scope, such as application assets shared with a scene or a theme catalog used by UI widgets. + * Avoid importing unrelated scene or document catalogs: keeping imports explicit prevents name + * collisions and resource leakage between independent documents. UISceneNode imports the default + * resource catalog automatically unless that behavior is disabled at construction time. + */ +class EE_API ResourceScope { + public: + static ResourceScopePtr New(); + + ResourceScope(); + ~ResourceScope(); + + FontService& getFontService(); + const FontService& getFontService() const; + + TexturePtr findTexture( const ResourceKey& key ) const; + TexturePtr findTexture( const std::string& key ) const; + DrawablePtr findDrawableSource( const ResourceKey& key ) const; + DrawablePtr findDrawableSource( const std::string& key ) const; + DrawablePtr findDrawable( const std::string& name, bool firstSearchSprite = false ) const; + DrawablePtr findDrawable( ResourceNameHash hash ) const; + DrawablePtr findDrawable( String::HashType legacyHash ) const; + TextureAtlasPtr findAtlas( const ResourceKey& key ) const; + TextureAtlasPtr findAtlas( const std::string& key ) const; + std::vector getAtlases() const; + FontPtr findFont( const ResourceKey& key ) const; + FontPtr findFont( const std::string& key ) const; + FontPtr findFont( ResourceNameHash hash ) const; + std::vector getFonts() const; + ShaderProgramPtr findShaderProgram( const ResourceKey& key ) const; + ShaderProgramPtr findShaderProgram( const std::string& key ) const; + std::vector getShaderPrograms() const; + std::vector + findTextureRegionsByPattern( const std::string& name, const std::string& extension = "", + TextureAtlas* searchInTextureAtlas = nullptr ) const; + std::vector + findTextureRegionsByPatternId( const String::HashType& id, const std::string& extension = "", + TextureAtlas* searchInTextureAtlas = nullptr ) const; + + void publishLocal( ResourceKey key, TexturePtr texture ); + void publishLocal( std::string key, TexturePtr texture ); + void publishLocalDrawable( ResourceKey key, DrawablePtr drawable ); + void publishLocalDrawable( std::string key, DrawablePtr drawable ); + void publishLocalAtlas( ResourceKey key, TextureAtlasPtr atlas ); + void publishLocalAtlas( std::string key, TextureAtlasPtr atlas ); + /** + * @brief Publishes a font under @p key, replacing any existing local binding for that key. + * + * The requested semantic key is preserved. Fonts are never renamed to avoid a collision. + * A FontTrueType instance can belong locally to only one ResourceScope because it has one + * FontService association. To expose the same font to another scope, import this scope's local + * catalog instead of publishing the same handle locally again. + */ + void publishLocalFont( ResourceKey key, FontPtr font ); + void publishLocalFont( std::string key, FontPtr font ); + void publishLocalShaderProgram( ResourceKey key, ShaderProgramPtr program ); + void publishLocalShaderProgram( std::string key, ShaderProgramPtr program ); + bool eraseLocal( const ResourceKey& key ); + bool eraseLocal( const std::string& key ); + bool eraseLocalDrawable( const ResourceKey& key ); + bool eraseLocalDrawable( const std::string& key ); + bool eraseLocalAtlas( const ResourceKey& key ); + bool eraseLocalAtlas( const std::string& key ); + bool eraseLocalFont( const ResourceKey& key ); + bool eraseLocalFont( const std::string& key ); + bool eraseLocalFont( Font* font ); + bool eraseLocalShaderProgram( const ResourceKey& key ); + bool eraseLocalShaderProgram( const std::string& key ); + void clearLocal(); + + /** + * @brief Makes resources from @p catalog visible after this scope's local resources. + * + * Importing the same catalog more than once has no effect. The catalog is retained strongly for + * as long as it remains imported. Catalog contents stay live: resources published after this + * call become visible without importing the catalog again. + */ + void importCatalog( ResourceCatalogPtr catalog ); + + /** @brief Removes an imported catalog without modifying the catalog or its resources. */ + bool removeCatalog( const ResourceCatalogPtr& catalog ); + + /** @brief Removes every imported catalog while preserving this scope's local resources. */ + void clearImports(); + + ResourceCatalogPtr getLocalCatalog() const; + + private: + void attachFontService( const FontPtr& font ); + void detachFontService( const FontPtr& font ); + + ResourceCatalogPtr mLocalCatalog; + FontService mFontService; + std::vector mImports; + mutable System::Mutex mMutex; +}; + +/** Engine-owned process defaults for pure Graphics and application-wide resolution. */ +EE_API ResourceCatalog& globalResourceCatalog(); +EE_API ResourceScope& defaultResourceScope(); + +}} // namespace EE::Graphics + +#endif diff --git a/include/eepp/graphics/scrollparallax.hpp b/include/eepp/graphics/scrollparallax.hpp index 034f28b2b..40c92e13a 100644 --- a/include/eepp/graphics/scrollparallax.hpp +++ b/include/eepp/graphics/scrollparallax.hpp @@ -89,7 +89,7 @@ class EE_API ScrollParallax { const Vector2f& getSpeed() const; private: - TextureRegion* mTextureRegion; + TextureRegionPtr mTextureRegion; BlendMode mBlend; Color mColor; Vector2f mInitPos; diff --git a/include/eepp/graphics/shader.hpp b/include/eepp/graphics/shader.hpp index fb2fb8d92..bdf93d67a 100644 --- a/include/eepp/graphics/shader.hpp +++ b/include/eepp/graphics/shader.hpp @@ -2,12 +2,17 @@ #define EE_GRAPHICSCSHADER_H #include +#include #include using namespace EE::System; namespace EE { namespace Graphics { +class Shader; +using ShaderPtr = ResourcePtr; +using ShaderWeakPtr = ResourceWeakPtr; + /** @brief The basic shader class. */ class EE_API Shader { public: diff --git a/include/eepp/graphics/shaderprogram.hpp b/include/eepp/graphics/shaderprogram.hpp index 3e0127752..cdd0ee409 100644 --- a/include/eepp/graphics/shaderprogram.hpp +++ b/include/eepp/graphics/shaderprogram.hpp @@ -6,6 +6,10 @@ namespace EE { namespace Graphics { +class ShaderProgram; +using ShaderProgramPtr = ResourcePtr; +using ShaderProgramWeakPtr = ResourceWeakPtr; + /** @brief The Shader Program Class. @short Program is a GPU-executed program that is ready to be used for manipulating geometry and colors. * ShaderProgram can encapsulate vertex and fragment shaders or just one of them. If only @@ -15,40 +19,42 @@ other stage. class EE_API ShaderProgram { public: /** Creates an empty shader program */ - static ShaderProgram* New( const std::string& Name = "" ); + static ShaderProgramPtr New( const std::string& Name = "" ); /** Creates a program shader with a vector of shaders and link them. */ - static ShaderProgram* New( const std::vector& Shaders, const std::string& Name = "" ); + static ShaderProgramPtr New( const std::vector& Shaders, + const std::string& Name = "" ); /** Creates a VertexShader from file and a Fragment Shader from file, and link them. */ - static ShaderProgram* New( const std::string& VertexShaderFile, - const std::string& FragmentShaderFile, - const std::string& Name = "" ); + static ShaderProgramPtr New( const std::string& VertexShaderFile, + const std::string& FragmentShaderFile, + const std::string& Name = "" ); /** Creates a VertexShader from memory and a Fragment Shader from memory, and link them. */ - static ShaderProgram* New( const char* VertexShaderData, const Uint32& VertexShaderDataSize, - const char* FragmentShaderData, const Uint32& FragmentShaderDataSize, - const std::string& Name = "" ); + static ShaderProgramPtr New( const char* VertexShaderData, const Uint32& VertexShaderDataSize, + const char* FragmentShaderData, + const Uint32& FragmentShaderDataSize, + const std::string& Name = "" ); /** Creates the vertex shader and fragment shader from two files inside a pack */ - static ShaderProgram* New( Pack* Pack, const std::string& VertexShaderPath, - const std::string& FragmentShaderPath, - const std::string& Name = "" ); + static ShaderProgramPtr New( Pack* Pack, const std::string& VertexShaderPath, + const std::string& FragmentShaderPath, + const std::string& Name = "" ); /** Creates the vertex and fragment shader from an array of strings */ - static ShaderProgram* New( const char** VertexShaderData, const Uint32& NumLinesVS, - const char** FragmentShaderData, const Uint32& NumLinesFS, - const std::string& Name = "" ); + static ShaderProgramPtr New( const char** VertexShaderData, const Uint32& NumLinesVS, + const char** FragmentShaderData, const Uint32& NumLinesFS, + const std::string& Name = "" ); typedef std::function ShaderProgramReloadCb; virtual ~ShaderProgram(); /** Add a new shader */ - void addShader( Shader* Shader ); + void addShader( ShaderPtr shader ); /** Add a vector of shaders */ - void addShaders( const std::vector& Shaders ); + void addShaders( const std::vector& shaders ); virtual bool link(); @@ -151,7 +157,7 @@ class EE_API ShaderProgram { bool mValid; std::string mLinkLog; - std::vector mShaders; + std::vector mShaders; std::map mUniformLocations; std::map mAttributeLocations; @@ -159,15 +165,15 @@ class EE_API ShaderProgram { void init(); - void addToManager( const std::string& Name ); + void addToRegistry( const std::string& Name ); - void removeFromManager(); + void removeFromRegistry(); /** Creates an empty shader program */ ShaderProgram( const std::string& Name = "" ); /** Construct a program shader with a vector of shaders and link them. */ - ShaderProgram( const std::vector& Shaders, const std::string& Name = "" ); + ShaderProgram( const std::vector& Shaders, const std::string& Name = "" ); /** Constructor that creates a VertexShader from file and a Fragment Shader from file, and link * them. */ diff --git a/include/eepp/graphics/shaderprogrammanager.hpp b/include/eepp/graphics/shaderprogrammanager.hpp index b8c5c031d..4c9e91d62 100644 --- a/include/eepp/graphics/shaderprogrammanager.hpp +++ b/include/eepp/graphics/shaderprogrammanager.hpp @@ -3,26 +3,23 @@ #include #include - -#include +#include #include using namespace EE::System; namespace EE { namespace Graphics { -/** @brief The Shader Program Manager is a singleton class that manages all the instances of Shader - Programs instantiated. Releases the Shader Program instances automatically. So the user doesn't - need to release any Shader Program instance. */ -class EE_API ShaderProgramManager : public ResourceManager { - SINGLETON_DECLARE_HEADERS( ShaderProgramManager ) +/** Non-owning registry of shader programs associated with the active graphics context. */ +class EE_API ShaderProgramRegistry : public Container { + SINGLETON_DECLARE_HEADERS( ShaderProgramRegistry ) public: - virtual ~ShaderProgramManager(); + virtual ~ShaderProgramRegistry(); void reload(); protected: - ShaderProgramManager(); + ShaderProgramRegistry(); }; }} // namespace EE::Graphics diff --git a/include/eepp/graphics/sprite.hpp b/include/eepp/graphics/sprite.hpp index 9ce4d4f95..2e0c6fdbb 100644 --- a/include/eepp/graphics/sprite.hpp +++ b/include/eepp/graphics/sprite.hpp @@ -11,6 +11,10 @@ using namespace EE::System; namespace EE { namespace Graphics { +class Sprite; +class ResourceScope; +using SpritePtr = ResourcePtr; + /** @brief A Sprite controller class, can hold and control sprites animations. */ class EE_API Sprite : public Drawable { public: @@ -26,22 +30,28 @@ class EE_API Sprite : public Drawable { SPRITE_EVENT_USER // User vents }; - static Sprite* New(); + static SpritePtr New(); - static Sprite* New( const std::string& name, const std::string& extension = "", - TextureAtlas* SearchInTextureAtlas = NULL ); + static SpritePtr New( const std::string& name, const std::string& extension = "", + TextureAtlas* SearchInTextureAtlas = NULL ); - static Sprite* New( TextureRegion* TextureRegion ); + static SpritePtr New( ResourceScope& resourceScope, const std::string& name, + const std::string& extension = "", + TextureAtlas* SearchInTextureAtlas = NULL ); - static Sprite* New( ResourceId textureId, const Sizef& DestSize = Sizef( 0, 0 ), - const Vector2i& offset = Vector2i( 0, 0 ), - const Rect& TexSector = Rect( 0, 0, 0, 0 ) ); + static SpritePtr New( TextureRegion* TextureRegion ); - static Sprite* fromGif( IOStream& gif ); + static SpritePtr New( ResourceId textureId, const Sizef& DestSize = Sizef( 0, 0 ), + const Vector2i& offset = Vector2i( 0, 0 ), + const Rect& TexSector = Rect( 0, 0, 0, 0 ) ); + + static SpritePtr fromGif( IOStream& gif ); /** Instantiate an empty sprite */ Sprite(); + Sprite( const Sprite& other ); + /** Creates an animated Sprite from a animation name. It will search for a pattern name. * For example search for name "car" with extensions "png", i will try to find car00.png * car01.png car02.png, and so on, it will continue if find something, otherwise it will stop ( @@ -51,11 +61,14 @@ class EE_API Sprite : public Drawable { * @param SearchInTextureAtlas If you want only to search in a especific atlas ( NULL if you * want to search in all atlases ) * @note Texture atlases saves the TextureRegions names without extension by default. - * @see TextureAtlasManager::GetTextureRegionsByPattern + * @see ResourceScope::findTextureRegionsByPattern */ Sprite( const std::string& name, const std::string& extension = "", TextureAtlas* SearchInTextureAtlas = NULL ); + Sprite( ResourceScope& resourceScope, const std::string& name, + const std::string& extension = "", TextureAtlas* SearchInTextureAtlas = NULL ); + /** Creates a Sprite from a TextureRegion ** @param TextureRegion The TextureRegion to use */ Sprite( TextureRegion* TextureRegion ); @@ -211,7 +224,7 @@ class EE_API Sprite : public Drawable { * @param TexSector The texture sector to be rendered ( default all the texture ) * @return True if success */ - bool createStatic( Texture* tex, const Sizef& DestSize = Sizef( 0, 0 ), + bool createStatic( TexturePtr tex, const Sizef& DestSize = Sizef( 0, 0 ), const Vector2i& offset = Vector2i( 0, 0 ), const Rect& TexSector = Rect( 0, 0, 0, 0 ) ); @@ -238,7 +251,7 @@ class EE_API Sprite : public Drawable { * @param TexSector The texture sector to be rendered ( default all the texture ) * @return The frame position or 0 if fails */ - unsigned int addFrame( Texture* tex, const Sizef& DestSize = Sizef( 0, 0 ), + unsigned int addFrame( TexturePtr tex, const Sizef& DestSize = Sizef( 0, 0 ), const Vector2i& offset = Vector2i( 0, 0 ), const Rect& TexSector = Rect( 0, 0, 0, 0 ) ); @@ -253,13 +266,21 @@ class EE_API Sprite : public Drawable { */ bool addFrames( const std::vector TextureRegions ); - /** @see TextureAtlasManager::GetTextureRegionsByPattern */ + /** @see ResourceScope::findTextureRegionsByPattern */ bool addFramesByPattern( const std::string& name, const std::string& extension = "", TextureAtlas* SearchInTextureAtlas = NULL ); + bool addFramesByPattern( ResourceScope& resourceScope, const std::string& name, + const std::string& extension = "", + TextureAtlas* SearchInTextureAtlas = NULL ); + bool addFramesByPatternId( const Uint32& TextureRegionId, const std::string& extension, TextureAtlas* SearchInTextureAtlas ); + bool addFramesByPatternId( ResourceScope& resourceScope, const Uint32& TextureRegionId, + const std::string& extension, + TextureAtlas* SearchInTextureAtlas = NULL ); + /** Add a frame on an specific subframe to the sprite * @param tex The texture * @param NumFrame The Frame Number @@ -269,7 +290,7 @@ class EE_API Sprite : public Drawable { * @param TexSector The texture sector to be rendered ( default all the texture ) * @return True if success */ - bool addSubFrame( Texture* tex, const unsigned int& NumFrame, const unsigned int& NumSubFrame, + bool addSubFrame( TexturePtr tex, const unsigned int& NumFrame, const unsigned int& NumSubFrame, const Sizef& DestSize = Sizef( 0, 0 ), const Vector2i& offset = Vector2i( 0, 0 ), const Rect& TexSector = Rect( 0, 0, 0, 0 ) ); @@ -312,6 +333,8 @@ class EE_API Sprite : public Drawable { virtual bool isStateful() { return false; } + DrawablePtr clone() const; + /** Set the number of repetitions of the animation. Any number below 0 the animation will loop. */ void setRepetitions( const int& Repeations ); @@ -379,8 +402,8 @@ class EE_API Sprite : public Drawable { /** Pop the event callback id indicated. */ bool popEventsCallback( const Uint32& callbackId ); - /** Creates a copy of the current sprite and returns it */ - Sprite clone(); + /** Creates an independent sprite sharing the same texture resources. */ + SpritePtr cloneSprite() const; /** Update the sprite animation */ void update( const Time& ElapsedTime ); @@ -391,14 +414,6 @@ class EE_API Sprite : public Drawable { /** Fire a User Event in the sprite */ void fireEvent( const Uint32& Event ); - Sprite& setAsTextureRegionOwner( bool set ); - - bool isTextureRegionOwner() const; - - Sprite& setAsTextureOwner( bool set ); - - bool isTextureOwner() const; - protected: enum SpriteFlags { SPRITE_FLAG_AUTO_ANIM = ( 1 << 0 ), @@ -406,8 +421,6 @@ class EE_API Sprite : public Drawable { SPRITE_FLAG_ANIM_PAUSED = ( 1 << 2 ), SPRITE_FLAG_ANIM_TO_FRAME_AND_STOP = ( 1 << 3 ), SPRITE_FLAG_EVENTS_ENABLED = ( 1 << 4 ), - SPRITE_FLAG_TEXTURE_OWNER = ( 1 << 5 ), - SPRITE_FLAG_TEXTURE_REGION_OWNER = ( 1 << 6 ), }; Uint32 mFlags{ SPRITE_FLAG_AUTO_ANIM | SPRITE_FLAG_EVENTS_ENABLED }; @@ -438,7 +451,7 @@ class EE_API Sprite : public Drawable { UnorderedMap mCallbacks; struct Frame { - std::vector Spr; + std::vector Spr; }; std::vector mFrames; @@ -446,11 +459,12 @@ class EE_API Sprite : public Drawable { void clearFrame(); - void cleanUpResources(); - unsigned int getFrame( const unsigned int& FrameNum ); unsigned int getSubFrame( const unsigned int& SubFrame ); + + bool addSubFrame( TextureRegionPtr textureRegion, const unsigned int& numFrame, + const unsigned int& numSubFrame ); }; }} // namespace EE::Graphics diff --git a/include/eepp/graphics/statelistdrawable.hpp b/include/eepp/graphics/statelistdrawable.hpp index 9b2c50371..c01bb266e 100644 --- a/include/eepp/graphics/statelistdrawable.hpp +++ b/include/eepp/graphics/statelistdrawable.hpp @@ -28,14 +28,15 @@ class EE_API StateListDrawable : public StatefulDrawable { virtual bool isStateful(); + DrawablePtr clone() const; + virtual StatefulDrawable* setState( Uint32 state ); virtual const Uint32& getState() const; virtual Drawable* getStateDrawable( const Uint32& state ); - virtual StateListDrawable* setStateDrawable( const Uint32& state, Drawable* drawable, - bool ownIt = false ); + virtual StateListDrawable* setStateDrawable( const Uint32& state, DrawablePtr drawable ); virtual Sizef getStateSize( const Uint32& state ); @@ -56,8 +57,7 @@ class EE_API StateListDrawable : public StatefulDrawable { protected: Uint32 mCurrentState; Drawable* mCurrentDrawable; - std::map mDrawables; - std::map mDrawablesOwnership; + std::map mDrawables; std::map mDrawableColors; explicit StateListDrawable( const std::string& name = "" ); diff --git a/include/eepp/graphics/textlayout.hpp b/include/eepp/graphics/textlayout.hpp index d8802d3e4..796421a13 100644 --- a/include/eepp/graphics/textlayout.hpp +++ b/include/eepp/graphics/textlayout.hpp @@ -48,7 +48,16 @@ class EE_API TextLayout { LineWrapMode lineWrapMode = LineWrapMode::NoWrap, Uint32 wrapWidth = 0, bool keepIndentation = false, Float initialXOffset = 0 ); - static void clearLayoutCache(); + /** + * Removes entries from the shared text-layout LRU cache. + * + * @param font Optional borrowed font identity. When provided, only layouts requested with this + * font or containing shaped glyphs produced by this font are evicted. Passing nullptr clears + * the entire cache. Font destruction uses selective eviction before its borrowed pointers + * become invalid. + */ + static void clearLayoutCache( Font* font = nullptr ); + protected: static void wrapLayout( const String::View& string, TextLayout&, LineWrapMode lineWrapMode, Float wrapWidth, Float vspace, bool keepIndentation, Font* font, diff --git a/include/eepp/graphics/texture.hpp b/include/eepp/graphics/texture.hpp index cd2f3bd1c..e3395ee79 100644 --- a/include/eepp/graphics/texture.hpp +++ b/include/eepp/graphics/texture.hpp @@ -34,7 +34,7 @@ class EE_API Texture : public DrawableResource, public Image, private NonCopyabl static Uint32 getMaximumSize(); /* @return an array of Textures and the delay of the first frame */ - static std::pair, int> loadGif( IOStream& stream ); + static std::pair, int> loadGif( IOStream& stream ); /** Set the OpenGL Texture Id (texture handle) */ void setHandle( const int& texture ) { mTexture = texture; } @@ -294,6 +294,8 @@ class EE_API Texture : public DrawableResource, public Image, private NonCopyabl virtual bool isStateful() { return false; } + DrawablePtr clone() const; + /** @return The process-wide identity assigned to this texture. */ ResourceId getTextureId() const; diff --git a/include/eepp/graphics/textureatlas.hpp b/include/eepp/graphics/textureatlas.hpp index cda6c8002..eb1343d84 100644 --- a/include/eepp/graphics/textureatlas.hpp +++ b/include/eepp/graphics/textureatlas.hpp @@ -3,18 +3,21 @@ #include #include -#include -using namespace EE::System; +#include namespace EE { namespace Graphics { +class TextureAtlas; +using TextureAtlasPtr = ResourcePtr; +using TextureAtlasWeakPtr = ResourceWeakPtr; + /** @brief The texture atlas class represents a large image containing a collection of sub-images, * or "atlas" which contains many smaller sub-images. The texture atlas in eepp can represent more * than one texture or image, but the common use should be a image with sub-images. * More information about Texture Atlases: http://en.wikipedia.org/wiki/Texture_atlas */ -class EE_API TextureAtlas : public ResourceManager { +class EE_API TextureAtlas { public: - static TextureAtlas* New( const std::string& name = "" ); + static TextureAtlasPtr New( const std::string& name = "" ); /** Creates a new texture atlas with the given name. */ TextureAtlas( const std::string& name = "" ); @@ -22,21 +25,21 @@ class EE_API TextureAtlas : public ResourceManager { ~TextureAtlas(); /** Adds a TextureRegion to the Texture Atlas */ - TextureRegion* add( TextureRegion* textureRegion ); + TextureRegionPtr add( TextureRegionPtr textureRegion ); /** Creates and add to the texture atlas a TextureRegion from a Texture. It will use the full *Texture as a TextureRegion. * @param textureId The texture identity * @param Name The texture name ( if any ) */ - TextureRegion* add( ResourceId textureId, const std::string& Name = "" ); + TextureRegionPtr add( ResourceId textureId, const std::string& Name = "" ); /** Creates and add to the texture atlas a TextureRegion of the indicated part of the texture. * @param textureId The texture identity * @param SrcRect The texture part that will be used as the TextureRegion. * @param Name The texture name ( if any ) */ - TextureRegion* add( ResourceId textureId, const Rect& SrcRect, const std::string& Name = "" ); + TextureRegionPtr add( ResourceId textureId, const Rect& SrcRect, const std::string& Name = "" ); /** Creates and add to the texture atlas a TextureRegion of the indicated part of the texture. * @param textureId The texture identity @@ -44,8 +47,8 @@ class EE_API TextureAtlas : public ResourceManager { * @param DestSize The destination size that the TextureRegion will have when rendered. * @param Name The texture name ( if any ) */ - TextureRegion* add( ResourceId textureId, const Rect& SrcRect, const Sizef& DestSize, - const std::string& Name = "" ); + TextureRegionPtr add( ResourceId textureId, const Rect& SrcRect, const Sizef& DestSize, + const std::string& Name = "" ); /** Creates and add to the texture atlas a TextureRegion of the indicated part of the texture. * @param textureId The texture identity @@ -55,22 +58,22 @@ class EE_API TextureAtlas : public ResourceManager { *used. * @param Name The texture name ( if any ) */ - TextureRegion* add( ResourceId textureId, const Rect& SrcRect, const Sizef& DestSize, - const Vector2i& Offset, const std::string& Name = "" ); + TextureRegionPtr add( ResourceId textureId, const Rect& SrcRect, const Sizef& DestSize, + const Vector2i& Offset, const std::string& Name = "" ); /** Creates and add to the texture atlas a TextureRegion from a Texture. It will use the full *Texture as a TextureRegion. * @param tex The texture * @param Name The texture name ( if any ) */ - TextureRegion* add( Texture* tex, const std::string& Name = "" ); + TextureRegionPtr add( TexturePtr tex, const std::string& Name = "" ); /** Creates and add to the texture atlas a TextureRegion of the indicated part of the texture. * @param tex The texture * @param SrcRect The texture part that will be used as the TextureRegion. * @param Name The texture name ( if any ) */ - TextureRegion* add( Texture* tex, const Rect& SrcRect, const std::string& Name = "" ); + TextureRegionPtr add( TexturePtr tex, const Rect& SrcRect, const std::string& Name = "" ); /** Creates and add to the texture atlas a TextureRegion of the indicated part of the texture. * @param tex The texture @@ -78,8 +81,8 @@ class EE_API TextureAtlas : public ResourceManager { * @param DestSize The destination size that the TextureRegion will have when rendered. * @param Name The texture name ( if any ) */ - TextureRegion* add( Texture* tex, const Rect& SrcRect, const Sizef& DestSize, - const std::string& Name = "" ); + TextureRegionPtr add( TexturePtr tex, const Rect& SrcRect, const Sizef& DestSize, + const std::string& Name = "" ); /** Creates and add to the texture atlas a TextureRegion of the indicated part of the texture. * @param tex The texture @@ -89,8 +92,19 @@ class EE_API TextureAtlas : public ResourceManager { *used. * @param Name The texture name ( if any ) */ - TextureRegion* add( Texture* tex, const Rect& SrcRect, const Sizef& DestSize, - const Vector2i& Offset, const std::string& Name = "" ); + TextureRegionPtr add( TexturePtr tex, const Rect& SrcRect, const Sizef& DestSize, + const Vector2i& Offset, const std::string& Name = "" ); + + TextureRegionPtr getByName( const std::string& name ) const; + TextureRegionPtr getById( const String::HashType& id ) const; + bool remove( const TextureRegionPtr& textureRegion ); + bool removeByName( const std::string& name ); + bool removeById( const String::HashType& id ); + bool exists( const std::string& name ) const; + bool existsId( const String::HashType& id ) const; + void clear(); + void printNames() const; + const UnorderedMap& getResources() const; /** @return The texture atlas name. */ const std::string& getName() const; @@ -108,7 +122,7 @@ class EE_API TextureAtlas : public ResourceManager { const String::HashType& getId() const; /** @return The number of TextureRegions inside the texture atlas. */ - Uint32 getCount(); + Uint32 getCount() const; /** @return The texture that corresponds to the texture atlas. * @param texnum The texture index. A texture atlas can use more than one texture, so it can be @@ -119,10 +133,10 @@ class EE_API TextureAtlas : public ResourceManager { * linked to a texture. \n The Global Texture Atlas for example doesn't have any texture linked * to it. */ - Texture* getTexture( const Uint32& texnum = 0 ) const; + const TexturePtr& getTexture( const Uint32& texnum = 0 ) const; /** @return The number of textures linked to the texture atlas. */ - Uint32 getTexturesCount(); + Uint32 getTexturesCount() const; protected: friend class TextureAtlasLoader; @@ -130,9 +144,11 @@ class EE_API TextureAtlas : public ResourceManager { std::string mName; String::HashType mId; std::string mPath; - std::vector mTextures; + std::vector mTextures; + mutable System::Mutex mMutex; + UnorderedMap mResources; - void setTextures( std::vector textures ); + void setTextures( std::vector textures ); }; }} // namespace EE::Graphics diff --git a/include/eepp/graphics/textureatlasloader.hpp b/include/eepp/graphics/textureatlasloader.hpp index 7a4dfd97e..018432e40 100644 --- a/include/eepp/graphics/textureatlasloader.hpp +++ b/include/eepp/graphics/textureatlasloader.hpp @@ -3,6 +3,8 @@ #include #include +#include +#include #include #include #include @@ -12,8 +14,6 @@ namespace EE { namespace Graphics { using namespace Private; -class TextureAtlas; - /** @brief The Texture Atlas Loader loads any previously created Texture Atlas. */ class EE_API TextureAtlasLoader { public: @@ -146,13 +146,13 @@ class EE_API TextureAtlasLoader { * 0 to GetTexturesLoadedCount(). Usually a texture atlas corresponds to only one texture, so * the texture index is 0. */ - Texture* getTexture( const Uint32& texnum = 0 ) const; + const TexturePtr& getTexture( const Uint32& texnum = 0 ) const; /** @return The number of textures linked to the texture atlas. */ Uint32 getTexturesLoadedCount(); /** @return The texture atlas instance pointer ( NULL if the atlas isn't loaded yet ). */ - TextureAtlas* getTextureAtlas() const; + const TextureAtlasPtr& getTextureAtlas() const; /** Sets a load notification callback. */ void setLoadCallback( GLLoadCallback LoadCallback ); @@ -161,6 +161,11 @@ class EE_API TextureAtlasLoader { void setTextureFilter( const Texture::Filter& textureFilter ); + /** Sets the semantic texture lookup boundary used by subsequent loads. */ + void setResourceScope( ResourceScopePtr resourceScope ); + + const ResourceScopePtr& getResourceScope() const; + protected: std::string mTextureAtlasPath; bool mThreaded; @@ -168,13 +173,15 @@ class EE_API TextureAtlasLoader { Pack* mPack; bool mSkipResourceLoad; std::atomic mIsLoading; - TextureAtlas* mTextureAtlas; + TextureAtlasPtr mTextureAtlas; GLLoadCallback mLoadCallback; - std::vector mTexturesLoaded; + ResourceScopePtr mResourceScope; + std::vector mTexturesLoaded; struct sTempTexAtlas { sTextureHdr Texture; std::vector TextureRegions; + TexturePtr LoadedTexture; }; sTextureAtlasHdr mTexGrHdr; diff --git a/include/eepp/graphics/textureatlasmanager.hpp b/include/eepp/graphics/textureatlasmanager.hpp deleted file mode 100644 index b35151912..000000000 --- a/include/eepp/graphics/textureatlasmanager.hpp +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef EE_GRAPHICSCTEXTUREATLASMANAGER_HPP -#define EE_GRAPHICSCTEXTUREATLASMANAGER_HPP - -#include -#include -#include - -#include -#include -using namespace EE::System; - -namespace EE { namespace Graphics { - -/** @brief The Texture Atlas Manager is a singleton class that manages all the instances of Texture - Atlases instantiated. Releases the Texture Atlases instances automatically. So the user doesn't - need to release any Texture Atlas instance. */ -class EE_API TextureAtlasManager : public ResourceManagerMulti { - SINGLETON_DECLARE_HEADERS( TextureAtlasManager ) - - public: - virtual ~TextureAtlasManager(); - - /** Loads a texture atlas from its path ( the texture atlas binary is expected, not the texture, - * the ".eta" file ). */ - TextureAtlas* loadFromFile( const std::string& TextureAtlasPath ); - - /** Loads a texture atlas from a io stream. */ - TextureAtlas* loadFromStream( IOStream& IOS ); - - /** Loads a texture atlas from memory. */ - TextureAtlas* loadFromMemory( const Uint8* Data, const Uint32& DataSize, - const std::string& TextureAtlasName ); - - /** Loads a texture atlas from a pack file. */ - TextureAtlas* loadFromPack( Pack* Pack, const std::string& FilePackPath ); - - /** It will search for a TextureRegion Name in the texture atlases loaded. - * @return The first TextureRegion found with the given name in any atlas. */ - TextureRegion* getTextureRegionByName( const std::string& Name ); - - /** It will search for a TextureRegion Id in the texture atlases loaded. - * @return The first TextureRegion found with the given id in any atlas. */ - TextureRegion* getTextureRegionById( const String::HashType& Id ); - - /** Search for a pattern name - * For example search for name "car" with extensions "png", i will try to find car00.png - * car01.png car02.png, and so on, it will continue if find something, otherwise it will stop ( - * it will always search at least for car00.png and car01.png ) - * @param name First part of the sub texture name - * @param extension Extension of the sub texture name ( if have one, otherwise is empty ) - * @param SearchInTextureAtlas If you want only to search in a especific atlas ( NULL if you - * want to search in all atlases ) - * @note Texture atlases saves the TextureRegions names without extension by default. - */ - std::vector - getTextureRegionsByPattern( const std::string& name, const std::string& extension = "", - TextureAtlas* SearchInTextureAtlas = NULL ); - - /** Search for a pattern id. - * This will look for the TextureRegion with the id passed, and it will try to find any pattern - *by the TextureRegion name. - * @see GetTextureRegionsByPattern - */ - std::vector - getTextureRegionsByPatternId( const Uint32& TextureRegionId, const std::string& extension = "", - TextureAtlas* SearchInTextureAtlas = NULL ); - - /** Prints all the resources name to the screen. */ - void printResources(); - - /** Sets if the warnings for not finding a resource must be printed in screen. */ - void setPrintWarnings( const bool& warn ); - - /** @return If warnings are being printed. */ - const bool& getPrintWarnings() const; - - protected: - bool mWarnings; - - TextureAtlasManager(); -}; - -}} // namespace EE::Graphics - -#endif diff --git a/include/eepp/graphics/texturedrawable.hpp b/include/eepp/graphics/texturedrawable.hpp new file mode 100644 index 000000000..8b6b994ea --- /dev/null +++ b/include/eepp/graphics/texturedrawable.hpp @@ -0,0 +1,36 @@ +#ifndef EE_GRAPHICS_TEXTUREDRAWABLE_HPP +#define EE_GRAPHICS_TEXTUREDRAWABLE_HPP + +#include +#include + +namespace EE { namespace Graphics { + +class TextureDrawable; +using TextureDrawablePtr = ResourcePtr; + +/** Per-consumer drawable state backed by a shared texture resource. */ +class EE_API TextureDrawable : public DrawableResource { + public: + static TextureDrawablePtr New( TexturePtr texture ); + + explicit TextureDrawable( TexturePtr texture ); + + Sizef getSize(); + Sizef getPixelsSize(); + void draw(); + void draw( const Vector2f& position ); + void draw( const Vector2f& position, const Sizef& size ); + bool isStateful(); + DrawablePtr clone() const; + + const TexturePtr& getTexture() const; + + protected: + TexturePtr mTexture; + DrawableResourceConnection mTextureChangeConnection; +}; + +}} // namespace EE::Graphics + +#endif diff --git a/include/eepp/graphics/texturefactory.hpp b/include/eepp/graphics/texturefactory.hpp index f2ba74cd5..ed8fe5389 100644 --- a/include/eepp/graphics/texturefactory.hpp +++ b/include/eepp/graphics/texturefactory.hpp @@ -21,7 +21,7 @@ struct TextureRegistryRecord { using TextureRegistrySnapshot = std::vector; -/** @brief The Texture Manager Class. Here we do all the textures stuff. (Singleton Class) */ +/** Creates textures and weakly observes every live texture for diagnostics. */ class EE_API TextureFactory : protected Mutex { SINGLETON_DECLARE_HEADERS( TextureFactory ) @@ -39,7 +39,7 @@ class EE_API TextureFactory : protected Mutex { * @param Filename A filename to recognize the texture. * @return The created texture */ - Texture* createEmptyTexture( + TexturePtr createEmptyTexture( const unsigned int& Width, const unsigned int& Height, const unsigned int& Channels = 4, const Color& DefaultColor = Color( 0, 0, 0, 255 ), const bool& Mipmap = false, const Texture::ClampMode& ClampMode = Texture::ClampMode::ClampToEdge, @@ -60,12 +60,13 @@ class EE_API TextureFactory : protected Mutex { * outside the texture factory ). * @return The texture loaded or null if error */ - Texture* loadFromPixels( const unsigned char* Pixels, const unsigned int& Width, - const unsigned int& Height, const unsigned int& Channels, - const bool& Mipmap = false, - const Texture::ClampMode& ClampMode = Texture::ClampMode::ClampToEdge, - const bool& CompressTexture = false, const bool& KeepLocalCopy = false, - const std::string& FileName = std::string( "" ) ); + TexturePtr + loadFromPixels( const unsigned char* Pixels, const unsigned int& Width, + const unsigned int& Height, const unsigned int& Channels, + const bool& Mipmap = false, + const Texture::ClampMode& ClampMode = Texture::ClampMode::ClampToEdge, + const bool& CompressTexture = false, const bool& KeepLocalCopy = false, + const std::string& FileName = std::string( "" ) ); /** Load a texture from Pack file * @param Pack Pointer to the pack instance @@ -79,7 +80,7 @@ class EE_API TextureFactory : protected Mutex { * the image. * @return The texture loaded or null if error */ - Texture* loadFromPack( + TexturePtr loadFromPack( Pack* Pack, const std::string& FilePackPath, const bool& Mipmap = false, const Texture::ClampMode& ClampMode = Texture::ClampMode::ClampToEdge, const bool& CompressTexture = false, const bool& KeepLocalCopy = false, @@ -97,7 +98,7 @@ class EE_API TextureFactory : protected Mutex { * the image. * @return The texture loaded or null if error */ - Texture* loadFromMemory( + TexturePtr loadFromMemory( const unsigned char* ImagePtr, const unsigned int& Size, const bool& Mipmap = false, const Texture::ClampMode& ClampMode = Texture::ClampMode::ClampToEdge, const bool& CompressTexture = false, const bool& KeepLocalCopy = false, @@ -114,7 +115,7 @@ class EE_API TextureFactory : protected Mutex { * the image. * @return The texture loaded or null if error */ - Texture* loadFromStream( + TexturePtr loadFromStream( IOStream& Stream, const bool& Mipmap = false, const Texture::ClampMode& ClampMode = Texture::ClampMode::ClampToEdge, const bool& CompressTexture = false, const bool& KeepLocalCopy = false, @@ -131,24 +132,12 @@ class EE_API TextureFactory : protected Mutex { * the image. * @return The texture loaded or null if error */ - Texture* loadFromFile( + TexturePtr loadFromFile( const std::string& Filepath, const bool& Mipmap = false, const Texture::ClampMode& ClampMode = Texture::ClampMode::ClampToEdge, const bool& CompressTexture = false, const bool& KeepLocalCopy = false, const Image::FormatConfiguration& imageformatConfiguration = Image::FormatConfiguration() ); - /** Removes and unloads the texture identified by @p textureId. - * @param textureId The process-wide texture identity. - * @return True if was removed - */ - bool remove( ResourceId textureId ); - - /** Removes and Unload the Texture - * @param texture The texture pointer - * @return True if was removed - */ - bool remove( Texture* texture ); - /** Binds the texture identity indicated. This is useful if you are rendering a texture * outside this class. * @param textureId The process-wide texture identity. @@ -183,12 +172,9 @@ class EE_API TextureFactory : protected Mutex { */ void setCurrentTexture( const int& textureHandle, const Uint32& TextureUnit ); - /** Returns the number of textures loaded */ + /** Returns the number of currently live textures. */ Uint32 getTextureCount(); - /** @return All the active textures */ - std::vector getTextures(); - /** @return A non-owning diagnostic snapshot of every currently live texture. */ TextureRegistrySnapshot snapshotTextures(); @@ -215,14 +201,11 @@ class EE_API TextureFactory : protected Mutex { */ unsigned int getValidTextureSize( const unsigned int& Size ); - /** Determines whether the texture identity exists in the factory. */ + /** Determines whether the texture identity is currently live. */ bool existsId( ResourceId textureId ); - /** Determines whether the texture is retained by the factory. */ - bool exists( const Texture* tex ); - - /** @return The texture matching @p textureId, or null if it is not factory-retained. */ - Texture* getTexture( ResourceId textureId ); + /** @return The live texture matching @p textureId, or null if it has expired. */ + TexturePtr getTexture( ResourceId textureId ); /** @return The memory used by the textures (in bytes) */ unsigned int getTextureMemorySize(); @@ -243,24 +226,12 @@ class EE_API TextureFactory : protected Mutex { * @param MemSize The size of the texture in memory ( just if you need to specify the real size * in memory, just useful to calculate the total texture memory ). */ - Texture* pushTexture( const std::string& Filepath, const Uint32& textureHandle, - const unsigned int& Width, const unsigned int& Height, - const unsigned int& ImgWidth, const unsigned int& ImgHeight, - const bool& Mipmap, const unsigned int& Channels, - const Texture::ClampMode& ClampMode, const bool& CompressTexture, - const bool& LocalCopy = false, const Uint32& MemSize = 0 ); - - /** Return a texture by it file path name - * @param Name File path name - * @return The texture, NULL if not exists. - */ - Texture* getByName( const std::string& Name ); - - /** Return a texture by it hash path name - * @param Hash The file path hash - * @return The texture, NULL if not exists - */ - Texture* getByHash( const String::HashType& hash ); + TexturePtr pushTexture( const std::string& Filepath, const Uint32& textureHandle, + const unsigned int& Width, const unsigned int& Height, + const unsigned int& ImgWidth, const unsigned int& ImgHeight, + const bool& Mipmap, const unsigned int& Channels, + const Texture::ClampMode& ClampMode, const bool& CompressTexture, + const bool& LocalCopy = false, const Uint32& MemSize = 0 ); ~TextureFactory(); @@ -273,8 +244,6 @@ class EE_API TextureFactory : protected Mutex { std::vector mCurrentTexture; - using TextureMap = UnorderedMap; - struct LiveTextureRecord { ResourceId id; TextureWeakPtr texture; @@ -284,17 +253,16 @@ class EE_API TextureFactory : protected Mutex { void operator()( Texture* texture ) const noexcept; }; - TextureMap mTextures; UnorderedMap mLiveTextures; std::vector mReleasedTextures; std::atomic mLiveTextureGeneration{ 0 }; Texture::CoordinateType mLastCoordinateType; - void unloadTextures(); - void resetTextureBinding( const Texture* texture ); + /** Thread-safe final-handle handoff. Actual Texture destruction remains graphics-thread-only. + */ void queueReleasedTexture( Texture* texture ); void diagnoseLiveTexturesAtShutdown(); diff --git a/include/eepp/graphics/textureloader.hpp b/include/eepp/graphics/textureloader.hpp index 2f5706ba6..1307240df 100644 --- a/include/eepp/graphics/textureloader.hpp +++ b/include/eepp/graphics/textureloader.hpp @@ -89,9 +89,9 @@ class EE_API TextureLoader { * is done. */ void setColorKey( RGB Color ); - /** @brief Releases the texture loaded ( if was already loaded ), it will destroy the texture - * from memory. */ - void unload(); + /** Clears the loader state and releases its texture handle. Other texture owners remain valid. + */ + void reset(); /** @return The file path to the texture ( if any ) */ const std::string& getFilepath() const; @@ -100,7 +100,7 @@ class EE_API TextureLoader { ResourceId getId() const; /** @return The texture instance ( if it was loaded ). */ - Texture* getTexture() const; + const TexturePtr& getTexture() const; Image::FormatConfiguration getFormatConfiguration() const; @@ -112,7 +112,7 @@ class EE_API TextureLoader { protected: Uint32 mLoadType{ 0 }; // From memory, from path, from pack Uint8* mPixels{ nullptr }; // Texture Info - Texture* mTexture{ nullptr }; + TexturePtr mTexture; Int32 mImgWidth{ 0 }; Int32 mImgHeight{ 0 }; @@ -133,18 +133,18 @@ class EE_API TextureLoader { RGB* mColorKey{ nullptr }; Image::FormatConfiguration mFormatConfiguration; - void reset(); - private: bool mLoaded{ false }; bool mTexLoaded{ false }; bool mDirectUpload{ false }; + bool mPixelsUseSystemFree{ false }; Image::Format mImgType{ 0 }; int mIsCompressed{ 0 }; Clock mTE; void loadFile(); + void freePixels(); void loadFromFile(); void loadFromMemory(); void loadFromPack(); diff --git a/include/eepp/graphics/textureregion.hpp b/include/eepp/graphics/textureregion.hpp index 177a7e892..bca539b11 100644 --- a/include/eepp/graphics/textureregion.hpp +++ b/include/eepp/graphics/textureregion.hpp @@ -8,31 +8,36 @@ namespace EE { namespace Graphics { +class TextureRegion; +using TextureRegionPtr = ResourcePtr; +using TextureRegionWeakPtr = ResourceWeakPtr; + /** @brief A TextureRegion is a part of a texture that represent an sprite.*/ class EE_API TextureRegion : public DrawableResource { public: - static TextureRegion* New(); + static TextureRegionPtr New(); - static TextureRegion* New( ResourceId textureId, const std::string& name = "" ); + static TextureRegionPtr New( ResourceId textureId, const std::string& name = "" ); - static TextureRegion* New( ResourceId textureId, const Rect& srcRect, - const std::string& name = "" ); + static TextureRegionPtr New( ResourceId textureId, const Rect& srcRect, + const std::string& name = "" ); - static TextureRegion* New( ResourceId textureId, const Rect& srcRect, const Sizef& destSize, - const std::string& name = "" ); + static TextureRegionPtr New( ResourceId textureId, const Rect& srcRect, const Sizef& destSize, + const std::string& name = "" ); - static TextureRegion* New( ResourceId textureId, const Rect& srcRect, const Sizef& destSize, - const Vector2i& offset, const std::string& name = "" ); + static TextureRegionPtr New( ResourceId textureId, const Rect& srcRect, const Sizef& destSize, + const Vector2i& offset, const std::string& name = "" ); - static TextureRegion* New( Texture* tex, const std::string& name = "" ); + static TextureRegionPtr New( TexturePtr tex, const std::string& name = "" ); - static TextureRegion* New( Texture* tex, const Rect& srcRect, const std::string& name = "" ); + static TextureRegionPtr New( TexturePtr tex, const Rect& srcRect, + const std::string& name = "" ); - static TextureRegion* New( Texture* tex, const Rect& srcRect, const Sizef& destSize, - const std::string& name = "" ); + static TextureRegionPtr New( TexturePtr tex, const Rect& srcRect, const Sizef& destSize, + const std::string& name = "" ); - static TextureRegion* New( Texture* tex, const Rect& srcRect, const Sizef& destSize, - const Vector2i& offset, const std::string& name = "" ); + static TextureRegionPtr New( TexturePtr tex, const Rect& srcRect, const Sizef& destSize, + const Vector2i& offset, const std::string& name = "" ); /** Creates an empty TextureRegion */ TextureRegion(); @@ -41,14 +46,14 @@ class EE_API TextureRegion : public DrawableResource { * @param tex The texture * @param name The texture name ( if any ) */ - TextureRegion( Texture* tex, const std::string& name = "" ); + TextureRegion( TexturePtr tex, const std::string& name = "" ); /** Creates a TextureRegion of the indicated part of the texture. * @param tex The texture * @param srcRect The texture part that will be used as the TextureRegion. * @param name The texture name ( if any ) */ - TextureRegion( Texture* tex, const Rect& srcRect, const std::string& name = "" ); + TextureRegion( TexturePtr tex, const Rect& srcRect, const std::string& name = "" ); /** Creates a TextureRegion of the indicated part of the texture. * @param tex The texture @@ -56,7 +61,7 @@ class EE_API TextureRegion : public DrawableResource { * @param destSize The destination size that the TextureRegion will have when rendered. * @param name The texture name ( if any ) */ - TextureRegion( Texture* tex, const Rect& srcRect, const Sizef& destSize, + TextureRegion( TexturePtr tex, const Rect& srcRect, const Sizef& destSize, const std::string& name = "" ); /** Creates a TextureRegion of the indicated part of the texture. @@ -67,8 +72,8 @@ class EE_API TextureRegion : public DrawableResource { *used. * @param name The texture name ( if any ) */ - TextureRegion( Texture* tex, const Rect& srcRect, const Sizef& destSize, const Vector2i& offset, - const std::string& name = "" ); + TextureRegion( TexturePtr tex, const Rect& srcRect, const Sizef& destSize, + const Vector2i& offset, const std::string& name = "" ); virtual ~TextureRegion(); @@ -76,7 +81,7 @@ class EE_API TextureRegion : public DrawableResource { void setTextureId( ResourceId textureId ); /** Set the Texture that holds the TextureRegion. */ - void setTexture( Texture* texture ); + void setTexture( TexturePtr texture ); /** @return The Texture sector that represents the TextureRegion */ const Rect& getSrcRect() const; @@ -126,8 +131,10 @@ class EE_API TextureRegion : public DrawableResource { virtual bool isStateful() { return false; } + DrawablePtr clone() const; + /** @return The texture instance used by the TextureRegion. */ - Graphics::Texture* getTexture(); + const TexturePtr& getTexture() const; /** Replaces a color in the TextureRegion ( needs Lock() ) */ void replaceColor( Color ColorKey, Color NewColor ); @@ -204,7 +211,7 @@ class EE_API TextureRegion : public DrawableResource { protected: Uint8* mPixels; Uint8* mAlphaMask; - Graphics::Texture* mTexture; + TexturePtr mTexture; Rect mSrcRect; Sizef mOriDestSize; Sizef mDestSize; diff --git a/include/eepp/graphics/triangledrawable.hpp b/include/eepp/graphics/triangledrawable.hpp index 5c3560018..625fc68d0 100644 --- a/include/eepp/graphics/triangledrawable.hpp +++ b/include/eepp/graphics/triangledrawable.hpp @@ -28,6 +28,8 @@ class EE_API TriangleDrawable : public PrimitiveDrawable { virtual bool isStateful() { return false; } + DrawablePtr clone() const; + void setSize( const Sizef& size ); const Triangle2f& getTriangle() const; @@ -41,7 +43,7 @@ class EE_API TriangleDrawable : public PrimitiveDrawable { Triangle2f mComputedTriangle; Sizef mSize; Color mColors[3]; - bool mCustomColors; + bool mCustomColors{ false }; virtual void onColorFilterChange(); diff --git a/include/eepp/graphics/vertexbuffer.hpp b/include/eepp/graphics/vertexbuffer.hpp index 4d42be7a4..dd360b6ce 100644 --- a/include/eepp/graphics/vertexbuffer.hpp +++ b/include/eepp/graphics/vertexbuffer.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -10,6 +11,9 @@ using namespace EE::System; namespace EE { namespace Graphics { +class VertexBuffer; +using VertexBufferUniquePtr = std::unique_ptr>; + /** @brief The vertex buffer class holds vertex data. The vertex position, colors, texture *coordinates and indexes. This is useful to accelerate and encapsulate data. */ @@ -27,14 +31,14 @@ class EE_API VertexBuffer { *extensions are supported ( almost for sure that it's supported ). More information here: *http://www.opengl.org/sdk/docs/man/xhtml/glBufferData.xml */ - static VertexBuffer* New( const Uint32& vertexFlags = VERTEX_FLAGS_DEFAULT, - PrimitiveType drawType = PRIMITIVE_QUADS, - const Int32& reserveVertexSize = 0, const Int32& reserveIndexSize = 0, - VertexBufferUsageType usageType = VertexBufferUsageType::Static ); + static VertexBufferUniquePtr + New( const Uint32& vertexFlags = VERTEX_FLAGS_DEFAULT, PrimitiveType drawType = PRIMITIVE_QUADS, + const Int32& reserveVertexSize = 0, const Int32& reserveIndexSize = 0, + VertexBufferUsageType usageType = VertexBufferUsageType::Static ); /** Creates the simple vertex array implementation ( without VBOs or VAO ), which it's faster * for many cases. */ - static VertexBuffer* + static VertexBufferUniquePtr NewVertexArray( const Uint32& vertexFlags = VERTEX_FLAGS_DEFAULT, PrimitiveType drawType = PRIMITIVE_QUADS, const Int32& reserveVertexSize = 0, const Int32& reserveIndexSize = 0, @@ -215,7 +219,7 @@ class EE_API VertexBuffer { // Creates a rounded rectangle. Polygon2f Poly = Polygon2f::createRoundedRectangle( 0, 0, 256, 50 ); - VertexBuffer * VBO = VertexBuffer::New( VERTEX_FLAGS_PRIMITIVE, PRIMITIVE_TRIANGLE_FAN ); + auto VBO = VertexBuffer::New( VERTEX_FLAGS_PRIMITIVE, PRIMITIVE_TRIANGLE_FAN ); if ( NULL != VBO ) { // Upload the rounded rectangle data to the vertex buffer. diff --git a/include/eepp/graphics/vertexbuffermanager.hpp b/include/eepp/graphics/vertexbuffermanager.hpp index 4525ba6dd..3988793d7 100644 --- a/include/eepp/graphics/vertexbuffermanager.hpp +++ b/include/eepp/graphics/vertexbuffermanager.hpp @@ -10,16 +10,17 @@ using namespace EE::System; namespace EE { namespace Graphics { namespace Private { -class EE_API VertexBufferManager : public Container { - SINGLETON_DECLARE_HEADERS( VertexBufferManager ) +/** Non-owning registry of vertex buffers visible to the active graphics context. */ +class EE_API VertexBufferRegistry : public Container { + SINGLETON_DECLARE_HEADERS( VertexBufferRegistry ) public: - virtual ~VertexBufferManager(); + virtual ~VertexBufferRegistry(); void reload(); protected: - VertexBufferManager(); + VertexBufferRegistry(); }; }}} // namespace EE::Graphics::Private diff --git a/include/eepp/scene/scenenode.hpp b/include/eepp/scene/scenenode.hpp index ff1fa121f..2a56e74c1 100644 --- a/include/eepp/scene/scenenode.hpp +++ b/include/eepp/scene/scenenode.hpp @@ -1,14 +1,12 @@ #ifndef EE_SCENENODE_HPP #define EE_SCENENODE_HPP +#include #include #include #include #include -namespace EE { namespace Graphics { -class FrameBuffer; -}} // namespace EE::Graphics using namespace EE::Graphics; namespace EE { namespace Window { @@ -421,7 +419,7 @@ class EE_API SceneNode : public Node { EE::Window::Window* mWindow; ActionManager* mActionManager; - FrameBuffer* mFrameBuffer; + Graphics::FrameBufferUniquePtr mFrameBuffer; EventDispatcher* mEventDispatcher; CloseList mCloseList; Clock mClock; diff --git a/include/eepp/system.hpp b/include/eepp/system.hpp index 39d9982a6..5fb93e7f3 100644 --- a/include/eepp/system.hpp +++ b/include/eepp/system.hpp @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include diff --git a/include/eepp/system/resourcemanager.hpp b/include/eepp/system/resourcemanager.hpp deleted file mode 100644 index cd72f795d..000000000 --- a/include/eepp/system/resourcemanager.hpp +++ /dev/null @@ -1,411 +0,0 @@ -#ifndef EE_SYSTEMTRESOURCEMANAGER_HPP -#define EE_SYSTEMTRESOURCEMANAGER_HPP - -#include -#include -#include -#include -#include -#include - -namespace EE { namespace System { - -/** @brief A simple resource manager. It keeps a list of the resources, and free the instances of - * the resources when the manager is closed. Resources must implement getId() and getName() - * properties getId() is the string hash of getName(). - */ -template class ResourceManager { - public: - ResourceManager(); - - /** @brief The destructor will call destroy() and destroy all the resources added to the manager - */ - virtual ~ResourceManager(); - - /** @brief Add the resource to the resource manager - ** @param resource The resource to be managed by the manager */ - virtual T* add( T* resource ); - - /** @brief Removes the resource from the manager - ** @param resource The resource to remove - ** @param remove Indicates if the resource must be destroyed after being removed from the - *manager */ - bool remove( T* resource, bool remove = true ); - - /** @brief Removes the resource by its id - ** @see remove */ - bool removeById( const String::HashType& id, bool remove = true ); - - /** @brief Removes the resource by its name - ** @see remove */ - bool removeByName( const std::string& name, bool remove = true ); - - /** @returns A resource by its name. If not found returns NULL. */ - T* getByName( const std::string& name ); - - /** @returns A resource by its id. If not found returns NULL. */ - T* getById( const String::HashType& id ); - - /** @returns The number of resources added */ - Uint32 getCount(); - - /** @returns The number of resources that where added with the indicated name. */ - Uint32 getCount( const std::string& name ); - - /** @returns The number of resources that where added with the indicated id. */ - Uint32 getCount( const String::HashType& id ); - - /** @returns If the resource name exists in the resources list. */ - bool exists( const std::string& name ); - - /** @returns If the resource id exists in the resources list. */ - bool existsId( const String::HashType& id ); - - /** @brief Destroy all the resources added ( delete the instances of the resources ) */ - void destroy(); - - /** @brief Prints all the resources names added to the manager. */ - void printNames(); - - /** @returns A reference to the resources list of the manager. */ - UnorderedMap& getResources(); - - /** @brief Indicates if the resource manager is destroy the resources. */ - const bool& isDestroying() const; - - template void each( Predicate pred ) const { - for ( const auto& res : mResources ) - pred( res ); - } - - template void each( Predicate pred ) { - for ( auto& res : mResources ) - pred( res ); - } - - template T* findIf( Predicate pred ) const { - for ( const auto& res : mResources ) - if ( pred( res ) ) - return res.second; - return nullptr; - } - - template T* findIf( Predicate pred ) { - for ( auto& res : mResources ) - if ( pred( res ) ) - return res.second; - return nullptr; - } - - protected: - Mutex mMutex; - UnorderedMap mResources; - bool mIsDestroying; -}; - -template ResourceManager::ResourceManager() : mIsDestroying( false ) {} - -template const bool& ResourceManager::isDestroying() const { - return mIsDestroying; -} - -template ResourceManager::~ResourceManager() { - destroy(); -} - -template void ResourceManager::destroy() { - mIsDestroying = true; - - { - Lock l( mMutex ); - for ( auto& it : mResources ) { - T* res = it.second; - eeSAFE_DELETE( res ); - } - mResources.clear(); - } - - mIsDestroying = false; -} - -// This is not thread safe -template UnorderedMap& ResourceManager::getResources() { - return mResources; -} - -template T* ResourceManager::add( T* resource ) { - if ( NULL != resource ) { - if ( !existsId( resource->getId() ) ) { - Lock l( mMutex ); - mResources[resource->getId()] = resource; - - return resource; - } else { - std::string realName( resource->getName() ); - Uint32 c = 1; - - while ( existsId( resource->getId() ) ) { - c++; - resource->setName( realName + String::toString( c ) ); - } - - return add( resource ); - } - - Lock l( mMutex ); - mResources[resource->getId()] = resource; - return resource; - } - return NULL; -} - -template bool ResourceManager::remove( T* resource, bool remove ) { - if ( NULL != resource ) { - { - - Lock l( mMutex ); - mResources.erase( resource->getId() ); - } - if ( remove ) - eeSAFE_DELETE( resource ); - - return true; - } - - return false; -} - -template bool ResourceManager::removeById( const String::HashType& id, bool _remove ) { - return remove( getById( id ), _remove ); -} - -template bool ResourceManager::removeByName( const std::string& name, bool _remove ) { - return remove( getByName( name ), _remove ); -} - -template bool ResourceManager::exists( const std::string& name ) { - return existsId( String::hash( name ) ); -} - -template bool ResourceManager::existsId( const String::HashType& id ) { - Lock l( mMutex ); - return mResources.find( id ) != mResources.end(); -} - -template T* ResourceManager::getByName( const std::string& name ) { - return getById( String::hash( name ) ); -} - -template T* ResourceManager::getById( const String::HashType& id ) { - Lock l( mMutex ); - auto it = mResources.find( id ); - return it != mResources.end() ? it->second : nullptr; -} - -template void ResourceManager::printNames() { - Lock l( mMutex ); - for ( auto& it : mResources ) { - eePRINTL( "'%s'", it.second->getName().c_str() ); - } -} - -template Uint32 ResourceManager::getCount() { - Lock l( mMutex ); - return (Uint32)mResources.size(); -} - -template Uint32 ResourceManager::getCount( const String::HashType& id ) { - return existsId( id ) ? 1 : 0; -} - -template Uint32 ResourceManager::getCount( const std::string& name ) { - return getCount( String::hash( name ) ); -} - -/** @brief A simple resource manager. It keeps a list of the resources, and free the instances of - * the resources when the manager is closed. Resources must implement getId() and getName() - * properties getId() is the string hash of getName(). Allows repeated keys. - */ -template class ResourceManagerMulti { - public: - /** @param UniqueId Indicates if the resources id must be unique */ - ResourceManagerMulti(); - - /** @brief The destructor will call destroy() and destroy all the resources added to the manager - */ - virtual ~ResourceManagerMulti(); - - /** @brief Add the resource to the resource manager - ** @param resource The resource to be managed by the manager */ - virtual T* add( T* resource ); - - /** @brief Removes the resource from the manager - ** @param resource The resource to remove - ** @param remove Indicates if the resource must be destroyed after being removed from the - *manager */ - bool remove( T* resource, bool remove = true ); - - /** @brief Removes the resource by its id - ** @see remove */ - bool removeById( const String::HashType& id, bool remove = true ); - - /** @brief Removes the resource by its name - ** @see remove */ - bool removeByName( const std::string& name, bool remove = true ); - - /** @returns A resource by its name. If not found returns NULL. */ - T* getByName( const std::string& name ); - - /** @returns A resource by its id. If not found returns NULL. */ - T* getById( const String::HashType& id ); - - /** @returns The number of resources added */ - Uint32 getCount(); - - /** @returns The number of resources that where added with the indicated name. */ - Uint32 getCount( const std::string& name ); - - /** @returns The number of resources that where added with the indicated id. */ - Uint32 getCount( const String::HashType& id ); - - /** @returns If the resource name exists in the resources list. */ - bool exists( const std::string& name ); - - /** @returns If the resource id exists in the resources list. */ - bool existsId( const String::HashType& id ); - - /** @brief Destroy all the resources added ( delete the instances of the resources ) */ - void destroy(); - - /** @brief Prints all the resources names added to the manager. */ - void printNames(); - - /** @returns A reference to the resources list of the manager. */ - std::unordered_multimap& getResources(); - - /** @brief Indicates if the resource manager is destroy the resources. */ - const bool& isDestroying() const; - - protected: - Mutex mMutex; - std::unordered_multimap mResources; - bool mIsDestroying; -}; - -template ResourceManagerMulti::ResourceManagerMulti() : mIsDestroying( false ) {} - -template const bool& ResourceManagerMulti::isDestroying() const { - return mIsDestroying; -} - -template ResourceManagerMulti::~ResourceManagerMulti() { - destroy(); -} - -template void ResourceManagerMulti::destroy() { - mIsDestroying = true; - - { - Lock l( mMutex ); - for ( auto& it : mResources ) { - T* res = it.second; - eeSAFE_DELETE( res ); - } - - mResources.clear(); - } - - mIsDestroying = false; -} - -template -std::unordered_multimap& ResourceManagerMulti::getResources() { - return mResources; -} - -template T* ResourceManagerMulti::add( T* resource ) { - if ( NULL != resource ) { - Lock l( mMutex ); - mResources.insert( std::pair( resource->getId(), resource ) ); - return resource; - } - return NULL; -} - -template bool ResourceManagerMulti::remove( T* resource, bool remove ) { - if ( NULL != resource ) { - { - Lock l( mMutex ); - auto range = mResources.equal_range( resource->getId() ); - auto it = range.first; - while ( it != range.second ) { - if ( it->second == resource ) { - mResources.erase( it ); - break; - } - it++; - } - } - - if ( remove ) - eeSAFE_DELETE( resource ); - - return true; - } - - return false; -} - -template -bool ResourceManagerMulti::removeById( const String::HashType& id, bool _remove ) { - return remove( getById( id ), _remove ); -} - -template -bool ResourceManagerMulti::removeByName( const std::string& name, bool _remove ) { - return remove( getByName( name ), _remove ); -} - -template bool ResourceManagerMulti::exists( const std::string& name ) { - return existsId( String::hash( name ) ); -} - -template bool ResourceManagerMulti::existsId( const String::HashType& id ) { - Lock l( mMutex ); - return mResources.find( id ) != mResources.end(); -} - -template T* ResourceManagerMulti::getByName( const std::string& name ) { - return getById( String::hash( name ) ); -} - -template T* ResourceManagerMulti::getById( const String::HashType& id ) { - Lock l( mMutex ); - auto it = mResources.find( id ); - return it != mResources.end() ? it->second : nullptr; -} - -template void ResourceManagerMulti::printNames() { - Lock l( mMutex ); - for ( auto& it : mResources ) { - eePRINTL( "'%s'", it.second->getName().c_str() ); - } -} - -template Uint32 ResourceManagerMulti::getCount() { - Lock l( mMutex ); - return (Uint32)mResources.size(); -} - -template Uint32 ResourceManagerMulti::getCount( const String::HashType& id ) { - Lock l( mMutex ); - return mResources.count( id ); -} - -template Uint32 ResourceManagerMulti::getCount( const std::string& name ) { - return getCount( String::hash( name ) ); -} - -}} // namespace EE::System - -#endif diff --git a/include/eepp/ui.hpp b/include/eepp/ui.hpp index ee836c590..38488a760 100644 --- a/include/eepp/ui.hpp +++ b/include/eepp/ui.hpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -179,6 +180,7 @@ #include #include #include +#include #include #endif diff --git a/include/eepp/ui/css/drawableimageparser.hpp b/include/eepp/ui/css/drawableimageparser.hpp index 5e9160a45..d70809597 100644 --- a/include/eepp/ui/css/drawableimageparser.hpp +++ b/include/eepp/ui/css/drawableimageparser.hpp @@ -2,14 +2,12 @@ #define EE_UI_CSS_DRAWABLEIMAGEPARSER_HPP #include +#include #include #include #include #include -namespace EE { namespace Graphics { -class Drawable; -}} // namespace EE::Graphics namespace EE { namespace UI { class UINode; }} // namespace EE::UI @@ -20,8 +18,8 @@ using namespace EE::System; namespace EE { namespace UI { namespace CSS { -typedef std::function +typedef std::function DrawableImageParserFunc; class EE_API DrawableImageParser { @@ -30,8 +28,7 @@ class EE_API DrawableImageParser { bool exists( const std::string& name ) const; - Drawable* createDrawable( const std::string& value, const Sizef& size, bool& ownIt, - UINode* node ); + DrawablePtr createDrawable( const std::string& value, const Sizef& size, UINode* node ); void addParser( const std::string& name, const DrawableImageParserFunc& func ); diff --git a/include/eepp/ui/drawableresolver.hpp b/include/eepp/ui/drawableresolver.hpp new file mode 100644 index 000000000..ee4c94d54 --- /dev/null +++ b/include/eepp/ui/drawableresolver.hpp @@ -0,0 +1,32 @@ +#ifndef EE_UI_DRAWABLERESOLVER_HPP +#define EE_UI_DRAWABLERESOLVER_HPP + +#include +#include + +namespace EE { namespace UI { + +class UISceneNode; + +/** Resolves named drawables through an explicit UI scene or Graphics resource scope. */ +class EE_API DrawableResolver { + public: + explicit DrawableResolver( UISceneNode& sceneNode ); + explicit DrawableResolver( Graphics::ResourceScope& resourceScope ); + + Graphics::DrawablePtr resolve( const std::string& name, bool firstSearchSprite = false ) const; + Graphics::DrawablePtr resolveById( Graphics::ResourceNameHash hash ) const; + Graphics::DrawablePtr resolveById( String::HashType legacyHash ) const; + + void setPrintWarnings( bool printWarnings ); + bool getPrintWarnings() const; + + protected: + UISceneNode* mSceneNode{ nullptr }; + Graphics::ResourceScope* mResourceScope{ nullptr }; + bool mPrintWarnings{ false }; +}; + +}} // namespace EE::UI + +#endif diff --git a/include/eepp/ui/iconmanager.hpp b/include/eepp/ui/iconmanager.hpp index 91274f898..b404a6627 100644 --- a/include/eepp/ui/iconmanager.hpp +++ b/include/eepp/ui/iconmanager.hpp @@ -13,8 +13,9 @@ class UIIconTheme; class EE_API IconManager { public: - static UIIconTheme* init( const std::string& iconThemeName, FontTrueType* remixIconFont, - FontTrueType* noniconFont, FontTrueType* codIconFont ); + static ResourcePtr init( const std::string& iconThemeName, + FontTrueType* remixIconFont, FontTrueType* noniconFont, + FontTrueType* codIconFont ); }; }} // namespace EE::UI diff --git a/include/eepp/ui/lineargradientdrawable.hpp b/include/eepp/ui/lineargradientdrawable.hpp index 5f2ea908f..e4fe8e1e7 100644 --- a/include/eepp/ui/lineargradientdrawable.hpp +++ b/include/eepp/ui/lineargradientdrawable.hpp @@ -50,6 +50,8 @@ class EE_API LinearGradientDrawable : public Graphics::Drawable { virtual bool isStateful() { return false; } + Graphics::DrawablePtr clone() const; + const std::vector& getColorStops() const; void setColorStops( std::vector stops ); diff --git a/include/eepp/ui/models/variant.hpp b/include/eepp/ui/models/variant.hpp index 4c47e67c0..15d30c646 100644 --- a/include/eepp/ui/models/variant.hpp +++ b/include/eepp/ui/models/variant.hpp @@ -43,10 +43,7 @@ class EE_API Variant { explicit Variant( const String* string ) : mType( Type::StringPtr ) { mValue.asStringPtr = string; } - Variant( Drawable* drawable, bool ownDrawable = false ) : mType( Type::Drawable ) { - mValue.asDrawable = drawable; - mOwnsObject = ownDrawable; - } + Variant( DrawablePtr drawable ) : mDrawable( std::move( drawable ) ), mType( Type::Drawable ) {} Variant( UIIcon* icon ) : mType( Type::Icon ) { mValue.asIcon = icon; } Variant( const Vector2f& v ) : mType( Type::Vector2f ) { mValue.asVector2f = eeNew( Vector2f, ( v ) ); @@ -62,7 +59,7 @@ class EE_API Variant { explicit Variant( const char* data ) : mType( Type::cstr ) { mValue.asCStr = data; } ~Variant() { reset(); } - Variant( const Variant& other ) : mType( Type::Invalid ), mOwnsObject( other.mOwnsObject ) { + Variant( const Variant& other ) : mType( Type::Invalid ) { switch ( other.mType ) { case Type::StdString: mValue.asStdString = eeNew( std::string, ( *other.mValue.asStdString ) ); @@ -74,7 +71,7 @@ class EE_API Variant { mValue.asStringPtr = other.mValue.asStringPtr; break; case Type::Drawable: - mValue.asDrawable = other.mValue.asDrawable; + mDrawable = other.mDrawable; break; case Type::Icon: mValue.asIcon = other.mValue.asIcon; @@ -116,9 +113,8 @@ class EE_API Variant { } Variant( Variant&& other ) noexcept : - mValue( other.mValue ), mType( other.mType ), mOwnsObject( other.mOwnsObject ) { + mValue( other.mValue ), mDrawable( std::move( other.mDrawable ) ), mType( other.mType ) { other.mType = Type::Invalid; - other.mOwnsObject = false; other.mValue = {}; } @@ -134,10 +130,9 @@ class EE_API Variant { if ( this != &other ) { reset(); mType = other.mType; - mOwnsObject = other.mOwnsObject; + mDrawable = std::move( other.mDrawable ); mValue = other.mValue; other.mType = Type::Invalid; - other.mOwnsObject = false; other.mValue = {}; } return *this; @@ -146,7 +141,7 @@ class EE_API Variant { const std::string& asStdString() const { return *mValue.asStdString; } const String& asString() const { return *mValue.asString; } const String& asStringPtr() const { return *mValue.asStringPtr; } - Drawable* asDrawable() const { return mValue.asDrawable; } + const DrawablePtr& asDrawable() const { return mDrawable; } const bool& asBool() const { return mValue.asBool; } const Float& asFloat() const { return mValue.asFloat; } const int& asInt() const { return mValue.asInt; } @@ -172,8 +167,7 @@ class EE_API Variant { eeSAFE_DELETE( mValue.asString ); break; case Type::Drawable: - if ( mOwnsObject ) - eeSAFE_DELETE( mValue.asDrawable ); + mDrawable.reset(); break; case Type::Vector2f: eeSAFE_DELETE( mValue.asVector2f ); @@ -210,7 +204,7 @@ class EE_API Variant { return asStringPtr(); case Type::Drawable: return asDrawable()->isDrawableResource() - ? static_cast( asDrawable() )->getName() + ? static_cast( asDrawable().get() )->getName() : "Drawable"; case Type::Icon: return asIcon()->getName(); @@ -330,7 +324,7 @@ class EE_API Variant { case Type::StringPtr: return asStringPtr().size(); case Type::Drawable: - return sizeof( mValue.asDrawable ); + return sizeof( mDrawable ); case Type::Icon: return asIcon()->getName().size(); case Type::DataPtr: @@ -350,7 +344,6 @@ class EE_API Variant { private: union { void* asDataPtr{ nullptr }; - Drawable* asDrawable; UIIcon* asIcon; std::string* asStdString; String* asString; @@ -365,8 +358,8 @@ class EE_API Variant { Rectf* asRectf; const char* asCStr; } mValue; + DrawablePtr mDrawable; Type mType; - bool mOwnsObject{ false }; }; }}} // namespace EE::UI::Models diff --git a/include/eepp/ui/radialgradientdrawable.hpp b/include/eepp/ui/radialgradientdrawable.hpp index 1a9b94b83..1161f1753 100644 --- a/include/eepp/ui/radialgradientdrawable.hpp +++ b/include/eepp/ui/radialgradientdrawable.hpp @@ -54,6 +54,8 @@ class EE_API RadialGradientDrawable : public Graphics::Drawable { virtual bool isStateful() { return false; } + Graphics::DrawablePtr clone() const; + const std::vector& getColorStops() const; void setColorStops( std::vector stops ); diff --git a/include/eepp/ui/tools/htmlformatter.hpp b/include/eepp/ui/tools/htmlformatter.hpp index f3760d933..76ff4baab 100644 --- a/include/eepp/ui/tools/htmlformatter.hpp +++ b/include/eepp/ui/tools/htmlformatter.hpp @@ -9,6 +9,9 @@ namespace EE { namespace UI { namespace Tools { class EE_API HTMLFormatter { public: static std::string HTMLtoXML( const std::string& layoutString ); + + /** Converts HTML to XML, serializing only the children of the parsed body element. */ + static std::string HTMLBodyToXML( const std::string& layoutString ); }; }}} // namespace EE::UI::Tools diff --git a/include/eepp/ui/tools/textureatlaseditor.hpp b/include/eepp/ui/tools/textureatlaseditor.hpp index 4b151abbc..9e7cb7f41 100644 --- a/include/eepp/ui/tools/textureatlaseditor.hpp +++ b/include/eepp/ui/tools/textureatlaseditor.hpp @@ -2,7 +2,6 @@ #define EE_UITOOLSCTEXTUREATLASEDITOR_HPP #include -#include #include #include #include diff --git a/include/eepp/ui/tools/uicolorpicker.hpp b/include/eepp/ui/tools/uicolorpicker.hpp index 409757b65..4021eb7d3 100644 --- a/include/eepp/ui/tools/uicolorpicker.hpp +++ b/include/eepp/ui/tools/uicolorpicker.hpp @@ -78,9 +78,9 @@ class EE_API UIColorPicker { void windowClose( const Event* Event ); - Texture* createHueTexture( const Sizef& size ); + TexturePtr createHueTexture( const Sizef& size ); - Texture* createGridTexture(); + TexturePtr createGridTexture(); void updateColorPicker(); diff --git a/include/eepp/ui/tools/uifontpickerdialog.hpp b/include/eepp/ui/tools/uifontpickerdialog.hpp index 58d1858ed..1c70dd623 100644 --- a/include/eepp/ui/tools/uifontpickerdialog.hpp +++ b/include/eepp/ui/tools/uifontpickerdialog.hpp @@ -118,6 +118,9 @@ class EE_API UIFontPickerDialog : public UIWindow { std::vector mSizes; UnorderedSet mLoadedFontKeys; UnorderedMap mFontTags; + Graphics::FontTrueTypePtr mPreviewFont; + Graphics::Font* mPreviewTextDefaultFont{ nullptr }; + Graphics::Font* mPreviewInputDefaultFont{ nullptr }; std::shared_ptr mFamilyModel; std::shared_ptr mStyleModel; std::shared_ptr mSizeModel; @@ -171,7 +174,7 @@ class EE_API UIFontPickerDialog : public UIWindow { void sortFonts(); - void mergeFontManagerFonts( std::vector& fonts ); + void mergeLoadedFonts( std::vector& fonts ); void updateFontTags(); @@ -189,6 +192,8 @@ class EE_API UIFontPickerDialog : public UIWindow { void updatePreview(); + void clearPreviewFont(); + void selectInitialRows(); void selectFamily( const std::string& family ); diff --git a/include/eepp/ui/uibackgrounddrawable.hpp b/include/eepp/ui/uibackgrounddrawable.hpp index b1957e1d1..cf6d0bf85 100644 --- a/include/eepp/ui/uibackgrounddrawable.hpp +++ b/include/eepp/ui/uibackgrounddrawable.hpp @@ -2,6 +2,7 @@ #define EE_UI_UIBACKGROUNDDRAWABLE_HPP #include +#include #include using namespace EE::Graphics; @@ -63,7 +64,7 @@ class EE_API UIBackgroundDrawable : public Drawable { protected: const UINode* mOwner; BorderRadiuseStr mRadiusesStr; - VertexBuffer* mVertexBuffer; + Graphics::VertexBufferUniquePtr mVertexBuffer; Sizef mSize; BorderRadiuses mRadiuses; bool mNeedsUpdate; diff --git a/include/eepp/ui/uiborderdrawable.hpp b/include/eepp/ui/uiborderdrawable.hpp index 9f2b50577..7cda1cabc 100644 --- a/include/eepp/ui/uiborderdrawable.hpp +++ b/include/eepp/ui/uiborderdrawable.hpp @@ -2,6 +2,7 @@ #define EE_UI_UIBORDERDRAWABLE_HPP #include +#include #include #include @@ -87,7 +88,7 @@ class EE_API UIBorderDrawable : public Drawable { protected: const UINode* mOwner; - VertexBuffer* mVertexBuffer; + Graphics::VertexBufferUniquePtr mVertexBuffer; mutable Borders mBorders; BorderStr mBorderStr; BorderType mBorderType; diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index bce99ca02..c59a66c69 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -768,13 +768,13 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void setShowFoldingRegion( bool showFoldingRegion ); - Drawable* getFoldDrawable() const; + const DrawablePtr& getFoldDrawable() const; - void setFoldDrawable( Drawable* foldDrawable ); + void setFoldDrawable( DrawablePtr foldDrawable ); - Drawable* getFoldedDrawable() const; + const DrawablePtr& getFoldedDrawable() const; - void setFoldedDrawable( Drawable* foldedDrawable ); + void setFoldedDrawable( DrawablePtr foldedDrawable ); bool getFoldsAlwaysVisible() const; @@ -999,8 +999,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { UIIcon* mFileLockIcon{ nullptr }; std::string mFileLockIconName{ "file-lock-fill" }; LineWrapType mLineWrapType{ LineWrapType::Viewport }; - Drawable* mFoldDrawable{ nullptr }; - Drawable* mFoldedDrawable{ nullptr }; + DrawablePtr mFoldDrawable; + DrawablePtr mFoldedDrawable; String::HashType mTagFoldRange{ 0 }; Uint32 mTabIndentCharacter{ 187 /*'»'*/ }; CharacterAlignment mTabIndentAlignment{ CharacterAlignment::Center }; @@ -1157,7 +1157,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void updateGlyphWidth(); - Drawable* findIcon( const std::string& name ); + DrawablePtr findIcon( const std::string& name ); void createDefaultContextMenuOptions( UIPopUpMenu* menu ); diff --git a/include/eepp/ui/uiconsole.hpp b/include/eepp/ui/uiconsole.hpp index 884052ab4..68b159196 100644 --- a/include/eepp/ui/uiconsole.hpp +++ b/include/eepp/ui/uiconsole.hpp @@ -343,7 +343,7 @@ class EE_API UIConsole : public UIWidget, UIMenuItem* menuAdd( UIPopUpMenu* menu, const String& translateString, const std::string& icon, const std::string& cmd ); - Drawable* findIcon( const std::string& name ); + DrawablePtr findIcon( const std::string& name ); void copySelection(); diff --git a/include/eepp/ui/uiicon.hpp b/include/eepp/ui/uiicon.hpp index f7f4c1821..2fe78dce3 100644 --- a/include/eepp/ui/uiicon.hpp +++ b/include/eepp/ui/uiicon.hpp @@ -12,32 +12,44 @@ using namespace EE::Graphics; namespace EE { namespace UI { +class UIIcon; +using UIIconPtr = ResourcePtr; +using UIIconWeakPtr = ResourceWeakPtr; + class EE_API UIIcon { public: - static UIIcon* New( const std::string& name ); + static UIIconPtr New( const std::string& name ); virtual ~UIIcon(); const std::string& getName() const; - virtual Drawable* getSize( const int& size ) const; + /** Returns the icon source closest to the requested size. + * + * The returned drawable is shared icon data and must not be mutated by consumers. Use + * createDrawable() when a consumer needs its own mutable drawable instance. */ + virtual const DrawablePtr& getSource( const int& size ) const; - virtual void setSize( const int& size, Drawable* drawable ); + /** Creates a private drawable instance for a consumer. This must not be called from a rendering + * loop; retain the returned instance instead. */ + DrawablePtr createDrawable( const int& size ) const; + + virtual void setSource( const int& size, DrawablePtr drawable ); protected: UIIcon( const std::string& name ); std::string mName; - mutable UnorderedMap mSizes; + mutable UnorderedMap mSizes; }; class EE_API UIGlyphIcon : public UIIcon { public: - static UIIcon* New( const std::string& name, FontTrueType* font, const Uint32& codePoint ); + static UIIconPtr New( const std::string& name, FontTrueType* font, const Uint32& codePoint ); virtual ~UIGlyphIcon(); - virtual Drawable* getSize( const int& size ) const; + virtual const DrawablePtr& getSource( const int& size ) const; protected: UIGlyphIcon( const std::string& name, FontTrueType* font, const Uint32& codePoint ); @@ -49,17 +61,16 @@ class EE_API UIGlyphIcon : public UIIcon { class EE_API UISVGIcon : public UIIcon { public: - static UIIcon* New( const std::string& name, const std::string& svgXML ); + static UIIconPtr New( const std::string& name, const std::string& svgXML ); virtual ~UISVGIcon(); - virtual Drawable* getSize( const int& size ) const; + virtual const DrawablePtr& getSource( const int& size ) const; protected: UISVGIcon( const std::string& name, const std::string& svgXML ); std::string mSVGXml; - mutable UnorderedMap mSVGs; mutable Sizei mOriSize; mutable int mOriChannels{ 0 }; }; diff --git a/include/eepp/ui/uiicontheme.hpp b/include/eepp/ui/uiicontheme.hpp index 853cd9778..f6594a208 100644 --- a/include/eepp/ui/uiicontheme.hpp +++ b/include/eepp/ui/uiicontheme.hpp @@ -9,15 +9,19 @@ using namespace EE::Graphics; namespace EE { namespace UI { +class UIIconTheme; +using UIIconThemePtr = ResourcePtr; +using UIIconThemeWeakPtr = ResourceWeakPtr; + class EE_API UIIconTheme { public: - static UIIconTheme* New( const std::string& name ); + static UIIconThemePtr New( const std::string& name ); ~UIIconTheme(); - UIIconTheme* add( UIIcon* icon ); + UIIconTheme* add( UIIconPtr icon ); - UIIconTheme* add( const std::unordered_map& icons ); + UIIconTheme* add( const std::unordered_map& icons ); const std::string& getName() const; @@ -25,7 +29,7 @@ class EE_API UIIconTheme { protected: std::string mName; - std::unordered_map mIcons; + std::unordered_map mIcons; UIIconTheme( const std::string& name ); }; diff --git a/include/eepp/ui/uiiconthememanager.hpp b/include/eepp/ui/uiiconthememanager.hpp index d358d0d68..b9028a8ec 100644 --- a/include/eepp/ui/uiiconthememanager.hpp +++ b/include/eepp/ui/uiiconthememanager.hpp @@ -17,15 +17,15 @@ class EE_API UIIconThemeManager { ~UIIconThemeManager(); - UIIconThemeManager* add( UIIconTheme* iconTheme ); + UIIconThemeManager* add( UIIconThemePtr iconTheme ); UIIconTheme* getCurrentTheme() const; - UIIconThemeManager* setCurrentTheme( UIIconTheme* currentTheme ); + UIIconThemeManager* setCurrentTheme( UIIconThemePtr currentTheme ); UIIconTheme* getFallbackTheme() const; - UIIconThemeManager* setFallbackTheme( UIIconTheme* fallbackTheme ); + UIIconThemeManager* setFallbackTheme( UIIconThemePtr fallbackTheme ); UIIcon* findIcon( const std::string& name ); @@ -36,7 +36,7 @@ class EE_API UIIconThemeManager { void remove( UIIconTheme* iconTheme ); protected: - std::vector mIconThemes; + std::vector mIconThemes; UIIconTheme* mCurrentTheme{ nullptr }; UIIconTheme* mFallbackTheme{ nullptr }; UIThemeManager* mFallbackThemeManager{ nullptr }; diff --git a/include/eepp/ui/uiimage.hpp b/include/eepp/ui/uiimage.hpp index 37569f65a..3467cb2d6 100644 --- a/include/eepp/ui/uiimage.hpp +++ b/include/eepp/ui/uiimage.hpp @@ -24,9 +24,11 @@ class EE_API UIImage : public UIWidget { virtual void setAlpha( const Float& alpha ); - Drawable* getDrawable() const; + const DrawablePtr& getDrawable() const; - UIImage* setDrawable( Drawable* drawable, bool ownIt = false ); + UIImage* setDrawable( DrawablePtr drawable ); + + UIImage* setDrawable( TexturePtr texture ); const Color& getColor() const; @@ -53,12 +55,12 @@ class EE_API UIImage : public UIWidget { protected: UIScaleType mScaleType; - Drawable* mDrawable; + DrawablePtr mDrawable; Color mColor; Vector2f mAlignOffset; Vector2f mDestSize; - Uint32 mResourceChangeCb; - bool mDrawableOwner; + DrawableResourceConnection mResourceChangeConnection; + Uint32 mSpriteChangeCb{ 0 }; bool mDeferLoad{ false }; std::shared_ptr> mAsyncImageAlive; Uint64 mRemoteImageLoadId{ 0 }; @@ -79,9 +81,9 @@ class EE_API UIImage : public UIWidget { void autoAlign(); - void safeDeleteDrawable(); + void clearDrawable(); - void onDrawableResourceEvent( DrawableResource::Event event, DrawableResource* ); + void onDrawableResourceChange(); bool loadFileDrawable( const Network::URI& uri ); diff --git a/include/eepp/ui/uimenu.hpp b/include/eepp/ui/uimenu.hpp index c6b04ee45..923fe7c49 100644 --- a/include/eepp/ui/uimenu.hpp +++ b/include/eepp/ui/uimenu.hpp @@ -24,7 +24,7 @@ class EE_API UIMenu : public UIWidget { virtual bool isType( const Uint32& type ) const; - UIMenuItem* add( const String& text, Drawable* icon = NULL, const String& shortcutText = "" ); + UIMenuItem* add( const String& text, DrawablePtr icon = {}, const String& shortcutText = "" ); UIWidget* add( UIWidget* widget ); @@ -35,7 +35,8 @@ class EE_API UIMenu : public UIWidget { UIMenuRadioButton* addRadioButton( const String& text, const bool& active = false ); - UIMenuSubMenu* addSubMenu( const String& text, Drawable* icon = NULL, UIMenu* subMenu = NULL ); + UIMenuSubMenu* addSubMenu( const String& text, DrawablePtr icon = {}, + UIMenu* subMenu = NULL ); UIWidget* getItem( const Uint32& index ); @@ -55,7 +56,7 @@ class EE_API UIMenu : public UIWidget { void removeAll(); - void insert( const String& text, Drawable* icon, const Uint32& index ); + void insert( const String& text, DrawablePtr icon, const Uint32& index ); void insert( UIWidget* widget, const Uint32& index ); @@ -121,7 +122,7 @@ class EE_API UIMenu : public UIWidget { void resizeMe(); - UIMenuItem* createMenuItem( const String& text, Drawable* icon, + UIMenuItem* createMenuItem( const String& text, DrawablePtr icon, const String& shortcutText = "" ); UIMenuCheckBox* createMenuCheckBox( const String& text, const bool& active, @@ -129,7 +130,7 @@ class EE_API UIMenu : public UIWidget { UIMenuRadioButton* createMenuRadioButton( const String& text, const bool& active ); - UIMenuSubMenu* createSubMenu( const String& text, Drawable* icon, UIMenu* subMenu ); + UIMenuSubMenu* createSubMenu( const String& text, DrawablePtr icon, UIMenu* subMenu ); void onThemeLoaded(); diff --git a/include/eepp/ui/uinode.hpp b/include/eepp/ui/uinode.hpp index f78228dd3..855c1a061 100644 --- a/include/eepp/ui/uinode.hpp +++ b/include/eepp/ui/uinode.hpp @@ -336,12 +336,11 @@ class EE_API UINode : public Node { * * Enables background fill and sets the specified drawable at the given index. * - * @param drawable Pointer to the Drawable to use. - * @param ownIt If true, the node takes ownership of the drawable. + * @param drawable Drawable instance to use. * @param index The layer index (0-based). * @return Pointer to this node for method chaining. */ - UINode* setBackgroundDrawable( Drawable* drawable, bool ownIt = false, int index = 0 ); + UINode* setBackgroundDrawable( DrawablePtr drawable, int index = 0 ); /** * @brief Sets a background drawable from a skin name. @@ -521,12 +520,11 @@ class EE_API UINode : public Node { * * Enables foreground fill and sets the specified drawable at the given index. * - * @param drawable Pointer to the Drawable to use. - * @param ownIt If true, the node takes ownership of the drawable. + * @param drawable Drawable instance to use. * @param index The layer index (0-based). * @return Pointer to this node for method chaining. */ - UINode* setForegroundDrawable( Drawable* drawable, bool ownIt = false, int index = 0 ); + UINode* setForegroundDrawable( DrawablePtr drawable, int index = 0 ); /** * @brief Sets a foreground drawable from a skin name. @@ -852,12 +850,12 @@ class EE_API UINode : public Node { void setThemeByName( const std::string& Theme ); /** - * @brief Sets the theme for this node. + * @brief Sets the borrowed theme used by this node. * - * Applies the specified UITheme to this node, affecting its visual appearance - * through skins and styles. + * The node does not retain @p Theme. Its owner, normally the containing scene's UIThemeManager, + * must keep the theme alive until this node switches themes or is destroyed. * - * @param Theme Pointer to the UITheme to apply. + * @param Theme Borrowed theme to apply, or null to use no explicit theme. */ virtual void setTheme( UITheme* Theme ); diff --git a/include/eepp/ui/uinodedrawable.hpp b/include/eepp/ui/uinodedrawable.hpp index c9b7f47c8..c589eedb7 100644 --- a/include/eepp/ui/uinodedrawable.hpp +++ b/include/eepp/ui/uinodedrawable.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -38,6 +39,8 @@ class EE_API UINodeDrawable : public Drawable { static LayerDrawable* New( UINodeDrawable* container ); LayerDrawable( UINodeDrawable* container ); + LayerDrawable( const LayerDrawable& ) = delete; + LayerDrawable& operator=( const LayerDrawable& ) = delete; virtual ~LayerDrawable(); @@ -55,11 +58,13 @@ class EE_API UINodeDrawable : public Drawable { virtual void setSize( const Sizef& size ); - Drawable* getDrawable() const; + const DrawablePtr& getDrawable() const; const std::string& getDrawableRef() const; - void setDrawable( Drawable* drawable, const bool& ownIt ); + void setDrawable( DrawablePtr drawable ); + + void setDrawable( TexturePtr texture ); void setDrawable( const std::string& drawableRef ); @@ -128,11 +133,10 @@ class EE_API UINodeDrawable : public Drawable { std::string mPositionY; std::string mSizeEq; bool mNeedsUpdate{ false }; - bool mOwnsDrawable{ false }; bool mColorWasSet{ false }; - Drawable* mDrawable; + DrawablePtr mDrawable; std::string mDrawableRef; - Uint32 mResourceChangeCbId; + DrawableResourceConnection mResourceChangeConnection; RepeatX mRepeatX{ RepeatX::NoRepeat }; RepeatY mRepeatY{ RepeatY::NoRepeat }; std::string mOriginEq{ "padding-box" }; @@ -141,16 +145,13 @@ class EE_API UINodeDrawable : public Drawable { Origin mOrigin{ Origin::PaddingBox }; Clip mClip{ Clip::BorderBox }; Attachment mAttachment{ Attachment::Scroll }; - std::shared_ptr> mAsyncDrawableAlive; - Uint64 mRemoteDrawableLoadId{ 0 }; - virtual void onPositionChange(); virtual void onColorFilterChange(); void update(); - Drawable* createDrawable( const std::string& value, const Sizef& size, bool& ownIt ); + DrawablePtr createDrawable( const std::string& value, const Sizef& size ); bool loadRemoteDrawable( const std::string& value ); }; @@ -158,6 +159,8 @@ class EE_API UINodeDrawable : public Drawable { static UINodeDrawable* New( UINode* owner ); UINodeDrawable( UINode* owner ); + UINodeDrawable( const UINodeDrawable& ) = delete; + UINodeDrawable& operator=( const UINodeDrawable& ) = delete; virtual ~UINodeDrawable(); @@ -187,7 +190,7 @@ class EE_API UINodeDrawable : public Drawable { LayerDrawable* getLayer( int index ); - void setDrawable( int index, Drawable* drawable, bool ownIt ); + void setDrawable( int index, DrawablePtr drawable ); void setDrawable( int index, const std::string& drawable ); @@ -234,7 +237,8 @@ class EE_API UINodeDrawable : public Drawable { protected: UINode* mOwner; UIBackgroundDrawable mBackgroundColor; - std::map mGroup; + using LayerDrawablePtr = std::unique_ptr>; + std::map mGroup; Sizef mSize; bool mNeedsUpdate{ true }; bool mClipEnabled{ false }; diff --git a/include/eepp/ui/uipushbutton.hpp b/include/eepp/ui/uipushbutton.hpp index 87e81276d..5367bcb2a 100644 --- a/include/eepp/ui/uipushbutton.hpp +++ b/include/eepp/ui/uipushbutton.hpp @@ -40,7 +40,7 @@ class EE_API UIPushButton : public UIWidget { virtual void setTheme( UITheme* Theme ); - virtual UIPushButton* setIcon( Drawable* icon, bool ownIt = false ); + virtual UIPushButton* setIcon( DrawablePtr icon ); virtual UIImage* getIcon(); diff --git a/include/eepp/ui/uiscenenode.hpp b/include/eepp/ui/uiscenenode.hpp index 121674a63..3252ef752 100644 --- a/include/eepp/ui/uiscenenode.hpp +++ b/include/eepp/ui/uiscenenode.hpp @@ -1,6 +1,7 @@ #ifndef EE_UISCENENODE_HPP #define EE_UISCENENODE_HPP +#include #include #include #include @@ -9,8 +10,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -20,6 +23,7 @@ using namespace EE::Network; namespace EE { namespace Graphics { class Font; +using FontPtr = ResourcePtr; }} // namespace EE::Graphics namespace EE { namespace Window { @@ -54,9 +58,14 @@ class EE_API UISceneNode : public SceneNode { * * @param window Pointer to the window to associate with this UI scene node. * If NULL, uses the current window from Engine. + * @param importDefaultResources Whether the scene scope automatically imports the catalog from + * Graphics::defaultResourceScope(). Keep this enabled for normal + * application scenes. Disable it for intentionally isolated + * scenes that must only resolve local or explicitly imported resources. * @return Pointer to the newly created UISceneNode instance. */ - static UISceneNode* New( EE::Window::Window* window = NULL ); + static UISceneNode* New( EE::Window::Window* window = NULL, + bool importDefaultResources = true ); /** * @brief Destroys the UISceneNode and cleans up resources. @@ -153,8 +162,8 @@ class EE_API UISceneNode : public SceneNode { * * Copies only shared platform/configuration services: dispatcher, DPI/window pointer, * thread pool, color/contrast preferences, and default font/theme pointers. Stylesheets, - * URI, referer, cookies, navigation callbacks, actions, roots, and dirty queues remain owned - * by this scene. + * URI, referer, cookies, navigation callbacks, actions, roots, resource scope, and dirty queues + * remain owned by this scene. */ void initializeEmbeddedFromHost( UISceneNode* hostScene ); @@ -563,7 +572,13 @@ class EE_API UISceneNode : public SceneNode { * @param drawableSize The desired size of the drawable in pixels. * @return Pointer to the Drawable, or nullptr if not found. */ - Drawable* findIconDrawable( const std::string& iconName, const size_t& drawableSize ); + DrawablePtr findIconDrawable( const std::string& iconName, const size_t& drawableSize ); + + /** @return This scene's drawable resolver. */ + DrawableResolver& getDrawableResolver(); + + /** @return This scene's drawable resolver. */ + const DrawableResolver& getDrawableResolver() const; /** * @brief Gets the keybindings manager. @@ -770,12 +785,27 @@ class EE_API UISceneNode : public SceneNode { */ void setThreadPool( const std::shared_ptr& threadPool ); + /** @return The Graphics resource lookup and ownership boundary of this scene. */ + const Graphics::ResourceScopePtr& getResourceScope() const; + /** - * @brief Sets the theme for the entire UI scene. + * @brief Replaces this scene's resource boundary, allowing intentional sharing between scenes. * - * Applies the theme to the root widget and all children. + * Scenes created with default-resource importing enabled also import the default catalog into + * the replacement scope. Scenes created with it disabled leave the replacement scope unchanged. + */ + UISceneNode* setResourceScope( Graphics::ResourceScopePtr resourceScope ); + + /** + * @brief Applies a borrowed theme to the widgets in this UI scene. * - * @param theme Pointer to the UITheme to set. + * Each affected widget stores a non-owning `UITheme*`; this function does not retain @p theme. + * The theme must therefore outlive every widget using it. Normally callers establish that + * lifetime first by adding the corresponding `UIThemePtr` to this scene's UIThemeManager or by + * setting it as the manager's default theme. + * + * @param theme Borrowed theme to apply recursively. May be null to clear explicit widget + * themes. */ void setTheme( UITheme* theme ); @@ -836,6 +866,21 @@ class EE_API UISceneNode : public SceneNode { Network::CookieManager& getCookieManager() { return mCookieManager; } + const WebResourceCachePtr& getWebResourceCache() const { return mWebResourceCache; } + + UISceneNode* setWebResourceCache( WebResourceCachePtr cache, CachePartitionId partition = 0 ); + + DocumentSessionId getDocumentSessionId() const { return mDocumentSessionId; } + + Uint64 beginDocumentNavigation( const URI& uri ); + + Uint64 getDocumentGeneration() const; + + void requestWebResource( WebResourceRequest request, WebResourceCache::Callback callback ); + + Graphics::TexturePtr requestWebTexture( WebResourceRequest request, + WebResourceCache::Callback callback = {} ); + void invalidateAsyncResourceLoads(); virtual void invalidate( Node* invalidator ); @@ -898,15 +943,21 @@ class EE_API UISceneNode : public SceneNode { bool mStyleDuringLoad{ false }; UIThemeManager* mUIThemeManager{ nullptr }; UIIconThemeManager* mUIIconThemeManager{ nullptr }; - std::vector mFontFaces; + std::vector mFontFaces; UnorderedMap mFontFaceAliases; UnorderedMap mFontFaceFamilies; std::shared_ptr mAsyncResourceLoadState; + bool mImportDefaultResources{ true }; + Graphics::ResourceScopePtr mResourceScope; + DrawableResolver mDrawableResolver; + WebResourceCachePtr mWebResourceCache; + DocumentSessionId mDocumentSessionId{ 0 }; KeyBindings mKeyBindings; std::map mKeyBindingCommands; UnorderedSet mDirtyStyle; UnorderedSet mDirtyStyleState; UnorderedMap mDirtyStyleStateCSSAnimations; + SmallVector, 64> mDirtyStyleStateSnapshot; UnorderedSet mDirtyLayouts; SmallVector mDirtyLayoutsSnapshot; std::vector> mTimes; @@ -936,8 +987,9 @@ class EE_API UISceneNode : public SceneNode { * Creates a UISceneNode with optional window association. * * @param window Pointer to the window, or NULL for default. + * @param importDefaultResources Whether the scene scope imports the default resource catalog. */ - explicit UISceneNode( EE::Window::Window* window = NULL ); + explicit UISceneNode( EE::Window::Window* window = NULL, bool importDefaultResources = true ); /** * @brief Handles node resize. @@ -1151,11 +1203,13 @@ class EE_API UISceneNode : public SceneNode { void resetTooltips( Node* node ); /** - * @brief Applies a theme to a node and its subtree. + * @brief Applies a borrowed theme to the widgets below a node. * - * Recursively applies the specified UITheme to all widgets in the subtree. + * Each affected widget stores a non-owning pointer. This function does not retain @p theme; an + * owner such as this scene's UIThemeManager must keep it alive for the complete period in which + * the subtree uses it. * - * @param theme Pointer to the UITheme to apply. + * @param theme Borrowed theme to apply. May be null to clear explicit widget themes. * @param to The root node of the subtree to theme. */ void setTheme( UITheme* theme, Node* to ); diff --git a/include/eepp/ui/uiskin.hpp b/include/eepp/ui/uiskin.hpp index 79846d2e9..a58c7a7c0 100644 --- a/include/eepp/ui/uiskin.hpp +++ b/include/eepp/ui/uiskin.hpp @@ -8,7 +8,7 @@ namespace EE { namespace UI { class EE_API UISkin : public StateListDrawable { public: - static UISkin* New( const std::string& name = "" ); + static ResourcePtr New( const std::string& name = "" ); virtual ~UISkin(); @@ -20,9 +20,11 @@ class EE_API UISkin : public StateListDrawable { virtual Sizef getPixelsSize(); - virtual UISkin* clone(); + DrawablePtr clone() const; - virtual UISkin* clone( const std::string& NewName ); + ResourcePtr cloneSkin() const; + + ResourcePtr clone( const std::string& newName ) const; virtual Rectf getBorderSize( const Uint32& state ); diff --git a/include/eepp/ui/uiskinstate.hpp b/include/eepp/ui/uiskinstate.hpp index 057dc1114..84b65ae88 100644 --- a/include/eepp/ui/uiskinstate.hpp +++ b/include/eepp/ui/uiskinstate.hpp @@ -2,6 +2,7 @@ #define EE_UI_UISKINSTATE_HPP #include +#include #include namespace EE { namespace UI { @@ -10,7 +11,7 @@ class UISkin; class EE_API UISkinState : public UIState { public: - static UISkinState* New( UISkin* skin ); + static UISkinState* New( ResourcePtr skin ); virtual ~UISkinState(); @@ -28,11 +29,11 @@ class EE_API UISkinState : public UIState { bool hasStateColor( const Uint32& state ) const; protected: - UISkin* mSkin; + ResourcePtr mSkin; std::map mColors; Color mCurrentColor; - explicit UISkinState( UISkin* Skin ); + explicit UISkinState( ResourcePtr skin ); void updateState(); diff --git a/include/eepp/ui/uisprite.hpp b/include/eepp/ui/uisprite.hpp index f76da0520..24736150e 100644 --- a/include/eepp/ui/uisprite.hpp +++ b/include/eepp/ui/uisprite.hpp @@ -1,13 +1,9 @@ #ifndef EE_UICUISPRITE_HPP #define EE_UICUISPRITE_HPP +#include #include -namespace EE { namespace Graphics { -class Sprite; -class TextureRegion; -}} // namespace EE::Graphics - namespace EE { namespace UI { class EE_API UISprite : public UIWidget { @@ -26,11 +22,9 @@ class EE_API UISprite : public UIWidget { virtual void setAlpha( const Float& alpha ); - Graphics::Sprite* getSprite() const; + const Graphics::SpritePtr& getSprite() const; - Drawable* getDrawable() const; - - UISprite* setSprite( Graphics::Sprite* sprite ); + UISprite* setSprite( Graphics::SpritePtr sprite ); Color getColor() const; @@ -42,10 +36,6 @@ class EE_API UISprite : public UIWidget { const Vector2f& getAlignOffset() const; - UISprite* setIsSpriteOwner( const bool& dealloc ); - - bool getDeallocSprite(); - virtual bool applyProperty( const StyleSheetProperty& attribute ); virtual std::string getPropertyString( const PropertyDefinition* propertyDef, @@ -54,11 +44,10 @@ class EE_API UISprite : public UIWidget { virtual std::vector getPropertiesImplemented() const; protected: - Graphics::Sprite* mSprite; + Graphics::SpritePtr mSprite; RenderMode mRender; Vector2f mAlignOffset; TextureRegion* mTextureRegionLast; - bool mDealloc; UISprite(); @@ -70,7 +59,6 @@ class EE_API UISprite : public UIWidget { virtual void onSizeChange(); - Uint32 deallocSprite(); }; }} // namespace EE::UI diff --git a/include/eepp/ui/uitabwidget.hpp b/include/eepp/ui/uitabwidget.hpp index 48ccd3012..aa9632bc4 100644 --- a/include/eepp/ui/uitabwidget.hpp +++ b/include/eepp/ui/uitabwidget.hpp @@ -68,7 +68,7 @@ class EE_API UITabWidget : public UIWidget { virtual bool isType( const Uint32& type ) const; - UITab* add( const String& text, UINode* nodeOwned, Drawable* icon = NULL ); + UITab* add( const String& text, UINode* nodeOwned, DrawablePtr icon = {} ); UITabWidget* add( UITab* tab ); @@ -92,7 +92,7 @@ class EE_API UITabWidget : public UIWidget { void removeAllTabs( bool destroyOwnedNode = true, bool immediateClose = false ); - void insertTab( const String& text, UINode* nodeOwned, Drawable* icon, const Uint32& index ); + void insertTab( const String& text, UINode* nodeOwned, DrawablePtr icon, const Uint32& index ); void insertTab( UITab* tab, const Uint32& index ); @@ -265,7 +265,7 @@ class EE_API UITabWidget : public UIWidget { void onThemeLoaded(); - UITab* createTab( const String& text, UINode* nodeOwned, Drawable* icon ); + UITab* createTab( const String& text, UINode* nodeOwned, DrawablePtr icon ); void removeTab( const Uint32& index, bool destroyOwnedNode, bool destroyTab, bool immediateClose, diff --git a/include/eepp/ui/uitextinput.hpp b/include/eepp/ui/uitextinput.hpp index 4fa7c5e69..c2d4ada97 100644 --- a/include/eepp/ui/uitextinput.hpp +++ b/include/eepp/ui/uitextinput.hpp @@ -268,7 +268,7 @@ class EE_API UITextInput : public UITextView, public TextDocument::Client { UIMenuItem* menuAdd( UIPopUpMenu* menu, const String& translateString, const std::string& icon, const std::string& cmd ); - Drawable* findIcon( const std::string& name ); + DrawablePtr findIcon( const std::string& name ); }; }} // namespace EE::UI diff --git a/include/eepp/ui/uitextureregion.hpp b/include/eepp/ui/uitextureregion.hpp index 067fef7be..edc31c1da 100644 --- a/include/eepp/ui/uitextureregion.hpp +++ b/include/eepp/ui/uitextureregion.hpp @@ -51,7 +51,7 @@ class EE_API UITextureRegion : public UIWidget { virtual std::vector getPropertiesImplemented() const; protected: - UIScaleType mScaleType; + UIScaleType mScaleType{ UIScaleType::None }; Graphics::TextureRegion* mTextureRegion; Color mColor; RenderMode mRender; @@ -65,7 +65,9 @@ class EE_API UITextureRegion : public UIWidget { void autoAlign(); - void drawTextureRegion(); + void drawTextureRegion( const Sizef& destSize, const Vector2i& offset ); + + void autoAlign( const Sizef& drawableSize ); }; }} // namespace EE::UI diff --git a/include/eepp/ui/uitheme.hpp b/include/eepp/ui/uitheme.hpp index ecd7f9574..87f35325d 100644 --- a/include/eepp/ui/uitheme.hpp +++ b/include/eepp/ui/uitheme.hpp @@ -1,11 +1,12 @@ #ifndef EE_UICUITHEME_HPP #define EE_UICUITHEME_HPP -#include +#include #include #include #include #include +#include #include namespace EE { namespace Graphics { @@ -20,36 +21,37 @@ namespace EE { namespace UI { class UIIcon; class UIIconTheme; -class EE_API UITheme : protected ResourceManagerMulti { +class UITheme; +using UIThemePtr = ResourcePtr; +using UIThemeWeakPtr = ResourceWeakPtr; +using UISkinPtr = ResourcePtr; + +class EE_API UITheme { public: - using ResourceManagerMulti::getById; - using ResourceManagerMulti::getByName; - using ResourceManagerMulti::exists; - using ResourceManagerMulti::existsId; + static UIThemePtr New( const std::string& name, const std::string& abbr, + Graphics::Font* defaultFont = NULL ); - static UITheme* New( const std::string& name, const std::string& abbr, - Graphics::Font* defaultFont = NULL ); + static UIThemePtr load( const std::string& name, const std::string& abbr, + const std::string& textureAtlasPath, Graphics::Font* defaultFont, + const std::string& styleSheetPath ); - static UITheme* load( const std::string& name, const std::string& abbr, - const std::string& textureAtlasPath, Graphics::Font* defaultFont, - const std::string& styleSheetPath ); + static UIThemePtr loadFromString( const std::string& name, const std::string& abbr, + const std::string& textureAtlasPath, + Graphics::Font* defaultFont, + const std::string& styleSheetString ); - static UITheme* loadFromString( const std::string& name, const std::string& abbr, - const std::string& textureAtlasPath, - Graphics::Font* defaultFont, - const std::string& styleSheetString ); + static UIThemePtr loadFromTextureAtlas( UIThemePtr theme, + Graphics::TextureAtlasPtr textureAtlas ); - static UITheme* loadFromTextureAtlas( UITheme* tTheme, - Graphics::TextureAtlas* getTextureAtlas ); + static UIThemePtr loadFromTextureAtlas( Graphics::TextureAtlasPtr textureAtlas, + const std::string& Name, const std::string& NameAbbr ); - static UITheme* loadFromTextureAtlas( Graphics::TextureAtlas* getTextureAtlas, - const std::string& Name, const std::string& NameAbbr ); + static UIThemePtr loadFromDirectory( UIThemePtr theme, const std::string& Path, + const Float& pixelDensity = 1 ); - static UITheme* loadFromDirectory( UITheme* tTheme, const std::string& Path, - const Float& pixelDensity = 1 ); - - static UITheme* loadFromDirectory( const std::string& Path, const std::string& Name, - const std::string& NameAbbr, const Float& pixelDensity = 1 ); + static UIThemePtr loadFromDirectory( const std::string& Path, const std::string& Name, + const std::string& NameAbbr, + const Float& pixelDensity = 1 ); virtual ~UITheme(); @@ -61,7 +63,15 @@ class EE_API UITheme : protected ResourceManagerMulti { const std::string& getAbbr() const; - virtual UISkin* add( UISkin* Resource ); + UISkin* add( UISkinPtr skin ); + + UISkin* getById( const String::HashType& id ) const; + + UISkin* getByName( const std::string& name ) const; + + bool exists( const std::string& name ) const; + + bool existsId( const String::HashType& id ) const; Graphics::TextureAtlas* getTextureAtlas() const; @@ -87,6 +97,8 @@ class EE_API UITheme : protected ResourceManagerMulti { UIIconTheme* getIconTheme() const; + const Graphics::ResourceCatalogPtr& getResourceCatalog() const; + const std::string& getStyleSheetPath() const; void setStyleSheetPath( const std::string& styleSheetPath ); @@ -97,14 +109,16 @@ class EE_API UITheme : protected ResourceManagerMulti { std::string mName; String::HashType mNameHash; std::string mAbbr; - Graphics::TextureAtlas* mTextureAtlas; + Graphics::TextureAtlasPtr mTextureAtlas; Font* mDefaultFont; Float mDefaultFontSize; CSS::StyleSheet mStyleSheet; std::string mStyleSheetPath; - UIIconTheme* mIconTheme; + UIIconThemePtr mIconTheme; + UnorderedMap> mSkins; + Graphics::ResourceCatalogPtr mResourceCatalog; - void setTextureAtlas( Graphics::TextureAtlas* SG ); + void setTextureAtlas( Graphics::TextureAtlasPtr textureAtlas ); UITheme( const std::string& name, const std::string& abbr, Graphics::Font* defaultFont = NULL ); }; diff --git a/include/eepp/ui/uithememanager.hpp b/include/eepp/ui/uithememanager.hpp index 1bf4ef441..95c846fe1 100644 --- a/include/eepp/ui/uithememanager.hpp +++ b/include/eepp/ui/uithememanager.hpp @@ -1,6 +1,7 @@ #ifndef EE_UICTHEMEMANAGER #define EE_UICTHEMEMANAGER +#include #include #include @@ -8,12 +9,22 @@ namespace EE { namespace UI { class UINode; -class EE_API UIThemeManager : public ResourceManager { +class EE_API UIThemeManager { public: static UIThemeManager* New(); virtual ~UIThemeManager(); + UITheme* add( UIThemePtr theme ); + + bool remove( UITheme* theme ); + + bool removeById( const String::HashType& id ); + + bool removeByName( const std::string& name ); + + UIThemeManager* setResourceScope( Graphics::ResourceScopePtr resourceScope ); + UIThemeManager* setDefaultFont( Font* Font ); Font* getDefaultFont() const; @@ -24,10 +35,19 @@ class EE_API UIThemeManager : public ResourceManager { UIThemeManager* setDefaultTheme( UITheme* Theme ); + UIThemeManager* setDefaultTheme( UIThemePtr theme ); + UIThemeManager* setDefaultTheme( const std::string& Theme ); UITheme* getDefaultTheme() const; + /** @return An owning handle to the default theme, or an empty handle when unset. */ + UIThemePtr getDefaultThemeHandle() const; + + UITheme* getById( const String::HashType& id ) const; + + UITheme* getByName( const std::string& name ) const; + UIThemeManager* applyDefaultTheme( UINode* node ); UIThemeManager* setAutoApplyDefaultTheme( const bool& apply ); @@ -61,7 +81,8 @@ class EE_API UIThemeManager : public ResourceManager { protected: Font* mFont; Float mFontSize; - UITheme* mThemeDefault; + UIThemePtr mThemeDefault; + UnorderedMap mThemes; bool mAutoApplyDefaultTheme; bool mEnableDefaultEffects; @@ -72,6 +93,7 @@ class EE_API UIThemeManager : public ResourceManager { bool mTooltipFollowMouse; Sizei mCursorSize; + Graphics::ResourceScopePtr mResourceScope; UIThemeManager(); }; diff --git a/include/eepp/ui/uiwebview.hpp b/include/eepp/ui/uiwebview.hpp index b650c4aa8..4308a26b9 100644 --- a/include/eepp/ui/uiwebview.hpp +++ b/include/eepp/ui/uiwebview.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -72,6 +73,10 @@ class EE_API UIWebView : public UIScrollView { UISceneNode* getDocumentSceneNode() const; + const WebResourceCachePtr& getWebResourceCache() const; + + UIWebView* setWebResourceCache( WebResourceCachePtr cache, CachePartitionId partition = 0 ); + void setStyleSheetDefaultMarker( Uint32 marker ); void setUserAgent( const std::string& userAgent ); @@ -117,6 +122,7 @@ class EE_API UIWebView : public UIScrollView { Uint64 mNavigationGeneration{ 0 }; std::string mUserAgent; Time mDefaultTimeout{ Seconds( 30 ) }; + Time mWebResourceCachePruneElapsed; Uint32 mStyleSheetDefaultMarker{ 0 }; void loadURI( URI uri, bool isHistoryNav ); diff --git a/include/eepp/ui/uiwidget.hpp b/include/eepp/ui/uiwidget.hpp index fb9db77ff..002d99949 100644 --- a/include/eepp/ui/uiwidget.hpp +++ b/include/eepp/ui/uiwidget.hpp @@ -130,12 +130,14 @@ class EE_API UIWidget : public UINode { virtual UIWidget* setAnchors( const Uint32& flags ); /** - * @brief Sets the theme for this widget. + * @brief Sets the borrowed theme used by this widget. * - * Applies the specified theme to the widget, affecting its visual appearance. - * The theme controls colors, fonts, borders, and other visual properties. + * The widget stores @p Theme as a non-owning pointer and does not increment its reference + * count. The theme must outlive this use; normally it is retained by the containing scene's + * UIThemeManager. This keeps per-widget theme access inexpensive while centralizing ownership + * at the scene boundary. * - * @param Theme Pointer to the UITheme to apply. + * @param Theme Borrowed theme to apply, or null to use no explicit theme. */ virtual void setTheme( UITheme* Theme ); diff --git a/include/eepp/ui/uiwindow.hpp b/include/eepp/ui/uiwindow.hpp index cf829ae07..bbb3a3473 100644 --- a/include/eepp/ui/uiwindow.hpp +++ b/include/eepp/ui/uiwindow.hpp @@ -220,7 +220,7 @@ class EE_API UIWindow : public UIWidget { RESIZE_TOPRIGHT }; - FrameBuffer* mFrameBuffer; + Graphics::FrameBufferUniquePtr mFrameBuffer; StyleConfig mStyleConfig; UIWidget* mWindowDecoration; UIWidget* mBorderLeft; diff --git a/include/eepp/ui/webresourcecache.hpp b/include/eepp/ui/webresourcecache.hpp new file mode 100644 index 000000000..a88663025 --- /dev/null +++ b/include/eepp/ui/webresourcecache.hpp @@ -0,0 +1,306 @@ +#ifndef EE_UI_WEBRESOURCECACHE_HPP +#define EE_UI_WEBRESOURCECACHE_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace EE { namespace UI { + +/** Shared ownership handle for a WebResourceCache. + * + * A cache can be installed into more than one UIWebView. Sharing the cache does not by itself make + * all resources visible between those views: sessions must also use the same CachePartitionId. */ +using WebResourceCachePtr = std::shared_ptr; + +/** Identifies one document consumer of a WebResourceCache. + * + * UISceneNode creates a session for its current document. A session tracks the current navigation + * generation and the cache entries leased by that document. IDs are process-wide and opaque. */ +using DocumentSessionId = Uint64; + +/** Identifies a cache sharing and privacy boundary. + * + * Sessions in the same partition may reuse and coalesce resources. Sessions in different + * partitions never share entries, even when requesting the same URI. A partition should therefore + * represent one intentionally shared HTTP state, normally one cookie jar/authentication context. + * + * For example, browser tabs that share cookies should use the same WebResourceCache and + * CachePartitionId. A private tab or a view logged into a different account must use another + * partition. Passing zero to createSession() creates a new private partition automatically. + * Partition IDs are process-wide and opaque. + * + * @code + * auto cache = WebResourceCache::New(); + * auto normalProfile = cache->createPartition(); + * firstTab->setWebResourceCache( cache, normalProfile ); + * secondTab->setWebResourceCache( cache, normalProfile ); // May reuse firstTab resources. + * privateTab->setWebResourceCache( cache, cache->createPartition() ); // Fully isolated entries. + * @endcode */ +using CachePartitionId = Uint64; + +/** Canonical network origin used to describe a document navigation. + * + * Origins compare scheme, host, and port. Consequently HTTP and HTTPS origins are distinct, as are + * equal hosts using different ports. Paths, queries, and fragments are not part of an origin. */ +struct EE_API WebOriginKey { + /** Lowercase URI scheme, such as "http" or "https". */ + std::string scheme; + /** Lowercase host name. */ + std::string host; + /** URI port, or the URI implementation's default/empty port value. */ + Uint16 port{ 0 }; + + /** @return True when no origin components have been assigned. */ + bool empty() const { return scheme.empty() && host.empty() && port == 0; } + /** @return True when both values describe the same scheme, host, and port. */ + bool operator==( const WebOriginKey& other ) const; + /** @return True when at least one origin component differs. */ + bool operator!=( const WebOriginKey& other ) const { return !( *this == other ); } + + /** Creates an origin key from a URI, normalizing the scheme and host to lowercase. */ + static WebOriginKey fromURI( const Network::URI& uri ); +}; + +/** Semantic type of a cached web resource. + * + * The kind is part of cache identity, so one URI requested as a document and as a stylesheet uses + * distinct entries. Images additionally carry texture decode options in WebResourceRequest. */ +enum class WebResourceKind : Uint8 { + /** Top-level HTML or another document response. */ + Document, + /** CSS stylesheet response. */ + StyleSheet, + /** Downloadable font response. */ + Font, + /** Image response decoded into a Graphics::Texture. */ + Image +}; + +/** Current lifecycle state of a cache entry. */ +enum class WebResourceLoadState : Uint8 { + /** Entry exists but has not started loading. */ + Empty, + /** One fetch is active; additional subscribers join it. */ + Loading, + /** Resource completed successfully and is available for reuse. */ + Ready, + /** Last fetch failed and remains subject to the retry delay. */ + Failed, + /** Entry was cleared and any late fetch completion will be ignored. */ + Cancelled +}; + +/** Complete input required to identify and fetch one web resource variant. + * + * Cache identity includes the partition, canonical URI, resource kind, method, non-navigation + * headers, body, and image decode options. Cookie and Referer are deliberately excluded from entry + * identity: the partition isolates cookie contexts, while these two values naturally change during + * navigation. They are still sent with the HTTP request. + * + * Transport policy such as timeout, proxy, and completionDispatcher controls a fetch but does not + * describe the resulting resource and is therefore not part of cache identity. */ +struct EE_API WebResourceRequest { + /** Dispatches a completion function to the thread on which it is safe to finalize a resource. + */ + using CompletionDispatcher = std::function )>; + + /** Absolute URI to fetch. Fragments are removed and the URI is normalized for cache lookup. */ + Network::URI uri; + /** Semantic resource type. */ + WebResourceKind kind{ WebResourceKind::Document }; + /** HTTP method. The method is part of cache identity. */ + Network::Http::Request::Method method{ Network::Http::Request::Method::Get }; + /** HTTP request headers. See the type documentation for cache-key rules. */ + Network::Http::Request::FieldTable headers; + /** HTTP request body. The complete body is part of cache identity. */ + std::string body; + /** Maximum duration allowed for the HTTP request. */ + System::Time timeout{ System::Seconds( 5 ) }; + /** Whether HTTPS certificates must be validated. */ + bool validateCertificate{ true }; + /** Optional HTTP proxy URI. An empty URI uses a direct connection. */ + Network::URI proxy; + /** Whether HTTP redirects should be followed. */ + bool followRedirect{ true }; + /** Image-only texture wrapping policy. True selects ClampToEdge. */ + bool clampToEdge{ true }; + /** Image-only option controlling mipmap generation. */ + bool mipmaps{ false }; + /** Image-only option requesting texture compression. */ + bool compressTexture{ false }; + /** Image-only SVG rasterization scale. */ + Float svgScale{ 1.f }; + /** Image-only completion dispatcher. + * + * Image decode/upload completion must execute where a graphics context is current. This policy + * is not part of cache identity and only the request that starts a coalesced load supplies it. + */ + CompletionDispatcher completionDispatcher; +}; + +/** Result delivered to a WebResourceCache subscriber. + * + * Data resources set data, while image resources set texture. A texture request may already have + * returned the same TexturePtr as a transparent placeholder before this result is delivered. */ +struct EE_API WebResourceResult { + /** True when the request completed and produced a usable resource. */ + bool success{ false }; + /** Final cache-entry state observed by this completion. */ + WebResourceLoadState state{ WebResourceLoadState::Empty }; + /** Numeric HTTP response status. */ + int status{ 0 }; + /** Failure description. Empty for successful results. */ + std::string error; + /** Set-Cookie value from the final response, or from its redirect chain when applicable. */ + std::string setCookie; + /** Shared immutable response body for documents, stylesheets, and fonts. */ + std::shared_ptr data; + /** Shared texture for image resources. */ + Graphics::TexturePtr texture; +}; + +/** Shared HTTP-backed document resource cache. + * + * The cache separates three concepts: + * - A partition defines which HTTP/cookie context may share entries. + * - A session represents one active document using entries from that partition. + * - A navigation generation prevents an obsolete document from receiving asynchronous results. + * + * Entries are strongly retained by the cache after their final document lease is released. Their + * TTL starts at that release point, allowing Back/Forward navigation to reuse them. Expired entries + * are removed by prune(); UIWebView invokes it periodically. A byte budget additionally evicts the + * least-recently-used unleased entries. Active leases and in-flight loads are not evicted. + * + * Requests for the same key are coalesced into one HTTP operation. Each current subscriber receives + * the result, while callbacks belonging to obsolete navigation generations are discarded. Public + * operations are internally synchronized. Subscriber callbacks run without the cache mutex held. */ +class EE_API WebResourceCache : public std::enable_shared_from_this { + public: + /** Receives the completed result of a data or texture request. */ + using Callback = std::function; + + /** @return A newly allocated shared web-resource cache. */ + static WebResourceCachePtr New(); + + /** Creates an empty cache with a 30-second TTL, 1-second retry delay, and 64 MiB budget. */ + WebResourceCache(); + /** Cancels cache delivery and releases all retained entries and sessions. */ + ~WebResourceCache(); + + /** Allocates a new process-wide partition identifier. + * @return An opaque, non-zero partition ID. */ + CachePartitionId createPartition(); + + /** Creates a document session. + * @param partition Partition to join. Zero creates a new private partition. + * @return An opaque, non-zero document session ID. */ + DocumentSessionId createSession( CachePartitionId partition = 0 ); + + /** Destroys a session and releases all entries leased by it. + * + * Releasing the final lease starts each entry's TTL; it does not normally erase entries + * immediately. Pending callbacks for the destroyed session will not be delivered. */ + void destroySession( DocumentSessionId session ); + + /** Begins a new document navigation in an existing session. + * + * Previous document leases are released and the generation is incremented. Pending subscribers + * from older generations become stale, but shared in-flight requests continue for other current + * subscribers. + * @param session Session performing the navigation. + * @param uri Destination document URI, used to record its origin. + * @return The new generation, or zero when the session does not exist. */ + Uint64 beginNavigation( DocumentSessionId session, const Network::URI& uri ); + + /** @return The current generation for a session, or zero when it does not exist. */ + Uint64 getSessionGeneration( DocumentSessionId session ) const; + /** @return The partition used by a session, or zero when it does not exist. */ + CachePartitionId getSessionPartition( DocumentSessionId session ) const; + + /** Requests a document, stylesheet, font, or other non-texture response body. + * + * A ready cache hit may invoke callback synchronously. A miss starts or joins an asynchronous + * fetch. The callback is omitted when the session/generation is stale. + * @param session Requesting document session. + * @param generation Generation returned by beginNavigation(). + * @param request Resource and transport description. + * @param callback Optional completion callback. */ + void requestData( DocumentSessionId session, Uint64 generation, WebResourceRequest request, + Callback callback ); + + /** Requests an image as a shared texture. + * + * On a miss, this creates and returns a transparent placeholder immediately. The same + * TexturePtr is populated after download and decode. A ready hit returns the existing texture + * without a new HTTP request. This function must be called where texture creation is valid, and + * the request must provide an appropriate completionDispatcher for asynchronous GPU upload. + * @return The cached/new texture, or null for an invalid or stale session. */ + Graphics::TexturePtr requestTexture( DocumentSessionId session, Uint64 generation, + WebResourceRequest request, Callback callback = {} ); + + /** Sets the retention duration used when an entry's final document lease is released. */ + void setTTL( System::Time ttl ); + /** @return The current post-lease retention duration. */ + System::Time getTTL() const; + /** Sets how long a failed entry suppresses another fetch attempt. */ + void setRetryDelay( System::Time delay ); + /** @return The current failed-request retry delay. */ + System::Time getRetryDelay() const; + /** Sets the maximum retained response/texture byte estimate. + * + * Applying a smaller budget immediately evicts least-recently-used unleased entries where + * possible. Active entries may temporarily make retained bytes exceed the budget. */ + void setByteBudget( std::size_t bytes ); + /** @return The configured retained-byte budget. */ + std::size_t getByteBudget() const; + /** @return Estimated bytes retained by ready data bodies and decoded image textures. */ + std::size_t getRetainedBytes() const; + /** @return Number of cache entries in all states and partitions. */ + std::size_t getEntryCount() const; + /** @return Number of HTTP requests belonging to the cache's current epoch. + * + * `clear()` resets this count even though invalidated underlying operations may still finish; + * those stale completions are ignored and do not alter the count. */ + std::size_t getInFlightCount() const; + + /** Removes expired entries and enforces the byte budget. + * + * Active document leases and loading entries are preserved. UIWebView calls this periodically; + * other cache owners should provide their own maintenance point. */ + void prune(); + + /** Removes every entry immediately and suppresses delivery from outstanding fetches. + * + * Sessions remain valid but no longer lease entries. This does not cancel the underlying HTTP + * operation. A late completion is ignored using its operation identity, including when a new + * request has already recreated an entry with the same cache key. */ + void clear(); + + /** Completion supplied to a custom Fetcher. */ + using FetchCompletion = std::function; + /** Custom fetch implementation, primarily intended for deterministic tests. + * + * The fetcher must eventually invoke FetchCompletion exactly once. Production uses + * Http::requestAsync when no custom fetcher is installed. */ + using Fetcher = std::function; + + /** Installs or clears a custom HTTP fetch implementation. */ + void setFetcher( Fetcher fetcher ); + + private: + /** Private implementation containing synchronized sessions and cache entries. */ + struct Impl; + /** Exclusive implementation state. */ + std::unique_ptr mImpl; +}; + +}} // namespace EE::UI + +#endif diff --git a/include/eepp/window/cursor.hpp b/include/eepp/window/cursor.hpp index dac650a5a..f8b99cb06 100644 --- a/include/eepp/window/cursor.hpp +++ b/include/eepp/window/cursor.hpp @@ -78,6 +78,7 @@ class EE_API Cursor { Vector2i mHotSpot; EE::Window::Window* mWindow; + // The texture pixels are copied synchronously; Cursor does not retain this borrow. Cursor( Texture* tex, const Vector2i& hotspot, const std::string& getName, EE::Window::Window* window ); diff --git a/include/eepp/window/engine.hpp b/include/eepp/window/engine.hpp index 233c64ec8..d03d97852 100644 --- a/include/eepp/window/engine.hpp +++ b/include/eepp/window/engine.hpp @@ -6,6 +6,13 @@ #include #include +#include + +namespace EE { namespace Graphics { +class ResourceCatalog; +class ResourceScope; +}} // namespace EE::Graphics + namespace EE { namespace System { class IniFile; class Pack; @@ -140,6 +147,12 @@ class EE_API Engine { /** @return The display manager. Holds the physical displays information. */ DisplayManager* getDisplayManager(); + /** @return The catalog used for resources intentionally exported application-wide. */ + std::shared_ptr getGlobalResourceCatalog() const; + + /** @return The default Graphics scope. It explicitly imports the global resource catalog. */ + std::shared_ptr getDefaultResourceScope() const; + /** Open a URL in a separate, system-provided application. * @return true if success */ @@ -156,6 +169,8 @@ class EE_API Engine { PlatformHelper* mPlatformHelper; Pack* mZip; DisplayManager* mDisplayManager; + std::shared_ptr mGlobalResourceCatalog; + std::shared_ptr mDefaultResourceScope; Engine(); diff --git a/premake4.lua b/premake4.lua index 534fc22a4..387cf30d0 100644 --- a/premake4.lua +++ b/premake4.lua @@ -654,7 +654,7 @@ function build_link_configuration( package_name, use_ee_icon ) add_cross_config_links() configuration "emscripten" - linkoptions { "-s TOTAL_MEMORY=536870912 -s ALLOW_MEMORY_GROWTH=1 -s USE_SDL=2" } + linkoptions { "-s TOTAL_MEMORY=536870912 -s ALLOW_MEMORY_GROWTH=1 -s USE_SDL=2 -s ENVIRONMENT=worker,web" } buildoptions { "-s USE_SDL=2" } buildoptions { "-s USE_PTHREADS=1" } linkoptions { "-s USE_PTHREADS=1 -sPTHREAD_POOL_SIZE=8" } diff --git a/premake5.lua b/premake5.lua index 6f0f8c7fb..7eb71be33 100644 --- a/premake5.lua +++ b/premake5.lua @@ -372,7 +372,7 @@ function build_base_configuration( package_name ) buildoptions { "/utf-8" } filter "system:emscripten" - buildoptions { "-O3 -s USE_SDL=2 -s PRECISE_F32=1 -s ENVIRONMENT=worker,web" } + buildoptions { "-O3 -s USE_SDL=2" } buildoptions { "-s USE_PTHREADS=1" } filter {} @@ -412,7 +412,7 @@ function build_base_cpp_configuration( package_name ) symbols "On" filter "system:emscripten" - buildoptions { "-O3 -s USE_SDL=2 -s PRECISE_F32=1 -s ENVIRONMENT=worker,web" } + buildoptions { "-O3 -s USE_SDL=2" } buildoptions { "-s USE_PTHREADS=1" } filter {} @@ -445,6 +445,23 @@ function build_link_configuration( package_name, use_ee_icon ) incdirs { "include" } local extension = ""; + if os.istarget("emscripten") and package_name ~= "eepp" and package_name ~= "eepp-static" then + local without_assets = { + ["eepp-empty-window"] = true, + ["eepp-sound"] = true, + ["eepp-vbo-fbo-batch"] = true, + ["eepp-physics-demo"] = true, + ["eepp-http-request"] = true, + } + if not without_assets[package_name] then + if package_name == "ecode" then + linkoptions { "--preload-file " .. package_name .. "/assets/" } + else + linkoptions { "--preload-file assets/" } + end + end + end + if package_name == "eepp" then defines { "EE_EXPORTS" } elseif package_name == "eepp-static" then @@ -583,8 +600,11 @@ function build_link_configuration( package_name, use_ee_icon ) filter "system:emscripten" targetname ( package_name .. extension ) - linkoptions { "-O3 -s TOTAL_MEMORY=536870912 -s ALLOW_MEMORY_GROWTH=1 -s USE_SDL=2" } - buildoptions { "-O3 -s USE_SDL=2 -s PRECISE_F32=1 -s ENVIRONMENT=worker,web" } + if package_name ~= "eepp" and package_name ~= "eepp-static" then + targetextension ".html" + end + linkoptions { "-O3 -s TOTAL_MEMORY=536870912 -s ALLOW_MEMORY_GROWTH=1 -s USE_SDL=2 -s ENVIRONMENT=worker,web" } + buildoptions { "-O3 -s USE_SDL=2" } buildoptions { "-s USE_PTHREADS=1" } linkoptions { "-s USE_PTHREADS=1 -sPTHREAD_POOL_SIZE=8" } @@ -739,7 +759,9 @@ end function add_sdl2() print("Using SDL2 backend"); if not can_add_static_backend("SDL2") then - table.insert( link_list, get_backend_link_name( "SDL2" ) ) + if not os.istarget("emscripten") then + table.insert( link_list, get_backend_link_name( "SDL2" ) ) + end else print("Using static backend") insert_static_backend( "SDL2" ) @@ -1042,6 +1064,8 @@ function build_eepp( build_name ) end function target_dir_lib(path) + filter "architecture:wasm32" + targetdir("libs/" .. os.target() .. "/wasm32/" .. path .. "/") filter "architecture:x86" targetdir("libs/" .. os.target() .. "/x86/" .. path .. "/") filter "architecture:x86_64" @@ -1084,6 +1108,9 @@ workspace "eepp" if _ACTION == "ninja" then configurations { "debug", "release" } platforms { get_architecture() } + elseif os.istarget("emscripten") then + configurations { "debug", "release" } + platforms { "wasm32" } else configurations { "debug", "release" } platforms { "x86_64", "x86", "arm64" } @@ -1111,6 +1138,9 @@ workspace "eepp" filter "platforms:x86_64" architecture "x86_64" + filter "platforms:wasm32" + architecture "wasm32" + filter { "platforms:x86_64", "system:macosx" } architecture "x86_64" buildoptions { "-arch x86_64" } @@ -1518,19 +1548,21 @@ workspace "eepp" filter "action:not vs*" buildoptions { "-Wall" } - project "eepp-maps" - kind "SharedLib" - language "C++" - cppdialect "C++20" - incdirs { "include", "src/modules/maps/include/","src/modules/maps/src/" } - files { "src/modules/maps/src/**.cpp" } - links { "eepp-shared" } - defines { "EE_MAPS_EXPORTS" } - build_base_cpp_configuration( "eepp-maps" ) - postsymlinklib_arch( "eepp-maps" ) - target_dir_lib("") - filter "action:not vs*" - buildoptions { "-Wall" } + if not os.istarget("emscripten") then + project "eepp-maps" + kind "SharedLib" + language "C++" + cppdialect "C++20" + incdirs { "include", "src/modules/maps/include/","src/modules/maps/src/" } + files { "src/modules/maps/src/**.cpp" } + links { "eepp-shared" } + defines { "EE_MAPS_EXPORTS" } + build_base_cpp_configuration( "eepp-maps" ) + postsymlinklib_arch( "eepp-maps" ) + target_dir_lib("") + filter "action:not vs*" + buildoptions { "-Wall" } + end project "eepp-physics-static" kind "StaticLib" @@ -1547,19 +1579,21 @@ workspace "eepp" filter "action:not vs*" buildoptions { "-Wall" } - project "eepp-physics" - kind "SharedLib" - language "C++" - cppdialect "C++20" - incdirs { "include", "src/modules/physics/include/","src/modules/physics/src/" } - files { "src/modules/physics/src/**.cpp", "src/eepp/physics/constraints/*.cpp" } - links { "chipmunk-static", "eepp-shared" } - defines { "EE_PHYSICS_EXPORTS" } - build_base_cpp_configuration( "eepp-physics" ) - postsymlinklib_arch( "eepp-physics" ) - target_dir_lib("") - filter "action:not vs*" - buildoptions { "-Wall" } + if not os.istarget("emscripten") then + project "eepp-physics" + kind "SharedLib" + language "C++" + cppdialect "C++20" + incdirs { "include", "src/modules/physics/include/","src/modules/physics/src/" } + files { "src/modules/physics/src/**.cpp", "src/eepp/physics/constraints/*.cpp" } + links { "chipmunk-static", "eepp-shared" } + defines { "EE_PHYSICS_EXPORTS" } + build_base_cpp_configuration( "eepp-physics" ) + postsymlinklib_arch( "eepp-physics" ) + target_dir_lib("") + filter "action:not vs*" + buildoptions { "-Wall" } + end project "eterm-static" kind "StaticLib" @@ -1605,12 +1639,14 @@ workspace "eepp" target_dir_lib("") end - project "eepp-shared" - kind "SharedLib" - language "C++" - build_eepp( "eepp" ) - postsymlinklib_arch( "eepp" ) - target_dir_lib("") + if not os.istarget("emscripten") then + project "eepp-shared" + kind "SharedLib" + language "C++" + build_eepp( "eepp" ) + postsymlinklib_arch( "eepp" ) + target_dir_lib("") + end -- Examples project "eepp-external-shader" diff --git a/projects/emscripten/make.sh b/projects/emscripten/make.sh index f34a2b868..8ee7888d5 100755 --- a/projects/emscripten/make.sh +++ b/projects/emscripten/make.sh @@ -1,17 +1,25 @@ #!/bin/sh -# Currently latest emsdk tested and working version: latest-fastcomp -# remember to first set the environment +set -e + +# Currently tested emsdk version: 4.0.1 +# Remember to first set the environment: # source /path/to/emsdk/emsdk_env.sh -cd $(dirname "$0") || exit +cd "$(dirname "$0")" unset CPLUS_INCLUDE_PATH -premake4 --file=../../premake4.lua --with-gles2 --with-static-eepp --platform=emscripten --with-backend=SDL2 gmake -cd ../../make/emscripten/ || exit + +PREMAKE5=${PREMAKE5:-premake5} +if [ -x ../../premake5 ]; then + PREMAKE5=../../premake5 +fi + +"$PREMAKE5" --file=../../premake5.lua --os=emscripten --with-gles2 --with-static-eepp --with-backend=SDL2 gmake +cd ../../make/emscripten/ rm -rf ./assets cp -r ../../bin/assets/ . -rm assets/fonts/NotoColorEmoji.ttf assets/fonts/DejaVuSansMonoNerdFontComplete.ttf assets/fonts/DroidSansFallbackFull.ttf +rm -f assets/fonts/NotoColorEmoji.ttf assets/fonts/DejaVuSansMonoNerdFontComplete.ttf assets/fonts/DroidSansFallbackFull.ttf rm -rf ./ecode mkdir ecode cp -r ../../bin/assets/ ecode/assets/ -rm ecode/assets/fonts/DejaVuSansMonoNerdFontComplete.ttf ecode/assets/fonts/DroidSansFallbackFull.ttf ecode/assets/fonts/NotoColorEmoji.ttf ecode/assets/test.zip ecode/assets/ca-bundle.pem ecode/assets/icon/ee.icns ecode/assets/icon/ee.rc ecode/assets/icon/ee.res ecode/assets/icon/ee.ico ecode/assets/fonts/*.png ecode/assets/fonts/*.fnt ecode/assets/fonts/OpenSans-Regular.ttf ecode/assets/icon/ecode.icns ecode/assets/icon/eterm* ecode/assets/icon/*.svg -rm -r ecode/assets/atlases ecode/assets/screenshots ecode/assets/cursors ecode/assets/layouts ecode/assets/maps ecode/assets/sounds ecode/assets/sprites ecode/assets/tiles ecode/assets/shaders ecode/assets/ui/uitheme* +rm -f ecode/assets/fonts/DejaVuSansMonoNerdFontComplete.ttf ecode/assets/fonts/DroidSansFallbackFull.ttf ecode/assets/fonts/NotoColorEmoji.ttf ecode/assets/test.zip ecode/assets/ca-bundle.pem ecode/assets/icon/ee.icns ecode/assets/icon/ee.rc ecode/assets/icon/ee.res ecode/assets/icon/ee.ico ecode/assets/fonts/*.png ecode/assets/fonts/*.fnt ecode/assets/fonts/OpenSans-Regular.ttf ecode/assets/icon/ecode.icns ecode/assets/icon/eterm* ecode/assets/icon/*.svg +rm -rf ecode/assets/atlases ecode/assets/screenshots ecode/assets/cursors ecode/assets/layouts ecode/assets/maps ecode/assets/sounds ecode/assets/sprites ecode/assets/tiles ecode/assets/shaders ecode/assets/ui/uitheme* emmake make -j"$(nproc)" "$@" diff --git a/projects/linux/ee.files b/projects/linux/ee.files index b703fcf55..2e85401ed 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -57,11 +57,12 @@ ../../include/eepp/graphics/drawablegroup.hpp ../../include/eepp/graphics/drawable.hpp ../../include/eepp/graphics/drawableresource.hpp -../../include/eepp/graphics/drawablesearcher.hpp +../../include/eepp/ui/drawableresolver.hpp +../../include/eepp/ui/webresourcecache.hpp ../../include/eepp/graphics/fontbmfont.hpp ../../include/eepp/graphics/font.hpp ../../include/eepp/graphics/fontfamily.hpp -../../include/eepp/graphics/fontmanager.hpp +../../include/eepp/graphics/fontservice.hpp ../../include/eepp/graphics/fontsprite.hpp ../../include/eepp/graphics/fontstyleconfig.hpp ../../include/eepp/graphics/fonttruetype.hpp @@ -73,7 +74,6 @@ ../../include/eepp/graphics/glyphdrawable.hpp ../../include/eepp/graphics/image.hpp ../../include/eepp/graphics/ninepatch.hpp -../../include/eepp/graphics/ninepatchmanager.hpp ../../include/eepp/graphics/packerhelper.hpp ../../include/eepp/graphics/particle.hpp ../../include/eepp/graphics/particlesystem.hpp @@ -555,11 +555,12 @@ ../../src/eepp/graphics/drawable.cpp ../../src/eepp/graphics/drawablegroup.cpp ../../src/eepp/graphics/drawableresource.cpp -../../src/eepp/graphics/drawablesearcher.cpp +../../src/eepp/ui/drawableresolver.cpp +../../src/eepp/ui/webresourcecache.cpp ../../src/eepp/graphics/fontbmfont.cpp ../../src/eepp/graphics/font.cpp ../../src/eepp/graphics/fontfamily.cpp -../../src/eepp/graphics/fontmanager.cpp +../../src/eepp/graphics/fontservice.cpp ../../src/eepp/graphics/fontsprite.cpp ../../src/eepp/graphics/fonttruetype.cpp ../../src/eepp/graphics/framebuffer.cpp @@ -572,7 +573,6 @@ ../../src/eepp/graphics/glyphdrawable.cpp ../../src/eepp/graphics/image.cpp ../../src/eepp/graphics/ninepatch.cpp -../../src/eepp/graphics/ninepatchmanager.cpp ../../src/eepp/graphics/particle.cpp ../../src/eepp/graphics/particlesystem.cpp ../../src/eepp/graphics/pixeldensity.cpp diff --git a/projects/macos/ee.files b/projects/macos/ee.files index 466e77c6b..69aa2bb6e 100644 --- a/projects/macos/ee.files +++ b/projects/macos/ee.files @@ -57,11 +57,12 @@ ../../include/eepp/graphics/drawablegroup.hpp ../../include/eepp/graphics/drawable.hpp ../../include/eepp/graphics/drawableresource.hpp -../../include/eepp/graphics/drawablesearcher.hpp +../../include/eepp/ui/drawableresolver.hpp +../../include/eepp/ui/webresourcecache.hpp ../../include/eepp/graphics/fontbmfont.hpp ../../include/eepp/graphics/font.hpp ../../include/eepp/graphics/fontfamily.hpp -../../include/eepp/graphics/fontmanager.hpp +../../include/eepp/graphics/fontservice.hpp ../../include/eepp/graphics/fontsprite.hpp ../../include/eepp/graphics/fontstyleconfig.hpp ../../include/eepp/graphics/fonttruetype.hpp @@ -73,7 +74,6 @@ ../../include/eepp/graphics/glyphdrawable.hpp ../../include/eepp/graphics/image.hpp ../../include/eepp/graphics/ninepatch.hpp -../../include/eepp/graphics/ninepatchmanager.hpp ../../include/eepp/graphics/packerhelper.hpp ../../include/eepp/graphics/particle.hpp ../../include/eepp/graphics/particlesystem.hpp @@ -546,11 +546,12 @@ ../../src/eepp/graphics/drawable.cpp ../../src/eepp/graphics/drawablegroup.cpp ../../src/eepp/graphics/drawableresource.cpp -../../src/eepp/graphics/drawablesearcher.cpp +../../src/eepp/ui/drawableresolver.cpp +../../src/eepp/ui/webresourcecache.cpp ../../src/eepp/graphics/fontbmfont.cpp ../../src/eepp/graphics/font.cpp ../../src/eepp/graphics/fontfamily.cpp -../../src/eepp/graphics/fontmanager.cpp +../../src/eepp/graphics/fontservice.cpp ../../src/eepp/graphics/fontsprite.cpp ../../src/eepp/graphics/fonttruetype.cpp ../../src/eepp/graphics/framebuffer.cpp @@ -563,7 +564,6 @@ ../../src/eepp/graphics/glyphdrawable.cpp ../../src/eepp/graphics/image.cpp ../../src/eepp/graphics/ninepatch.cpp -../../src/eepp/graphics/ninepatchmanager.cpp ../../src/eepp/graphics/particle.cpp ../../src/eepp/graphics/particlesystem.cpp ../../src/eepp/graphics/pixeldensity.cpp diff --git a/projects/windows/ee.files b/projects/windows/ee.files index 18a331b57..d7359ae4f 100644 --- a/projects/windows/ee.files +++ b/projects/windows/ee.files @@ -56,10 +56,11 @@ ../../include/eepp/graphics/drawablegroup.hpp ../../include/eepp/graphics/drawable.hpp ../../include/eepp/graphics/drawableresource.hpp -../../include/eepp/graphics/drawablesearcher.hpp +../../include/eepp/ui/drawableresolver.hpp +../../include/eepp/ui/webresourcecache.hpp ../../include/eepp/graphics/fontbmfont.hpp ../../include/eepp/graphics/font.hpp -../../include/eepp/graphics/fontmanager.hpp +../../include/eepp/graphics/fontservice.hpp ../../include/eepp/graphics/fontsprite.hpp ../../include/eepp/graphics/fontstyleconfig.hpp ../../include/eepp/graphics/fonttruetype.hpp @@ -71,7 +72,6 @@ ../../include/eepp/graphics/glyphdrawable.hpp ../../include/eepp/graphics/image.hpp ../../include/eepp/graphics/ninepatch.hpp -../../include/eepp/graphics/ninepatchmanager.hpp ../../include/eepp/graphics/packerhelper.hpp ../../include/eepp/graphics/particle.hpp ../../include/eepp/graphics/particlesystem.hpp @@ -538,10 +538,11 @@ ../../src/eepp/graphics/drawable.cpp ../../src/eepp/graphics/drawablegroup.cpp ../../src/eepp/graphics/drawableresource.cpp -../../src/eepp/graphics/drawablesearcher.cpp +../../src/eepp/ui/drawableresolver.cpp +../../src/eepp/ui/webresourcecache.cpp ../../src/eepp/graphics/fontbmfont.cpp ../../src/eepp/graphics/font.cpp -../../src/eepp/graphics/fontmanager.cpp +../../src/eepp/graphics/fontservice.cpp ../../src/eepp/graphics/fontsprite.cpp ../../src/eepp/graphics/fonttruetype.cpp ../../src/eepp/graphics/framebuffer.cpp @@ -554,7 +555,6 @@ ../../src/eepp/graphics/glyphdrawable.cpp ../../src/eepp/graphics/image.cpp ../../src/eepp/graphics/ninepatch.cpp -../../src/eepp/graphics/ninepatchmanager.cpp ../../src/eepp/graphics/particle.cpp ../../src/eepp/graphics/particlesystem.cpp ../../src/eepp/graphics/pixeldensity.cpp diff --git a/src/benchmarks/inline_layout_benchmark.cpp b/src/benchmarks/inline_layout_benchmark.cpp index 97952c155..5bd292442 100644 --- a/src/benchmarks/inline_layout_benchmark.cpp +++ b/src/benchmarks/inline_layout_benchmark.cpp @@ -236,7 +236,7 @@ UTEST( Benchmark, InlineLayout ) { 800, 600, "bench", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); if ( !font->loaded() ) { Engine::destroySingleton(); @@ -323,9 +323,9 @@ UTEST( Benchmark, MarkdownReadme ) { UISceneNode* ui = UISceneNode::New( window ); SceneManager::instance()->add( ui ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); - FontTrueType* monoFont = FontTrueType::New( "monospace" ); + FontTrueType* monoFont = FontTrueType::New( "monospace" ).get(); monoFont->loadFromFile( "../assets/fonts/DejaVuSansMono.ttf" ); if ( !font->loaded() || !monoFont->loaded() ) { Engine::destroySingleton(); diff --git a/src/eepp/graphics/arcdrawable.cpp b/src/eepp/graphics/arcdrawable.cpp index 5cbf6ace7..959435d14 100644 --- a/src/eepp/graphics/arcdrawable.cpp +++ b/src/eepp/graphics/arcdrawable.cpp @@ -31,6 +31,18 @@ ArcDrawable::ArcDrawable( const Float& radius, Uint32 segmentsCount, const Float mSegmentsCount = mSegmentsCount > 360 ? 360 : mSegmentsCount; } +DrawablePtr ArcDrawable::clone() const { + auto instance = makeResource( mRadius, mSegmentsCount, mArcAngle, mArcStartAngle ); + instance->mOffset = mOffset; + instance->mFillMode = mFillMode; + instance->mBlendMode = mBlendMode; + instance->mLineWidth = mLineWidth; + instance->mSmooth = mSmooth; + instance->mColor = mColor; + instance->mPosition = mPosition; + return instance; +} + Sizef ArcDrawable::getSize() { return Sizef( mRadius * 2, mRadius * 2 ); } diff --git a/src/eepp/graphics/batchrenderer.cpp b/src/eepp/graphics/batchrenderer.cpp index 781c98d96..0928d87eb 100644 --- a/src/eepp/graphics/batchrenderer.cpp +++ b/src/eepp/graphics/batchrenderer.cpp @@ -53,16 +53,26 @@ void BatchRenderer::discard() { mNumVertex = 0; mTVertex = nullptr; mTexture = nullptr; + mTextureOwner.reset(); } void BatchRenderer::setTexture( const Texture* texture, Texture::CoordinateType coordinateType ) { - if ( mTexture != texture || mCoordinateType != coordinateType ) + if ( mTexture != texture || mCoordinateType != coordinateType ) { flush(); + mTextureOwner.reset(); + } mTexture = texture; mCoordinateType = coordinateType; } +void BatchRenderer::setTexture( const TexturePtr& texture, + Texture::CoordinateType coordinateType ) { + setTexture( texture.get(), coordinateType ); + if ( mTextureOwner != texture ) + mTextureOwner = texture; +} + void BatchRenderer::setBlendMode( const BlendMode& blend ) { if ( blend != mBlend ) flush(); @@ -94,8 +104,10 @@ void BatchRenderer::setDrawMode( const PrimitiveType& Mode, const bool& Force ) } void BatchRenderer::flush() { - if ( mNumVertex == 0 ) + if ( mNumVertex == 0 ) { + mTextureOwner.reset(); return; + } if ( GlobalBatchRenderer::instance() != this ) GlobalBatchRenderer::instance()->draw(); @@ -163,6 +175,8 @@ void BatchRenderer::flush() { GLi->enable( GL_TEXTURE_2D ); GLi->enableClientState( GL_TEXTURE_COORD_ARRAY ); } + + mTextureOwner.reset(); } void BatchRenderer::batchQuad( const Float& x, const Float& y, const Float& width, diff --git a/src/eepp/graphics/circledrawable.cpp b/src/eepp/graphics/circledrawable.cpp index f2d91b336..575f2323c 100644 --- a/src/eepp/graphics/circledrawable.cpp +++ b/src/eepp/graphics/circledrawable.cpp @@ -15,4 +15,18 @@ CircleDrawable::CircleDrawable() : ArcDrawable( 0, 64 ) {} CircleDrawable::CircleDrawable( const Float& radius, const Uint32& segmentsCount ) : ArcDrawable( radius, segmentsCount ) {} +DrawablePtr CircleDrawable::clone() const { + auto instance = makeResource( mRadius, mSegmentsCount ); + instance->mArcAngle = mArcAngle; + instance->mArcStartAngle = mArcStartAngle; + instance->mOffset = mOffset; + instance->mFillMode = mFillMode; + instance->mBlendMode = mBlendMode; + instance->mLineWidth = mLineWidth; + instance->mSmooth = mSmooth; + instance->mColor = mColor; + instance->mPosition = mPosition; + return instance; +} + }} // namespace EE::Graphics diff --git a/src/eepp/graphics/convexshapedrawable.cpp b/src/eepp/graphics/convexshapedrawable.cpp index a6ee38dd7..2f1d3d0fd 100644 --- a/src/eepp/graphics/convexshapedrawable.cpp +++ b/src/eepp/graphics/convexshapedrawable.cpp @@ -9,6 +9,19 @@ ConvexShapeDrawable* ConvexShapeDrawable::New() { ConvexShapeDrawable::ConvexShapeDrawable() : PrimitiveDrawable( Drawable::CONVEXSHAPE ) {} +DrawablePtr ConvexShapeDrawable::clone() const { + auto instance = makeResource(); + instance->mPolygon = mPolygon; + instance->mIndexColor = mIndexColor; + instance->mFillMode = mFillMode; + instance->mBlendMode = mBlendMode; + instance->mLineWidth = mLineWidth; + instance->mSmooth = mSmooth; + instance->mColor = mColor; + instance->mPosition = mPosition; + return instance; +} + Sizef ConvexShapeDrawable::getSize() { return mPolygon.getBounds().getSize(); } diff --git a/src/eepp/graphics/drawable.cpp b/src/eepp/graphics/drawable.cpp index 5bce0691b..fce244bbf 100644 --- a/src/eepp/graphics/drawable.cpp +++ b/src/eepp/graphics/drawable.cpp @@ -9,6 +9,10 @@ Drawable::Drawable( Type drawableType ) : Drawable::~Drawable() {} +DrawablePtr Drawable::clone() const { + return {}; +} + void Drawable::setAlpha( Uint8 alpha ) { if ( mColor.a != alpha ) { mColor.a = alpha; diff --git a/src/eepp/graphics/drawablegroup.cpp b/src/eepp/graphics/drawablegroup.cpp index 5148436f0..93ae78534 100644 --- a/src/eepp/graphics/drawablegroup.cpp +++ b/src/eepp/graphics/drawablegroup.cpp @@ -4,36 +4,47 @@ namespace EE { namespace Graphics { -DrawableGroup* DrawableGroup::New() { - return eeNew( DrawableGroup, () ); +ResourcePtr DrawableGroup::New() { + return makeResource(); } DrawableGroup::DrawableGroup() : - Drawable( Drawable::GROUP ), - mNeedsUpdate( true ), - mClipEnabled( false ), - mDrawableOwner( true ) {} + Drawable( Drawable::GROUP ), mNeedsUpdate( true ), mClipEnabled( false ) {} DrawableGroup::~DrawableGroup() { clearDrawables(); } -void DrawableGroup::clearDrawables() { - if ( mDrawableOwner ) { - for ( std::size_t i = 0; i < mGroup.size(); i++ ) { - Drawable* drawable = mGroup[i]; - eeSAFE_DELETE( drawable ); - } +DrawablePtr DrawableGroup::clone() const { + auto instance = makeResource(); + instance->mPosition = mPosition; + instance->mColor = mColor; + instance->mSize = mSize; + instance->mClipEnabled = mClipEnabled; + + for ( const auto& drawable : mGroup ) { + if ( !drawable ) + continue; + DrawablePtr drawableInstance = drawable->clone(); + if ( !drawableInstance ) + return {}; + instance->addDrawable( std::move( drawableInstance ) ); } + return instance; +} + +void DrawableGroup::clearDrawables() { mGroup.clear(); mPos.clear(); } -Drawable* DrawableGroup::addDrawable( Drawable* drawable ) { - mGroup.push_back( drawable ); +DrawablePtr DrawableGroup::addDrawable( DrawablePtr drawable ) { + if ( !drawable ) + return {}; mPos.push_back( drawable->getPosition() ); - return drawable; + mGroup.push_back( std::move( drawable ) ); + return mGroup.back(); } Uint32 DrawableGroup::getDrawableCount() const { @@ -48,15 +59,7 @@ void DrawableGroup::setClipEnabled( bool clipEnabled ) { mClipEnabled = clipEnabled; } -bool DrawableGroup::isDrawableOwner() const { - return mDrawableOwner; -} - -void DrawableGroup::setDrawableOwner( bool drawableOwner ) { - mDrawableOwner = drawableOwner; -} - -std::vector& DrawableGroup::getGroup() { +std::vector& DrawableGroup::getGroup() { return mGroup; } @@ -89,7 +92,7 @@ void DrawableGroup::draw( const Vector2f& position, const Sizef& size ) { GLi->getClippingMask()->clipPlaneEnable( mPosition.x, mPosition.y, mSize.x, mSize.y ); for ( std::size_t i = 0; i < mGroup.size(); i++ ) { - Drawable* drawable = mGroup[i]; + Drawable* drawable = mGroup[i].get(); drawable->draw(); } @@ -111,7 +114,7 @@ void DrawableGroup::onPositionChange() { void DrawableGroup::onAlphaChange() { for ( std::size_t i = 0; i < mGroup.size(); i++ ) { - Drawable* drawable = mGroup[i]; + Drawable* drawable = mGroup[i].get(); drawable->setAlpha( getAlpha() ); } } @@ -120,7 +123,7 @@ void DrawableGroup::update() { Sizef nSize( mSize ); for ( std::size_t i = 0; i < mGroup.size(); i++ ) { - Drawable* drawable = mGroup[i]; + Drawable* drawable = mGroup[i].get(); Vector2f pos( mPosition + mPos[i] ); Sizef s( mPos[i] + drawable->getSize() ); diff --git a/src/eepp/graphics/drawableresource.cpp b/src/eepp/graphics/drawableresource.cpp index cff407930..cc7261a99 100644 --- a/src/eepp/graphics/drawableresource.cpp +++ b/src/eepp/graphics/drawableresource.cpp @@ -1,20 +1,62 @@ #include +#include + namespace EE { namespace Graphics { -DrawableResource::DrawableResource( Type drawableType ) : - Drawable( drawableType ), mId( 0 ), mNumCallBacks( 0 ) { +DrawableResourceConnection::DrawableResourceConnection( + std::weak_ptr state, Uint32 id ) : + mState( std::move( state ) ), mId( id ) {} + +DrawableResourceConnection::~DrawableResourceConnection() { + disconnect(); +} + +DrawableResourceConnection::DrawableResourceConnection( + DrawableResourceConnection&& other ) noexcept : + mState( std::move( other.mState ) ), mId( other.mId ) { + other.mId = 0; +} + +DrawableResourceConnection& +DrawableResourceConnection::operator=( DrawableResourceConnection&& other ) noexcept { + if ( this != &other ) { + disconnect(); + mState = std::move( other.mState ); + mId = other.mId; + other.mId = 0; + } + return *this; +} + +void DrawableResourceConnection::disconnect() { + if ( mId != 0 ) { + if ( auto state = mState.lock() ) { + auto callback = + std::find_if( state->callbacks.begin(), state->callbacks.end(), + [this]( const auto& callback ) { return callback.first == mId; } ); + if ( callback != state->callbacks.end() ) + state->callbacks.erase( callback ); + } + } + mState.reset(); + mId = 0; +} + +DrawableResourceConnection::operator bool() const { + return mId != 0 && !mState.expired(); +} + +DrawableResource::DrawableResource( Type drawableType ) : Drawable( drawableType ), mId( 0 ) { createUnnamed(); } DrawableResource::DrawableResource( Type drawableType, const std::string& name ) : - Drawable( drawableType ), mId( 0 ), mNumCallBacks( 0 ) { + Drawable( drawableType ), mId( 0 ) { setName( name ); } -DrawableResource::~DrawableResource() { - sendEvent( Event::Unload ); -} +DrawableResource::~DrawableResource() {} const String::HashType& DrawableResource::getId() const { return mId; @@ -39,23 +81,28 @@ bool DrawableResource::isDrawableResource() const { } void DrawableResource::onResourceChange() { - sendEvent( Event::Change ); + sendResourceChanged(); } -void DrawableResource::sendEvent( const Event& event ) { - for ( const auto& cb : mCallbacks ) { - cb.second( cb.first, event, this ); - } +void DrawableResource::sendResourceChanged() { + if ( !mCallbackState ) + return; + + SmallVector callbacks; + for ( const auto& callback : mCallbackState->callbacks ) + callbacks.emplace_back( callback.second ); + for ( const auto& callback : callbacks ) + callback( *this ); } -Uint32 DrawableResource::pushResourceChangeCallback( const OnResourceChangeCallback& cb ) { - mNumCallBacks++; - mCallbacks[mNumCallBacks] = cb; - return mNumCallBacks; -} +DrawableResourceConnection +DrawableResource::connectResourceChange( OnResourceChangeCallback callback ) { + if ( !mCallbackState ) + mCallbackState = std::make_shared(); -bool DrawableResource::popResourceChangeCallback( const Uint32& callbackId ) { - return mCallbacks.erase( callbackId ) > 0; + Uint32 id = ++mCallbackState->nextId; + mCallbackState->callbacks.emplace_back( id, std::move( callback ) ); + return DrawableResourceConnection( mCallbackState, id ); } }} // namespace EE::Graphics diff --git a/src/eepp/graphics/drawablesearcher.cpp b/src/eepp/graphics/drawablesearcher.cpp deleted file mode 100644 index 7bb4ad7ba..000000000 --- a/src/eepp/graphics/drawablesearcher.cpp +++ /dev/null @@ -1,232 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -using namespace EE::Window; -using namespace EE::Network; - -namespace EE { namespace Graphics { - -bool DrawableSearcher::sPrintWarnings = false; - -static Drawable* getSprite( const std::string& sprite ) { - std::vector tTextureRegionVec = - TextureAtlasManager::instance()->getTextureRegionsByPattern( sprite ); - - if ( tTextureRegionVec.size() ) { - Sprite* tSprite = Graphics::Sprite::New(); - tSprite->createAnimation(); - tSprite->addFrames( tTextureRegionVec ); - - return tSprite; - } - - return NULL; -} - -static Drawable* searchByNameInternal( const std::string& name ) { - String::HashType id = String::hash( name ); - Drawable* drawable = TextureAtlasManager::instance()->getTextureRegionById( id ); - - if ( NULL == drawable ) { - drawable = NinePatchManager::instance()->getById( id ); - } - - if ( NULL == drawable ) { - drawable = TextureFactory::instance()->getByHash( id ); - } - - return drawable; -} - -static Drawable* parseDataURI( const std::string& name ) { - auto hash = MD5::fromString( name ).toHexString(); - Drawable* drawable = TextureFactory::instance()->getByName( hash ); - std::string::size_type formatAndEncSep; - if ( nullptr == drawable && - ( formatAndEncSep = name.find_first_of( ',' ) ) != std::string::npos ) { - std::string decodingType = "urldecode"; - std::string mediaType = name.substr( 0, formatAndEncSep ); - std::string format; - auto parts = String::split( mediaType, ';' ); - if ( parts.empty() ) - return nullptr; - auto formatNamePos = parts[0].find_first_of( '/' ); - if ( formatNamePos + 1 < mediaType.size() ) - format = parts[0].substr( formatNamePos + 1 ); - if ( parts.size() > 1 ) { - for ( size_t i = 1; i < parts.size(); ++i ) { - if ( "base64" == parts[i] ) { - decodingType = parts[i]; - break; - } - } - } - - Texture* tex = nullptr; - if ( !format.empty() && - ( Image::isImageExtension( "." + format ) || format == "svg+xml" ) ) { - Image::FormatConfiguration format; - format.svgScale( PixelDensity::getPixelDensity() ); - if ( decodingType == "base64" ) { - int fileStart = formatAndEncSep + 1; - std::string_view fileBase64 = std::string_view{ name }.substr( fileStart ); - std::string buffer; - int len = Base64::decode( fileBase64, buffer ); - if ( len > 0 ) { - tex = TextureFactory::instance()->loadFromMemory( - (const unsigned char*)buffer.c_str(), buffer.size(), false, - Texture::ClampMode::ClampToEdge, false, false, format ); - } - } else if ( decodingType == "urldecode" ) { - int fileStart = formatAndEncSep + 1; - std::string decoded( URI::decode( name.substr( fileStart ) ) ); - if ( !decoded.empty() ) { - tex = TextureFactory::instance()->loadFromMemory( - (const unsigned char*)decoded.c_str(), decoded.size(), false, - Texture::ClampMode::ClampToEdge, false, false, format ); - } - } - } - - if ( tex ) { - tex->setName( hash ); - drawable = tex; - } - } - return drawable; -} - -Drawable* DrawableSearcher::searchByName( const std::string& name, bool firstSearchSprite, - Network::URI referer ) { - Drawable* drawable = NULL; - - if ( name.size() ) { - bool searchedSprite = false; - - if ( firstSearchSprite ) { - if ( String::startsWith( name, "@sprite/" ) ) { - drawable = getSprite( name.substr( 8 ) ); - } else { - drawable = getSprite( name ); - } - - if ( NULL != drawable ) { - return drawable; - } - - searchedSprite = true; - } - - if ( name[0] == '@' ) { - if ( String::startsWith( name, "@textureregion/" ) ) { - drawable = - TextureAtlasManager::instance()->getTextureRegionByName( name.substr( 12 ) ); - } else if ( String::startsWith( name, "@image/" ) ) { - drawable = TextureFactory::instance()->getByName( name.substr( 7 ) ); - } else if ( String::startsWith( name, "@texture/" ) ) { - drawable = TextureFactory::instance()->getByName( name.substr( 9 ) ); - } else if ( String::startsWith( name, "@sprite/" ) && !searchedSprite ) { - drawable = getSprite( name.substr( 8 ) ); - } else if ( String::startsWith( name, "@drawable/" ) ) { - drawable = searchByNameInternal( name.substr( 10 ) ); - } else if ( String::startsWith( name, "@9p/" ) ) { - drawable = NinePatchManager::instance()->getByName( name.substr( 4 ) ); - } else { - drawable = searchByNameInternal( name ); - } - } else if ( String::startsWith( name, "file://" ) ) { - std::string filePath( name.substr( 7 ) ); - -#if EE_PLATFORM == EE_PLATFORM_WIN - if ( filePath.size() >= 3 && filePath[0] == '/' && String::isLetter( filePath[1] ) && - filePath[2] == ':' ) { - filePath = filePath.substr( 1 ); - } -#endif - - FileSystem::filePathRemoveProcessPath( filePath ); - - drawable = TextureFactory::instance()->getByName( filePath ); - - if ( NULL == drawable ) { - Texture* tex = TextureFactory::instance()->loadFromFile( filePath ); - - if ( tex ) - drawable = tex; - } - } else if ( String::startsWith( name, "http://" ) || - String::startsWith( name, "https://" ) ) { - Texture* texture = TextureFactory::instance()->getByName( name ); - - if ( NULL == texture && Engine::instance()->isSharedGLContextEnabled() ) { - texture = TextureFactory::instance()->createEmptyTexture( - 1, 1, 4, Color::Transparent, false, Texture::ClampMode::ClampToEdge, false, - false, name ); - - std::map headers; - if ( !referer.empty() ) - headers["referer"] = referer.toString(); - - Http::getAsync( - [texture, name]( const Http&, Http::Request&, Http::Response& response ) { - if ( response.isOK() && !response.getBody().empty() ) { - Image image( (const Uint8*)&response.getBody()[0], - response.getBody().size() ); - - if ( image.getPixels() != NULL ) - texture->replace( &image ); - } else { - Log::debug( "DrawableSearcher::searchByName: could not download image: " - "%s. Error: %d\n%s", - name, response.getStatus(), response.getBody() ); - } - }, - URI( name ), Seconds( 5 ), {}, headers ); - } - - drawable = texture; - } else if ( String::startsWith( name, "data:image/" ) ) { - drawable = parseDataURI( name ); - } else { - drawable = searchByNameInternal( name ); - } - } - - if ( NULL == drawable && sPrintWarnings ) - Log::warning( "DrawableSearcher::searchByName: \"%s\" not found", name.c_str() ); - - return drawable; -} - -Drawable* DrawableSearcher::searchById( const Uint32& id ) { - Drawable* drawable = TextureAtlasManager::instance()->getTextureRegionById( id ); - - if ( NULL == drawable ) { - drawable = TextureFactory::instance()->getByHash( id ); - } - - if ( NULL == drawable && sPrintWarnings ) - Log::warning( "DrawableSearcher::searchById: \"%ld\" not found", id ); - - return drawable; -} - -void DrawableSearcher::setPrintWarnings( const bool& print ) { - sPrintWarnings = print; -} - -bool DrawableSearcher::getPrintWarnings() { - return sPrintWarnings; -} - -}} // namespace EE::Graphics diff --git a/src/eepp/graphics/font.cpp b/src/eepp/graphics/font.cpp index d99a017c3..e58c2918c 100644 --- a/src/eepp/graphics/font.cpp +++ b/src/eepp/graphics/font.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -80,19 +79,19 @@ std::vector Font::emojiCodePointsPositions( const String& string ) Font::Font( const FontType& Type, const std::string& Name ) : mType( Type ), mNumCallBacks( 0 ) { this->setName( Name ); - FontManager::instance()->add( this ); } -Font::~Font() { - if ( !FontManager::instance()->isDestroying() ) { - FontManager::instance()->remove( this, false ); - } -} +Font::~Font() {} const FontType& Font::getType() const { return mType; } +Float Font::getGlyphAdvance( Uint32 codePoint, unsigned int characterSize, bool bold, bool italic, + Float outlineThickness ) const { + return getGlyph( codePoint, characterSize, bold, italic, outlineThickness ).advance; +} + const std::string& Font::getName() const { return mFontName; } diff --git a/src/eepp/graphics/fontbmfont.cpp b/src/eepp/graphics/fontbmfont.cpp index 2763a8d03..625d3d52f 100644 --- a/src/eepp/graphics/fontbmfont.cpp +++ b/src/eepp/graphics/fontbmfont.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -10,12 +11,27 @@ namespace EE { namespace Graphics { -FontBMFont* FontBMFont::New( const std::string fontName ) { - return eeNew( FontBMFont, ( fontName ) ); +FontBMFontPtr FontBMFont::New( const std::string fontName ) { + FontBMFontPtr font( eeNew( FontBMFont, ( fontName ) ), ResourceDeleter() ); + defaultResourceScope().publishLocalFont( fontName, font ); + return font; } -FontBMFont* FontBMFont::New( const std::string fontName, const std::string& filename ) { - FontBMFont* fontBMFont = New( fontName ); +FontBMFontPtr FontBMFont::New( const std::string fontName, ResourceScope& resourceScope ) { + FontBMFontPtr font( eeNew( FontBMFont, ( fontName ) ), ResourceDeleter() ); + resourceScope.publishLocalFont( fontName, font ); + return font; +} + +FontBMFontPtr FontBMFont::New( const std::string fontName, const std::string& filename ) { + FontBMFontPtr fontBMFont = New( fontName ); + fontBMFont->loadFromFile( filename ); + return fontBMFont; +} + +FontBMFontPtr FontBMFont::New( const std::string fontName, const std::string& filename, + ResourceScope& resourceScope ) { + FontBMFontPtr fontBMFont = New( fontName, resourceScope ); fontBMFont->loadFromFile( filename ); return fontBMFont; } @@ -113,13 +129,13 @@ bool FontBMFont::loadFromStream( IOStream& stream ) { } } - Texture* tex = TF->loadFromPixels( rgbaImg.getPixelsPtr(), rgbaImg.getWidth(), - rgbaImg.getHeight(), rgbaImg.getChannels() ); + TexturePtr tex = TF->loadFromPixels( rgbaImg.getPixelsPtr(), rgbaImg.getWidth(), + rgbaImg.getHeight(), rgbaImg.getChannels() ); mPages[mFontSize].texture = tex; } else { - Texture* tex = TF->loadFromPixels( img.getPixelsPtr(), img.getWidth(), - img.getHeight(), img.getChannels() ); + TexturePtr tex = TF->loadFromPixels( img.getPixelsPtr(), img.getWidth(), + img.getHeight(), img.getChannels() ); mPages[mFontSize].texture = tex; } @@ -194,6 +210,11 @@ Glyph FontBMFont::getGlyph( Uint32 codePoint, unsigned int characterSize, bool b } } +Float FontBMFont::getGlyphAdvance( Uint32 codePoint, unsigned int characterSize, bool bold, + bool italic, Float outlineThickness ) const { + return getGlyph( codePoint, characterSize, bold, italic, outlineThickness ).advance; +} + GlyphDrawable* FontBMFont::getGlyphDrawable( Uint32 codePoint, unsigned int characterSize, bool bold, bool italic, Float outlineThickness ) const { @@ -254,7 +275,7 @@ Float FontBMFont::getUnderlineThickness( unsigned int ) const { return 0.f; } -Texture* FontBMFont::getTexture( unsigned int ) const { +const TexturePtr& FontBMFont::getTexture( unsigned int ) const { return mPages[mFontSize].texture; } @@ -274,9 +295,6 @@ FontBMFont& FontBMFont::operator=( const FontBMFont& right ) { FontBMFont::Page::~Page() { for ( auto drawable : drawables ) eeDelete( drawable.second ); - - if ( NULL != texture && TextureFactory::existsSingleton() ) - TextureFactory::instance()->remove( texture->getTextureId() ); } }} // namespace EE::Graphics diff --git a/src/eepp/graphics/fontfamily.cpp b/src/eepp/graphics/fontfamily.cpp index c8ca1ea79..e07f79aaf 100644 --- a/src/eepp/graphics/fontfamily.cpp +++ b/src/eepp/graphics/fontfamily.cpp @@ -1,4 +1,5 @@ #include +#include #include using namespace std::literals; @@ -53,11 +54,15 @@ std::string FontFamily::findType( const std::string& fontpath, const std::string return ""; } -FontTrueType* FontFamily::setFont( FontTrueType* font, const std::string& fontpath, - const std::string_view& fontType ) { +void FontFamily::setFont( FontTrueType* font, const std::string& fontpath, + const std::string_view& fontType ) { if ( fontpath.empty() ) - return nullptr; - FontTrueType* loadedFont = FontTrueType::New( font->getName() + "-" + fontType, fontpath ); + return; + FontService* fontService = font->getFontService(); + FontTrueTypePtr loadedFont = + fontService ? FontTrueType::New( font->getName() + "-" + fontType, fontpath, + fontService->getResourceScope() ) + : FontTrueType::New( font->getName() + "-" + fontType, fontpath ); if ( fontType == "bold"sv ) font->setBoldFont( loadedFont ); else if ( fontType == "italic"sv ) @@ -66,7 +71,6 @@ FontTrueType* FontFamily::setFont( FontTrueType* font, const std::string& fontpa font->setBoldItalicFont( loadedFont ); loadedFont->setBoldAdvanceSameAsRegular( font->getBoldAdvanceSameAsRegular() ); loadedFont->setEnableDynamicMonospace( font->getEnableDynamicMonospace() ); - return loadedFont; } }} // namespace EE::Graphics diff --git a/src/eepp/graphics/fontmanager.cpp b/src/eepp/graphics/fontmanager.cpp deleted file mode 100644 index 9d785ec6d..000000000 --- a/src/eepp/graphics/fontmanager.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include -#include -#include -#include - -namespace EE { namespace Graphics { - -SINGLETON_DECLARE_IMPLEMENTATION( FontManager ) - -FontManager::FontManager() {} - -FontManager::~FontManager() { - mEmojiFont = nullptr; - mColorEmojiFont = nullptr; - mFallbackFonts.clear(); - mSystemFallbackFonts.clear(); -} - -Graphics::Font* FontManager::add( Graphics::Font* font ) { - eeASSERT( NULL != font ); - return ResourceManager::add( font ); -} - -void FontManager::setColorEmojiFont( Font* font ) { - mColorEmojiFont = font; -} - -Graphics::Font* FontManager::getColorEmojiFont() const { - return mColorEmojiFont; -} - -Graphics::Font* FontManager::getEmojiFont() const { - return mEmojiFont; -} - -void FontManager::setEmojiFont( Graphics::Font* newEmojiFont ) { - mEmojiFont = newEmojiFont; -} - -const std::vector& FontManager::getFallbackFonts() const { - return mFallbackFonts; -} - -bool FontManager::hasFallbackFonts() const { - return !mFallbackFonts.empty(); -} - -bool FontManager::addFallbackFont( Font* fallbackFont ) { - if ( fallbackFont && std::find( mFallbackFonts.begin(), mFallbackFonts.end(), fallbackFont ) == - mFallbackFonts.end() ) { - mFallbackFonts.emplace_back( fallbackFont ); - return true; - } - return false; -} - -bool FontManager::removeFallbackFont( Font* fallbackFont ) { - auto fallbackFontIt = std::find( mFallbackFonts.begin(), mFallbackFonts.end(), fallbackFont ); - if ( fallbackFontIt != mFallbackFonts.end() ) { - mFallbackFonts.erase( fallbackFontIt ); - return true; - } - return false; -} - -FontHinting FontManager::getHinting() const { - return mHinting; -} - -void FontManager::setHinting( FontHinting hinting ) { - mHinting = hinting; - - for ( auto [_, font] : mResources ) { - if ( font->getType() == FontType::TTF ) { - auto ttf = static_cast( font ); - if ( !ttf->isEmojiFont() ) - ttf->setHinting( hinting ); - } - } -} - -FontAntialiasing FontManager::getAntialiasing() const { - return mAntialiasing; -} - -void FontManager::setAntialiasing( FontAntialiasing antialiasing ) { - mAntialiasing = antialiasing; - - for ( auto [_, font] : mResources ) { - if ( font->getType() == FontType::TTF ) { - auto ttf = static_cast( font ); - if ( !ttf->isEmojiFont() ) - ttf->setAntialiasing( antialiasing ); - } - } -} - -Font* FontManager::getByInternalId( Uint32 internalId ) const { - for ( auto [_, font] : mResources ) { - if ( font->getType() == FontType::TTF && - static_cast( font )->getFontInternalId() == internalId ) - return font; - } - return nullptr; -} - -FontTrueType* FontManager::getOrLoadSystemFallbackFont( const FontDesc& desc ) { - if ( desc.path.empty() ) - return nullptr; - - for ( auto* font : mSystemFallbackFonts ) { - if ( font->getType() == FontType::TTF ) { - auto* ttf = static_cast( font ); - if ( ttf->getInfo().fontpath + ttf->getInfo().filename == desc.path && - ttf->getFaceIndex() == desc.faceIndex ) - return ttf; - } - } - - FontTrueType* ttf = FontTrueType::New( desc.family, desc.path, desc.faceIndex ); - if ( !ttf || !ttf->loaded() ) { - eeSAFE_DELETE( ttf ); - return nullptr; - } - - mSystemFallbackFonts.push_back( ttf ); - - return ttf; -} - -}} // namespace EE::Graphics diff --git a/src/eepp/graphics/fontservice.cpp b/src/eepp/graphics/fontservice.cpp new file mode 100644 index 000000000..250fd4dc6 --- /dev/null +++ b/src/eepp/graphics/fontservice.cpp @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include + +namespace EE { namespace Graphics { + +FontService::FontService( ResourceScope& resourceScope ) : mResourceScope( resourceScope ) {} + +ResourceScope& FontService::getResourceScope() const { + return mResourceScope; +} + +FontPtr FontService::findHandle( Font* font ) const { + if ( !font ) + return {}; + FontPtr handle = mResourceScope.findFont( font->getName() ); + return handle.get() == font ? handle : FontPtr{}; +} + +void FontService::onFontRemoved( Font* font ) { + if ( mColorEmojiFont.get() == font ) + mColorEmojiFont.reset(); + if ( mEmojiFont.get() == font ) + mEmojiFont.reset(); + removeFallbackFont( font ); + mSystemFallbackFonts.erase( + std::remove_if( mSystemFallbackFonts.begin(), mSystemFallbackFonts.end(), + [font]( const FontPtr& systemFont ) { return systemFont.get() == font; } ), + mSystemFallbackFonts.end() ); +} + +void FontService::setColorEmojiFont( Font* font ) { + mColorEmojiFont = findHandle( font ); +} + +Font* FontService::getColorEmojiFont() const { + return mColorEmojiFont.get(); +} + +Font* FontService::getEmojiFont() const { + return mEmojiFont.get(); +} + +void FontService::setEmojiFont( Font* font ) { + mEmojiFont = findHandle( font ); +} + +const std::vector& FontService::getFallbackFonts() const { + return mFallbackFonts; +} + +bool FontService::hasFallbackFonts() const { + return !mFallbackFonts.empty(); +} + +bool FontService::addFallbackFont( FontPtr fallbackFont ) { + if ( fallbackFont && std::find( mFallbackFonts.begin(), mFallbackFonts.end(), fallbackFont ) == + mFallbackFonts.end() ) { + mFallbackFonts.emplace_back( std::move( fallbackFont ) ); + return true; + } + return false; +} + +bool FontService::addFallbackFont( Font* fallbackFont ) { + return addFallbackFont( findHandle( fallbackFont ) ); +} + +bool FontService::removeFallbackFont( Font* fallbackFont ) { + auto it = std::find_if( + mFallbackFonts.begin(), mFallbackFonts.end(), + [fallbackFont]( const FontPtr& font ) { return font.get() == fallbackFont; } ); + if ( it == mFallbackFonts.end() ) + return false; + mFallbackFonts.erase( it ); + return true; +} + +FontHinting FontService::getHinting() const { + return mHinting; +} + +void FontService::setHinting( FontHinting hinting ) { + mHinting = hinting; + for ( const FontPtr& fontHandle : mResourceScope.getFonts() ) { + Font* font = fontHandle.get(); + if ( font->getType() == FontType::TTF ) { + auto ttf = static_cast( font ); + if ( ttf->getFontService() == this && !ttf->isEmojiFont() ) + ttf->setHinting( hinting ); + } + } +} + +FontAntialiasing FontService::getAntialiasing() const { + return mAntialiasing; +} + +void FontService::setAntialiasing( FontAntialiasing antialiasing ) { + mAntialiasing = antialiasing; + for ( const FontPtr& fontHandle : mResourceScope.getFonts() ) { + Font* font = fontHandle.get(); + if ( font->getType() == FontType::TTF ) { + auto ttf = static_cast( font ); + if ( ttf->getFontService() == this && !ttf->isEmojiFont() ) + ttf->setAntialiasing( antialiasing ); + } + } +} + +Font* FontService::getByInternalId( Uint32 internalId ) const { + for ( const FontPtr& fontHandle : mResourceScope.getFonts() ) { + Font* font = fontHandle.get(); + if ( font->getType() == FontType::TTF && + static_cast( font )->getFontInternalId() == internalId ) + return font; + } + return nullptr; +} + +ResourcePtr FontService::loadSystemFont( const FontDesc& desc ) { + if ( desc.path.empty() ) + return {}; + + FontTrueTypePtr ttf( eeNew( FontTrueType, ( desc.family, *this ) ), + ResourceDeleter() ); + if ( !ttf->loadFromFile( desc.path, desc.faceIndex ) ) + return {}; + + ttf->setHinting( mHinting ); + ttf->setAntialiasing( mAntialiasing ); + // A standalone font can outlive this service, so it must not retain a borrowed service pointer. + ttf->setFontService( nullptr ); + return ttf; +} + +FontTrueType* FontService::getOrLoadSystemFallbackFont( const FontDesc& desc ) { + if ( desc.path.empty() ) + return nullptr; + + for ( const FontPtr& fontHandle : mSystemFallbackFonts ) { + auto* ttf = static_cast( fontHandle.get() ); + if ( ttf->getInfo().fontpath + ttf->getInfo().filename == desc.path && + ttf->getFaceIndex() == desc.faceIndex ) + return ttf; + } + + FontTrueTypePtr ttf = + FontTrueType::New( desc.family, desc.path, desc.faceIndex, mResourceScope ); + if ( !ttf || !ttf->loaded() ) { + if ( ttf ) + mResourceScope.eraseLocalFont( ttf.get() ); + return nullptr; + } + + mSystemFallbackFonts.emplace_back( ttf ); + return ttf.get(); +} + +}} // namespace EE::Graphics diff --git a/src/eepp/graphics/fontsprite.cpp b/src/eepp/graphics/fontsprite.cpp index fcd51031e..211b7debb 100644 --- a/src/eepp/graphics/fontsprite.cpp +++ b/src/eepp/graphics/fontsprite.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -10,12 +11,27 @@ namespace EE { namespace Graphics { -FontSprite* FontSprite::New( const std::string fontName ) { - return eeNew( FontSprite, ( fontName ) ); +FontSpritePtr FontSprite::New( const std::string fontName ) { + FontSpritePtr font( eeNew( FontSprite, ( fontName ) ), ResourceDeleter() ); + defaultResourceScope().publishLocalFont( fontName, font ); + return font; } -FontSprite* FontSprite::New( const std::string fontName, const std::string& filename ) { - FontSprite* fontSprite = New( fontName ); +FontSpritePtr FontSprite::New( const std::string fontName, ResourceScope& resourceScope ) { + FontSpritePtr font( eeNew( FontSprite, ( fontName ) ), ResourceDeleter() ); + resourceScope.publishLocalFont( fontName, font ); + return font; +} + +FontSpritePtr FontSprite::New( const std::string fontName, const std::string& filename ) { + FontSpritePtr fontSprite = New( fontName ); + fontSprite->loadFromFile( filename ); + return fontSprite; +} + +FontSpritePtr FontSprite::New( const std::string fontName, const std::string& filename, + ResourceScope& resourceScope ) { + FontSpritePtr fontSprite = New( fontName, resourceScope ); fontSprite->loadFromFile( filename ); return fontSprite; } @@ -136,7 +152,7 @@ bool FontSprite::loadFromStream( IOStream& stream, Color key, Uint32 firstChar, img.createMaskFromColor( Color::Fuchsia, 0 ); - Texture* texture = TextureFactory::instance()->loadFromPixels( + TexturePtr texture = TextureFactory::instance()->loadFromPixels( img.getPixelsPtr(), img.getWidth(), img.getHeight(), img.getChannels() ); mPages[mFontSize].texture = texture; if ( NULL != texture ) { @@ -232,7 +248,7 @@ Float FontSprite::getUnderlineThickness( unsigned int ) const { return 0.f; } -Texture* FontSprite::getTexture( unsigned int ) const { +const TexturePtr& FontSprite::getTexture( unsigned int ) const { return mPages[mFontSize].texture; } @@ -252,9 +268,6 @@ FontSprite& FontSprite::operator=( const FontSprite& right ) { FontSprite::Page::~Page() { for ( auto drawable : drawables ) eeDelete( drawable.second ); - - if ( NULL != texture && TextureFactory::existsSingleton() ) - TextureFactory::instance()->remove( texture->getTextureId() ); } }} // namespace EE::Graphics diff --git a/src/eepp/graphics/fonttruetype.cpp b/src/eepp/graphics/fonttruetype.cpp index 0518ab2a4..d27d8dead 100644 --- a/src/eepp/graphics/fonttruetype.cpp +++ b/src/eepp/graphics/fonttruetype.cpp @@ -1,8 +1,10 @@ #include -#include +#include #include +#include #include #include +#include #include #include #include @@ -266,24 +268,49 @@ static inline Uint64 getKerningKey( Uint32 first, Uint32 second, unsigned int ch ( static_cast( static_cast( outlineThickness * 100.f ) & 0xFF ) ); } -FontTrueType* FontTrueType::New( const std::string& FontName ) { - return eeNew( FontTrueType, ( FontName ) ); +FontTrueTypePtr FontTrueType::New( const std::string& FontName ) { + FontTrueTypePtr font( + eeNew( FontTrueType, ( FontName, defaultResourceScope().getFontService() ) ), + ResourceDeleter() ); + defaultResourceScope().publishLocalFont( FontName, font ); + return font; } -FontTrueType* FontTrueType::New( const std::string& FontName, const std::string& filename ) { - FontTrueType* fontTrueType = New( FontName ); +FontTrueTypePtr FontTrueType::New( const std::string& FontName, ResourceScope& resourceScope ) { + FontTrueTypePtr font( eeNew( FontTrueType, ( FontName, resourceScope.getFontService() ) ), + ResourceDeleter() ); + resourceScope.publishLocalFont( FontName, font ); + return font; +} + +FontTrueTypePtr FontTrueType::New( const std::string& FontName, const std::string& filename ) { + FontTrueTypePtr fontTrueType = New( FontName ); fontTrueType->loadFromFile( filename ); return fontTrueType; } -FontTrueType* FontTrueType::New( const std::string& FontName, const std::string& filename, - Uint32 faceIndex ) { - FontTrueType* fontTrueType = New( FontName ); +FontTrueTypePtr FontTrueType::New( const std::string& FontName, const std::string& filename, + ResourceScope& resourceScope ) { + FontTrueTypePtr fontTrueType = New( FontName, resourceScope ); + fontTrueType->loadFromFile( filename ); + return fontTrueType; +} + +FontTrueTypePtr FontTrueType::New( const std::string& FontName, const std::string& filename, + Uint32 faceIndex ) { + FontTrueTypePtr fontTrueType = New( FontName ); fontTrueType->loadFromFile( filename, faceIndex ); return fontTrueType; } -FontTrueType::FontTrueType( const std::string& FontName ) : +FontTrueTypePtr FontTrueType::New( const std::string& FontName, const std::string& filename, + Uint32 faceIndex, ResourceScope& resourceScope ) { + FontTrueTypePtr fontTrueType = New( FontName, resourceScope ); + fontTrueType->loadFromFile( filename, faceIndex ); + return fontTrueType; +} + +FontTrueType::FontTrueType( const std::string& FontName, FontService& fontService ) : Font( FontType::TTF, FontName ), mLibrary( NULL ), mFace( NULL ), @@ -307,17 +334,19 @@ FontTrueType::FontTrueType( const std::string& FontName ) : mIsBold( false ), mIsItalic( false ), mIsMonospaceCompletePending( false ), - mHinting( FontManager::instance()->getHinting() ), - mAntialiasing( FontManager::instance()->getAntialiasing() ), - mFaceIndex( 0 ), - mFontBold( nullptr ), - mFontItalic( nullptr ), - mFontBoldItalic( nullptr ), - mFontBoldCb( 0 ), - mFontItalicCb( 0 ), - mFontBoldItalicCb( 0 ) {} + mHinting( fontService.getHinting() ), + mAntialiasing( fontService.getAntialiasing() ), + mFontService( &fontService ), + mFaceIndex( 0 ) {} + +void FontTrueType::setFontService( FontService* fontService ) { + mFontService = fontService; +} FontTrueType::~FontTrueType() { + // Cached shaped glyphs borrow FontTrueType pointers. Drop layouts referencing this font before + // it dies, while preserving unrelated entries in the shared cache. + TextLayout::clearLayoutCache( this ); cleanup(); } @@ -516,12 +545,12 @@ bool FontTrueType::setFontFace( void* _face ) { #endif } - if ( ( mIsColorEmojiFont || mHasColrGlyphs ) && - FontManager::instance()->getColorEmojiFont() == nullptr ) - FontManager::instance()->setColorEmojiFont( this ); + if ( mFontService && ( mIsColorEmojiFont || mHasColrGlyphs ) && + mFontService->getColorEmojiFont() == nullptr ) + mFontService->setColorEmojiFont( this ); - if ( mIsEmojiFont && !mHasSvgGlyphs && FontManager::instance()->getEmojiFont() == nullptr ) - FontManager::instance()->setEmojiFont( this ); + if ( mFontService && mIsEmojiFont && !mHasSvgGlyphs && mFontService->getEmojiFont() == nullptr ) + mFontService->setEmojiFont( this ); // Load the stroker that will be used to outline the font FT_Stroker stroker = nullptr; @@ -611,6 +640,7 @@ bool FontTrueType::setVariableFontWeight( FontWeight weight ) { mKeyCache.clear(); mKerningCache.clear(); mKerningGlyphCache.clear(); + mGlyphAdvanceCache.clear(); #ifdef EE_TEXT_SHAPER_ENABLED if ( mHBFont ) hb_ft_font_changed( static_cast( mHBFont ) ); @@ -649,11 +679,11 @@ Glyph FontTrueType::getGlyph( Uint32 codePoint, unsigned int characterSize, bool Uint32 idx = 0; if ( mEnableEmojiFallback && !mIsColorEmojiFont && !mHasSvgGlyphs && !mHasColrGlyphs && !mIsEmojiFont && Font::isEmojiCodePoint( codePoint ) ) { - if ( !mIsColorEmojiFont && FontManager::instance()->getColorEmojiFont() != nullptr && - FontManager::instance()->getColorEmojiFont()->getType() == FontType::TTF ) { + if ( mFontService && !mIsColorEmojiFont && mFontService->getColorEmojiFont() != nullptr && + mFontService->getColorEmojiFont()->getType() == FontType::TTF ) { FontTrueType* fontEmoji = - static_cast( FontManager::instance()->getColorEmojiFont() ); + static_cast( mFontService->getColorEmojiFont() ); if ( ( idx = fontEmoji->getGlyphIndex( codePoint ) ) ) { if ( mIsMonospace && mEnableDynamicMonospace ) { mIsMonospaceComplete = false; @@ -662,11 +692,10 @@ Glyph FontTrueType::getGlyph( Uint32 codePoint, unsigned int characterSize, bool return fontEmoji->getGlyphByIndex( idx, characterSize, bold, italic, outlineThickness, getPage( characterSize ) ); } - } else if ( !mIsEmojiFont && FontManager::instance()->getEmojiFont() != nullptr && - FontManager::instance()->getEmojiFont()->getType() == FontType::TTF ) { + } else if ( mFontService && !mIsEmojiFont && mFontService->getEmojiFont() != nullptr && + mFontService->getEmojiFont()->getType() == FontType::TTF ) { - FontTrueType* fontEmoji = - static_cast( FontManager::instance()->getEmojiFont() ); + FontTrueType* fontEmoji = static_cast( mFontService->getEmojiFont() ); if ( ( idx = fontEmoji->getGlyphIndex( codePoint ) ) ) { if ( mIsMonospace && mEnableDynamicMonospace ) { mIsMonospaceComplete = false; @@ -678,27 +707,26 @@ Glyph FontTrueType::getGlyph( Uint32 codePoint, unsigned int characterSize, bool } } - if ( bold && italic && mFontBoldItalic != nullptr && + if ( bold && italic && mFontBoldItalic && ( idx = mFontBoldItalic->getGlyphIndex( codePoint ) ) ) { return mFontBoldItalic->getGlyphByIndex( idx, characterSize, true, true, outlineThickness, getPage( characterSize ) ); } - if ( bold && !italic && mFontBold != nullptr && - ( idx = mFontBold->getGlyphIndex( codePoint ) ) ) { + if ( bold && !italic && mFontBold && ( idx = mFontBold->getGlyphIndex( codePoint ) ) ) { return mFontBold->getGlyphByIndex( idx, characterSize, true, false, outlineThickness, getPage( characterSize ) ); } - if ( italic && !bold && mFontItalic != nullptr && - ( idx = mFontItalic->getGlyphIndex( codePoint ) ) ) { + if ( italic && !bold && mFontItalic && ( idx = mFontItalic->getGlyphIndex( codePoint ) ) ) { return mFontItalic->getGlyphByIndex( idx, characterSize, false, true, outlineThickness, getPage( characterSize ) ); } idx = getGlyphIndex( codePoint ); - if ( 0 == idx && mEnableFallbackFont && FontManager::instance()->hasFallbackFonts() ) { - for ( Font* fallbackFontPtr : FontManager::instance()->getFallbackFonts() ) { + if ( 0 == idx && mEnableFallbackFont && mFontService && mFontService->hasFallbackFonts() ) { + for ( const FontPtr& fallbackFontHandle : mFontService->getFallbackFonts() ) { + Font* fallbackFontPtr = fallbackFontHandle.get(); if ( fallbackFontPtr->getType() != FontType::TTF ) continue; FontTrueType* fallbackFont = static_cast( fallbackFontPtr ); @@ -718,7 +746,7 @@ Glyph FontTrueType::getGlyph( Uint32 codePoint, unsigned int characterSize, bool codePoint, FontWeight::Normal, false ); if ( !fallbackDesc.path.empty() ) { FontTrueType* systemFallback = - FontManager::instance()->getOrLoadSystemFallbackFont( fallbackDesc ); + mFontService ? mFontService->getOrLoadSystemFallbackFont( fallbackDesc ) : nullptr; if ( systemFallback && ( idx = systemFallback->getGlyphIndex( codePoint ) ) ) { if ( mIsMonospace && mEnableDynamicMonospace ) { mIsMonospaceComplete = false; @@ -791,10 +819,11 @@ GlyphDrawable* FontTrueType::getGlyphDrawable( Uint32 codePoint, unsigned int ch if ( mEnableEmojiFallback && Font::isEmojiCodePoint( codePoint ) && !mIsColorEmojiFont && !mIsEmojiFont ) { - if ( !mIsColorEmojiFont && FontManager::instance()->getColorEmojiFont() != nullptr && - FontManager::instance()->getColorEmojiFont()->getType() == FontType::TTF ) { + if ( mFontService && !mIsColorEmojiFont && + mFontService->getColorEmojiFont() != nullptr && + mFontService->getColorEmojiFont()->getType() == FontType::TTF ) { FontTrueType* fontEmoji = - static_cast( FontManager::instance()->getColorEmojiFont() ); + static_cast( mFontService->getColorEmojiFont() ); tGlyphIndex = fontEmoji->getGlyphIndex( codePoint ); if ( 0 != tGlyphIndex ) { glyphIndex = tGlyphIndex; @@ -802,10 +831,10 @@ GlyphDrawable* FontTrueType::getGlyphDrawable( Uint32 codePoint, unsigned int ch } else { glyphIndex = getGlyphIndex( codePoint ); } - } else if ( !mIsEmojiFont && FontManager::instance()->getEmojiFont() != nullptr && - FontManager::instance()->getEmojiFont()->getType() == FontType::TTF ) { + } else if ( mFontService && !mIsEmojiFont && mFontService->getEmojiFont() != nullptr && + mFontService->getEmojiFont()->getType() == FontType::TTF ) { FontTrueType* fontEmoji = - static_cast( FontManager::instance()->getEmojiFont() ); + static_cast( mFontService->getEmojiFont() ); tGlyphIndex = fontEmoji->getGlyphIndex( codePoint ); if ( 0 != tGlyphIndex ) { glyphIndex = tGlyphIndex; @@ -820,29 +849,30 @@ GlyphDrawable* FontTrueType::getGlyphDrawable( Uint32 codePoint, unsigned int ch glyphIndex = getGlyphIndex( codePoint ); } - if ( bold && italic && mFontBoldItalic != nullptr && + if ( bold && italic && mFontBoldItalic && ( tGlyphIndex = mFontBoldItalic->getGlyphIndex( codePoint ) ) ) { glyphIndex = tGlyphIndex; fontInternalId = mFontBoldItalic->getFontInternalId(); isItalic = true; } - if ( bold && !italic && mFontBold != nullptr && + if ( bold && !italic && mFontBold && ( tGlyphIndex = mFontBold->getGlyphIndex( codePoint ) ) ) { glyphIndex = tGlyphIndex; fontInternalId = mFontBold->getFontInternalId(); } - if ( italic && !bold && mFontItalic != nullptr && + if ( italic && !bold && mFontItalic && ( tGlyphIndex = mFontItalic->getGlyphIndex( codePoint ) ) ) { glyphIndex = tGlyphIndex; fontInternalId = mFontItalic->getFontInternalId(); isItalic = true; } - if ( 0 == glyphIndex && mEnableFallbackFont && - FontManager::instance()->hasFallbackFonts() ) { - for ( Font* fontFallbackPtr : FontManager::instance()->getFallbackFonts() ) { + if ( 0 == glyphIndex && mEnableFallbackFont && mFontService && + mFontService->hasFallbackFonts() ) { + for ( const FontPtr& fontFallbackHandle : mFontService->getFallbackFonts() ) { + Font* fontFallbackPtr = fontFallbackHandle.get(); if ( fontFallbackPtr->getType() != FontType::TTF ) continue; FontTrueType* fontFallback = static_cast( fontFallbackPtr ); @@ -866,7 +896,8 @@ GlyphDrawable* FontTrueType::getGlyphDrawable( Uint32 codePoint, unsigned int ch codePoint, FontWeight::Normal, false ); if ( !fallbackDesc.path.empty() ) { FontTrueType* systemFallback = - FontManager::instance()->getOrLoadSystemFallbackFont( fallbackDesc ); + mFontService ? mFontService->getOrLoadSystemFallbackFont( fallbackDesc ) + : nullptr; if ( systemFallback && ( tGlyphIndex = systemFallback->getGlyphIndex( codePoint ) ) ) { glyphIndex = tGlyphIndex; @@ -1135,7 +1166,7 @@ Float FontTrueType::getUnderlineThickness( unsigned int characterSize ) const { } } -Texture* FontTrueType::getTexture( unsigned int characterSize ) const { +const TexturePtr& FontTrueType::getTexture( unsigned int characterSize ) const { return getPage( characterSize ).texture; } @@ -1143,15 +1174,15 @@ bool FontTrueType::loaded() const { return NULL != mFace; } +FontService* FontTrueType::getFontService() const { + return mFontService; +} + void FontTrueType::cleanup() { sendEvent( Event::Unload ); - if ( FontManager::existsSingleton() && FontManager::instance()->getColorEmojiFont() == this ) - FontManager::instance()->setColorEmojiFont( nullptr ); - - disconnectBoldItalicFont(); - disconnectBoldFont(); - disconnectItalicFont(); + if ( mFontService && mFontService->getColorEmojiFont() == this ) + mFontService->setColorEmojiFont( nullptr ); mCallbacks.clear(); mNumCallBacks = 0; @@ -1202,16 +1233,14 @@ void FontTrueType::cleanup() { mIsBold = false; mIsItalic = false; mIsMonospaceCompletePending = false; - mFontBold = nullptr; - mFontItalic = nullptr; - mFontBoldItalic = nullptr; - mFontBoldCb = 0; - mFontItalicCb = 0; - mFontBoldItalicCb = 0; + mFontBold.reset(); + mFontItalic.reset(); + mFontBoldItalic.reset(); mPages.clear(); std::vector().swap( mPixelBuffer ); mKerningCache.clear(); mKerningGlyphCache.clear(); + mGlyphAdvanceCache.clear(); mCodePointIndexCache.clear(); mKeyCache.clear(); mClosestCharacterSize.clear(); @@ -1226,6 +1255,81 @@ static int fontSetLoadOptions( FontAntialiasing antialiasing, FontHinting hintin return load_target | hint; } +Float FontTrueType::getGlyphAdvance( Uint32 codePoint, unsigned int characterSize, bool bold, + bool italic, Float outlineThickness ) const { + Uint32 index = 0; + if ( mEnableEmojiFallback && !mIsColorEmojiFont && !mHasSvgGlyphs && !mHasColrGlyphs && + !mIsEmojiFont && mFontService && Font::isEmojiCodePoint( codePoint ) ) { + Font* emojiFont = mFontService->getColorEmojiFont(); + if ( !emojiFont ) + emojiFont = mFontService->getEmojiFont(); + if ( emojiFont && emojiFont->getType() == FontType::TTF ) { + auto emojiTrueType = static_cast( emojiFont ); + if ( emojiTrueType->getGlyphIndex( codePoint ) ) + return emojiTrueType->getGlyphAdvance( codePoint, characterSize, bold, italic, + outlineThickness ); + } + } + + if ( bold && italic ) { + if ( mFontBoldItalic && ( index = mFontBoldItalic->getGlyphIndex( codePoint ) ) ) + return mFontBoldItalic->getGlyphAdvance( codePoint, characterSize, true, true, + outlineThickness ); + } else if ( bold ) { + if ( mFontBold && ( index = mFontBold->getGlyphIndex( codePoint ) ) ) + return mFontBold->getGlyphAdvance( codePoint, characterSize, true, false, + outlineThickness ); + } else if ( italic ) { + if ( mFontItalic && ( index = mFontItalic->getGlyphIndex( codePoint ) ) ) + return mFontItalic->getGlyphAdvance( codePoint, characterSize, false, true, + outlineThickness ); + } + + const FontTrueType* advanceFont = this; + index = getGlyphIndex( codePoint ); + if ( index == 0 && mEnableFallbackFont && mFontService && mFontService->hasFallbackFonts() ) { + for ( const FontPtr& fallbackHandle : mFontService->getFallbackFonts() ) { + if ( !fallbackHandle || fallbackHandle->getType() != FontType::TTF ) + continue; + auto fallbackFont = static_cast( fallbackHandle.get() ); + if ( ( index = fallbackFont->getGlyphIndex( codePoint ) ) ) { + advanceFont = fallbackFont; + break; + } + } + } + if ( index == 0 && mEnableSystemFallback && mFontService && + SystemFontResolver::existsSingleton() ) { + FontDesc fallbackDesc = SystemFontResolver::instance()->getFallbackForCodepoint( + codePoint, FontWeight::Normal, false ); + FontTrueType* fallbackFont = + fallbackDesc.path.empty() ? nullptr + : mFontService->getOrLoadSystemFallbackFont( fallbackDesc ); + if ( fallbackFont && ( index = fallbackFont->getGlyphIndex( codePoint ) ) ) + advanceFont = fallbackFont; + } + + Uint64 key = getIndexKey( advanceFont->mFontInternalId, index, bold, italic, outlineThickness ); + auto& advances = advanceFont->mGlyphAdvanceCache[characterSize]; + auto advanceIt = advances.find( key ); + if ( advanceIt != advances.end() ) + return advanceIt->second; + FT_Face face = static_cast( advanceFont->mFace ); + if ( !face || !advanceFont->setCurrentSize( characterSize ) ) + return 0.f; + FT_Int32 flags = + fontSetLoadOptions( advanceFont->mAntialiasing, advanceFont->mHinting ) | FT_LOAD_COLOR; + if ( FT_Load_Glyph( face, index, flags ) != 0 ) + return 0.f; + + Float advance = + static_cast( face->glyph->metrics.horiAdvance ) / static_cast( 1 << 6 ); + if ( bold && !advanceFont->mIsBold && !advanceFont->mBoldAdvanceSameAsRegular ) + advance += 1.f; + advances[key] = advance; + return advance; +} + static constexpr FT_Render_Mode fontSetRenderOptions( FT_Library library, FontAntialiasing antialiasing, FontHinting hinting, @@ -1621,7 +1725,7 @@ Rect FontTrueType::findGlyphRect( Page& page, unsigned int width, unsigned int h // Make the texture 2 times bigger Image newImage; newImage.create( textureWidth * 2, textureHeight * 2, 4 ); - newImage.copyImage( page.texture ); + newImage.copyImage( page.texture.get() ); page.texture->replace( &newImage ); } else { @@ -1786,7 +1890,10 @@ bool FontTrueType::isFallbackFontEnabled() const { } void FontTrueType::setEnableFallbackFont( bool enableFallbackFont ) { - mEnableFallbackFont = enableFallbackFont; + if ( mEnableFallbackFont != enableFallbackFont ) { + mEnableFallbackFont = enableFallbackFont; + mGlyphAdvanceCache.clear(); + } } bool FontTrueType::isSystemFallbackEnabled() const { @@ -1794,7 +1901,10 @@ bool FontTrueType::isSystemFallbackEnabled() const { } void FontTrueType::setEnableSystemFallback( bool enableSystemFallback ) { - mEnableSystemFallback = enableSystemFallback; + if ( mEnableSystemFallback != enableSystemFallback ) { + mEnableSystemFallback = enableSystemFallback; + mGlyphAdvanceCache.clear(); + } } bool FontTrueType::isEmojiFallbackEnabled() const { @@ -1802,7 +1912,10 @@ bool FontTrueType::isEmojiFallbackEnabled() const { } void FontTrueType::setEnableEmojiFallback( bool enableEmojiFallback ) { - mEnableEmojiFallback = enableEmojiFallback; + if ( mEnableEmojiFallback != enableEmojiFallback ) { + mEnableEmojiFallback = enableEmojiFallback; + mGlyphAdvanceCache.clear(); + } } const Uint32& FontTrueType::getFontInternalId() const { @@ -1848,7 +1961,10 @@ bool FontTrueType::getBoldAdvanceSameAsRegular() const { } void FontTrueType::setBoldAdvanceSameAsRegular( bool boldAdvanceSameAsRegular ) { - mBoldAdvanceSameAsRegular = boldAdvanceSameAsRegular; + if ( mBoldAdvanceSameAsRegular != boldAdvanceSameAsRegular ) { + mBoldAdvanceSameAsRegular = boldAdvanceSameAsRegular; + mGlyphAdvanceCache.clear(); + } } void FontTrueType::updateMonospaceState() const { @@ -1858,96 +1974,40 @@ void FontTrueType::updateMonospaceState() const { return; } mIsMonospaceCompletePending = false; - if ( mIsMonospaceComplete && mFontBold != nullptr ) { + if ( mIsMonospaceComplete && mFontBold ) { mIsMonospaceComplete = mIsMonospaceComplete && mFontBold->isMonospace() && - getGlyph( ' ', 10, false, false ).advance == - mFontBold->getGlyph( ' ', 10, false, false ).advance; + getGlyphAdvance( ' ', 10 ) == mFontBold->getGlyphAdvance( ' ', 10 ); } - if ( mIsMonospaceComplete && mFontItalic != nullptr ) { - mIsMonospaceComplete = mIsMonospaceComplete && mFontItalic->isMonospace() && - getGlyph( ' ', 10, false, false ).advance == - mFontItalic->getGlyph( ' ', 10, false, false ).advance; + if ( mIsMonospaceComplete && mFontItalic ) { + mIsMonospaceComplete = + mIsMonospaceComplete && mFontItalic->isMonospace() && + getGlyphAdvance( ' ', 10 ) == mFontItalic->getGlyphAdvance( ' ', 10 ); } - if ( mIsMonospaceComplete && mFontBoldItalic != nullptr ) { - mIsMonospaceComplete = mIsMonospaceComplete && mFontBoldItalic->isMonospace() && - getGlyph( ' ', 10, false, false ).advance == - mFontBoldItalic->getGlyph( ' ', 10, false, false ).advance; + if ( mIsMonospaceComplete && mFontBoldItalic ) { + mIsMonospaceComplete = + mIsMonospaceComplete && mFontBoldItalic->isMonospace() && + getGlyphAdvance( ' ', 10 ) == mFontBoldItalic->getGlyphAdvance( ' ', 10 ); } } -void FontTrueType::setBoldFont( FontTrueType* fontBold ) { - if ( fontBold == mFontBold ) - return; - disconnectBoldFont(); +void FontTrueType::setBoldFont( const FontTrueTypePtr& fontBold ) { mFontBold = fontBold; - if ( mFontBold != nullptr ) { - mFontBoldCb = mFontBold->pushFontEventCallback( [this]( Uint32, Event event, Font* ) { - if ( event == Font::Event::Unload ) { - // Maybe we should recreate the page table - mFontBold = nullptr; - mFontBoldCb = 0; - } - } ); - } + mGlyphAdvanceCache.clear(); updateMonospaceState(); } -void FontTrueType::setItalicFont( FontTrueType* fontItalic ) { - if ( fontItalic == mFontItalic ) - return; - disconnectItalicFont(); +void FontTrueType::setItalicFont( const FontTrueTypePtr& fontItalic ) { mFontItalic = fontItalic; - if ( mFontItalic != nullptr ) { - mFontItalicCb = mFontItalic->pushFontEventCallback( [this]( Uint32, Event event, Font* ) { - if ( event == Font::Event::Unload ) { - // Maybe we should recreate the page table - mFontItalic = nullptr; - mFontItalicCb = 0; - } - } ); - } + mGlyphAdvanceCache.clear(); updateMonospaceState(); } -void FontTrueType::setBoldItalicFont( FontTrueType* fontBoldItalic ) { - if ( fontBoldItalic == mFontBoldItalic ) - return; - disconnectBoldItalicFont(); +void FontTrueType::setBoldItalicFont( const FontTrueTypePtr& fontBoldItalic ) { mFontBoldItalic = fontBoldItalic; - if ( mFontBoldItalic != nullptr ) { - mFontBoldItalicCb = - mFontBoldItalic->pushFontEventCallback( [this]( Uint32, Event event, Font* ) { - if ( event == Font::Event::Unload ) { - // Maybe we should recreate the page table - mFontBoldItalic = nullptr; - mFontBoldItalicCb = 0; - } - } ); - } + mGlyphAdvanceCache.clear(); updateMonospaceState(); } -void FontTrueType::disconnectBoldFont() { - if ( mFontBoldCb != 0 && mFontBold != nullptr ) - mFontBold->popFontEventCallback( mFontBoldCb ); - mFontBold = nullptr; - mFontBoldCb = 0; -} - -void FontTrueType::disconnectItalicFont() { - if ( mFontItalicCb != 0 && mFontItalic != nullptr ) - mFontItalic->popFontEventCallback( mFontItalicCb ); - mFontItalic = nullptr; - mFontItalicCb = 0; -} - -void FontTrueType::disconnectBoldItalicFont() { - if ( mFontBoldItalicCb != 0 && mFontBoldItalic != nullptr ) - mFontBoldItalic->popFontEventCallback( mFontBoldItalicCb ); - mFontBoldItalic = nullptr; - mFontBoldItalicCb = 0; -} - bool FontTrueType::hasSvgGlyphs() const { return mHasSvgGlyphs; } @@ -1958,7 +2018,7 @@ bool FontTrueType::hasColrGlyphs() const { FontTrueType::Page::Page( const Uint32 fontInternalId, const std::string& pageName, const FontTrueType* font ) : - texture( NULL ), fontInternalId( fontInternalId ), nextRow( 3 ), font( font ) { + texture(), fontInternalId( fontInternalId ), nextRow( 3 ), font( font ) { // Make sure that the texture is initialized by default Image image; image.create( 128, 128, 4 ); @@ -1979,9 +2039,6 @@ FontTrueType::Page::Page( const Uint32 fontInternalId, const std::string& pageNa FontTrueType::Page::~Page() { for ( auto drawable : drawables ) eeDelete( drawable.second ); - - if ( NULL != texture && TextureFactory::existsSingleton() ) - TextureFactory::instance()->remove( texture->getTextureId() ); } void FontTrueType::clearCache() { @@ -1991,6 +2048,7 @@ void FontTrueType::clearCache() { mKeyCache.clear(); mKerningCache.clear(); mKerningGlyphCache.clear(); + mGlyphAdvanceCache.clear(); Text::GlobalInvalidationId++; } diff --git a/src/eepp/graphics/framebuffer.cpp b/src/eepp/graphics/framebuffer.cpp index 369f3e61d..b28294c69 100644 --- a/src/eepp/graphics/framebuffer.cpp +++ b/src/eepp/graphics/framebuffer.cpp @@ -14,14 +14,15 @@ namespace EE { namespace Graphics { static std::vector sFBOActiveViews; -FrameBuffer* FrameBuffer::New( const Uint32& Width, const Uint32& Height, bool StencilBuffer, - bool DepthBuffer, bool useColorBuffer, const Uint32& channels, - EE::Window::Window* window ) { +FrameBufferUniquePtr FrameBuffer::New( const Uint32& Width, const Uint32& Height, + bool StencilBuffer, bool DepthBuffer, bool useColorBuffer, + const Uint32& channels, EE::Window::Window* window ) { if ( FrameBufferFBO::isSupported() ) - return eeNew( FrameBufferFBO, ( Width, Height, StencilBuffer, DepthBuffer, useColorBuffer, - channels, window ) ); + return FrameBufferUniquePtr( + eeNew( FrameBufferFBO, ( Width, Height, StencilBuffer, DepthBuffer, useColorBuffer, + channels, window ) ) ); Log::warning( "FBO not supported" ); - return NULL; + return {}; } FrameBuffer::FrameBuffer( EE::Window::Window* window ) : @@ -32,23 +33,21 @@ FrameBuffer::FrameBuffer( EE::Window::Window* window ) : mHasColorBuffer( false ), mHasDepthBuffer( false ), mHasStencilBuffer( false ), - mTexture( NULL ), + mTexture(), mClearColor( 0, 0, 0, 0 ) { if ( NULL == mWindow ) { mWindow = Engine::instance()->getCurrentWindow(); } - FrameBufferManager::instance()->add( this ); + FrameBufferRegistry::instance()->add( this ); } FrameBuffer::~FrameBuffer() { - if ( mTexture && TextureFactory::existsSingleton() ) - TextureFactory::instance()->remove( mTexture ); - - FrameBufferManager::instance()->remove( this ); + if ( FrameBufferRegistry::existsSingleton() ) + FrameBufferRegistry::instance()->remove( this ); } -Texture* FrameBuffer::getTexture() const { +const TexturePtr& FrameBuffer::getTexture() const { return mTexture; } diff --git a/src/eepp/graphics/framebufferfbo.cpp b/src/eepp/graphics/framebufferfbo.cpp index 3630877a5..dd8a7923f 100644 --- a/src/eepp/graphics/framebufferfbo.cpp +++ b/src/eepp/graphics/framebufferfbo.cpp @@ -163,11 +163,11 @@ bool FrameBufferFBO::create( const Uint32& Width, const Uint32& Height, bool Ste } else { if ( NULL == mTexture ) { - Texture* tex = TextureFactory::instance()->createEmptyTexture( Width, Height, channels, - Color::Transparent ); + TexturePtr texture = TextureFactory::instance()->createEmptyTexture( + Width, Height, channels, Color::Transparent ); - if ( tex ) { - mTexture = tex; + if ( texture ) { + mTexture = std::move( texture ); } else { Log::error( "FrameBufferFBO::create: failed to create texture" ); return false; diff --git a/src/eepp/graphics/framebuffermanager.cpp b/src/eepp/graphics/framebuffermanager.cpp index b1b7688a6..955a08899 100644 --- a/src/eepp/graphics/framebuffermanager.cpp +++ b/src/eepp/graphics/framebuffermanager.cpp @@ -3,13 +3,13 @@ namespace EE { namespace Graphics { namespace Private { -SINGLETON_DECLARE_IMPLEMENTATION( FrameBufferManager ) +SINGLETON_DECLARE_IMPLEMENTATION( FrameBufferRegistry ) -FrameBufferManager::FrameBufferManager() {} +FrameBufferRegistry::FrameBufferRegistry() {} -FrameBufferManager::~FrameBufferManager() {} +FrameBufferRegistry::~FrameBufferRegistry() {} -FrameBuffer* FrameBufferManager::getCurrentlyBound() { +FrameBuffer* FrameBufferRegistry::getCurrentlyBound() { int curFB; glGetIntegerv( GL_FRAMEBUFFER_BINDING, &curFB ); @@ -25,11 +25,11 @@ FrameBuffer* FrameBufferManager::getCurrentlyBound() { return NULL; } -FrameBuffer* FrameBufferManager::getFromName( const std::string& name ) { +FrameBuffer* FrameBufferRegistry::getFromName( const std::string& name ) { return getFromId( String::hash( name ) ); } -FrameBuffer* FrameBufferManager::getFromId( const String::HashType& id ) { +FrameBuffer* FrameBufferRegistry::getFromId( const String::HashType& id ) { for ( auto& fb : mResources ) { if ( fb->getId() == id ) { return fb; diff --git a/src/eepp/graphics/globaltextureatlas.cpp b/src/eepp/graphics/globaltextureatlas.cpp deleted file mode 100644 index 94f63ee7e..000000000 --- a/src/eepp/graphics/globaltextureatlas.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include - -namespace EE { namespace Graphics { - -SINGLETON_DECLARE_IMPLEMENTATION( GlobalTextureAtlas ) - -GlobalTextureAtlas::GlobalTextureAtlas() : TextureAtlas( "global" ) {} - -GlobalTextureAtlas::~GlobalTextureAtlas() {} - -}} // namespace EE::Graphics diff --git a/src/eepp/graphics/glyphdrawable.cpp b/src/eepp/graphics/glyphdrawable.cpp index d5fd2eb79..1ddea05de 100644 --- a/src/eepp/graphics/glyphdrawable.cpp +++ b/src/eepp/graphics/glyphdrawable.cpp @@ -5,15 +5,15 @@ namespace EE { namespace Graphics { -GlyphDrawable* GlyphDrawable::New( Texture* texture, const Rect& srcRect, const Sizef& destSize, +GlyphDrawable* GlyphDrawable::New( TexturePtr texture, const Rect& srcRect, const Sizef& destSize, const std::string& resourceName ) { - return eeNew( GlyphDrawable, ( texture, srcRect, destSize, resourceName ) ); + return eeNew( GlyphDrawable, ( std::move( texture ), srcRect, destSize, resourceName ) ); } -GlyphDrawable::GlyphDrawable( Texture* texture, const Rect& srcRect, const Sizef& destSize, +GlyphDrawable::GlyphDrawable( TexturePtr texture, const Rect& srcRect, const Sizef& destSize, const std::string& resourceName ) : DrawableResource( Drawable::GLYPH, resourceName ), - mTexture( texture ), + mTexture( std::move( texture ) ), mSrcRect( srcRect.asFloat() ), mDestSize( destSize ), mAdvance( destSize.getWidth() ) { @@ -80,7 +80,19 @@ bool GlyphDrawable::isStateful() { return false; } -Texture* GlyphDrawable::getTexture() { +DrawablePtr GlyphDrawable::clone() const { + auto instance = makeResource( mTexture, mSrcRect.asInt(), mDestSize, mName ); + instance->setPixelDensity( mPixelDensity ); + instance->setGlyphOffset( mGlyphOffset ); + instance->setDrawMode( mDrawMode ); + instance->setIsItalic( mIsItalic ); + instance->setAdvance( mAdvance ); + instance->setColor( mColor ); + instance->setPosition( mPosition ); + return instance; +} + +const TexturePtr& GlyphDrawable::getTexture() const { return mTexture; } diff --git a/src/eepp/graphics/image.cpp b/src/eepp/graphics/image.cpp index 51e902b06..400d9b83e 100644 --- a/src/eepp/graphics/image.cpp +++ b/src/eepp/graphics/image.cpp @@ -929,7 +929,7 @@ void Image::webpLoad( const Uint8* imageData, size_t imageDataSize ) { } if ( errdec || nullptr == dstImage ) { - eeSAFE_FREE( dstImage ); + free( dstImage ); return; } diff --git a/src/eepp/graphics/ninepatch.cpp b/src/eepp/graphics/ninepatch.cpp index e0136e7a7..54fd323ca 100644 --- a/src/eepp/graphics/ninepatch.cpp +++ b/src/eepp/graphics/ninepatch.cpp @@ -4,30 +4,28 @@ namespace EE { namespace Graphics { -NinePatch* NinePatch::New( ResourceId textureId, int left, int top, int right, int bottom, - const Float& pixelDensity, const std::string& name ) { - return eeNew( NinePatch, ( TextureFactory::instance()->getTexture( textureId ), left, top, - right, bottom, pixelDensity, name ) ); +NinePatchPtr NinePatch::New( ResourceId textureId, int left, int top, int right, int bottom, + const Float& pixelDensity, const std::string& name ) { + return makeResource( TextureFactory::instance()->getTexture( textureId ), left, top, + right, bottom, pixelDensity, name ); } -NinePatch* NinePatch::New( Texture* tex, int left, int top, int right, int bottom, - const Float& pixelDensity, const std::string& name ) { - return eeNew( NinePatch, ( tex, left, top, right, bottom, pixelDensity, name ) ); +NinePatchPtr NinePatch::New( TexturePtr tex, int left, int top, int right, int bottom, + const Float& pixelDensity, const std::string& name ) { + return makeResource( std::move( tex ), left, top, right, bottom, pixelDensity, + name ); } -NinePatch* NinePatch::New( TextureRegion* textureRegion, int left, int top, int right, int bottom, - const std::string& name ) { - return eeNew( NinePatch, ( textureRegion, left, top, right, bottom, name ) ); +NinePatchPtr NinePatch::New( TextureRegion* textureRegion, int left, int top, int right, int bottom, + const std::string& name ) { + return makeResource( textureRegion, left, top, right, bottom, name ); } -NinePatch::NinePatch( Texture* tex, int left, int top, int right, int bottom, +NinePatch::NinePatch( TexturePtr tex, int left, int top, int right, int bottom, const Float& pixelDensity, const std::string& name ) : DrawableResource( Drawable::NINEPATCH, name ), mRect( left, top, right, bottom ), mPixelDensity( pixelDensity ) { - for ( Int32 i = 0; i < SideCount; i++ ) - mDrawable[i] = NULL; - if ( NULL != tex ) { mSize = tex->getPixelsSize(); @@ -40,22 +38,17 @@ NinePatch::NinePatch( TextureRegion* textureRegion, int left, int top, int right DrawableResource( Drawable::NINEPATCH, name ), mRect( left, top, right, bottom ), mPixelDensity( 1 ) { - for ( Int32 i = 0; i < SideCount; i++ ) - mDrawable[i] = NULL; - - Texture* tex; - - if ( NULL != textureRegion && ( tex = textureRegion->getTexture() ) != NULL ) { + if ( NULL != textureRegion && textureRegion->getTexture() != NULL ) { mPixelDensity = textureRegion->getPixelDensity(); Rectf r( textureRegion->getSrcRect().asFloat() ); mSize = r.getSize(); - createFromTexture( tex, left, top, right, bottom ); + createFromTexture( textureRegion->getTexture(), left, top, right, bottom ); for ( int i = 0; i < SideCount; i++ ) { - TextureRegion* side = static_cast( mDrawable[i] ); + TextureRegion* side = mDrawable[i].get(); side->setPixelDensity( textureRegion->getPixelDensity() ); @@ -71,9 +64,27 @@ NinePatch::NinePatch( TextureRegion* textureRegion, int left, int top, int right } } -NinePatch::~NinePatch() { - for ( Int32 i = 0; i < SideCount; i++ ) - eeSAFE_DELETE( mDrawable[i] ); +NinePatch::~NinePatch() {} + +DrawablePtr NinePatch::clone() const { + if ( !mDrawable[Center] ) + return {}; + auto instance = makeResource( mDrawable[Center]->getTexture(), mRect.Left, mRect.Top, + mRect.Right, mRect.Bottom, mPixelDensity, mName ); + for ( int i = 0; i < SideCount; ++i ) { + instance->mDrawable[i] = + mDrawable[i] ? std::static_pointer_cast( mDrawable[i]->clone() ) + : TextureRegionPtr{}; + if ( mDrawable[i] && !instance->mDrawable[i] ) + return {}; + } + instance->mRect = mRect; + instance->mRectf = mRectf; + instance->mSize = mSize; + instance->mDestSize = mDestSize; + instance->setColor( mColor ); + instance->setPosition( mPosition ); + return instance; } Sizef NinePatch::getSize() { @@ -117,31 +128,32 @@ void NinePatch::draw( const Vector2f& position, const Sizef& size ) { TextureRegion* NinePatch::getTextureRegion( const int& side ) { if ( side < SideCount ) - return mDrawable[side]; + return mDrawable[side].get(); return NULL; } -void NinePatch::createFromTexture( Texture* tex, int left, int top, int right, int bottom ) { +void NinePatch::createFromTexture( const TexturePtr& tex, int left, int top, int right, + int bottom ) { Rect r; r = Rect( 0, top, left, mSize.getHeight() - bottom ); - mDrawable[Left] = TextureRegion::New( tex, r, r.getSize().asFloat() ); + mDrawable[Left] = makeResource( tex, r, r.getSize().asFloat() ); r = Rect( mSize.getWidth() - right, top, mSize.getWidth(), mSize.getHeight() - bottom ); - mDrawable[Right] = TextureRegion::New( tex, r, r.getSize().asFloat() ); + mDrawable[Right] = makeResource( tex, r, r.getSize().asFloat() ); r = Rect( left, mSize.getHeight() - bottom, mSize.getWidth() - right, mSize.getHeight() ); - mDrawable[Down] = TextureRegion::New( tex, r, r.getSize().asFloat() ); + mDrawable[Down] = makeResource( tex, r, r.getSize().asFloat() ); r = Rect( left, 0, mSize.getWidth() - right, top ); - mDrawable[Up] = TextureRegion::New( tex, r, r.getSize().asFloat() ); + mDrawable[Up] = makeResource( tex, r, r.getSize().asFloat() ); r = Rect( 0, 0, left, top ); - mDrawable[UpLeft] = TextureRegion::New( tex, r, r.getSize().asFloat() ); + mDrawable[UpLeft] = makeResource( tex, r, r.getSize().asFloat() ); r = Rect( mSize.getWidth() - right, 0, mSize.getWidth(), top ); - mDrawable[UpRight] = TextureRegion::New( tex, r, r.getSize().asFloat() ); + mDrawable[UpRight] = makeResource( tex, r, r.getSize().asFloat() ); r = Rect( 0, mSize.getHeight() - bottom, left, mSize.getHeight() ); - mDrawable[DownLeft] = TextureRegion::New( tex, r, r.getSize().asFloat() ); + mDrawable[DownLeft] = makeResource( tex, r, r.getSize().asFloat() ); r = Rect( mSize.getWidth() - right, mSize.getHeight() - bottom, mSize.getWidth(), mSize.getHeight() ); - mDrawable[DownRight] = TextureRegion::New( tex, r, r.getSize().asFloat() ); + mDrawable[DownRight] = makeResource( tex, r, r.getSize().asFloat() ); r = Rect( left, top, mSize.getWidth() - right, mSize.getHeight() - bottom ); - mDrawable[Center] = TextureRegion::New( tex, r, r.getSize().asFloat() ); + mDrawable[Center] = makeResource( tex, r, r.getSize().asFloat() ); mRect = Rect( left, top, right, bottom ); diff --git a/src/eepp/graphics/ninepatchmanager.cpp b/src/eepp/graphics/ninepatchmanager.cpp deleted file mode 100644 index 73aea7ecc..000000000 --- a/src/eepp/graphics/ninepatchmanager.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include - -namespace EE { namespace Graphics { - -SINGLETON_DECLARE_IMPLEMENTATION( NinePatchManager ) - -NinePatchManager::~NinePatchManager() {} - -}} // namespace EE::Graphics diff --git a/src/eepp/graphics/particlesystem.cpp b/src/eepp/graphics/particlesystem.cpp index c85da4698..7f531da6b 100644 --- a/src/eepp/graphics/particlesystem.cpp +++ b/src/eepp/graphics/particlesystem.cpp @@ -14,7 +14,7 @@ namespace EE { namespace Graphics { ParticleSystem::ParticleSystem() : mParticle( NULL ), mPCount( 0 ), - mTexture( 0 ), + mTexture(), mPLeft( 0 ), mLoops( 0 ), mEffect( ParticleEffect::Nofx ), @@ -324,7 +324,7 @@ void ParticleSystem::draw() { if ( mPointsSup ) { if ( NULL != mTexture ) { - const_cast( mTexture )->bind(); + mTexture->bind(); } else { GLi->disable( GL_TEXTURE_2D ); GLi->disableClientState( GL_TEXTURE_COORD_ARRAY ); diff --git a/src/eepp/graphics/primitivedrawable.cpp b/src/eepp/graphics/primitivedrawable.cpp index ec51382be..4cb0547ea 100644 --- a/src/eepp/graphics/primitivedrawable.cpp +++ b/src/eepp/graphics/primitivedrawable.cpp @@ -12,11 +12,9 @@ PrimitiveDrawable::PrimitiveDrawable( Type drawableType ) : mLineWidth( 1.f ), mNeedsUpdate( true ), mRecreateVertexBuffer( true ), - mVertexBuffer( NULL ) {} + mVertexBuffer( nullptr ) {} -PrimitiveDrawable::~PrimitiveDrawable() { - eeSAFE_DELETE( mVertexBuffer ); -} +PrimitiveDrawable::~PrimitiveDrawable() = default; void PrimitiveDrawable::draw( const Vector2f& position, const Sizef& ) { if ( mPosition != position ) { @@ -109,7 +107,7 @@ void PrimitiveDrawable::onPositionChange() { void PrimitiveDrawable::prepareVertexBuffer( const PrimitiveType& drawableType ) { if ( mRecreateVertexBuffer ) { - eeSAFE_DELETE( mVertexBuffer ); + mVertexBuffer.reset(); mVertexBuffer = VertexBuffer::NewVertexArray( VERTEX_FLAGS_PRIMITIVE, drawableType ); mRecreateVertexBuffer = false; } diff --git a/src/eepp/graphics/rectangledrawable.cpp b/src/eepp/graphics/rectangledrawable.cpp index 8a2e0e5f9..8ea6e6e03 100644 --- a/src/eepp/graphics/rectangledrawable.cpp +++ b/src/eepp/graphics/rectangledrawable.cpp @@ -29,6 +29,21 @@ RectangleDrawable::RectangleDrawable( const Vector2f& position, const Sizef& siz mPosition = position; } +DrawablePtr RectangleDrawable::clone() const { + auto instance = makeResource( mPosition, mSize ); + instance->mRotation = mRotation; + instance->mScale = mScale; + instance->mCorners = mCorners; + instance->mRectColors = mRectColors; + instance->mUsingRectColors = mUsingRectColors; + instance->mFillMode = mFillMode; + instance->mBlendMode = mBlendMode; + instance->mLineWidth = mLineWidth; + instance->mSmooth = mSmooth; + instance->mColor = mColor; + return instance; +} + Sizef RectangleDrawable::getSize() { return mSize; } diff --git a/src/eepp/graphics/renderer/renderergl3.cpp b/src/eepp/graphics/renderer/renderergl3.cpp index 3a681bb3b..9c082a64d 100644 --- a/src/eepp/graphics/renderer/renderergl3.cpp +++ b/src/eepp/graphics/renderer/renderergl3.cpp @@ -122,12 +122,12 @@ void RendererGL3::reloadShader( ShaderProgram* Shader ) { } void RendererGL3::setShader( const EEGL3_SHADERS& Shader ) { - setShader( mShaders[Shader] ); + setShader( mShaders[Shader].get() ); } void RendererGL3::setShader( ShaderProgram* Shader ) { if ( NULL == Shader ) { - Shader = mShaders[EEGL3_SHADER_BASE]; + Shader = mShaders[EEGL3_SHADER_BASE].get(); } if ( mCurShader == Shader ) { diff --git a/src/eepp/graphics/renderer/renderergl3cp.cpp b/src/eepp/graphics/renderer/renderergl3cp.cpp index 96244aa7e..af5bf5cf1 100644 --- a/src/eepp/graphics/renderer/renderergl3cp.cpp +++ b/src/eepp/graphics/renderer/renderergl3cp.cpp @@ -134,7 +134,7 @@ void RendererGL3CP::init() { clientActiveTexture( GL_TEXTURE0 ); - setShader( mShaders[EEGL3CP_SHADER_BASE] ); + setShader( mShaders[EEGL3CP_SHADER_BASE].get() ); mLoaded = true; } @@ -154,12 +154,12 @@ void RendererGL3CP::reloadShader( ShaderProgram* Shader ) { } void RendererGL3CP::setShader( const EEGL3CP_SHADERS& Shader ) { - setShader( mShaders[Shader] ); + setShader( mShaders[Shader].get() ); } void RendererGL3CP::setShader( ShaderProgram* Shader ) { if ( NULL == Shader ) { - Shader = mShaders[EEGL3CP_SHADER_BASE]; + Shader = mShaders[EEGL3CP_SHADER_BASE].get(); } if ( mCurShader == Shader ) { diff --git a/src/eepp/graphics/renderer/renderergles2.cpp b/src/eepp/graphics/renderer/renderergles2.cpp index aa7911a50..83de668b1 100644 --- a/src/eepp/graphics/renderer/renderergles2.cpp +++ b/src/eepp/graphics/renderer/renderergles2.cpp @@ -155,12 +155,12 @@ void RendererGLES2::reloadShader( ShaderProgram* Shader ) { } void RendererGLES2::setShader( const EEGLES2_SHADERS& Shader ) { - setShader( mShaders[Shader] ); + setShader( mShaders[Shader].get() ); } void RendererGLES2::checkLocalShader() { for ( Uint32 i = 0; i < EEGLES2_SHADERS_COUNT; i++ ) { - if ( mShaders[i] == mCurShader ) { + if ( mShaders[i].get() == mCurShader ) { mCurShaderLocal = true; return; } @@ -171,7 +171,7 @@ void RendererGLES2::checkLocalShader() { void RendererGLES2::setShader( ShaderProgram* Shader ) { if ( NULL == Shader ) { - Shader = mShaders[EEGLES2_SHADER_BASE]; + Shader = mShaders[EEGLES2_SHADER_BASE].get(); } if ( mCurShader == Shader ) { @@ -432,7 +432,7 @@ void RendererGLES2::texCoordPointer( int size, unsigned int type, int stride, co unsigned int /*allocate*/ ) { if ( mCurShaderLocal ) { if ( 1 == mTexActive ) { - if ( mCurShader == mShaders[EEGLES2_SHADER_PRIMITIVE] ) { + if ( mCurShader == mShaders[EEGLES2_SHADER_PRIMITIVE].get() ) { if ( mClippingEnabled ) { setShader( EEGLES2_SHADER_CLIPPED ); } else if ( mPointSpriteEnabled ) { diff --git a/src/eepp/graphics/resourcecatalog.cpp b/src/eepp/graphics/resourcecatalog.cpp new file mode 100644 index 000000000..92c083273 --- /dev/null +++ b/src/eepp/graphics/resourcecatalog.cpp @@ -0,0 +1,400 @@ +#include +#include + +using namespace EE::System; + +namespace EE { namespace Graphics { + +ResourceCatalogPtr ResourceCatalog::New() { + return ResourceCatalogPtr( eeNew( ResourceCatalog, () ), ResourceDeleter() ); +} + +void ResourceCatalog::publish( ResourceKey key, TexturePtr texture ) { + publish( key.value(), std::move( texture ) ); +} + +void ResourceCatalog::publish( std::string key, TexturePtr texture ) { + if ( key.empty() ) + return; + + if ( !texture ) { + erase( key ); + return; + } + + TexturePtr previous; + { + Lock lock( mMutex ); + auto it = mTextures.find( key ); + if ( it == mTextures.end() ) { + mTextures.emplace( std::move( key ), std::move( texture ) ); + return; + } + + previous = std::move( it->second ); + it->second = std::move( texture ); + } + + // A replaced handle may be the final owner. Release it without holding the catalog mutex. + previous.reset(); +} + +void ResourceCatalog::publishDrawable( ResourceKey key, DrawablePtr drawable ) { + publishDrawable( key.value(), std::move( drawable ) ); +} + +void ResourceCatalog::publishDrawable( std::string key, DrawablePtr drawable ) { + if ( key.empty() ) + return; + + if ( !drawable ) { + eraseDrawable( key ); + return; + } + + DrawablePtr previous; + ResourceNameHash hash = resourceNameHash( key ); + { + Lock lock( mMutex ); + auto it = mDrawables.find( key ); + if ( it == mDrawables.end() ) { + mDrawables.emplace( std::move( key ), drawable ); + mDrawablesByNameHash[hash] = drawable; + return; + } + + previous = std::move( it->second ); + it->second = drawable; + mDrawablesByNameHash[hash] = drawable; + } + + previous.reset(); +} + +void ResourceCatalog::publishAtlas( ResourceKey key, TextureAtlasPtr atlas ) { + publishAtlas( key.value(), std::move( atlas ) ); +} + +void ResourceCatalog::publishAtlas( std::string key, TextureAtlasPtr atlas ) { + if ( key.empty() ) + return; + + if ( !atlas ) { + eraseAtlas( key ); + return; + } + + TextureAtlasPtr previous; + { + Lock lock( mMutex ); + auto it = mAtlases.find( key ); + if ( it == mAtlases.end() ) { + mAtlases.emplace( std::move( key ), std::move( atlas ) ); + return; + } + previous = std::move( it->second ); + it->second = std::move( atlas ); + } + previous.reset(); +} + +void ResourceCatalog::publishFont( ResourceKey key, FontPtr font ) { + publishFont( key.value(), std::move( font ) ); +} + +void ResourceCatalog::publishFont( std::string key, FontPtr font ) { + if ( key.empty() ) + return; + if ( !font ) { + eraseFont( key ); + return; + } + + FontPtr previous; + ResourceNameHash hash = resourceNameHash( key ); + { + Lock lock( mMutex ); + auto it = mFonts.find( key ); + if ( it == mFonts.end() ) { + mFonts.emplace( std::move( key ), font ); + mFontsByNameHash[hash] = font; + return; + } + previous = std::move( it->second ); + it->second = font; + mFontsByNameHash[hash] = font; + } + previous.reset(); +} + +void ResourceCatalog::publishShaderProgram( ResourceKey key, ShaderProgramPtr program ) { + publishShaderProgram( key.value(), std::move( program ) ); +} + +void ResourceCatalog::publishShaderProgram( std::string key, ShaderProgramPtr program ) { + if ( key.empty() ) + return; + if ( !program ) { + eraseShaderProgram( key ); + return; + } + ShaderProgramPtr previous; + { + Lock lock( mMutex ); + auto it = mShaderPrograms.find( key ); + if ( it == mShaderPrograms.end() ) { + mShaderPrograms.emplace( std::move( key ), std::move( program ) ); + return; + } + previous = std::move( it->second ); + it->second = std::move( program ); + } + previous.reset(); +} + +TexturePtr ResourceCatalog::findTexture( const ResourceKey& key ) const { + return findTexture( key.value() ); +} + +TexturePtr ResourceCatalog::findTexture( const std::string& key ) const { + Lock lock( mMutex ); + auto it = mTextures.find( key ); + return it != mTextures.end() ? it->second : TexturePtr{}; +} + +DrawablePtr ResourceCatalog::findDrawable( const ResourceKey& key ) const { + return findDrawable( key.value() ); +} + +DrawablePtr ResourceCatalog::findDrawable( const std::string& key ) const { + Lock lock( mMutex ); + auto it = mDrawables.find( key ); + return it != mDrawables.end() ? it->second : DrawablePtr{}; +} + +DrawablePtr ResourceCatalog::findDrawable( ResourceNameHash hash ) const { + Lock lock( mMutex ); + auto it = mDrawablesByNameHash.find( hash ); + return it != mDrawablesByNameHash.end() ? it->second.lock() : DrawablePtr{}; +} + +DrawablePtr ResourceCatalog::findDrawable( String::HashType legacyHash ) const { + Lock lock( mMutex ); + for ( const auto& [key, drawable] : mDrawables ) { + if ( String::hash( key ) == legacyHash ) + return drawable; + } + return {}; +} + +TextureAtlasPtr ResourceCatalog::findAtlas( const ResourceKey& key ) const { + return findAtlas( key.value() ); +} + +TextureAtlasPtr ResourceCatalog::findAtlas( const std::string& key ) const { + Lock lock( mMutex ); + auto it = mAtlases.find( key ); + return it != mAtlases.end() ? it->second : TextureAtlasPtr{}; +} + +std::vector ResourceCatalog::getAtlases() const { + std::vector atlases; + Lock lock( mMutex ); + atlases.reserve( mAtlases.size() ); + for ( const auto& atlas : mAtlases ) + atlases.emplace_back( atlas.second ); + return atlases; +} + +FontPtr ResourceCatalog::findFont( const ResourceKey& key ) const { + return findFont( key.value() ); +} + +FontPtr ResourceCatalog::findFont( const std::string& key ) const { + Lock lock( mMutex ); + auto it = mFonts.find( key ); + return it != mFonts.end() ? it->second : FontPtr{}; +} + +FontPtr ResourceCatalog::findFont( ResourceNameHash hash ) const { + Lock lock( mMutex ); + auto it = mFontsByNameHash.find( hash ); + return it != mFontsByNameHash.end() ? it->second.lock() : FontPtr{}; +} + +std::vector ResourceCatalog::getFonts() const { + std::vector fonts; + Lock lock( mMutex ); + fonts.reserve( mFonts.size() ); + for ( const auto& font : mFonts ) + fonts.emplace_back( font.second ); + return fonts; +} + +ShaderProgramPtr ResourceCatalog::findShaderProgram( const ResourceKey& key ) const { + return findShaderProgram( key.value() ); +} + +ShaderProgramPtr ResourceCatalog::findShaderProgram( const std::string& key ) const { + Lock lock( mMutex ); + auto it = mShaderPrograms.find( key ); + return it != mShaderPrograms.end() ? it->second : ShaderProgramPtr{}; +} + +std::vector ResourceCatalog::getShaderPrograms() const { + std::vector programs; + Lock lock( mMutex ); + programs.reserve( mShaderPrograms.size() ); + for ( const auto& program : mShaderPrograms ) + programs.emplace_back( program.second ); + return programs; +} + +bool ResourceCatalog::erase( const ResourceKey& key ) { + return erase( key.value() ); +} + +bool ResourceCatalog::erase( const std::string& key ) { + TexturePtr texture; + { + Lock lock( mMutex ); + auto it = mTextures.find( key ); + if ( it == mTextures.end() ) + return false; + + texture = std::move( it->second ); + mTextures.erase( it ); + } + + texture.reset(); + return true; +} + +bool ResourceCatalog::eraseDrawable( const ResourceKey& key ) { + return eraseDrawable( key.value() ); +} + +bool ResourceCatalog::eraseDrawable( const std::string& key ) { + DrawablePtr drawable; + { + Lock lock( mMutex ); + auto it = mDrawables.find( key ); + if ( it == mDrawables.end() ) + return false; + + drawable = std::move( it->second ); + mDrawables.erase( it ); + mDrawablesByNameHash.erase( resourceNameHash( key ) ); + } + + drawable.reset(); + return true; +} + +bool ResourceCatalog::eraseAtlas( const ResourceKey& key ) { + return eraseAtlas( key.value() ); +} + +bool ResourceCatalog::eraseAtlas( const std::string& key ) { + TextureAtlasPtr atlas; + { + Lock lock( mMutex ); + auto it = mAtlases.find( key ); + if ( it == mAtlases.end() ) + return false; + atlas = std::move( it->second ); + mAtlases.erase( it ); + } + atlas.reset(); + return true; +} + +bool ResourceCatalog::eraseFont( const ResourceKey& key ) { + return eraseFont( key.value() ); +} + +bool ResourceCatalog::eraseFont( const std::string& key ) { + FontPtr font; + { + Lock lock( mMutex ); + auto it = mFonts.find( key ); + if ( it == mFonts.end() ) + return false; + font = std::move( it->second ); + mFonts.erase( it ); + mFontsByNameHash.erase( resourceNameHash( key ) ); + } + font.reset(); + return true; +} + +bool ResourceCatalog::eraseFont( Font* font ) { + if ( !font ) + return false; + FontPtr removed; + { + Lock lock( mMutex ); + auto it = mFonts.find( font->getName() ); + if ( it == mFonts.end() || it->second.get() != font ) + return false; + removed = std::move( it->second ); + mFonts.erase( it ); + mFontsByNameHash.erase( resourceNameHash( font->getName() ) ); + } + removed.reset(); + return true; +} + +bool ResourceCatalog::eraseShaderProgram( const ResourceKey& key ) { + return eraseShaderProgram( key.value() ); +} + +bool ResourceCatalog::eraseShaderProgram( const std::string& key ) { + ShaderProgramPtr program; + { + Lock lock( mMutex ); + auto it = mShaderPrograms.find( key ); + if ( it == mShaderPrograms.end() ) + return false; + program = std::move( it->second ); + mShaderPrograms.erase( it ); + } + program.reset(); + return true; +} + +void ResourceCatalog::clear() { + UnorderedMap textures; + UnorderedMap drawables; + UnorderedMap drawablesByNameHash; + UnorderedMap atlases; + UnorderedMap fonts; + UnorderedMap fontsByNameHash; + UnorderedMap shaderPrograms; + { + Lock lock( mMutex ); + textures = std::move( mTextures ); + drawables = std::move( mDrawables ); + drawablesByNameHash = std::move( mDrawablesByNameHash ); + atlases = std::move( mAtlases ); + fonts = std::move( mFonts ); + fontsByNameHash = std::move( mFontsByNameHash ); + shaderPrograms = std::move( mShaderPrograms ); + } + + textures.clear(); + drawables.clear(); + drawablesByNameHash.clear(); + atlases.clear(); + fonts.clear(); + fontsByNameHash.clear(); + shaderPrograms.clear(); +} + +std::size_t ResourceCatalog::size() const { + Lock lock( mMutex ); + return mTextures.size() + mDrawables.size() + mAtlases.size() + mFonts.size() + + mShaderPrograms.size(); +} + +}} // namespace EE::Graphics diff --git a/src/eepp/graphics/resourcescope.cpp b/src/eepp/graphics/resourcescope.cpp new file mode 100644 index 000000000..282007f04 --- /dev/null +++ b/src/eepp/graphics/resourcescope.cpp @@ -0,0 +1,472 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace EE::System; +using namespace EE::Window; + +namespace EE { namespace Graphics { + +ResourceScopePtr ResourceScope::New() { + return ResourceScopePtr( eeNew( ResourceScope, () ), ResourceDeleter() ); +} + +ResourceScope::ResourceScope() : mLocalCatalog( ResourceCatalog::New() ), mFontService( *this ) {} + +ResourceScope::~ResourceScope() { + for ( const FontPtr& font : mLocalCatalog->getFonts() ) + detachFontService( font ); +} + +void ResourceScope::attachFontService( const FontPtr& font ) { + // FontTrueType currently carries one borrowed service pointer. Catalog imports deliberately do + // not call this function; sharing must preserve the service of the font's owning local scope. + // Moving fallback resolution out of FontTrueType would allow true multi-scope local + // publication. + if ( font && font->getType() == FontType::TTF ) + static_cast( font.get() )->setFontService( &mFontService ); +} + +void ResourceScope::detachFontService( const FontPtr& font ) { + if ( font ) { + mFontService.onFontRemoved( font.get() ); + if ( font->getType() != FontType::TTF ) + return; + auto* ttf = static_cast( font.get() ); + if ( ttf->getFontService() == &mFontService ) { + ttf->setFontService( nullptr ); + } + } +} + +FontService& ResourceScope::getFontService() { + return mFontService; +} + +const FontService& ResourceScope::getFontService() const { + return mFontService; +} + +TexturePtr ResourceScope::findTexture( const ResourceKey& key ) const { + return findTexture( key.value() ); +} + +TexturePtr ResourceScope::findTexture( const std::string& key ) const { + if ( TexturePtr texture = mLocalCatalog->findTexture( key ) ) + return texture; + + Lock lock( mMutex ); + for ( const ResourceCatalogPtr& catalog : mImports ) { + if ( TexturePtr texture = catalog->findTexture( key ) ) + return texture; + } + return {}; +} + +DrawablePtr ResourceScope::findDrawableSource( const ResourceKey& key ) const { + return findDrawableSource( key.value() ); +} + +DrawablePtr ResourceScope::findDrawableSource( const std::string& key ) const { + if ( DrawablePtr drawable = mLocalCatalog->findDrawable( key ) ) + return drawable; + + Lock lock( mMutex ); + for ( const ResourceCatalogPtr& catalog : mImports ) { + if ( DrawablePtr drawable = catalog->findDrawable( key ) ) + return drawable; + } + return {}; +} + +TextureAtlasPtr ResourceScope::findAtlas( const ResourceKey& key ) const { + return findAtlas( key.value() ); +} + +TextureAtlasPtr ResourceScope::findAtlas( const std::string& key ) const { + if ( TextureAtlasPtr atlas = mLocalCatalog->findAtlas( key ) ) + return atlas; + + Lock lock( mMutex ); + for ( const ResourceCatalogPtr& catalog : mImports ) { + if ( TextureAtlasPtr atlas = catalog->findAtlas( key ) ) + return atlas; + } + return {}; +} + +std::vector ResourceScope::getAtlases() const { + std::vector atlases = mLocalCatalog->getAtlases(); + Lock lock( mMutex ); + for ( const ResourceCatalogPtr& catalog : mImports ) { + std::vector imported = catalog->getAtlases(); + atlases.insert( atlases.end(), imported.begin(), imported.end() ); + } + return atlases; +} + +FontPtr ResourceScope::findFont( const ResourceKey& key ) const { + return findFont( key.value() ); +} + +FontPtr ResourceScope::findFont( const std::string& key ) const { + if ( FontPtr font = mLocalCatalog->findFont( key ) ) + return font; + Lock lock( mMutex ); + for ( const ResourceCatalogPtr& catalog : mImports ) { + if ( FontPtr font = catalog->findFont( key ) ) + return font; + } + return {}; +} + +FontPtr ResourceScope::findFont( ResourceNameHash hash ) const { + if ( FontPtr font = mLocalCatalog->findFont( hash ) ) + return font; + Lock lock( mMutex ); + for ( const ResourceCatalogPtr& catalog : mImports ) { + if ( FontPtr font = catalog->findFont( hash ) ) + return font; + } + return {}; +} + +std::vector ResourceScope::getFonts() const { + std::vector fonts = mLocalCatalog->getFonts(); + Lock lock( mMutex ); + for ( const ResourceCatalogPtr& catalog : mImports ) { + std::vector imported = catalog->getFonts(); + fonts.insert( fonts.end(), imported.begin(), imported.end() ); + } + return fonts; +} + +ShaderProgramPtr ResourceScope::findShaderProgram( const ResourceKey& key ) const { + return findShaderProgram( key.value() ); +} + +ShaderProgramPtr ResourceScope::findShaderProgram( const std::string& key ) const { + if ( ShaderProgramPtr program = mLocalCatalog->findShaderProgram( key ) ) + return program; + Lock lock( mMutex ); + for ( const ResourceCatalogPtr& catalog : mImports ) { + if ( ShaderProgramPtr program = catalog->findShaderProgram( key ) ) + return program; + } + return {}; +} + +std::vector ResourceScope::getShaderPrograms() const { + std::vector programs = mLocalCatalog->getShaderPrograms(); + Lock lock( mMutex ); + for ( const ResourceCatalogPtr& catalog : mImports ) { + auto imported = catalog->getShaderPrograms(); + programs.insert( programs.end(), imported.begin(), imported.end() ); + } + return programs; +} + +std::vector +ResourceScope::findTextureRegionsByPattern( const std::string& name, const std::string& extension, + TextureAtlas* searchInTextureAtlas ) const { + std::vector regions; + std::string suffix = extension.empty() ? "" : "." + extension; + int padding = 0; + + auto findRegion = [&]( const std::string& key ) -> TextureRegionPtr { + DrawablePtr drawable = searchInTextureAtlas ? searchInTextureAtlas->getByName( key ) + : findDrawableSource( key ); + return drawable && drawable->getDrawableType() == Drawable::TEXTUREREGION + ? std::static_pointer_cast( drawable ) + : TextureRegionPtr{}; + }; + + for ( int len = 1; len < 7 && padding == 0; ++len ) { + for ( int i = 0; i < 2; ++i ) { + std::string format( "%s%0" + String::toString( len ) + "d%s" ); + if ( findRegion( String::format( format.c_str(), name.c_str(), i, suffix.c_str() ) ) ) { + padding = len; + break; + } + } + } + + if ( padding == 0 ) + return regions; + + for ( int i = 0;; ++i ) { + std::string format( "%s%0" + String::toString( padding ) + "d%s" ); + TextureRegionPtr region = + findRegion( String::format( format.c_str(), name.c_str(), i, suffix.c_str() ) ); + if ( region ) { + regions.emplace_back( std::move( region ) ); + } else if ( i != 0 ) { + break; + } + } + return regions; +} + +std::vector +ResourceScope::findTextureRegionsByPatternId( const String::HashType& id, + const std::string& extension, + TextureAtlas* searchInTextureAtlas ) const { + DrawablePtr drawable; + if ( searchInTextureAtlas ) { + drawable = searchInTextureAtlas->getById( id ); + } else { + drawable = mLocalCatalog->findDrawable( id ); + if ( !drawable ) { + Lock lock( mMutex ); + for ( const ResourceCatalogPtr& catalog : mImports ) { + if ( ( drawable = catalog->findDrawable( id ) ) ) + break; + } + } + } + + if ( !drawable || drawable->getDrawableType() != Drawable::TEXTUREREGION ) + return {}; + std::string name = String::removeNumbersAtEnd( FileSystem::fileRemoveExtension( + static_cast( drawable.get() )->getName() ) ); + return findTextureRegionsByPattern( name, extension, searchInTextureAtlas ); +} + +DrawablePtr ResourceScope::findDrawable( const std::string& name, bool firstSearchSprite ) const { + if ( name.empty() ) + return {}; + + auto findSprite = [this]( const std::string& pattern ) -> DrawablePtr { + std::vector textureRegions = findTextureRegionsByPattern( pattern ); + if ( textureRegions.empty() ) + return {}; + SpritePtr sprite = Sprite::New(); + sprite->createAnimation(); + for ( const TextureRegionPtr& textureRegion : textureRegions ) + sprite->addFrame( textureRegion.get() ); + return sprite; + }; + + bool searchedSprite = false; + if ( firstSearchSprite ) { + DrawablePtr sprite = + findSprite( String::startsWith( name, "@sprite/" ) ? name.substr( 8 ) : name ); + if ( sprite ) + return sprite; + searchedSprite = true; + } + + if ( name[0] == '@' ) { + if ( String::startsWith( name, "@textureregion/" ) ) { + DrawablePtr source = findDrawableSource( name.substr( 12 ) ); + return source ? source->clone() : DrawablePtr{}; + } + if ( String::startsWith( name, "@image/" ) ) { + TexturePtr texture = findTexture( name.substr( 7 ) ); + return texture ? texture->clone() : DrawablePtr{}; + } + if ( String::startsWith( name, "@texture/" ) ) { + TexturePtr texture = findTexture( name.substr( 9 ) ); + return texture ? texture->clone() : DrawablePtr{}; + } + if ( String::startsWith( name, "@sprite/" ) && !searchedSprite ) + return findSprite( name.substr( 8 ) ); + if ( String::startsWith( name, "@drawable/" ) ) + return findDrawable( name.substr( 10 ) ); + if ( String::startsWith( name, "@9p/" ) ) { + DrawablePtr source = findDrawableSource( name.substr( 4 ) ); + return source && source->getDrawableType() == Drawable::NINEPATCH ? source->clone() + : DrawablePtr{}; + } + } + + if ( DrawablePtr source = findDrawableSource( name ) ) + return source->clone(); + + TexturePtr texture = findTexture( name ); + return texture ? texture->clone() : DrawablePtr{}; +} + +DrawablePtr ResourceScope::findDrawable( ResourceNameHash hash ) const { + DrawablePtr source = mLocalCatalog->findDrawable( hash ); + if ( !source ) { + Lock lock( mMutex ); + for ( const ResourceCatalogPtr& catalog : mImports ) { + if ( ( source = catalog->findDrawable( hash ) ) ) + break; + } + } + return source ? source->clone() : DrawablePtr{}; +} + +DrawablePtr ResourceScope::findDrawable( String::HashType legacyHash ) const { + DrawablePtr source = mLocalCatalog->findDrawable( legacyHash ); + if ( !source ) { + Lock lock( mMutex ); + for ( const ResourceCatalogPtr& catalog : mImports ) { + if ( ( source = catalog->findDrawable( legacyHash ) ) ) + break; + } + } + return source ? source->clone() : DrawablePtr{}; +} + +void ResourceScope::publishLocal( ResourceKey key, TexturePtr texture ) { + publishLocal( key.value(), std::move( texture ) ); +} + +void ResourceScope::publishLocal( std::string key, TexturePtr texture ) { + mLocalCatalog->publish( std::move( key ), std::move( texture ) ); +} + +void ResourceScope::publishLocalDrawable( ResourceKey key, DrawablePtr drawable ) { + publishLocalDrawable( key.value(), std::move( drawable ) ); +} + +void ResourceScope::publishLocalDrawable( std::string key, DrawablePtr drawable ) { + mLocalCatalog->publishDrawable( std::move( key ), std::move( drawable ) ); +} + +void ResourceScope::publishLocalAtlas( ResourceKey key, TextureAtlasPtr atlas ) { + publishLocalAtlas( key.value(), std::move( atlas ) ); +} + +void ResourceScope::publishLocalAtlas( std::string key, TextureAtlasPtr atlas ) { + mLocalCatalog->publishAtlas( std::move( key ), std::move( atlas ) ); +} + +void ResourceScope::publishLocalFont( ResourceKey key, FontPtr font ) { + publishLocalFont( key.value(), std::move( font ) ); +} + +void ResourceScope::publishLocalFont( std::string key, FontPtr font ) { + FontPtr replaced = mLocalCatalog->findFont( key ); + if ( replaced && replaced != font ) + detachFontService( replaced ); + attachFontService( font ); + mLocalCatalog->publishFont( std::move( key ), std::move( font ) ); +} + +void ResourceScope::publishLocalShaderProgram( ResourceKey key, ShaderProgramPtr program ) { + publishLocalShaderProgram( key.value(), std::move( program ) ); +} + +void ResourceScope::publishLocalShaderProgram( std::string key, ShaderProgramPtr program ) { + mLocalCatalog->publishShaderProgram( std::move( key ), std::move( program ) ); +} + +bool ResourceScope::eraseLocal( const ResourceKey& key ) { + return mLocalCatalog->erase( key ); +} + +bool ResourceScope::eraseLocal( const std::string& key ) { + return mLocalCatalog->erase( key ); +} + +bool ResourceScope::eraseLocalDrawable( const ResourceKey& key ) { + return mLocalCatalog->eraseDrawable( key ); +} + +bool ResourceScope::eraseLocalDrawable( const std::string& key ) { + return mLocalCatalog->eraseDrawable( key ); +} + +bool ResourceScope::eraseLocalAtlas( const ResourceKey& key ) { + return mLocalCatalog->eraseAtlas( key ); +} + +bool ResourceScope::eraseLocalAtlas( const std::string& key ) { + return mLocalCatalog->eraseAtlas( key ); +} + +bool ResourceScope::eraseLocalFont( const ResourceKey& key ) { + return eraseLocalFont( key.value() ); +} + +bool ResourceScope::eraseLocalFont( const std::string& key ) { + FontPtr font = mLocalCatalog->findFont( key ); + if ( !font ) + return false; + detachFontService( font ); + return mLocalCatalog->eraseFont( key ); +} + +bool ResourceScope::eraseLocalFont( Font* font ) { + if ( !font ) + return false; + FontPtr handle = mLocalCatalog->findFont( font->getName() ); + if ( handle.get() != font ) + return false; + if ( !mLocalCatalog->eraseFont( font ) ) + return false; + detachFontService( handle ); + return true; +} + +bool ResourceScope::eraseLocalShaderProgram( const ResourceKey& key ) { + return mLocalCatalog->eraseShaderProgram( key ); +} + +bool ResourceScope::eraseLocalShaderProgram( const std::string& key ) { + return mLocalCatalog->eraseShaderProgram( key ); +} + +void ResourceScope::clearLocal() { + for ( const FontPtr& font : mLocalCatalog->getFonts() ) + detachFontService( font ); + mLocalCatalog->clear(); +} + +void ResourceScope::importCatalog( ResourceCatalogPtr catalog ) { + if ( !catalog || catalog == mLocalCatalog ) + return; + + Lock lock( mMutex ); + if ( std::find( mImports.begin(), mImports.end(), catalog ) == mImports.end() ) + mImports.emplace_back( std::move( catalog ) ); +} + +bool ResourceScope::removeCatalog( const ResourceCatalogPtr& catalog ) { + ResourceCatalogPtr removed; + { + Lock lock( mMutex ); + auto it = std::find( mImports.begin(), mImports.end(), catalog ); + if ( it == mImports.end() ) + return false; + + removed = std::move( *it ); + mImports.erase( it ); + } + + removed.reset(); + return true; +} + +void ResourceScope::clearImports() { + std::vector imports; + { + Lock lock( mMutex ); + imports = std::move( mImports ); + } + + imports.clear(); +} + +ResourceCatalogPtr ResourceScope::getLocalCatalog() const { + return mLocalCatalog; +} + +ResourceCatalog& globalResourceCatalog() { + return *Engine::instance()->getGlobalResourceCatalog(); +} + +ResourceScope& defaultResourceScope() { + return *Engine::instance()->getDefaultResourceScope(); +} + +}} // namespace EE::Graphics diff --git a/src/eepp/graphics/richtext.cpp b/src/eepp/graphics/richtext.cpp index f9f81c3ee..6fc0c5740 100644 --- a/src/eepp/graphics/richtext.cpp +++ b/src/eepp/graphics/richtext.cpp @@ -1,8 +1,8 @@ #include #include -#include #include #include +#include #include namespace EE { namespace Graphics { diff --git a/src/eepp/graphics/scrollparallax.cpp b/src/eepp/graphics/scrollparallax.cpp index 16ebc652e..3535b80a9 100644 --- a/src/eepp/graphics/scrollparallax.cpp +++ b/src/eepp/graphics/scrollparallax.cpp @@ -15,11 +15,13 @@ ScrollParallax::ScrollParallax( TextureRegion* textureRegion, const Vector2f& Po } TextureRegion* ScrollParallax::getTextureRegion() const { - return mTextureRegion; + return mTextureRegion.get(); } void ScrollParallax::setTextureRegion( TextureRegion* textureRegion ) { - mTextureRegion = textureRegion; + mTextureRegion = + textureRegion ? std::static_pointer_cast( textureRegion->clone() ) + : TextureRegionPtr{}; setTextureRegion(); } @@ -43,7 +45,9 @@ void ScrollParallax::setAABB() { bool ScrollParallax::create( TextureRegion* textureRegion, const Vector2f& Position, const Sizef& Size, const Vector2f& Speed, const Color& Color, const BlendMode& Blend ) { - mTextureRegion = textureRegion; + mTextureRegion = + textureRegion ? std::static_pointer_cast( textureRegion->clone() ) + : TextureRegionPtr{}; mPos = Position; mSize = Size; mInitPos = mPos; @@ -97,8 +101,8 @@ void ScrollParallax::draw() { Vector2f Pos = mPos; - Pos.x = ( Float )(Int32)Pos.x; - Pos.y = ( Float )(Int32)Pos.y; + Pos.x = (Float)(Int32)Pos.x; + Pos.y = (Float)(Int32)Pos.y; if ( mSpeed.x > 0.f ) Pos.x -= mRealSize.getWidth(); @@ -111,53 +115,52 @@ void ScrollParallax::draw() { for ( Int32 y = -1; y < mTiles.y; y++ ) { for ( Int32 x = -1; x < mTiles.x; x++ ) { - Rect Rect = mRect; + Rect rect = mRect; Rectf AABB( Pos.x, Pos.y, Pos.x + mRealSize.getWidth(), Pos.y + mRealSize.getHeight() ); if ( AABB.intersect( mAABB ) ) { if ( Pos.x < mAABB.Left ) { - Rect.Left += ( Int32 )( ( mAABB.Left - Pos.x ) * pd ); + rect.Left += (Int32)( ( mAABB.Left - Pos.x ) * pd ); AABB.Left = mAABB.Left; } if ( Pos.x + mRealSize.getWidth() > mAABB.Right ) { - Rect.Right -= - ( Int32 )( ( ( Pos.x + mRealSize.getWidth() ) - mAABB.Right ) * pd ); + rect.Right -= + (Int32)( ( ( Pos.x + mRealSize.getWidth() ) - mAABB.Right ) * pd ); } if ( Pos.y < mAABB.Top ) { - Rect.Top += ( Int32 )( ( mAABB.Top - Pos.y ) * pd ); + rect.Top += (Int32)( ( mAABB.Top - Pos.y ) * pd ); AABB.Top = mAABB.Top; } if ( Pos.y + mRealSize.getHeight() > mAABB.Bottom ) { - Rect.Bottom -= - ( Int32 )( ( ( Pos.y + mRealSize.getHeight() ) - mAABB.Bottom ) * pd ); + rect.Bottom -= + (Int32)( ( ( Pos.y + mRealSize.getHeight() ) - mAABB.Bottom ) * pd ); } - mTextureRegion->setSrcRect( Rect ); - mTextureRegion->setDestSize( - Vector2f( Rect.getSize().x * ps, Rect.getSize().y * ps ) ); - - if ( !( Rect.Right == 0 || Rect.Bottom == 0 ) ) - mTextureRegion->draw( AABB.Left, AABB.Top, mColor, 0.f, Vector2f::One, - mBlend ); + const TexturePtr& texture = mTextureRegion->getTexture(); + if ( texture && !( rect.Right == 0 || rect.Bottom == 0 ) ) { + const Vector2i& offset = mTextureRegion->getOffset(); + texture->drawEx( AABB.Left + offset.x, AABB.Top + offset.y, + rect.getSize().x * ps, rect.getSize().y * ps, 0.f, + Vector2f::One, mColor, mColor, mColor, mColor, mBlend, + RENDER_NORMAL, OriginPoint( OriginPoint::OriginCenter ), + rect ); + } } Pos.x += mRealSize.getWidth(); } - Pos.x = ( Float )(Int32)mPos.x; + Pos.x = (Float)(Int32)mPos.x; if ( mSpeed.x > 0.f ) Pos.x -= mRealSize.getWidth(); Pos.y += mRealSize.getHeight(); } - - mTextureRegion->setSrcRect( mRect ); - mTextureRegion->resetDestSize(); } } diff --git a/src/eepp/graphics/shaderprogram.cpp b/src/eepp/graphics/shaderprogram.cpp index 838d26575..3396ccea3 100644 --- a/src/eepp/graphics/shaderprogram.cpp +++ b/src/eepp/graphics/shaderprogram.cpp @@ -7,48 +7,58 @@ namespace EE { namespace Graphics { -ShaderProgram* ShaderProgram::New( const std::string& name ) { - return eeNew( ShaderProgram, ( name ) ); +ShaderProgramPtr ShaderProgram::New( const std::string& name ) { + return ShaderProgramPtr( eeNew( ShaderProgram, ( name ) ), ResourceDeleter() ); } -ShaderProgram* ShaderProgram::New( const std::vector& Shaders, const std::string& name ) { - return eeNew( ShaderProgram, ( Shaders, name ) ); +ShaderProgramPtr ShaderProgram::New( const std::vector& shaders, + const std::string& name ) { + return ShaderProgramPtr( eeNew( ShaderProgram, ( shaders, name ) ), + ResourceDeleter() ); } -ShaderProgram* ShaderProgram::New( const std::string& VertexShaderFile, - const std::string& FragmentShaderFile, - const std::string& name ) { - return eeNew( ShaderProgram, ( VertexShaderFile, FragmentShaderFile, name ) ); +ShaderProgramPtr ShaderProgram::New( const std::string& VertexShaderFile, + const std::string& FragmentShaderFile, + const std::string& name ) { + return ShaderProgramPtr( eeNew( ShaderProgram, ( VertexShaderFile, FragmentShaderFile, name ) ), + ResourceDeleter() ); } -ShaderProgram* ShaderProgram::New( const char* VertexShaderData, const Uint32& VertexShaderDataSize, - const char* FragmentShaderData, - const Uint32& FragmentShaderDataSize, const std::string& name ) { - return eeNew( ShaderProgram, ( VertexShaderData, VertexShaderDataSize, FragmentShaderData, - FragmentShaderDataSize, name ) ); +ShaderProgramPtr ShaderProgram::New( const char* VertexShaderData, + const Uint32& VertexShaderDataSize, + const char* FragmentShaderData, + const Uint32& FragmentShaderDataSize, + const std::string& name ) { + return ShaderProgramPtr( + eeNew( ShaderProgram, ( VertexShaderData, VertexShaderDataSize, FragmentShaderData, + FragmentShaderDataSize, name ) ), + ResourceDeleter() ); } -ShaderProgram* ShaderProgram::New( Pack* Pack, const std::string& VertexShaderPath, - const std::string& FragmentShaderPath, - const std::string& name ) { - return eeNew( ShaderProgram, ( Pack, VertexShaderPath, FragmentShaderPath, name ) ); +ShaderProgramPtr ShaderProgram::New( Pack* Pack, const std::string& VertexShaderPath, + const std::string& FragmentShaderPath, + const std::string& name ) { + return ShaderProgramPtr( + eeNew( ShaderProgram, ( Pack, VertexShaderPath, FragmentShaderPath, name ) ), + ResourceDeleter() ); } -ShaderProgram* ShaderProgram::New( const char** VertexShaderData, const Uint32& NumLinesVS, - const char** FragmentShaderData, const Uint32& NumLinesFS, - const std::string& name ) { - return eeNew( ShaderProgram, - ( VertexShaderData, NumLinesVS, FragmentShaderData, NumLinesFS, name ) ); +ShaderProgramPtr ShaderProgram::New( const char** VertexShaderData, const Uint32& NumLinesVS, + const char** FragmentShaderData, const Uint32& NumLinesFS, + const std::string& name ) { + return ShaderProgramPtr( eeNew( ShaderProgram, ( VertexShaderData, NumLinesVS, + FragmentShaderData, NumLinesFS, name ) ), + ResourceDeleter() ); } ShaderProgram::ShaderProgram( const std::string& name ) : mHandler( 0 ), mId( 0 ) { - addToManager( name ); + addToRegistry( name ); init(); } -ShaderProgram::ShaderProgram( const std::vector& Shaders, const std::string& name ) : +ShaderProgram::ShaderProgram( const std::vector& Shaders, const std::string& name ) : mHandler( 0 ), mId( 0 ) { - addToManager( name ); + addToRegistry( name ); init(); addShaders( Shaders ); @@ -59,15 +69,13 @@ ShaderProgram::ShaderProgram( const std::vector& Shaders, const std::st ShaderProgram::ShaderProgram( const std::string& VertexShaderFile, const std::string& FragmentShaderFile, const std::string& name ) : mHandler( 0 ), mId( 0 ) { - addToManager( name ); + addToRegistry( name ); init(); - VertexShader* vs = eeNew( VertexShader, ( VertexShaderFile ) ); - FragmentShader* fs = eeNew( FragmentShader, ( FragmentShaderFile ) ); + ShaderPtr vs( eeNew( VertexShader, ( VertexShaderFile ) ), ResourceDeleter() ); + ShaderPtr fs( eeNew( FragmentShader, ( FragmentShaderFile ) ), ResourceDeleter() ); if ( !vs->isValid() || !fs->isValid() ) { - eeSAFE_DELETE( vs ); - eeSAFE_DELETE( fs ); return; } @@ -80,17 +88,17 @@ ShaderProgram::ShaderProgram( const std::string& VertexShaderFile, ShaderProgram::ShaderProgram( Pack* Pack, const std::string& VertexShaderPath, const std::string& FragmentShaderPath, const std::string& name ) : mHandler( 0 ), mId( 0 ) { - addToManager( name ); + addToRegistry( name ); init(); if ( NULL != Pack && Pack->isOpen() && -1 != Pack->exists( VertexShaderPath ) && -1 != Pack->exists( FragmentShaderPath ) ) { - VertexShader* vs = eeNew( VertexShader, ( Pack, VertexShaderPath ) ); - FragmentShader* fs = eeNew( FragmentShader, ( Pack, FragmentShaderPath ) ); + ShaderPtr vs( eeNew( VertexShader, ( Pack, VertexShaderPath ) ), + ResourceDeleter() ); + ShaderPtr fs( eeNew( FragmentShader, ( Pack, FragmentShaderPath ) ), + ResourceDeleter() ); if ( !vs->isValid() || !fs->isValid() ) { - eeSAFE_DELETE( vs ); - eeSAFE_DELETE( fs ); return; } @@ -105,15 +113,15 @@ ShaderProgram::ShaderProgram( const char* VertexShaderData, const Uint32& Vertex const char* FragmentShaderData, const Uint32& FragmentShaderDataSize, const std::string& name ) : mHandler( 0 ), mId( 0 ) { - addToManager( name ); + addToRegistry( name ); init(); - VertexShader* vs = eeNew( VertexShader, ( VertexShaderData, VertexShaderDataSize ) ); - FragmentShader* fs = eeNew( FragmentShader, ( FragmentShaderData, FragmentShaderDataSize ) ); + ShaderPtr vs( eeNew( VertexShader, ( VertexShaderData, VertexShaderDataSize ) ), + ResourceDeleter() ); + ShaderPtr fs( eeNew( FragmentShader, ( FragmentShaderData, FragmentShaderDataSize ) ), + ResourceDeleter() ); if ( !vs->isValid() || !fs->isValid() ) { - eeSAFE_DELETE( vs ); - eeSAFE_DELETE( fs ); return; } @@ -127,15 +135,15 @@ ShaderProgram::ShaderProgram( const char** VertexShaderData, const Uint32& NumLi const char** FragmentShaderData, const Uint32& NumLinesFS, const std::string& name ) : mHandler( 0 ), mId( 0 ) { - addToManager( name ); + addToRegistry( name ); init(); - VertexShader* vs = eeNew( VertexShader, ( VertexShaderData, NumLinesVS ) ); - FragmentShader* fs = eeNew( FragmentShader, ( FragmentShaderData, NumLinesFS ) ); + ShaderPtr vs( eeNew( VertexShader, ( VertexShaderData, NumLinesVS ) ), + ResourceDeleter() ); + ShaderPtr fs( eeNew( FragmentShader, ( FragmentShaderData, NumLinesFS ) ), + ResourceDeleter() ); if ( !vs->isValid() || !fs->isValid() ) { - eeSAFE_DELETE( vs ); - eeSAFE_DELETE( fs ); return; } @@ -155,22 +163,18 @@ ShaderProgram::~ShaderProgram() { mUniformLocations.clear(); mAttributeLocations.clear(); - for ( unsigned int i = 0; i < mShaders.size(); i++ ) - eeSAFE_DELETE( mShaders[i] ); - - if ( !ShaderProgramManager::instance()->isDestroying() ) { - removeFromManager(); - } + removeFromRegistry(); } -void ShaderProgram::addToManager( const std::string& name ) { +void ShaderProgram::addToRegistry( const std::string& name ) { setName( name ); - ShaderProgramManager::instance()->add( this ); + ShaderProgramRegistry::instance()->add( this ); } -void ShaderProgram::removeFromManager() { - ShaderProgramManager::instance()->remove( this, false ); +void ShaderProgram::removeFromRegistry() { + if ( ShaderProgramRegistry::existsSingleton() ) + ShaderProgramRegistry::instance()->remove( this ); } void ShaderProgram::init() { @@ -191,7 +195,7 @@ void ShaderProgram::reload() { init(); - std::vector tmpShader = mShaders; + std::vector tmpShader = mShaders; mShaders.clear(); @@ -207,24 +211,24 @@ void ShaderProgram::reload() { } } -void ShaderProgram::addShader( Shader* Shader ) { - if ( !Shader->isValid() ) { +void ShaderProgram::addShader( ShaderPtr shader ) { + if ( !shader || !shader->isValid() ) { Log::error( "ShaderProgram::addShader() %s: Cannot add invalid shader", mName.c_str() ); return; } if ( 0 != getHandler() ) { #ifdef EE_SHADERS_SUPPORTED - GLi->attachShader( getHandler(), Shader->getId() ); + GLi->attachShader( getHandler(), shader->getId() ); #endif - mShaders.push_back( Shader ); + mShaders.emplace_back( std::move( shader ) ); } } -void ShaderProgram::addShaders( const std::vector& Shaders ) { - for ( Uint32 i = 0; i < Shaders.size(); i++ ) - addShader( Shaders[i] ); +void ShaderProgram::addShaders( const std::vector& shaders ) { + for ( const auto& shader : shaders ) + addShader( shader ); } bool ShaderProgram::link() { @@ -411,12 +415,6 @@ const std::string& ShaderProgram::getName() const { void ShaderProgram::setName( const std::string& name ) { mName = name; mId = String::hash( mName ); - - Uint32 NameCount = ShaderProgramManager::instance()->exists( mName ); - - if ( 0 != NameCount || 0 == name.size() ) { - setName( name + String::toString( NameCount + 1 ) ); - } } void ShaderProgram::setReloadCb( ShaderProgramReloadCb Cb ) { diff --git a/src/eepp/graphics/shaderprogrammanager.cpp b/src/eepp/graphics/shaderprogrammanager.cpp index d27c633f8..f690d779a 100644 --- a/src/eepp/graphics/shaderprogrammanager.cpp +++ b/src/eepp/graphics/shaderprogrammanager.cpp @@ -2,15 +2,15 @@ namespace EE { namespace Graphics { -SINGLETON_DECLARE_IMPLEMENTATION( ShaderProgramManager ) +SINGLETON_DECLARE_IMPLEMENTATION( ShaderProgramRegistry ) -ShaderProgramManager::ShaderProgramManager() {} +ShaderProgramRegistry::ShaderProgramRegistry() {} -ShaderProgramManager::~ShaderProgramManager() {} +ShaderProgramRegistry::~ShaderProgramRegistry() {} -void ShaderProgramManager::reload() { - for ( auto& res : mResources ) - res.second->reload(); +void ShaderProgramRegistry::reload() { + for ( auto* program : mResources ) + program->reload(); } }} // namespace EE::Graphics diff --git a/src/eepp/graphics/sprite.cpp b/src/eepp/graphics/sprite.cpp index a073343fd..947adff6f 100644 --- a/src/eepp/graphics/sprite.cpp +++ b/src/eepp/graphics/sprite.cpp @@ -1,6 +1,5 @@ -#include +#include #include -#include #include #include @@ -10,45 +9,58 @@ using namespace EE::Window; namespace EE { namespace Graphics { -Sprite* Sprite::New() { - return eeNew( Sprite, () ); +SpritePtr Sprite::New() { + return makeResource(); } -Sprite* Sprite::New( const std::string& name, const std::string& extension, - TextureAtlas* SearchInTextureAtlas ) { - return eeNew( Sprite, ( name, extension, SearchInTextureAtlas ) ); +SpritePtr Sprite::New( const std::string& name, const std::string& extension, + TextureAtlas* SearchInTextureAtlas ) { + return makeResource( name, extension, SearchInTextureAtlas ); } -Sprite* Sprite::New( TextureRegion* TextureRegion ) { - return eeNew( Sprite, ( TextureRegion ) ); +SpritePtr Sprite::New( ResourceScope& resourceScope, const std::string& name, + const std::string& extension, TextureAtlas* SearchInTextureAtlas ) { + return makeResource( resourceScope, name, extension, SearchInTextureAtlas ); } -Sprite* Sprite::New( ResourceId textureId, const Sizef& DestSize, const Vector2i& offset, - const Rect& TexSector ) { - return eeNew( Sprite, ( textureId, DestSize, offset, TexSector ) ); +SpritePtr Sprite::New( TextureRegion* TextureRegion ) { + return makeResource( TextureRegion ); } -Sprite* Sprite::fromGif( IOStream& stream ) { +SpritePtr Sprite::New( ResourceId textureId, const Sizef& DestSize, const Vector2i& offset, + const Rect& TexSector ) { + return makeResource( textureId, DestSize, offset, TexSector ); +} + +SpritePtr Sprite::fromGif( IOStream& stream ) { auto [gif, delay] = Texture::loadGif( stream ); - Sprite* sprite = Sprite::New(); + SpritePtr sprite = Sprite::New(); for ( const auto& texture : gif ) sprite->addFrame( texture ); sprite->setAnimationSpeed( 1000.f / (float)delay ); - sprite->setAsTextureRegionOwner( true ); - sprite->setAsTextureOwner( true ); return sprite; } Sprite::Sprite() : Drawable( Drawable::SPRITE ) {} +Sprite::Sprite( const Sprite& other ) : Drawable( Drawable::SPRITE ) { + *this = other; +} + Sprite::Sprite( const std::string& name, const std::string& extension, TextureAtlas* SearchInTextureAtlas ) : Drawable( Drawable::SPRITE ) { addFramesByPattern( name, extension, SearchInTextureAtlas ); } +Sprite::Sprite( ResourceScope& resourceScope, const std::string& name, const std::string& extension, + TextureAtlas* SearchInTextureAtlas ) : + Drawable( Drawable::SPRITE ) { + addFramesByPattern( resourceScope, name, extension, SearchInTextureAtlas ); +} + Sprite::Sprite( TextureRegion* TextureRegion ) : Drawable( Drawable::SPRITE ) { createStatic( TextureRegion ); } @@ -60,16 +72,29 @@ Sprite::Sprite( ResourceId textureId, const Sizef& DestSize, const Vector2i& Off } Sprite::~Sprite() { - cleanUpResources(); eeSAFE_DELETE_ARRAY( mVertexColors ); } Sprite& Sprite::operator=( const Sprite& Other ) { + if ( this == &Other ) + return *this; + mDrawableType = Other.mDrawableType; - mFrames = Other.mFrames; + mFrames.clear(); + mFrames.reserve( Other.mFrames.size() ); + for ( const Frame& otherFrame : Other.mFrames ) { + Frame frame; + frame.Spr.reserve( otherFrame.Spr.size() ); + for ( const TextureRegionPtr& region : otherFrame.Spr ) { + frame.Spr.emplace_back( + region ? std::static_pointer_cast( region->clone() ) : nullptr ); + } + mFrames.emplace_back( std::move( frame ) ); + } mFlags = Other.mFlags; mColor = Other.mColor; mPosition = Other.mPosition; + mOrigin = Other.mOrigin; mRotation = Other.mRotation; mScale = Other.mScale; mAnimSpeed = Other.mAnimSpeed; @@ -81,8 +106,10 @@ Sprite& Sprite::operator=( const Sprite& Other ) { mCurrentSubFrame = Other.mCurrentSubFrame; mSubFrames = Other.mSubFrames; mAnimTo = Other.mAnimTo; - mCallbacks = Other.mCallbacks; - mNumCallBacks = Other.mNumCallBacks; + mCallbacks.clear(); + mNumCallBacks = 0; + + eeSAFE_DELETE_ARRAY( mVertexColors ); if ( NULL != Other.mVertexColors ) { mVertexColors = eeNewArray( Color, 4 ); @@ -97,39 +124,12 @@ Sprite& Sprite::operator=( const Sprite& Other ) { return *this; } -Sprite Sprite::clone() { - Sprite Spr; +SpritePtr Sprite::cloneSprite() const { + return makeResource( *this ); +} - Spr.mDrawableType = mDrawableType; - Spr.mColor = mColor; - Spr.mFrames = mFrames; - Spr.mFlags = mFlags; - Spr.mPosition = mPosition; - Spr.mRotation = mRotation; - Spr.mScale = mScale; - Spr.mAnimSpeed = mAnimSpeed; - Spr.mRepetitions = mRepetitions; - Spr.mBlend = mBlend; - Spr.mEffect = mEffect; - Spr.mCurrentFrame = mCurrentFrame; - Spr.mfCurrentFrame = mfCurrentFrame; - Spr.mCurrentSubFrame = mCurrentSubFrame; - Spr.mSubFrames = mSubFrames; - Spr.mAnimTo = mAnimTo; - Spr.mCallbacks = mCallbacks; - Spr.mNumCallBacks = mNumCallBacks; - - if ( NULL != mVertexColors ) { - Spr.mVertexColors = eeNewArray( Color, 4 ); - Spr.mVertexColors[0] = mVertexColors[0]; - Spr.mVertexColors[1] = mVertexColors[1]; - Spr.mVertexColors[2] = mVertexColors[2]; - Spr.mVertexColors[3] = mVertexColors[3]; - } else { - Spr.mVertexColors = NULL; - } - - return Spr; +DrawablePtr Sprite::clone() const { + return cloneSprite(); } void Sprite::clearFrame() { @@ -139,29 +139,7 @@ void Sprite::clearFrame() { mFrames.clear(); } -void Sprite::cleanUpResources() { - if ( isTextureRegionOwner() || isTextureOwner() ) { - size_t frames = getNumFrames(); - - for ( size_t i = 0; i < frames; i++ ) { - for ( size_t f = 0; f < mFrames[i].Spr.size(); f++ ) { - TextureRegion* region = mFrames[i].Spr[f]; - Texture* texture = region->getTexture(); - - if ( isTextureOwner() && texture && - TextureFactory::instance()->exists( texture ) ) { - TextureFactory::instance()->remove( texture ); - } - - if ( isTextureRegionOwner() ) - GlobalTextureAtlas::instance()->remove( region ); - } - } - } -} - void Sprite::reset() { - cleanUpResources(); clearFrame(); mFlags = SPRITE_FLAG_AUTO_ANIM | SPRITE_FLAG_EVENTS_ENABLED; @@ -338,12 +316,12 @@ bool Sprite::createStatic( ResourceId textureId, const Sizef& DestSize, const Ve return false; } -bool Sprite::createStatic( Texture* tex, const Sizef& DestSize, const Vector2i& offset, +bool Sprite::createStatic( TexturePtr tex, const Sizef& DestSize, const Vector2i& offset, const Rect& TexSector ) { if ( tex ) { reset(); - addFrame( tex->getTextureId(), DestSize, offset, TexSector ); + addFrame( std::move( tex ), DestSize, offset, TexSector ); return true; } @@ -376,12 +354,19 @@ bool Sprite::addFrames( const std::vector TextureRegions ) { bool Sprite::addFramesByPatternId( const Uint32& TextureRegionId, const std::string& extension, TextureAtlas* SearchInTextureAtlas ) { - std::vector TextureRegions = - TextureAtlasManager::instance()->getTextureRegionsByPatternId( TextureRegionId, extension, - SearchInTextureAtlas ); + return addFramesByPatternId( defaultResourceScope(), TextureRegionId, extension, + SearchInTextureAtlas ); +} + +bool Sprite::addFramesByPatternId( ResourceScope& resourceScope, const Uint32& TextureRegionId, + const std::string& extension, + TextureAtlas* SearchInTextureAtlas ) { + std::vector TextureRegions = resourceScope.findTextureRegionsByPatternId( + TextureRegionId, extension, SearchInTextureAtlas ); if ( TextureRegions.size() ) { - addFrames( TextureRegions ); + for ( const TextureRegionPtr& textureRegion : TextureRegions ) + addFrame( textureRegion.get() ); return true; } @@ -394,12 +379,18 @@ bool Sprite::addFramesByPatternId( const Uint32& TextureRegionId, const std::str bool Sprite::addFramesByPattern( const std::string& name, const std::string& extension, TextureAtlas* SearchInTextureAtlas ) { - std::vector TextureRegions = - TextureAtlasManager::instance()->getTextureRegionsByPattern( name, extension, - SearchInTextureAtlas ); + return addFramesByPattern( defaultResourceScope(), name, extension, SearchInTextureAtlas ); +} + +bool Sprite::addFramesByPattern( ResourceScope& resourceScope, const std::string& name, + const std::string& extension, + TextureAtlas* SearchInTextureAtlas ) { + std::vector TextureRegions = + resourceScope.findTextureRegionsByPattern( name, extension, SearchInTextureAtlas ); if ( TextureRegions.size() ) { - addFrames( TextureRegions ); + for ( const TextureRegionPtr& textureRegion : TextureRegions ) + addFrame( textureRegion.get() ); return true; } @@ -411,6 +402,14 @@ bool Sprite::addFramesByPattern( const std::string& name, const std::string& ext bool Sprite::addSubFrame( TextureRegion* TextureRegion, const unsigned int& NumFrame, const unsigned int& NumSubFrame ) { + return addSubFrame( + TextureRegion ? std::static_pointer_cast( TextureRegion->clone() ) + : TextureRegionPtr{}, + NumFrame, NumSubFrame ); +} + +bool Sprite::addSubFrame( TextureRegionPtr TextureRegion, const unsigned int& NumFrame, + const unsigned int& NumSubFrame ) { unsigned int NF, NSF; if ( NumFrame >= mFrames.size() ) @@ -453,22 +452,39 @@ unsigned int Sprite::addFrame( ResourceId textureId, const Sizef& DestSize, cons return 0; } -unsigned int Sprite::addFrame( Texture* tex, const Sizef& DestSize, const Vector2i& offset, +unsigned int Sprite::addFrame( TexturePtr tex, const Sizef& DestSize, const Vector2i& offset, const Rect& TexSector ) { unsigned int id = framePos(); - if ( addSubFrame( tex, id, mCurrentSubFrame, DestSize, offset, TexSector ) ) + if ( addSubFrame( std::move( tex ), id, mCurrentSubFrame, DestSize, offset, TexSector ) ) return id; return 0; } -bool Sprite::addSubFrame( Texture* tex, const unsigned int& NumFrame, +bool Sprite::addSubFrame( TexturePtr tex, const unsigned int& NumFrame, const unsigned int& NumSubFrame, const Sizef& DestSize, const Vector2i& Offset, const Rect& TexSector ) { - if ( tex ) - return addSubFrame( tex->getTextureId(), NumFrame, NumSubFrame, DestSize, Offset, - TexSector ); + if ( tex ) { + TextureRegionPtr region = makeResource(); + region->setTexture( std::move( tex ) ); + + if ( TexSector.Right > 0 && TexSector.Bottom > 0 ) + region->setSrcRect( TexSector ); + else + region->setSrcRect( Rect( 0, 0, (Int32)region->getTexture()->getImageWidth(), + (Int32)region->getTexture()->getImageHeight() ) ); + + Sizef destSize( DestSize ); + if ( destSize.x <= 0 ) + destSize.x = static_cast( region->getSrcRect().getWidth() ); + if ( destSize.y <= 0 ) + destSize.y = static_cast( region->getSrcRect().getHeight() ); + + region->setDestSize( destSize ); + region->setOffset( Offset ); + return addSubFrame( std::move( region ), NumFrame, NumSubFrame ); + } return false; } @@ -478,32 +494,8 @@ bool Sprite::addSubFrame( ResourceId textureId, const unsigned int& NumFrame, if ( !TextureFactory::instance()->existsId( textureId ) ) return false; - Texture* Tex = TextureFactory::instance()->getTexture( textureId ); - TextureRegion* S = GlobalTextureAtlas::instance()->add( TextureRegion::New() ); - - S->setTextureId( textureId ); - - if ( TexSector.Right > 0 && TexSector.Bottom > 0 ) - S->setSrcRect( TexSector ); - else - S->setSrcRect( Rect( 0, 0, (Int32)Tex->getImageWidth(), (Int32)Tex->getImageHeight() ) ); - - Sizef destSize( DestSize ); - - if ( destSize.x <= 0 ) { - destSize.x = static_cast( S->getSrcRect().Right - S->getSrcRect().Left ); - } - - if ( destSize.y <= 0 ) { - destSize.y = static_cast( S->getSrcRect().Bottom - S->getSrcRect().Top ); - } - - S->setDestSize( destSize ); - S->setOffset( Offset ); - - addSubFrame( S, NumFrame, NumSubFrame ); - - return true; + return addSubFrame( TextureFactory::instance()->getTexture( textureId ), NumFrame, NumSubFrame, + DestSize, Offset, TexSector ); } void Sprite::update() { @@ -748,21 +740,21 @@ bool Sprite::getAutoAnimate() const { TextureRegion* Sprite::getCurrentTextureRegion() { if ( mFrames.size() ) - return mFrames[mCurrentFrame].Spr[mCurrentSubFrame]; + return mFrames[mCurrentFrame].Spr[mCurrentSubFrame].get(); return NULL; } TextureRegion* Sprite::getTextureRegion( const unsigned int& frame ) { if ( frame < mFrames.size() ) - return mFrames[frame].Spr[mCurrentSubFrame]; + return mFrames[frame].Spr[mCurrentSubFrame].get(); return NULL; } TextureRegion* Sprite::getTextureRegion( const unsigned int& frame, const unsigned int& SubFrame ) { if ( frame < mFrames.size() ) - return mFrames[frame].Spr[SubFrame]; + return mFrames[frame].Spr[SubFrame].get(); return NULL; } @@ -913,30 +905,6 @@ void Sprite::fireEvent( const Uint32& Event ) { } } -Sprite& Sprite::setAsTextureRegionOwner( bool set ) { - if ( set ) - mFlags |= SPRITE_FLAG_TEXTURE_REGION_OWNER; - else - mFlags &= ~SPRITE_FLAG_TEXTURE_REGION_OWNER; - return *this; -} - -bool Sprite::isTextureRegionOwner() const { - return mFlags & SPRITE_FLAG_TEXTURE_REGION_OWNER; -} - -Sprite& Sprite::setAsTextureOwner( bool set ) { - if ( set ) - mFlags |= SPRITE_FLAG_TEXTURE_OWNER; - else - mFlags &= ~SPRITE_FLAG_TEXTURE_OWNER; - return *this; -} - -bool Sprite::isTextureOwner() const { - return mFlags & SPRITE_FLAG_TEXTURE_OWNER; -} - void Sprite::setOrigin( const OriginPoint& origin ) { mOrigin = origin; } diff --git a/src/eepp/graphics/statelistdrawable.cpp b/src/eepp/graphics/statelistdrawable.cpp index 4010bdcc2..11824d8e9 100644 --- a/src/eepp/graphics/statelistdrawable.cpp +++ b/src/eepp/graphics/statelistdrawable.cpp @@ -13,27 +13,29 @@ StateListDrawable::StateListDrawable( Type type, const std::string& name ) : StateListDrawable::StateListDrawable( const std::string& name ) : StatefulDrawable( STATELIST, name ), mCurrentState( 0 ), mCurrentDrawable( NULL ) {} -StateListDrawable::~StateListDrawable() { - clearDrawables(); -} +StateListDrawable::~StateListDrawable() {} void StateListDrawable::clearDrawables() { - std::vector removeOwnershipState; + mCurrentDrawable = nullptr; + mDrawables.clear(); +} - for ( auto it = mDrawables.begin(); it != mDrawables.end(); ++it ) { - Drawable* drawable = it->second; - - if ( mDrawablesOwnership[drawable] ) { - removeOwnershipState.push_back( drawable ); - eeSAFE_DELETE( drawable ); +DrawablePtr StateListDrawable::clone() const { + auto instance = ResourcePtr( eeNew( StateListDrawable, ( mName ) ), + ResourceDeleter() ); + instance->setColor( mColor ); + instance->setPosition( mPosition ); + for ( const auto& state : mDrawables ) { + if ( state.second ) { + DrawablePtr drawable = state.second->clone(); + if ( !drawable ) + return {}; + instance->setStateDrawable( state.first, std::move( drawable ) ); } } - - for ( auto& removeOwnership : removeOwnershipState ) { - mDrawablesOwnership.erase( removeOwnership ); - } - - mDrawables.clear(); + instance->mDrawableColors = mDrawableColors; + instance->setState( mCurrentState ); + return instance; } Sizef StateListDrawable::getSize() { @@ -94,17 +96,11 @@ bool StateListDrawable::isStateful() { } StatefulDrawable* StateListDrawable::setState( Uint32 state ) { - if ( state != mCurrentState || mCurrentDrawable == NULL || - mCurrentDrawable != mDrawables[mCurrentState] ) { + auto current = mDrawables.find( state ); + Drawable* stateDrawable = current != mDrawables.end() ? current->second.get() : nullptr; + if ( state != mCurrentState || mCurrentDrawable != stateDrawable ) { mCurrentState = state; - - auto it = mDrawables.find( state ); - - if ( it != mDrawables.end() ) { - mCurrentDrawable = it->second; - } else { - mCurrentDrawable = NULL; - } + mCurrentDrawable = stateDrawable; } return this; @@ -116,28 +112,21 @@ const Uint32& StateListDrawable::getState() const { Drawable* StateListDrawable::getStateDrawable( const Uint32& state ) { if ( hasDrawableState( state ) ) - return mDrawables[state]; + return mDrawables[state].get(); return NULL; } -StateListDrawable* StateListDrawable::setStateDrawable( const Uint32& state, Drawable* drawable, - bool ownIt ) { +StateListDrawable* StateListDrawable::setStateDrawable( const Uint32& state, + DrawablePtr drawable ) { if ( NULL != drawable ) { - if ( hasDrawableState( state ) && mDrawablesOwnership[mDrawables[state]] ) { + if ( hasDrawableState( state ) && mCurrentDrawable == mDrawables[state].get() ) + mCurrentDrawable = NULL; - if ( mCurrentDrawable == mDrawables[state] ) - mCurrentDrawable = NULL; - - mDrawablesOwnership.erase( mDrawables[state] ); - eeDelete( mDrawables[state] ); - } - - mDrawables[state] = drawable; - mDrawablesOwnership[drawable] = ownIt; + mDrawables[state] = std::move( drawable ); if ( hasDrawableStateColor( state ) ) - drawable->setColor( mDrawableColors[state] ); + mDrawables[state]->setColor( mDrawableColors[state] ); if ( state == mCurrentState ) setState( state ); @@ -194,9 +183,9 @@ bool StateListDrawable::hasDrawableStateColor( const Uint32& state ) const { void StateListDrawable::onColorFilterChange() { for ( auto it = mDrawables.begin(); it != mDrawables.end(); ++it ) { - Drawable* drawable = it->second; - - drawable->setColor( mColor ); + Drawable* drawable = it->second.get(); + if ( drawable ) + drawable->setColor( mColor ); } } diff --git a/src/eepp/graphics/text.cpp b/src/eepp/graphics/text.cpp index 5e253647b..c0bfaab91 100644 --- a/src/eepp/graphics/text.cpp +++ b/src/eepp/graphics/text.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include @@ -341,11 +341,12 @@ Sizef Text::draw( const StringType& string, const Vector2f& pos, Font* font, Flo String::StringBaseType prevChar = 0; bool isBold = ( style & Text::Bold ) != 0; bool isItalic = ( style & Text::Italic ) != 0; - bool fallbacksToColorEmoji = - font && font->getType() == FontType::TTF && - !static_cast( font )->isColorEmojiFont() && - FontManager::instance()->getColorEmojiFont() != nullptr && - FontManager::instance()->getColorEmojiFont()->getType() == FontType::TTF; + FontTrueType* trueTypeFont = + font && font->getType() == FontType::TTF ? static_cast( font ) : nullptr; + FontService* fontService = trueTypeFont ? trueTypeFont->getFontService() : nullptr; + bool fallbacksToColorEmoji = trueTypeFont && !trueTypeFont->isColorEmojiFont() && fontService && + fontService->getColorEmojiFont() != nullptr && + fontService->getColorEmojiFont()->getType() == FontType::TTF; bool isMonospace = font && ( font->isMonospace() || ( font->getType() == FontType::TTF && static_cast( font )->isIdentifiedAsMonospace() && @@ -355,12 +356,14 @@ Sizef Text::draw( const StringType& string, const Vector2f& pos, Font* font, Flo Float height = font->getLineSpacing( fontSize ); Sizef size{ 0, height }; size_t ssize = string.size(); + if ( ssize == 0 ) + return size; BatchRenderer* BR = GlobalBatchRenderer::instance(); - Texture* fontTexture = font->getTexture( fontSize ); + const TexturePtr& fontTexture = font->getTexture( fontSize ); Float tabAlign = 0; GlyphDrawable* spaceGlyph = nullptr; GlyphDrawable* tabGlyph = nullptr; - Float hspace = font->getGlyph( ' ', fontSize, isBold, isItalic ).advance; + Float hspace = font->getGlyphAdvance( ' ', fontSize, isBold, isItalic ); std::optional tabOffset{ whitespaceDisplayConfig.tabOffset }; if ( whitespaceDisplayConfig.tabDisplayCharacter ) tabGlyph = font->getGlyphDrawable( whitespaceDisplayConfig.tabDisplayCharacter, fontSize ); @@ -640,9 +643,10 @@ void Text::create( Font* font, const String& text, Color FontColor, Color FontSh void Text::checkColorEmojis() { mContainsColorEmoji = false; - if ( mFontStyleConfig.Font && FontManager::instance()->getColorEmojiFont() != nullptr ) { - if ( mFontStyleConfig.Font->getType() == FontType::TTF ) { - FontTrueType* fontTrueType = static_cast( mFontStyleConfig.Font ); + if ( mFontStyleConfig.Font && mFontStyleConfig.Font->getType() == FontType::TTF ) { + FontTrueType* fontTrueType = static_cast( mFontStyleConfig.Font ); + FontService* fontService = fontTrueType->getFontService(); + if ( fontService && fontService->getColorEmojiFont() != nullptr ) { if ( fontTrueType->isColorEmojiFont() || !fontTrueType->isEmojiFont() ) mContainsColorEmoji = Font::containsEmojiCodePoint( mString ); } @@ -1763,7 +1767,7 @@ void Text::draw( const Float& X, const Float& Y, const Vector2f& scale, const Fl if ( mColors.empty() ) return; - Texture* texture = mFontStyleConfig.Font->getTexture( mFontStyleConfig.CharacterSize ); + const TexturePtr& texture = mFontStyleConfig.Font->getTexture( mFontStyleConfig.CharacterSize ); if ( !texture ) return; texture->bind(); @@ -2963,11 +2967,9 @@ SmallVector Text::getSelectionRects( TextSelectionRange range ) { size_t startLine = findVisualLineFromCharIndex( range.start ); size_t endLine = findVisualLineFromCharIndex( range.end ); - Float hspace = - mFontStyleConfig.Font - ->getGlyph( ' ', mFontStyleConfig.CharacterSize, mFontStyleConfig.Style & Text::Bold, - mFontStyleConfig.Style & Text::Italic ) - .advance; + Float hspace = mFontStyleConfig.Font->getGlyphAdvance( ' ', mFontStyleConfig.CharacterSize, + mFontStyleConfig.Style & Text::Bold, + mFontStyleConfig.Style & Text::Italic ); Float vspace = getLineSpacing(); for ( size_t i = startLine; i <= endLine; ++i ) { diff --git a/src/eepp/graphics/textlayout.cpp b/src/eepp/graphics/textlayout.cpp index 70324a882..547bf5b16 100644 --- a/src/eepp/graphics/textlayout.cpp +++ b/src/eepp/graphics/textlayout.cpp @@ -13,7 +13,12 @@ namespace EE::Graphics { -using LRULayoutCache = LRUCache<8192, Uint64, TextLayout::Cache>; +struct LayoutCacheEntry { + Font* sourceFont{ nullptr }; + TextLayout::Cache layout; +}; + +using LRULayoutCache = LRUCache<8192, Uint64, LayoutCacheEntry>; #ifdef EE_TEXT_SHAPER_ENABLED @@ -236,15 +241,30 @@ static inline Uint64 textLayoutHash( const String::View& string, Font* font, std::hash()( initialXOffset ) ); } -static LRULayoutCache& getLayoutCache( bool invalidate = false ) { +static LRULayoutCache& getLayoutCache( bool invalidate = false, Font* font = nullptr ) { static LRULayoutCache sLayoutCache; - if ( invalidate ) - sLayoutCache.clear(); + if ( invalidate ) { + if ( !font ) { + sLayoutCache.clear(); + } else { + sLayoutCache.eraseIf( [font]( Uint64, const LayoutCacheEntry& entry ) { + if ( !entry.layout || entry.sourceFont == font ) + return true; + for ( const ShapedTextParagraph& paragraph : entry.layout->paragraphs ) { + for ( const ShapedGlyph& glyph : paragraph.shapedGlyphs ) { + if ( glyph.font == font ) + return true; + } + } + return false; + } ); + } + } return sLayoutCache; } -void TextLayout::clearLayoutCache() { - getLayoutCache( true ); +void TextLayout::clearLayoutCache( Font* font ) { + getLayoutCache( true, font ); } TextLayout::Cache TextLayout::layout( const String::View& string, Font* font, @@ -270,13 +290,13 @@ TextLayout::Cache TextLayout::layout( const String::View& string, Font* font, auto cacheHit = getLayoutCache().get( hash ); if ( cacheHit.has_value() ) - return *cacheHit; + return cacheHit->layout; } bool bold = ( style & Text::Bold ) != 0; bool italic = ( style & Text::Italic ) != 0; Uint32 spaceGlyphIndex = 0; - Float hspace = font->getGlyph( ' ', characterSize, bold, italic, outlineThickness ).advance; + Float hspace = font->getGlyphAdvance( ' ', characterSize, bold, italic, outlineThickness ); Float vspace = font->getLineSpacing( characterSize ); Vector2f pen{ initialXOffset, 0 }; Float maxWidth = 0; @@ -523,7 +543,7 @@ TextLayout::Cache TextLayout::layout( const String::View& string, Font* font, characterSize, style, tabWidth, outlineThickness, hspace ); } - getLayoutCache().put( hash, resultPtr ); + getLayoutCache().put( hash, { font, resultPtr } ); return resultPtr; } diff --git a/src/eepp/graphics/texture.cpp b/src/eepp/graphics/texture.cpp index af4cc6024..9c5dc43a0 100644 --- a/src/eepp/graphics/texture.cpp +++ b/src/eepp/graphics/texture.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,16 @@ using namespace EE::Graphics::Private; namespace EE { namespace Graphics { +DrawablePtr Texture::clone() const { + TexturePtr texture = TextureFactory::instance()->getTexture( getTextureId() ); + if ( !texture ) + return {}; + TextureDrawablePtr instance = TextureDrawable::New( std::move( texture ) ); + instance->setColor( mColor ); + instance->setPosition( mPosition ); + return instance; +} + Uint32 Texture::getMaximumSize() { static bool checked = false; static GLint size = 0; @@ -919,7 +930,7 @@ void Texture::draw( const Vector2f& position, const Sizef& size ) { size.y ); } -std::pair, int> Texture::loadGif( IOStream& stream ) { +std::pair, int> Texture::loadGif( IOStream& stream ) { stbi_io_callbacks callbacks; callbacks.read = &IOCb::read; callbacks.skip = &IOCb::skip; @@ -929,7 +940,7 @@ std::pair, int> Texture::loadGif( IOStream& stream ) { if ( type != STBI_gif ) return {}; stream.seek( 0 ); - std::vector gif; + std::vector gif; ScopedBuffer buf( stream.getSize() ); stream.read( (char*)buf.get(), buf.size() ); int width, height, frames, comp; diff --git a/src/eepp/graphics/textureatlas.cpp b/src/eepp/graphics/textureatlas.cpp index 09d4355e6..843b8fa40 100644 --- a/src/eepp/graphics/textureatlas.cpp +++ b/src/eepp/graphics/textureatlas.cpp @@ -1,12 +1,15 @@ #include +#include + +using namespace EE::System; namespace EE { namespace Graphics { -TextureAtlas* TextureAtlas::New( const std::string& name ) { - return eeNew( TextureAtlas, ( name ) ); +TextureAtlasPtr TextureAtlas::New( const std::string& name ) { + return makeResource( name ); } -TextureAtlas::TextureAtlas( const std::string& name ) : ResourceManager() { +TextureAtlas::TextureAtlas( const std::string& name ) { setName( name ); } @@ -33,62 +36,140 @@ const String::HashType& TextureAtlas::getId() const { return mId; } -TextureRegion* TextureAtlas::add( TextureRegion* textureRegion ) { - return ResourceManager::add( textureRegion ); +TextureRegionPtr TextureAtlas::add( TextureRegionPtr textureRegion ) { + if ( !textureRegion ) + return {}; + + std::string realName( textureRegion->getName() ); + Uint32 count = 1; + for ( ;; ) { + { + Lock lock( mMutex ); + if ( mResources.find( textureRegion->getId() ) == mResources.end() ) { + mResources[textureRegion->getId()] = textureRegion; + return textureRegion; + } + } + + // setName() can notify listeners. Never invoke callbacks while holding the atlas mutex. + textureRegion->setName( realName + String::toString( ++count ) ); + } } -TextureRegion* TextureAtlas::add( ResourceId textureId, const std::string& Name ) { +TextureRegionPtr TextureAtlas::add( ResourceId textureId, const std::string& Name ) { return add( TextureRegion::New( textureId, Name ) ); } -TextureRegion* TextureAtlas::add( ResourceId textureId, const Rect& SrcRect, - const std::string& Name ) { +TextureRegionPtr TextureAtlas::add( ResourceId textureId, const Rect& SrcRect, + const std::string& Name ) { return add( TextureRegion::New( textureId, SrcRect, Name ) ); } -TextureRegion* TextureAtlas::add( ResourceId textureId, const Rect& SrcRect, const Sizef& DestSize, - const std::string& Name ) { +TextureRegionPtr TextureAtlas::add( ResourceId textureId, const Rect& SrcRect, + const Sizef& DestSize, const std::string& Name ) { return add( TextureRegion::New( textureId, SrcRect, DestSize, Name ) ); } -TextureRegion* TextureAtlas::add( ResourceId textureId, const Rect& SrcRect, const Sizef& DestSize, - const Vector2i& Offset, const std::string& Name ) { +TextureRegionPtr TextureAtlas::add( ResourceId textureId, const Rect& SrcRect, + const Sizef& DestSize, const Vector2i& Offset, + const std::string& Name ) { return add( TextureRegion::New( textureId, SrcRect, DestSize, Offset, Name ) ); } -TextureRegion* TextureAtlas::add( Texture* tex, const std::string& Name ) { - return add( TextureRegion::New( tex, Name ) ); +TextureRegionPtr TextureAtlas::add( TexturePtr tex, const std::string& Name ) { + return add( TextureRegion::New( std::move( tex ), Name ) ); } -TextureRegion* TextureAtlas::add( Texture* tex, const Rect& SrcRect, const std::string& Name ) { - return add( TextureRegion::New( tex, SrcRect, Name ) ); +TextureRegionPtr TextureAtlas::add( TexturePtr tex, const Rect& SrcRect, const std::string& Name ) { + return add( TextureRegion::New( std::move( tex ), SrcRect, Name ) ); } -TextureRegion* TextureAtlas::add( Texture* tex, const Rect& SrcRect, const Sizef& DestSize, - const std::string& Name ) { - return add( TextureRegion::New( tex, SrcRect, DestSize, Name ) ); +TextureRegionPtr TextureAtlas::add( TexturePtr tex, const Rect& SrcRect, const Sizef& DestSize, + const std::string& Name ) { + return add( TextureRegion::New( std::move( tex ), SrcRect, DestSize, Name ) ); } -TextureRegion* TextureAtlas::add( Texture* tex, const Rect& SrcRect, const Sizef& DestSize, - const Vector2i& Offset, const std::string& Name ) { - return add( TextureRegion::New( tex, SrcRect, DestSize, Offset, Name ) ); +TextureRegionPtr TextureAtlas::add( TexturePtr tex, const Rect& SrcRect, const Sizef& DestSize, + const Vector2i& Offset, const std::string& Name ) { + return add( TextureRegion::New( std::move( tex ), SrcRect, DestSize, Offset, Name ) ); } -Uint32 TextureAtlas::getCount() { - return ResourceManager::getCount(); +TextureRegionPtr TextureAtlas::getByName( const std::string& name ) const { + return getById( String::hash( name ) ); } -void TextureAtlas::setTextures( std::vector textures ) { - mTextures = textures; +TextureRegionPtr TextureAtlas::getById( const String::HashType& id ) const { + Lock lock( mMutex ); + auto it = mResources.find( id ); + return it != mResources.end() ? it->second : TextureRegionPtr{}; } -Texture* TextureAtlas::getTexture( const Uint32& texnum ) const { +bool TextureAtlas::remove( const TextureRegionPtr& textureRegion ) { + return textureRegion && removeById( textureRegion->getId() ); +} + +bool TextureAtlas::removeByName( const std::string& name ) { + return removeById( String::hash( name ) ); +} + +bool TextureAtlas::removeById( const String::HashType& id ) { + TextureRegionPtr textureRegion; + { + Lock lock( mMutex ); + auto it = mResources.find( id ); + if ( it == mResources.end() ) + return false; + textureRegion = std::move( it->second ); + mResources.erase( it ); + } + textureRegion.reset(); + return true; +} + +bool TextureAtlas::exists( const std::string& name ) const { + return existsId( String::hash( name ) ); +} + +bool TextureAtlas::existsId( const String::HashType& id ) const { + Lock lock( mMutex ); + return mResources.find( id ) != mResources.end(); +} + +void TextureAtlas::clear() { + UnorderedMap resources; + { + Lock lock( mMutex ); + resources = std::move( mResources ); + } + resources.clear(); +} + +void TextureAtlas::printNames() const { + Lock lock( mMutex ); + for ( const auto& resource : mResources ) + eePRINTL( "'%s'", resource.second->getName().c_str() ); +} + +const UnorderedMap& TextureAtlas::getResources() const { + return mResources; +} + +Uint32 TextureAtlas::getCount() const { + Lock lock( mMutex ); + return static_cast( mResources.size() ); +} + +void TextureAtlas::setTextures( std::vector textures ) { + mTextures = std::move( textures ); +} + +const TexturePtr& TextureAtlas::getTexture( const Uint32& texnum ) const { eeASSERT( texnum < mTextures.size() ); return mTextures[texnum]; } -Uint32 TextureAtlas::getTexturesCount() { - return mTextures.size(); +Uint32 TextureAtlas::getTexturesCount() const { + return static_cast( mTextures.size() ); } }} // namespace EE::Graphics diff --git a/src/eepp/graphics/textureatlasloader.cpp b/src/eepp/graphics/textureatlasloader.cpp index 7709f9201..c6d69a5c4 100644 --- a/src/eepp/graphics/textureatlasloader.cpp +++ b/src/eepp/graphics/textureatlasloader.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -10,8 +9,11 @@ #include #include #include +#include #include +using namespace EE::Window; + namespace EE { namespace Graphics { using namespace Private; @@ -48,7 +50,7 @@ TextureAtlasLoader::TextureAtlasLoader() : mPack( NULL ), mSkipResourceLoad( false ), mIsLoading( false ), - mTextureAtlas( NULL ) {} + mTextureAtlas() {} TextureAtlasLoader::TextureAtlasLoader( const std::string& TextureAtlasPath, const bool& Threaded, GLLoadCallback LoadCallback ) : @@ -58,7 +60,7 @@ TextureAtlasLoader::TextureAtlasLoader( const std::string& TextureAtlasPath, con mPack( NULL ), mSkipResourceLoad( false ), mIsLoading( false ), - mTextureAtlas( NULL ), + mTextureAtlas(), mLoadCallback( LoadCallback ) { loadFromFile(); } @@ -72,7 +74,7 @@ TextureAtlasLoader::TextureAtlasLoader( const Uint8* Data, const Uint32& DataSiz mPack( NULL ), mSkipResourceLoad( false ), mIsLoading( false ), - mTextureAtlas( NULL ), + mTextureAtlas(), mLoadCallback( LoadCallback ) { loadFromMemory( Data, DataSize, TextureAtlasName ); } @@ -85,7 +87,7 @@ TextureAtlasLoader::TextureAtlasLoader( Pack* Pack, const std::string& FilePackP mPack( NULL ), mSkipResourceLoad( false ), mIsLoading( false ), - mTextureAtlas( NULL ), + mTextureAtlas(), mLoadCallback( LoadCallback ) { loadFromPack( Pack, FilePackPath ); } @@ -97,7 +99,7 @@ TextureAtlasLoader::TextureAtlasLoader( IOStream& IOS, const bool& Threaded, mPack( NULL ), mSkipResourceLoad( false ), mIsLoading( false ), - mTextureAtlas( NULL ), + mTextureAtlas(), mLoadCallback( LoadCallback ) { loadFromStream( IOS ); } @@ -123,13 +125,26 @@ void TextureAtlasLoader::setTextureFilter( const Texture::Filter& textureFilter mTextureAtlas->getTexture( i )->setFilter( textureFilter ); } +void TextureAtlasLoader::setResourceScope( ResourceScopePtr resourceScope ) { + mResourceScope = std::move( resourceScope ); +} + +const ResourceScopePtr& TextureAtlasLoader::getResourceScope() const { + return mResourceScope; +} + void TextureAtlasLoader::loadFromStream( IOStream& IOS ) { + if ( !mResourceScope ) + mResourceScope = Engine::instance()->getDefaultResourceScope(); + mRL.setThreaded( mThreaded ); if ( IOS.isOpen() ) { IOS.read( (char*)&mTexGrHdr, sizeof( sTextureAtlasHdr ) ); if ( mTexGrHdr.Magic == EE_TEXTURE_ATLAS_MAGIC ) { + // The complete entry vector is built before mRL starts. Each task below writes only the + // LoadedTexture member at its captured index, so worker execution cannot race a resize. for ( Uint32 i = 0; i < mTexGrHdr.TextureCount; i++ ) { sTextureHdr tTextureHdr; sTempTexAtlas tTexAtlas; @@ -141,18 +156,28 @@ void TextureAtlasLoader::loadFromStream( IOStream& IOS ) { std::string name( &tTextureHdr.Name[0] ); std::string path( FileSystem::fileRemoveFileName( mTextureAtlasPath ) + name ); + FileSystem::filePathRemoveProcessPath( path ); //! Checks if the texture is already loaded - Texture* tTex = TextureFactory::instance()->getByName( path ); + TexturePtr tTex = mResourceScope->findTexture( path ); + tTexAtlas.LoadedTexture = tTex; if ( !mSkipResourceLoad && NULL == tTex ) { + const std::size_t textureIndex = mTempAtlass.size(); if ( NULL != mPack ) { - mRL.add( [this, path = std::move( path )] { - TextureFactory::instance()->loadFromPack( mPack, path ); + mRL.add( [this, textureIndex, path = std::move( path )] { + TexturePtr texture = + TextureFactory::instance()->loadFromPack( mPack, path ); + if ( texture ) + mResourceScope->publishLocal( path, texture ); + mTempAtlass[textureIndex].LoadedTexture = std::move( texture ); } ); } else { - mRL.add( [path = std::move( path )] { - TextureFactory::instance()->loadFromFile( path ); + mRL.add( [this, textureIndex, path = std::move( path )] { + TexturePtr texture = TextureFactory::instance()->loadFromFile( path ); + if ( texture ) + mResourceScope->publishLocal( path, texture ); + mTempAtlass[textureIndex].LoadedTexture = std::move( texture ); } ); } } @@ -160,7 +185,7 @@ void TextureAtlasLoader::loadFromStream( IOStream& IOS ) { IOS.read( (char*)&tTexAtlas.TextureRegions[0], sizeof( sTextureRegionHdr ) * tTextureHdr.TextureRegionCount ); - mTempAtlass.push_back( tTexAtlas ); + mTempAtlass.push_back( std::move( tTexAtlas ) ); } } @@ -216,7 +241,7 @@ void TextureAtlasLoader::loadFromMemory( const Uint8* Data, const Uint32& DataSi loadFromStream( IOS ); } -TextureAtlas* TextureAtlasLoader::getTextureAtlas() const { +const TextureAtlasPtr& TextureAtlasLoader::getTextureAtlas() const { return mTextureAtlas; } @@ -233,7 +258,7 @@ void TextureAtlasLoader::createTextureRegions() { FileSystem::filePathRemoveProcessPath( path ); - Texture* tTex = TextureFactory::instance()->getByName( path ); + TexturePtr tTex = tTexAtlas->LoadedTexture; if ( NULL != tTex ) mTexturesLoaded.push_back( tTex ); @@ -247,7 +272,7 @@ void TextureAtlasLoader::createTextureRegions() { std::string etapath = FileSystem::fileRemoveExtension( path ) + EE_TEXTURE_ATLAS_EXTENSION; - TextureAtlas* tTextureAtlas = TextureAtlasManager::instance()->getByName( name ); + TextureAtlasPtr tTextureAtlas = mResourceScope->findAtlas( name ); if ( NULL != tTextureAtlas && tTextureAtlas->getPath() == etapath ) { mTextureAtlas = tTextureAtlas; @@ -258,7 +283,7 @@ void TextureAtlasLoader::createTextureRegions() { mTextureAtlas->setPath( etapath ); - TextureAtlasManager::instance()->add( mTextureAtlas ); + mResourceScope->publishLocalAtlas( name, mTextureAtlas ); } } @@ -274,7 +299,7 @@ void TextureAtlasLoader::createTextureRegions() { Rect tRect( tSh->X, tSh->Y, tSh->X + tSh->Width, tSh->Y + tSh->Height ); - TextureRegion* tTextureRegion = TextureRegion::New( + TextureRegionPtr tTextureRegion = TextureRegion::New( tTex->getTextureId(), tRect, Sizef( (Float)tSh->DestWidth, (Float)tSh->DestHeight ), Vector2i( tSh->OffsetX, tSh->OffsetY ), TextureRegionName ); @@ -283,7 +308,8 @@ void TextureAtlasLoader::createTextureRegions() { // if ( tSh->Flags & HDR_TEXTUREREGION_FLAG_FLIPPED ) // Should rotate the sub texture, but.. sub texture rotation is not stored. - mTextureAtlas->add( tTextureRegion ); + tTextureRegion = mTextureAtlas->add( std::move( tTextureRegion ) ); + mResourceScope->publishLocalDrawable( TextureRegionName, tTextureRegion ); } } } else { @@ -327,7 +353,7 @@ bool TextureAtlasLoader::isLoading() const { return mIsLoading.load(); } -Texture* TextureAtlasLoader::getTexture( const Uint32& texnum ) const { +const TexturePtr& TextureAtlasLoader::getTexture( const Uint32& texnum ) const { eeASSERT( texnum < mTexturesLoaded.size() ); return mTexturesLoaded[texnum]; } @@ -347,7 +373,7 @@ bool TextureAtlasLoader::updateTextureAtlas() { for ( Int32 i = 0; i < tTexHdr->TextureRegionCount; i++ ) { sTextureRegionHdr* tSh = &tTexAtlas->TextureRegions[i]; - TextureRegion* tTextureRegion = mTextureAtlas->getById( tSh->ResourceID ); + TextureRegionPtr tTextureRegion = mTextureAtlas->getById( tSh->ResourceID ); if ( NULL != tTextureRegion ) { tSh->OffsetX = tTextureRegion->getOffset().x; diff --git a/src/eepp/graphics/textureatlasmanager.cpp b/src/eepp/graphics/textureatlasmanager.cpp deleted file mode 100644 index 9f35f63e3..000000000 --- a/src/eepp/graphics/textureatlasmanager.cpp +++ /dev/null @@ -1,176 +0,0 @@ -#include -#include -#include -#include - -namespace EE { namespace Graphics { - -SINGLETON_DECLARE_IMPLEMENTATION( TextureAtlasManager ) - -TextureAtlasManager::TextureAtlasManager() : - ResourceManagerMulti(), mWarnings( false ) { - add( GlobalTextureAtlas::instance() ); -} - -TextureAtlasManager::~TextureAtlasManager() { - GlobalTextureAtlas::detachSingleton(); -} - -TextureAtlas* TextureAtlasManager::loadFromFile( const std::string& TextureAtlasPath ) { - TextureAtlasLoader loader( TextureAtlasPath ); - - return loader.getTextureAtlas(); -} - -TextureAtlas* TextureAtlasManager::loadFromStream( IOStream& IOS ) { - TextureAtlasLoader loader( IOS ); - - return loader.getTextureAtlas(); -} - -TextureAtlas* TextureAtlasManager::loadFromMemory( const Uint8* Data, const Uint32& DataSize, - const std::string& TextureAtlasName ) { - TextureAtlasLoader loader( Data, DataSize, TextureAtlasName ); - - return loader.getTextureAtlas(); -} - -TextureAtlas* TextureAtlasManager::loadFromPack( Pack* Pack, const std::string& FilePackPath ) { - TextureAtlasLoader loader( Pack, FilePackPath ); - - return loader.getTextureAtlas(); -} - -TextureRegion* TextureAtlasManager::getTextureRegionByName( const std::string& Name ) { - TextureRegion* tTextureRegion = getTextureRegionById( String::hash( Name ) ); - - if ( mWarnings ) { - eePRINTC( NULL == tTextureRegion, - "TextureAtlasManager::getTextureRegionByName TextureRegion '%s' not found\n", - Name.c_str() ); - } - - return tTextureRegion; -} - -TextureRegion* TextureAtlasManager::getTextureRegionById( const String::HashType& Id ) { - TextureAtlas* tSG = NULL; - TextureRegion* tTextureRegion = NULL; - - for ( auto& it : mResources ) { - tSG = it.second; - - tTextureRegion = tSG->getById( Id ); - - if ( NULL != tTextureRegion ) - return tTextureRegion; - } - - return NULL; -} - -void TextureAtlasManager::printResources() { - for ( auto& it : mResources ) - it.second->printNames(); -} - -std::vector -TextureAtlasManager::getTextureRegionsByPatternId( const Uint32& TextureRegionId, - const std::string& extension, - TextureAtlas* SearchInTextureAtlas ) { - TextureRegion* tTextureRegion = NULL; - std::string tName; - - if ( NULL == SearchInTextureAtlas ) - tTextureRegion = getTextureRegionById( TextureRegionId ); - else - tTextureRegion = SearchInTextureAtlas->getById( TextureRegionId ); - - if ( NULL != tTextureRegion ) { - if ( extension.size() ) - tName = String::removeNumbersAtEnd( - FileSystem::fileRemoveExtension( tTextureRegion->getName() ) ) + - extension; - else - tName = tTextureRegion->getName(); - - return getTextureRegionsByPattern( String::removeNumbersAtEnd( tTextureRegion->getName() ), - "", SearchInTextureAtlas ); - } - - return std::vector(); -} - -void TextureAtlasManager::setPrintWarnings( const bool& warn ) { - mWarnings = warn; -} - -const bool& TextureAtlasManager::getPrintWarnings() const { - return mWarnings; -} - -std::vector TextureAtlasManager::getTextureRegionsByPattern( - const std::string& name, const std::string& extension, TextureAtlas* SearchInTextureAtlas ) { - std::vector TextureRegions; - std::string search; - bool found = true; - TextureRegion* tTextureRegion = NULL; - std::string realext = ""; - int c = 0; - int numPadding = 0; - int i; - - if ( extension.size() ) - realext = "." + extension; - - for ( int len = 1; len < 7; len++ ) { - for ( i = 0; i < 2; i++ ) { - std::string formatStr( "%s%0" + String::toString( len ) + "d%s" ); - search = String::format( formatStr.c_str(), name.c_str(), i, realext.c_str() ); - - if ( NULL == SearchInTextureAtlas ) - tTextureRegion = getTextureRegionByName( search ); - else - tTextureRegion = SearchInTextureAtlas->getByName( search ); - - if ( NULL != tTextureRegion ) { - numPadding = len; - - break; - } - } - - if ( 0 != numPadding ) { - break; - } - } - - if ( 0 != numPadding ) { - do { - std::string formatStr( "%s%0" + String::toString( numPadding ) + "d%s" ); - search = String::format( formatStr.c_str(), name.c_str(), c, realext.c_str() ); - - if ( NULL == SearchInTextureAtlas ) - tTextureRegion = getTextureRegionByName( search ); - else - tTextureRegion = SearchInTextureAtlas->getByName( search ); - - if ( NULL != tTextureRegion ) { - TextureRegions.push_back( tTextureRegion ); - - found = true; - } else { - if ( 0 == c ) // if didn't found "00", will search at least for "01" - found = true; - else - found = false; - } - - c++; - } while ( found ); - } - - return TextureRegions; -} - -}} // namespace EE::Graphics diff --git a/src/eepp/graphics/texturedrawable.cpp b/src/eepp/graphics/texturedrawable.cpp new file mode 100644 index 000000000..ef8dbcd5d --- /dev/null +++ b/src/eepp/graphics/texturedrawable.cpp @@ -0,0 +1,55 @@ +#include + +namespace EE { namespace Graphics { + +TextureDrawablePtr TextureDrawable::New( TexturePtr texture ) { + return makeResource( std::move( texture ) ); +} + +TextureDrawable::TextureDrawable( TexturePtr texture ) : + DrawableResource( Drawable::TEXTUREDRAWABLE, texture ? texture->getName() : "" ), + mTexture( std::move( texture ) ) { + if ( mTexture ) { + mTextureChangeConnection = + mTexture->connectResourceChange( [this]( DrawableResource& ) { onResourceChange(); } ); + } +} + +Sizef TextureDrawable::getSize() { + return mTexture ? mTexture->getSize() : Sizef{}; +} + +Sizef TextureDrawable::getPixelsSize() { + return mTexture ? mTexture->getPixelsSize() : Sizef{}; +} + +void TextureDrawable::draw() { + draw( mPosition ); +} + +void TextureDrawable::draw( const Vector2f& position ) { + draw( position, getPixelsSize() ); +} + +void TextureDrawable::draw( const Vector2f& position, const Sizef& size ) { + if ( mTexture ) + mTexture->drawEx( position.x, position.y, size.x, size.y, 0, Vector2f::One, mColor, mColor, + mColor, mColor ); +} + +bool TextureDrawable::isStateful() { + return false; +} + +DrawablePtr TextureDrawable::clone() const { + TextureDrawablePtr instance = New( mTexture ); + instance->setColor( mColor ); + instance->setPosition( mPosition ); + return instance; +} + +const TexturePtr& TextureDrawable::getTexture() const { + return mTexture; +} + +}} // namespace EE::Graphics diff --git a/src/eepp/graphics/texturefactory.cpp b/src/eepp/graphics/texturefactory.cpp index 085ecf95c..33cc5b4d8 100644 --- a/src/eepp/graphics/texturefactory.cpp +++ b/src/eepp/graphics/texturefactory.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -32,7 +31,6 @@ const Texture::CoordinateType& TextureFactory::getLastCoordinateType() const { } TextureFactory::~TextureFactory() { - unloadTextures(); collectReleasedTextures(); diagnoseLiveTexturesAtShutdown(); } @@ -57,29 +55,27 @@ void TextureFactory::TextureDeleter::operator()( Texture* texture ) const noexce eeASSERTM( false, Texture_released_after_TextureFactory_destruction ); } -Texture* TextureFactory::createEmptyTexture( const unsigned int& Width, const unsigned int& Height, - const unsigned int& Channels, - const Color& DefaultColor, const bool& Mipmap, - const Texture::ClampMode& ClampMode, - const bool& CompressTexture, const bool& KeepLocalCopy, - const std::string& Filename ) { +TexturePtr TextureFactory::createEmptyTexture( + const unsigned int& Width, const unsigned int& Height, const unsigned int& Channels, + const Color& DefaultColor, const bool& Mipmap, const Texture::ClampMode& ClampMode, + const bool& CompressTexture, const bool& KeepLocalCopy, const std::string& Filename ) { Image TmpImg( Width, Height, Channels, DefaultColor ); return loadFromPixels( TmpImg.getPixelsPtr(), Width, Height, Channels, Mipmap, ClampMode, CompressTexture, KeepLocalCopy, Filename ); } -Texture* TextureFactory::loadFromPixels( const unsigned char* Pixels, const unsigned int& Width, - const unsigned int& Height, const unsigned int& Channels, - const bool& Mipmap, const Texture::ClampMode& ClampMode, - const bool& CompressTexture, const bool& KeepLocalCopy, - const std::string& FileName ) { +TexturePtr TextureFactory::loadFromPixels( const unsigned char* Pixels, const unsigned int& Width, + const unsigned int& Height, const unsigned int& Channels, + const bool& Mipmap, const Texture::ClampMode& ClampMode, + const bool& CompressTexture, const bool& KeepLocalCopy, + const std::string& FileName ) { TextureLoader myTex( Pixels, Width, Height, Channels, Mipmap, ClampMode, CompressTexture, KeepLocalCopy, FileName ); myTex.load(); return myTex.getTexture(); } -Texture* +TexturePtr TextureFactory::loadFromPack( Pack* Pack, const std::string& FilePackPath, const bool& Mipmap, const Texture::ClampMode& ClampMode, const bool& CompressTexture, const bool& KeepLocalCopy, @@ -90,7 +86,7 @@ TextureFactory::loadFromPack( Pack* Pack, const std::string& FilePackPath, const return myTex.getTexture(); } -Texture* +TexturePtr TextureFactory::loadFromMemory( const unsigned char* ImagePtr, const unsigned int& Size, const bool& Mipmap, const Texture::ClampMode& ClampMode, const bool& CompressTexture, const bool& KeepLocalCopy, @@ -101,7 +97,7 @@ TextureFactory::loadFromMemory( const unsigned char* ImagePtr, const unsigned in return myTex.getTexture(); } -Texture* +TexturePtr TextureFactory::loadFromStream( IOStream& Stream, const bool& Mipmap, const Texture::ClampMode& ClampMode, const bool& CompressTexture, const bool& KeepLocalCopy, @@ -112,7 +108,7 @@ TextureFactory::loadFromStream( IOStream& Stream, const bool& Mipmap, return myTex.getTexture(); } -Texture* +TexturePtr TextureFactory::loadFromFile( const std::string& Filepath, const bool& Mipmap, const Texture::ClampMode& ClampMode, const bool& CompressTexture, const bool& KeepLocalCopy, @@ -123,13 +119,13 @@ TextureFactory::loadFromFile( const std::string& Filepath, const bool& Mipmap, return myTex.getTexture(); } -Texture* TextureFactory::pushTexture( const std::string& Filepath, const Uint32& textureHandle, - const unsigned int& Width, const unsigned int& Height, - const unsigned int& ImgWidth, const unsigned int& ImgHeight, - const bool& Mipmap, const unsigned int& Channels, - const Texture::ClampMode& ClampMode, - const bool& CompressTexture, const bool& LocalCopy, - const Uint32& MemSize ) { +TexturePtr TextureFactory::pushTexture( const std::string& Filepath, const Uint32& textureHandle, + const unsigned int& Width, const unsigned int& Height, + const unsigned int& ImgWidth, const unsigned int& ImgHeight, + const bool& Mipmap, const unsigned int& Channels, + const Texture::ClampMode& ClampMode, + const bool& CompressTexture, const bool& LocalCopy, + const Uint32& MemSize ) { Lock l( *this ); std::string FPath( Filepath ); @@ -144,7 +140,6 @@ Texture* TextureFactory::pushTexture( const std::string& Filepath, const Uint32& Tex->create( textureHandle, Width, Height, ImgWidth, ImgHeight, Mipmap, Channels, FPath, ClampMode, CompressTexture, MemSize ); TextureWeakPtr weakTexture( texture ); - mTextures.emplace( resourceId.value(), std::move( texture ) ); mLiveTextures.emplace( resourceId.value(), LiveTextureRecord{ resourceId, std::move( weakTexture ) } ); mLiveTextureGeneration.fetch_add( 1, std::memory_order_release ); @@ -154,7 +149,7 @@ Texture* TextureFactory::pushTexture( const std::string& Filepath, const Uint32& Tex->unlock( true, false ); } - return Tex; + return texture; } void TextureFactory::bind( const Texture* texture, Texture::CoordinateType coordinateType, @@ -202,58 +197,7 @@ void TextureFactory::bind( const Texture* texture, Texture::CoordinateType coord void TextureFactory::bind( ResourceId textureId, Texture::CoordinateType coordinateType, const Uint32& textureUnit, const bool& forceRebind ) { - bind( getTexture( textureId ), coordinateType, textureUnit, forceRebind ); -} - -void TextureFactory::unloadTextures() { - TextureMap textures; - { - Lock l( *this ); - textures = std::move( mTextures ); - std::fill( mCurrentTexture.begin(), mCurrentTexture.end(), 0 ); - } - - // DrawableResource destruction emits callbacks, so release factory ownership without holding - // the registry/factory mutex. - textures.clear(); - - Log::debug( "Textures Unloaded." ); -} - -bool TextureFactory::remove( ResourceId textureId ) { - TexturePtr texture; - { - Lock l( *this ); - auto it = mTextures.find( textureId.value() ); - if ( it == mTextures.end() ) - return false; - - texture = std::move( it->second ); - mTextures.erase( it ); - resetTextureBinding( texture.get() ); - } - - texture.reset(); - return true; -} - -bool TextureFactory::remove( Texture* texture ) { - TexturePtr removed; - { - Lock l( *this ); - auto it = std::find_if( mTextures.begin(), mTextures.end(), [texture]( const auto& pair ) { - return pair.second.get() == texture; - } ); - if ( it == mTextures.end() ) - return false; - - removed = std::move( it->second ); - mTextures.erase( it ); - resetTextureBinding( removed.get() ); - } - - removed.reset(); - return true; + bind( getTexture( textureId ).get(), coordinateType, textureUnit, forceRebind ); } void TextureFactory::resetTextureBinding( const Texture* texture ) { @@ -277,22 +221,6 @@ void TextureFactory::setCurrentTexture( const int& textureHandle, const Uint32& mCurrentTexture[TextureUnit] = textureHandle; } -std::vector TextureFactory::getTextures() { - Lock l( *this ); - - std::vector textures; - textures.reserve( mTextures.size() ); - - for ( const auto& texture : mTextures ) { - Texture* Tex = texture.second.get(); - - if ( Tex ) - textures.push_back( Tex ); - } - - return textures; -} - TextureRegistrySnapshot TextureFactory::snapshotTextures() { struct LockedTextureRecord { LiveTextureRecord record; @@ -339,9 +267,6 @@ Uint64 TextureFactory::getLiveTextureGeneration() const { } void TextureFactory::queueReleasedTexture( Texture* texture ) { - eeASSERTM( Window::Engine::existsSingleton() && Window::Engine::isMainThread(), - Texture_final_release_must_run_on_the_graphics_thread ); - Lock l( *this ); mReleasedTextures.push_back( texture ); mLiveTextureGeneration.fetch_add( 1, std::memory_order_release ); @@ -394,10 +319,15 @@ void TextureFactory::diagnoseLiveTexturesAtShutdown() { Log::error( "TextureFactory shutdown found %zu externally retained texture(s).", survivors.size() ); + eePRINTL( "TextureFactory shutdown found %zu externally retained texture(s).", + survivors.size() ); for ( const TexturePtr& texture : survivors ) { Log::error( "Texture %llu ('%s') survived shutdown with %zu external owner(s).", static_cast( texture->getTextureId().value() ), texture->getName().c_str(), texture.use_count() - 1 ); + eePRINTL( "Texture %llu ('%s') survived shutdown with %zu external owner(s).", + static_cast( texture->getTextureId().value() ), + texture->getName().c_str(), texture.use_count() - 1 ); texture->deleteTexture(); } @@ -419,61 +349,38 @@ unsigned int TextureFactory::getValidTextureSize( const unsigned int& Size ) { bool TextureFactory::existsId( ResourceId textureId ) { Lock l( *this ); - - return mTextures.find( textureId.value() ) != mTextures.end(); + auto it = mLiveTextures.find( textureId.value() ); + return it != mLiveTextures.end() && !it->second.texture.expired(); } -bool TextureFactory::exists( const Texture* tex ) { +TexturePtr TextureFactory::getTexture( ResourceId textureId ) { Lock l( *this ); - return std::find_if( mTextures.begin(), mTextures.end(), [tex]( const auto& pair ) { - return pair.second.get() == tex; - } ) != mTextures.end(); -} - -Texture* TextureFactory::getTexture( ResourceId textureId ) { - Lock l( *this ); - - auto it = mTextures.find( textureId.value() ); - return it != mTextures.end() ? it->second.get() : NULL; -} - -Texture* TextureFactory::getByName( const std::string& Name ) { - return getByHash( String::hash( Name ) ); + auto it = mLiveTextures.find( textureId.value() ); + return it != mLiveTextures.end() ? it->second.texture.lock() : TexturePtr{}; } Uint32 TextureFactory::getTextureCount() { + purgeExpiredTextures(); Lock l( *this ); - - return (Uint32)mTextures.size(); + return static_cast( mLiveTextures.size() ); } unsigned int TextureFactory::getTextureMemorySize() { - Lock l( *this ); - - std::size_t memorySize = 0; - for ( const auto& texture : mLiveTextures ) { - if ( TexturePtr liveTexture = texture.second.texture.lock() ) - memorySize += liveTexture->getMemSize(); - } - return static_cast( memorySize ); -} - -Texture* TextureFactory::getByHash( const String::HashType& hash ) { - Lock l( *this ); - - Uint64 latestId = 0; - Texture* latestTexture = NULL; - for ( const auto& texture : mTextures ) { - Texture* tTex = texture.second.get(); - - if ( NULL != tTex && texture.first > latestId && tTex->getHashName() == hash ) { - latestId = texture.first; - latestTexture = tTex; + std::vector liveTextures; + { + Lock l( *this ); + liveTextures.reserve( mLiveTextures.size() ); + for ( const auto& record : mLiveTextures ) { + if ( TexturePtr texture = record.second.texture.lock() ) + liveTextures.emplace_back( std::move( texture ) ); } } - return latestTexture; + std::size_t memorySize = 0; + for ( const TexturePtr& texture : liveTextures ) + memorySize += texture->getMemSize(); + return static_cast( memorySize ); } }} // namespace EE::Graphics diff --git a/src/eepp/graphics/textureloader.cpp b/src/eepp/graphics/textureloader.cpp index c31615108..5b0f6951b 100644 --- a/src/eepp/graphics/textureloader.cpp +++ b/src/eepp/graphics/textureloader.cpp @@ -83,9 +83,27 @@ TextureLoader::TextureLoader( const unsigned char* Pixels, const unsigned int& W TextureLoader::~TextureLoader() { eeSAFE_DELETE( mColorKey ); + freePixels(); +} - if ( TEX_LT_PIXELS != mLoadType ) - eeSAFE_FREE( mPixels ); +void TextureLoader::freePixels() { + if ( !mPixels ) { + mPixelsUseSystemFree = false; + return; + } + + if ( TEX_LT_PIXELS == mLoadType ) { + mPixels = nullptr; + mPixelsUseSystemFree = false; + return; + } + + if ( mPixelsUseSystemFree ) + ::free( mPixels ); + else + eeFree( mPixels ); + mPixels = nullptr; + mPixelsUseSystemFree = false; } void TextureLoader::load() { @@ -146,6 +164,7 @@ void TextureLoader::loadFromFile() { mFormatConfiguration ); image.avoidFreeImage( true ); mPixels = image.getPixels(); + mPixelsUseSystemFree = true; mImgWidth = image.getWidth(); mImgHeight = image.getHeight(); mChannels = image.getChannels(); @@ -200,6 +219,7 @@ void TextureLoader::loadFromMemory() { mFormatConfiguration ); image.avoidFreeImage( true ); mPixels = image.getPixels(); + mPixelsUseSystemFree = true; mImgWidth = image.getWidth(); mImgHeight = image.getHeight(); mChannels = image.getChannels(); @@ -257,6 +277,7 @@ void TextureLoader::loadFromStream() { mFormatConfiguration ); image.avoidFreeImage( true ); mPixels = image.getPixels(); + mPixelsUseSystemFree = true; mImgWidth = image.getWidth(); mImgHeight = image.getHeight(); mChannels = image.getChannels(); @@ -432,7 +453,7 @@ const std::string& TextureLoader::getFilepath() const { return mFilepath; } -Texture* TextureLoader::getTexture() const { +const TexturePtr& TextureLoader::getTexture() const { return mTexture; } @@ -445,17 +466,9 @@ void TextureLoader::setFormatConfiguration( mFormatConfiguration = formatConfiguration; } -void TextureLoader::unload() { - if ( mLoaded && mTexture != nullptr ) { - TextureFactory::instance()->remove( mTexture->getTextureId() ); - - reset(); - } -} - void TextureLoader::reset() { - mPixels = nullptr; - mTexture = nullptr; + freePixels(); + mTexture.reset(); mImgWidth = 0; mImgHeight = 0; mWidth = 0; diff --git a/src/eepp/graphics/textureregion.cpp b/src/eepp/graphics/textureregion.cpp index 4137fb8b5..9e9634b12 100644 --- a/src/eepp/graphics/textureregion.cpp +++ b/src/eepp/graphics/textureregion.cpp @@ -10,66 +10,68 @@ using namespace EE::Graphics::Private; namespace EE { namespace Graphics { -TextureRegion* TextureRegion::New() { - return eeNew( TextureRegion, () ); +TextureRegionPtr TextureRegion::New() { + return makeResource(); } -TextureRegion* TextureRegion::New( ResourceId textureId, const std::string& name ) { - return eeNew( TextureRegion, ( TextureFactory::instance()->getTexture( textureId ), name ) ); +TextureRegionPtr TextureRegion::New( ResourceId textureId, const std::string& name ) { + return makeResource( TextureFactory::instance()->getTexture( textureId ), name ); } -TextureRegion* TextureRegion::New( ResourceId textureId, const Rect& srcRect, - const std::string& name ) { - return eeNew( TextureRegion, - ( TextureFactory::instance()->getTexture( textureId ), srcRect, name ) ); +TextureRegionPtr TextureRegion::New( ResourceId textureId, const Rect& srcRect, + const std::string& name ) { + return makeResource( TextureFactory::instance()->getTexture( textureId ), + srcRect, name ); } -TextureRegion* TextureRegion::New( ResourceId textureId, const Rect& srcRect, const Sizef& destSize, - const std::string& name ) { - return eeNew( TextureRegion, ( TextureFactory::instance()->getTexture( textureId ), srcRect, - destSize, name ) ); +TextureRegionPtr TextureRegion::New( ResourceId textureId, const Rect& srcRect, + const Sizef& destSize, const std::string& name ) { + return makeResource( TextureFactory::instance()->getTexture( textureId ), + srcRect, destSize, name ); } -TextureRegion* TextureRegion::New( ResourceId textureId, const Rect& srcRect, const Sizef& destSize, - const Vector2i& offset, const std::string& name ) { - return eeNew( TextureRegion, ( TextureFactory::instance()->getTexture( textureId ), srcRect, - destSize, offset, name ) ); +TextureRegionPtr TextureRegion::New( ResourceId textureId, const Rect& srcRect, + const Sizef& destSize, const Vector2i& offset, + const std::string& name ) { + return makeResource( TextureFactory::instance()->getTexture( textureId ), + srcRect, destSize, offset, name ); } -TextureRegion* TextureRegion::New( Texture* tex, const std::string& name ) { - return eeNew( TextureRegion, ( tex, name ) ); +TextureRegionPtr TextureRegion::New( TexturePtr tex, const std::string& name ) { + return makeResource( std::move( tex ), name ); } -TextureRegion* TextureRegion::New( Texture* tex, const Rect& srcRect, const std::string& name ) { - return eeNew( TextureRegion, ( tex, srcRect, name ) ); +TextureRegionPtr TextureRegion::New( TexturePtr tex, const Rect& srcRect, + const std::string& name ) { + return makeResource( std::move( tex ), srcRect, name ); } -TextureRegion* TextureRegion::New( Texture* tex, const Rect& srcRect, const Sizef& destSize, - const std::string& name ) { - return eeNew( TextureRegion, ( tex, srcRect, destSize, name ) ); +TextureRegionPtr TextureRegion::New( TexturePtr tex, const Rect& srcRect, const Sizef& destSize, + const std::string& name ) { + return makeResource( std::move( tex ), srcRect, destSize, name ); } -TextureRegion* TextureRegion::New( Texture* tex, const Rect& srcRect, const Sizef& destSize, - const Vector2i& offset, const std::string& name ) { - return eeNew( TextureRegion, ( tex, srcRect, destSize, offset, name ) ); +TextureRegionPtr TextureRegion::New( TexturePtr tex, const Rect& srcRect, const Sizef& destSize, + const Vector2i& offset, const std::string& name ) { + return makeResource( std::move( tex ), srcRect, destSize, offset, name ); } TextureRegion::TextureRegion() : DrawableResource( Drawable::TEXTUREREGION ), mPixels( NULL ), mAlphaMask( NULL ), - mTexture( NULL ), + mTexture(), mSrcRect( Rect( 0, 0, 0, 0 ) ), mOriDestSize( 0, 0 ), mDestSize( 0, 0 ), mOffset( 0, 0 ), mPixelDensity( 1 ) {} -TextureRegion::TextureRegion( Texture* tex, const std::string& name ) : +TextureRegion::TextureRegion( TexturePtr tex, const std::string& name ) : DrawableResource( Drawable::TEXTUREREGION, name ), mPixels( NULL ), mAlphaMask( NULL ), - mTexture( tex ), + mTexture( std::move( tex ) ), mSrcRect( Rect( 0, 0, NULL != mTexture ? mTexture->getImageWidth() : 0, NULL != mTexture ? mTexture->getImageHeight() : 0 ) ), mOriDestSize( PixelDensity::dpToPx( mSrcRect.getSize().asFloat() ) ), @@ -77,11 +79,11 @@ TextureRegion::TextureRegion( Texture* tex, const std::string& name ) : mOffset( 0, 0 ), mPixelDensity( 1 ) {} -TextureRegion::TextureRegion( Texture* tex, const Rect& SrcRect, const std::string& name ) : +TextureRegion::TextureRegion( TexturePtr tex, const Rect& SrcRect, const std::string& name ) : DrawableResource( Drawable::TEXTUREREGION, name ), mPixels( NULL ), mAlphaMask( NULL ), - mTexture( tex ), + mTexture( std::move( tex ) ), mSrcRect( SrcRect ), mOriDestSize( PixelDensity::dpToPx( Sizef( (Float)( mSrcRect.Right - mSrcRect.Left ), (Float)( mSrcRect.Bottom - mSrcRect.Top ) ) ) ), @@ -89,24 +91,24 @@ TextureRegion::TextureRegion( Texture* tex, const Rect& SrcRect, const std::stri mOffset( 0, 0 ), mPixelDensity( 1 ) {} -TextureRegion::TextureRegion( Texture* tex, const Rect& SrcRect, const Sizef& DestSize, +TextureRegion::TextureRegion( TexturePtr tex, const Rect& SrcRect, const Sizef& DestSize, const std::string& name ) : DrawableResource( Drawable::TEXTUREREGION, name ), mPixels( NULL ), mAlphaMask( NULL ), - mTexture( tex ), + mTexture( std::move( tex ) ), mSrcRect( SrcRect ), mOriDestSize( DestSize ), mDestSize( DestSize ), mOffset( 0, 0 ), mPixelDensity( 1 ) {} -TextureRegion::TextureRegion( Texture* tex, const Rect& SrcRect, const Sizef& DestSize, +TextureRegion::TextureRegion( TexturePtr tex, const Rect& SrcRect, const Sizef& DestSize, const Vector2i& Offset, const std::string& name ) : DrawableResource( Drawable::TEXTUREREGION, name ), mPixels( NULL ), mAlphaMask( NULL ), - mTexture( tex ), + mTexture( std::move( tex ) ), mSrcRect( SrcRect ), mOriDestSize( DestSize ), mDestSize( DestSize ), @@ -117,12 +119,21 @@ TextureRegion::~TextureRegion() { clearCache(); } +DrawablePtr TextureRegion::clone() const { + auto instance = makeResource( mTexture, mSrcRect, mDestSize, mOffset, mName ); + instance->setOriDestSize( mOriDestSize ); + instance->setPixelDensity( mPixelDensity ); + instance->setColor( mColor ); + instance->setPosition( mPosition ); + return instance; +} + void TextureRegion::setTextureId( ResourceId textureId ) { mTexture = TextureFactory::instance()->getTexture( textureId ); } -void TextureRegion::setTexture( Texture* texture ) { - mTexture = texture; +void TextureRegion::setTexture( TexturePtr texture ) { + mTexture = std::move( texture ); } const Rect& TextureRegion::getSrcRect() const { @@ -199,7 +210,7 @@ void TextureRegion::draw( const Vector2f& position, const Sizef& size ) { mDestSize = oldSize; } -Graphics::Texture* TextureRegion::getTexture() { +const TexturePtr& TextureRegion::getTexture() const { return mTexture; } diff --git a/src/eepp/graphics/triangledrawable.cpp b/src/eepp/graphics/triangledrawable.cpp index c63975dc4..1c77650f0 100644 --- a/src/eepp/graphics/triangledrawable.cpp +++ b/src/eepp/graphics/triangledrawable.cpp @@ -18,6 +18,22 @@ TriangleDrawable::TriangleDrawable( const Vector2f& position, const Sizef& size mPosition = position; } +DrawablePtr TriangleDrawable::clone() const { + auto instance = makeResource( mPosition, mSize ); + instance->mTriangle = mTriangle; + instance->mComputedTriangle = mComputedTriangle; + instance->mColors[0] = mColors[0]; + instance->mColors[1] = mColors[1]; + instance->mColors[2] = mColors[2]; + instance->mCustomColors = mCustomColors; + instance->mFillMode = mFillMode; + instance->mBlendMode = mBlendMode; + instance->mLineWidth = mLineWidth; + instance->mSmooth = mSmooth; + instance->mColor = mColor; + return instance; +} + Sizef TriangleDrawable::getSize() { return mTriangle.getSize(); } diff --git a/src/eepp/graphics/vertexbuffer.cpp b/src/eepp/graphics/vertexbuffer.cpp index dd45b401e..96b8bdb08 100644 --- a/src/eepp/graphics/vertexbuffer.cpp +++ b/src/eepp/graphics/vertexbuffer.cpp @@ -7,23 +7,28 @@ using namespace EE::Graphics::Private; namespace EE { namespace Graphics { -VertexBuffer* VertexBuffer::New( const Uint32& vertexFlags, PrimitiveType drawType, - const Int32& reserveVertexSize, const Int32& reserveIndexSize, - VertexBufferUsageType usageType ) { +VertexBufferUniquePtr VertexBuffer::New( const Uint32& vertexFlags, PrimitiveType drawType, + const Int32& reserveVertexSize, + const Int32& reserveIndexSize, + VertexBufferUsageType usageType ) { if ( GLi->isExtension( EEGL_ARB_vertex_buffer_object ) || GLi->version() == GLv_3CP ) - return eeNew( VertexBufferVBO, - ( vertexFlags, drawType, reserveVertexSize, reserveIndexSize, usageType ) ); + return VertexBufferUniquePtr( + eeNew( VertexBufferVBO, + ( vertexFlags, drawType, reserveVertexSize, reserveIndexSize, usageType ) ) ); - return eeNew( VertexBufferOGL, - ( vertexFlags, drawType, reserveVertexSize, reserveIndexSize, usageType ) ); + return VertexBufferUniquePtr( + eeNew( VertexBufferOGL, + ( vertexFlags, drawType, reserveVertexSize, reserveIndexSize, usageType ) ) ); } -VertexBuffer* VertexBuffer::NewVertexArray( const Uint32& vertexFlags, PrimitiveType drawType, - const Int32& reserveVertexSize, - const Int32& reserveIndexSize, - VertexBufferUsageType usageType ) { - return eeNew( VertexBufferOGL, - ( vertexFlags, drawType, reserveVertexSize, reserveIndexSize, usageType ) ); +VertexBufferUniquePtr VertexBuffer::NewVertexArray( const Uint32& vertexFlags, + PrimitiveType drawType, + const Int32& reserveVertexSize, + const Int32& reserveIndexSize, + VertexBufferUsageType usageType ) { + return VertexBufferUniquePtr( + eeNew( VertexBufferOGL, + ( vertexFlags, drawType, reserveVertexSize, reserveIndexSize, usageType ) ) ); } VertexBuffer::VertexBuffer( const Uint32& vertexFlags, PrimitiveType drawType, @@ -48,11 +53,12 @@ VertexBuffer::VertexBuffer( const Uint32& vertexFlags, PrimitiveType drawType, mIndexArray.reserve( reserveIndexSize ); } - VertexBufferManager::instance()->add( this ); + VertexBufferRegistry::instance()->add( this ); } VertexBuffer::~VertexBuffer() { - VertexBufferManager::instance()->remove( this ); + if ( VertexBufferRegistry::existsSingleton() ) + VertexBufferRegistry::instance()->remove( this ); } void VertexBuffer::addVertex( const Uint32& type, const Vector2f& vertex ) { diff --git a/src/eepp/graphics/vertexbuffermanager.cpp b/src/eepp/graphics/vertexbuffermanager.cpp index 937bf277c..4fce728af 100644 --- a/src/eepp/graphics/vertexbuffermanager.cpp +++ b/src/eepp/graphics/vertexbuffermanager.cpp @@ -2,13 +2,13 @@ namespace EE { namespace Graphics { namespace Private { -SINGLETON_DECLARE_IMPLEMENTATION( VertexBufferManager ) +SINGLETON_DECLARE_IMPLEMENTATION( VertexBufferRegistry ) -VertexBufferManager::VertexBufferManager() {} +VertexBufferRegistry::VertexBufferRegistry() {} -VertexBufferManager::~VertexBufferManager() {} +VertexBufferRegistry::~VertexBufferRegistry() {} -void VertexBufferManager::reload() { +void VertexBufferRegistry::reload() { for ( auto& vb : mResources ) vb->reload(); } diff --git a/src/eepp/scene/scenenode.cpp b/src/eepp/scene/scenenode.cpp index 77a158e75..392c40ea3 100644 --- a/src/eepp/scene/scenenode.cpp +++ b/src/eepp/scene/scenenode.cpp @@ -18,7 +18,7 @@ SceneNode::SceneNode( EE::Window::Window* window ) : Node(), mWindow( window ), mActionManager( ActionManager::New() ), - mFrameBuffer( NULL ), + mFrameBuffer( nullptr ), mEventDispatcher( NULL ), mFrameBufferBound( false ), mUseInvalidation( false ), @@ -65,7 +65,7 @@ SceneNode::~SceneNode() { if ( !mParentNode ) eeSAFE_DELETE( mEventDispatcher ); - eeSAFE_DELETE( mFrameBuffer ); + mFrameBuffer.reset(); } void SceneNode::enableFrameBuffer() { @@ -74,7 +74,7 @@ void SceneNode::enableFrameBuffer() { } void SceneNode::disableFrameBuffer() { - eeSAFE_DELETE( mFrameBuffer ); + mFrameBuffer.reset(); writeNodeFlag( NODE_FLAG_FRAME_BUFFER, 0 ); } @@ -260,7 +260,7 @@ Sizei SceneNode::getFrameBufferSize() { void SceneNode::createFrameBuffer() { writeNodeFlag( NODE_FLAG_FRAME_BUFFER, 1 ); - eeSAFE_DELETE( mFrameBuffer ); + mFrameBuffer.reset(); Sizei fboSize( getFrameBufferSize() ); if ( fboSize.getWidth() < 1 ) fboSize.setWidth( 1 ); @@ -271,7 +271,7 @@ void SceneNode::createFrameBuffer() { // Frame buffer failed to create? if ( mFrameBuffer == nullptr || !mFrameBuffer->created() ) { - eeSAFE_DELETE( mFrameBuffer ); + mFrameBuffer.reset(); } } @@ -351,7 +351,7 @@ void SceneNode::resizeNode( EE::Window::Window* ) { } FrameBuffer* SceneNode::getFrameBuffer() const { - return mFrameBuffer; + return mFrameBuffer.get(); } void SceneNode::setEventDispatcher( EventDispatcher* eventDispatcher ) { diff --git a/src/eepp/system/sys.cpp b/src/eepp/system/sys.cpp index 59ae9cfae..45c4841e0 100644 --- a/src/eepp/system/sys.cpp +++ b/src/eepp/system/sys.cpp @@ -2257,7 +2257,9 @@ static bool _isOSUsingDarkColorScheme() { #elif EE_PLATFORM == EE_PLATFORM_EMSCRIPTEN // Executes JavaScript: window.matchMedia('(prefers-color-scheme: dark)').matches return EM_ASM_INT( { - if ( typeof window != = 'undefined' && window.matchMedia ) { + // EM_ASM stringifies C/C++ preprocessing tokens. JavaScript's !== is split into + // "!= =" and becomes invalid, so use != for this typeof string comparison. + if ( typeof window != 'undefined' && window.matchMedia ) { return window.matchMedia( '(prefers-color-scheme: dark)' ).matches ? 1 : 0; } return 0; diff --git a/src/eepp/ui/abstract/uiabstracttableview.cpp b/src/eepp/ui/abstract/uiabstracttableview.cpp index dd6e331a2..2f1818200 100644 --- a/src/eepp/ui/abstract/uiabstracttableview.cpp +++ b/src/eepp/ui/abstract/uiabstracttableview.cpp @@ -667,7 +667,7 @@ UIWidget* UIAbstractTableView::updateCell( const Vector2& posIndex, const cell->setIcon( icon.asDrawable() ); } else if ( icon.is( Variant::Type::Icon ) && icon.asIcon() ) { isVisible = true; - cell->setIcon( icon.asIcon()->getSize( mIconSize ) ); + cell->setIcon( icon.asIcon()->createDrawable( mIconSize ) ); } if ( cell->hasIcon() ) cell->getIcon()->setVisible( isVisible ); @@ -891,7 +891,7 @@ void UIAbstractTableView::onSortColumn( const size_t& colIndex ) { UIImage* image = columnData( model->keyColumn() ).widget->getExtraInnerWidget()->asType(); image->setForegroundFillEnabled( false ); - image->setDrawable( nullptr ); + image->setDrawable( DrawablePtr{} ); } SortOrder sortOrder = model->sortOrder() == SortOrder::Ascending ? SortOrder::Descending : SortOrder::Ascending; @@ -904,10 +904,10 @@ void UIAbstractTableView::onSortColumn( const size_t& colIndex ) { if ( image->getForeground() ) image->getForeground()->setAlpha( 255 ); if ( image && image->getForeground() == nullptr ) { - Drawable* icon = mUISceneNode->findIconDrawable( + DrawablePtr icon = mUISceneNode->findIconDrawable( sortOrder == SortOrder::Ascending ? "arrow-down" : "arrow-up", mSortIconSize ); if ( icon ) - image->setDrawable( icon ); + image->setDrawable( std::move( icon ) ); } model->sort( colIndex, sortOrder ); } diff --git a/src/eepp/ui/css/drawableimageparser.cpp b/src/eepp/ui/css/drawableimageparser.cpp index f17c1e440..e6d75dc8c 100644 --- a/src/eepp/ui/css/drawableimageparser.cpp +++ b/src/eepp/ui/css/drawableimageparser.cpp @@ -1,11 +1,9 @@ #include #include #include -#include -#include #include +#include #include -#include #include #include #include @@ -16,7 +14,6 @@ #include using namespace EE::Graphics; -using namespace EE::Scene; namespace EE { namespace UI { namespace CSS { @@ -153,26 +150,22 @@ bool DrawableImageParser::exists( const std::string& name ) const { return mFuncs.find( name ) != mFuncs.end(); } -Drawable* DrawableImageParser::createDrawable( const std::string& value, const Sizef& size, - bool& ownIt, UINode* node ) { +DrawablePtr DrawableImageParser::createDrawable( const std::string& value, const Sizef& size, + UINode* node ) { FunctionString functionType = FunctionString::parse( value ); - Drawable* res = NULL; - ownIt = false; if ( "none" == value ) - return NULL; + return {}; if ( !functionType.isEmpty() ) { if ( exists( functionType.getName() ) ) - return mFuncs[functionType.getName()]( functionType, size, ownIt, node ); - } else if ( NULL != ( res = DrawableSearcher::searchByName( - value, false, node->getUISceneNode()->getReferer() ) ) ) { - if ( res->getDrawableType() == Drawable::SPRITE ) - ownIt = true; - return res; + return mFuncs[functionType.getName()]( functionType, size, node ); + } else if ( DrawablePtr drawable = + node->getUISceneNode()->getDrawableResolver().resolve( value ) ) { + return drawable; } - return res; + return {}; } void DrawableImageParser::addParser( const std::string& name, @@ -189,11 +182,11 @@ void DrawableImageParser::addParser( const std::string& name, void DrawableImageParser::registerBaseParsers() { // Shared parsing logic for linear-gradient and repeating-linear-gradient - auto parseGradient = []( const FunctionString& functionType, bool& ownIt, UINode* node, - bool repeating ) -> Drawable* { + auto parseGradient = []( const FunctionString& functionType, UINode* node, + bool repeating ) -> DrawablePtr { const auto& params( functionType.getParameters() ); if ( params.size() < 2 ) - return NULL; + return {}; size_t paramIdx = 0; Float angle = 180.f; /* default: to bottom */ @@ -331,7 +324,7 @@ void DrawableImageParser::registerBaseParsers() { colorStopCount++; } if ( colorStopCount < 2 ) - return NULL; + return {}; } // Sort by position. Hints with the same position as a color stop are @@ -450,34 +443,33 @@ void DrawableImageParser::registerBaseParsers() { } if ( stops.size() < 2 ) - return NULL; + return {}; - LinearGradientDrawable* drawable = - repeating ? LinearGradientDrawable::NewRepeating() : LinearGradientDrawable::New(); + auto drawable = makeResource( + repeating ? Drawable::REPEATINGLINEARGRADIENT : Drawable::LINEARGRADIENT ); drawable->setColorStops( std::move( stops ) ); drawable->setAngle( angle ); - ownIt = true; return drawable; }; mFuncs["linear-gradient"] = [parseGradient]( const FunctionString& functionType, - const Sizef& /*size*/, bool& ownIt, - UINode* node ) -> Drawable* { - return parseGradient( functionType, ownIt, node, false ); + const Sizef& /*size*/, + UINode* node ) -> DrawablePtr { + return parseGradient( functionType, node, false ); }; mFuncs["repeating-linear-gradient"] = [parseGradient]( const FunctionString& functionType, - const Sizef& /*size*/, bool& ownIt, - UINode* node ) -> Drawable* { - return parseGradient( functionType, ownIt, node, true ); + const Sizef& /*size*/, + UINode* node ) -> DrawablePtr { + return parseGradient( functionType, node, true ); }; // Shared parsing logic for radial-gradient and repeating-radial-gradient - auto parseRadialGradient = []( const FunctionString& functionType, bool& ownIt, UINode* node, - bool repeating ) -> Drawable* { + auto parseRadialGradient = []( const FunctionString& functionType, UINode* node, + bool repeating ) -> DrawablePtr { const auto& params( functionType.getParameters() ); if ( params.size() < 2 ) - return NULL; + return {}; size_t paramIdx = 0; RadialGradientDrawable::ShapeType shape = RadialGradientDrawable::CIRCLE; @@ -557,7 +549,7 @@ void DrawableImageParser::registerBaseParsers() { colorStopCount++; } if ( colorStopCount < 2 ) - return NULL; + return {}; } std::sort( gradientStops.begin(), gradientStops.end(), @@ -671,37 +663,36 @@ void DrawableImageParser::registerBaseParsers() { } if ( stops.size() < 2 ) - return NULL; + return {}; - RadialGradientDrawable* drawable = - repeating ? RadialGradientDrawable::NewRepeating() : RadialGradientDrawable::New(); + auto drawable = makeResource( + repeating ? Drawable::REPEATINGRADIALGRADIENT : Drawable::RADIALGRADIENT ); drawable->setColorStops( std::move( stops ) ); drawable->setShape( shape ); drawable->setExtent( extent ); drawable->setCenter( center ); - ownIt = true; return drawable; }; mFuncs["radial-gradient"] = [parseRadialGradient]( const FunctionString& functionType, - const Sizef& /*size*/, bool& ownIt, - UINode* node ) -> Drawable* { - return parseRadialGradient( functionType, ownIt, node, false ); + const Sizef& /*size*/, + UINode* node ) -> DrawablePtr { + return parseRadialGradient( functionType, node, false ); }; mFuncs["repeating-radial-gradient"] = [parseRadialGradient]( const FunctionString& functionType, - const Sizef& /*size*/, bool& ownIt, - UINode* node ) -> Drawable* { - return parseRadialGradient( functionType, ownIt, node, true ); + const Sizef& /*size*/, + UINode* node ) -> DrawablePtr { + return parseRadialGradient( functionType, node, true ); }; - mFuncs["circle"] = []( const FunctionString& functionType, const Sizef& size, bool& ownIt, - UINode* node ) -> Drawable* { + mFuncs["circle"] = []( const FunctionString& functionType, const Sizef& size, + UINode* node ) -> DrawablePtr { if ( functionType.getParameters().size() < 1 ) { - return NULL; + return {}; } - CircleDrawable* drawable = CircleDrawable::New(); + auto drawable = makeResource(); const auto& params( functionType.getParameters() ); @@ -722,17 +713,16 @@ void DrawableImageParser::registerBaseParsers() { } drawable->setOffset( drawable->getSize() / 2.f ); - ownIt = true; return drawable; }; - mFuncs["rectangle"] = []( const FunctionString& functionType, const Sizef& size, bool& ownIt, - UINode* node ) -> Drawable* { + mFuncs["rectangle"] = []( const FunctionString& functionType, const Sizef& size, + UINode* node ) -> DrawablePtr { if ( functionType.getParameters().size() < 1 ) { - return NULL; + return {}; } - RectangleDrawable* drawable = RectangleDrawable::New(); + auto drawable = makeResource(); RectColors rectColors; std::vector colors; @@ -779,22 +769,19 @@ void DrawableImageParser::registerBaseParsers() { rectColors.BottomRight = colors[2]; rectColors.TopRight = colors[3]; drawable->setRectColors( rectColors ); - ownIt = true; return drawable; - } else { - eeSAFE_DELETE( drawable ); } - return drawable; + return {}; }; - mFuncs["triangle"] = []( const FunctionString& functionType, const Sizef& size, bool& ownIt, - UINode* node ) -> Drawable* { + mFuncs["triangle"] = []( const FunctionString& functionType, const Sizef& size, + UINode* node ) -> DrawablePtr { if ( functionType.getParameters().size() < 2 ) { - return NULL; + return {}; } - TriangleDrawable* drawable = TriangleDrawable::New(); + auto drawable = makeResource(); std::vector colors; std::vector vertices; @@ -849,22 +836,19 @@ void DrawableImageParser::registerBaseParsers() { } drawable->setTriangle( triangle ); - ownIt = true; return drawable; - } else { - eeSAFE_DELETE( drawable ); } - return drawable; + return {}; }; - mFuncs["poly"] = []( const FunctionString& functionType, const Sizef& size, bool& ownIt, - UINode* node ) -> Drawable* { + mFuncs["poly"] = []( const FunctionString& functionType, const Sizef& size, + UINode* node ) -> DrawablePtr { if ( functionType.getParameters().size() < 2 ) { - return NULL; + return {}; } - ConvexShapeDrawable* drawable = ConvexShapeDrawable::New(); + auto drawable = makeResource(); std::vector colors; std::vector vertices; @@ -908,25 +892,22 @@ void DrawableImageParser::registerBaseParsers() { drawable->addPoint( vertices[i], colors[i % colors.size()] ); } - ownIt = true; return drawable; - } else { - eeSAFE_DELETE( drawable ); } - return drawable; + return {}; }; - mFuncs["url"] = []( const FunctionString& functionType, const Sizef& /*size*/, bool& /*ownIt*/, - UINode* node ) -> Drawable* { + mFuncs["url"] = []( const FunctionString& functionType, const Sizef& /*size*/, + UINode* node ) -> DrawablePtr { if ( functionType.getParameters().size() < 1 ) - return NULL; + return {}; const auto& param = functionType.getParameters().at( 0 ); if ( functionType.getName() == "url" && !param.empty() && param[0] != '@' && !String::startsWith( param, "data:image/" ) ) { - return DrawableSearcher::searchByName( - node->getUISceneNode()->solveRelativePath( param ).toString(), false, - node->getUISceneNode()->getReferer() ); + DrawablePtr drawable = node->getUISceneNode()->getDrawableResolver().resolve( + node->getUISceneNode()->solveRelativePath( param ).toString() ); + return drawable; } else if ( functionType.getParameters().size() > 1 && String::startsWith( param, "data:image/" ) ) { auto cparam = functionType.getParameters().at( 0 ); @@ -934,15 +915,16 @@ void DrawableImageParser::registerBaseParsers() { cparam += ','; cparam += functionType.getParameters().at( i ); } - return DrawableSearcher::searchByName( cparam, false, - node->getUISceneNode()->getReferer() ); + DrawablePtr drawable = node->getUISceneNode()->getDrawableResolver().resolve( cparam ); + return drawable; } - return DrawableSearcher::searchByName( param, false, node->getUISceneNode()->getReferer() ); + DrawablePtr drawable = node->getUISceneNode()->getDrawableResolver().resolve( param ); + return drawable; }; - mFuncs["icon"] = []( const FunctionString& functionType, const Sizef& size, bool&, - UINode* node ) -> Drawable* { - auto* uiScene = SceneManager::instance()->getUISceneNode(); + mFuncs["icon"] = []( const FunctionString& functionType, const Sizef& size, + UINode* node ) -> DrawablePtr { + auto* uiScene = node->getUISceneNode(); const auto& params = functionType.getParameters(); if ( params.size() < 2 ) return nullptr; @@ -951,12 +933,12 @@ void DrawableImageParser::registerBaseParsers() { node->convertLength( length, size.getWidth() ) ); }; - mFuncs["glyph"] = []( const FunctionString& functionType, const Sizef& size, bool&, - UINode* node ) -> Drawable* { + mFuncs["glyph"] = []( const FunctionString& functionType, const Sizef& size, + UINode* node ) -> DrawablePtr { const auto& params = functionType.getParameters(); if ( params.size() < 3 ) return nullptr; - Font* font = FontManager::instance()->getByName( params[0] ); + Font* font = node->getUISceneNode()->getResourceScope()->findFont( params[0] ).get(); if ( font == nullptr ) return nullptr; Uint32 codePoint = 0; @@ -972,8 +954,9 @@ void DrawableImageParser::registerBaseParsers() { } else if ( String::fromString( value, buffer ) ) { codePoint = value; } - return font->getGlyphDrawable( codePoint, - node->convertLength( params[1], size.getWidth() ) ); + Drawable* drawable = + font->getGlyphDrawable( codePoint, node->convertLength( params[1], size.getWidth() ) ); + return drawable ? drawable->clone() : DrawablePtr{}; }; } diff --git a/src/eepp/ui/drawableresolver.cpp b/src/eepp/ui/drawableresolver.cpp new file mode 100644 index 000000000..cd3b1866e --- /dev/null +++ b/src/eepp/ui/drawableresolver.cpp @@ -0,0 +1,183 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace EE::Graphics; +using namespace EE::Network; +using namespace EE::Window; + +namespace EE { namespace UI { + +namespace { + +DrawablePtr parseDataURI( const std::string& name, ResourceScope& resourceScope ) { + std::string hash = MD5::fromString( name ).toHexString(); + TexturePtr texture = resourceScope.findTexture( hash ); + std::string::size_type formatAndEncSep; + if ( !texture && ( formatAndEncSep = name.find_first_of( ',' ) ) != std::string::npos ) { + std::string decodingType = "urldecode"; + std::string mediaType = name.substr( 0, formatAndEncSep ); + std::string formatName; + auto parts = String::split( mediaType, ';' ); + if ( parts.empty() ) + return {}; + auto formatNamePos = parts[0].find_first_of( '/' ); + if ( formatNamePos + 1 < mediaType.size() ) + formatName = parts[0].substr( formatNamePos + 1 ); + for ( size_t i = 1; i < parts.size(); ++i ) { + if ( parts[i] == "base64" ) { + decodingType = parts[i]; + break; + } + } + + TexturePtr decodedTexture; + if ( !formatName.empty() && + ( Image::isImageExtension( "." + formatName ) || formatName == "svg+xml" ) ) { + Image::FormatConfiguration format; + format.svgScale( PixelDensity::getPixelDensity() ); + std::string_view encoded = std::string_view{ name }.substr( formatAndEncSep + 1 ); + if ( decodingType == "base64" ) { + std::string buffer; + if ( Base64::decode( encoded, buffer ) > 0 ) { + decodedTexture = TextureFactory::instance()->loadFromMemory( + reinterpret_cast( buffer.data() ), buffer.size(), + false, Texture::ClampMode::ClampToEdge, false, false, format ); + } + } else { + std::string decoded = URI::decode( encoded ); + if ( !decoded.empty() ) { + decodedTexture = TextureFactory::instance()->loadFromMemory( + reinterpret_cast( decoded.data() ), decoded.size(), + false, Texture::ClampMode::ClampToEdge, false, false, format ); + } + } + } + + if ( decodedTexture ) { + decodedTexture->setName( hash ); + resourceScope.publishLocal( hash, decodedTexture ); + texture = std::move( decodedTexture ); + } + } + + return texture ? texture->clone() : DrawablePtr{}; +} + +} // namespace + +DrawableResolver::DrawableResolver( UISceneNode& sceneNode ) : mSceneNode( &sceneNode ) {} + +DrawableResolver::DrawableResolver( ResourceScope& resourceScope ) : + mResourceScope( &resourceScope ) {} + +DrawablePtr DrawableResolver::resolve( const std::string& name, bool firstSearchSprite ) const { + DrawablePtr drawable; + if ( name.empty() ) + return drawable; + + ResourceScope& resourceScope = mSceneNode ? *mSceneNode->getResourceScope() : *mResourceScope; + if ( String::startsWith( name, "file://" ) ) { + std::string filePath = name.substr( 7 ); +#if EE_PLATFORM == EE_PLATFORM_WIN + if ( filePath.size() >= 3 && filePath[0] == '/' && String::isLetter( filePath[1] ) && + filePath[2] == ':' ) + filePath.erase( 0, 1 ); +#endif + FileSystem::filePathRemoveProcessPath( filePath ); + TexturePtr texture = resourceScope.findTexture( filePath ); + if ( !texture ) { + texture = TextureFactory::instance()->loadFromFile( filePath ); + if ( texture ) + resourceScope.publishLocal( filePath, texture ); + } + drawable = texture ? texture->clone() : DrawablePtr{}; + } else if ( String::startsWith( name, "http://" ) || String::startsWith( name, "https://" ) ) { + TexturePtr texture = resourceScope.findTexture( name ); + if ( mSceneNode && Engine::instance()->isSharedGLContextEnabled() ) { + if ( !texture ) { + WebResourceRequest request; + request.uri = URI( name ); + request.kind = WebResourceKind::Image; + texture = mSceneNode->requestWebTexture( + std::move( request ), [name]( const WebResourceResult& result ) { + if ( !result.success ) + Log::debug( "DrawableResolver::resolve: could not download image: %s. " + "Error: %d\n%s", + name, result.status, result.error ); + } ); + } + } + if ( !mSceneNode && !texture && Engine::instance()->isSharedGLContextEnabled() ) { + texture = TextureFactory::instance()->createEmptyTexture( + 1, 1, 4, Color::Transparent, false, Texture::ClampMode::ClampToEdge, false, false, + name ); + if ( texture ) + resourceScope.publishLocal( name, texture ); + + Http::Request::FieldTable headers; + if ( mSceneNode && !mSceneNode->getReferer().empty() ) + headers["referer"] = mSceneNode->getReferer().toString(); + Http::getAsync( + [texture, name]( const Http&, Http::Request&, Http::Response& response ) { + if ( response.isOK() && !response.getBody().empty() ) { + Image image( reinterpret_cast( response.getBody().data() ), + response.getBody().size() ); + if ( image.getPixels() ) + texture->replace( &image ); + } else { + Log::debug( + "DrawableResolver::resolve: could not download image: %s. Error: " + "%d\n%s", + name, response.getStatus(), response.getBody() ); + } + }, + URI( name ), Seconds( 5 ), {}, headers ); + } + drawable = texture ? texture->clone() : DrawablePtr{}; + } else if ( String::startsWith( name, "data:image/" ) ) { + drawable = parseDataURI( name, resourceScope ); + } else { + drawable = resourceScope.findDrawable( name, firstSearchSprite ); + } + + if ( !drawable && mPrintWarnings ) + Log::warning( "DrawableResolver::resolve: \"%s\" not found", name.c_str() ); + return drawable; +} + +DrawablePtr DrawableResolver::resolveById( ResourceNameHash hash ) const { + ResourceScope& resourceScope = mSceneNode ? *mSceneNode->getResourceScope() : *mResourceScope; + DrawablePtr drawable = resourceScope.findDrawable( hash ); + if ( !drawable && mPrintWarnings ) + Log::warning( "DrawableResolver::resolveById: \"%llu\" not found", + static_cast( hash.value() ) ); + return drawable; +} + +DrawablePtr DrawableResolver::resolveById( String::HashType legacyHash ) const { + ResourceScope& resourceScope = mSceneNode ? *mSceneNode->getResourceScope() : *mResourceScope; + DrawablePtr drawable = resourceScope.findDrawable( legacyHash ); + if ( !drawable && mPrintWarnings ) + Log::warning( "DrawableResolver::resolveById: legacy hash \"%u\" not found", legacyHash ); + return drawable; +} + +void DrawableResolver::setPrintWarnings( bool printWarnings ) { + mPrintWarnings = printWarnings; +} + +bool DrawableResolver::getPrintWarnings() const { + return mPrintWarnings; +} + +}} // namespace EE::UI diff --git a/src/eepp/ui/iconmanager.cpp b/src/eepp/ui/iconmanager.cpp index 94e8ce8f7..7368d3e81 100644 --- a/src/eepp/ui/iconmanager.cpp +++ b/src/eepp/ui/iconmanager.cpp @@ -6,10 +6,11 @@ namespace EE { namespace UI { using IconPair = std::pair; -UIIconTheme* IconManager::init( const std::string& iconThemeName, FontTrueType* remixIconFont, - FontTrueType* noniconFont, FontTrueType* codIconFont ) { +ResourcePtr IconManager::init( const std::string& iconThemeName, + FontTrueType* remixIconFont, FontTrueType* noniconFont, + FontTrueType* codIconFont ) { - UIIconTheme* iconTheme = UIIconTheme::New( iconThemeName ); + auto iconTheme = UIIconTheme::New( iconThemeName ); if ( remixIconFont && remixIconFont->loaded() ) { remixIconFont->setIsEmojiFont( true ); diff --git a/src/eepp/ui/lineargradientdrawable.cpp b/src/eepp/ui/lineargradientdrawable.cpp index 3c42db598..268ebceb9 100644 --- a/src/eepp/ui/lineargradientdrawable.cpp +++ b/src/eepp/ui/lineargradientdrawable.cpp @@ -18,6 +18,16 @@ LinearGradientDrawable* LinearGradientDrawable::NewRepeating() { LinearGradientDrawable::LinearGradientDrawable( Graphics::Drawable::Type drawableType ) : Drawable( drawableType ) {} +DrawablePtr LinearGradientDrawable::clone() const { + auto instance = makeResource( mDrawableType ); + instance->mColorStops = mColorStops; + instance->mAngle = mAngle; + instance->mSize = mSize; + instance->mColor = mColor; + instance->mPosition = mPosition; + return instance; +} + Sizef LinearGradientDrawable::getSize() { return mSize; } diff --git a/src/eepp/ui/radialgradientdrawable.cpp b/src/eepp/ui/radialgradientdrawable.cpp index b506e35d0..a9b80792b 100644 --- a/src/eepp/ui/radialgradientdrawable.cpp +++ b/src/eepp/ui/radialgradientdrawable.cpp @@ -18,6 +18,18 @@ RadialGradientDrawable* RadialGradientDrawable::NewRepeating() { RadialGradientDrawable::RadialGradientDrawable( Graphics::Drawable::Type drawableType ) : Drawable( drawableType ) {} +DrawablePtr RadialGradientDrawable::clone() const { + auto instance = makeResource( mDrawableType ); + instance->mColorStops = mColorStops; + instance->mShape = mShape; + instance->mExtent = mExtent; + instance->mCenter = mCenter; + instance->mSize = mSize; + instance->mColor = mColor; + instance->mPosition = mPosition; + return instance; +} + Sizef RadialGradientDrawable::getSize() { return mSize; } @@ -75,7 +87,6 @@ void RadialGradientDrawable::draw( const Vector2f& position, const Sizef& size ) std::sort( stops.begin(), stops.end(), []( const ColorStop& a, const ColorStop& b ) { return a.value < b.value; } ); - const int SEGMENTS = 48; Float angleStep = 2.f * EE_PI / (Float)SEGMENTS; @@ -133,16 +144,14 @@ void RadialGradientDrawable::draw( const Vector2f& position, const Sizef& size ) Float frac1 = ( clip1 - p0 ) / bw; const Color& sc0 = stops[i].color; const Color& sc1 = stops[i + 1].color; - Color cc0( - (Uint8)( (Float)sc0.r + frac0 * (Float)( sc1.r - sc0.r ) ), - (Uint8)( (Float)sc0.g + frac0 * (Float)( sc1.g - sc0.g ) ), - (Uint8)( (Float)sc0.b + frac0 * (Float)( sc1.b - sc0.b ) ), - (Uint8)( (Float)sc0.a + frac0 * (Float)( sc1.a - sc0.a ) ) ); - Color cc1( - (Uint8)( (Float)sc0.r + frac1 * (Float)( sc1.r - sc0.r ) ), - (Uint8)( (Float)sc0.g + frac1 * (Float)( sc1.g - sc0.g ) ), - (Uint8)( (Float)sc0.b + frac1 * (Float)( sc1.b - sc0.b ) ), - (Uint8)( (Float)sc0.a + frac1 * (Float)( sc1.a - sc0.a ) ) ); + Color cc0( (Uint8)( (Float)sc0.r + frac0 * (Float)( sc1.r - sc0.r ) ), + (Uint8)( (Float)sc0.g + frac0 * (Float)( sc1.g - sc0.g ) ), + (Uint8)( (Float)sc0.b + frac0 * (Float)( sc1.b - sc0.b ) ), + (Uint8)( (Float)sc0.a + frac0 * (Float)( sc1.a - sc0.a ) ) ); + Color cc1( (Uint8)( (Float)sc0.r + frac1 * (Float)( sc1.r - sc0.r ) ), + (Uint8)( (Float)sc0.g + frac1 * (Float)( sc1.g - sc0.g ) ), + (Uint8)( (Float)sc0.b + frac1 * (Float)( sc1.b - sc0.b ) ), + (Uint8)( (Float)sc0.a + frac1 * (Float)( sc1.a - sc0.a ) ) ); Color fc0 = ( mColor.a == 255 ) ? cc0 : Color( cc0 ).blendAlpha( mColor.a ); Color fc1 = ( mColor.a == 255 ) ? cc1 : Color( cc1 ).blendAlpha( mColor.a ); @@ -175,9 +184,8 @@ void RadialGradientDrawable::draw( const Vector2f& position, const Sizef& size ) Float cj = cosVals[j], sj = sinVals[j]; Float cj1 = cosVals[j + 1], sj1 = sinVals[j + 1]; - sBR->batchQuadFree( cx + r0 * cj + posX, cy + r0 * sj + posY, - cx + r1 * cj + posX, cy + r1 * sj + posY, - cx + r1 * cj1 + posX, cy + r1 * sj1 + posY, + sBR->batchQuadFree( cx + r0 * cj + posX, cy + r0 * sj + posY, cx + r1 * cj + posX, + cy + r1 * sj + posY, cx + r1 * cj1 + posX, cy + r1 * sj1 + posY, cx + r0 * cj1 + posX, cy + r0 * sj1 + posY ); } } diff --git a/src/eepp/ui/tools/htmlformatter.cpp b/src/eepp/ui/tools/htmlformatter.cpp index 13ca0653a..c3f669685 100644 --- a/src/eepp/ui/tools/htmlformatter.cpp +++ b/src/eepp/ui/tools/htmlformatter.cpp @@ -156,7 +156,28 @@ static void serializeGumboNodeToXML( GumboNode* node, std::string& out ) { } } -std::string HTMLFormatter::HTMLtoXML( const std::string& layoutString ) { +static GumboNode* findElement( GumboNode* node, GumboTag tag ) { + if ( !node ) + return nullptr; + if ( node->type == GUMBO_NODE_ELEMENT && node->v.element.tag == tag ) + return node; + + GumboVector* children = nullptr; + if ( node->type == GUMBO_NODE_DOCUMENT ) + children = &node->v.document.children; + else if ( node->type == GUMBO_NODE_ELEMENT ) + children = &node->v.element.children; + if ( !children ) + return nullptr; + + for ( unsigned int i = 0; i < children->length; ++i ) { + if ( auto* element = findElement( static_cast( children->data[i] ), tag ) ) + return element; + } + return nullptr; +} + +static std::string htmlToXML( const std::string& layoutString, bool bodyChildrenOnly ) { if ( layoutString.empty() ) return ""; @@ -174,7 +195,15 @@ std::string HTMLFormatter::HTMLtoXML( const std::string& layoutString ) { // 2. Serialize the AST into strict XML std::string strict_xml; - serializeGumboNodeToXML( output->root, strict_xml ); + if ( bodyChildrenOnly ) { + if ( auto* body = findElement( output->root, GUMBO_TAG_BODY ) ) { + GumboVector* children = &body->v.element.children; + for ( unsigned int i = 0; i < children->length; ++i ) + serializeGumboNodeToXML( static_cast( children->data[i] ), strict_xml ); + } + } else { + serializeGumboNodeToXML( output->root, strict_xml ); + } // 3. Cleanup Gumbo's memory gumbo_destroy_output( &kGumboDefaultOptions, output ); @@ -182,4 +211,12 @@ std::string HTMLFormatter::HTMLtoXML( const std::string& layoutString ) { return strict_xml; } +std::string HTMLFormatter::HTMLtoXML( const std::string& layoutString ) { + return htmlToXML( layoutString, false ); +} + +std::string HTMLFormatter::HTMLBodyToXML( const std::string& layoutString ) { + return htmlToXML( layoutString, true ); +} + }}} // namespace EE::UI::Tools diff --git a/src/eepp/ui/tools/textureatlaseditor.cpp b/src/eepp/ui/tools/textureatlaseditor.cpp index cb2a2b85a..2bedcab3c 100644 --- a/src/eepp/ui/tools/textureatlaseditor.cpp +++ b/src/eepp/ui/tools/textureatlaseditor.cpp @@ -384,7 +384,7 @@ void TextureAtlasEditor::fillTextureRegionList() { mTextureRegionGrid->closeAllChildren(); for ( auto& it : res ) { - TextureRegion* tr = it.second; + TextureRegion* tr = it.second.get(); UITextureRegion::New() ->setTextureRegion( tr ) @@ -400,11 +400,13 @@ void TextureAtlasEditor::fillTextureRegionList() { void TextureAtlasEditor::onTextureRegionChange( const Event* Event ) { if ( NULL != mTextureAtlasLoader && NULL != mTextureAtlasLoader->getTextureAtlas() ) { - mCurTextureRegion = Event->getNode()->isType( UI_TYPE_TEXTUREREGION ) - ? mTextureAtlasLoader->getTextureAtlas()->getByName( - static_cast( Event->getNode() )->getTooltipText() ) - : mTextureAtlasLoader->getTextureAtlas()->getByName( - mTextureRegionList->getItemSelectedText() ); + mCurTextureRegion = + ( Event->getNode()->isType( UI_TYPE_TEXTUREREGION ) + ? mTextureAtlasLoader->getTextureAtlas()->getByName( + static_cast( Event->getNode() )->getTooltipText() ) + : mTextureAtlasLoader->getTextureAtlas()->getByName( + mTextureRegionList->getItemSelectedText() ) ) + .get(); if ( Event->getNode()->isType( UI_TYPE_TEXTUREREGION ) ) mTextureRegionList->setSelected( @@ -459,8 +461,6 @@ void TextureAtlasEditor::saveTextureAtlas( const Event* ) { } void TextureAtlasEditor::onTextureAtlasClose( const Event* ) { - if ( NULL != mTextureAtlasLoader && NULL != mTextureAtlasLoader->getTextureAtlas() ) - TextureAtlasManager::instance()->remove( mTextureAtlasLoader->getTextureAtlas() ); eeSAFE_DELETE( mTextureAtlasLoader ); mTextureRegionList->clear(); mTextureRegionGrid->closeAllChildren(); diff --git a/src/eepp/ui/tools/uicolorpicker.cpp b/src/eepp/ui/tools/uicolorpicker.cpp index 4d1c0a778..1f1b21bc0 100644 --- a/src/eepp/ui/tools/uicolorpicker.cpp +++ b/src/eepp/ui/tools/uicolorpicker.cpp @@ -287,8 +287,9 @@ UIColorPicker::UIColorPicker( UIWindow* attachTo, const UIColorPicker::ColorPick mRoot->on( Event::OnLayoutUpdate, [this]( const Event* ) { if ( mHuePicker->getDrawable() == nullptr ) { - mHuePicker->setDrawable( createHueTexture( mHuePicker->getPixelsSize() ), true ); - mCurrentColor->setBackgroundDrawable( createGridTexture(), true ); + mHuePicker->setDrawable( createHueTexture( mHuePicker->getPixelsSize() ) ); + mCurrentColor->setBackgroundDrawable( + makeResource( createGridTexture() ) ); mCurrentColor->setBackgroundRepeat( "repeat" ); updateAll(); } @@ -369,7 +370,7 @@ void UIColorPicker::windowClose( const Event* ) { eeDelete( this ); } -Texture* UIColorPicker::createHueTexture( const Sizef& size ) { +TexturePtr UIColorPicker::createHueTexture( const Sizef& size ) { Image image( 1, (Uint32)size.getHeight(), 3 ); for ( Uint32 y = 0; y < image.getHeight(); y++ ) { @@ -385,7 +386,7 @@ Texture* UIColorPicker::createHueTexture( const Sizef& size ) { image.getChannels() ); } -Texture* UIColorPicker::createGridTexture() { +TexturePtr UIColorPicker::createGridTexture() { Sizef size( PixelDensity::dpToPx( Sizef( 26, 24 ) ) ); Image image( size.getWidth(), size.getHeight(), 3, Color( 128, 128, 128, 255 ) ); Color highlightColor( 204, 204, 204, 255 ); @@ -404,9 +405,9 @@ Texture* UIColorPicker::createGridTexture() { } void UIColorPicker::updateColorPicker() { - DrawableGroup* colorRectangle = DrawableGroup::New(); + auto colorRectangle = DrawableGroup::New(); - RectangleDrawable* rectDrawable = RectangleDrawable::New(); + auto rectDrawable = makeResource(); RectColors rectColors; rectDrawable->setSize( mColorPicker->getPixelsSize() ); @@ -417,7 +418,7 @@ void UIColorPicker::updateColorPicker() { rectDrawable->setRectColors( rectColors ); colorRectangle->addDrawable( rectDrawable ); - rectDrawable = RectangleDrawable::New(); + rectDrawable = makeResource(); rectDrawable->setSize( mColorPicker->getPixelsSize() ); rectColors.TopLeft = Color::Transparent; rectColors.BottomLeft = Color::Black; @@ -426,7 +427,7 @@ void UIColorPicker::updateColorPicker() { rectDrawable->setRectColors( rectColors ); colorRectangle->addDrawable( rectDrawable ); - mColorPicker->setDrawable( colorRectangle, true ); + mColorPicker->setDrawable( std::move( colorRectangle ) ); } void UIColorPicker::updateGuideLines() { @@ -573,7 +574,7 @@ void UIColorPicker::registerEvents() { ->setPosition( 0, 0 ) ->setSize( mRoot->getSceneNode()->getSize() ); mCoverWidget->setAnchors( UI_ANCHOR_LEFT | UI_ANCHOR_TOP | UI_ANCHOR_RIGHT | - UI_ANCHOR_BOTTOM ); + UI_ANCHOR_BOTTOM ); mCoverWidget->on( Event::MouseMove, [this]( const Event* event ) { Vector2i position = reinterpret_cast( event )->getPosition(); setColor( GLi->readPixel( position.x, diff --git a/src/eepp/ui/tools/uidiffview.cpp b/src/eepp/ui/tools/uidiffview.cpp index 47afee452..e58637438 100644 --- a/src/eepp/ui/tools/uidiffview.cpp +++ b/src/eepp/ui/tools/uidiffview.cpp @@ -67,13 +67,12 @@ static Sprite* setImageViewerImage( UIImageViewer* viewer, Image* image ) { auto sprite = Sprite::New(); sprite->createStatic( texture ); - sprite->setAsTextureOwner( true ); - sprite->setAsTextureRegionOwner( true ); + Sprite* spritePtr = sprite.get(); viewer->reset(); - viewer->getImage()->setDrawable( sprite, true ); + viewer->getImage()->setDrawable( std::move( sprite ) ); setImageViewerImageSize( viewer ); - return sprite; + return spritePtr; } UIScrollView* UIDiffView::NewMultiFileDiffViewer( const std::string& patchText, diff --git a/src/eepp/ui/tools/uifontpickerdialog.cpp b/src/eepp/ui/tools/uifontpickerdialog.cpp index 64d594144..73dbefd2c 100644 --- a/src/eepp/ui/tools/uifontpickerdialog.cpp +++ b/src/eepp/ui/tools/uifontpickerdialog.cpp @@ -1,7 +1,7 @@ #include #include -#include #include +#include #include #include #include @@ -267,6 +267,7 @@ UIFontPickerDialog::~UIFontPickerDialog() { mColorPicker = nullptr; mColorPickerCloseCb = 0; clearBrowseDialog(); + clearPreviewFont(); } Uint32 UIFontPickerDialog::getType() const { @@ -281,21 +282,21 @@ void UIFontPickerDialog::setTheme( UITheme* theme ) { UIWindow::setTheme( theme ); if ( mButtonOK ) { - if ( Drawable* icon = + if ( DrawablePtr icon = getUISceneNode()->findIconDrawable( "ok", PixelDensity::dpToPxI( 16 ) ) ) - mButtonOK->setIcon( icon ); + mButtonOK->setIcon( std::move( icon ) ); } if ( mButtonCancel ) { - if ( Drawable* icon = + if ( DrawablePtr icon = getUISceneNode()->findIconDrawable( "cancel", PixelDensity::dpToPxI( 16 ) ) ) - mButtonCancel->setIcon( icon ); + mButtonCancel->setIcon( std::move( icon ) ); } if ( mButtonBrowse ) { - if ( Drawable* icon = getUISceneNode()->findIconDrawable( "document-open", - PixelDensity::dpToPxI( 16 ) ) ) - mButtonBrowse->setIcon( icon ); + if ( DrawablePtr icon = getUISceneNode()->findIconDrawable( "document-open", + PixelDensity::dpToPxI( 16 ) ) ) + mButtonBrowse->setIcon( std::move( icon ) ); } onThemeLoaded(); @@ -438,7 +439,7 @@ void UIFontPickerDialog::loadFonts() { void UIFontPickerDialog::setFonts( std::vector fonts ) { FontDesc selectedFont = mSelection.font; - mergeFontManagerFonts( fonts ); + mergeLoadedFonts( fonts ); for ( const auto& font : mFonts ) { if ( std::find_if( fonts.begin(), fonts.end(), [&]( const FontDesc& desc ) { return desc.sameFile( font ); @@ -471,21 +472,23 @@ void UIFontPickerDialog::sortFonts() { } ); } -void UIFontPickerDialog::mergeFontManagerFonts( std::vector& fonts ) { - FontManager::instance()->each( [&]( const auto& res ) { - if ( res.second == nullptr || res.second->getType() != FontType::TTF ) - return; +void UIFontPickerDialog::mergeLoadedFonts( std::vector& fonts ) { + if ( !getUISceneNode() ) + return; + for ( const FontPtr& font : getUISceneNode()->getResourceScope()->getFonts() ) { + if ( font == nullptr || font->getType() != FontType::TTF ) + continue; FontDesc desc; - if ( !static_cast( res.second )->getFontDesc( desc ) ) - return; + if ( !static_cast( font.get() )->getFontDesc( desc ) ) + continue; mLoadedFontKeys.insert( desc.getFileKey() ); if ( std::find_if( fonts.begin(), fonts.end(), [&]( const FontDesc& font ) { return font.sameFile( desc ); } ) == fonts.end() ) fonts.push_back( desc ); - } ); + } } void UIFontPickerDialog::updateFontTags() { @@ -628,10 +631,24 @@ void UIFontPickerDialog::updatePreview() { if ( !mPreviewText ) return; - FontTrueType* font = FontManager::instance()->getOrLoadSystemFallbackFont( mSelection.font ); - if ( font ) { - mPreviewText->setFont( font ); - mPreviewInput->setFont( font ); + if ( !mPreviewTextDefaultFont ) + mPreviewTextDefaultFont = mPreviewText->getFont(); + if ( !mPreviewInputDefaultFont ) + mPreviewInputDefaultFont = mPreviewInput->getFont(); + + FontDesc previewDesc; + const bool previewMatchesSelection = mPreviewFont && mPreviewFont->getFontDesc( previewDesc ) && + previewDesc.sameFile( mSelection.font ); + if ( !previewMatchesSelection && !mSelection.font.path.empty() && getUISceneNode() ) { + FontTrueTypePtr font = + getUISceneNode()->getResourceScope()->getFontService().loadSystemFont( + mSelection.font ); + if ( font ) { + mPreviewText->setFont( font.get() ); + mPreviewInput->setFont( font.get() ); + clearPreviewFont(); + mPreviewFont = std::move( font ); + } } mPreviewText->setFontSize( PixelDensity::dpToPxI( mSelection.size * 2 ) ); @@ -657,6 +674,17 @@ void UIFontPickerDialog::updatePreview() { } } +void UIFontPickerDialog::clearPreviewFont() { + if ( !mPreviewFont ) + return; + if ( mPreviewText && mPreviewTextDefaultFont && mPreviewText->getFont() == mPreviewFont.get() ) + mPreviewText->setFont( mPreviewTextDefaultFont ); + if ( mPreviewInput && mPreviewInputDefaultFont && + mPreviewInput->getFont() == mPreviewFont.get() ) + mPreviewInput->setFont( mPreviewInputDefaultFont ); + mPreviewFont.reset(); +} + void UIFontPickerDialog::selectInitialRows() { mSizeModel = ItemListModel::create( mSizes ); mSizeList->setModel( mSizeModel ); @@ -782,18 +810,21 @@ bool UIFontPickerDialog::addExternalFont( const std::string& path, Uint32 faceIn const std::string fontName( FileSystem::fileRemoveExtension( FileSystem::fileNameFromPath( path ) ) ); - FontTrueType* font = FontTrueType::New( fontName ); + if ( !getUISceneNode() ) + return false; + ResourceScope& resourceScope = *getUISceneNode()->getResourceScope(); + FontTrueTypePtr font = FontTrueType::New( fontName, resourceScope ); if ( !font || !font->loadFromFile( path, faceIndex ) ) { - eeSAFE_DELETE( font ); + resourceScope.eraseLocalFont( font.get() ); return false; } FontDesc desc; if ( !font->getFontDesc( desc ) ) { - eeSAFE_DELETE( font ); + resourceScope.eraseLocalFont( font.get() ); return false; } - eeSAFE_DELETE( font ); + resourceScope.eraseLocalFont( font.get() ); mLoadedFontKeys.insert( desc.getFileKey() ); mFonts.push_back( desc ); diff --git a/src/eepp/ui/tools/uiimageviewer.cpp b/src/eepp/ui/tools/uiimageviewer.cpp index 866c82076..0845ed089 100644 --- a/src/eepp/ui/tools/uiimageviewer.cpp +++ b/src/eepp/ui/tools/uiimageviewer.cpp @@ -155,7 +155,7 @@ void UIImageViewer::loadImageAsync( std::string_view path, bool isContents, bool if ( format == Image::Format::Unknown ) return; - Sprite* image = nullptr; + DrawablePtr image; if ( mClosing ) return; @@ -165,20 +165,16 @@ void UIImageViewer::loadImageAsync( std::string_view path, bool isContents, bool reinterpret_cast( path.c_str() ), path.size() ) : TextureFactory::instance()->loadFromFile( path ); - Sprite* sprite = Sprite::New(); + SpritePtr sprite = Sprite::New(); sprite->createStatic( tex ); - sprite->setAsTextureOwner( true ); - sprite->setAsTextureRegionOwner( true ); - image = sprite; + image = std::move( sprite ); } else { IOStream* stream = isContents ? (IOStream*)new IOStreamMemory( path.c_str(), path.size() ) : (IOStream*)new IOStreamFile( path ); - Sprite* sprite = Sprite::fromGif( *stream ); - sprite->setAsTextureOwner( true ); - sprite->setAsTextureRegionOwner( true ); + SpritePtr sprite = Sprite::fromGif( *stream ); sprite->setAutoAnimate( false ); - image = sprite; + image = std::move( sprite ); delete stream; } @@ -190,7 +186,7 @@ void UIImageViewer::loadImageAsync( std::string_view path, bool isContents, bool mCurFileType = format; runOnMainThread( [this, image] { - mImage->setDrawable( image, true ); + mImage->setDrawable( image ); updateTextDisplay(); auto s( image->getPixelsSize() ); auto scale( s.x > mSize.x || s.y > mSize.y @@ -212,7 +208,7 @@ void UIImageViewer::onSizeChange() { } void UIImageViewer::reset() { - mImage->setDrawable( nullptr )->setVisible( false ); + mImage->setDrawable( DrawablePtr{} )->setVisible( false ); } Uint32 UIImageViewer::onMessage( const NodeMessage* msg ) { @@ -313,7 +309,7 @@ Uint32 UIImageViewer::onKeyDown( const KeyEvent& event ) { } else if ( event.getKeyCode() == KEY_T ) { resetImageView(); } else if ( event.getKeyCode() == KEY_X ) { - auto sprite = static_cast( mImage->getDrawable() ); + auto sprite = static_cast( mImage->getDrawable().get() ); auto mode = sprite->getRenderMode(); if ( mode == RENDER_NORMAL ) mode = RENDER_FLIPPED; @@ -326,7 +322,7 @@ Uint32 UIImageViewer::onKeyDown( const KeyEvent& event ) { sprite->setRenderMode( mode ); invalidateDraw(); } else if ( event.getKeyCode() == KEY_C ) { - auto sprite = static_cast( mImage->getDrawable() ); + auto sprite = static_cast( mImage->getDrawable().get() ); auto mode = sprite->getRenderMode(); if ( mode == RENDER_NORMAL ) mode = RENDER_MIRROR; diff --git a/src/eepp/ui/tools/uitabwidgetsplitter.cpp b/src/eepp/ui/tools/uitabwidgetsplitter.cpp index c7acdbfc3..7fc460016 100644 --- a/src/eepp/ui/tools/uitabwidgetsplitter.cpp +++ b/src/eepp/ui/tools/uitabwidgetsplitter.cpp @@ -912,7 +912,7 @@ void UITabWidgetSplitter::unserializeNode( const nlohmann::json& j, UITabWidget* !result.title.empty() ? result.title : file.value( "title", "" ); auto [tab, _] = createWidgetInTabWidget( curTabWidget, result.widget, title ); if ( result.icon ) - tab->setIcon( result.icon ); + tab->setIcon( result.icon->clone() ); } } if ( curTabWidget->getTabCount() > 0 ) { diff --git a/src/eepp/ui/tools/uitextureviewer.cpp b/src/eepp/ui/tools/uitextureviewer.cpp index 672c684d8..540016255 100644 --- a/src/eepp/ui/tools/uitextureviewer.cpp +++ b/src/eepp/ui/tools/uitextureviewer.cpp @@ -6,6 +6,59 @@ namespace EE { namespace UI { namespace Tools { +namespace { + +class UIWeakTexturePreview : public UIImage { + public: + static UIWeakTexturePreview* New( TextureWeakPtr texture ) { + return eeNew( UIWeakTexturePreview, ( std::move( texture ) ) ); + } + + virtual void draw() { + UINode::draw(); + + if ( !mVisible || mAlpha == 0.f ) + return; + + TexturePtr texture = mTexture.lock(); + if ( !texture ) + return; + + const Sizef textureSize( texture->getPixelsSize() ); + if ( textureSize.x <= 0.f || textureSize.y <= 0.f ) + return; + + const Sizef availableSize( mSize.x - mPaddingPx.Left - mPaddingPx.Right, + mSize.y - mPaddingPx.Top - mPaddingPx.Bottom ); + Float scale = eemax( 0.f, eemin( 1.f, eemin( availableSize.x / textureSize.x, + availableSize.y / textureSize.y ) ) ); + Sizef destSize( ( textureSize * scale ).floor() ); + Vector2f position( std::trunc( mScreenPos.x ) + mPaddingPx.Left, + std::trunc( mScreenPos.y ) + mPaddingPx.Top ); + + if ( Font::getHorizontalAlign( mFlags ) == UI_HALIGN_CENTER ) + position.x += ( availableSize.x - destSize.x ) * 0.5f; + else if ( Font::getHorizontalAlign( mFlags ) == UI_HALIGN_RIGHT ) + position.x += availableSize.x - destSize.x; + if ( Font::getVerticalAlign( mFlags ) == UI_VALIGN_CENTER ) + position.y += ( availableSize.y - destSize.y ) * 0.5f; + else if ( Font::getVerticalAlign( mFlags ) == UI_VALIGN_BOTTOM ) + position.y += availableSize.y - destSize.y; + + const Color previousColor( texture->getColor() ); + texture->setColor( mColor ); + texture->draw( position, destSize ); + texture->setColor( previousColor ); + } + + protected: + explicit UIWeakTexturePreview( TextureWeakPtr texture ) : mTexture( std::move( texture ) ) {} + + TextureWeakPtr mTexture; +}; + +} // namespace + UITextureViewer* UITextureViewer::New() { return eeNew( UITextureViewer, () ); } @@ -22,12 +75,12 @@ void UITextureViewer::setImage( TexturePtr texture ) { return; mSelectedTexture = std::move( texture ); mImageLayout->setEnabled( true )->setVisible( true ); - imageView->setDrawable( mSelectedTexture.get() ); + imageView->setDrawable( mSelectedTexture ); } void UITextureViewer::clearSelectedTexture() { if ( UIImage* imageView = mImageLayout->findByType( UI_TYPE_IMAGE ) ) - imageView->setDrawable( nullptr ); + imageView->setDrawable( DrawablePtr{} ); mSelectedTexture.reset(); mImageLayout->setEnabled( false )->setVisible( false ); } @@ -107,11 +160,10 @@ void UITextureViewer::insertTexture( const TextureRegistryRecord& record ) { if ( !texture ) return; - UIImage* img = UIImage::New(); + UIImage* img = UIWeakTexturePreview::New( record.texture ); std::string uid( String::format( "texture-%llu", static_cast( record.id.value() ) ) ); - img->setDrawable( texture.get() ) - ->setScaleType( UIScaleType::FitInside ) + img->setScaleType( UIScaleType::FitInside ) ->setClasses( { "texture-preview", uid } ) ->setTooltipText( getTextureDescription( texture.get() ) ) ->setGravity( UI_HALIGN_CENTER | UI_VALIGN_CENTER ) diff --git a/src/eepp/ui/tools/uiwidgetinspector.cpp b/src/eepp/ui/tools/uiwidgetinspector.cpp index df3853d43..ebbc8ab91 100644 --- a/src/eepp/ui/tools/uiwidgetinspector.cpp +++ b/src/eepp/ui/tools/uiwidgetinspector.cpp @@ -148,11 +148,11 @@ UIWindow* UIWidgetInspector::create( UISceneNode* sceneNode, const Float& menuIc UIPushButton* button = cont->find( "pick_widget" ); if ( button->getIcon() == nullptr ) { - Drawable* cursorPointer = button->getUISceneNode()->findIconDrawable( + DrawablePtr cursorPointer = button->getUISceneNode()->findIconDrawable( "cursor-pointer", PixelDensity::dpToPx( 16 ) ); if ( cursorPointer ) - button->setIcon( cursorPointer, true ); + button->setIcon( std::move( cursorPointer ) ); } button->on( Event::MouseClick, [sceneNode, nodeTree, computedView]( const Event* event ) { diff --git a/src/eepp/ui/uiapplication.cpp b/src/eepp/ui/uiapplication.cpp index 0fe03f806..b15b34e97 100644 --- a/src/eepp/ui/uiapplication.cpp +++ b/src/eepp/ui/uiapplication.cpp @@ -59,17 +59,20 @@ UIApplication::UIApplication( const WindowSettings& windowSettings, const Settin if ( !appSettings.loadBaseResources ) return; - - Font* font = appSettings.baseFont - ? appSettings.baseFont - : FontTrueType::New( "NotoSans-Regular", "assets/fonts/NotoSans-Regular.ttf" ); + FontTrueTypePtr loadedBaseFont; + if ( !appSettings.baseFont ) + loadedBaseFont = + FontTrueType::New( "NotoSans-Regular", "assets/fonts/NotoSans-Regular.ttf" ); + Font* font = appSettings.baseFont ? appSettings.baseFont : loadedBaseFont.get(); if ( font && font->getType() == FontType::TTF ) FontFamily::loadFromRegular( static_cast( font ) ); - Font* monospaceFont = appSettings.monospaceFont - ? appSettings.monospaceFont - : FontTrueType::New( "monospace", "assets/fonts/DejaVuSansMono.ttf" ); + FontTrueTypePtr loadedMonospaceFont; + if ( !appSettings.monospaceFont ) + loadedMonospaceFont = FontTrueType::New( "monospace", "assets/fonts/DejaVuSansMono.ttf" ); + Font* monospaceFont = + appSettings.monospaceFont ? appSettings.monospaceFont : loadedMonospaceFont.get(); if ( monospaceFont && monospaceFont->getType() == FontType::TTF ) { static_cast( monospaceFont )->setEnableDynamicMonospace( true ); @@ -91,14 +94,14 @@ UIApplication::UIApplication( const WindowSettings& windowSettings, const Settin mUISceneNode->getRoot()->addClass( "appbackground" ); mUISceneNode->getUIThemeManager()->setDefaultEffectsEnabled( true )->setDefaultFont( font ); - UITheme* theme = UITheme::load( "uitheme", "uitheme", "", font, - appSettings.baseStyleSheetPath ? *appSettings.baseStyleSheetPath - : "assets/ui/breeze.css" ); + UIThemePtr theme = UITheme::load( + "uitheme", "uitheme", "", font, + appSettings.baseStyleSheetPath ? *appSettings.baseStyleSheetPath : "assets/ui/breeze.css" ); mStyleSheetMarker = String::hash( "uitheme" ); mUISceneNode->setStyleSheet( theme->getStyleSheet() ); mUISceneNode->getStyleSheet().setMarker( mStyleSheetMarker ); - mUISceneNode->getUIThemeManager()->setDefaultTheme( theme )->add( theme ); + mUISceneNode->getUIThemeManager()->setDefaultTheme( std::move( theme ) ); } UIApplication::~UIApplication() { diff --git a/src/eepp/ui/uibackgrounddrawable.cpp b/src/eepp/ui/uibackgrounddrawable.cpp index d94270397..6a32ae30d 100644 --- a/src/eepp/ui/uibackgrounddrawable.cpp +++ b/src/eepp/ui/uibackgrounddrawable.cpp @@ -18,9 +18,7 @@ UIBackgroundDrawable::UIBackgroundDrawable( const UINode* owner ) : mNeedsRadiusUpdate( false ), mColorNeedsUpdate( false ) {} -UIBackgroundDrawable::~UIBackgroundDrawable() { - eeSAFE_DELETE( mVertexBuffer ); -} +UIBackgroundDrawable::~UIBackgroundDrawable() = default; void UIBackgroundDrawable::draw() { draw( mPosition, mSize ); @@ -178,7 +176,7 @@ void UIBackgroundDrawable::update() { mVertexBuffer = VertexBuffer::NewVertexArray( VERTEX_FLAGS_PRIMITIVE, PRIMITIVE_TRIANGLE_FAN ); } - Borders::createBackground( mVertexBuffer, mRadiuses, mPosition, mSize, mColor ); + Borders::createBackground( mVertexBuffer.get(), mRadiuses, mPosition, mSize, mColor ); } mColorNeedsUpdate = false; mNeedsRadiusUpdate = false; diff --git a/src/eepp/ui/uiborderdrawable.cpp b/src/eepp/ui/uiborderdrawable.cpp index bf6e26621..f136ec71b 100644 --- a/src/eepp/ui/uiborderdrawable.cpp +++ b/src/eepp/ui/uiborderdrawable.cpp @@ -20,9 +20,7 @@ UIBorderDrawable::UIBorderDrawable( const UINode* owner ) : mColorNeedsUpdate( false ), mHasBorder( false ) {} -UIBorderDrawable::~UIBorderDrawable() { - eeSAFE_DELETE( mVertexBuffer ); -} +UIBorderDrawable::~UIBorderDrawable() = default; void UIBorderDrawable::draw() { draw( mPosition, mSize ); @@ -309,12 +307,12 @@ void UIBorderDrawable::update() { size.y += mBorders.bottom.width * 2; } - Borders::createBorders( mVertexBuffer, mBorders, pos, size ); + Borders::createBorders( mVertexBuffer.get(), mBorders, pos, size ); break; } case BorderType::Inside: { - Borders::createBorders( mVertexBuffer, mBorders, Vector2f::Zero, mSize ); + Borders::createBorders( mVertexBuffer.get(), mBorders, Vector2f::Zero, mSize ); break; } case BorderType::Outline: { @@ -337,7 +335,7 @@ void UIBorderDrawable::update() { size.y += mBorders.bottom.width; } - Borders::createBorders( mVertexBuffer, mBorders, pos, size ); + Borders::createBorders( mVertexBuffer.get(), mBorders, pos, size ); break; } diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 83a8b23f9..9b3a44a5b 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -1,10 +1,10 @@ #include "eepp/ui/uistyle.hpp" #include -#include #include #include #include #include +#include #include #include #include @@ -138,7 +138,7 @@ const MouseBindings::ShortcutMap UICodeEditor::getDefaultMousebindings() { UICodeEditor::UICodeEditor( const std::string& elementTag, const bool& autoRegisterBaseCommands, const bool& autoRegisterBaseKeybindings ) : UIWidget( elementTag ), - mFont( FontManager::instance()->getByName( "monospace" ) ), + mFont( getUISceneNode()->getResourceScope()->findFont( "monospace" ).get() ), mDoc( std::make_shared() ), mDocView( mDoc, mFontStyleConfig, { .tabStops = mTabStops } ), mBlinkTime( Seconds( 0.5f ) ), @@ -1151,7 +1151,7 @@ void UICodeEditor::drawLockedIcon( const Vector2f start ) { if ( mFileLockIcon == nullptr ) return; - Drawable* fileLockIcon = mFileLockIcon->getSize( PixelDensity::dpToPxI( 16 ) ); + Drawable* fileLockIcon = mFileLockIcon->getSource( PixelDensity::dpToPxI( 16 ) ).get(); if ( fileLockIcon == nullptr ) return; @@ -3533,11 +3533,9 @@ void UICodeEditor::updateGlyphWidth() { invalidateLineWrapMaxWidth( false ); } -Drawable* UICodeEditor::findIcon( const std::string& name ) { +DrawablePtr UICodeEditor::findIcon( const std::string& name ) { UIIcon* icon = getUISceneNode()->findIcon( name ); - if ( icon ) - return icon->getSize( mMenuIconSize ); - return nullptr; + return icon ? icon->createDrawable( mMenuIconSize ) : DrawablePtr{}; } const bool& UICodeEditor::getColorPreview() const { @@ -4610,7 +4608,7 @@ void UICodeEditor::drawLineNumbers( const DocumentLineRange& lineRange, const Ve if ( mFoldsAlwaysVisible || mFoldsVisible || currentLineHasFold ) { if ( ( isFolded && mFoldedDrawable ) || ( !isFolded && mFoldedDrawable ) ) { - Drawable* drawable = isFolded ? mFoldedDrawable : mFoldDrawable; + Drawable* drawable = ( isFolded ? mFoldedDrawable : mFoldDrawable ).get(); GlyphDrawable::DrawMode oldMode; if ( drawable->getDrawableType() == Drawable::Type::GLYPH ) { @@ -5462,20 +5460,20 @@ void UICodeEditor::setShowFoldingRegion( bool showFoldingRegion ) { } } -Drawable* UICodeEditor::getFoldDrawable() const { +const DrawablePtr& UICodeEditor::getFoldDrawable() const { return mFoldDrawable; } -void UICodeEditor::setFoldDrawable( Drawable* foldDrawable ) { - mFoldDrawable = foldDrawable; +void UICodeEditor::setFoldDrawable( DrawablePtr foldDrawable ) { + mFoldDrawable = std::move( foldDrawable ); } -Drawable* UICodeEditor::getFoldedDrawable() const { +const DrawablePtr& UICodeEditor::getFoldedDrawable() const { return mFoldedDrawable; } -void UICodeEditor::setFoldedDrawable( Drawable* foldedDrawable ) { - mFoldedDrawable = foldedDrawable; +void UICodeEditor::setFoldedDrawable( DrawablePtr foldedDrawable ) { + mFoldedDrawable = std::move( foldedDrawable ); } bool UICodeEditor::getFoldsAlwaysVisible() const { diff --git a/src/eepp/ui/uiconsole.cpp b/src/eepp/ui/uiconsole.cpp index 7fbb1e308..935cdc557 100644 --- a/src/eepp/ui/uiconsole.cpp +++ b/src/eepp/ui/uiconsole.cpp @@ -1,9 +1,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -52,7 +52,7 @@ UIConsole::UIConsole( Font* font, const bool& makeDefaultCommands, const bool& a mFontStyleConfig.Font = font; if ( nullptr == font ) - mFontStyleConfig.Font = FontManager::instance()->getByName( "monospace" ); + mFontStyleConfig.Font = getUISceneNode()->getResourceScope()->findFont( "monospace" ).get(); mMaxLogLines = maxLogLines; @@ -1227,11 +1227,9 @@ void UIConsole::onDocumentSaved( TextDocument* ) {} void UIConsole::onDocumentMoved( TextDocument* ) {} -Drawable* UIConsole::findIcon( const std::string& name ) { +DrawablePtr UIConsole::findIcon( const std::string& name ) { UIIcon* icon = getUISceneNode()->findIcon( name ); - if ( icon ) - return icon->getSize( mMenuIconSize ); - return nullptr; + return icon ? icon->createDrawable( mMenuIconSize ) : DrawablePtr{}; } void UIConsole::copySelection() { diff --git a/src/eepp/ui/uifiledialog.cpp b/src/eepp/ui/uifiledialog.cpp index fd30c2b44..b137dbda5 100644 --- a/src/eepp/ui/uifiledialog.cpp +++ b/src/eepp/ui/uifiledialog.cpp @@ -341,31 +341,31 @@ void UIFileDialog::setTheme( UITheme* Theme ) { mFile->setTheme( Theme ); mFiletype->setTheme( Theme ); - Drawable* icon = getUISceneNode()->findIconDrawable( "go-up", PixelDensity::dpToPxI( 16 ) ); + DrawablePtr icon = getUISceneNode()->findIconDrawable( "go-up", PixelDensity::dpToPxI( 16 ) ); if ( icon ) { mButtonUp->setText( "" ); - mButtonUp->setIcon( icon ); + mButtonUp->setIcon( std::move( icon ) ); mButtonUp->setTooltipText( i18n( "uifiledialog_go_up", "Up" ) ); } icon = getUISceneNode()->findIconDrawable( "folder-add", PixelDensity::dpToPxI( 16 ) ); if ( icon ) { mButtonNewFolder->setText( "" ); - mButtonNewFolder->setIcon( icon ); + mButtonNewFolder->setIcon( std::move( icon ) ); mButtonNewFolder->setTooltipText( i18n( "uifiledialog_new_folder", "New Folder" ) ); } icon = getUISceneNode()->findIconDrawable( "list-view", PixelDensity::dpToPxI( 16 ) ); if ( icon ) { mButtonListView->setText( "" ); - mButtonListView->setIcon( icon ); + mButtonListView->setIcon( std::move( icon ) ); mButtonListView->setTooltipText( i18n( "uifiledialog_list", "List" ) ); } icon = getUISceneNode()->findIconDrawable( "table-view", PixelDensity::dpToPxI( 16 ) ); if ( icon ) { mButtonTableView->setText( "" ); - mButtonTableView->setIcon( icon ); + mButtonTableView->setIcon( std::move( icon ) ); mButtonTableView->setTooltipText( i18n( "uifiledialog_table", "Table" ) ); } diff --git a/src/eepp/ui/uiicon.cpp b/src/eepp/ui/uiicon.cpp index 2e1745674..f11cd410a 100644 --- a/src/eepp/ui/uiicon.cpp +++ b/src/eepp/ui/uiicon.cpp @@ -1,11 +1,12 @@ #include #include #include +#include namespace EE { namespace UI { -UIIcon* UIIcon::New( const std::string& name ) { - return eeNew( UIIcon, ( name ) ); +UIIconPtr UIIcon::New( const std::string& name ) { + return UIIconPtr( eeNew( UIIcon, ( name ) ), ResourceDeleter() ); } UIIcon::UIIcon( const std::string& name ) : mName( name ) {} @@ -16,39 +17,48 @@ const std::string& UIIcon::getName() const { return mName; } -Drawable* UIIcon::getSize( const int& size ) const { +const DrawablePtr& UIIcon::getSource( const int& size ) const { + static const DrawablePtr empty; auto it = mSizes.find( size ); if ( it != mSizes.end() ) return it->second; - int distance = UINT32_MAX; - Drawable* closest = nullptr; + int distance = std::numeric_limits::max(); + const DrawablePtr* closest = nullptr; for ( const auto& sit : mSizes ) { int diff = abs( sit.first - size ); if ( diff < distance ) { distance = diff; - closest = sit.second; + closest = &sit.second; } } - return closest; + return closest ? *closest : empty; } -void UIIcon::setSize( const int& size, Drawable* drawable ) { - mSizes[size] = drawable; +DrawablePtr UIIcon::createDrawable( const int& size ) const { + const DrawablePtr& source = getSource( size ); + return source ? source->clone() : DrawablePtr{}; } -UIIcon* UIGlyphIcon::New( const std::string& name, FontTrueType* font, const Uint32& codePoint ) { - return eeNew( UIGlyphIcon, ( name, font, codePoint ) ); +void UIIcon::setSource( const int& size, DrawablePtr drawable ) { + mSizes[size] = std::move( drawable ); } -Drawable* UIGlyphIcon::getSize( const int& size ) const { +UIIconPtr UIGlyphIcon::New( const std::string& name, FontTrueType* font, const Uint32& codePoint ) { + return UIIconPtr( eeNew( UIGlyphIcon, ( name, font, codePoint ) ), ResourceDeleter() ); +} + +const DrawablePtr& UIGlyphIcon::getSource( const int& size ) const { + static const DrawablePtr empty; if ( !mFont ) - return nullptr; + return empty; auto it = mSizes.find( size ); if ( it != mSizes.end() ) return it->second; GlyphDrawable* drawable = mFont->getGlyphDrawable( mCodePoint, size ); - const_cast( this )->setSize( size, drawable ); - return drawable; + if ( !drawable ) + return empty; + const_cast( this )->setSource( size, drawable->clone() ); + return UIIcon::getSource( size ); } UIGlyphIcon::UIGlyphIcon( const std::string& name, FontTrueType* font, const Uint32& codePoint ) : @@ -68,15 +78,16 @@ UIGlyphIcon::~UIGlyphIcon() { } } -UIIcon* UISVGIcon::New( const std::string& name, const std::string& svgXML ) { - return eeNew( UISVGIcon, ( name, svgXML ) ); +UIIconPtr UISVGIcon::New( const std::string& name, const std::string& svgXML ) { + return UIIconPtr( eeNew( UISVGIcon, ( name, svgXML ) ), ResourceDeleter() ); } UISVGIcon::~UISVGIcon() {} -Drawable* UISVGIcon::getSize( const int& size ) const { - auto it = mSVGs.find( size ); - if ( it != mSVGs.end() ) +const DrawablePtr& UISVGIcon::getSource( const int& size ) const { + static const DrawablePtr empty; + auto it = mSizes.find( size ); + if ( it != mSizes.end() ) return it->second; Image::FormatConfiguration format; @@ -87,16 +98,18 @@ Drawable* UISVGIcon::getSize( const int& size ) const { mOriSize = { w, h }; mOriChannels = c; } else { - return nullptr; + return empty; } } format.svgScale( size / (Float)eemax( mOriSize.x, mOriSize.y ) ); - Texture* texture = TextureFactory::instance()->loadFromMemory( + TexturePtr texture = TextureFactory::instance()->loadFromMemory( (const unsigned char*)&mSVGXml[0], mSVGXml.size(), false, Texture::ClampMode::ClampToEdge, false, false, format ); - mSVGs[size] = texture; - return texture; + if ( !texture ) + return empty; + const_cast( this )->setSource( size, std::move( texture ) ); + return UIIcon::getSource( size ); } UISVGIcon::UISVGIcon( const std::string& name, const std::string& svgXML ) : diff --git a/src/eepp/ui/uiicontheme.cpp b/src/eepp/ui/uiicontheme.cpp index 5096debd9..f42a6f8dd 100644 --- a/src/eepp/ui/uiicontheme.cpp +++ b/src/eepp/ui/uiicontheme.cpp @@ -3,26 +3,21 @@ namespace EE { namespace UI { -UIIconTheme* UIIconTheme::New( const std::string& name ) { - return eeNew( UIIconTheme, ( name ) ); +UIIconThemePtr UIIconTheme::New( const std::string& name ) { + return UIIconThemePtr( eeNew( UIIconTheme, ( name ) ), ResourceDeleter() ); } -UIIconTheme::~UIIconTheme() { - for ( auto icon : mIcons ) - eeDelete( icon.second ); -} +UIIconTheme::~UIIconTheme() = default; UIIconTheme::UIIconTheme( const std::string& name ) : mName( name ) {} -UIIconTheme* UIIconTheme::add( UIIcon* icon ) { - auto iconExists = mIcons.find( icon->getName() ); - if ( iconExists != mIcons.end() ) - eeDelete( iconExists->second ); - mIcons[icon->getName()] = icon; +UIIconTheme* UIIconTheme::add( UIIconPtr icon ) { + if ( icon ) + mIcons[icon->getName()] = std::move( icon ); return this; } -UIIconTheme* UIIconTheme::add( const std::unordered_map& icons ) { +UIIconTheme* UIIconTheme::add( const std::unordered_map& icons ) { mIcons.insert( icons.begin(), icons.end() ); return this; } @@ -33,7 +28,7 @@ const std::string& UIIconTheme::getName() const { UIIcon* UIIconTheme::getIcon( const std::string& name ) const { auto it = mIcons.find( name ); - return it != mIcons.end() ? it->second : nullptr; + return it != mIcons.end() ? it->second.get() : nullptr; } }} // namespace EE::UI diff --git a/src/eepp/ui/uiiconthememanager.cpp b/src/eepp/ui/uiiconthememanager.cpp index 54357498a..cea69dbe4 100644 --- a/src/eepp/ui/uiiconthememanager.cpp +++ b/src/eepp/ui/uiiconthememanager.cpp @@ -20,16 +20,13 @@ UIIconThemeManager* UIIconThemeManager::New() { return eeNew( UIIconThemeManager, () ); } -UIIconThemeManager::~UIIconThemeManager() { - for ( UIIconTheme* theme : mIconThemes ) - eeDelete( theme ); -} +UIIconThemeManager::~UIIconThemeManager() = default; UIIconThemeManager::UIIconThemeManager() {} -UIIconThemeManager* UIIconThemeManager::add( UIIconTheme* iconTheme ) { - if ( !isPresent( iconTheme ) ) { - mIconThemes.push_back( iconTheme ); +UIIconThemeManager* UIIconThemeManager::add( UIIconThemePtr iconTheme ) { + if ( iconTheme && !isPresent( iconTheme.get() ) ) { + mIconThemes.emplace_back( std::move( iconTheme ) ); } return this; } @@ -38,11 +35,10 @@ UIIconTheme* UIIconThemeManager::getCurrentTheme() const { return mCurrentTheme; } -UIIconThemeManager* UIIconThemeManager::setCurrentTheme( UIIconTheme* currentTheme ) { - if ( currentTheme != mCurrentTheme && currentTheme != mFallbackTheme ) { - if ( !isPresent( currentTheme ) ) - add( currentTheme ); - mCurrentTheme = currentTheme; +UIIconThemeManager* UIIconThemeManager::setCurrentTheme( UIIconThemePtr currentTheme ) { + if ( currentTheme.get() != mCurrentTheme && currentTheme.get() != mFallbackTheme ) { + mCurrentTheme = currentTheme.get(); + add( std::move( currentTheme ) ); } return this; } @@ -51,11 +47,10 @@ UIIconTheme* UIIconThemeManager::getFallbackTheme() const { return mFallbackTheme; } -UIIconThemeManager* UIIconThemeManager::setFallbackTheme( UIIconTheme* fallbackTheme ) { - if ( fallbackTheme != mFallbackTheme && fallbackTheme != mCurrentTheme ) { - if ( !isPresent( fallbackTheme ) ) - add( fallbackTheme ); - mFallbackTheme = fallbackTheme; +UIIconThemeManager* UIIconThemeManager::setFallbackTheme( UIIconThemePtr fallbackTheme ) { + if ( fallbackTheme.get() != mFallbackTheme && fallbackTheme.get() != mCurrentTheme ) { + mFallbackTheme = fallbackTheme.get(); + add( std::move( fallbackTheme ) ); } return this; } @@ -89,12 +84,14 @@ UIIconThemeManager::setFallbackThemeManager( UIThemeManager* fallbackThemeManage } void UIIconThemeManager::remove( UIIconTheme* iconTheme ) { - auto pos = std::find( mIconThemes.begin(), mIconThemes.end(), iconTheme ); + auto pos = std::find_if( + mIconThemes.begin(), mIconThemes.end(), + [iconTheme]( const UIIconThemePtr& theme ) { return theme.get() == iconTheme; } ); if ( pos != mIconThemes.end() ) { - if ( *pos == mCurrentTheme ) { + if ( pos->get() == mCurrentTheme ) { mCurrentTheme = mFallbackTheme; mFallbackTheme = nullptr; - } else if ( *pos == mFallbackTheme ) { + } else if ( pos->get() == mFallbackTheme ) { mFallbackTheme = nullptr; } mIconThemes.erase( pos ); @@ -102,7 +99,10 @@ void UIIconThemeManager::remove( UIIconTheme* iconTheme ) { } bool UIIconThemeManager::isPresent( UIIconTheme* iconTheme ) { - return std::find( mIconThemes.begin(), mIconThemes.end(), iconTheme ) != mIconThemes.end(); + return std::find_if( mIconThemes.begin(), mIconThemes.end(), + [iconTheme]( const UIIconThemePtr& theme ) { + return theme.get() == iconTheme; + } ) != mIconThemes.end(); } }} // namespace EE::UI diff --git a/src/eepp/ui/uiimage.cpp b/src/eepp/ui/uiimage.cpp index fb25d9510..fdeb8523b 100644 --- a/src/eepp/ui/uiimage.cpp +++ b/src/eepp/ui/uiimage.cpp @@ -1,8 +1,8 @@ #include -#include #include #include #include +#include #include #include #include @@ -38,15 +38,19 @@ std::string getTextureCacheName( const Network::URI& uri ) { return filePath; } -Texture* loadFileTextureCached( const std::string& filePath, const std::string& cacheName ) { +TexturePtr loadFileTextureCached( const ResourceScopePtr& scope, const std::string& filePath, + const std::string& cacheName ) { static std::mutex loadMutex; std::lock_guard lock( loadMutex ); - if ( Texture* texture = TextureFactory::instance()->getByName( cacheName ) ) + if ( TexturePtr texture = scope->findTexture( cacheName ) ) return texture; - return TextureFactory::instance()->loadFromFile( + TexturePtr texture = TextureFactory::instance()->loadFromFile( filePath, false, Texture::ClampMode::ClampToEdge, false, false ); + if ( texture ) + scope->publishLocal( cacheName, texture ); + return texture; } } // namespace @@ -60,14 +64,7 @@ UIImage* UIImage::NewWithTag( const std::string& tag ) { } UIImage::UIImage( const std::string& tag ) : - UIWidget( tag ), - mScaleType( UIScaleType::None ), - mDrawable( NULL ), - mColor(), - mAlignOffset( 0, 0 ), - mResourceChangeCb( 0 ), - mDrawableOwner( false ), - mAsyncImageAlive( std::make_shared>( true ) ) { + UIWidget( tag ), mScaleType( UIScaleType::None ), mColor(), mAlignOffset( 0, 0 ) { mFlags |= UI_AUTO_SIZE; applyDefaultTheme(); @@ -78,7 +75,7 @@ UIImage::UIImage() : UIImage( "image" ) {} UIImage::~UIImage() { if ( mAsyncImageAlive ) mAsyncImageAlive->store( false, std::memory_order_release ); - safeDeleteDrawable(); + clearDrawable(); } Uint32 UIImage::getType() const { @@ -89,16 +86,15 @@ bool UIImage::isType( const Uint32& type ) const { return UIImage::getType() == type ? true : UIWidget::isType( type ); } -UIImage* UIImage::setDrawable( Drawable* drawable, bool ownIt ) { +UIImage* UIImage::setDrawable( DrawablePtr drawable ) { if ( drawable == mDrawable ) return this; Sizef oldSize( mSize ); - safeDeleteDrawable(); + clearDrawable(); - mDrawable = drawable; - mDrawableOwner = ownIt; + mDrawable = std::move( drawable ); sendCommonEvent( Event::OnResourceChange ); if ( mDrawable ) { @@ -106,17 +102,15 @@ UIImage* UIImage::setDrawable( Drawable* drawable, bool ownIt ) { if ( !isSubscribedForScheduledUpdate() ) subscribeScheduledUpdate(); - mResourceChangeCb = - static_cast( mDrawable )->pushEventsCallback( [this]( auto, auto, auto ) { - invalidateDraw(); - } ); + mSpriteChangeCb = + static_cast( mDrawable.get() ) + ->pushEventsCallback( [this]( auto, auto, auto ) { invalidateDraw(); } ); } else { if ( mDrawable->isDrawableResource() ) { - mResourceChangeCb = - static_cast( mDrawable ) - ->pushResourceChangeCallback( [this]( auto, auto event, auto res ) { - onDrawableResourceEvent( event, res ); - } ); + mResourceChangeConnection = + static_cast( mDrawable.get() ) + ->connectResourceChange( + [this]( DrawableResource& ) { onDrawableResourceChange(); } ); } if ( isSubscribedForScheduledUpdate() ) @@ -136,6 +130,10 @@ UIImage* UIImage::setDrawable( Drawable* drawable, bool ownIt ) { return this; } +UIImage* UIImage::setDrawable( TexturePtr texture ) { + return setDrawable( texture ? TextureDrawable::New( std::move( texture ) ) : DrawablePtr{} ); +} + void UIImage::onAutoSize() { if ( nullptr == mDrawable ) return; @@ -267,7 +265,7 @@ void UIImage::setAlpha( const Float& alpha ) { mColor.a = (Uint8)alpha; } -Drawable* UIImage::getDrawable() const { +const DrawablePtr& UIImage::getDrawable() const { return mDrawable; } @@ -305,34 +303,25 @@ void UIImage::autoAlign() { } } -void UIImage::safeDeleteDrawable() { +void UIImage::clearDrawable() { if ( mDrawable && mDrawable->getDrawableType() == Drawable::SPRITE ) { - static_cast( mDrawable )->popEventsCallback( mResourceChangeCb ); - } else if ( mDrawable && mDrawable->isDrawableResource() ) { - static_cast( mDrawable )->popResourceChangeCallback( mResourceChangeCb ); - mResourceChangeCb = 0; + static_cast( mDrawable.get() )->popEventsCallback( mSpriteChangeCb ); + mSpriteChangeCb = 0; } - if ( mDrawable && mDrawableOwner ) { - eeSAFE_DELETE( mDrawable ); - - mDrawableOwner = false; - } + mResourceChangeConnection.disconnect(); + mDrawable.reset(); } -void UIImage::onDrawableResourceEvent( DrawableResource::Event event, DrawableResource* ) { - if ( event == DrawableResource::Change ) { - runOnMainThread( [this] { - auto s = mSize; - onAutoSize(); - calcDestSize(); - if ( mSize != s ) - notifyLayoutAttrChangeParent( LayoutInvalidation::ParentReplacedFormatting ); - invalidateDraw(); - } ); - } else if ( event == DrawableResource::Unload ) { - mDrawable = NULL; - } +void UIImage::onDrawableResourceChange() { + runOnMainThread( [this] { + auto s = mSize; + onAutoSize(); + calcDestSize(); + if ( mSize != s ) + notifyLayoutAttrChangeParent( LayoutInvalidation::ParentReplacedFormatting ); + invalidateDraw(); + } ); } bool UIImage::loadFileDrawable( const Network::URI& uri ) { @@ -344,24 +333,27 @@ bool UIImage::loadFileDrawable( const Network::URI& uri ) { Uint64 loadId = ++mRemoteImageLoadId; std::string filePath = uri.getFSPath(); std::string cacheName = getTextureCacheName( uri ); - if ( Texture* texture = TextureFactory::instance()->getByName( cacheName ) ) { - setDrawable( texture, false ); + ResourceScopePtr resourceScope = scene->getResourceScope(); + if ( TexturePtr texture = resourceScope->findTexture( cacheName ) ) { + setDrawable( std::move( texture ) ); return true; } auto resourceState = scene->getAsyncResourceLoadState(); Uint64 resourceGeneration = resourceState ? resourceState->generation.load( std::memory_order_acquire ) : 0; + if ( !mAsyncImageAlive ) + mAsyncImageAlive = std::make_shared>( true ); auto alive = mAsyncImageAlive; - scene->getThreadPool()->run( [resourceState, resourceGeneration, alive, loadId, + scene->getThreadPool()->run( [resourceState, resourceGeneration, resourceScope, alive, loadId, filePath = std::move( filePath ), cacheName = std::move( cacheName ), this] { if ( !UISceneNode::isAsyncResourceLoadCurrent( resourceState, resourceGeneration ) || !alive || !alive->load( std::memory_order_acquire ) ) return; - Texture* texture = loadFileTextureCached( filePath, cacheName ); + TexturePtr texture = loadFileTextureCached( resourceScope, filePath, cacheName ); if ( texture == nullptr ) return; @@ -371,7 +363,7 @@ bool UIImage::loadFileDrawable( const Network::URI& uri ) { loadId != mRemoteImageLoadId ) return; - setDrawable( texture, false ); + setDrawable( std::move( texture ) ); } ); } ); @@ -384,56 +376,38 @@ void UIImage::loadRemoteDrawable( const Network::URI& uri ) { return; std::string url = uri.toString(); - if ( Texture* texture = TextureFactory::instance()->getByName( url ) ) { - if ( mDrawable != texture ) { + if ( TexturePtr texture = scene->getResourceScope()->findTexture( url ) ) { + TextureDrawable* current = + mDrawable && mDrawable->getDrawableType() == Drawable::TEXTUREDRAWABLE + ? static_cast( mDrawable.get() ) + : nullptr; + if ( !current || current->getTexture() != texture ) { ++mRemoteImageLoadId; - setDrawable( texture, false ); + setDrawable( std::move( texture ) ); } return; } - - auto resourceState = scene->getAsyncResourceLoadState(); - Uint64 resourceGeneration = - resourceState ? resourceState->generation.load( std::memory_order_acquire ) : 0; - Uint64 loadId = ++mRemoteImageLoadId; - auto alive = mAsyncImageAlive; - Texture* texture = TextureFactory::instance()->createEmptyTexture( - 1, 1, 4, Color::Transparent, false, Texture::ClampMode::ClampToEdge, false, false, url ); - if ( texture ) - setDrawable( texture, false ); - - Http::Request::FieldTable headers; - if ( !scene->getReferer().empty() ) - headers["referer"] = scene->getReferer().toString(); - Http::getAsync( - [resourceState, resourceGeneration, alive, loadId, texture, url = std::move( url ), - this]( const Http&, Http::Request&, Http::Response& response ) { - if ( !UISceneNode::isAsyncResourceLoadCurrent( resourceState, resourceGeneration ) || - !alive || !alive->load( std::memory_order_acquire ) || texture == nullptr ) - return; - - if ( response.isOK() && !response.getBody().empty() ) { - std::string imageData( response.getBody() ); - UISceneNode::runAsyncResourceOnMainThread( - resourceState, resourceGeneration, - [alive, loadId, texture, imageData = std::move( imageData ), - this]( UISceneNode* ) mutable { - if ( !alive || !alive->load( std::memory_order_acquire ) || - loadId != mRemoteImageLoadId || mDrawable != texture ) - return; - - Image image( reinterpret_cast( imageData.data() ), - imageData.size() ); - if ( image.getPixels() != nullptr ) - texture->replace( &image ); - } ); - } else { + WebResourceRequest request; + request.uri = uri; + request.kind = WebResourceKind::Image; + request.proxy = Http::getEnvProxyURI(); + TexturePtr texture = scene->requestWebTexture( + std::move( request ), [url = std::move( url )]( const WebResourceResult& result ) { + if ( !result.success ) Log::debug( "UIImage::loadRemoteDrawable: could not download image: %s. Error: " "%d\n%s", - url, response.getStatus(), response.getBody() ); - } - }, - uri, Seconds( 5 ), {}, headers, "", true, Http::getEnvProxyURI() ); + url, result.status, result.error ); + } ); + if ( texture ) { + TextureDrawable* current = + mDrawable && mDrawable->getDrawableType() == Drawable::TEXTUREDRAWABLE + ? static_cast( mDrawable.get() ) + : nullptr; + if ( !current || current->getTexture() != texture ) { + ++mRemoteImageLoadId; + setDrawable( std::move( texture ) ); + } + } } void UIImage::onSizeChange() { @@ -498,7 +472,7 @@ std::string UIImage::getPropertyString( const PropertyDefinition* propertyDef, void UIImage::scheduledUpdate( const Time& time ) { if ( mDrawable && mDrawable->getDrawableType() == Drawable::SPRITE ) - static_cast( mDrawable )->update( time ); + static_cast( mDrawable.get() )->update( time ); } std::vector UIImage::getPropertiesImplemented() const { @@ -529,7 +503,6 @@ bool UIImage::applyProperty( const StyleSheetProperty& attribute ) { std::string path( attribute.getValue() ); URI uri( path ); - bool ownIt; UISceneNode* scene = getUISceneNode(); if ( scene && uri.getScheme().empty() && !scene->getURI().empty() ) { @@ -545,32 +518,31 @@ bool UIImage::applyProperty( const StyleSheetProperty& attribute ) { if ( mDeferLoad && uri.getScheme() == "file" && loadFileDrawable( uri ) ) break; - Drawable* createdDrawable = + DrawablePtr createdDrawable = StyleSheetSpecification::instance()->getDrawableImageParser().createDrawable( - path, mSize, ownIt, this ); + path, mSize, this ); if ( createdDrawable ) { - setDrawable( createdDrawable, ownIt ); + setDrawable( std::move( createdDrawable ) ); } else { - Drawable* res = NULL; - if ( NULL != ( res = DrawableSearcher::searchByName( - path, false, scene ? scene->getReferer() : URI() ) ) ) - setDrawable( res, res->getDrawableType() == Drawable::SPRITE ); + if ( scene ) { + setDrawable( scene->getDrawableResolver().resolve( path ) ); + } else { + DrawableResolver resolver( defaultResourceScope() ); + setDrawable( resolver.resolve( path ) ); + } } break; } case PropertyId::Icon: { std::string val = attribute.asString(); - Drawable* icon = NULL; - bool ownIt; UIIcon* iconF = getUISceneNode()->findIcon( val ); if ( iconF ) { setDrawable( - iconF->getSize( mSize.getHeight() - mPaddingPx.Top - mPadding.Bottom ) ); - } else if ( NULL != - ( icon = StyleSheetSpecification::instance() - ->getDrawableImageParser() - .createDrawable( val, getPixelsSize(), ownIt, this ) ) ) { - setDrawable( icon, ownIt ); + iconF->createDrawable( mSize.getHeight() - mPaddingPx.Top - mPadding.Bottom ) ); + } else if ( DrawablePtr icon = StyleSheetSpecification::instance() + ->getDrawableImageParser() + .createDrawable( val, getPixelsSize(), this ) ) { + setDrawable( std::move( icon ) ); } break; } diff --git a/src/eepp/ui/uilistbox.cpp b/src/eepp/ui/uilistbox.cpp index 0e8a42063..a9c67ee17 100644 --- a/src/eepp/ui/uilistbox.cpp +++ b/src/eepp/ui/uilistbox.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include diff --git a/src/eepp/ui/uimarkdownview.cpp b/src/eepp/ui/uimarkdownview.cpp index 56e5f4b95..70b9458b6 100644 --- a/src/eepp/ui/uimarkdownview.cpp +++ b/src/eepp/ui/uimarkdownview.cpp @@ -17,6 +17,7 @@ UIMarkdownView* UIMarkdownView::New() { UIMarkdownView::UIMarkdownView() : UILinearLayout( "markdownview", UIOrientation::Vertical ) { mWidthPolicy = SizePolicy::MatchParent; mHeightPolicy = SizePolicy::WrapContent; + getUISceneNode()->loadHTMLBaseCSS(); } Uint32 UIMarkdownView::getType() const { @@ -29,7 +30,7 @@ bool UIMarkdownView::isType( const Uint32& type ) const { void UIMarkdownView::loadFromString( std::string_view markdown ) { closeAllChildren(); - auto xhtml = Tools::HTMLFormatter::HTMLtoXML( Markdown::toXHTML( markdown ) ); + auto xhtml = Tools::HTMLFormatter::HTMLBodyToXML( Markdown::toXHTML( markdown ) ); getUISceneNode()->loadLayoutFromString( xhtml, this ); } diff --git a/src/eepp/ui/uimenu.cpp b/src/eepp/ui/uimenu.cpp index 469197ff0..68e5c5831 100644 --- a/src/eepp/ui/uimenu.cpp +++ b/src/eepp/ui/uimenu.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -58,20 +57,20 @@ void UIMenu::onPaddingChange() { widgetsSetPos(); } -UIMenuItem* UIMenu::createMenuItem( const String& text, Drawable* icon, +UIMenuItem* UIMenu::createMenuItem( const String& text, DrawablePtr icon, const String& shortcutText ) { UIMenuItem* widget = UIMenuItem::New(); widget->setHorizontalAlign( UI_HALIGN_LEFT ); widget->setParent( this ); widget->setIconMinimumSize( mIconMinSize ); - widget->setIcon( icon ); + widget->setIcon( std::move( icon ) ); widget->setText( text ); widget->setShortcutText( shortcutText ); return widget; } -UIMenuItem* UIMenu::add( const String& text, Drawable* icon, const String& shortcutText ) { - UIMenuItem* menuItem = createMenuItem( text, icon, shortcutText ); +UIMenuItem* UIMenu::add( const String& text, DrawablePtr icon, const String& shortcutText ) { + UIMenuItem* menuItem = createMenuItem( text, std::move( icon ), shortcutText ); add( menuItem ); return menuItem; } @@ -113,19 +112,19 @@ UIMenuRadioButton* UIMenu::addRadioButton( const String& text, const bool& activ return radioButton; } -UIMenuSubMenu* UIMenu::createSubMenu( const String& text, Drawable* icon, UIMenu* subMenu ) { +UIMenuSubMenu* UIMenu::createSubMenu( const String& text, DrawablePtr icon, UIMenu* subMenu ) { UIMenuSubMenu* menu = UIMenuSubMenu::New(); menu->setHorizontalAlign( UI_HALIGN_LEFT ); menu->setParent( this ); menu->setIconMinimumSize( mIconMinSize ); - menu->setIcon( icon ); + menu->setIcon( std::move( icon ) ); menu->setText( text ); menu->setSubMenu( subMenu ); return menu; } -UIMenuSubMenu* UIMenu::addSubMenu( const String& text, Drawable* icon, UIMenu* subMenu ) { - UIMenuSubMenu* menu = createSubMenu( text, icon, subMenu ); +UIMenuSubMenu* UIMenu::addSubMenu( const String& text, DrawablePtr icon, UIMenu* subMenu ) { + UIMenuSubMenu* menu = createSubMenu( text, std::move( icon ), subMenu ); add( menu ); return menu; } @@ -269,8 +268,8 @@ void UIMenu::removeAll() { resizeMe(); } -void UIMenu::insert( const String& text, Drawable* icon, const Uint32& index ) { - insert( createMenuItem( text, icon ), index ); +void UIMenu::insert( const String& text, DrawablePtr icon, const Uint32& index ) { + insert( createMenuItem( text, std::move( icon ) ), index ); } void UIMenu::insert( UIWidget* widget, const Uint32& index ) { @@ -553,17 +552,23 @@ Uint32 UIMenu::onKeyDown( const KeyEvent& event ) { return UIWidget::onKeyDown( event ); } -static Drawable* getIconDrawable( const std::string& name, UIIconThemeManager* iconThemeManager ) { - Drawable* iconDrawable = nullptr; - if ( nullptr != iconThemeManager ) { - UIIcon* icon = iconThemeManager->findIcon( name ); +static DrawablePtr getIconDrawable( const std::string& name, UISceneNode* sceneNode ) { + DrawablePtr iconDrawable; + if ( sceneNode ) { + UIIcon* icon = sceneNode->findIcon( name ); if ( icon ) { // TODO: Fix size - iconDrawable = icon->getSize( PixelDensity::dpToPx( 16 ) ); + iconDrawable = icon->createDrawable( PixelDensity::dpToPx( 16 ) ); + } + } + if ( !iconDrawable ) { + if ( sceneNode ) { + iconDrawable = sceneNode->getDrawableResolver().resolve( name ); + } else { + DrawableResolver resolver( defaultResourceScope() ); + iconDrawable = resolver.resolve( name ); } } - if ( nullptr == iconDrawable ) - iconDrawable = DrawableSearcher::searchByName( name ); return iconDrawable; } @@ -578,8 +583,7 @@ void UIMenu::loadFromXmlNode( const pugi::xml_node& node ) { std::string text( item.attribute( "text" ).as_string() ); std::string icon( item.attribute( "icon" ).as_string() ); if ( nullptr != mSceneNode && mSceneNode->isUISceneNode() ) - add( getTranslatorString( text ), - getIconDrawable( icon, getUISceneNode()->getUIIconThemeManager() ) ); + add( getTranslatorString( text ), getIconDrawable( icon, getUISceneNode() ) ); } else if ( name == "menuseparator" || name == "separator" ) { addSeparator(); } else if ( name == "menucheckbox" || name == "checkbox" ) { @@ -597,8 +601,7 @@ void UIMenu::loadFromXmlNode( const pugi::xml_node& node ) { if ( nullptr != getDrawInvalidator() ) subMenu->setParent( getDrawInvalidator() ); subMenu->loadFromXmlNode( item ); - addSubMenu( getTranslatorString( text ), - getIconDrawable( icon, getUISceneNode()->getUIIconThemeManager() ), + addSubMenu( getTranslatorString( text ), getIconDrawable( icon, getUISceneNode() ), subMenu ); } } diff --git a/src/eepp/ui/uimessagebox.cpp b/src/eepp/ui/uimessagebox.cpp index 444253374..7381b8f33 100644 --- a/src/eepp/ui/uimessagebox.cpp +++ b/src/eepp/ui/uimessagebox.cpp @@ -155,16 +155,17 @@ void UIMessageBox::setTheme( UITheme* theme ) { mButtonCancel->setTheme( theme ); if ( i18n( "msg_box_retry", "Retry" ) != mButtonOK->getText() ) { - Drawable* okIcon = getUISceneNode()->findIconDrawable( "ok", PixelDensity::dpToPxI( 16 ) ); - Drawable* cancelIcon = + DrawablePtr okIcon = + getUISceneNode()->findIconDrawable( "ok", PixelDensity::dpToPxI( 16 ) ); + DrawablePtr cancelIcon = getUISceneNode()->findIconDrawable( "cancel", PixelDensity::dpToPxI( 16 ) ); if ( NULL != okIcon ) { - mButtonOK->setIcon( okIcon ); + mButtonOK->setIcon( std::move( okIcon ) ); } if ( NULL != cancelIcon ) { - mButtonCancel->setIcon( cancelIcon ); + mButtonCancel->setIcon( std::move( cancelIcon ) ); } } diff --git a/src/eepp/ui/uinode.cpp b/src/eepp/ui/uinode.cpp index 997eb2e63..8fe08d863 100644 --- a/src/eepp/ui/uinode.cpp +++ b/src/eepp/ui/uinode.cpp @@ -725,8 +725,8 @@ UINodeDrawable* UINode::setBackgroundFillEnabled( bool enabled ) { return mBackground; } -UINode* UINode::setBackgroundDrawable( Drawable* drawable, bool ownIt, int index ) { - setBackgroundFillEnabled( true )->setDrawable( index, drawable, ownIt ); +UINode* UINode::setBackgroundDrawable( DrawablePtr drawable, int index ) { + setBackgroundFillEnabled( true )->setDrawable( index, std::move( drawable ) ); return this; } @@ -838,8 +838,8 @@ UINodeDrawable* UINode::setForegroundFillEnabled( bool enabled ) { return mForeground; } -UINode* UINode::setForegroundDrawable( Drawable* drawable, bool ownIt, int index ) { - setForegroundFillEnabled( true )->setDrawable( index, drawable, ownIt ); +UINode* UINode::setForegroundDrawable( DrawablePtr drawable, int index ) { + setForegroundFillEnabled( true )->setDrawable( index, std::move( drawable ) ); return this; } @@ -1253,12 +1253,7 @@ UINode* UINode::setThemeSkin( UITheme* Theme, const std::string& skinName ) { UINode* UINode::setSkin( const UISkin& Skin ) { removeSkin(); - - writeNodeFlag( NODE_FLAG_SKIN_OWNER, 1 ); - - UISkin* SkinCopy = const_cast( &Skin )->clone(); - - mSkinState = UISkinState::New( SkinCopy ); + mSkinState = UISkinState::New( Skin.cloneSkin() ); onThemeLoaded(); @@ -1278,7 +1273,7 @@ UINode* UINode::setSkin( UISkin* skin ) { removeSkin(); - mSkinState = UISkinState::New( skin ); + mSkinState = UISkinState::New( skin->cloneSkin() ); mSkinState->setState( InitialState ); onThemeLoaded(); @@ -1302,12 +1297,6 @@ const Color& UINode::getSkinColor() const { } void UINode::removeSkin() { - if ( NULL != mSkinState && ( mNodeFlags & NODE_FLAG_SKIN_OWNER ) ) { - UISkin* tSkin = mSkinState->getSkin(); - - eeSAFE_DELETE( tSkin ); - } - eeSAFE_DELETE( mSkinState ); } diff --git a/src/eepp/ui/uinodedrawable.cpp b/src/eepp/ui/uinodedrawable.cpp index bad00f63e..5f3b5f7ce 100644 --- a/src/eepp/ui/uinodedrawable.cpp +++ b/src/eepp/ui/uinodedrawable.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -100,10 +101,6 @@ UINodeDrawable::~UINodeDrawable() { } void UINodeDrawable::clearDrawables() { - for ( auto& drawable : mGroup ) { - eeDelete( drawable.second ); - } - mGroup.clear(); mBackgroundColor.setColor( Color::Transparent ); } @@ -124,24 +121,24 @@ UINodeDrawable::LayerDrawable* UINodeDrawable::getLayer( int index ) { auto it = mGroup.find( index ); if ( it == mGroup.end() ) { - mGroup[index] = UINodeDrawable::LayerDrawable::New( this ); + mGroup[index] = LayerDrawablePtr( UINodeDrawable::LayerDrawable::New( this ) ); // HTML background-repeat defaults to "repeat", non-HTML to // "no-repeat". The LayerDrawable constructor uses NoRepeat // (the eepp/non-HTML default), so reset it for Html mode. if ( mBackgroundMode == BackgroundMode::Html ) { - auto* layer = mGroup[index]; + auto* layer = mGroup[index].get(); layer->setRepeatX( RepeatX::Repeat ); layer->setRepeatY( RepeatY::Repeat ); } } - return mGroup[index]; + return mGroup[index].get(); } -void UINodeDrawable::setDrawable( int index, Drawable* drawable, bool ownIt ) { +void UINodeDrawable::setDrawable( int index, DrawablePtr drawable ) { if ( drawable != getLayer( index )->getDrawable() ) { - getLayer( index )->setDrawable( drawable, ownIt ); + getLayer( index )->setDrawable( std::move( drawable ) ); } } @@ -245,7 +242,7 @@ void UINodeDrawable::setBackgroundMode( BackgroundMode mode ) { // still carry the LayerDrawable default (NoRepeat for both axes). if ( mode == BackgroundMode::Html ) { for ( auto& entry : mGroup ) { - auto* layer = entry.second; + auto* layer = entry.second.get(); if ( layer->getRepeatX() == RepeatX::NoRepeat && layer->getRepeatY() == RepeatY::NoRepeat ) { layer->setRepeatX( RepeatX::Repeat ); @@ -331,7 +328,7 @@ void UINodeDrawable::draw( const Vector2f& position, const Sizef& size, const Ui // "The background images are drawn on stacking context layers on top of each other. The first // layer specified is drawn as if it is closest to the user." for ( auto drawableIt = mGroup.rbegin(); drawableIt != mGroup.rend(); ++drawableIt ) { - UINodeDrawable::LayerDrawable* drawable = drawableIt->second; + UINodeDrawable::LayerDrawable* drawable = drawableIt->second.get(); bool clipContent = mBackgroundMode == BackgroundMode::Html && drawable->getClip() == LayerDrawable::Clip::ContentBox; @@ -411,9 +408,6 @@ UINodeDrawable::LayerDrawable::LayerDrawable( UINodeDrawable* container ) : mPositionY( "0px" ), mSizeEq( "auto" ), mNeedsUpdate( false ), - mOwnsDrawable( false ), - mDrawable( NULL ), - mResourceChangeCbId( 0 ), mRepeatX( RepeatX::NoRepeat ), mRepeatY( RepeatY::NoRepeat ), mOriginEq( "padding-box" ), @@ -421,20 +415,10 @@ UINodeDrawable::LayerDrawable::LayerDrawable( UINodeDrawable* container ) : mAttachmentEq( "scroll" ), mOrigin( Origin::PaddingBox ), mClip( Clip::BorderBox ), - mAttachment( Attachment::Scroll ), - mAsyncDrawableAlive( std::make_shared>( true ) ) {} + mAttachment( Attachment::Scroll ) {} UINodeDrawable::LayerDrawable::~LayerDrawable() { - if ( mAsyncDrawableAlive ) - mAsyncDrawableAlive->store( false, std::memory_order_release ); - - if ( NULL != mDrawable && 0 != mResourceChangeCbId && mDrawable->isDrawableResource() ) { - reinterpret_cast( mDrawable ) - ->popResourceChangeCallback( mResourceChangeCbId ); - } - - if ( mOwnsDrawable ) - eeSAFE_DELETE( mDrawable ); + mResourceChangeConnection.disconnect(); } void UINodeDrawable::LayerDrawable::draw() { @@ -497,8 +481,8 @@ void UINodeDrawable::LayerDrawable::draw( const Vector2f& position, const Sizef& mDrawable->draw( Vector2f( xPos, effectivePos.y + mOffset.y ), tileSz ); break; case RepeatY::Repeat: - repeatYdraw( mDrawable, effectivePos, Vector2f( xPos - effectivePos.x, mOffset.y ), - mSize, tileSz ); + repeatYdraw( mDrawable.get(), effectivePos, + Vector2f( xPos - effectivePos.x, mOffset.y ), mSize, tileSz ); break; case RepeatY::Space: { if ( drawH <= 0 ) @@ -610,7 +594,7 @@ void UINodeDrawable::LayerDrawable::setSize( const Sizef& size ) { } } -Drawable* UINodeDrawable::LayerDrawable::getDrawable() const { +const DrawablePtr& UINodeDrawable::LayerDrawable::getDrawable() const { return mDrawable; } @@ -618,44 +602,30 @@ const std::string& UINodeDrawable::LayerDrawable::getDrawableRef() const { return mDrawableRef; } -void UINodeDrawable::LayerDrawable::setDrawable( Drawable* drawable, const bool& ownIt ) { +void UINodeDrawable::LayerDrawable::setDrawable( DrawablePtr drawable ) { if ( drawable == mDrawable ) return; - if ( NULL != mDrawable ) { - if ( mDrawable->isDrawableResource() ) { - reinterpret_cast( mDrawable ) - ->popResourceChangeCallback( mResourceChangeCbId ); - } + mResourceChangeConnection.disconnect(); - if ( mOwnsDrawable ) { - eeSAFE_DELETE( mDrawable ); - } - } - - mDrawable = drawable; + mDrawable = std::move( drawable ); mDrawableRef = ""; - mOwnsDrawable = ownIt; invalidate(); if ( NULL != mDrawable && mDrawable->isDrawableResource() ) { - mResourceChangeCbId = - reinterpret_cast( mDrawable ) - ->pushResourceChangeCallback( - [this]( Uint32, DrawableResource::Event event, DrawableResource* ) { - invalidate(); - if ( event == DrawableResource::Event::Unload ) { - mResourceChangeCbId = 0; - mDrawable = NULL; - mOwnsDrawable = false; - } - } ); + mResourceChangeConnection = + reinterpret_cast( mDrawable.get() ) + ->connectResourceChange( [this]( DrawableResource& ) { invalidate(); } ); } } +void UINodeDrawable::LayerDrawable::setDrawable( TexturePtr texture ) { + setDrawable( texture ? TextureDrawable::New( std::move( texture ) ) : DrawablePtr{} ); +} + void UINodeDrawable::LayerDrawable::setDrawable( const std::string& drawableRef ) { if ( drawableRef == "none" ) { - setDrawable( nullptr, false ); + setDrawable( DrawablePtr{} ); return; } @@ -664,10 +634,9 @@ void UINodeDrawable::LayerDrawable::setDrawable( const std::string& drawableRef return; } - bool ownIt; - Drawable* drawable = createDrawable( drawableRef, mSize, ownIt ); + DrawablePtr drawable = createDrawable( drawableRef, mSize ); - setDrawable( drawable, ownIt ); + setDrawable( std::move( drawable ) ); mDrawableRef = drawableRef; } @@ -705,64 +674,42 @@ bool UINodeDrawable::LayerDrawable::loadRemoteDrawable( const std::string& value return true; std::string url = uri.toString(); - if ( Texture* texture = TextureFactory::instance()->getByName( url ) ) { - if ( mDrawable != texture ) { - ++mRemoteDrawableLoadId; - setDrawable( texture, false ); - } + if ( TexturePtr texture = scene->getResourceScope()->findTexture( url ) ) { + TextureDrawable* current = + mDrawable && mDrawable->getDrawableType() == Drawable::TEXTUREDRAWABLE + ? static_cast( mDrawable.get() ) + : nullptr; + if ( !current || current->getTexture() != texture ) + setDrawable( std::move( texture ) ); return true; } - - auto resourceState = scene->getAsyncResourceLoadState(); - Uint64 resourceGeneration = - resourceState ? resourceState->generation.load( std::memory_order_acquire ) : 0; - Uint64 loadId = ++mRemoteDrawableLoadId; - auto alive = mAsyncDrawableAlive; - Texture* texture = TextureFactory::instance()->createEmptyTexture( - 1, 1, 4, Color::Transparent, false, Texture::ClampMode::ClampToEdge, false, false, url ); - if ( texture ) - setDrawable( texture, false ); - - Http::Request::FieldTable headers; - if ( !scene->getReferer().empty() ) - headers["referer"] = scene->getReferer().toString(); - Http::getAsync( - [resourceState, resourceGeneration, alive, loadId, texture, url = std::move( url ), - this]( const Http&, Http::Request&, Http::Response& response ) { - if ( !UISceneNode::isAsyncResourceLoadCurrent( resourceState, resourceGeneration ) || - !alive || !alive->load( std::memory_order_acquire ) || texture == nullptr ) - return; - - if ( response.isOK() && !response.getBody().empty() ) { - std::string imageData( response.getBody() ); - UISceneNode::runAsyncResourceOnMainThread( - resourceState, resourceGeneration, - [alive, loadId, texture, imageData = std::move( imageData ), - this]( UISceneNode* ) mutable { - if ( !alive || !alive->load( std::memory_order_acquire ) || - loadId != mRemoteDrawableLoadId || mDrawable != texture ) - return; - - Image image( reinterpret_cast( imageData.data() ), - imageData.size() ); - if ( image.getPixels() != nullptr ) - texture->replace( &image ); - } ); - } else { + WebResourceRequest request; + request.uri = uri; + request.kind = WebResourceKind::Image; + request.proxy = Http::getEnvProxyURI(); + TexturePtr texture = scene->requestWebTexture( + std::move( request ), [url = std::move( url )]( const WebResourceResult& result ) { + if ( !result.success ) Log::debug( "UINodeDrawable::LayerDrawable::loadRemoteDrawable: could not " "download image: %s. Error: %d\n%s", - url, response.getStatus(), response.getBody() ); - } - }, - uri, Seconds( 5 ), {}, headers, "", true, Http::getEnvProxyURI() ); + url, result.status, result.error ); + } ); + if ( texture ) { + TextureDrawable* current = + mDrawable && mDrawable->getDrawableType() == Drawable::TEXTUREDRAWABLE + ? static_cast( mDrawable.get() ) + : nullptr; + if ( !current || current->getTexture() != texture ) + setDrawable( std::move( texture ) ); + } return true; } -Drawable* UINodeDrawable::LayerDrawable::createDrawable( const std::string& value, - const Sizef& size, bool& ownIt ) { +DrawablePtr UINodeDrawable::LayerDrawable::createDrawable( const std::string& value, + const Sizef& size ) { return CSS::StyleSheetSpecification::instance()->getDrawableImageParser().createDrawable( - value, size, ownIt, mContainer->getOwner() ); + value, size, mContainer->getOwner() ); } const Vector2f& UINodeDrawable::LayerDrawable::getOffset() const { diff --git a/src/eepp/ui/uiprogressbar.cpp b/src/eepp/ui/uiprogressbar.cpp index e4a848fb2..53a304b97 100644 --- a/src/eepp/ui/uiprogressbar.cpp +++ b/src/eepp/ui/uiprogressbar.cpp @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/src/eepp/ui/uipushbutton.cpp b/src/eepp/ui/uipushbutton.cpp index e65346603..ed85c12a3 100644 --- a/src/eepp/ui/uipushbutton.cpp +++ b/src/eepp/ui/uipushbutton.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -397,13 +396,13 @@ void UIPushButton::updateTextBox() { } } -UIPushButton* UIPushButton::setIcon( Drawable* icon, bool ownIt ) { +UIPushButton* UIPushButton::setIcon( DrawablePtr icon ) { if ( nullptr == mIcon || mIcon->getDrawable() != icon ) { if ( icon ) getIcon()->setPixelsSize( icon->getPixelsSize() ); if ( icon == nullptr && mIcon == nullptr ) return this; - getIcon()->setDrawable( icon, ownIt ); + getIcon()->setDrawable( std::move( icon ) ); updateTextBox(); } return this; @@ -681,18 +680,15 @@ bool UIPushButton::applyProperty( const StyleSheetProperty& attribute ) { break; case PropertyId::Icon: { const std::string& val = attribute.value(); - Drawable* icon = NULL; - bool ownIt; UIIcon* iconF = getUISceneNode()->findIcon( val ); if ( iconF ) { - setIcon( iconF->getSize( + setIcon( iconF->createDrawable( eemax( mSize.getHeight() - mPaddingPx.Top - mPadding.Bottom, - PixelDensity::dpToPxI( 16 ) ) ) ); - } else if ( NULL != - ( icon = StyleSheetSpecification::instance() - ->getDrawableImageParser() - .createDrawable( val, getPixelsSize(), ownIt, this ) ) ) { - setIcon( icon, ownIt ); + PixelDensity::dpToPxI( 16 ) ) ) ); + } else if ( DrawablePtr icon = StyleSheetSpecification::instance() + ->getDrawableImageParser() + .createDrawable( val, getPixelsSize(), this ) ) { + setIcon( std::move( icon ) ); } break; } diff --git a/src/eepp/ui/uirichtext.cpp b/src/eepp/ui/uirichtext.cpp index 244d59448..a8b2e3320 100644 --- a/src/eepp/ui/uirichtext.cpp +++ b/src/eepp/ui/uirichtext.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include diff --git a/src/eepp/ui/uiscenenode.cpp b/src/eepp/ui/uiscenenode.cpp index e59c57f81..b39dc158d 100644 --- a/src/eepp/ui/uiscenenode.cpp +++ b/src/eepp/ui/uiscenenode.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include @@ -33,6 +33,7 @@ #define PUGIXML_HEADER_ONLY #include +using namespace EE::Graphics; using namespace EE::Network; namespace EE { namespace UI { @@ -128,11 +129,11 @@ static void refreshWebViewDocumentLayoutAfterStyleChange( UIWidget* root ) { } } -UISceneNode* UISceneNode::New( EE::Window::Window* window ) { - return eeNew( UISceneNode, ( window ) ); +UISceneNode* UISceneNode::New( EE::Window::Window* window, bool importDefaultResources ) { + return eeNew( UISceneNode, ( window, importDefaultResources ) ); } -UISceneNode::UISceneNode( EE::Window::Window* window ) : +UISceneNode::UISceneNode( EE::Window::Window* window, bool importDefaultResources ) : SceneNode( window ), mRoot( NULL ), mIsLoading( false ), @@ -140,7 +141,14 @@ UISceneNode::UISceneNode( EE::Window::Window* window ) : mUIThemeManager( UIThemeManager::New() ), mUIIconThemeManager( UIIconThemeManager::New()->setFallbackThemeManager( mUIThemeManager ) ), mAsyncResourceLoadState( std::make_shared() ), + mImportDefaultResources( importDefaultResources ), + mResourceScope( ResourceScope::New() ), + mDrawableResolver( *this ), + mWebResourceCache( WebResourceCache::New() ), mKeyBindings( mWindow->getInput() ) { + if ( mImportDefaultResources ) + mResourceScope->importCatalog( defaultResourceScope().getLocalCatalog() ); + // Reset size since the SceneNode already set it but needs to set the size from zero to emit // the required events to its children. mSize = Sizef(); @@ -157,6 +165,8 @@ UISceneNode::UISceneNode( EE::Window::Window* window ) : mRoot->setParent( this )->setPosition( 0, 0 )->setId( "uiscenenode_root_node" ); mRoot->enableReportSizeChangeToChildren(); mAsyncResourceLoadState->owner.store( this, std::memory_order_release ); + mDocumentSessionId = mWebResourceCache->createSession(); + mUIThemeManager->setResourceScope( mResourceScope ); resizeNode( mWindow ); } @@ -172,6 +182,8 @@ UISceneNode::~UISceneNode() { mHostUISceneNode->unregisterChildUISceneNode( this ); clearFontFaces(); + if ( mWebResourceCache && mDocumentSessionId ) + mWebResourceCache->destroySession( mDocumentSessionId ); eeSAFE_DELETE( mUIThemeManager ); eeSAFE_DELETE( mUIIconThemeManager ); @@ -310,7 +322,7 @@ void UISceneNode::initializeEmbeddedFromHost( UISceneNode* hostScene ) { if ( hostThemeManager ) { mUIThemeManager->setDefaultFont( hostThemeManager->getDefaultFont() ); mUIThemeManager->setDefaultFontSize( hostThemeManager->getDefaultFontSize() ); - mUIThemeManager->setDefaultTheme( hostThemeManager->getDefaultTheme() ); + mUIThemeManager->setDefaultTheme( hostThemeManager->getDefaultThemeHandle() ); mUIThemeManager->setAutoApplyDefaultTheme( hostThemeManager->getAutoApplyDefaultTheme() ); mUIThemeManager->setDefaultEffectsEnabled( hostThemeManager->getDefaultEffectsEnabled() ); mUIThemeManager->setWidgetsFadeInTime( hostThemeManager->getWidgetsFadeInTime() ); @@ -750,6 +762,106 @@ void UISceneNode::setThreadPool( const std::shared_ptr& threadPool ) mThreadPool = threadPool; } +const ResourceScopePtr& UISceneNode::getResourceScope() const { + return mResourceScope; +} + +UISceneNode* UISceneNode::setResourceScope( ResourceScopePtr resourceScope ) { + mResourceScope = resourceScope ? std::move( resourceScope ) : ResourceScope::New(); + if ( mImportDefaultResources ) + mResourceScope->importCatalog( defaultResourceScope().getLocalCatalog() ); + mUIThemeManager->setResourceScope( mResourceScope ); + return this; +} + +UISceneNode* UISceneNode::setWebResourceCache( WebResourceCachePtr cache, + CachePartitionId partition ) { + if ( !cache ) + cache = WebResourceCache::New(); + if ( cache == mWebResourceCache && + ( partition == 0 || partition == cache->getSessionPartition( mDocumentSessionId ) ) ) + return this; + if ( mWebResourceCache && mDocumentSessionId ) + mWebResourceCache->destroySession( mDocumentSessionId ); + mWebResourceCache = std::move( cache ); + mDocumentSessionId = mWebResourceCache->createSession( partition ); + return this; +} + +Uint64 UISceneNode::beginDocumentNavigation( const URI& uri ) { + return mWebResourceCache && mDocumentSessionId + ? mWebResourceCache->beginNavigation( mDocumentSessionId, uri ) + : 0; +} + +Uint64 UISceneNode::getDocumentGeneration() const { + return mWebResourceCache && mDocumentSessionId + ? mWebResourceCache->getSessionGeneration( mDocumentSessionId ) + : 0; +} + +void UISceneNode::requestWebResource( WebResourceRequest request, + WebResourceCache::Callback callback ) { + if ( !mWebResourceCache || !mDocumentSessionId ) + return; + if ( !mReferer.empty() ) + request.headers.emplace( "referer", mReferer.toString() ); + std::string cookie = mCookieManager.getCookieHeader( request.uri.getAuthority() ); + if ( !cookie.empty() ) + request.headers["Cookie"] = std::move( cookie ); + auto resourceState = mAsyncResourceLoadState; + Uint64 resourceGeneration = + resourceState ? resourceState->generation.load( std::memory_order_acquire ) : 0; + auto wrapped = [resourceState, resourceGeneration, callback = std::move( callback ), + authority = request.uri.getAuthority()]( const WebResourceResult& result ) { + if ( !UISceneNode::isAsyncResourceLoadCurrent( resourceState, resourceGeneration ) ) + return; + UISceneNode* scene = resourceState->owner.load( std::memory_order_acquire ); + if ( !scene ) + return; + if ( !result.setCookie.empty() ) + scene->mCookieManager.storeCookiesFromHeader( authority, result.setCookie ); + if ( callback ) + callback( result ); + }; + mWebResourceCache->requestData( mDocumentSessionId, getDocumentGeneration(), + std::move( request ), std::move( wrapped ) ); +} + +TexturePtr UISceneNode::requestWebTexture( WebResourceRequest request, + WebResourceCache::Callback callback ) { + if ( !mWebResourceCache || !mDocumentSessionId ) + return {}; + if ( !mReferer.empty() ) + request.headers.emplace( "referer", mReferer.toString() ); + std::string cookie = mCookieManager.getCookieHeader( request.uri.getAuthority() ); + if ( !cookie.empty() ) + request.headers["Cookie"] = std::move( cookie ); + auto resourceState = mAsyncResourceLoadState; + Uint64 resourceGeneration = + resourceState ? resourceState->generation.load( std::memory_order_acquire ) : 0; + request.completionDispatcher = [resourceState, + resourceGeneration]( std::function completion ) { + UISceneNode::runAsyncResourceOnMainThread( + resourceState, resourceGeneration, + [completion = std::move( completion )]( UISceneNode* ) { completion(); } ); + }; + auto wrapped = [resourceState, resourceGeneration, callback = std::move( callback ), + authority = request.uri.getAuthority()]( const WebResourceResult& result ) { + if ( !UISceneNode::isAsyncResourceLoadCurrent( resourceState, resourceGeneration ) ) + return; + UISceneNode* scene = resourceState->owner.load( std::memory_order_acquire ); + if ( !scene ) + return; + if ( !result.setCookie.empty() ) + scene->mCookieManager.storeCookiesFromHeader( authority, result.setCookie ); + if ( callback ) + callback( result ); + }; + return mWebResourceCache->requestTexture( mDocumentSessionId, getDocumentGeneration(), + std::move( request ), std::move( wrapped ) ); +} + static std::string getErrorContext( size_t offset, std::string_view content ) { static constexpr std::size_t CONTEXT_LENGTH = 50; std::size_t minVal = offset >= CONTEXT_LENGTH ? offset - CONTEXT_LENGTH : 0; @@ -1312,12 +1424,24 @@ void UISceneNode::updateDirtyStyles() { void UISceneNode::updateDirtyStyleStates() { if ( !mDirtyStyleState.empty() ) { Clock clock; - for ( auto& node : mDirtyStyleState ) { - node->reportStyleStateChangeRecursive( mDirtyStyleStateCSSAnimations[node] ); + + // Applying a style state can create widgets (for example a button icon). Widget + // construction invalidates its style state, so iterating mDirtyStyleState directly would + // mutate and potentially reallocate its vector-backed unordered_dense storage. Snapshot the + // current pass and leave new invalidations queued for the outer invalidation-depth loop. + mDirtyStyleStateSnapshot.clear(); + mDirtyStyleStateSnapshot.reserve( mDirtyStyleState.size() ); + for ( UIWidget* node : mDirtyStyleState ) { + auto animations = mDirtyStyleStateCSSAnimations.find( node ); + mDirtyStyleStateSnapshot.emplace_back( + node, animations != mDirtyStyleStateCSSAnimations.end() && animations->second ); } mDirtyStyleState.clear(); mDirtyStyleStateCSSAnimations.clear(); + for ( const auto& dirtyState : mDirtyStyleStateSnapshot ) + dirtyState.first->reportStyleStateChangeRecursive( dirtyState.second ); + if ( mVerbose ) Log::debug( "CSS Style State Invalidated, reapplied state in %.2f ms", clock.getElapsedTime().asMilliseconds() ); @@ -1336,11 +1460,18 @@ UIIcon* UISceneNode::findIcon( const std::string& iconName ) { return getUIIconThemeManager()->findIcon( iconName ); } -Drawable* UISceneNode::findIconDrawable( const std::string& iconName, const size_t& drawableSize ) { +DrawablePtr UISceneNode::findIconDrawable( const std::string& iconName, + const size_t& drawableSize ) { UIIcon* icon = findIcon( iconName ); - if ( icon ) - return icon->getSize( drawableSize ); - return nullptr; + return icon ? icon->createDrawable( drawableSize ) : DrawablePtr{}; +} + +DrawableResolver& UISceneNode::getDrawableResolver() { + return mDrawableResolver; +} + +const DrawableResolver& UISceneNode::getDrawableResolver() const { + return mDrawableResolver; } CSS::MediaFeatures UISceneNode::getMediaFeatures() const { @@ -1405,7 +1536,7 @@ void UISceneNode::loadGlyphIcon( const StyleSheetStyleVector& styles ) { CSS::StyleSheetProperty glyphProp( *glyph ); if ( !familyProp.isEmpty() && !nameProp.isEmpty() && !glyphProp.isEmpty() ) { - Font* fontSearch = FontManager::instance()->getByName( familyProp.getValue() ); + Font* fontSearch = mResourceScope->findFont( familyProp.getValue() ).get(); if ( nullptr == fontSearch ) continue; @@ -1550,11 +1681,11 @@ void UISceneNode::loadFontFaces( const StyleSheetStyleVector& styles, URI baseUR fontStyle, static_cast( fontWeight ) ); }; auto registerLoadedFont = [this, authorFamily, fontStyle, - fontWeight]( FontTrueType* font ) { + fontWeight]( FontTrueTypePtr font ) { if ( font == nullptr || !font->loaded() ) return false; font->setVariableFontWeight( fontWeight ); - registerFontFaceAlias( authorFamily, fontStyle, fontWeight, font ); + registerFontFaceAlias( authorFamily, fontStyle, fontWeight, font.get() ); mFontFaces.push_back( font ); mRoot->reloadFontFamily(); return true; @@ -1581,12 +1712,13 @@ void UISceneNode::loadFontFaces( const StyleSheetStyleVector& styles, URI baseUR if ( isBase64 && !data.empty() ) { std::string decoded; Base64::decode( data, decoded ); - FontTrueType* font = FontTrueType::New( - makeInternalFontName( authorFamily, fontStyle, fontWeight ) ); + FontTrueTypePtr font = FontTrueType::New( + makeInternalFontName( authorFamily, fontStyle, fontWeight ), + *mResourceScope ); if ( font->loadFromMemory( &decoded[0], decoded.size() ) ) { registerLoadedFont( font ); } else - eeSAFE_DELETE( font ); + mResourceScope->eraseLocalFont( font.get() ); } } return; @@ -1598,14 +1730,14 @@ void UISceneNode::loadFontFaces( const StyleSheetStyleVector& styles, URI baseUR if ( String::startsWith( path, "file://" ) ) { std::string filePath( resolvedURI.getFSPath() ); - FontTrueType* font = - FontTrueType::New( makeInternalFontName( authorFamily, fontStyle, fontWeight ) ); + FontTrueTypePtr font = FontTrueType::New( + makeInternalFontName( authorFamily, fontStyle, fontWeight ), *mResourceScope ); if ( font->loadFromFile( filePath ) ) { registerLoadedFont( font ); runOnMainThread( [this] { mRoot->reloadFontFamily(); } ); } else - eeSAFE_DELETE( font ); + mResourceScope->eraseLocalFont( font.get() ); } else if ( String::startsWith( path, "http://" ) || String::startsWith( path, "https://" ) ) { std::string internalFontName( @@ -1613,56 +1745,58 @@ void UISceneNode::loadFontFaces( const StyleSheetStyleVector& styles, URI baseUR auto resourceState = mAsyncResourceLoadState; Uint64 resourceGeneration = resourceState ? resourceState->generation.load( std::memory_order_acquire ) : 0; - Http::getAsync( - [resourceState, resourceGeneration, internalFontName, authorFamily, fontStyle, - fontWeight, path]( const Http&, Http::Request&, Http::Response& response ) { - if ( !UISceneNode::isAsyncResourceLoadCurrent( resourceState, - resourceGeneration ) ) - return; + WebResourceRequest request; + request.uri = URI( path ); + request.kind = WebResourceKind::Font; + request.timeout = Seconds( 5 ); + requestWebResource( std::move( request ), [resourceState, resourceGeneration, + internalFontName, authorFamily, fontStyle, + fontWeight, + path]( const WebResourceResult& result ) { + if ( !UISceneNode::isAsyncResourceLoadCurrent( resourceState, resourceGeneration ) ) + return; - if ( response.isOK() && !response.getBody().empty() ) { - std::string fontData( response.getBody() ); - UISceneNode::runAsyncResourceOnMainThread( - resourceState, resourceGeneration, - [fontData = std::move( fontData ), internalFontName, authorFamily, - fontStyle, fontWeight]( UISceneNode* scene ) mutable { - FontTrueType* font = FontTrueType::New( internalFontName ); - if ( font->loadFromMemory( &fontData[0], fontData.size() ) && - font->loaded() ) { - font->setVariableFontWeight( fontWeight ); - scene->registerFontFaceAlias( authorFamily, fontStyle, - fontWeight, font ); - scene->mFontFaces.push_back( font ); - if ( scene->mRoot ) - scene->mRoot->reloadFontFamily(); - } else { - eeSAFE_DELETE( font ); - } - } ); - } else { - UISceneNode::runAsyncResourceOnMainThread( - resourceState, resourceGeneration, - [internalFontName, path, status = response.getStatus(), - statusDescription = - std::string( response.getStatusDescription() )]( UISceneNode* ) { - Log::error( "UISceneNode::loadFontFaces: Failed to load font " - "\"%s\", from: %s. Request response status code: %d " - "(%s)", - internalFontName, path, status, - statusDescription.c_str() ); - } ); - } - }, - URI( path ), Seconds( 5 ) ); + if ( result.success && result.data && !result.data->empty() ) { + std::string fontData( *result.data ); + UISceneNode::runAsyncResourceOnMainThread( + resourceState, resourceGeneration, + [fontData = std::move( fontData ), internalFontName, authorFamily, + fontStyle, fontWeight]( UISceneNode* scene ) mutable { + FontTrueTypePtr font = + FontTrueType::New( internalFontName, *scene->mResourceScope ); + if ( font->loadFromMemory( &fontData[0], fontData.size() ) && + font->loaded() ) { + font->setVariableFontWeight( fontWeight ); + scene->registerFontFaceAlias( authorFamily, fontStyle, fontWeight, + font.get() ); + scene->mFontFaces.push_back( font ); + if ( scene->mRoot ) + scene->mRoot->reloadFontFamily(); + } else { + scene->mResourceScope->eraseLocalFont( font.get() ); + } + } ); + } else { + UISceneNode::runAsyncResourceOnMainThread( + resourceState, resourceGeneration, + [internalFontName, path, status = result.status, + statusDescription = result.error]( UISceneNode* ) { + Log::error( "UISceneNode::loadFontFaces: Failed to load font " + "\"%s\", from: %s. Request response status code: %d " + "(%s)", + internalFontName, path, status, statusDescription.c_str() ); + } ); + } + } ); } else if ( VFS::instance()->fileExists( path ) ) { - FontTrueType* font = - FontTrueType::New( makeInternalFontName( authorFamily, fontStyle, fontWeight ) ); + FontTrueTypePtr font = FontTrueType::New( + makeInternalFontName( authorFamily, fontStyle, fontWeight ), *mResourceScope ); IOStream* stream = VFS::instance()->getFileFromPath( path ); if ( font->loadFromStream( *stream ) ) { registerLoadedFont( font ); } else - eeSAFE_DELETE( font ); + mResourceScope->eraseLocalFont( font.get() ); } }; @@ -1767,26 +1901,27 @@ void UISceneNode::loadCSS( URI uri, std::optional