@quybt93/pcgf 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/plugins/marketplace.json +20 -0
- package/.agents/skills/bmad-advanced-elicitation/SKILL.md +64 -0
- package/.agents/skills/bmad-advanced-elicitation/assets/methods.csv +72 -0
- package/.agents/skills/bmad-advanced-elicitation/customize.toml +54 -0
- package/.agents/skills/bmad-advanced-elicitation/scripts/pick_methods.py +233 -0
- package/.agents/skills/bmad-advanced-elicitation/scripts/tests/test_pick_methods.py +228 -0
- package/.agents/skills/bmad-agent-analyst/SKILL.md +76 -0
- package/.agents/skills/bmad-agent-analyst/customize.toml +103 -0
- package/.agents/skills/bmad-agent-architect/SKILL.md +76 -0
- package/.agents/skills/bmad-agent-architect/customize.toml +63 -0
- package/.agents/skills/bmad-agent-dev/SKILL.md +76 -0
- package/.agents/skills/bmad-agent-dev/customize.toml +81 -0
- package/.agents/skills/bmad-agent-pm/SKILL.md +76 -0
- package/.agents/skills/bmad-agent-pm/customize.toml +73 -0
- package/.agents/skills/bmad-agent-ux-designer/SKILL.md +76 -0
- package/.agents/skills/bmad-agent-ux-designer/customize.toml +58 -0
- package/.agents/skills/bmad-architecture/SKILL.md +85 -0
- package/.agents/skills/bmad-architecture/assets/spine-template.md +79 -0
- package/.agents/skills/bmad-architecture/customize.toml +101 -0
- package/.agents/skills/bmad-architecture/references/headless.md +26 -0
- package/.agents/skills/bmad-architecture/references/reviewer-gate.md +13 -0
- package/.agents/skills/bmad-architecture/scripts/lint_spine.py +257 -0
- package/.agents/skills/bmad-architecture/scripts/tests/test_lint_spine.py +270 -0
- package/.agents/skills/bmad-brainstorming/SKILL.md +80 -0
- package/.agents/skills/bmad-brainstorming/assets/brain-icons.json +166 -0
- package/.agents/skills/bmad-brainstorming/assets/brain-methods.csv +109 -0
- package/.agents/skills/bmad-brainstorming/assets/brain-selector.html +328 -0
- package/.agents/skills/bmad-brainstorming/customize.toml +83 -0
- package/.agents/skills/bmad-brainstorming/references/converge.md +24 -0
- package/.agents/skills/bmad-brainstorming/references/finalize.md +26 -0
- package/.agents/skills/bmad-brainstorming/references/headless.md +54 -0
- package/.agents/skills/bmad-brainstorming/references/in-chat-techniques.md +18 -0
- package/.agents/skills/bmad-brainstorming/references/mode-autonomous.md +10 -0
- package/.agents/skills/bmad-brainstorming/references/mode-facilitator.md +11 -0
- package/.agents/skills/bmad-brainstorming/references/mode-partner.md +16 -0
- package/.agents/skills/bmad-brainstorming/references/resume.md +5 -0
- package/.agents/skills/bmad-brainstorming/scripts/brain.py +789 -0
- package/.agents/skills/bmad-brainstorming/scripts/tests/test_brain.py +289 -0
- package/.agents/skills/bmad-build/SKILL.md +13 -0
- package/.agents/skills/bmad-build/compile-epic-context.md +62 -0
- package/.agents/skills/bmad-build/customize.toml +152 -0
- package/.agents/skills/bmad-build/references/claims-check.md +14 -0
- package/.agents/skills/bmad-build/references/deletion-check.md +14 -0
- package/.agents/skills/bmad-build/review-prompts/edge-case-hunter.md +112 -0
- package/.agents/skills/bmad-build/review-prompts/verification-gap.md +113 -0
- package/.agents/skills/bmad-build/spec-template.md +111 -0
- package/.agents/skills/bmad-build/step-01-clarify-and-route.md +100 -0
- package/.agents/skills/bmad-build/step-02-plan.md +62 -0
- package/.agents/skills/bmad-build/step-03-implement.md +51 -0
- package/.agents/skills/bmad-build/step-04-review.md +85 -0
- package/.agents/skills/bmad-build/step-05-present.md +43 -0
- package/.agents/skills/bmad-build/step-oneshot.md +107 -0
- package/.agents/skills/bmad-build/sync-sprint-status.md +5 -0
- package/.agents/skills/bmad-build/workflow.md +84 -0
- package/.agents/skills/bmad-build-auto/SKILL.md +13 -0
- package/.agents/skills/bmad-build-auto/compile-epic-context.md +62 -0
- package/.agents/skills/bmad-build-auto/customize.toml +119 -0
- package/.agents/skills/bmad-build-auto/references/claims-check.md +14 -0
- package/.agents/skills/bmad-build-auto/references/deletion-check.md +14 -0
- package/.agents/skills/bmad-build-auto/review-prompts/edge-case-hunter.md +112 -0
- package/.agents/skills/bmad-build-auto/review-prompts/verification-gap.md +113 -0
- package/.agents/skills/bmad-build-auto/spec-template.md +96 -0
- package/.agents/skills/bmad-build-auto/step-01-clarify-and-route.md +84 -0
- package/.agents/skills/bmad-build-auto/step-02-plan.md +27 -0
- package/.agents/skills/bmad-build-auto/step-03-implement.md +48 -0
- package/.agents/skills/bmad-build-auto/step-04-review.md +116 -0
- package/.agents/skills/bmad-build-auto/workflow.md +104 -0
- package/.agents/skills/bmad-code-review/SKILL.md +90 -0
- package/.agents/skills/bmad-code-review/customize.toml +110 -0
- package/.agents/skills/bmad-code-review/references/claims-check.md +14 -0
- package/.agents/skills/bmad-code-review/references/deletion-check.md +14 -0
- package/.agents/skills/bmad-code-review/review-prompts/edge-case-hunter.md +112 -0
- package/.agents/skills/bmad-code-review/review-prompts/verification-gap.md +113 -0
- package/.agents/skills/bmad-code-review/steps/step-01-gather-context.md +94 -0
- package/.agents/skills/bmad-code-review/steps/step-02-review.md +32 -0
- package/.agents/skills/bmad-code-review/steps/step-03-triage.md +54 -0
- package/.agents/skills/bmad-code-review/steps/step-04-present.md +134 -0
- package/.agents/skills/bmad-correct-course/SKILL.md +308 -0
- package/.agents/skills/bmad-correct-course/checklist.md +288 -0
- package/.agents/skills/bmad-correct-course/customize.toml +39 -0
- package/.agents/skills/bmad-create-epics-and-stories/SKILL.md +93 -0
- package/.agents/skills/bmad-create-epics-and-stories/customize.toml +39 -0
- package/.agents/skills/bmad-create-epics-and-stories/steps/step-01-validate-prerequisites.md +263 -0
- package/.agents/skills/bmad-create-epics-and-stories/steps/step-02-design-epics.md +242 -0
- package/.agents/skills/bmad-create-epics-and-stories/steps/step-03-create-stories.md +255 -0
- package/.agents/skills/bmad-create-epics-and-stories/steps/step-04-final-validation.md +143 -0
- package/.agents/skills/bmad-create-epics-and-stories/templates/epics-template.md +61 -0
- package/.agents/skills/bmad-customize/SKILL.md +111 -0
- package/.agents/skills/bmad-customize/scripts/list_customizable_skills.py +231 -0
- package/.agents/skills/bmad-customize/scripts/tests/test_list_customizable_skills.py +249 -0
- package/.agents/skills/bmad-deep-recon/SKILL.md +82 -0
- package/.agents/skills/bmad-deep-recon/assets/research.template.md +18 -0
- package/.agents/skills/bmad-deep-recon/customize.toml +212 -0
- package/.agents/skills/bmad-deep-recon/references/draft.md +8 -0
- package/.agents/skills/bmad-deep-recon/references/finalize.md +11 -0
- package/.agents/skills/bmad-deep-recon/references/html-briefing.md +16 -0
- package/.agents/skills/bmad-deep-recon/references/lifecycle.md +11 -0
- package/.agents/skills/bmad-deep-recon/references/process.md +10 -0
- package/.agents/skills/bmad-deep-recon/references/run.md +73 -0
- package/.agents/skills/bmad-deep-recon/references/selection.md +13 -0
- package/.agents/skills/bmad-deep-recon/references/synthesis.md +16 -0
- package/.agents/skills/bmad-deep-recon/references/verification.md +29 -0
- package/.agents/skills/bmad-deep-recon/scripts/recon_kit.py +322 -0
- package/.agents/skills/bmad-deep-recon/scripts/tests/test_recon_kit.py +144 -0
- package/.agents/skills/bmad-deep-recon/types/academic-lit.md +19 -0
- package/.agents/skills/bmad-deep-recon/types/competitive.md +19 -0
- package/.agents/skills/bmad-deep-recon/types/domain.md +19 -0
- package/.agents/skills/bmad-deep-recon/types/market.md +19 -0
- package/.agents/skills/bmad-deep-recon/types/technical.md +19 -0
- package/.agents/skills/bmad-deep-recon/types/user-voice.md +19 -0
- package/.agents/skills/bmad-forge-idea/SKILL.md +107 -0
- package/.agents/skills/bmad-forge-idea/customize.toml +41 -0
- package/.agents/skills/bmad-forge-idea/scripts/resolve_personas.py +277 -0
- package/.agents/skills/bmad-forge-idea/scripts/tests/test_resolve_personas.py +160 -0
- package/.agents/skills/bmad-help/SKILL.md +77 -0
- package/.agents/skills/bmad-party-mode/SKILL.md +60 -0
- package/.agents/skills/bmad-party-mode/customize.toml +211 -0
- package/.agents/skills/bmad-party-mode/references/create-party.md +70 -0
- package/.agents/skills/bmad-party-mode/references/mode-agent-team.md +13 -0
- package/.agents/skills/bmad-party-mode/references/mode-auto.md +13 -0
- package/.agents/skills/bmad-party-mode/references/mode-subagent.md +31 -0
- package/.agents/skills/bmad-party-mode/references/party-memory.md +51 -0
- package/.agents/skills/bmad-party-mode/scripts/resolve_party.py +284 -0
- package/.agents/skills/bmad-party-mode/scripts/tests/test_resolve_party.py +168 -0
- package/.agents/skills/bmad-prd/SKILL.md +94 -0
- package/.agents/skills/bmad-prd/assets/headless-schemas.md +76 -0
- package/.agents/skills/bmad-prd/assets/prd-template.md +165 -0
- package/.agents/skills/bmad-prd/assets/prd-validation-checklist.md +135 -0
- package/.agents/skills/bmad-prd/assets/validation-report-template.html +325 -0
- package/.agents/skills/bmad-prd/customize.toml +148 -0
- package/.agents/skills/bmad-prd/references/headless.md +39 -0
- package/.agents/skills/bmad-prd/references/validate.md +97 -0
- package/.agents/skills/bmad-prfaq/SKILL.md +135 -0
- package/.agents/skills/bmad-prfaq/agents/artifact-analyzer.md +60 -0
- package/.agents/skills/bmad-prfaq/agents/web-researcher.md +49 -0
- package/.agents/skills/bmad-prfaq/assets/prfaq-template.md +62 -0
- package/.agents/skills/bmad-prfaq/bmad-manifest.json +16 -0
- package/.agents/skills/bmad-prfaq/customize.toml +39 -0
- package/.agents/skills/bmad-prfaq/references/customer-faq.md +55 -0
- package/.agents/skills/bmad-prfaq/references/internal-faq.md +51 -0
- package/.agents/skills/bmad-prfaq/references/press-release.md +60 -0
- package/.agents/skills/bmad-prfaq/references/verdict.md +83 -0
- package/.agents/skills/bmad-product-brief/SKILL.md +91 -0
- package/.agents/skills/bmad-product-brief/assets/brief-template.md +41 -0
- package/.agents/skills/bmad-product-brief/customize.toml +100 -0
- package/.agents/skills/bmad-project-context/SKILL.md +121 -0
- package/.agents/skills/bmad-project-context/customize.toml +24 -0
- package/.agents/skills/bmad-project-context/references/best-practices.md +84 -0
- package/.agents/skills/bmad-project-context/references/template.md +55 -0
- package/.agents/skills/bmad-qa-generate-e2e-tests/SKILL.md +176 -0
- package/.agents/skills/bmad-qa-generate-e2e-tests/checklist.md +33 -0
- package/.agents/skills/bmad-qa-generate-e2e-tests/customize.toml +39 -0
- package/.agents/skills/bmad-retrospective/SKILL.md +94 -0
- package/.agents/skills/bmad-retrospective/customize.toml +39 -0
- package/.agents/skills/bmad-retrospective/references/acceptance-verdict.md +55 -0
- package/.agents/skills/bmad-retrospective/references/aggregate-views.md +17 -0
- package/.agents/skills/bmad-retrospective/references/evidence-gathering.md +30 -0
- package/.agents/skills/bmad-retrospective/references/retro-document.md +84 -0
- package/.agents/skills/bmad-retrospective/references/team-discussion.md +22 -0
- package/.agents/skills/bmad-retrospective/scripts/git_evidence.py +304 -0
- package/.agents/skills/bmad-retrospective/scripts/sprint_status.py +746 -0
- package/.agents/skills/bmad-retrospective/scripts/tests/fixtures/sprint-status-template.yaml +71 -0
- package/.agents/skills/bmad-retrospective/scripts/tests/test_git_evidence.py +750 -0
- package/.agents/skills/bmad-retrospective/scripts/tests/test_sprint_status.py +1579 -0
- package/.agents/skills/bmad-review/SKILL.md +50 -0
- package/.agents/skills/bmad-review/customize.toml +141 -0
- package/.agents/skills/bmad-review/references/editorial-common.md +56 -0
- package/.agents/skills/bmad-review/references/lens-adversarial.md +19 -0
- package/.agents/skills/bmad-review/references/lens-edge-case-hunter.md +72 -0
- package/.agents/skills/bmad-review/references/lens-prose.md +7 -0
- package/.agents/skills/bmad-review/references/lens-structure.md +9 -0
- package/.agents/skills/bmad-review/references/lens-verification-gap.md +92 -0
- package/.agents/skills/bmad-review/references/structure-models.md +44 -0
- package/.agents/skills/bmad-review/scripts/tests/test_word_metrics.py +62 -0
- package/.agents/skills/bmad-review/scripts/word_metrics.py +102 -0
- package/.agents/skills/bmad-spec/SKILL.md +160 -0
- package/.agents/skills/bmad-spec/assets/headless-schemas.md +33 -0
- package/.agents/skills/bmad-spec/assets/spec-template.md +49 -0
- package/.agents/skills/bmad-spec/assets/stories-schema.md +44 -0
- package/.agents/skills/bmad-spec/customize.toml +52 -0
- package/.agents/skills/bmad-sprint-planning/SKILL.md +62 -0
- package/.agents/skills/bmad-sprint-planning/customize.toml +39 -0
- package/.agents/skills/bmad-sprint-planning/references/fix-sprint-status.md +30 -0
- package/.agents/skills/bmad-sprint-planning/references/generate-tracking.md +25 -0
- package/.agents/skills/bmad-sprint-planning/references/readiness-gate.md +20 -0
- package/.agents/skills/bmad-sprint-planning/references/status-view.md +14 -0
- package/.agents/skills/bmad-sprint-planning/references/validate.md +10 -0
- package/.agents/skills/bmad-sprint-planning/scripts/sprint_plan.py +697 -0
- package/.agents/skills/bmad-sprint-planning/scripts/tests/test_sprint_plan.py +524 -0
- package/.agents/skills/bmad-sprint-planning/sprint-status-template.yaml +71 -0
- package/.agents/skills/bmad-ux/SKILL.md +90 -0
- package/.agents/skills/bmad-ux/assets/color-themes.md +9 -0
- package/.agents/skills/bmad-ux/assets/design-directions.md +9 -0
- package/.agents/skills/bmad-ux/assets/design-example-editorial.md +158 -0
- package/.agents/skills/bmad-ux/assets/design-example-mobile.md +93 -0
- package/.agents/skills/bmad-ux/assets/design-example-shadcn.md +109 -0
- package/.agents/skills/bmad-ux/assets/excalidraw-wireframe.md +19 -0
- package/.agents/skills/bmad-ux/assets/experience-example-mobile.md +112 -0
- package/.agents/skills/bmad-ux/assets/experience-example-shadcn.md +133 -0
- package/.agents/skills/bmad-ux/assets/headless-schemas.md +84 -0
- package/.agents/skills/bmad-ux/assets/key-screens.md +29 -0
- package/.agents/skills/bmad-ux/assets/validation-report-template.html +319 -0
- package/.agents/skills/bmad-ux/customize.toml +100 -0
- package/.agents/skills/bmad-ux/references/creative-tools.md +19 -0
- package/.agents/skills/bmad-ux/references/design-md-spec.md +50 -0
- package/.agents/skills/bmad-ux/references/headless.md +37 -0
- package/.agents/skills/bmad-ux/references/validate.md +115 -0
- package/.agents/skills/bmad-walkthrough/SKILL.md +68 -0
- package/.agents/skills/bmad-walkthrough/customize.toml +39 -0
- package/.agents/skills/bmad-walkthrough/generate-trail.md +38 -0
- package/.agents/skills/bmad-walkthrough/step-01-orientation.md +105 -0
- package/.agents/skills/bmad-walkthrough/step-02-walkthrough.md +89 -0
- package/.agents/skills/bmad-walkthrough/step-03-detail-pass.md +106 -0
- package/.agents/skills/bmad-walkthrough/step-04-testing.md +74 -0
- package/.agents/skills/bmad-walkthrough/step-05-wrapup.md +30 -0
- package/README.md +111 -0
- package/bin/pcgf.js +8 -0
- package/installer/clean.js +137 -0
- package/installer/cli.js +479 -0
- package/installer/deps.js +160 -0
- package/installer/fs-utils.js +58 -0
- package/installer/install.js +201 -0
- package/installer/paths.js +17 -0
- package/installer/scaffold.js +274 -0
- package/installer/tools.js +38 -0
- package/package.json +37 -0
- package/plugins/phaser-casual-game-factory/.codex-plugin/plugin.json +27 -0
- package/plugins/phaser-casual-game-factory/PROGRESS.md +120 -0
- package/plugins/phaser-casual-game-factory/README.md +79 -0
- package/plugins/phaser-casual-game-factory/assets/sfx/README.md +8 -0
- package/plugins/phaser-casual-game-factory/assets/sfx/ui_click.mp3 +0 -0
- package/plugins/phaser-casual-game-factory/assets/sfx/ui_close.mp3 +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/README.md +9 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_l_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_l_DarkGray.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_l_Gray.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_l_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_l_Mint.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_l_Pink.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_l_Purple.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_l_Red.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_l_Sky.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_l_White_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_l_White_Light1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_l_White_Light2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_l_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_s_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_s_DarkGray.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_s_Flat.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_s_Gray.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_s_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_s_Mint.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_s_PInk.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_s_Purple.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_s_Red.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_s_Sky.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_s_White_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_s_White_Light1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_s_White_Light2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button01_s_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Circle118.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Circle120.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Circle122.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Circle74_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Circle74_Border.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Circle82.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_LeftFlush_Dark.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_LeftFlush_White1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_LeftFlush_White2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round01_White_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round01_White_Light1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round01_White_Light2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round01_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round02_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round02_White_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round02_White_Light.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round03_Dark.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round03_WhiteBg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round03_White_Light.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round04_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round04_Oblique.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round05.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round06_Red.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round06_White.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round07.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round08.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round09.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round10_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Round10_Light.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_SkillBtnMask.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_SkillBtn_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_SkillBtn_Dark.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_SkillBtn_Orange.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_SkillBtn_Red.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_SkillBtn_White_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_SkillBtn_White_Light.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Tapered_White1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Tapered_White2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Tapered_White3.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Button_Tapered_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Menu_BottomBtn_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Menu_BottomBtn_TabFocus.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Menu_BottomBtn_TabFocus_Light.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Menu_MiddleBtn_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Button/Menu_MiddleBtn_TabFocus.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame00_01~02_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame00_01~02_InnerBg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame00_01~02_InnerLine.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame00_01~02_Light.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame00_01~02_Pattern.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame00_01~02_Pattern.psd +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame00_03_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame00_03_Border.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame00_04~06_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame00_04~06_BgLight.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame00_04~06_Border.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame00_04~06_Pattern.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame03_Demo.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame04_Demo.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame05_Demo.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame06_Demo_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame06_Demo__Gray.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BannerFrame07.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BasicFrame_Circle160.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BasicFrame_Hexagon87.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BasicFrame_Oval44.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BasicFrame_Oval50.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BasicFrame_Round12.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BasicFrame_Round17.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BasicFrame_Round20.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BasicFrame_Round24.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Circle60.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Circle75_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Circle75_Light.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Circle78.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Flag_Demo_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Flag_Demo_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Flag_Demo_Red.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Flag_White_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Flag_White_BgGradient.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Flag_White_BgLine01.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Flag_White_BgLine02.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Oval46_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Oval46_InnerLine.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Oval58.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Rhombus_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Rhombus_Inner.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Round18_Transparent.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Round20_Demo_Dark.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Round20_Demo_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Round20_White_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Round20_White_Light.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Round24.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Round30.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Squrare_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BorderFrame_Squrare_Inner.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame01_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame01_BgArrow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame01_BgShadow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame01_Hexagon_Bg_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame01_Hexagon_Bg_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame01_Hexagon_Bg_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame01_Hexagon_FocusBorder.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame01_Hexagon_FocusGlow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame02_Arrow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame02_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame02_BgShadow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame03_Arrow_Left.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame03_Arrow_Right.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame03_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame04_Blue_Arrow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame04_Blue_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame04_White_Arrow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame04_White_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame05_InnerBg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame05_InnerBorder.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame05_Left_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame05_Left_BgShadow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame05_Right_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/BubbleFrame05_Right_BgShadow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame01_Bg_d.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame01_Bg_d_Pattern.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame01_Bg_n.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame01_Border.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame01_Bottom.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame01_Glow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame01_Gradient.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame01_Gradient_Hologram.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame02_d_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame02_d_Bg_Pattern.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame02_d_Border.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame02_n_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame02_n_Bottom.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame02_n_Light.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame03_d.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame03_n_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame03_n_InnerBg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame03_n_Light.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame04_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame04_Bg_DividerLine.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame04_Bg_Gradient.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame04_Border.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame04_FocusBorder.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame04_FocusGlow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame04_Glow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame05_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame05_Bg_Corner.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame05_Border.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame05_FocusBorder.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame05_FocusGlow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame05_Glow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame06_Demo_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame06_Demo_Bg_Glow_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame06_Demo_Deco_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame06_Demo_Frame_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame06_White_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame06_White_Bg_Glow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame06_White_Deco.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/CardFrame06_White_Frame.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame00~03_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame00~03_BgGlow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame00~03_BgLight.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame00~03_Bg_Hologram.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame00~03_Border.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame00~03_CornerDeco.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame00~03_FocusLine.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame01_Demo_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame01_Demo_Gray.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame01_Demo_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame01_Demo_Hologram1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame01_Demo_Hologram2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame01_Demo_Navy.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame01_Demo_OffWhite.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame01_Demo_Purple.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame01_Demo_Red.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame01_Demo_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame02_Demo_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame02_Demo_Gray.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame02_Demo_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame02_Demo_Hologram1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame02_Demo_Hologram2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame02_Demo_Navy.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame02_Demo_OffWhite.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame02_Demo_Purple.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame02_Demo_Red.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame02_Demo_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame03_Demo_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame03_Demo_Gray.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame03_Demo_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame03_Demo_Hologram.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame03_Demo_Navy.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame03_Demo_Purple.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame03_Demo_Red.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ItemFrame03_Demo_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ListFrame00_01~02_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ListFrame00_01~02_BgInner.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ListFrame00_01~02_Oblique.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ListFrame00_03~04_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ListFrame00_03~04_BgInner.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ListFrame00_03~04_Dim.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ListFrame04_Demo_f_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ListFrame04_Demo_f_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/PanelFrame01_Line.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/PanelFrame02.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/PanelFrame03_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/PanelFrame03_Oblique.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/PanelFrame04_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/PanelFrame04_OutTilte1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/PanelFrame04_OutTilte2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ProfileFrame01_l_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ProfileFrame01_l_Border.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ProfileFrame01_m~s_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ProfileFrame01_m~s_Inner.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ProfileFrame02_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ProfileFrame02_Inner.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ProfileFrame_Circle275_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/ProfileFrame_Circle275_Inner.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/SkillFrame_l~m_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/SkillFrame_l~m_Border.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/SkillFrame_l~m_Empty.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/SkillFrame_s_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/SkillFrame_s_Border.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Frame/SkillFrame_s_Empty.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_brz.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_chn.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_cze.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_deu.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_eng.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_esp.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_fra.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_gbr.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_gre.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_ind.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_ita.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_jpn.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_kor.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_pol.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_prt.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_rou.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_rsa.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_rus.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_swe.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_tha.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_tur.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_twn.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_ukr.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_Flag/Small/Icon_Flag_usa.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_GradeBadge/256/Icon_GradeBadge_Beginner.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_GradeBadge/256/Icon_GradeBadge_Bronze.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_GradeBadge/256/Icon_GradeBadge_Challenger.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_GradeBadge/256/Icon_GradeBadge_Diamond.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_GradeBadge/256/Icon_GradeBadge_Gold.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_GradeBadge/256/Icon_GradeBadge_Grandmaster.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_GradeBadge/256/Icon_GradeBadge_Iron.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_GradeBadge/256/Icon_GradeBadge_Master.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_GradeBadge/256/Icon_GradeBadge_Platinum.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_GradeBadge/256/Icon_GradeBadge_Silver.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Badge_Crown.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Badge_Gold.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Bag_Brown.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Bag_Gray.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Battle.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Book_1_Purple.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Book_1_Wine.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Calendar.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Calendar_Check.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Chat.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Chest_Gem.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Chest_Gold.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Chest_Premium.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Chest_Wood.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Clover_Gold.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Clover_Green.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Damage.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Dungeon.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Earth.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Energy_Blue.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Friend.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Gear.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Gear_Armor_Bottom.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Gear_Armor_Top.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Gear_Boots.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Gear_Hat_Witch.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Gear_Ring.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Gear_Sword.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Gem_Circle.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Gem_Diamond_Purple.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Gem_Ellipse_Red.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Gem_Star_Blue.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Gift_Blue.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Gift_Green.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Gift_Red.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Heart_Dimmed.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Heart_Red.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Key_Gold.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Mail.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Map.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Meat.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Medal_Bronze_1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Medal_Bronze_2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Medal_Gold_1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Medal_Gold_2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Medal_Silver_1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Medal_Silver_2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Medalstand.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_MemoPad.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Money_Bill.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Money_Coin.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Pot.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Shop.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Skull.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Star_Blue.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Star_Bronze.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Star_Gold.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Star_Purple.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Star_Red.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Star_Silver.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_TV_AdBlock.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Ticket_Gold.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Ticket_Red.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Trophy_Bronze.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Trophy_Gold.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Trophy_Silver.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_ItemIcons/256/ItemIcon_Video.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Again_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Again_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Aiming_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Aiming_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Alarm.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Anchor.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Archer.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Armor.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Arrow_Down.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Arrow_Left_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Arrow_Left_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Arrow_Left_3.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Arrow_Right_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Arrow_Right_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Arrow_Right_3.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Arrow_Up.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_At.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Attack.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Ax.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Ax_Double.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Back_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Back_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Badge.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Bag.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Bank.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Basket.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Battery_Charge.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Battle.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Bell.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Bell_Off.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Bell_On.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Bell_Sleep.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Blacksmith.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Block.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Bluetooth.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Bomb_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Bomb_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Book_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Book_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Book_3.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Boots.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Boss.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Bow.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Brief_Bag.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Brush.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Bulb.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Bulb_Charge.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Bullet.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Calendar.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Camera.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Camera_Off.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Cap.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Card_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Card_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Cart_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Cart_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Castle.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Caution.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Certification.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Change.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Chat_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Chat_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Check_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Check_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Chest.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Chisel.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Class_A.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Class_B.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Class_C.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Class_D.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Class_S.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Class_Ss.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Clock_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Clock_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Closet.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Clothes.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Coin.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Coin_Star.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Compass.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Console_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Console_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Contraction.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Cooking.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Copy.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Costume.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Critical_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Critical_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Critical_3.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Crown_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Crown_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Crystal.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Delete.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Diamond.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Dice.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Direction_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Direction_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Discount.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Dollar_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Dollar_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Download.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Earth.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Earth_Pin.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Effect.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Energy_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Energy_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Exit.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Exit_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Exit_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Exp.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Expansion.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Face.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Faint.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Female.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Fire.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Firework.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Fish.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Fishing.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Flag.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Float.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Food.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Freezee.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Friends.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Gallery.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Game.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Gear.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Gift.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Glasses.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Globe.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Graduation_Cap.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Hammer.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Hammer_Claw.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Hand_Touch.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Hanger.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Hat.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Headset.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Heart.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Heart_Add.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Heart_Empty.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Helmet_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Helmet_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Help_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Help_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_History.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Home_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Home_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Hp.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Icecream.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Illuminati.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Infinity.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Info_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Info_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Info_3.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Info_4.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Key.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Kitchen_Knife.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Layer_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Layer_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Leaf.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Link.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Lock_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Lock_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Lock_Open.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Magic.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Magic_Ball.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Mail_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Mail_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Male.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Map_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Map_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Map_Pin.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Meal.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Medal_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Medal_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Megaphone.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Menu_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Menu_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Menu_3.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Menu_4.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Menu_5.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Menu_6.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Merge.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Message_Bottle.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Minus_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Minus_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Mission.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Money_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Money_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Money_Pouch.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Movie.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Mushroom.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Music_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Music_1_Off.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Music_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Music_2_Off.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Necklace.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Night.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Note.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Object.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Page_Next.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Page_Prev.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Paper_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Paper_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Path.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Pause_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Pause_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Paw.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Pen.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Pen_Feather.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Pencil.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Phone.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Photo_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Photo_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Pick.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Pickax.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Plants.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Play_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Play_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Plus_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Plus_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Potion_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Potion_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Potion_3.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Pouch.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Power_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Power_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Power_Off.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Rampart.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Rank_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Rank_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Recommendation.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Recycle.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Refresh_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Refresh_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Refresh_3.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Refresh_4.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Ring.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Roulette.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Safebox.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Sandglass.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Saw.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Scale.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Scroll_Old.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Search.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Security.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Setting.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Share.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Sheos.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Sheos_Wing.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Sheriff.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Shield_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Shield_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Shield_Viking.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Ship.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Shop_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Shop_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Shop_3.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Shovel.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Sickle.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Sign.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Skull.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Sound.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Sound_Off.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Star_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Star_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Star_3.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Star_Empty.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Step.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Stopwatch_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Stopwatch_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Storage.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Sun.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Switch.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Sword_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Sword_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Tag.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Target.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Telescope.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Temple.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Ticket.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Tool_Fix.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Top.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Trophy_1.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Trophy_2.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Tv.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Typhoon.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Under.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_User.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_User_Add.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_User_Info.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Video.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_View.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_View_Off.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Vr.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Warehouse.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Water.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Wing.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Wood.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_World.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Zoom_In.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_PictoIcons/256/PictoIcon_Zoom_Out.Png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_TutorialHand/256/tutorial_hand_1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_TutorialHand/256/tutorial_hand_2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_TutorialHand/256/tutorial_hand_3.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_TutorialHand/256/tutorial_hand_4.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_TutorialHand/256/tutorial_hand_5.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Icon_TutorialHand/256/tutorial_hand_6.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Badge01_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Badge01_Purple.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Badge01_Red.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Badge01_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Badge02.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Bubble_Arrow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Bubble_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Parallelogram_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Parallelogram_Gray.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Parallelogram_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Parallelogram_Hologram.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Parallelogram_Purple.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Parallelogram_Red.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Parallelogram_White_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Parallelogram_White_Inner.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Parallelogram_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Ribbon_Hologram.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Ribbon_White.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Tail_Demo_Red.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Tail_White_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Label_Tail_White_Border.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_LIne04_Under.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Line01_Divider_Left.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Line01_Divider_Right.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Line02_Divider_Left.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Line02_Divider_Right.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Line03_Divider.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Oval.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon01_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon01_Gray.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon01_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon01_Magenta.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon01_Navy.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon01_Orange.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon01_Peacock.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon01_Plum.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon01_Purple.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon01_Red.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon01_Sky.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon01_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon02_Red.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon02_White1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon02_White2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon03_Red.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon03_White1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon03_White2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon04_Red.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Ribbon05.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Tapered01.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Tapered02.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Label/Title_Tapered03.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/PACK.md +6 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup01_Basic_Demo.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup01_Basic_White_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup01_Basic_White_Light.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup02~09_Topber_Demo_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup02~09_Topber_White_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup02~09_Topber_White_BgTop.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup02~09_Topber_White_BgTopLight.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup03_Gradient.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup06_Glow_Bottom.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup06_Pattern.psd +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup09_Topber_White_Label_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup09_Topber_White_Label_Inner.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup_Chat_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup_Chat_BgLight.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup_Chat_TabFocus.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup_FullWidth_Dark.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup_FullWidth_White.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup_List_VerticalLayout.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup_OutTitle_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup_OutTitle_Bg_Title.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup_OutTitle_Inner_Bottom.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup_OutTitle_Inner_Top.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup_Ticket_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup_Ticket_Border.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup_Ticket_BottomPattern.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Popup/Popup_Ticket_Gradient.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic01_Bg_Demo.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic01_Bg_White1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic01_Bg_White2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic01_FillMask.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic01_Fill_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic01_Fill_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic01_Fill_White.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic01_Fill_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic02_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic02_FillMask.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic02_Fill_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic02_Fill_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic02_Fill_White.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic02_Fill_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic03_Bg_Demo.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic03_Bg_White1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic03_Bg_White2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic03_FillMask.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic03_Fill_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic03_Fill_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic03_Fill_White.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic03_Fill_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic04_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic04_Fill_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic04_Fill_Orange.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic04_White_Fill1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Basic04_White_Fill2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Handle_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Handle_FillMask.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Handle_Fill_Pink.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Handle_Fill_White1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Handle_Fill_White2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Handle_Handle.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Icon01_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Icon01_FillMask.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Icon01_Fill_Orange.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Icon01_Fill_White.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Icon01_Icon1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Icon01_Icon2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Icon02_Badge1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Icon02_Badge2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Icon02_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Icon02_FillMask.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Icon02_Fill_Orange.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Icon02_Fill_White.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Icon02_Icon1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level01_Badge_l.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level01_Badge_s.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level01_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level01_Fill.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level01_FillMask.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level02_Bg_Demo.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level02_Bg_White1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level02_Bg_White2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level02_Fill01_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level02_Fill01_White.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level02_Fill01_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level02_Fill02_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level02_Fill02_White.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level02_Fill02_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level02_Icon_Badge_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level02_Icon_Badge_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level02_Icon_Up_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level02_Icon_Up_White.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level03_Badge1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level03_Badge2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level03_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level03_Fill1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Level03_Fill2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Panel_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Panel_Fill1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Panel_Fill2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Play01_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Play01_Fill.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Play01_FillMask.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Play02_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Play02_FillMask.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_StageHorizontal_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_StageHorizontal_Icon_l_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_StageHorizontal_Icon_l_Gray.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_StageHorizontal_Icon_l_White1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_StageHorizontal_Icon_l_White2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_StageHorizontal_Icon_l_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_StageHorizontal_Icon_s_Blue.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_StageHorizontal_Icon_s_White1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_StageHorizontal_Icon_s_White2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_StageHorizontal_Icon_s_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_StageVertical_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_StageVertical_Icon1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_StageVertical_Icon2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Wave_Badge1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Wave_Badge2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Wave_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/Slider_Wave_Fill.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/_Slider_Basic_R6_Fill_Orange.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/_Slider_Round6_01_White_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/Slider/_Slider_Round6_01_White_Fill.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Alert_Circle_Icon_Exclamation.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Alert_Circle_l_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Alert_Circle_l_Border.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Alert_Circle_s_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Alert_Circle_s_Border.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Alert_Dot_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Alert_Dot_Border.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Alert_Dot_Light.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/InputField01_Bg_Demo.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/InputField01_Bg_White1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/InputField01_Bg_White2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/InputField01_Bg_White3.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/InputField01_Icon_Lock.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/InputField01_Icon_Mail.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/InputField02_Demo.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/InputField02_White1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/InputField02_White2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/InputField03.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/InputField04.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ResourceBar_Demo_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ResourceBar_Demo_Btn_Blue1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ResourceBar_Demo_Btn_Blue2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ResourceBar_Demo_Btn_Purple1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ResourceBar_Demo_Btn_Purple2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ResourceBar_Demo_Btn_Yellow1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ResourceBar_Demo_Btn_Yellow2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ResourceBar_Demo_Icon_Coin.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ResourceBar_Demo_Icon_Energy.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ResourceBar_Demo_Icon_Gem.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ResourceBar_Demo_Icon_Monster.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ResourceBar_White_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ResourceBar_White_Btn1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ResourceBar_White_Btn2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ResourceBar_White_Btn3.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ResourceBar_White_Icon1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ResourceBar_White_Icon2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Switch_Demo_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Switch_Demo_Fill.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Switch_Demo_Handle.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Switch_White_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Switch_White_BgInner.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Switch_White_Fill1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Switch_White_Fill2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Switch_White_Handle.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ToastMessage_Bg_Gray.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ToastMessage_Bg_Green.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ToastMessage_Bg_Red.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ToastMessage_Bg_White.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ToastMessage_Bg_Yellow.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ToastMessage_Gradient.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ToastMessage_Light1.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/ToastMessage_Light2.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Toggle_Check_Demo_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Toggle_Check_Demo_Icon.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Toggle_Check_White_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Toggle_Check_White_BgLight.png +0 -0
- package/plugins/phaser-casual-game-factory/assets/ui-packs/super-casual/UI_Etc/Toggle_Check_White_Icon.png +0 -0
- package/plugins/phaser-casual-game-factory/examples/README.md +44 -0
- package/plugins/phaser-casual-game-factory/examples/economy-demo-config.ts +255 -0
- package/plugins/phaser-casual-game-factory/examples/economy-demo.ts +89 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/index.html +342 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/package-lock.json +1293 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/package.json +21 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/playwright.config.ts +26 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/public/assets/sfx/ui_click.mp3 +0 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/public/assets/sfx/ui_close.mp3 +0 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/public/assets/ui/BG_lobby.png +0 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/public/assets/ui/Bar_Bg.png +0 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/public/assets/ui/progress_inner.png +0 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/public/assets/ui/progress_outer.png +0 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/public/fonts/bvp-400-latin-OEyTajQY.woff2 +0 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/public/fonts/bvp-400-viet-SOx6iaFb.woff2 +0 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/public/fonts/bvp-600-latin-xAC4kEK0.woff2 +0 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/public/fonts/bvp-600-viet-DbGrrtCq.woff2 +0 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/public/fonts/bvp-700-latin-CPfR8Jz_.woff2 +0 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/public/fonts/bvp-700-viet-DptIoWwl.woff2 +0 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/public/fonts/bvp-800-latin-B5VTH9An.woff2 +0 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/public/fonts/bvp-800-viet-BEUwTqXl.woff2 +0 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/public/fonts/bvp-900-latin-B6esVQUu.woff2 +0 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/public/fonts/bvp-900-viet-DUJ3fpCl.woff2 +0 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/public/logo-frog.png +0 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/src/brand/BootLoadingScreen.js +7 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/src/brand/SplashFullscreenBg.js +4 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/src/brand-shims.d.ts +33 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/src/demo-scenes.ts +12 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/src/demo-tools.ts +34 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/src/main.ts +1358 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/src/scenes/BootScene.ts +236 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/src/scenes/GameScene.ts +152 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/src/scenes/HomeScene.ts +21 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/src/scenes/scene-hooks.ts +12 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/src/styles.css +623 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/src/ui-audio.ts +156 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/tests/browser-smoke.spec.ts +170 -0
- package/plugins/phaser-casual-game-factory/examples/factory-browser-demo/vite.config.ts +23 -0
- package/plugins/phaser-casual-game-factory/examples/leveling-demo-config.ts +107 -0
- package/plugins/phaser-casual-game-factory/references/condition-service.md +38 -0
- package/plugins/phaser-casual-game-factory/references/core-contracts.md +39 -0
- package/plugins/phaser-casual-game-factory/references/daily-login-service.md +32 -0
- package/plugins/phaser-casual-game-factory/references/economy-service.md +192 -0
- package/plugins/phaser-casual-game-factory/references/leveling-service.md +56 -0
- package/plugins/phaser-casual-game-factory/references/loading-view.md +181 -0
- package/plugins/phaser-casual-game-factory/references/time-service.md +42 -0
- package/plugins/phaser-casual-game-factory/scripts/new-phaser-casual-game.ps1 +152 -0
- package/plugins/phaser-casual-game-factory/scripts/run-browser-demo.ps1 +13 -0
- package/plugins/phaser-casual-game-factory/scripts/run-economy-demo.ps1 +4 -0
- package/plugins/phaser-casual-game-factory/scripts/test-all.ps1 +11 -0
- package/plugins/phaser-casual-game-factory/scripts/test-browser.ps1 +10 -0
- package/plugins/phaser-casual-game-factory/scripts/test-economy.ps1 +4 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/SKILL.md +846 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/client/.env.example +19 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/client/bgremove.py +127 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/client/codex_extract.py +123 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/client/codex_gen.sh +172 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/client/envfile.py +121 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/client/gemini_image_mcp.py +1870 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/client/imglib.py +2138 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/client/remote_backend.py +498 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/client/remote_codex.py +59 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/client/remote_proto.py +135 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/client/requirements-client.txt +22 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/client/test_imglib.py +1647 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/client/test_restyle_tools.py +256 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/client/tools/restyle/styles/unscrew-casual.refs/ref-lose.jpg +0 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/client/tools/restyle/styles/unscrew-casual.refs/ref-welcome.jpg +0 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/client/tools/restyle/styles/unscrew-casual.refs/ref-win.jpg +0 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/client/upscale.py +289 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/reference/client-guide.md +180 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/reference/mcp-image-input.md +148 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/reference/prompt-template.md +216 -0
- package/plugins/phaser-casual-game-factory/skills/game-art/reference/remote-lan.md +224 -0
- package/plugins/phaser-casual-game-factory/skills/pcgf-git-merge-request/SKILL.md +96 -0
- package/plugins/phaser-casual-game-factory/skills/pcgf-ui-reskin/SKILL.md +144 -0
- package/plugins/phaser-casual-game-factory/skills/phaser-adaptive-loading/SKILL.md +33 -0
- package/plugins/phaser-casual-game-factory/skills/phaser-casual-game-factory/SKILL.md +225 -0
- package/plugins/phaser-casual-game-factory/skills/phaser-condition-service/SKILL.md +21 -0
- package/plugins/phaser-casual-game-factory/skills/phaser-daily-login-service/SKILL.md +20 -0
- package/plugins/phaser-casual-game-factory/skills/phaser-economy-service/SKILL.md +72 -0
- package/plugins/phaser-casual-game-factory/skills/phaser-foundation/SKILL.md +60 -0
- package/plugins/phaser-casual-game-factory/skills/phaser-leveling-service/SKILL.md +39 -0
- package/plugins/phaser-casual-game-factory/skills/phaser-loading-view/SKILL.md +167 -0
- package/plugins/phaser-casual-game-factory/skills/phaser-studio25-boot-brand/SKILL.md +107 -0
- package/plugins/phaser-casual-game-factory/skills/phaser-time-service/SKILL.md +26 -0
- package/plugins/phaser-casual-game-factory/skills/phaser-ui-motion/SKILL.md +161 -0
- package/plugins/phaser-casual-game-factory/skills/phaser-ui-motion/fix-off-canvas-press.md +198 -0
- package/plugins/phaser-casual-game-factory/skills/phaser-ui-sfx/SKILL.md +83 -0
- package/plugins/phaser-casual-game-factory/skills/theme-cream-casual/SKILL.md +136 -0
- package/plugins/phaser-casual-game-factory/skills/theme-dark-casual/SKILL.md +23 -0
- package/plugins/phaser-casual-game-factory/skills/tool-demo-qa/SKILL.md +264 -0
- package/plugins/phaser-casual-game-factory/templates/adaptive-loading/adaptive-loading-manager.ts +103 -0
- package/plugins/phaser-casual-game-factory/templates/adaptive-loading/device-profile.ts +76 -0
- package/plugins/phaser-casual-game-factory/templates/adaptive-loading/device-tier-session.ts +133 -0
- package/plugins/phaser-casual-game-factory/templates/adaptive-loading/index.ts +4 -0
- package/plugins/phaser-casual-game-factory/templates/adaptive-loading/types.ts +27 -0
- package/plugins/phaser-casual-game-factory/templates/boot/brand/BootLoadingScreen.js +370 -0
- package/plugins/phaser-casual-game-factory/templates/boot/brand/SplashFullscreenBg.js +18 -0
- package/plugins/phaser-casual-game-factory/templates/boot/brand/brand-defaults.js +36 -0
- package/plugins/phaser-casual-game-factory/templates/boot/brand-shims.d.ts +40 -0
- package/plugins/phaser-casual-game-factory/templates/boot/close-styles.ts +26 -0
- package/plugins/phaser-casual-game-factory/templates/boot/index.ts +32 -0
- package/plugins/phaser-casual-game-factory/templates/boot/iris-close.ts +85 -0
- package/plugins/phaser-casual-game-factory/templates/boot/map-dom-rect-to-game.ts +53 -0
- package/plugins/phaser-casual-game-factory/templates/boot/play-boot-loading-view.ts +124 -0
- package/plugins/phaser-casual-game-factory/templates/commerce/commerce-service.ts +22 -0
- package/plugins/phaser-casual-game-factory/templates/commerce/index.ts +1 -0
- package/plugins/phaser-casual-game-factory/templates/commerce/types.ts +6 -0
- package/plugins/phaser-casual-game-factory/templates/condition/condition-service.ts +166 -0
- package/plugins/phaser-casual-game-factory/templates/condition/index.ts +2 -0
- package/plugins/phaser-casual-game-factory/templates/condition/types.ts +69 -0
- package/plugins/phaser-casual-game-factory/templates/core/events.ts +71 -0
- package/plugins/phaser-casual-game-factory/templates/core/gameplay.ts +15 -0
- package/plugins/phaser-casual-game-factory/templates/core/index.ts +9 -0
- package/plugins/phaser-casual-game-factory/templates/core/layout.ts +25 -0
- package/plugins/phaser-casual-game-factory/templates/core/loading.ts +33 -0
- package/plugins/phaser-casual-game-factory/templates/core/service-registry.ts +28 -0
- package/plugins/phaser-casual-game-factory/templates/core/services.ts +19 -0
- package/plugins/phaser-casual-game-factory/templates/core/storage.ts +7 -0
- package/plugins/phaser-casual-game-factory/templates/core/time.ts +70 -0
- package/plugins/phaser-casual-game-factory/templates/core/types.ts +21 -0
- package/plugins/phaser-casual-game-factory/templates/daily-login/daily-login-service.ts +136 -0
- package/plugins/phaser-casual-game-factory/templates/daily-login/index.ts +3 -0
- package/plugins/phaser-casual-game-factory/templates/daily-login/reward-service.ts +108 -0
- package/plugins/phaser-casual-game-factory/templates/daily-login/types.ts +39 -0
- package/plugins/phaser-casual-game-factory/templates/demo-tools/demo-device-tier.ts +73 -0
- package/plugins/phaser-casual-game-factory/templates/demo-tools/demo-perf.ts +232 -0
- package/plugins/phaser-casual-game-factory/templates/demo-tools/demo-tools.css +355 -0
- package/plugins/phaser-casual-game-factory/templates/demo-tools/demo-tools.ts +305 -0
- package/plugins/phaser-casual-game-factory/templates/demo-tools/index.ts +7 -0
- package/plugins/phaser-casual-game-factory/templates/demo-tools/init-demo-qa-tools.ts +101 -0
- package/plugins/phaser-casual-game-factory/templates/demo-tools/tool-param-fields.ts +214 -0
- package/plugins/phaser-casual-game-factory/templates/demo-tools/tool-sections.ts +238 -0
- package/plugins/phaser-casual-game-factory/templates/demo-tools/tool-shell.html +57 -0
- package/plugins/phaser-casual-game-factory/templates/demo-tools/tool-shell.ts +141 -0
- package/plugins/phaser-casual-game-factory/templates/economy/default-registry.ts +14 -0
- package/plugins/phaser-casual-game-factory/templates/economy/economy-service.ts +65 -0
- package/plugins/phaser-casual-game-factory/templates/economy/exchange-handlers.ts +58 -0
- package/plugins/phaser-casual-game-factory/templates/economy/grant-handler.ts +58 -0
- package/plugins/phaser-casual-game-factory/templates/economy/index.ts +15 -0
- package/plugins/phaser-casual-game-factory/templates/economy/inventory-service.ts +170 -0
- package/plugins/phaser-casual-game-factory/templates/economy/item-service.ts +220 -0
- package/plugins/phaser-casual-game-factory/templates/economy/item-utils.ts +126 -0
- package/plugins/phaser-casual-game-factory/templates/economy/merge-handler.ts +42 -0
- package/plugins/phaser-casual-game-factory/templates/economy/operation-handler.ts +44 -0
- package/plugins/phaser-casual-game-factory/templates/economy/package-handler.ts +41 -0
- package/plugins/phaser-casual-game-factory/templates/economy/random.ts +33 -0
- package/plugins/phaser-casual-game-factory/templates/economy/repository.ts +64 -0
- package/plugins/phaser-casual-game-factory/templates/economy/state-utils.ts +221 -0
- package/plugins/phaser-casual-game-factory/templates/economy/types.ts +309 -0
- package/plugins/phaser-casual-game-factory/templates/economy/ui-view-models.ts +79 -0
- package/plugins/phaser-casual-game-factory/templates/entitlement/entitlement-service.ts +29 -0
- package/plugins/phaser-casual-game-factory/templates/entitlement/index.ts +1 -0
- package/plugins/phaser-casual-game-factory/templates/entitlement/types.ts +26 -0
- package/plugins/phaser-casual-game-factory/templates/leveling/condition-evaluator.ts +17 -0
- package/plugins/phaser-casual-game-factory/templates/leveling/config-validator.ts +63 -0
- package/plugins/phaser-casual-game-factory/templates/leveling/index.ts +5 -0
- package/plugins/phaser-casual-game-factory/templates/leveling/leveling-service.ts +306 -0
- package/plugins/phaser-casual-game-factory/templates/leveling/types.ts +80 -0
- package/plugins/phaser-casual-game-factory/templates/leveling/view-model.ts +52 -0
- package/plugins/phaser-casual-game-factory/templates/loading-view/index.ts +4 -0
- package/plugins/phaser-casual-game-factory/templates/loading-view/loading-view-model.ts +203 -0
- package/plugins/phaser-casual-game-factory/templates/loading-view/presets.ts +26 -0
- package/plugins/phaser-casual-game-factory/templates/loading-view/progress.ts +30 -0
- package/plugins/phaser-casual-game-factory/templates/loading-view/types.ts +49 -0
- package/plugins/phaser-casual-game-factory/templates/motion/index.ts +2 -0
- package/plugins/phaser-casual-game-factory/templates/motion/phaser-ui-motion.ts +363 -0
- package/plugins/phaser-casual-game-factory/templates/motion/types.ts +21 -0
- package/plugins/phaser-casual-game-factory/templates/phaser/browser-storage-adapter.ts +91 -0
- package/plugins/phaser-casual-game-factory/templates/phaser/design-size.ts +34 -0
- package/plugins/phaser-casual-game-factory/templates/phaser/foundation-scene.ts +39 -0
- package/plugins/phaser-casual-game-factory/templates/phaser/hybrid-loading-layer.css +46 -0
- package/plugins/phaser-casual-game-factory/templates/phaser/loading-overlay-scene.ts +653 -0
- package/plugins/phaser-casual-game-factory/templates/phaser/phaser-event-bus.ts +35 -0
- package/plugins/phaser-casual-game-factory/templates/phaser/scene-keys.ts +12 -0
- package/plugins/phaser-casual-game-factory/templates/phaser/scene-registration.ts +28 -0
- package/plugins/phaser-casual-game-factory/templates/phaser/system-time-adapter.ts +33 -0
- package/plugins/phaser-casual-game-factory/templates/time/index.ts +2 -0
- package/plugins/phaser-casual-game-factory/templates/time/time-service.ts +184 -0
- package/plugins/phaser-casual-game-factory/templates/ui/audio-settings.ts +148 -0
- package/plugins/phaser-casual-game-factory/templates/ui/bind-factory-dom-press.ts +108 -0
- package/plugins/phaser-casual-game-factory/templates/ui/factory-cream-casual.css +380 -0
- package/plugins/phaser-casual-game-factory/templates/ui/factory-dark-casual.css +11 -0
- package/plugins/phaser-casual-game-factory/templates/ui/index.ts +5 -0
- package/plugins/phaser-casual-game-factory/templates/ui/settings-popup-dom.ts +349 -0
- package/plugins/phaser-casual-game-factory/templates/ui/settings-popup-phaser.ts +284 -0
- package/plugins/phaser-casual-game-factory/templates/ui/ui-sfx.ts +131 -0
- package/plugins/phaser-casual-game-factory/templates/ui-reskin/compare.html +235 -0
- package/plugins/phaser-casual-game-factory/tests/README.md +29 -0
- package/plugins/phaser-casual-game-factory/tests/condition.test.ts +92 -0
- package/plugins/phaser-casual-game-factory/tests/daily-login-reward.test.ts +45 -0
- package/plugins/phaser-casual-game-factory/tests/daily-login.test.ts +73 -0
- package/plugins/phaser-casual-game-factory/tests/economy.test.ts +258 -0
- package/plugins/phaser-casual-game-factory/tests/entitlement.test.ts +18 -0
- package/plugins/phaser-casual-game-factory/tests/leveling.test.ts +158 -0
- package/plugins/phaser-casual-game-factory/tests/loading-view.test.ts +39 -0
- package/plugins/phaser-casual-game-factory/tests/time.test.ts +60 -0
|
@@ -0,0 +1,2138 @@
|
|
|
1
|
+
"""Hàm xử lý ảnh thuần cho skill restyle-asset.
|
|
2
|
+
|
|
3
|
+
Không CLI, không mạng, không biết layout thư mục — chỉ nhận/trả PIL Image.
|
|
4
|
+
Nhờ vậy test được offline. Mọi phép toán pixel dùng numpy (Pillow 12 đã bỏ ImageMath.eval).
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import base64
|
|
9
|
+
import html as _html
|
|
10
|
+
import io
|
|
11
|
+
import json as _json
|
|
12
|
+
import math
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
from PIL import Image, ImageDraw, ImageFilter
|
|
16
|
+
|
|
17
|
+
# Màu key ứng viên, theo thứ tự ưu tiên. Xanh lá đứng đầu vì đó là mặc định mong muốn.
|
|
18
|
+
KEY_CANDIDATES = ["#00FF00", "#FF00FF", "#00FFFF", "#0000FF"]
|
|
19
|
+
|
|
20
|
+
# chroma_key() mặc định t_high=170: alpha CHỈ đạt 255 (đục hoàn toàn) khi khoảng cách tới
|
|
21
|
+
# key >= 170. Một margin "an toàn" mà thấp hơn t_high là VÔ NGHĨA — pixel chủ thể ở đúng
|
|
22
|
+
# khoảng cách margin đó sẽ không đạt alpha đục, tức là chủ thể bị bào mờ (Finding I1: đo
|
|
23
|
+
# thực tế asset màu (0,230,70), margin=131 tới #00FF00, ra alpha~169 — ma). Margin phải
|
|
24
|
+
# LỚN HƠN t_high mới thật sự an toàn; 190 = t_high(170) + buffer ~20 unit dự phòng
|
|
25
|
+
# rounding/variance giữa các lần đo màu.
|
|
26
|
+
MIN_KEY_MARGIN = 190.0
|
|
27
|
+
|
|
28
|
+
# "An toàn" nghĩa là margin của asset gốc tới #00FF00 phải vượt MIN_KEY_MARGIN. Dùng
|
|
29
|
+
# CHUNG một hằng số ở đây và ở cảnh báo `key_safe` trong CLI `plan` để hai nơi luôn khớp
|
|
30
|
+
# nhau — tách rời chúng là đúng lỗi I1 (một ngưỡng "an toàn" thấp hơn t_high thực tế).
|
|
31
|
+
# Thực tế khoảng cách giữa #00FF00 và #00C800 (xanh lá đậm) là 110.0 trên thang redmean,
|
|
32
|
+
# tức là dưới MIN_KEY_MARGIN, nên xanh lá đậm vẫn ĐẨY được ta sang màu key khác — đúng ý.
|
|
33
|
+
GREEN_SAFE_THRESHOLD = MIN_KEY_MARGIN
|
|
34
|
+
|
|
35
|
+
ALPHA_FLOOR = 8 # alpha > ngưỡng này mới tính là nội dung (khi tìm bbox)
|
|
36
|
+
PALETTE_ALPHA_FLOOR = 16 # alpha > ngưỡng này khi lấy mẫu bảng màu (muốn pixel mờ bị bỏ qua vì màu blended)
|
|
37
|
+
BAD_HIGH = 0.95 # alpha phủ hơn mức này -> nền không tách được
|
|
38
|
+
BAD_LOW = 0.02 # alpha phủ dưới mức này -> chủ thể NHỎ (không nhất thiết hỏng, xem
|
|
39
|
+
# bad_subject trong postprocess() — coverage thấp một mình không còn
|
|
40
|
+
# đủ để kết luận "hỏng" kể từ Finding I3)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def hex_to_rgb(h: str) -> tuple[int, int, int]:
|
|
44
|
+
h = h.lstrip("#")
|
|
45
|
+
return (int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def rgb_to_hex(c) -> str:
|
|
49
|
+
r, g, b = (int(round(v)) for v in tuple(c)[:3])
|
|
50
|
+
return f"#{r:02X}{g:02X}{b:02X}"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def color_dist(c1, c2) -> float:
|
|
54
|
+
"""Khoảng cách màu 'redmean' — xấp xỉ cảm nhận mắt người, rẻ hơn CIELAB nhiều.
|
|
55
|
+
|
|
56
|
+
Thang 0..~765. Dùng chung một công thức cho cả pick_key_color và chroma_key
|
|
57
|
+
để ngưỡng ở hai nơi so sánh được với nhau.
|
|
58
|
+
"""
|
|
59
|
+
r1, g1, b1 = (float(v) for v in c1[:3])
|
|
60
|
+
r2, g2, b2 = (float(v) for v in c2[:3])
|
|
61
|
+
rm = (r1 + r2) / 2.0
|
|
62
|
+
dr, dg, db = r1 - r2, g1 - g2, b1 - b2
|
|
63
|
+
return math.sqrt(
|
|
64
|
+
(2 + rm / 256.0) * dr * dr + 4 * dg * dg + (2 + (255 - rm) / 256.0) * db * db
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def extract_palette(img: Image.Image, n: int = 8) -> list[tuple[str, float]]:
|
|
69
|
+
"""Bảng màu chủ đạo, bỏ qua pixel trong suốt. Trả [(hex, tỉ lệ)] giảm dần."""
|
|
70
|
+
arr = np.asarray(img.convert("RGBA"))
|
|
71
|
+
opaque = arr[arr[..., 3] > PALETTE_ALPHA_FLOOR][:, :3]
|
|
72
|
+
if opaque.size == 0:
|
|
73
|
+
return []
|
|
74
|
+
|
|
75
|
+
strip = Image.fromarray(opaque.reshape(1, -1, 3).astype(np.uint8), "RGB")
|
|
76
|
+
q = strip.quantize(colors=max(1, n), method=Image.Quantize.MEDIANCUT)
|
|
77
|
+
pal = q.getpalette() or []
|
|
78
|
+
counts = q.getcolors(maxcolors=256) or []
|
|
79
|
+
total = float(sum(c for c, _ in counts)) or 1.0
|
|
80
|
+
|
|
81
|
+
out: list[tuple[str, float]] = []
|
|
82
|
+
for count, idx in sorted(counts, key=lambda t: t[0], reverse=True):
|
|
83
|
+
rgb = pal[idx * 3 : idx * 3 + 3]
|
|
84
|
+
if len(rgb) == 3:
|
|
85
|
+
out.append((rgb_to_hex(rgb), count / total))
|
|
86
|
+
return out
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def pick_key_color(
|
|
90
|
+
img: Image.Image, avoid: list[tuple[int, int, int]] | None = None
|
|
91
|
+
) -> tuple[str, float]:
|
|
92
|
+
"""Chọn màu key xa mọi màu trong asset nhất, ưu tiên xanh lá.
|
|
93
|
+
|
|
94
|
+
Trả (hex, margin) — margin là khoảng cách tới màu GẦN NHẤT trong asset (hoặc trong
|
|
95
|
+
`avoid`, xem dưới). Margin thấp = rủi ro ăn thủng chủ thể. KHÔNG BAO GIỜ raise — kể cả
|
|
96
|
+
khi mọi ứng viên đều tệ (margin thấp/0), hàm này vẫn trả ứng viên tốt nhất tìm được;
|
|
97
|
+
nơi gọi (CLI `plan`) chịu trách nhiệm surface rủi ro đó qua field `key_safe`, không
|
|
98
|
+
phải hàm này từ chối chạy.
|
|
99
|
+
|
|
100
|
+
`avoid`: các màu RGB THÊM VÀO việc chấm điểm, không lấy từ palette của `img`. Cần vì
|
|
101
|
+
chroma_key() chạy trên ẢNH ĐÃ RESTYLE — một phong cách MỚI, bảng màu MỚI, khác hẳn
|
|
102
|
+
palette của asset gốc mà hàm này đọc được. Nếu phong cách mới có màu nổi bật (vd neon
|
|
103
|
+
xanh lá trên một asset gốc toàn màu đỏ), chấm điểm chỉ dựa trên palette nguồn sẽ báo
|
|
104
|
+
xanh lá "an toàn" (đúng với nguồn) rồi Gemini vẽ ra đúng highlight đó và bị chroma-key
|
|
105
|
+
ăn mất. Truyền bảng màu của style preset vào `avoid` (CLI: `plan --avoid`) để gộp
|
|
106
|
+
nguy cơ đó vào điểm số TRƯỚC khi sinh ảnh.
|
|
107
|
+
"""
|
|
108
|
+
colors = [hex_to_rgb(h) for h, _ in extract_palette(img, n=16)]
|
|
109
|
+
colors += list(avoid or [])
|
|
110
|
+
if not colors:
|
|
111
|
+
return KEY_CANDIDATES[0], float("inf")
|
|
112
|
+
|
|
113
|
+
scored = [
|
|
114
|
+
(min(color_dist(c, hex_to_rgb(k)) for c in colors), k) for k in KEY_CANDIDATES
|
|
115
|
+
]
|
|
116
|
+
green_margin = next(m for m, k in scored if k == "#00FF00")
|
|
117
|
+
if green_margin > GREEN_SAFE_THRESHOLD:
|
|
118
|
+
return "#00FF00", green_margin
|
|
119
|
+
|
|
120
|
+
# Green không an toàn; chọn ứng viên tốt nhất TRỪ green
|
|
121
|
+
margin, key = max((m, k) for m, k in scored if k != "#00FF00")
|
|
122
|
+
return key, margin
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _dist_map(arr_rgb: np.ndarray, key: tuple[int, int, int]) -> np.ndarray:
|
|
126
|
+
"""color_dist() bản vector hoá, cho cả ảnh cùng lúc."""
|
|
127
|
+
r = arr_rgb[..., 0].astype(np.float64)
|
|
128
|
+
g = arr_rgb[..., 1].astype(np.float64)
|
|
129
|
+
b = arr_rgb[..., 2].astype(np.float64)
|
|
130
|
+
kr, kg, kb = (float(v) for v in key)
|
|
131
|
+
rm = (r + kr) / 2.0
|
|
132
|
+
dr, dg, db = r - kr, g - kg, b - kb
|
|
133
|
+
return np.sqrt(
|
|
134
|
+
(2 + rm / 256.0) * dr * dr + 4 * dg * dg + (2 + (255 - rm) / 256.0) * db * db
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def chroma_key(
|
|
139
|
+
img: Image.Image, key_hex: str, t_low: float = 70.0, t_high: float = 170.0
|
|
140
|
+
) -> Image.Image:
|
|
141
|
+
"""Tách nền bằng ngưỡng MỀM.
|
|
142
|
+
|
|
143
|
+
Pixel cách màu key < t_low -> trong suốt hoàn toàn.
|
|
144
|
+
Pixel cách màu key > t_high -> đục hoàn toàn.
|
|
145
|
+
Ở giữa -> smoothstep. Ngưỡng mềm là thứ giữ cho mép không bị răng cưa;
|
|
146
|
+
ngưỡng cứng sẽ làm hỏng anti-alias mà Gemini vẽ ra.
|
|
147
|
+
"""
|
|
148
|
+
key = hex_to_rgb(key_hex)
|
|
149
|
+
rgb = np.asarray(img.convert("RGB"), dtype=np.uint8)
|
|
150
|
+
d = _dist_map(rgb, key)
|
|
151
|
+
|
|
152
|
+
t = np.clip((d - t_low) / max(1e-6, t_high - t_low), 0.0, 1.0)
|
|
153
|
+
alpha = t * t * (3.0 - 2.0 * t) # smoothstep
|
|
154
|
+
|
|
155
|
+
out = np.dstack([rgb.astype(np.float64), alpha * 255.0])
|
|
156
|
+
return Image.fromarray(np.rint(out).clip(0, 255).astype(np.uint8), "RGBA")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def despill(img: Image.Image, key_hex: str, strength: float = 1.0) -> Image.Image:
|
|
160
|
+
"""Khử ám màu key còn sót Ở MÉP (green fringe) — KHÔNG đụng vào chủ thể đục.
|
|
161
|
+
|
|
162
|
+
Kênh nào 'sáng' trong màu key thì bị kẹp xuống mức trung bình của các kênh còn lại.
|
|
163
|
+
Với #00FF00 -> kẹp G xuống (R+B)/2. Với #FF00FF -> kẹp R và B xuống G.
|
|
164
|
+
|
|
165
|
+
Mức kẹp được NHÂN theo `weight = (1 - alpha/255) * strength`, alpha là alpha của
|
|
166
|
+
CHÍNH pixel đó SAU chroma_key: pixel đục hoàn toàn (alpha=255, chủ thể thật) có
|
|
167
|
+
weight=0 -> KHÔNG bị đụng vào một chút nào; pixel trong suốt hoàn toàn (alpha=0, nền)
|
|
168
|
+
có weight=1 -> bị kẹp full; pixel mép bán trong suốt được kẹp tỉ lệ theo alpha.
|
|
169
|
+
|
|
170
|
+
VÌ SAO CẦN WEIGHT NÀY (Finding C1 — trước đây là bug CRITICAL): nền trong pipeline
|
|
171
|
+
này là một mảng phẳng số hoá mà Gemini vẽ ra (được yêu cầu vẽ nền màu key thuần),
|
|
172
|
+
không phải phông xanh vật lý bị ánh sáng dội lên diễn viên — nên KHÔNG hề có "ánh
|
|
173
|
+
sáng key dội vào chủ thể" ở pixel đục. Spill thật sự chỉ tồn tại ở dải anti-alias nơi
|
|
174
|
+
Gemini blend RGB của chủ thể với RGB của nền. Bản cũ áp công thức kẹp lên MỌI pixel
|
|
175
|
+
bất kể alpha — tức là một phép crush kênh TOÀN CỤC, không phải sửa mép — và ăn cả
|
|
176
|
+
màu vàng/nâu/đỏ hợp lệ của chủ thể: đo được trên bản cũ, vàng #FFD700 trên key xanh
|
|
177
|
+
lá ra (255,128,0) cam; đỏ (200,30,40) trên key hồng ra gần đen. Xem
|
|
178
|
+
test_despill_never_touches_opaque_subject_pixels (test hồi quy cho đúng bug này) và
|
|
179
|
+
test_despill_still_removes_edge_fringe (chứng minh mép bán trong suốt vẫn được sửa).
|
|
180
|
+
"""
|
|
181
|
+
key = hex_to_rgb(key_hex)
|
|
182
|
+
spill = [i for i in range(3) if key[i] >= 200]
|
|
183
|
+
base = [i for i in range(3) if key[i] < 200]
|
|
184
|
+
if not spill or not base:
|
|
185
|
+
return img.convert("RGBA")
|
|
186
|
+
|
|
187
|
+
arr = np.asarray(img.convert("RGBA"), dtype=np.float64)
|
|
188
|
+
weight = (1.0 - arr[..., 3] / 255.0) * strength
|
|
189
|
+
limit = arr[..., base].mean(axis=-1)
|
|
190
|
+
for i in spill:
|
|
191
|
+
ch = arr[..., i]
|
|
192
|
+
arr[..., i] = np.where(ch > limit, ch + (limit - ch) * weight, ch)
|
|
193
|
+
return Image.fromarray(np.rint(arr).clip(0, 255).astype(np.uint8), "RGBA")
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def alpha_coverage(img: Image.Image) -> float:
|
|
197
|
+
"""Tỉ lệ pixel được coi là nội dung. Dùng để phát hiện bản sinh hỏng."""
|
|
198
|
+
a = np.asarray(img.convert("RGBA"))[..., 3]
|
|
199
|
+
return float((a > ALPHA_FLOOR).sum()) / float(a.size)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# Alpha từ mức này trở lên mới coi là "đục thật" (không phải pixel mép anti-alias).
|
|
203
|
+
SOLID_ALPHA = 200
|
|
204
|
+
|
|
205
|
+
# Dưới tỉ lệ này thì chủ thể coi như bị bào thành bóng ma trong suốt.
|
|
206
|
+
# Đặt thấp (0.25) là cố ý: line-art mảnh (râu mèo) có rất nhiều pixel mép hợp lệ,
|
|
207
|
+
# không được báo nhầm chúng là hỏng.
|
|
208
|
+
BAD_SOLID_RATIO = 0.25
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def solid_ratio(img: Image.Image) -> float:
|
|
212
|
+
"""Tỉ lệ pixel ĐỤC THẬT trên tổng pixel CÓ NỘI DUNG.
|
|
213
|
+
|
|
214
|
+
alpha_coverage() chỉ trả lời "có pixel không", hàm này trả lời "pixel có đặc không".
|
|
215
|
+
Cần cả hai: màu key sát màu chủ thể sẽ bào chủ thể xuống alpha ~40 — vẫn đếm là
|
|
216
|
+
'có nội dung' nên coverage trông bình thường, nhưng asset thực tế là một bóng ma.
|
|
217
|
+
"""
|
|
218
|
+
a = np.asarray(img.convert("RGBA"))[..., 3]
|
|
219
|
+
content = int((a > ALPHA_FLOOR).sum())
|
|
220
|
+
if content == 0:
|
|
221
|
+
return 0.0
|
|
222
|
+
return float((a >= SOLID_ALPHA).sum()) / float(content)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def content_bbox(img: Image.Image, floor: int = ALPHA_FLOOR) -> tuple[int, int, int, int] | None:
|
|
226
|
+
"""Bbox của phần nhìn thấy được. Trả None nếu ảnh rỗng hoàn toàn.
|
|
227
|
+
|
|
228
|
+
Không dùng Image.getbbox() vì nó lấy alpha > 0, tức là dính cả nhiễu alpha=1
|
|
229
|
+
mà chroma key mềm để lại quanh mép.
|
|
230
|
+
"""
|
|
231
|
+
a = np.asarray(img.convert("RGBA"))[..., 3]
|
|
232
|
+
ys, xs = np.nonzero(a > floor)
|
|
233
|
+
if xs.size == 0:
|
|
234
|
+
return None
|
|
235
|
+
return (int(xs.min()), int(ys.min()), int(xs.max()) + 1, int(ys.max()) + 1)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
# Trên tỉ lệ này thì coi vùng thủng bên trong bbox là đáng ngờ (Finding I2).
|
|
239
|
+
BAD_HOLE_RATIO = 0.10
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def hole_ratio(img: Image.Image, bbox: tuple[int, int, int, int] | None = None) -> float:
|
|
243
|
+
"""Tỉ lệ pixel BÊN TRONG content_bbox bị thủng hoàn toàn (alpha <= ALPHA_FLOOR).
|
|
244
|
+
|
|
245
|
+
Bịt lỗ hổng cấu trúc của coverage/solid_ratio/border_impurity: cả ba đều đo tín hiệu
|
|
246
|
+
TOÀN CỤC hoặc VÀNH NGOÀI, nên một màu sát-key nằm LỌT THỎM giữa chủ thể (vd một viên
|
|
247
|
+
ngọc xanh lá trên nền chủ thể đỏ-nâu) bị chroma_key khoét thành lỗ trong suốt giữa
|
|
248
|
+
asset — coverage/solid_ratio/border_impurity đều không nhìn vào GIỮA bbox nên không
|
|
249
|
+
thấy gì, report ra "ok", `flags: []`, và asset lọt lưới với một lỗ thủng giữa thân.
|
|
250
|
+
|
|
251
|
+
CẢNH BÁO — cờ `bad_holes` dùng hàm này CHỈ MANG TÍNH THAM KHẢO, không phải gate cứng:
|
|
252
|
+
art hollow HỢP LỆ rất phổ biến (nhẫn, chữ "O", khung ảnh, bánh donut) và có thể có
|
|
253
|
+
hole_ratio cao mà không hề hỏng. BAD_HOLE_RATIO được chọn để bắt một viên ngọc bị
|
|
254
|
+
khoét GIỮA chủ thể (test_hole_ratio_flags_interior_hole) trong khi vẫn bỏ qua một
|
|
255
|
+
hình khối rỗng vừa phải, hợp lệ (test_hole_ratio_tolerates_legit_hollow_shape).
|
|
256
|
+
Ngưỡng này KHÔNG THỂ phân biệt hoàn hảo mọi trường hợp — art hollow rất lớn (interior
|
|
257
|
+
chiếm phần lớn bbox) vẫn có thể bị flag nhầm. Vì vậy `bad_holes` không bao giờ được
|
|
258
|
+
dùng để tự động loại bỏ asset (không trả None, không ép `bad_subject`) — contact
|
|
259
|
+
sheet, nơi người duyệt nhìn bằng mắt, mới là cửa gác thật sự.
|
|
260
|
+
"""
|
|
261
|
+
if bbox is None:
|
|
262
|
+
bbox = content_bbox(img)
|
|
263
|
+
if bbox is None:
|
|
264
|
+
return 0.0
|
|
265
|
+
x0, y0, x1, y1 = bbox
|
|
266
|
+
a = np.asarray(img.convert("RGBA"))[..., 3]
|
|
267
|
+
inner = a[y0:y1, x0:x1]
|
|
268
|
+
if inner.size == 0:
|
|
269
|
+
return 0.0
|
|
270
|
+
return float((inner <= ALPHA_FLOOR).sum()) / float(inner.size)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
# Vệt rác nhỏ hơn tỉ lệ này (so với mảng lớn nhất) thì bị xoá.
|
|
274
|
+
# Ca thật bắt buộc phải có nó: Gemini ĐÓNG DẤU WATERMARK ✦ lên góc dưới-phải MỌI ảnh nó sinh ra.
|
|
275
|
+
# Watermark đó là màu xanh hơi lệch key — đo được (81,249,77), cách #00FF00 đúng 176 trên thang
|
|
276
|
+
# redmean, tức là NHỈNH HƠN t_high=170 — nên nó sống sót qua chroma key và thành một chấm xanh
|
|
277
|
+
# ĐỤC HOÀN TOÀN nằm trong asset. Không despeckle thì mọi asset xuất ra đều dính chấm xanh.
|
|
278
|
+
SPECKLE_RATIO = 0.01
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _components(mask: np.ndarray) -> tuple[list, dict, dict]:
|
|
282
|
+
"""Gán nhãn liên thông 4-hướng bằng run-length + union-find.
|
|
283
|
+
|
|
284
|
+
scipy.ndimage.label không có trong môi trường này. Nhưng duyệt theo RUN (đoạn pixel liền
|
|
285
|
+
nhau trên một hàng) thay vì theo từng pixel thì số phần tử tụt từ ~1 triệu xuống vài nghìn,
|
|
286
|
+
nên union-find thuần Python vẫn nhanh.
|
|
287
|
+
"""
|
|
288
|
+
h, w = mask.shape
|
|
289
|
+
parent: dict[int, int] = {}
|
|
290
|
+
|
|
291
|
+
def find(x: int) -> int:
|
|
292
|
+
while parent[x] != x:
|
|
293
|
+
parent[x] = parent[parent[x]]
|
|
294
|
+
x = parent[x]
|
|
295
|
+
return x
|
|
296
|
+
|
|
297
|
+
def union(a: int, b: int) -> None:
|
|
298
|
+
ra, rb = find(a), find(b)
|
|
299
|
+
if ra != rb:
|
|
300
|
+
parent[rb] = ra
|
|
301
|
+
|
|
302
|
+
rows: list[list[tuple[int, int, int]]] = []
|
|
303
|
+
nxt = 0
|
|
304
|
+
for y in range(h):
|
|
305
|
+
edge = np.diff(np.concatenate(([0], mask[y].astype(np.int8), [0])))
|
|
306
|
+
runs = []
|
|
307
|
+
for s, e in zip(np.flatnonzero(edge == 1), np.flatnonzero(edge == -1)):
|
|
308
|
+
parent[nxt] = nxt
|
|
309
|
+
runs.append((int(s), int(e), nxt))
|
|
310
|
+
nxt += 1
|
|
311
|
+
if y > 0:
|
|
312
|
+
for s, e, lbl in runs:
|
|
313
|
+
for ps, pe, plbl in rows[-1]:
|
|
314
|
+
if ps < e and s < pe: # hai run chồng nhau theo cột -> cùng mảng
|
|
315
|
+
union(plbl, lbl)
|
|
316
|
+
rows.append(runs)
|
|
317
|
+
|
|
318
|
+
areas: dict[int, int] = {}
|
|
319
|
+
for runs in rows:
|
|
320
|
+
for s, e, lbl in runs:
|
|
321
|
+
r = find(lbl)
|
|
322
|
+
areas[r] = areas.get(r, 0) + (e - s)
|
|
323
|
+
return rows, {"find": find}, areas
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def component_boxes(mask: np.ndarray, min_size: int = 24) -> list[tuple[int, int, int, int]]:
|
|
327
|
+
"""Bounding box (x0, y0, x1, y1) của từng mảng liên thông True trong `mask`.
|
|
328
|
+
|
|
329
|
+
Dùng cho chế độ SCREEN SHEET: dải chi tiết là các chủ thể rời nhau trên nền key —
|
|
330
|
+
mỗi mảng liên thông = một asset. Bỏ mảng mà CẢ HAI chiều đều dưới `min_size`
|
|
331
|
+
(nhiễu/speck; asset mảnh dài như thanh progress 200×10 vẫn giữ vì một chiều đủ lớn).
|
|
332
|
+
|
|
333
|
+
Thứ tự trả về là THỨ TỰ ĐỌC: gom theo hàng (mảng nào có tâm-y nằm trong dải-y của
|
|
334
|
+
hàng hiện có thì vào hàng đó), hàng trên trước, trong hàng thì trái trước — để
|
|
335
|
+
cell1..cellN ổn định và khớp với cách người nhìn sheet."""
|
|
336
|
+
rows, uf, _areas = _components(mask)
|
|
337
|
+
find = uf["find"]
|
|
338
|
+
boxes: dict[int, list[int]] = {}
|
|
339
|
+
for y, runs in enumerate(rows):
|
|
340
|
+
for s, e, lbl in runs:
|
|
341
|
+
r = find(lbl)
|
|
342
|
+
b = boxes.get(r)
|
|
343
|
+
if b is None:
|
|
344
|
+
boxes[r] = [s, y, e, y + 1]
|
|
345
|
+
else:
|
|
346
|
+
if s < b[0]:
|
|
347
|
+
b[0] = s
|
|
348
|
+
if e > b[2]:
|
|
349
|
+
b[2] = e
|
|
350
|
+
b[3] = y + 1
|
|
351
|
+
keep = [tuple(b) for b in boxes.values()
|
|
352
|
+
if (b[2] - b[0]) >= min_size or (b[3] - b[1]) >= min_size]
|
|
353
|
+
|
|
354
|
+
keep.sort(key=lambda b: b[1])
|
|
355
|
+
bands: list[list[tuple[int, int, int, int]]] = []
|
|
356
|
+
for b in keep:
|
|
357
|
+
cy = (b[1] + b[3]) / 2
|
|
358
|
+
for band in bands:
|
|
359
|
+
if min(x[1] for x in band) <= cy <= max(x[3] for x in band):
|
|
360
|
+
band.append(b)
|
|
361
|
+
break
|
|
362
|
+
else:
|
|
363
|
+
bands.append([b])
|
|
364
|
+
ordered: list[tuple[int, int, int, int]] = []
|
|
365
|
+
for band in bands:
|
|
366
|
+
band.sort(key=lambda b: b[0])
|
|
367
|
+
ordered.extend(band)
|
|
368
|
+
return ordered
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def despeckle(img: Image.Image, ratio: float = SPECKLE_RATIO) -> tuple[Image.Image, int]:
|
|
372
|
+
"""Xoá các mảng liên thông nhỏ hơn `ratio` lần mảng lớn nhất. Trả (ảnh, số mảng đã xoá).
|
|
373
|
+
|
|
374
|
+
Chủ yếu để diệt watermark của Gemini (xem SPECKLE_RATIO), tiện thể dọn luôn nhiễu lấm tấm
|
|
375
|
+
mà chroma key để sót.
|
|
376
|
+
|
|
377
|
+
RỦI RO đã biết: asset gồm nhiều mảnh rời hợp lệ mà có mảnh rất nhỏ (hạt lấp lánh quanh ngôi
|
|
378
|
+
sao, dấu chấm trên chữ "i") thì mảnh đó có thể bị xoá oan. Ngưỡng 1% là bảo thủ, và số mảng
|
|
379
|
+
bị xoá LUÔN được báo trong report (`speckles_removed`) để người duyệt thấy — nhưng contact
|
|
380
|
+
sheet vẫn là cửa gác cuối, không phải con số này.
|
|
381
|
+
"""
|
|
382
|
+
arr = np.array(img.convert("RGBA"))
|
|
383
|
+
mask = arr[..., 3] > ALPHA_FLOOR
|
|
384
|
+
if not mask.any():
|
|
385
|
+
return img, 0
|
|
386
|
+
|
|
387
|
+
rows, uf, areas = _components(mask)
|
|
388
|
+
find = uf["find"]
|
|
389
|
+
biggest = max(areas.values())
|
|
390
|
+
doomed = {r for r, a in areas.items() if a < ratio * biggest}
|
|
391
|
+
if not doomed:
|
|
392
|
+
return img, 0
|
|
393
|
+
|
|
394
|
+
for y, runs in enumerate(rows):
|
|
395
|
+
for s, e, lbl in runs:
|
|
396
|
+
if find(lbl) in doomed:
|
|
397
|
+
arr[y, s:e, 3] = 0
|
|
398
|
+
|
|
399
|
+
return Image.fromarray(arr, "RGBA"), len(doomed)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
# ── Watermark ✦ của Gemini: hằng số ĐO ĐƯỢC, không phải đoán ────────────────────────────
|
|
403
|
+
# Gemini web dán một ngôi sao 4 cánh TRẮNG vào ô 48x48 px, cách cạnh phải và cạnh dưới đúng
|
|
404
|
+
# 96 px. Hiệu chuẩn bằng 3 ảnh "nền đen thuần" sinh riêng: hình học trùng khít từng pixel
|
|
405
|
+
# (828 pixel sáng, đỉnh 78/255, lệch giữa 3 mẫu < 1.4/255), và ba kênh RGB lệch nhau ĐÚNG 0.0
|
|
406
|
+
# -> overlay là TRẮNG THUẦN với alpha CỐ ĐỊNH. Đo lại trên canvas 1024x1024: vẫn 48x48 @ 96px,
|
|
407
|
+
# tức hình học KHÔNG scale theo cạnh ảnh.
|
|
408
|
+
#
|
|
409
|
+
# Vì alpha cố định, đây là phép trộn tuyến tính ĐẢO ĐƯỢC:
|
|
410
|
+
# O = S*(1-α) + 255*α => S = (O - 255*α) / (1-α)
|
|
411
|
+
# nên lấy lại được ĐÚNG pixel gốc — không cần inpaint/blur đoán nội dung. Sai số duy nhất là
|
|
412
|
+
# lượng tử hoá, bị khuếch đại 1/(1-α) ≈ 1.44 lần, tức tối đa ~±1.4 mức: mắt không thấy.
|
|
413
|
+
# Tính chất phụ tiện lợi: nền TRẮNG thuần (255) thì watermark vô hình VÀ phép đảo là ánh xạ
|
|
414
|
+
# đồng nhất, nên chạy trên ảnh nền trắng cũng vô hại.
|
|
415
|
+
_WM_BOX = 48 # cạnh ô watermark (px)
|
|
416
|
+
_WM_MARGIN = 96 # lề từ ô tới cạnh phải/dưới (px)
|
|
417
|
+
|
|
418
|
+
# 48x48 PNG grayscale, mỗi pixel = round(α*255). Nhúng base64 để imglib TỰ CHỨA: một file
|
|
419
|
+
# mask rời rất dễ bị bỏ quên khi copy skill sang máy khác, và thiếu nó thì watermark lặng lẽ
|
|
420
|
+
# không được xoá.
|
|
421
|
+
_WM_ALPHA_B64 = (
|
|
422
|
+
"iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAAAAAByaaZbAAABlUlEQVR42p2WwU/CMBTGv6KL0EQXswkjphcO48rReeU/94Ax"
|
|
423
|
+
"XDwKBzWOhBFpjB66mJHpgTlW1q6d79J07e+9167vawG1TaeagY76s0ep3woYAoNWwABgbQCPAo7fAmAAENoDlP2FsQRCqTED"
|
|
424
|
+
"tFgv8y2B6CiSZCeKhIZlqN2HRQQ6rrAX5gg0ciqj/VVuAqLzas+5jA3ApH8UkCaNwIQB5NAlgHtESIBzE9Q3zQ3eMw3gRq7q"
|
|
425
|
+
"33aDTaYERpOu+kQ7I8KrWe7NDz3oTdwLGaAhQ7PFS3EAmr2XyGoLgMAfMAd2JpLNlkz3ZUJ+LJlOsQjL+SAEXjDsWfrfvSWc"
|
|
426
|
+
"AIDHmMV0vuD/3lYAoLc9o3cJAMKxNvenl/rRaAiSzj/VpzVLrs5kF/t0ZkJXD9krrR/weJ43VFxSI+LH5ppOfFlRvx5MujRP"
|
|
427
|
+
"5fUadSnn1xUnd8Isld/5QWmWiY0YP5cFnC7s1LucNrOUe17IYyxsb6Cl1FgAgmsDaG7RBQCsWly7fAek2zYvgRhYt3o6rIGN"
|
|
428
|
+
"euRUU5Ip1BnhFzx/an1wRZA/AAAAAElFTkSuQmCC"
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
_WM_CACHE: dict[str, np.ndarray] = {}
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def _wm_alpha() -> np.ndarray:
|
|
435
|
+
"""Mask alpha 48x48 (float 0..1) của ngôi sao. Cache vì gọi cho mọi ảnh tải về."""
|
|
436
|
+
a = _WM_CACHE.get("a")
|
|
437
|
+
if a is None:
|
|
438
|
+
raw = base64.b64decode(_WM_ALPHA_B64)
|
|
439
|
+
a = np.asarray(Image.open(io.BytesIO(raw)).convert("L")).astype(np.float64) / 255.0
|
|
440
|
+
_WM_CACHE["a"] = a
|
|
441
|
+
return a
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def _wm_seam_energy(gray: np.ndarray, edge_w: np.ndarray) -> float:
|
|
445
|
+
"""Năng lượng gradient của `gray`, CHỈ tính trên đường viền ngôi sao (`edge_w`).
|
|
446
|
+
|
|
447
|
+
Dùng làm bằng chứng watermark có mặt hay không: viền ngôi sao là bước nhảy alpha
|
|
448
|
+
0 -> 0.3 trong ~1px, nên nếu watermark có thật thì gradient dồn đúng vào đó, và
|
|
449
|
+
sau khi đảo phép trộn nó phải TAN đi. Nếu ảnh KHÔNG có watermark, phép đảo tạo ra một
|
|
450
|
+
ngôi sao TỐI -> năng lượng viền TĂNG. Nhờ vậy một phép đo phân biệt được cả hai chiều.
|
|
451
|
+
"""
|
|
452
|
+
gy, gx = np.gradient(gray)
|
|
453
|
+
return float((np.hypot(gx, gy) * edge_w).sum())
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def unwatermark_gemini(
|
|
457
|
+
img: Image.Image, verify: bool = False, search: int = 0, min_gain: float = 0.25
|
|
458
|
+
) -> tuple[Image.Image, dict | None]:
|
|
459
|
+
"""Xoá ngôi sao ✦ của Gemini bằng cách ĐẢO phép trộn alpha. Trả (ảnh, info|None).
|
|
460
|
+
|
|
461
|
+
Phải chạy TRƯỚC mọi thao tác khác (tách nền, cắt sheet, resize): watermark nằm trong
|
|
462
|
+
pixel màu, nên mọi bước sau đó đều bôi nó ra hoặc bake nó vào asset. Sau khi resize/cắt,
|
|
463
|
+
hình học 48x48@96px không còn đúng và không còn cách nào lấy lại pixel gốc.
|
|
464
|
+
|
|
465
|
+
verify=False (mặc định): đảo VÔ ĐIỀU KIỆN tại đúng ô đã hiệu chuẩn. Dùng cho ảnh CHẮC
|
|
466
|
+
CHẮN từ Gemini web — ở đó không cần dò, và dò thì chỉ thêm đường hỏng.
|
|
467
|
+
verify=True: chỉ đảo khi đo được bằng chứng watermark có thật. Dùng cho ảnh NGUỒN LẪN
|
|
468
|
+
(vd process_raws nhận cả ảnh Codex/GPT Image 2 không có watermark) — ở đó đảo bừa sẽ
|
|
469
|
+
ĐÓNG một ngôi sao TỐI vào ảnh sạch.
|
|
470
|
+
|
|
471
|
+
`search` = bán kính dò lệch vị trí, MẶC ĐỊNH 0 vì hình học đã hiệu chuẩn là chính xác.
|
|
472
|
+
Bật lên chỉ khi Google dịch watermark: dò trên nền có hoa văn tuần hoàn có thể khớp
|
|
473
|
+
nhầm chỗ và đảo sai (đo được: nền vạch sọc chu kỳ 7px, sai số phục hồi vọt lên 67 mức).
|
|
474
|
+
|
|
475
|
+
info: {"box": (x0,y0,w,h), "gain": float|None} nếu đã xoá, None nếu bỏ qua.
|
|
476
|
+
"""
|
|
477
|
+
alpha = _wm_alpha()
|
|
478
|
+
box = _WM_BOX
|
|
479
|
+
rgb = img.convert("RGB") if img.mode not in ("RGB", "RGBA") else img
|
|
480
|
+
arr = np.asarray(rgb.convert("RGB")).astype(np.float64)
|
|
481
|
+
H, W = arr.shape[:2]
|
|
482
|
+
if W < box + _WM_MARGIN or H < box + _WM_MARGIN:
|
|
483
|
+
return img, None # ảnh nhỏ hơn cả lề -> không phải khung Gemini gốc
|
|
484
|
+
|
|
485
|
+
x_nom = W - _WM_MARGIN - box
|
|
486
|
+
y_nom = H - _WM_MARGIN - box
|
|
487
|
+
a3 = alpha[..., None]
|
|
488
|
+
inv = 1.0 - a3
|
|
489
|
+
|
|
490
|
+
def restore(x0: int, y0: int) -> np.ndarray:
|
|
491
|
+
reg = arr[y0:y0 + box, x0:x0 + box]
|
|
492
|
+
return (reg - 255.0 * a3) / inv
|
|
493
|
+
|
|
494
|
+
if not verify:
|
|
495
|
+
best = (x_nom, y_nom, None)
|
|
496
|
+
else:
|
|
497
|
+
gy, gx = np.gradient(alpha)
|
|
498
|
+
edge_w = np.hypot(gx, gy)
|
|
499
|
+
solid = alpha > 0.29 # lòng ngôi sao: alpha hằng, ~744 px
|
|
500
|
+
clear = alpha <= 0.0 # bốn góc ô 48x48: hoàn toàn ngoài ngôi sao, ~1400 px
|
|
501
|
+
if edge_w.sum() <= 0 or not solid.any() or not clear.any():
|
|
502
|
+
return img, None
|
|
503
|
+
a_solid = float(alpha[solid].mean())
|
|
504
|
+
|
|
505
|
+
# Hai VÀNH sát viền ngôi sao — một vành ngay trong, một vành ngay ngoài. Cần chúng vì
|
|
506
|
+
# so trung bình lòng-sao với bốn góc ô BỎ SÓT khi nền có hoa văn cỡ gần bằng ô (đo
|
|
507
|
+
# được: checker 16px lọt, do nền ở lòng sao khác nền ở góc). Hai vành kề nhau nên
|
|
508
|
+
# nền CỤC BỘ khớp, hiệu giữa chúng gần như chỉ còn phần watermark cộng vào.
|
|
509
|
+
sm = Image.fromarray((solid * 255).astype(np.uint8), "L")
|
|
510
|
+
grown = np.asarray(sm.filter(ImageFilter.MaxFilter(7))) > 127
|
|
511
|
+
shrunk = np.asarray(sm.filter(ImageFilter.MinFilter(7))) > 127
|
|
512
|
+
rim_in = solid & ~shrunk
|
|
513
|
+
rim_out = grown & clear
|
|
514
|
+
use_rim = rim_in.any() and rim_out.any()
|
|
515
|
+
a_rim = float(alpha[rim_in].mean()) if use_rim else 0.0
|
|
516
|
+
|
|
517
|
+
best_score, best = -1e9, None
|
|
518
|
+
for dy in range(-search, search + 1):
|
|
519
|
+
for dx in range(-search, search + 1):
|
|
520
|
+
x0, y0 = x_nom + dx, y_nom + dy
|
|
521
|
+
if x0 < 0 or y0 < 0 or x0 + box > W or y0 + box > H:
|
|
522
|
+
continue
|
|
523
|
+
reg = arr[y0:y0 + box, x0:x0 + box]
|
|
524
|
+
g = reg.mean(axis=2)
|
|
525
|
+
|
|
526
|
+
# RÀNG BUỘC CỨNG loại trước mọi phép đo mềm: nếu ô này thật sự có lớp trắng
|
|
527
|
+
# alpha α phủ lên ảnh gốc S ≥ 0 thì buộc O = S(1-α) + 255α ≥ 255α. Vậy chỉ cần
|
|
528
|
+
# thấy pixel trong lòng sao TỐI HƠN 255α là giả thiết SAI hẳn — không cần đoán
|
|
529
|
+
# thêm. Đây là thứ chặn ảnh sạch có chi tiết tối ở góc (đo được: nền checker
|
|
530
|
+
# 8px sạch từng bị xoá bừa trước khi có cổng này). Nới 6 mức cho nhiễu nén, và
|
|
531
|
+
# cho phép 1% pixel vi phạm.
|
|
532
|
+
floor = 255.0 * a_solid - 6.0
|
|
533
|
+
if float((g[solid] < floor).mean()) > 0.01:
|
|
534
|
+
continue
|
|
535
|
+
|
|
536
|
+
before = _wm_seam_energy(g, edge_w)
|
|
537
|
+
after = _wm_seam_energy(restore(x0, y0).mean(axis=2), edge_w)
|
|
538
|
+
gain = (before - after) / before if before > 1e-9 else 0.0
|
|
539
|
+
|
|
540
|
+
# Phép đo thứ hai, MIỄN NHIỄM với texture tần số cao: lấy trung bình trên
|
|
541
|
+
# ~744 px lòng sao so với ~1400 px bốn góc ô. Lớp phủ trắng alpha hằng nâng
|
|
542
|
+
# lòng sao lên đúng α*(255 - nền); nhiễu/checker bị triệt vì lấy trung bình
|
|
543
|
+
# hàng trăm pixel. Cần phép này vì đo gradient viền một mình BỎ SÓT khi nền
|
|
544
|
+
# rối (đo được: nền noise và checker 16px đều lọt).
|
|
545
|
+
def step_conf(inside: np.ndarray, outside: np.ndarray, a_in: float) -> float:
|
|
546
|
+
"""Độ tin cậy từ bậc sáng: 1.0 khi bậc đo được khớp đúng α*(255-nền)."""
|
|
547
|
+
bg = float(g[outside].mean())
|
|
548
|
+
pred = a_in * (255.0 - bg)
|
|
549
|
+
if pred <= 2.0: # nền gần trắng -> watermark vô hình, phép này vô nghĩa
|
|
550
|
+
return -1e9
|
|
551
|
+
r = (float(g[inside].mean()) - bg) / pred
|
|
552
|
+
return 1.0 - abs(r - 1.0) / 0.4 if 0.6 <= r <= 1.4 else -1e9
|
|
553
|
+
|
|
554
|
+
# Điểm = mức tin cậy CAO NHẤT trong ba phép đo độc lập, để mỗi phép bù điểm yếu
|
|
555
|
+
# của phép kia (gradient viền chịu nền rối; bậc sáng chịu nền có hoa văn to).
|
|
556
|
+
# Ràng buộc hai phía của tỉ lệ bậc sáng khiến ngôi sao THẬT với alpha khác
|
|
557
|
+
# (vd 0.6) KHÔNG bị nhận nhầm là watermark.
|
|
558
|
+
score = max(gain, step_conf(solid, clear, a_solid))
|
|
559
|
+
if use_rim:
|
|
560
|
+
score = max(score, step_conf(rim_in, rim_out, a_rim))
|
|
561
|
+
if score > best_score:
|
|
562
|
+
best_score, best = score, (x0, y0, gain)
|
|
563
|
+
if best is None or best_score < min_gain:
|
|
564
|
+
return img, None
|
|
565
|
+
|
|
566
|
+
x0, y0, gain = best
|
|
567
|
+
out = np.asarray(img.convert("RGBA") if img.mode == "RGBA" else rgb.convert("RGB")).copy()
|
|
568
|
+
out[y0:y0 + box, x0:x0 + box, :3] = np.clip(
|
|
569
|
+
np.rint(restore(x0, y0)), 0, 255).astype(np.uint8)
|
|
570
|
+
return Image.fromarray(out, img.mode if img.mode == "RGBA" else "RGB"), {
|
|
571
|
+
"box": (x0, y0, box, box), "gain": gain,
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
def strip_gemini_watermark(
|
|
576
|
+
img: Image.Image, corner: float = 0.22, max_ratio: float = 0.08
|
|
577
|
+
) -> tuple[Image.Image, int]:
|
|
578
|
+
"""LƯỚI AN TOÀN dọn dấu ✦ còn sót SAU khi đã tách nền. Không phải đường xoá chính.
|
|
579
|
+
|
|
580
|
+
Đường chính bây giờ là unwatermark_gemini(): nó đảo phép trộn alpha NGAY khi ảnh tải về,
|
|
581
|
+
lấy lại đúng pixel gốc. Bình thường tới đây không còn gì để xoá (`speckles_removed: 0`).
|
|
582
|
+
Giữ hàm này cho trường hợp Google đổi hình học watermark khiến phép đảo không khớp nữa —
|
|
583
|
+
lúc đó dấu ✦ lại sống sót qua chroma key và cần dọn thô ở đây.
|
|
584
|
+
|
|
585
|
+
Cách làm THÔ, chỉ dùng được sau tách nền: xoá mọi mảng liên thông nằm GỌN trong ô góc
|
|
586
|
+
dưới-phải (`corner` phần cạnh) VÀ nhỏ hơn `max_ratio` lần mảng lớn nhất — nên không đụng
|
|
587
|
+
chủ thể chính, nhưng cũng KHÔNG cứu được khi watermark nằm đè lên chủ thể hoặc trên nền
|
|
588
|
+
đục (vd mockup screen-sheet). Đó chính là lý do phải xoá sớm ở unwatermark_gemini().
|
|
589
|
+
"""
|
|
590
|
+
arr = np.array(img.convert("RGBA"))
|
|
591
|
+
H, W = arr.shape[:2]
|
|
592
|
+
mask = arr[..., 3] > ALPHA_FLOOR
|
|
593
|
+
if not mask.any():
|
|
594
|
+
return img, 0
|
|
595
|
+
|
|
596
|
+
rows, uf, areas = _components(mask)
|
|
597
|
+
find = uf["find"]
|
|
598
|
+
biggest = max(areas.values())
|
|
599
|
+
|
|
600
|
+
boxes: dict[int, list[int]] = {}
|
|
601
|
+
for y, runs in enumerate(rows):
|
|
602
|
+
for s, e, lbl in runs:
|
|
603
|
+
r = find(lbl)
|
|
604
|
+
b = boxes.get(r)
|
|
605
|
+
if b is None:
|
|
606
|
+
boxes[r] = [s, y, e, y + 1]
|
|
607
|
+
else:
|
|
608
|
+
b[0] = min(b[0], s); b[1] = min(b[1], y)
|
|
609
|
+
b[2] = max(b[2], e); b[3] = max(b[3], y + 1)
|
|
610
|
+
|
|
611
|
+
cx0, cy0 = W * (1.0 - corner), H * (1.0 - corner)
|
|
612
|
+
doomed = {
|
|
613
|
+
r for r, b in boxes.items()
|
|
614
|
+
if areas[r] < max_ratio * biggest and b[0] >= cx0 and b[1] >= cy0
|
|
615
|
+
}
|
|
616
|
+
if not doomed:
|
|
617
|
+
return img, 0
|
|
618
|
+
for y, runs in enumerate(rows):
|
|
619
|
+
for s, e, lbl in runs:
|
|
620
|
+
if find(lbl) in doomed:
|
|
621
|
+
arr[y, s:e, 3] = 0
|
|
622
|
+
return Image.fromarray(arr, "RGBA"), len(doomed)
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
def fit_into(
|
|
626
|
+
content: Image.Image,
|
|
627
|
+
canvas_size: tuple[int, int],
|
|
628
|
+
target_box: tuple[int, int, int, int],
|
|
629
|
+
upscale=None,
|
|
630
|
+
report: dict | None = None,
|
|
631
|
+
) -> Image.Image:
|
|
632
|
+
"""Scale `content` vừa khít (contain) vào target_box, căn giữa, dán lên canvas trong suốt.
|
|
633
|
+
|
|
634
|
+
Giữ đúng tỉ lệ của content — thà chừa khoảng trống còn hơn bóp méo asset.
|
|
635
|
+
|
|
636
|
+
`upscale`: callable phóng 4x một mảng HxWx3 uint8 (xem upscale.upscale_rgb). Chỉ dùng
|
|
637
|
+
khi phải PHÓNG TO (scale > UPSCALE_MIN_GAIN) — đó đúng là ca LANCZOS làm ảnh mềm nhũn.
|
|
638
|
+
Thu nhỏ thì LANCZOS đã là tốt nhất, không đụng tới. Truyền None để giữ hành vi cũ.
|
|
639
|
+
`report`: dict tuỳ chọn, sẽ được NHÉT THÊM báo cáo chọn cách phóng to (xem best_upscale).
|
|
640
|
+
"""
|
|
641
|
+
tx0, ty0, tx1, ty1 = target_box
|
|
642
|
+
tw, th = max(1, tx1 - tx0), max(1, ty1 - ty0)
|
|
643
|
+
cw, ch = content.size
|
|
644
|
+
|
|
645
|
+
scale = min(tw / cw, th / ch)
|
|
646
|
+
nw, nh = max(1, round(cw * scale)), max(1, round(ch * scale))
|
|
647
|
+
|
|
648
|
+
if scale > UPSCALE_MIN_GAIN:
|
|
649
|
+
scaled, up_rep = best_upscale(content, (nw, nh), backend=upscale)
|
|
650
|
+
else:
|
|
651
|
+
# Gọi thẳng resize(): Pillow tự convert RGBA -> RGBa (premultiplied) bên trong rồi
|
|
652
|
+
# convert ngược, nên mép không bị rỉ màu. Premultiply BẰNG TAY ở đây là phản tác dụng —
|
|
653
|
+
# Pillow sẽ nhân lần hai và phá màu ở alpha thấp (đo được: R 255 -> 0 tại alpha ~2).
|
|
654
|
+
scaled = content.convert("RGBA").resize((nw, nh), Image.LANCZOS)
|
|
655
|
+
up_rep = {"upscale_method": None, "upscale_factor": round(scale, 4)}
|
|
656
|
+
if report is not None:
|
|
657
|
+
report.update(up_rep)
|
|
658
|
+
canvas = Image.new("RGBA", canvas_size, (0, 0, 0, 0))
|
|
659
|
+
canvas.paste(scaled, (tx0 + (tw - nw) // 2, ty0 + (th - nh) // 2))
|
|
660
|
+
return canvas
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
# ---------------------------------------------------------------------- PHÓNG TO
|
|
664
|
+
# Ảnh AI sinh ra hay NHỎ hơn kích thước đích (Gemini/GPT trả về khung cố định, còn asset
|
|
665
|
+
# game thì cần đúng size gốc). LANCZOS phóng to chỉ trải mép ra cho mềm đi — nó không có
|
|
666
|
+
# thông tin nào để thêm. Real-ESRGAN anime_6B thì dựng lại nét. Nhưng nó cũng có thể BỊA:
|
|
667
|
+
# thêm quầng sáng, đổi nét, ăn mất chi tiết nhỏ. Nên ở đây cũng chạy nhiều cách rồi chấm
|
|
668
|
+
# điểm chọn bản tốt nhất, đúng như đường tách nền.
|
|
669
|
+
|
|
670
|
+
UPSCALE_MIN_GAIN = 1.02 # phóng dưới mức này thì LANCZOS đã đủ, không gọi model làm gì
|
|
671
|
+
UPSCALE_MAX_PASS = 2 # tối đa 2 lượt x4 = 16x; hơn nữa là ảnh nguồn quá nhỏ, vô vọng
|
|
672
|
+
UPSCALE_GRAY = 128.0 # nền xám để "nhìn thấy" ảnh RGBA lúc đo (xem _visible)
|
|
673
|
+
UPSCALE_RING_TOL = 3.0 # vượt biên min/max cục bộ quá mức này mới tính là quầng/ringing
|
|
674
|
+
UPSCALE_BLEED_ITERS = 8 # số vòng loang màu tiền cảnh ra vùng trong suốt trước khi phóng
|
|
675
|
+
UPSCALE_FLAT_ENERGY = 1e-6 # dưới mức này coi ảnh nguồn là PHẲNG (xem score_upscale)
|
|
676
|
+
|
|
677
|
+
# Ngưỡng LOẠI THẲNG — LƯỚI AN TOÀN cho ca thảm hoạ, KHÔNG phải công cụ xếp hạng. Đặt cao
|
|
678
|
+
# hẳn trên vùng giá trị của bản TỐT, vì loại nhầm bản tốt thì hậu quả là âm thầm rơi về
|
|
679
|
+
# LANCZOS mờ — tệ hơn hẳn việc để lọt một bản hơi kém.
|
|
680
|
+
# Số đo (bản dựng sẵn cho 4 cảnh x 2 độ phân giải, xem SKILL.md § Phóng to):
|
|
681
|
+
# lr_err bản OK 1.0–6.5 (asset thật lên tới 14.4) | ảnh SAI HẲN 72.8–87.7
|
|
682
|
+
# cov_err bản OK <= 0.053 | matte bị phá 0.099–0.122
|
|
683
|
+
UPSCALE_BAD_LR = 30.0 # thu nhỏ ngược lại mà lệch ảnh gốc quá mức này -> bịa nội dung
|
|
684
|
+
UPSCALE_BAD_COV = 0.08 # coverage lệch quá mức này -> matte bị ăn mòn hoặc phình ra
|
|
685
|
+
|
|
686
|
+
# Trọng số chấm điểm — DÒ ĐƯỢC, không phải chọn cho đẹp. Cách dò: vẽ cùng một cảnh ở CẢ
|
|
687
|
+
# HAI độ phân giải (không ảnh nào suy ra từ ảnh kia) rồi tìm bộ trọng số cho thứ tự xếp
|
|
688
|
+
# hạng trùng với LPIPS. Kết quả: đúng 87.5% trên phần huấn luyện VÀ 87.5% trên nửa giữ
|
|
689
|
+
# lại. Hai chi tiết đáng nhớ vì chúng đi ngược trực giác:
|
|
690
|
+
#
|
|
691
|
+
# 1. `ring` mới là tiêu chí phân định, không phải `lr_err`. Bỏ ring (W_RING=0) thì độ đúng
|
|
692
|
+
# tụt xuống 66.9% — đúng bằng mức của "cứ luôn chọn esrgan", tức là bộ điểm không còn
|
|
693
|
+
# đóng góp gì. Chính ring bắt được đúng nhóm ca mà LANCZOS thật sự thắng (hoạ tiết
|
|
694
|
+
# dày, nét mảnh san sát — chỗ model bịa ra nhiều nhất).
|
|
695
|
+
# 2. `lr_err` và `cov_err` KHÔNG có sức xếp hạng. Dò ra trọng số tối ưu của chúng bằng 0;
|
|
696
|
+
# đẩy W_LR lên 0.2 làm độ đúng tụt còn 80.6%. Giữ W_LR bé xíu chỉ để phá thế hoà, còn
|
|
697
|
+
# cov_err rời hẳn khỏi tổng điểm — nó sống bằng vai trò loại thẳng ở trên.
|
|
698
|
+
W_BLUR = 3.0
|
|
699
|
+
W_RING = 0.40 # trên thang mức màu vượt biên trung bình (0.2–0.6 đều cho 87.5%)
|
|
700
|
+
W_LR = 0.05 # trên thang mức màu 0..255 — chỉ để phá thế hoà
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
def _shift(a: np.ndarray, dy: int, dx: int) -> np.ndarray:
|
|
704
|
+
"""Dịch mảng `dy/dx` pixel, chỗ trống điền 0 (KHÔNG cuộn vòng như np.roll)."""
|
|
705
|
+
out = np.zeros_like(a)
|
|
706
|
+
h, w = a.shape[:2]
|
|
707
|
+
ys0, ys1 = max(0, -dy), h - max(0, dy)
|
|
708
|
+
xs0, xs1 = max(0, -dx), w - max(0, dx)
|
|
709
|
+
out[max(0, dy):h - max(0, -dy), max(0, dx):w - max(0, -dx)] = a[ys0:ys1, xs0:xs1]
|
|
710
|
+
return out
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
def _bleed_rgb(rgb: np.ndarray, opaque: np.ndarray,
|
|
714
|
+
iters: int = UPSCALE_BLEED_ITERS) -> np.ndarray:
|
|
715
|
+
"""Loang màu tiền cảnh ra vùng trong suốt trước khi đưa vào model phóng to.
|
|
716
|
+
|
|
717
|
+
BẮT BUỘC, không phải tinh chỉnh: sau unmix_cutout, pixel nền vẫn GIỮ NGUYÊN màu key
|
|
718
|
+
(magenta/xanh lá) với alpha 0 — vô hình khi hiển thị, nhưng model phóng to chỉ nhận
|
|
719
|
+
RGB nên nó thấy magenta thật và kéo màu đó vào mép chủ thể. Kết quả: viền hồng trên
|
|
720
|
+
ảnh mà đường tách nền vừa mới làm sạch tới mức cast = 0.
|
|
721
|
+
"""
|
|
722
|
+
out = rgb.astype(np.float32).copy()
|
|
723
|
+
if not opaque.any():
|
|
724
|
+
return out
|
|
725
|
+
known = opaque.copy()
|
|
726
|
+
for _ in range(max(0, iters)):
|
|
727
|
+
if known.all():
|
|
728
|
+
break
|
|
729
|
+
k = known.astype(np.float32)
|
|
730
|
+
s = np.zeros_like(out)
|
|
731
|
+
c = np.zeros_like(k)
|
|
732
|
+
for dy, dx in ((-1, 0), (1, 0), (0, -1), (0, 1)):
|
|
733
|
+
s += _shift(out * k[..., None], dy, dx)
|
|
734
|
+
c += _shift(k, dy, dx)
|
|
735
|
+
fill = (~known) & (c > 0)
|
|
736
|
+
if not fill.any():
|
|
737
|
+
break
|
|
738
|
+
out[fill] = s[fill] / c[fill][:, None]
|
|
739
|
+
known |= fill
|
|
740
|
+
if not known.all():
|
|
741
|
+
# Vùng trong suốt rộng, loang không tới: điền màu trung bình chủ thể. Miễn là KHÔNG
|
|
742
|
+
# còn màu key nằm lại, còn màu gì thì cũng vô hình (alpha 0).
|
|
743
|
+
out[~known] = rgb[opaque].mean(axis=0)
|
|
744
|
+
return out
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
def upscale_rgba(img: Image.Image, backend, alpha_esrgan: bool = True) -> Image.Image:
|
|
748
|
+
"""Phóng 4x một ảnh RGBA bằng `backend` (callable HxWx3 uint8 -> 4x uint8).
|
|
749
|
+
|
|
750
|
+
RGB và ALPHA được phóng RIÊNG: model chỉ ăn 3 kênh. Alpha đi qua chính model đó dưới
|
|
751
|
+
dạng ảnh xám 3 kênh (`alpha_esrgan=True`) — mặc định này là kết quả ĐO, không phải
|
|
752
|
+
phỏng đoán; xem bảng trong SKILL.md § Phóng to. Đặt False để phóng alpha bằng LANCZOS.
|
|
753
|
+
"""
|
|
754
|
+
a = np.asarray(img.convert("RGBA"))
|
|
755
|
+
rgb, al = a[..., :3], a[..., 3]
|
|
756
|
+
opaque = al > 0
|
|
757
|
+
|
|
758
|
+
if opaque.all():
|
|
759
|
+
big = backend(np.ascontiguousarray(rgb))
|
|
760
|
+
alpha = np.full(big.shape[:2], 255, np.uint8)
|
|
761
|
+
return Image.fromarray(np.dstack([big, alpha]), "RGBA")
|
|
762
|
+
|
|
763
|
+
filled = np.clip(_bleed_rgb(rgb.astype(np.float32), opaque), 0, 255).astype(np.uint8)
|
|
764
|
+
big = backend(np.ascontiguousarray(filled))
|
|
765
|
+
th, tw = big.shape[:2]
|
|
766
|
+
if alpha_esrgan:
|
|
767
|
+
gray = backend(np.ascontiguousarray(np.dstack([al, al, al])))
|
|
768
|
+
alpha = gray[..., 0]
|
|
769
|
+
if alpha.shape != (th, tw): # backend giả trong test có thể lệch
|
|
770
|
+
alpha = np.asarray(Image.fromarray(alpha, "L").resize((tw, th), Image.LANCZOS))
|
|
771
|
+
else:
|
|
772
|
+
alpha = np.asarray(Image.fromarray(al, "L").resize((tw, th), Image.LANCZOS))
|
|
773
|
+
return Image.fromarray(np.dstack([big, alpha]), "RGBA")
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
def _visible(img: Image.Image) -> np.ndarray:
|
|
777
|
+
"""Ảnh RGBA ghép lên nền xám -> float32 HxWx3: đúng thứ MẮT THẤY.
|
|
778
|
+
|
|
779
|
+
Đo trên kênh RGB thô là sai: sau tách nền, vùng alpha 0 vẫn mang màu key, và mọi tiêu
|
|
780
|
+
chí sẽ bị màu vô hình đó chi phối thay vì bị chi phối bởi chủ thể.
|
|
781
|
+
"""
|
|
782
|
+
a = np.asarray(img.convert("RGBA"), dtype=np.float32)
|
|
783
|
+
al = a[..., 3:4] / 255.0
|
|
784
|
+
return a[..., :3] * al + UPSCALE_GRAY * (1.0 - al)
|
|
785
|
+
|
|
786
|
+
|
|
787
|
+
def _grad_energy(g: np.ndarray) -> float:
|
|
788
|
+
"""NĂNG LƯỢNG gradient (bình phương), KHÔNG phải độ lớn trung bình.
|
|
789
|
+
|
|
790
|
+
Phân biệt sắc/mờ phải dùng bình phương: một cạnh cao Δ trải qua s pixel có tổng |∇|
|
|
791
|
+
KHÔNG đổi (vẫn = Δ) nên |∇| trung bình mù với độ mờ; còn tổng ∇² tụt từ Δ² xuống
|
|
792
|
+
Δ²/s. Đây chính là chỗ LANCZOS thua và là lý do tiêu chí `blur` tồn tại.
|
|
793
|
+
"""
|
|
794
|
+
gy = np.diff(g, axis=0)
|
|
795
|
+
gx = np.diff(g, axis=1)
|
|
796
|
+
return float((gy ** 2).mean() + (gx ** 2).mean())
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
def _local_minmax(v: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
|
|
800
|
+
"""Min/max trong cửa sổ 3x3 cho từng kênh (numpy thuần)."""
|
|
801
|
+
lo, hi = v.copy(), v.copy()
|
|
802
|
+
for dy in (-1, 0, 1):
|
|
803
|
+
for dx in (-1, 0, 1):
|
|
804
|
+
if dy == 0 and dx == 0:
|
|
805
|
+
continue
|
|
806
|
+
# Ngoài biên coi như chính pixel đó -> không tạo ra cực trị giả ở mép ảnh.
|
|
807
|
+
s = _shift(v, dy, dx)
|
|
808
|
+
edge = _shift(np.ones(v.shape[:2], np.float32), dy, dx) == 0
|
|
809
|
+
s[edge] = v[edge]
|
|
810
|
+
lo = np.minimum(lo, s)
|
|
811
|
+
hi = np.maximum(hi, s)
|
|
812
|
+
return lo, hi
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
def score_upscale(src: Image.Image, out: Image.Image) -> dict:
|
|
816
|
+
"""Chấm điểm một bản phóng to. ĐIỂM THẤP = TỐT (giống score_cutout).
|
|
817
|
+
|
|
818
|
+
XẾP HẠNG bằng hai tiêu chí, mỗi cái bắt một kiểu hỏng mà cái kia mù hoàn toàn:
|
|
819
|
+
|
|
820
|
+
blur Năng lượng gradient (bình phương) so với bản phóng NEAREST — NEAREST giữ
|
|
821
|
+
nguyên độ cao mọi bậc nhảy của ảnh gốc nên nó là trần lý thuyết của độ sắc.
|
|
822
|
+
Bắt ảnh MỀM NHŨN, đúng thứ LANCZOS luôn mắc phải khi phóng to.
|
|
823
|
+
ring Độ lớn TRUNG BÌNH mà pixel vượt ra ngoài biên min/max cục bộ của ảnh gốc
|
|
824
|
+
(đơn vị mức màu). Bắt QUẦNG SÁNG và chi tiết BỊA RA — thứ `blur` chấm
|
|
825
|
+
NGƯỢC: quầng làm gradient mạnh hơn nên blur tưởng là "sắc nét, tốt".
|
|
826
|
+
|
|
827
|
+
Hai tiêu chí còn lại được ĐO là KHÔNG có sức xếp hạng, nên chúng chỉ làm LƯỚI AN TOÀN
|
|
828
|
+
(loại thẳng), không kéo điểm:
|
|
829
|
+
|
|
830
|
+
lr_err Thu nhỏ bản phóng về đúng size gốc rồi so với gốc — chỗ duy nhất có sự thật
|
|
831
|
+
nền tuyệt đối. Nghe thì cứng nhất, nhưng nó thiên vị ảnh MỜ một cách có hệ
|
|
832
|
+
thống (đúng bài toán distortion–perception), nên dùng nó để xếp hạng là tự
|
|
833
|
+
tay chọn bản mờ. Vào tổng điểm với trọng số bé xíu, chỉ để phá thế hoà.
|
|
834
|
+
cov_err Chỉ cho ảnh RGBA: lệch alpha_coverage so với gốc. Bắt matte bị ăn mòn hoặc
|
|
835
|
+
phình ra. Đo được là quá thô để xếp hạng (matte hỏng vẫn cho cov_err 0.002),
|
|
836
|
+
nhưng vẫn chặn được ca phá hoại rõ rệt.
|
|
837
|
+
"""
|
|
838
|
+
sw, sh = src.size
|
|
839
|
+
tw, th = out.size
|
|
840
|
+
vs = _visible(src)
|
|
841
|
+
vo = _visible(out)
|
|
842
|
+
|
|
843
|
+
# 1. back-projection: thu nhỏ bản phóng về size gốc rồi so với gốc.
|
|
844
|
+
back = out.resize((sw, sh), Image.LANCZOS)
|
|
845
|
+
lr_err = float(np.sqrt(((_visible(back) - vs) ** 2).mean()))
|
|
846
|
+
|
|
847
|
+
# 2. độ sắc, lấy bản NEAREST làm mốc trần.
|
|
848
|
+
nn = src.convert("RGBA").resize((tw, th), Image.NEAREST)
|
|
849
|
+
e_nn = _grad_energy(_visible(nn) @ np.array([0.299, 0.587, 0.114], np.float32))
|
|
850
|
+
e_out = _grad_energy(vo @ np.array([0.299, 0.587, 0.114], np.float32))
|
|
851
|
+
if e_nn <= UPSCALE_FLAT_ENERGY:
|
|
852
|
+
# Ảnh nguồn PHẲNG (mảng màu đặc, khung trơn): không có cạnh nào để mà làm sắc, nên
|
|
853
|
+
# "mờ" là khái niệm vô nghĩa ở đây. Không có nhánh này thì e_nn ~ 0 biến sharp thành
|
|
854
|
+
# 0/0 -> blur = 1.0 cho MỌI ứng viên, điểm bằng nhau, và bản thắng rơi vào tay thứ
|
|
855
|
+
# tự dict — tức là chọn bừa. Bắt được nhờ test end-to-end trên chủ thể màu đặc.
|
|
856
|
+
sharp, blur = 1.0, 0.0
|
|
857
|
+
else:
|
|
858
|
+
sharp = e_out / e_nn
|
|
859
|
+
blur = max(0.0, 1.0 - min(1.0, sharp))
|
|
860
|
+
|
|
861
|
+
# 3. quầng sáng: ĐỘ LỚN vượt biên trung bình (đơn vị mức màu 0..255), không phải SỐ
|
|
862
|
+
# ĐẾM pixel vượt biên. Bản đếm đã thử và bỏ: nó báo 27–37% pixel "ringing" trên ảnh
|
|
863
|
+
# đục, trong khi sai số thật lại nói bản đó TỐT HƠN — thứ nó đếm là vân nhiễu li ti
|
|
864
|
+
# của GAN (vượt biên 1–4 mức), không phải quầng. Quầng thật vượt biên hàng chục mức
|
|
865
|
+
# và nằm cạnh cạnh mạnh, nên đo độ lớn mới tách được hai thứ đó ra.
|
|
866
|
+
lo, hi = _local_minmax(vs)
|
|
867
|
+
yi = np.minimum((np.arange(th) * sh) // max(1, th), sh - 1)
|
|
868
|
+
xi = np.minimum((np.arange(tw) * sw) // max(1, tw), sw - 1)
|
|
869
|
+
over = 0.0
|
|
870
|
+
for c in range(3):
|
|
871
|
+
lo_c = lo[..., c][yi][:, xi]
|
|
872
|
+
hi_c = hi[..., c][yi][:, xi]
|
|
873
|
+
o_c = vo[..., c]
|
|
874
|
+
ex = np.maximum(np.maximum(o_c - hi_c, lo_c - o_c) - UPSCALE_RING_TOL, 0.0)
|
|
875
|
+
over += float(ex.mean())
|
|
876
|
+
ring = over / 3.0
|
|
877
|
+
|
|
878
|
+
cov_err = abs(alpha_coverage(out) - alpha_coverage(src))
|
|
879
|
+
|
|
880
|
+
m = {
|
|
881
|
+
"lr_err": lr_err,
|
|
882
|
+
"blur": blur,
|
|
883
|
+
"sharp": sharp,
|
|
884
|
+
"ring": ring,
|
|
885
|
+
"cov_err": cov_err,
|
|
886
|
+
}
|
|
887
|
+
dq: list[str] = []
|
|
888
|
+
if lr_err > UPSCALE_BAD_LR:
|
|
889
|
+
dq.append("bia_noi_dung")
|
|
890
|
+
if cov_err > UPSCALE_BAD_COV:
|
|
891
|
+
dq.append("lech_matte")
|
|
892
|
+
m["disqualified"] = dq
|
|
893
|
+
|
|
894
|
+
m["score"] = W_BLUR * blur + W_RING * ring + W_LR * lr_err + (10.0 if dq else 0.0)
|
|
895
|
+
return m
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
def best_upscale(img: Image.Image, target: tuple[int, int], backend=None) -> tuple[Image.Image, dict]:
|
|
899
|
+
"""Phóng `img` lên đúng `target`, chạy NHIỀU cách rồi chấm điểm chọn bản tốt nhất.
|
|
900
|
+
|
|
901
|
+
Ứng viên:
|
|
902
|
+
lanczos Pillow LANCZOS thẳng tới đích. Luôn có, luôn là lưới an toàn.
|
|
903
|
+
esrgan Real-ESRGAN 4x (lặp tới khi đủ lớn, tối đa UPSCALE_MAX_PASS lượt) rồi
|
|
904
|
+
LANCZOS thu về đúng đích. Tốt nhất khi đích <= 4x.
|
|
905
|
+
esrgan-pre LANCZOS lên đích/4 TRƯỚC rồi mới 4x. Chỉ dựng khi đích > 4x: lúc đó
|
|
906
|
+
`esrgan` phải LANCZOS PHÓNG TO lần nữa sau model (mờ lại đúng thứ vừa
|
|
907
|
+
làm nét), còn cách này để model chạy sau cùng.
|
|
908
|
+
|
|
909
|
+
`backend=None` (thiếu torch) -> chỉ còn lanczos, vẫn trả ảnh đúng size. Model nổ giữa
|
|
910
|
+
chừng cũng vậy: ghi vào report["upscale_errors"] rồi đi tiếp, KHÔNG làm hỏng pipeline.
|
|
911
|
+
"""
|
|
912
|
+
tw, th = int(target[0]), int(target[1])
|
|
913
|
+
sw, sh = img.size
|
|
914
|
+
factor = max(tw / max(1, sw), th / max(1, sh))
|
|
915
|
+
src = img.convert("RGBA")
|
|
916
|
+
|
|
917
|
+
cands: list[tuple[str, Image.Image]] = [("lanczos", src.resize((tw, th), Image.LANCZOS))]
|
|
918
|
+
errs: list[str] = []
|
|
919
|
+
|
|
920
|
+
if backend is not None and factor > UPSCALE_MIN_GAIN:
|
|
921
|
+
def _net(im: Image.Image, passes: int) -> Image.Image:
|
|
922
|
+
for _ in range(passes):
|
|
923
|
+
im = upscale_rgba(im, backend)
|
|
924
|
+
return im
|
|
925
|
+
|
|
926
|
+
passes = min(UPSCALE_MAX_PASS, max(1, math.ceil(math.log(max(factor, 1.0), 4) - 1e-9)))
|
|
927
|
+
try:
|
|
928
|
+
big = _net(src, passes)
|
|
929
|
+
cands.append(("esrgan", big.resize((tw, th), Image.LANCZOS)))
|
|
930
|
+
except Exception as e:
|
|
931
|
+
errs.append(f"esrgan: {type(e).__name__}: {e}")
|
|
932
|
+
|
|
933
|
+
if factor > 4.0:
|
|
934
|
+
try:
|
|
935
|
+
pre = src.resize((max(1, -(-tw // 4)), max(1, -(-th // 4))), Image.LANCZOS)
|
|
936
|
+
out = _net(pre, 1)
|
|
937
|
+
if out.size != (tw, th):
|
|
938
|
+
out = out.resize((tw, th), Image.LANCZOS)
|
|
939
|
+
cands.append(("esrgan-pre", out))
|
|
940
|
+
except Exception as e:
|
|
941
|
+
errs.append(f"esrgan-pre: {type(e).__name__}: {e}")
|
|
942
|
+
|
|
943
|
+
table: dict[str, dict] = {}
|
|
944
|
+
for name, im in cands:
|
|
945
|
+
try:
|
|
946
|
+
table[name] = score_upscale(src, im)
|
|
947
|
+
except Exception as e: # chấm điểm hỏng != ảnh hỏng
|
|
948
|
+
errs.append(f"score {name}: {type(e).__name__}: {e}")
|
|
949
|
+
table[name] = {"score": float("inf"), "disqualified": ["khong_cham_duoc"]}
|
|
950
|
+
|
|
951
|
+
best = min(table, key=lambda k: table[k]["score"])
|
|
952
|
+
report = {
|
|
953
|
+
"upscale_method": best,
|
|
954
|
+
"upscale_score": table[best]["score"],
|
|
955
|
+
"upscale_table": table,
|
|
956
|
+
"upscale_factor": round(factor, 4),
|
|
957
|
+
"upscale_from": [sw, sh],
|
|
958
|
+
"upscale_to": [tw, th],
|
|
959
|
+
"upscale_candidates": [n for n, _ in cands],
|
|
960
|
+
"upscale_all_disqualified": all(t.get("disqualified") for t in table.values()),
|
|
961
|
+
}
|
|
962
|
+
if errs:
|
|
963
|
+
report["upscale_errors"] = errs
|
|
964
|
+
return dict(cands)[best], report
|
|
965
|
+
|
|
966
|
+
|
|
967
|
+
# Bề dày vành ngoài dùng để kiểm nền, tính bằng pixel của ảnh Gemini sinh ra.
|
|
968
|
+
BORDER_PX = 4
|
|
969
|
+
|
|
970
|
+
# Vành ngoài lệch màu key quá mức này -> nền không phẳng (Gemini vẽ bóng/gradient/viền lên nền).
|
|
971
|
+
BAD_BORDER_DIST = 60.0
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
def border_impurity(img: Image.Image, key_hex: str, border: int = BORDER_PX) -> float:
|
|
975
|
+
"""Khoảng cách trung bình của VÀNH NGOÀI ảnh so với màu key.
|
|
976
|
+
|
|
977
|
+
Kiểm nền một cách TRỰC TIẾP. alpha_coverage chỉ suy ra gián tiếp: nó chỉ nổ khi
|
|
978
|
+
nền hỏng TOÀN BỘ (coverage > 95%). Nền hỏng MỘT PHẦN — bóng đổ, gradient, viền —
|
|
979
|
+
thì coverage không thấy gì, mà mảng đặc đó lại thổi phồng solid_ratio và che mất
|
|
980
|
+
chủ thể bóng ma. Vành ngoài gần như luôn là nền thuần, nên đo thẳng vào đó.
|
|
981
|
+
|
|
982
|
+
GIẢ ĐỊNH CHƯA GHI RÕ TRƯỚC ĐÂY, nay ghi lại: hàm này giả định bản sinh của Gemini
|
|
983
|
+
CHỪA MỘT LỀ NỀN quanh chủ thể — tức là vành `border` px ngoài cùng thực sự là nền,
|
|
984
|
+
không phải một phần của chủ thể. Prompt gửi Gemini BẮT BUỘC phải yêu cầu điều này
|
|
985
|
+
("chủ thể không được chạm mép ảnh"). Nếu Gemini vẫn vẽ chủ thể tràn sát mép canvas
|
|
986
|
+
(không chừa lề key nào) — có thật, asset `cell_base.png` của game có bbox phủ kín
|
|
987
|
+
cả canvas 249x249, nên một model được yêu cầu restyle nó hoàn toàn có thể vẽ tràn
|
|
988
|
+
mép — thì vành ngoài sẽ chứa pixel CHỦ THỂ chứ không phải nền, và hàm này sẽ báo
|
|
989
|
+
`bad_background` dù nền thật ra ổn (hoặc dù không hề có "nền" nào cả).
|
|
990
|
+
|
|
991
|
+
Đây là đánh đổi CÓ CHỦ ĐÍCH, không sửa: false positive (báo hỏng nhầm) là lỗi AN
|
|
992
|
+
TOÀN — cái giá là một lần sinh lại tốn kém vài giây, và asset lỗi không bao giờ lọt
|
|
993
|
+
được vào game. False negative (không báo hỏng) mới là thứ nguy hiểm — asset bị bóng
|
|
994
|
+
ma/nền lỗi sẽ lọt thẳng vào game. Nên giữa hai loại sai, hàm này LUÔN chọn nghiêng
|
|
995
|
+
về false positive. Xem test_border_impurity_false_positive_when_subject_touches_edge
|
|
996
|
+
để thấy ca này được đo và chấp nhận, không phải một bug chờ sửa.
|
|
997
|
+
"""
|
|
998
|
+
rgb = np.asarray(img.convert("RGB"), dtype=np.uint8)
|
|
999
|
+
h, w = rgb.shape[:2]
|
|
1000
|
+
b = max(1, min(border, h // 2, w // 2))
|
|
1001
|
+
|
|
1002
|
+
ring = np.concatenate([
|
|
1003
|
+
rgb[:b, :, :].reshape(-1, 3),
|
|
1004
|
+
rgb[-b:, :, :].reshape(-1, 3),
|
|
1005
|
+
rgb[b:-b, :b, :].reshape(-1, 3),
|
|
1006
|
+
rgb[b:-b, -b:, :].reshape(-1, 3),
|
|
1007
|
+
])
|
|
1008
|
+
return float(_dist_map(ring.reshape(1, -1, 3), hex_to_rgb(key_hex)).mean())
|
|
1009
|
+
|
|
1010
|
+
|
|
1011
|
+
def sample_border_color(img: Image.Image, border: int = BORDER_PX) -> tuple[str, float]:
|
|
1012
|
+
"""Màu nền THẬT của bản sinh, đo từ vành ngoài `border`px, kèm độ thuần của vành.
|
|
1013
|
+
|
|
1014
|
+
Trả (hex, purity). purity = tỉ lệ pixel vành nằm trong BAD_BORDER_DIST quanh màu
|
|
1015
|
+
trung bình — purity cao nghĩa là vành thực sự là MỘT màu nền phẳng, và hex đó chính
|
|
1016
|
+
là màu key hiệu dụng để chroma_key.
|
|
1017
|
+
|
|
1018
|
+
Lý do tồn tại (đo thật): model có thể vẽ nền "xanh studio" (#2ED246, vignette nhẹ)
|
|
1019
|
+
thay vì #00FF00 được yêu cầu — chroma_key theo key yêu cầu khi đó trượt CẢ khung
|
|
1020
|
+
(coverage=1.0, bad_background) dù bản sinh hoàn toàn dùng được. Dò màu nền thật rồi
|
|
1021
|
+
key lại cứu được bản đó mà không tốn lượt sinh nào. Cùng giả định lề với
|
|
1022
|
+
border_impurity: vành ngoài phải là nền (prompt đã ép chủ thể chừa lề)."""
|
|
1023
|
+
rgb = np.asarray(img.convert("RGB"), dtype=np.float32)
|
|
1024
|
+
h, w = rgb.shape[:2]
|
|
1025
|
+
b = max(1, min(border, h // 2, w // 2))
|
|
1026
|
+
ring = np.concatenate([
|
|
1027
|
+
rgb[:b, :, :].reshape(-1, 3),
|
|
1028
|
+
rgb[-b:, :, :].reshape(-1, 3),
|
|
1029
|
+
rgb[b:-b, :b, :].reshape(-1, 3),
|
|
1030
|
+
rgb[b:-b, -b:, :].reshape(-1, 3),
|
|
1031
|
+
])
|
|
1032
|
+
mean = ring.mean(axis=0)
|
|
1033
|
+
d = np.sqrt(((ring - mean) ** 2).sum(axis=1))
|
|
1034
|
+
purity = float((d <= BAD_BORDER_DIST).mean())
|
|
1035
|
+
return rgb_to_hex(tuple(int(round(float(x))) for x in mean)), purity
|
|
1036
|
+
|
|
1037
|
+
|
|
1038
|
+
# Ngưỡng cho lớp dọn nền SÓT (strip_bg_residue / bg_residue). Đo theo redmean, so với màu
|
|
1039
|
+
# nền ĐO ĐƯỢC (sample_border_color) — KHÔNG phải màu key yêu cầu. Vì sao: model gần như không
|
|
1040
|
+
# bao giờ vẽ đúng #00FF00; đo thật trên ảnh sinh ra là #0CF508 và #0BD009 (G=209!). Key theo màu
|
|
1041
|
+
# YÊU CẦU để lại tới 100% nền dưới dạng màng bán trong suốt, key theo màu ĐO ĐƯỢC thì sạch 0 px.
|
|
1042
|
+
BG_RESIDUE_TOL = 48.0 # sát màu nền đo được thì mới coi là nền sót (chặt, tránh gặm vào art)
|
|
1043
|
+
BAD_BG_RESIDUE = 0.02 # còn >2% pixel đục mang màu nền -> cờ bad_background
|
|
1044
|
+
|
|
1045
|
+
|
|
1046
|
+
def bg_residue(img: Image.Image, bg_hex: str, tol: float = BG_RESIDUE_TOL) -> float:
|
|
1047
|
+
"""Tỉ lệ pixel ĐỤC vẫn đang mang màu nền — thước đo "tách nền còn bẩn không".
|
|
1048
|
+
|
|
1049
|
+
Thay cho border_impurity trên đường BEN2: border_impurity đo VÀNH ẢNH GỐC theo màu key,
|
|
1050
|
+
nên khi BEN2 chạy nó vô nghĩa và trước đây bị ép cứng `0.0` — tức KHÔNG cờ nào báo được
|
|
1051
|
+
nền còn sót, lỗi cứ thế đi thẳng vào asset. Hàm này đo trên ẢNH ĐÃ TÁCH nên đúng thứ cần biết.
|
|
1052
|
+
"""
|
|
1053
|
+
arr = np.asarray(img.convert("RGBA"))
|
|
1054
|
+
opaque = arr[..., 3] > ALPHA_FLOOR
|
|
1055
|
+
if not opaque.any():
|
|
1056
|
+
return 0.0
|
|
1057
|
+
d = _dist_map(arr[..., :3].astype(np.float64), hex_to_rgb(bg_hex))
|
|
1058
|
+
return float((d[opaque] <= tol).mean())
|
|
1059
|
+
|
|
1060
|
+
|
|
1061
|
+
def strip_bg_residue(
|
|
1062
|
+
img: Image.Image, bg_hex: str, tol: float = BG_RESIDUE_TOL
|
|
1063
|
+
) -> tuple[Image.Image, int]:
|
|
1064
|
+
"""Xoá màng/vệt nền còn sót sau tách nền, theo MÀU NỀN ĐO ĐƯỢC. Trả (ảnh, số px đã xoá).
|
|
1065
|
+
|
|
1066
|
+
Chỉ xoá vùng màu-nền **LIỀN MẠCH với nền đã trong suốt** (hoặc chạm vành ảnh). Ràng buộc
|
|
1067
|
+
liên thông này là phần quan trọng nhất: bản thử nghiệm xoá theo màu/hue KHÔNG kèm liên
|
|
1068
|
+
thông đã gặm mép thành rỗ như mối ăn (đo thật trên ảnh bánh răng) vì nó đục cả những pixel
|
|
1069
|
+
art tình cờ trùng màu. Có liên thông thì art nằm trong lòng chủ thể không bao giờ bị chạm —
|
|
1070
|
+
muốn bị xoá, một vùng phải nối được ra tới nền thật.
|
|
1071
|
+
|
|
1072
|
+
KHÔNG cứu được ca model dùng chính hue của key làm MÀU NÉT trong tranh (vd viền xanh đậm
|
|
1073
|
+
quanh bánh răng): thứ đó là art thật, nằm kẹp giữa hai lớp art khác nên không liền mạch
|
|
1074
|
+
với nền, và về nguyên tắc không phân biệt được bằng màu. Ca đó phải chặn từ prompt —
|
|
1075
|
+
xem reference/prompt-template.md § Đừng để model dùng màu key làm màu nét.
|
|
1076
|
+
"""
|
|
1077
|
+
arr = np.array(img.convert("RGBA"))
|
|
1078
|
+
alpha = arr[..., 3]
|
|
1079
|
+
d = _dist_map(arr[..., :3].astype(np.float64), hex_to_rgb(bg_hex))
|
|
1080
|
+
cand = d <= tol
|
|
1081
|
+
clear = alpha <= ALPHA_FLOOR
|
|
1082
|
+
if not cand.any():
|
|
1083
|
+
return img, 0
|
|
1084
|
+
|
|
1085
|
+
# Vùng để dò liên thông = pixel màu-nền CỘNG pixel đã trong suốt. Phải gộp cả pixel trong
|
|
1086
|
+
# suốt vào: nếu bộ tách nền xoá luôn RGB về 0 thì nền đã xoá không còn "màu nền" nữa và
|
|
1087
|
+
# mọi vệt sót sẽ mất đường nối ra ngoài.
|
|
1088
|
+
region = cand | clear
|
|
1089
|
+
rows, uf, _ = _components(region)
|
|
1090
|
+
find = uf["find"]
|
|
1091
|
+
|
|
1092
|
+
h, w = alpha.shape
|
|
1093
|
+
doomed: set[int] = set()
|
|
1094
|
+
for y, runs in enumerate(rows):
|
|
1095
|
+
edge_row = y == 0 or y == h - 1
|
|
1096
|
+
for s, e, lbl in runs:
|
|
1097
|
+
# Mầm: chạm nền đã trong suốt, hoặc chạm vành ảnh (theo hợp đồng prompt lề rộng,
|
|
1098
|
+
# vành ngoài là nền — cùng giả định của sample_border_color/border_impurity).
|
|
1099
|
+
if clear[y, s:e].any() or edge_row or s == 0 or e >= w:
|
|
1100
|
+
doomed.add(find(lbl))
|
|
1101
|
+
if not doomed:
|
|
1102
|
+
return img, 0
|
|
1103
|
+
|
|
1104
|
+
killed = 0
|
|
1105
|
+
for y, runs in enumerate(rows):
|
|
1106
|
+
for s, e, lbl in runs:
|
|
1107
|
+
if find(lbl) not in doomed:
|
|
1108
|
+
continue
|
|
1109
|
+
seg_a = arr[y, s:e, 3]
|
|
1110
|
+
seg_c = cand[y, s:e]
|
|
1111
|
+
hit = seg_c & (seg_a > ALPHA_FLOOR) # chỉ chạm pixel MÀU NỀN còn đang đục
|
|
1112
|
+
if hit.any():
|
|
1113
|
+
killed += int(hit.sum())
|
|
1114
|
+
seg_a[hit] = 0
|
|
1115
|
+
if not killed:
|
|
1116
|
+
return img, 0
|
|
1117
|
+
return Image.fromarray(arr, "RGBA"), killed
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
def _erode_alpha(img: Image.Image, px: int = 1) -> Image.Image:
|
|
1121
|
+
"""Bào rìa alpha `px` pixel bằng min-filter — cắt vành anti-alias/viền key còn
|
|
1122
|
+
sót quanh silhouette mà KHÔNG đổi màu pixel nào (chỉ hạ alpha ở mép). RGB giữ
|
|
1123
|
+
nguyên; chỉ kênh A co lại. `px<=0` -> trả nguyên ảnh."""
|
|
1124
|
+
if px <= 0:
|
|
1125
|
+
return img.convert("RGBA")
|
|
1126
|
+
rgba = img.convert("RGBA")
|
|
1127
|
+
a = rgba.getchannel("A")
|
|
1128
|
+
for _ in range(px):
|
|
1129
|
+
a = a.filter(ImageFilter.MinFilter(3))
|
|
1130
|
+
rgba.putalpha(a)
|
|
1131
|
+
return rgba
|
|
1132
|
+
|
|
1133
|
+
|
|
1134
|
+
# ══════════════════════════════════════════════════════════════════════════════════════════
|
|
1135
|
+
# Tách nền ĐA PHƯƠNG PHÁP: chạy nhiều cách, tự chấm điểm, lấy bản tốt nhất
|
|
1136
|
+
# ══════════════════════════════════════════════════════════════════════════════════════════
|
|
1137
|
+
# VÌ SAO không có MỘT phương pháp thắng mọi ảnh (đo thật, 10 cách trên 4 bản sinh):
|
|
1138
|
+
# • BEN2 hạng nhất trên asset rời có mép lông/tơ, nhưng trên ảnh NỀN full-bleed (thảm cỏ
|
|
1139
|
+
# tràn hết khung, không có "vật thể nổi bật") nó ăn mất 8.99% chủ thể và làm mép nhoè
|
|
1140
|
+
# thành dải 77.425 px — trong khi các cách theo màu chỉ ~1.500 px.
|
|
1141
|
+
# • chroma_key theo màu key YÊU CẦU để lại viền hồng nhìn thấy rõ, vì model vẽ #FB0EE0
|
|
1142
|
+
# chứ không phải #FF00FF được yêu cầu, và khoảng cách trung vị 57.7 chỉ còn 12 đơn vị
|
|
1143
|
+
# biên an toàn dưới t_low=70.
|
|
1144
|
+
# • Bề rộng dải anti-alias KHÁC NHAU theo nguồn: đo được 2px trên bản GPT nhưng 7–8px trên
|
|
1145
|
+
# bản Gemini (chân trời vẽ nhoè). Một hằng số cố định thì hoặc sót viền hoặc gặm chủ thể.
|
|
1146
|
+
# Nên cách đúng là ĐO rồi CHỌN, không phải chọn trước rồi tin.
|
|
1147
|
+
|
|
1148
|
+
CUTOUT_BAND_MAX = 12 # trần nới dải AA — trên mức này gần như chắc là đo sai
|
|
1149
|
+
CUTOUT_LOOSE_TOL = 60.0 # ngưỡng "coi như màu nền" khi dựng mask thô (redmean)
|
|
1150
|
+
CUTOUT_BAD_FG_LOSS = 0.02 # ăn >2% lòng chủ thể -> loại thẳng, không cần so điểm
|
|
1151
|
+
CUTOUT_MIN_BORDER_SHARE = 0.10 # mode viền phải chiếm >=10% vành mới tin là màu nền
|
|
1152
|
+
|
|
1153
|
+
|
|
1154
|
+
def modal_border_color(
|
|
1155
|
+
img: Image.Image, border: int = BORDER_PX, quant: int = 8,
|
|
1156
|
+
key_hex: str | None = None, max_dist: float = MIN_KEY_MARGIN,
|
|
1157
|
+
) -> tuple[str, float]:
|
|
1158
|
+
"""Màu nền trên vành ảnh theo MODE, kèm tỉ lệ vành mà nó chiếm. Trả (hex, share).
|
|
1159
|
+
|
|
1160
|
+
Khác `sample_border_color` ở chỗ dùng mode chứ không phải mean, và ĐÓ LÀ LÝ DO nó tồn
|
|
1161
|
+
tại: với ảnh NỀN full-bleed (nửa trên là trời màu key, nửa dưới là thảm cỏ tràn ra tận
|
|
1162
|
+
vành), mean trộn hai nửa thành bùn tím — đo thật trên home_grass/gpt1.png:
|
|
1163
|
+
sample_border_color trả `#AC4978` với purity **0.0002**, tức đường auto-key trong MCP
|
|
1164
|
+
KHÔNG BAO GIỜ kích hoạt và cả pipeline rơi về màu key yêu cầu. Mode trả `#FB0EE0`, đúng
|
|
1165
|
+
màu model đã vẽ.
|
|
1166
|
+
|
|
1167
|
+
`key_hex` (NÊN LUÔN TRUYỀN): chỉ xét pixel vành nằm trong `max_dist` quanh màu key YÊU
|
|
1168
|
+
CẦU, rồi lấy mode trong số đó. Không có nó thì mode toàn cục CHỈ ĐÚNG NHỜ MAY: nó thắng
|
|
1169
|
+
trên ảnh thật vì nền key phẳng còn thảm cỏ có vân (không bin xanh nào áp đảo), nhưng
|
|
1170
|
+
trên ảnh mà chủ thể PHẲNG và chiếm nhiều vành hơn nền thì mode trả về màu CHỦ THỂ — và
|
|
1171
|
+
key theo màu chủ thể sẽ xoá mất chính chủ thể. Ca này bắt được bằng test
|
|
1172
|
+
(`_plate` có nền xanh phẳng: vành có 1312 px xanh so với 1168 px hồng, mode toàn cục
|
|
1173
|
+
chọn XANH). Lọc theo màu key yêu cầu thì hết phụ thuộc vào vân bề mặt.
|
|
1174
|
+
|
|
1175
|
+
`quant` gộp màu về lưới 8 mức để nhiễu số hoá không xé mode thành nghìn bin riêng lẻ.
|
|
1176
|
+
share thấp (< CUTOUT_MIN_BORDER_SHARE) nghĩa là vành không có mảng màu-key nào đáng kể
|
|
1177
|
+
-> đừng tin, quay về màu key yêu cầu.
|
|
1178
|
+
"""
|
|
1179
|
+
rgb = np.asarray(img.convert("RGB"), dtype=np.int32)
|
|
1180
|
+
h, w = rgb.shape[:2]
|
|
1181
|
+
b = max(1, min(border, h // 2, w // 2))
|
|
1182
|
+
ring = np.concatenate([
|
|
1183
|
+
rgb[:b, :, :].reshape(-1, 3),
|
|
1184
|
+
rgb[-b:, :, :].reshape(-1, 3),
|
|
1185
|
+
rgb[b:-b, :b, :].reshape(-1, 3),
|
|
1186
|
+
rgb[b:-b, -b:, :].reshape(-1, 3),
|
|
1187
|
+
])
|
|
1188
|
+
total = len(ring)
|
|
1189
|
+
if not total:
|
|
1190
|
+
return "#000000", 0.0
|
|
1191
|
+
|
|
1192
|
+
if key_hex is not None:
|
|
1193
|
+
near = _dist_map(ring.reshape(1, -1, 3).astype(np.uint8), hex_to_rgb(key_hex))
|
|
1194
|
+
ring = ring[near.reshape(-1) <= max_dist]
|
|
1195
|
+
if not len(ring):
|
|
1196
|
+
return key_hex, 0.0
|
|
1197
|
+
|
|
1198
|
+
q = ring // quant
|
|
1199
|
+
keys = (q[:, 0] << 16) | (q[:, 1] << 8) | q[:, 2]
|
|
1200
|
+
vals, cnt = np.unique(keys, return_counts=True)
|
|
1201
|
+
top = vals[int(cnt.argmax())]
|
|
1202
|
+
share = float(cnt.max()) / float(total) # so với TOÀN vành, không chỉ phần đã lọc
|
|
1203
|
+
mean = ring[keys == top].mean(axis=0)
|
|
1204
|
+
return rgb_to_hex(tuple(int(round(float(x))) for x in mean)), share
|
|
1205
|
+
|
|
1206
|
+
|
|
1207
|
+
def _dilate(mask: np.ndarray, px: int = 1) -> np.ndarray:
|
|
1208
|
+
"""Nới mask `px` pixel theo 4 hướng (numpy thuần — không có scipy trong môi trường này)."""
|
|
1209
|
+
out = mask
|
|
1210
|
+
for _ in range(max(0, px)):
|
|
1211
|
+
p = np.pad(out, 1, constant_values=False)
|
|
1212
|
+
out = out | p[:-2, 1:-1] | p[2:, 1:-1] | p[1:-1, :-2] | p[1:-1, 2:]
|
|
1213
|
+
return out
|
|
1214
|
+
|
|
1215
|
+
|
|
1216
|
+
def _frame_mask(mask: np.ndarray) -> np.ndarray:
|
|
1217
|
+
"""Chỉ giữ những vùng liền mạch của `mask` có CHẠM vành ảnh — tức nền thật.
|
|
1218
|
+
|
|
1219
|
+
Cùng ràng buộc liên thông với strip_bg_residue, và cùng một lý do: art nằm trong lòng
|
|
1220
|
+
chủ thể mà tình cờ trùng màu nền thì không nối được ra vành, nên không bao giờ bị xoá.
|
|
1221
|
+
"""
|
|
1222
|
+
if not mask.any():
|
|
1223
|
+
return np.zeros_like(mask)
|
|
1224
|
+
rows, uf, _ = _components(mask)
|
|
1225
|
+
find = uf["find"]
|
|
1226
|
+
h, w = mask.shape
|
|
1227
|
+
good: set[int] = set()
|
|
1228
|
+
for y, runs in enumerate(rows):
|
|
1229
|
+
for s, e, lbl in runs:
|
|
1230
|
+
if y == 0 or y == h - 1 or s == 0 or e == w:
|
|
1231
|
+
good.add(find(lbl))
|
|
1232
|
+
out = np.zeros_like(mask)
|
|
1233
|
+
for y, runs in enumerate(rows):
|
|
1234
|
+
for s, e, lbl in runs:
|
|
1235
|
+
if find(lbl) in good:
|
|
1236
|
+
out[y, s:e] = True
|
|
1237
|
+
return out
|
|
1238
|
+
|
|
1239
|
+
|
|
1240
|
+
def _cutout_regions(img: Image.Image, bg_hex: str) -> dict:
|
|
1241
|
+
"""Các vùng dùng để CHẤM ĐIỂM, dựng từ ảnh gốc — KHÔNG từ đầu ra của cách nào cả.
|
|
1242
|
+
|
|
1243
|
+
Mọi phương pháp phải được đo trên cùng một bộ vùng, nếu không thì so điểm là vô nghĩa.
|
|
1244
|
+
Trả dict: hard (nền chắc chắn), band (dải AA đã đo bề rộng), far (lòng chủ thể), band_w.
|
|
1245
|
+
"""
|
|
1246
|
+
u8 = np.asarray(img.convert("RGB"))
|
|
1247
|
+
bg = hex_to_rgb(bg_hex)
|
|
1248
|
+
d = _dist_map(u8, bg)
|
|
1249
|
+
hard = _frame_mask(d <= CUTOUT_LOOSE_TOL)
|
|
1250
|
+
|
|
1251
|
+
out = {"hard": hard, "d": d, "band_w": 0,
|
|
1252
|
+
"band": np.zeros_like(hard), "far": ~hard, "d_fg": float(np.percentile(d, 95))}
|
|
1253
|
+
if not hard.any():
|
|
1254
|
+
return out
|
|
1255
|
+
|
|
1256
|
+
outer = ~_dilate(hard, CUTOUT_BAND_MAX + 4)
|
|
1257
|
+
out["d_fg"] = float(np.median(d[outer])) if outer.any() else float(np.percentile(d, 95))
|
|
1258
|
+
|
|
1259
|
+
# Bề rộng dải AA ĐO TỪ ẢNH, không phải hằng số: nới từng ring một, dừng khi ring đã
|
|
1260
|
+
# gần như tiền cảnh thuần. Đo thật: bản GPT dừng ở 2, bản Gemini đi tới 7–8.
|
|
1261
|
+
k, prev = 0, hard
|
|
1262
|
+
while k < CUTOUT_BAND_MAX:
|
|
1263
|
+
cur = _dilate(hard, k + 1)
|
|
1264
|
+
ring = cur & ~prev
|
|
1265
|
+
if not ring.any():
|
|
1266
|
+
break
|
|
1267
|
+
k += 1
|
|
1268
|
+
if float(np.median(d[ring])) >= 0.90 * out["d_fg"]:
|
|
1269
|
+
break # ring này gần như fg thuần -> đã lấy nó rồi thì dừng
|
|
1270
|
+
prev = cur
|
|
1271
|
+
out["band_w"] = max(1, k)
|
|
1272
|
+
out["band"] = _dilate(hard, out["band_w"]) & ~hard
|
|
1273
|
+
out["far"] = ~_dilate(hard, out["band_w"] + 3)
|
|
1274
|
+
return out
|
|
1275
|
+
|
|
1276
|
+
|
|
1277
|
+
def unmix_cutout(
|
|
1278
|
+
img: Image.Image, bg_hex: str, regions: dict | None = None
|
|
1279
|
+
) -> tuple[Image.Image, dict]:
|
|
1280
|
+
"""Tách nền bằng NGHỊCH ĐẢO ĐẠI SỐ phương trình ghép ảnh. Trả (RGBA, info).
|
|
1281
|
+
|
|
1282
|
+
despill là heuristic kẹp kênh. Nhưng ta ĐO ĐƯỢC màu nền, nên đảo thẳng được:
|
|
1283
|
+
|
|
1284
|
+
O = F·α + BG·(1−α) -> F = (O − BG·(1−α)) / α
|
|
1285
|
+
|
|
1286
|
+
α lấy từ ramp tuyến tính theo khoảng cách màu trên dải AA (bề rộng đo từ ảnh), 0 trong
|
|
1287
|
+
vùng nền liền-vành, 255 ngoài dải. Kiểm chứng bằng roundtrip (ghép kết quả trở lại lên
|
|
1288
|
+
màu nền rồi so với ảnh gốc): sai số trên dải mềm **0.228** so với 25.09 của region-grow
|
|
1289
|
+
+despill và 16.45 của chroma_key+despill. Nới rộng dải là AN TOÀN vì α tính từ khoảng
|
|
1290
|
+
cách màu thật — pixel đã là tiền cảnh thuần thì tự nhận α=255, nằm trong dải cũng không
|
|
1291
|
+
bị hạ. Đo thật: cast (px còn ám màu nền khi ghép lên trắng/đen) = 0 trên cả 4 bản sinh.
|
|
1292
|
+
"""
|
|
1293
|
+
u8 = np.asarray(img.convert("RGB"))
|
|
1294
|
+
rgb = u8.astype(np.float64)
|
|
1295
|
+
r = regions if regions is not None else _cutout_regions(img, bg_hex)
|
|
1296
|
+
hard, band, d = r["hard"], r["band"], r["d"]
|
|
1297
|
+
bg = np.array(hex_to_rgb(bg_hex), dtype=np.float64)
|
|
1298
|
+
|
|
1299
|
+
info = {"band_w": r["band_w"], "bg_hex": bg_hex, "soft_px": 0,
|
|
1300
|
+
"clipped_px": 0, "hard_frac": float(hard.mean())}
|
|
1301
|
+
if not hard.any():
|
|
1302
|
+
# Không có vùng màu-nền nào chạm vành -> không có gì để xoá. Trả ảnh đục nguyên vẹn
|
|
1303
|
+
# thay vì đoán bừa; lớp chấm điểm sẽ thấy bg_residue cao và loại nó nếu có cách khác.
|
|
1304
|
+
return Image.fromarray(
|
|
1305
|
+
np.dstack([u8, np.full(u8.shape[:2], 255, np.uint8)]), "RGBA"), info
|
|
1306
|
+
|
|
1307
|
+
base = float(np.percentile(d[hard], 98))
|
|
1308
|
+
span = max(1e-6, r["d_fg"] - base)
|
|
1309
|
+
alpha = np.where(hard, 0.0, 255.0)
|
|
1310
|
+
if band.any():
|
|
1311
|
+
alpha[band] = np.clip((d[band] - base) / span, 0.0, 1.0) * 255.0
|
|
1312
|
+
|
|
1313
|
+
a = (alpha / 255.0)[..., None]
|
|
1314
|
+
F = rgb.copy()
|
|
1315
|
+
solve = band & (alpha > ALPHA_FLOOR) & (alpha < 255.0)
|
|
1316
|
+
if solve.any():
|
|
1317
|
+
with np.errstate(divide="ignore", invalid="ignore"):
|
|
1318
|
+
cand = (rgb - bg[None, None, :] * (1.0 - a)) / np.maximum(a, 1e-6)
|
|
1319
|
+
info["clipped_px"] = int(((cand < -10) | (cand > 265)).any(axis=2)[solve].sum())
|
|
1320
|
+
F[solve] = np.clip(cand[solve], 0.0, 255.0)
|
|
1321
|
+
info["soft_px"] = int(solve.sum())
|
|
1322
|
+
|
|
1323
|
+
out = np.dstack([F, alpha])
|
|
1324
|
+
return Image.fromarray(np.rint(out).clip(0, 255).astype(np.uint8), "RGBA"), info
|
|
1325
|
+
|
|
1326
|
+
|
|
1327
|
+
def score_cutout(src: Image.Image, out: Image.Image, bg_hex: str,
|
|
1328
|
+
regions: dict | None = None) -> dict:
|
|
1329
|
+
"""Chấm điểm một bản tách nền — KHÔNG cần ảnh tham chiếu. Điểm THẤP là tốt.
|
|
1330
|
+
|
|
1331
|
+
Bốn thành phần, mỗi cái bắt một kiểu hỏng khác nhau; thiếu bất kỳ cái nào là mở đường
|
|
1332
|
+
cho một cách dở thắng điểm:
|
|
1333
|
+
• cast — số px còn ÁM MÀU NỀN khi ghép lên trắng/đen. Bắt "viền hồng/xanh còn
|
|
1334
|
+
sót". Đo trên dải AA lấy từ ẢNH GỐC nên mọi cách chịu cùng một thước.
|
|
1335
|
+
• f_err — màu tiền cảnh lệch bao nhiêu so với nghịch đảo đại số. Bắt "giữ nguyên
|
|
1336
|
+
màu đã bị nền trộn vào". Một mình nó KHÔNG đủ: cách nào để alpha=255
|
|
1337
|
+
khắp dải sẽ có f_err=0 hiển nhiên (vì khi α=1 thì F_ideal ≡ O) — chính
|
|
1338
|
+
`cast` mới bắt được ca đó.
|
|
1339
|
+
• fg_loss — % lòng chủ thể (vùng `far`) bị làm không-đục. Bắt "ăn mất chủ thể":
|
|
1340
|
+
BEN2 trên ảnh nền full-bleed đo được 8.99%.
|
|
1341
|
+
• bg_residue— % px đục vẫn mang màu nền. Bắt "để nguyên nền".
|
|
1342
|
+
Giả định: chủ thể ĐỤC ở trong lòng (đúng với asset game trên phông key phẳng). Chủ thể
|
|
1343
|
+
trong suốt thật (kính, khói) sẽ bị fg_loss phạt oan — khi đó dùng multi=False.
|
|
1344
|
+
"""
|
|
1345
|
+
r = regions if regions is not None else _cutout_regions(src, bg_hex)
|
|
1346
|
+
O = np.asarray(src.convert("RGB"), dtype=np.float64)
|
|
1347
|
+
arr = np.asarray(out.convert("RGBA"), dtype=np.float64)
|
|
1348
|
+
al, F = arr[..., 3], arr[..., :3]
|
|
1349
|
+
bg = np.array(hex_to_rgb(bg_hex), dtype=np.float64)
|
|
1350
|
+
|
|
1351
|
+
m: dict = {}
|
|
1352
|
+
far, band = r["far"], r["band"]
|
|
1353
|
+
|
|
1354
|
+
# `far` (lòng chủ thể) chỉ có nghĩa khi TÌM ĐƯỢC vùng màu nền. Nếu không có pixel nào
|
|
1355
|
+
# mang màu key chạm vành — ảnh chụp/tranh nền thật, không có phông key — thì `hard` rỗng,
|
|
1356
|
+
# `far` biến thành TOÀN ẢNH, và fg_loss quay ra đo "bao nhiêu phần ảnh không đục", tức
|
|
1357
|
+
# PHẠT ĐÚNG cách đã tách nền thành công. Bug thật đã gặp: BEN2 tách đúng chủ thể
|
|
1358
|
+
# (coverage 0.156 so với diện tích thật 0.181) nhưng bị loại với fg_loss 0.8484, và bản
|
|
1359
|
+
# KHÔNG LÀM GÌ CẢ thắng, giao ra ảnh nguyên vẹn. Khi không có phông key thì bỏ hẳn tiêu
|
|
1360
|
+
# chí này và để `coverage` làm cửa gác — nó loại đúng những cách phủ kín khung.
|
|
1361
|
+
fg_valid = bool(r["hard"].any()) and bool(far.any())
|
|
1362
|
+
m["fg_loss"] = float((al[far] < 247).mean()) if fg_valid else 0.0
|
|
1363
|
+
m["fg_loss_valid"] = fg_valid
|
|
1364
|
+
m["bg_residue"] = bg_residue(out, bg_hex)
|
|
1365
|
+
m["coverage"] = float((al > ALPHA_FLOOR).mean())
|
|
1366
|
+
m["soft_px"] = int(((al > ALPHA_FLOOR) & (al < 247)).sum())
|
|
1367
|
+
|
|
1368
|
+
# cast: hướng sắc của pixel đã ghép có trùng hướng sắc của màu nền không (không phụ
|
|
1369
|
+
# thuộc hue cụ thể -> dùng được cho cả key xanh lá và key hồng).
|
|
1370
|
+
vb = bg - bg.mean()
|
|
1371
|
+
nb = float(np.linalg.norm(vb))
|
|
1372
|
+
zone = band & (al > ALPHA_FLOOR)
|
|
1373
|
+
cast = 0
|
|
1374
|
+
if nb > 1e-6 and zone.any():
|
|
1375
|
+
for back in (255.0, 32.0):
|
|
1376
|
+
aa = (al[zone] / 255.0)[:, None]
|
|
1377
|
+
comp = F[zone] * aa + back * (1.0 - aa)
|
|
1378
|
+
u = comp - comp.mean(axis=1, keepdims=True)
|
|
1379
|
+
nu = np.linalg.norm(u, axis=1)
|
|
1380
|
+
cos = (u @ vb) / np.maximum(nu * nb, 1e-6)
|
|
1381
|
+
cast += int(((cos > 0.85) & (nu > 12.0)).sum())
|
|
1382
|
+
m["cast"] = cast
|
|
1383
|
+
|
|
1384
|
+
soft = band & (al > ALPHA_FLOOR) & (al < 247)
|
|
1385
|
+
if soft.sum() >= 50:
|
|
1386
|
+
aa = (al[soft] / 255.0)[:, None]
|
|
1387
|
+
ideal = (O[soft] - bg[None, :] * (1.0 - aa)) / np.maximum(aa, 1e-6)
|
|
1388
|
+
m["f_err"] = float(np.abs(F[soft] - np.clip(ideal, 0.0, 255.0)).mean())
|
|
1389
|
+
else:
|
|
1390
|
+
m["f_err"] = 0.0
|
|
1391
|
+
|
|
1392
|
+
aa = (al / 255.0)[..., None]
|
|
1393
|
+
m["roundtrip"] = float(np.abs(F * aa + bg[None, None, :] * (1.0 - aa) - O).mean())
|
|
1394
|
+
|
|
1395
|
+
# Loại thẳng (không so điểm) những kiểu hỏng mà không đánh đổi nào bù được.
|
|
1396
|
+
dq = []
|
|
1397
|
+
if m["fg_loss"] > CUTOUT_BAD_FG_LOSS:
|
|
1398
|
+
dq.append("an_chu_the")
|
|
1399
|
+
if m["bg_residue"] > BAD_BG_RESIDUE:
|
|
1400
|
+
dq.append("sot_nen")
|
|
1401
|
+
if m["coverage"] < BAD_LOW or m["coverage"] > BAD_HIGH:
|
|
1402
|
+
dq.append("phu_bat_thuong")
|
|
1403
|
+
m["disqualify"] = dq
|
|
1404
|
+
|
|
1405
|
+
band_n = max(1, int(band.sum()))
|
|
1406
|
+
m["score"] = (3.0 * cast / band_n
|
|
1407
|
+
+ 1.0 * m["f_err"] / 255.0
|
|
1408
|
+
+ 0.5 * m["roundtrip"] / 255.0
|
|
1409
|
+
+ 2.0 * m["fg_loss"]
|
|
1410
|
+
+ 2.0 * m["bg_residue"])
|
|
1411
|
+
return m
|
|
1412
|
+
|
|
1413
|
+
|
|
1414
|
+
def best_cutout(
|
|
1415
|
+
img: Image.Image, key_hex: str, bg_hex: str | None = None, cutout=None
|
|
1416
|
+
) -> tuple[Image.Image, dict]:
|
|
1417
|
+
"""Chạy NHIỀU cách tách nền, chấm điểm từng cách, trả bản điểm tốt nhất.
|
|
1418
|
+
|
|
1419
|
+
Trả (RGBA, report). report["cutout_method"] là tên cách đã thắng, report["cutout_table"]
|
|
1420
|
+
là bảng điểm ĐẦY ĐỦ của mọi cách (kể cả cách bị loại) để người duyệt thấy VÌ SAO nó thắng.
|
|
1421
|
+
|
|
1422
|
+
`bg_hex` bỏ trống -> tự đo bằng modal_border_color, nhưng chỉ nhận nếu vừa chiếm đủ
|
|
1423
|
+
vành vừa KHÔNG lệch màu key yêu cầu quá MIN_KEY_MARGIN. Cửa gác thứ hai đó là để chặn ca
|
|
1424
|
+
chủ thể tràn sát vành: khi đó màu đo được là màu CHỦ THỂ (vẫn "thuần" một màu!) và key
|
|
1425
|
+
theo nó sẽ xoá mất chính chủ thể. Ngưỡng 190 là ĐO ĐƯỢC, không phải đoán: độ lệch
|
|
1426
|
+
model-vẽ-so-với-yêu-cầu đo trên 6 bản sinh nằm trong 6.0–96.5, còn khoảng cách từ màu key
|
|
1427
|
+
tới màu chủ thể thật là 353–536. 190 nằm giữa với biên ~2× cả hai phía.
|
|
1428
|
+
"""
|
|
1429
|
+
# Lọc theo màu key yêu cầu NGAY trong modal_border_color: mode toàn cục có thể trả về
|
|
1430
|
+
# màu CHỦ THỂ khi chủ thể phẳng và chiếm nhiều vành hơn nền (xem docstring hàm đó).
|
|
1431
|
+
sampled, share = modal_border_color(img, key_hex=key_hex)
|
|
1432
|
+
chosen, why = bg_hex, "duoc_truyen_vao"
|
|
1433
|
+
if chosen is None:
|
|
1434
|
+
if share >= CUTOUT_MIN_BORDER_SHARE:
|
|
1435
|
+
chosen, why = sampled, "mode_vien"
|
|
1436
|
+
else:
|
|
1437
|
+
chosen, why = key_hex, "khong_thay_mau_key_o_vien"
|
|
1438
|
+
|
|
1439
|
+
regions = _cutout_regions(img, chosen)
|
|
1440
|
+
cands: list[tuple[str, Image.Image]] = []
|
|
1441
|
+
|
|
1442
|
+
unm, unm_info = unmix_cutout(img, chosen, regions)
|
|
1443
|
+
cands.append(("unmix", unm))
|
|
1444
|
+
cands.append(("chroma-bg", despill(chroma_key(img, chosen), chosen)))
|
|
1445
|
+
if color_dist(hex_to_rgb(chosen), hex_to_rgb(key_hex)) > 1.0:
|
|
1446
|
+
# Lưới an toàn: nếu màu đo được sai thì key theo màu YÊU CẦU vẫn còn đó để thắng.
|
|
1447
|
+
cands.append(("chroma-key", despill(chroma_key(img, key_hex), key_hex)))
|
|
1448
|
+
if cutout is not None:
|
|
1449
|
+
try:
|
|
1450
|
+
ben = cutout(img)
|
|
1451
|
+
if ben.mode != "RGBA":
|
|
1452
|
+
ben = ben.convert("RGBA")
|
|
1453
|
+
cands.append(("ben2", _erode_alpha(ben, 1)))
|
|
1454
|
+
except Exception as exc: # noqa: BLE001
|
|
1455
|
+
cands.append(("ben2-loi", None)) # ghi lại để báo cáo
|
|
1456
|
+
del exc
|
|
1457
|
+
|
|
1458
|
+
table = []
|
|
1459
|
+
ranked: list[tuple[float, str, Image.Image]] = []
|
|
1460
|
+
for name, im in cands:
|
|
1461
|
+
if im is None:
|
|
1462
|
+
table.append({"method": name, "error": True})
|
|
1463
|
+
continue
|
|
1464
|
+
s = score_cutout(img, im, chosen, regions)
|
|
1465
|
+
s["method"] = name
|
|
1466
|
+
table.append(s)
|
|
1467
|
+
if not s["disqualify"]:
|
|
1468
|
+
ranked.append((s["score"], name, im))
|
|
1469
|
+
|
|
1470
|
+
table.sort(key=lambda t: (bool(t.get("error")), bool(t.get("disqualify")), t.get("score", 9e9)))
|
|
1471
|
+
if ranked:
|
|
1472
|
+
ranked.sort(key=lambda t: t[0])
|
|
1473
|
+
win_score, win_name, win_img = ranked[0]
|
|
1474
|
+
else:
|
|
1475
|
+
# Mọi cách đều bị loại -> lấy cách điểm thấp nhất trong số không-lỗi và NÓI RA,
|
|
1476
|
+
# không âm thầm trả ảnh rỗng. Cờ bad_* của postprocess vẫn sẽ bắt tiếp.
|
|
1477
|
+
ok = [t for t in table if not t.get("error")]
|
|
1478
|
+
win_name = ok[0]["method"] if ok else "unmix"
|
|
1479
|
+
win_score = ok[0]["score"] if ok else float("nan")
|
|
1480
|
+
win_img = dict(cands)[win_name] if win_name in dict(cands) else unm
|
|
1481
|
+
|
|
1482
|
+
report = {
|
|
1483
|
+
"cutout_method": win_name,
|
|
1484
|
+
"cutout_score": win_score,
|
|
1485
|
+
"cutout_table": table,
|
|
1486
|
+
"cutout_candidates": [n for n, _ in cands],
|
|
1487
|
+
"bg_color_used": chosen,
|
|
1488
|
+
"bg_source": why,
|
|
1489
|
+
"bg_sampled_mode": sampled,
|
|
1490
|
+
"bg_mode_share": share,
|
|
1491
|
+
"band_w": regions["band_w"],
|
|
1492
|
+
"unmix_info": unm_info,
|
|
1493
|
+
"all_disqualified": not ranked,
|
|
1494
|
+
}
|
|
1495
|
+
return win_img, report
|
|
1496
|
+
|
|
1497
|
+
|
|
1498
|
+
def postprocess(
|
|
1499
|
+
gen: Image.Image,
|
|
1500
|
+
key_hex: str,
|
|
1501
|
+
src: Image.Image | None = None,
|
|
1502
|
+
size: tuple[int, int] | None = None,
|
|
1503
|
+
cutout=None,
|
|
1504
|
+
bg_hex: str | None = None,
|
|
1505
|
+
multi: bool = True,
|
|
1506
|
+
full_bleed: bool = False,
|
|
1507
|
+
upscale=None,
|
|
1508
|
+
) -> tuple[Image.Image | None, dict]:
|
|
1509
|
+
"""Ảnh Gemini thô -> asset game dùng được ngay.
|
|
1510
|
+
|
|
1511
|
+
Mặc định BÁM GỐC: canvas = size của src, nội dung căn vào đúng bbox của src
|
|
1512
|
+
-> thay 1-1 vào game, không phải chỉnh anchor/hitbox/layout.
|
|
1513
|
+
Truyền `size` để ép ra kích thước khác (asset hoàn toàn mới).
|
|
1514
|
+
|
|
1515
|
+
`cutout`: hàm (Image -> RGBA) để TÁCH NỀN. Truyền vào thì dùng nó (vd BEN2
|
|
1516
|
+
qua bgremove.remove_background) — segmentation ngữ nghĩa, không cần màu key,
|
|
1517
|
+
không viền xanh. BỎ TRỐNG -> quay về chroma-key + despill như cũ (đường mặc
|
|
1518
|
+
định, thuần, test offline được). `key_hex` vẫn cần cho despeckle/báo cáo.
|
|
1519
|
+
|
|
1520
|
+
`bg_hex`: màu nền THẬT đo từ bản sinh (sample_border_color). Truyền vào thì MỌI phép
|
|
1521
|
+
dựa-trên-màu dùng nó thay cho `key_hex`: chroma_key, despill và lớp dọn nền sót. Đây là
|
|
1522
|
+
thứ quyết định độ sạch — model gần như không bao giờ vẽ đúng màu key được yêu cầu (đo
|
|
1523
|
+
thật: yêu cầu #00FF00, model vẽ #0CF508 và #0BD009), và key theo màu yêu cầu có thể để
|
|
1524
|
+
lại NGUYÊN nền dưới dạng màng bán trong suốt. Bỏ trống -> dùng `key_hex` như cũ.
|
|
1525
|
+
|
|
1526
|
+
`multi` (MẶC ĐỊNH BẬT): chạy NHIỀU cách tách nền qua best_cutout() rồi lấy bản chấm
|
|
1527
|
+
điểm tốt nhất, thay vì tin sẵn một cách. Không có cách nào thắng mọi ảnh — đo thật 10
|
|
1528
|
+
cách trên 4 bản sinh: BEN2 hạng nhất ở asset rời nhưng ăn mất 8.99% chủ thể ở ảnh nền
|
|
1529
|
+
full-bleed; chroma_key theo màu key yêu cầu để lại viền hồng thấy rõ; bề rộng dải AA
|
|
1530
|
+
khác nhau 2px vs 7–8px tuỳ nguồn. `cutout` khi đó là MỘT ứng viên (không còn độc quyền).
|
|
1531
|
+
Tắt (`multi=False`) khi chủ thể trong suốt thật (kính, khói) — fg_loss sẽ phạt oan.
|
|
1532
|
+
|
|
1533
|
+
`full_bleed`: BẬT cho ảnh NỀN tràn viền (background plate, thumbnail) — tranh phủ kín
|
|
1534
|
+
khung, chủ thể CHẠM vành ảnh là đúng thiết kế. Nó tắt duy nhất cửa gác `border_impurity`,
|
|
1535
|
+
KHÔNG tắt `bg_residue`/`coverage`/bộ chấm điểm.
|
|
1536
|
+
|
|
1537
|
+
VÌ SAO phải là THAM SỐ chứ không tự suy ra: `border_impurity` giả định vành ảnh toàn là
|
|
1538
|
+
nền. Với ảnh nền full-bleed giả định đó sai (đo thật home_grass/gpt1.png: thảm cỏ tràn ra
|
|
1539
|
+
vành dưới và hai vành bên) nên nó báo bad_background trên một bản tách nền HOÀN HẢO —
|
|
1540
|
+
cast 0, bg_residue 0.0000 — và cả 4 bản sinh đều bị đánh hỏng. Đó không còn là "một lượt
|
|
1541
|
+
sinh lại tốn kém" như docstring border_impurity chấp nhận, mà là chặn vĩnh viễn cả một
|
|
1542
|
+
lớp asset. Nhưng KHÔNG suy ra được từ pixel: "thảm cỏ chạm vành dưới" và "mảng xám panel
|
|
1543
|
+
chưa key chạm vành trên" đều là nội dung đục chạm vành, chỉ khác Ý ĐỊNH. Tỉ lệ vành mang
|
|
1544
|
+
màu key cũng không tách được hai ca: đo tay 0.552 ở ca CẦN gác (test_postprocess_flags_
|
|
1545
|
+
partial_background_failure) so với 0.225–0.390 ở ảnh nền cần TẮT gác — quá gần nhau để
|
|
1546
|
+
đặt ngưỡng. Nên người gọi phải nói ra. Mặc định False giữ nguyên hành vi cũ.
|
|
1547
|
+
|
|
1548
|
+
`upscale`: hàm phóng 4x mảng HxWx3 uint8 (upscale.upscale_rgb). CHỈ dùng khi bản sinh
|
|
1549
|
+
NHỎ HƠN khung đích — lúc đó LANCZOS chỉ trải mép ra cho mềm chứ không thêm được gì.
|
|
1550
|
+
Cũng chạy nhiều cách rồi chấm điểm chọn (xem best_upscale). Bỏ trống -> LANCZOS như cũ.
|
|
1551
|
+
"""
|
|
1552
|
+
bg_for_color = bg_hex or key_hex
|
|
1553
|
+
multi_rep: dict | None = None
|
|
1554
|
+
diag: Image.Image | None = None
|
|
1555
|
+
if multi:
|
|
1556
|
+
keyed, multi_rep = best_cutout(gen, key_hex, bg_hex=bg_hex, cutout=cutout)
|
|
1557
|
+
bg_for_color = multi_rep["bg_color_used"]
|
|
1558
|
+
# Ảnh CHẨN ĐOÁN, tách khỏi ảnh GIAO RA. Đây là điểm dễ hiểu sai nhất của khối này.
|
|
1559
|
+
#
|
|
1560
|
+
# Toàn bộ lớp cờ của postprocess đọc alpha theo NGỮ NGHĨA RAMP MỀM của chroma_key:
|
|
1561
|
+
# "có nội dung nhưng không đục" (ALPHA_FLOOR < a < SOLID_ALPHA) chính là cách nó
|
|
1562
|
+
# phát hiện chủ thể bóng ma, lỗ khoét giữa thân, và mảng nền chưa key. `unmix` lại
|
|
1563
|
+
# cố ý cho alpha GẦN NHƯ NHỊ PHÂN — đúng cái làm nó sạch — nên nếu đo cờ trên đầu ra
|
|
1564
|
+
# của nó thì mọi tín hiệu đó biến mất: đo thật, 4 test defect (viên ngọc giữa thân,
|
|
1565
|
+
# bóng ma, mảng xám chưa key, hai-lỗi-cùng-lúc) đều tụt xuống status "ok".
|
|
1566
|
+
#
|
|
1567
|
+
# Nhưng các chỉ số đó trả lời câu "BẢN SINH có dùng được không?", không phải câu
|
|
1568
|
+
# "thuật toán tách nền nào sạch hơn?" — chúng độc lập với việc sau đó ta cắt bằng gì.
|
|
1569
|
+
# Nên: cờ đo trên bản chroma_key (giữ nguyên hợp đồng đời cũ), pixel giao ra lấy từ
|
|
1570
|
+
# bản thắng điểm. `bg_residue` thì vẫn đo trên bản GIAO RA, vì nó hỏi đúng câu
|
|
1571
|
+
# "ảnh mình sắp ghi ra có còn bẩn không".
|
|
1572
|
+
diag = chroma_key(gen, bg_for_color)
|
|
1573
|
+
impurity = None
|
|
1574
|
+
elif cutout is not None:
|
|
1575
|
+
# Đường BEN2: alpha đã là segmentation của chủ thể, không dính màu nền
|
|
1576
|
+
# -> border_impurity (đo theo màu key) VÔ NGHĨA ở đây, ép 0 để không báo
|
|
1577
|
+
# nhầm bad_background. Coverage>BAD_HIGH vẫn là cửa gác thật (segmentation
|
|
1578
|
+
# trượt, phủ nguyên khung -> đục hết).
|
|
1579
|
+
keyed = cutout(gen)
|
|
1580
|
+
if keyed.mode != "RGBA":
|
|
1581
|
+
keyed = keyed.convert("RGBA")
|
|
1582
|
+
# BEN2 đôi khi gộp cả dải anti-alias (nơi Gemini blend chủ thể với nền
|
|
1583
|
+
# key) vào mask ĐỤC -> viền xanh mảnh bám theo silhouette, rõ nhất trên chủ
|
|
1584
|
+
# thể sáng/viền trắng như logo. despill CHỈ chạm pixel bán trong suốt nên
|
|
1585
|
+
# KHÔNG dọn được dải đục này. Bào 1px alpha (min-filter) cắt đúng vành ngoài
|
|
1586
|
+
# đó — an toàn màu (không declamp xanh nên hoa xanh/teal không hề hấn), chỉ
|
|
1587
|
+
# thu silhouette 1px. Sau đó despill dọn nốt mép mềm mới lộ ra.
|
|
1588
|
+
keyed = _erode_alpha(keyed, 1)
|
|
1589
|
+
impurity = 0.0
|
|
1590
|
+
else:
|
|
1591
|
+
keyed = chroma_key(gen, bg_for_color)
|
|
1592
|
+
impurity = None # tính bằng border_impurity bên dưới
|
|
1593
|
+
|
|
1594
|
+
# Dọn nền SÓT rồi ĐO độ bẩn — cả hai việc phải làm TRƯỚC despill. despill kéo kênh màu key
|
|
1595
|
+
# xuống, nên sau nó nền sót không còn mang màu nền nữa và mọi phép so-màu đều mù: đo thật
|
|
1596
|
+
# trên ca nền sống sót 100% khung, chạy sau despill thì bg_residue báo 0.0000 và lớp dọn xoá
|
|
1597
|
+
# được 0 px. Đây cũng là lý do despill KHÔNG còn nằm chung dòng với chroma_key/cutout ở trên.
|
|
1598
|
+
keyed, bg_killed = strip_bg_residue(keyed, bg_for_color)
|
|
1599
|
+
residue = bg_residue(keyed, bg_for_color)
|
|
1600
|
+
if not multi:
|
|
1601
|
+
keyed = despill(keyed, bg_for_color)
|
|
1602
|
+
# Ở đường multi TUYỆT ĐỐI KHÔNG despill lần nữa. Hai lý do, cả hai đều làm ảnh XẤU ĐI:
|
|
1603
|
+
# 1. Ứng viên nào CẦN despill thì đã được despill ngay trong best_cutout() rồi
|
|
1604
|
+
# (`chroma-bg`, `chroma-key`) — chạy lần hai là kẹp kênh hai lần.
|
|
1605
|
+
# 2. Với bản `unmix` thắng, F đã là NGHIỆM CHÍNH XÁC của phương trình ghép ảnh.
|
|
1606
|
+
# despill chỉ chạm pixel alpha<255 — tức đúng dải AA mà unmix vừa giải xong — nên
|
|
1607
|
+
# nó sẽ kéo nghiệm đúng lệch trở lại về phía heuristic kẹp kênh. Đo thật: sai màu
|
|
1608
|
+
# tiền cảnh 0.44/255 sau unmix, chạy thêm despill thì vọt lên hàng chục.
|
|
1609
|
+
|
|
1610
|
+
# Despeckle TRƯỚC khi đo bất cứ chỉ số nào: watermark ✦ của Gemini là một mảng ĐỤC HOÀN
|
|
1611
|
+
# TOÀN ~580px ở góc dưới-phải mọi ảnh nó sinh. Để nguyên thì nó vừa hiện thành chấm xanh
|
|
1612
|
+
# trong asset, vừa làm lệch coverage/solid_ratio/bbox/hole_ratio. Đo sau khi dọn mới đúng.
|
|
1613
|
+
keyed, speckles = despeckle(keyed)
|
|
1614
|
+
keyed, wm = strip_gemini_watermark(keyed) # diệt hẳn dấu ✦ ở góc dưới-phải nếu despeckle sót
|
|
1615
|
+
speckles += wm
|
|
1616
|
+
|
|
1617
|
+
if impurity is None:
|
|
1618
|
+
impurity = border_impurity(gen, bg_for_color)
|
|
1619
|
+
|
|
1620
|
+
if diag is not None:
|
|
1621
|
+
# Cùng chuỗi dọn dẹp như bản giao ra, để cờ đo trên ảnh tương đương — chỉ khác là
|
|
1622
|
+
# bản này CÓ despill (đúng ngữ nghĩa đời cũ mà lớp cờ được thiết kế quanh nó).
|
|
1623
|
+
diag, _ = strip_bg_residue(diag, bg_for_color)
|
|
1624
|
+
diag = despill(diag, bg_for_color)
|
|
1625
|
+
diag, _ = despeckle(diag)
|
|
1626
|
+
diag, _ = strip_gemini_watermark(diag)
|
|
1627
|
+
metric_img = diag
|
|
1628
|
+
else:
|
|
1629
|
+
metric_img = keyed
|
|
1630
|
+
|
|
1631
|
+
coverage = alpha_coverage(metric_img)
|
|
1632
|
+
solid = solid_ratio(metric_img)
|
|
1633
|
+
bbox = content_bbox(metric_img)
|
|
1634
|
+
holes = hole_ratio(metric_img, bbox)
|
|
1635
|
+
|
|
1636
|
+
# bbox dùng để CẮT ảnh giao ra thì phải lấy từ chính ảnh giao ra, không phải từ bản
|
|
1637
|
+
# chẩn đoán: hai bản có silhouette khác nhau (unmix giữ lại vùng mà chroma_key khoét
|
|
1638
|
+
# thủng), cắt theo bbox của bản kia sẽ lệch khung.
|
|
1639
|
+
out_bbox = content_bbox(keyed) if diag is not None else bbox
|
|
1640
|
+
|
|
1641
|
+
flags: list[str] = []
|
|
1642
|
+
# `residue` là cửa gác THẬT cho đường BEN2 (ở đó impurity bị ép 0 vì đo theo vành ảnh gốc
|
|
1643
|
+
# là vô nghĩa). Đo trên ảnh ĐÃ tách nên bắt được đúng thứ trước đây trôi lọt.
|
|
1644
|
+
impurity_gates = not full_bleed # xem docstring `full_bleed`
|
|
1645
|
+
if ((impurity_gates and impurity > BAD_BORDER_DIST)
|
|
1646
|
+
or coverage > BAD_HIGH or residue > BAD_BG_RESIDUE):
|
|
1647
|
+
flags.append("bad_background")
|
|
1648
|
+
# Ở đường multi, bộ chấm điểm là một cửa gác ĐỘC LẬP: mọi ứng viên đều bị loại nghĩa là
|
|
1649
|
+
# KHÔNG cách nào tách sạch được bản sinh này (nền sót, hoặc ăn chủ thể, hoặc phủ bất
|
|
1650
|
+
# thường) -> đó là lỗi của BẢN SINH, cần sinh lại, chứ không phải chọn bản đỡ tệ nhất
|
|
1651
|
+
# rồi im lặng đi tiếp.
|
|
1652
|
+
elif multi_rep is not None and multi_rep.get("all_disqualified"):
|
|
1653
|
+
flags.append("bad_background")
|
|
1654
|
+
|
|
1655
|
+
# Finding I3: coverage thấp MỘT MÌNH không còn nghĩa là "hỏng" — icon/line-art nhỏ
|
|
1656
|
+
# (vd nút X 12px nét) đo được coverage=0.0161 < BAD_LOW nhưng solid_ratio=1.0, một
|
|
1657
|
+
# bản sinh HOÀN HẢO bị bản cũ vứt bỏ (trả None) vì "nhỏ" bị đánh đồng với "hỏng", và
|
|
1658
|
+
# retry đổi màu key không thể cứu ca này vì màu key chưa từng là vấn đề. bad_subject
|
|
1659
|
+
# giờ chỉ bật khi chủ thể THẬT SỰ bị phá huỷ: bbox rỗng hoàn toàn, HOẶC vừa nhỏ vừa
|
|
1660
|
+
# không đục (ăn thủng thành bóng ma mờ). "Nhỏ nhưng đặc" không phải lỗi.
|
|
1661
|
+
bad_subject = bbox is None or (coverage < BAD_LOW and solid < BAD_SOLID_RATIO)
|
|
1662
|
+
if bad_subject:
|
|
1663
|
+
flags.append("bad_subject")
|
|
1664
|
+
elif coverage < BAD_LOW:
|
|
1665
|
+
flags.append("sparse") # thông tin, KHÔNG phải lỗi — xem docstring ở trên
|
|
1666
|
+
|
|
1667
|
+
# Lưu ý: nếu bad_background đã lên tiếng thì solid_ratio KHÔNG còn đáng tin — mảng nền
|
|
1668
|
+
# hỏng (bóng/gradient/viền chưa key) tự nó đã đục, bơm phồng solid_ratio giả, không phản
|
|
1669
|
+
# ánh chủ thể thật (xem test_postprocess_flags_partial_background_failure). ĐỪNG bỏ
|
|
1670
|
+
# border_impurity đi với lý do "solid_ratio đã bắt được rồi" — nó không bắt được ca này.
|
|
1671
|
+
if solid < BAD_SOLID_RATIO:
|
|
1672
|
+
flags.append("bad_translucent")
|
|
1673
|
+
|
|
1674
|
+
# Finding I2: cờ THAM KHẢO, không phải gate cứng — xem docstring hole_ratio(). KHÔNG
|
|
1675
|
+
# ép bad_subject, KHÔNG trả None chỉ vì bad_holes — contact sheet mới là cửa gác thật.
|
|
1676
|
+
if bbox is not None and holes > BAD_HOLE_RATIO:
|
|
1677
|
+
flags.append("bad_holes")
|
|
1678
|
+
|
|
1679
|
+
report: dict = {
|
|
1680
|
+
"status": flags[0] if flags else "ok", # nặng nhất trước; xem thứ tự append ở trên
|
|
1681
|
+
"flags": flags, # BÁO ĐỦ: một bản sinh có thể hỏng nhiều kiểu
|
|
1682
|
+
"coverage": coverage,
|
|
1683
|
+
"solid_ratio": solid, # luôn báo cáo, kể cả khi ok — người duyệt cần thấy con số
|
|
1684
|
+
"border_impurity": impurity,
|
|
1685
|
+
"bg_residue": residue, # tỉ lệ pixel đục còn mang màu nền SAU khi dọn
|
|
1686
|
+
"bg_px_removed": bg_killed, # số px nền sót mà strip_bg_residue đã xoá
|
|
1687
|
+
"bg_color_used": bg_for_color, # màu thực sự dùng để key/despill/dọn sót
|
|
1688
|
+
"hole_ratio": holes, # luôn báo cáo, kể cả khi ok — xem hole_ratio() (advisory)
|
|
1689
|
+
"speckles_removed": speckles, # >0 là bình thường: watermark Gemini luôn bị dọn
|
|
1690
|
+
"key": key_hex,
|
|
1691
|
+
"full_bleed": full_bleed,
|
|
1692
|
+
# Nói RÕ border_impurity có quyền phủ quyết hay không, để người đọc report không phải
|
|
1693
|
+
# đoán vì sao một impurity cao lại không sinh ra cờ nào.
|
|
1694
|
+
"impurity_gated": impurity_gates,
|
|
1695
|
+
}
|
|
1696
|
+
if multi_rep is not None:
|
|
1697
|
+
# Báo cáo ĐỦ bảng điểm, kể cả ứng viên bị loại: người duyệt cần thấy VÌ SAO cách này
|
|
1698
|
+
# thắng, chứ không chỉ thấy tên nó.
|
|
1699
|
+
report.update({k: multi_rep[k] for k in (
|
|
1700
|
+
"cutout_method", "cutout_score", "cutout_table", "cutout_candidates",
|
|
1701
|
+
"bg_source", "bg_sampled_mode", "bg_mode_share", "band_w", "all_disqualified",
|
|
1702
|
+
)})
|
|
1703
|
+
# coverage/solid_ratio ở trên đo trên bản CHẨN ĐOÁN (xem khối diag). Hai số dưới đây
|
|
1704
|
+
# đo trên bản THỰC SỰ GIAO RA, để chênh lệch giữa chúng không bị che đi.
|
|
1705
|
+
report["out_coverage"] = alpha_coverage(keyed)
|
|
1706
|
+
report["out_solid_ratio"] = solid_ratio(keyed)
|
|
1707
|
+
|
|
1708
|
+
if bad_subject:
|
|
1709
|
+
report["status"] = "bad_subject"
|
|
1710
|
+
return None, report
|
|
1711
|
+
|
|
1712
|
+
if size is not None:
|
|
1713
|
+
canvas_size = size
|
|
1714
|
+
target_box = (0, 0, size[0], size[1])
|
|
1715
|
+
elif src is not None:
|
|
1716
|
+
canvas_size = src.size
|
|
1717
|
+
target_box = content_bbox(src) or (0, 0, src.size[0], src.size[1])
|
|
1718
|
+
else:
|
|
1719
|
+
raise ValueError("postprocess() cần `src` hoặc `size`")
|
|
1720
|
+
|
|
1721
|
+
report["size"] = canvas_size
|
|
1722
|
+
report["target_box"] = target_box
|
|
1723
|
+
out = fit_into(keyed.crop(out_bbox or bbox), canvas_size, target_box,
|
|
1724
|
+
upscale=upscale, report=report)
|
|
1725
|
+
return out, report
|
|
1726
|
+
|
|
1727
|
+
|
|
1728
|
+
LABEL_H = 20
|
|
1729
|
+
SHEET_LIGHT = (240, 240, 240)
|
|
1730
|
+
SHEET_DARK = (28, 28, 32)
|
|
1731
|
+
SHEET_CHECKER = ((205, 205, 205), (170, 170, 170))
|
|
1732
|
+
CHECKER_SIZE = 8
|
|
1733
|
+
|
|
1734
|
+
|
|
1735
|
+
# ── Thumbnail cửa hàng: 3 tỉ lệ cố định ─────────────────────────────────────────────────
|
|
1736
|
+
# Model sinh ảnh KHÔNG cho chọn kích thước đầu ra — nó trả size riêng của nó. Đo thật trên
|
|
1737
|
+
# Gemini web: yêu cầu dọc → 864x1216 (0.7105), ngang → 1376x768 (1.7917), vuông → 1024x1024
|
|
1738
|
+
# (1.0000). Tức nó TÔN TRỌNG HƯỚNG và rơi rất sát mục tiêu, nên đường đi đúng là: sinh ở tỉ lệ
|
|
1739
|
+
# gần nhất → cắt bù phần lệch (đo được ≤1%) → resize về ĐÚNG số pixel yêu cầu.
|
|
1740
|
+
# Giá trị dưới đây theo yêu cầu cửa hàng: 1:1 tối thiểu 512x512, 5:7 đúng 540x756,
|
|
1741
|
+
# 16:9 tối thiểu 1280x720. Với 1:1 giao 1024 thay vì 512 vì nguồn vốn đã 1024 — hạ xuống 512
|
|
1742
|
+
# là bỏ chi tiết không lý do (yêu cầu là SÀN, không phải trần).
|
|
1743
|
+
THUMB_PRESETS: dict[str, tuple[int, int]] = {
|
|
1744
|
+
"1:1": (1024, 1024),
|
|
1745
|
+
"5:7": (540, 756),
|
|
1746
|
+
"16:9": (1280, 720),
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
|
|
1750
|
+
def fit_thumb(
|
|
1751
|
+
img: Image.Image, target: tuple[int, int], anchor: str = "center", upscale=None
|
|
1752
|
+
) -> tuple[Image.Image, dict]:
|
|
1753
|
+
"""Cắt về đúng tỉ lệ `target` rồi resize về ĐÚNG số pixel đó. Trả (ảnh, info).
|
|
1754
|
+
|
|
1755
|
+
KHÔNG tách nền, KHÔNG bám bbox asset gốc — thumbnail là tranh full-bleed, khác hẳn đường
|
|
1756
|
+
restyle asset. Đừng đưa ảnh thumb qua postprocess/process_raws.
|
|
1757
|
+
|
|
1758
|
+
`anchor` quyết định giữ phần nào khi cắt: "center" (mặc định), "top", "bottom", "left",
|
|
1759
|
+
"right". Thumb dọc hay đặt logo/tiêu đề ở trên → dùng "top" cho chắc.
|
|
1760
|
+
|
|
1761
|
+
info: src, target, crop (hộp đã cắt), lost_frac (tỉ lệ pixel bị cắt bỏ), scale,
|
|
1762
|
+
upscaled (True nếu phải PHÓNG TO — ảnh sẽ mềm, nên biết mà sinh lại to hơn).
|
|
1763
|
+
"""
|
|
1764
|
+
tw, th = int(target[0]), int(target[1])
|
|
1765
|
+
if tw <= 0 or th <= 0:
|
|
1766
|
+
raise ValueError(f"target không hợp lệ: {target!r}")
|
|
1767
|
+
w, h = img.size
|
|
1768
|
+
ta, sa = tw / th, w / h
|
|
1769
|
+
|
|
1770
|
+
if sa > ta: # nguồn RỘNG hơn mục tiêu -> cắt bớt chiều ngang
|
|
1771
|
+
cw, ch = max(1, round(h * ta)), h
|
|
1772
|
+
else: # nguồn CAO hơn -> cắt bớt chiều dọc
|
|
1773
|
+
cw, ch = w, max(1, round(w / ta))
|
|
1774
|
+
cw, ch = min(cw, w), min(ch, h)
|
|
1775
|
+
|
|
1776
|
+
if anchor == "left":
|
|
1777
|
+
x0, y0 = 0, (h - ch) // 2
|
|
1778
|
+
elif anchor == "right":
|
|
1779
|
+
x0, y0 = w - cw, (h - ch) // 2
|
|
1780
|
+
elif anchor == "top":
|
|
1781
|
+
x0, y0 = (w - cw) // 2, 0
|
|
1782
|
+
elif anchor == "bottom":
|
|
1783
|
+
x0, y0 = (w - cw) // 2, h - ch
|
|
1784
|
+
else:
|
|
1785
|
+
x0, y0 = (w - cw) // 2, (h - ch) // 2
|
|
1786
|
+
|
|
1787
|
+
box = (x0, y0, x0 + cw, y0 + ch)
|
|
1788
|
+
out = img.crop(box)
|
|
1789
|
+
info = {
|
|
1790
|
+
"src": [w, h],
|
|
1791
|
+
"target": [tw, th],
|
|
1792
|
+
"crop": list(box),
|
|
1793
|
+
"lost_frac": 1.0 - (cw * ch) / float(w * h),
|
|
1794
|
+
"scale": tw / float(cw),
|
|
1795
|
+
"upscaled": tw > cw or th > ch,
|
|
1796
|
+
}
|
|
1797
|
+
if out.size != (tw, th):
|
|
1798
|
+
if info["upscaled"] and tw / float(cw) > UPSCALE_MIN_GAIN:
|
|
1799
|
+
out, up_rep = best_upscale(out, (tw, th), backend=upscale)
|
|
1800
|
+
info.update(up_rep)
|
|
1801
|
+
else:
|
|
1802
|
+
out = out.resize((tw, th), Image.LANCZOS)
|
|
1803
|
+
return out, info
|
|
1804
|
+
|
|
1805
|
+
|
|
1806
|
+
def _checker(size: tuple[int, int]) -> Image.Image:
|
|
1807
|
+
"""Nền caro — kiểu 'ô cờ' của Photoshop. Lộ rõ pixel bán trong suốt."""
|
|
1808
|
+
w, h = size
|
|
1809
|
+
a, b = SHEET_CHECKER
|
|
1810
|
+
tile = np.zeros((h, w, 3), dtype=np.uint8)
|
|
1811
|
+
yy, xx = np.mgrid[0:h, 0:w]
|
|
1812
|
+
odd = ((xx // CHECKER_SIZE) + (yy // CHECKER_SIZE)) % 2 == 1
|
|
1813
|
+
tile[...] = a
|
|
1814
|
+
tile[odd] = b
|
|
1815
|
+
return Image.fromarray(tile, "RGB")
|
|
1816
|
+
|
|
1817
|
+
|
|
1818
|
+
def _bg(size: tuple[int, int], row: int) -> Image.Image:
|
|
1819
|
+
if row == 0:
|
|
1820
|
+
return Image.new("RGB", size, SHEET_LIGHT)
|
|
1821
|
+
if row == 1:
|
|
1822
|
+
return Image.new("RGB", size, SHEET_DARK)
|
|
1823
|
+
return _checker(size)
|
|
1824
|
+
|
|
1825
|
+
|
|
1826
|
+
def _contain(img: Image.Image, box: int) -> Image.Image:
|
|
1827
|
+
w, h = img.size
|
|
1828
|
+
s = min(box / max(1, w), box / max(1, h))
|
|
1829
|
+
return img.resize((max(1, round(w * s)), max(1, round(h * s))), Image.LANCZOS)
|
|
1830
|
+
|
|
1831
|
+
|
|
1832
|
+
def make_sheet(
|
|
1833
|
+
cells: list[tuple[str, Image.Image]], cell: int = 160, pad: int = 12
|
|
1834
|
+
) -> Image.Image:
|
|
1835
|
+
"""Lưới so sánh: mỗi cột 1 ảnh, mỗi hàng 1 nền (sáng / tối / caro).
|
|
1836
|
+
|
|
1837
|
+
Ba nền là có chủ đích: nền sáng lộ viền tối, nền tối lộ viền sáng và spill,
|
|
1838
|
+
nền caro lộ vùng bán trong suốt. Một nền thôi là sẽ bỏ sót lỗi.
|
|
1839
|
+
"""
|
|
1840
|
+
n = max(1, len(cells))
|
|
1841
|
+
width = n * (cell + pad) + pad
|
|
1842
|
+
height = 3 * (cell + pad) + pad + LABEL_H
|
|
1843
|
+
|
|
1844
|
+
sheet = Image.new("RGB", (width, height), (255, 255, 255))
|
|
1845
|
+
draw = ImageDraw.Draw(sheet)
|
|
1846
|
+
|
|
1847
|
+
for col, (label, img) in enumerate(cells):
|
|
1848
|
+
x = pad + col * (cell + pad)
|
|
1849
|
+
draw.text((x, 4), label[:22], fill=(0, 0, 0))
|
|
1850
|
+
|
|
1851
|
+
thumb = _contain(img.convert("RGBA"), cell)
|
|
1852
|
+
for row in range(3):
|
|
1853
|
+
y = LABEL_H + pad + row * (cell + pad)
|
|
1854
|
+
tile = _bg((cell, cell), row)
|
|
1855
|
+
tile.paste(thumb, ((cell - thumb.width) // 2, (cell - thumb.height) // 2), thumb)
|
|
1856
|
+
sheet.paste(tile, (x, y))
|
|
1857
|
+
|
|
1858
|
+
return sheet
|
|
1859
|
+
|
|
1860
|
+
|
|
1861
|
+
def _png_b64(img: Image.Image) -> str:
|
|
1862
|
+
"""Ảnh -> chuỗi base64 PNG (RGBA) để nhúng thẳng vào HTML (self-contained)."""
|
|
1863
|
+
buf = io.BytesIO()
|
|
1864
|
+
img.convert("RGBA").save(buf, "PNG")
|
|
1865
|
+
return base64.b64encode(buf.getvalue()).decode("ascii")
|
|
1866
|
+
|
|
1867
|
+
|
|
1868
|
+
def compare_sheet(
|
|
1869
|
+
groups: list[tuple[str, Image.Image | None, list[tuple[str, Image.Image]]]],
|
|
1870
|
+
cell: int = 240,
|
|
1871
|
+
) -> str:
|
|
1872
|
+
"""Trang HTML ĐỐI CHIẾU gốc↔mới, tự chứa. Đây là bản giao cho user sau MỖI lượt thiết kế.
|
|
1873
|
+
|
|
1874
|
+
Khác `web_sheet` (lưới thẻ phẳng, mọi ảnh ngang hàng): ở đây ảnh được NHÓM THEO ASSET,
|
|
1875
|
+
bản gốc nằm ngay cạnh các biến thể của chính nó, và mỗi thẻ biến thể lật được về gốc
|
|
1876
|
+
NGAY TẠI CHỖ (nhấn giữ; nháy đúp để khoá). Đặt chồng đúng một vị trí và một kích thước
|
|
1877
|
+
là cách duy nhất thấy được lệch hình dạng/tỉ lệ nhỏ — hai thẻ nằm cạnh nhau thì mắt
|
|
1878
|
+
không bắt được. Lô nhiều asset mà không nhóm lại thì user phải tự nhớ ảnh nào của asset
|
|
1879
|
+
nào; đó là lý do gom nhóm chứ không phải để trang trí.
|
|
1880
|
+
|
|
1881
|
+
`groups`: list (tên asset, ảnh gốc | None, [(nhãn, ảnh biến thể), ...]).
|
|
1882
|
+
Gốc = None thì thẻ chỉ hiện biến thể, không có nút lật (vd cells screen-sheet vốn
|
|
1883
|
+
không có asset gốc 1-1).
|
|
1884
|
+
|
|
1885
|
+
Ảnh gốc được nhúng MỘT LẦN cho mỗi nhóm rồi gán bằng JS lúc lật, chứ không nhúng lại
|
|
1886
|
+
trong từng thẻ — một lô 10 asset × 4 biến thể mà nhúng lặp thì file phình vô ích.
|
|
1887
|
+
"""
|
|
1888
|
+
origs: list[str] = []
|
|
1889
|
+
sections: list[str] = []
|
|
1890
|
+
|
|
1891
|
+
for gi, (name, orig, variants) in enumerate(groups):
|
|
1892
|
+
nm = _html.escape(str(name))[:60]
|
|
1893
|
+
cards = []
|
|
1894
|
+
|
|
1895
|
+
if orig is not None:
|
|
1896
|
+
origs.append(_png_b64(orig))
|
|
1897
|
+
ow, oh = orig.size
|
|
1898
|
+
cards.append(
|
|
1899
|
+
'<figure class="card is-orig">'
|
|
1900
|
+
f'<figcaption><span class="lbl">GỐC</span>'
|
|
1901
|
+
f'<span class="dim">{ow}×{oh}</span></figcaption>'
|
|
1902
|
+
f'<div class="stage"><img src="data:image/png;base64,{origs[-1]}" '
|
|
1903
|
+
f'alt="{nm} gốc" loading="lazy"></div>'
|
|
1904
|
+
"</figure>"
|
|
1905
|
+
)
|
|
1906
|
+
else:
|
|
1907
|
+
origs.append("")
|
|
1908
|
+
|
|
1909
|
+
for label, img in variants:
|
|
1910
|
+
w, h = img.size
|
|
1911
|
+
lbl = _html.escape(str(label))[:40]
|
|
1912
|
+
flip = (
|
|
1913
|
+
f'<img class="ov" data-g="{gi}" alt="" loading="lazy">'
|
|
1914
|
+
if orig is not None else ""
|
|
1915
|
+
)
|
|
1916
|
+
cards.append(
|
|
1917
|
+
f'<figure class="card{" can-flip" if orig is not None else ""}" data-g="{gi}">'
|
|
1918
|
+
f'<figcaption><span class="lbl">{lbl}</span>'
|
|
1919
|
+
f'<span class="state"></span>'
|
|
1920
|
+
f'<span class="dim">{w}×{h}</span></figcaption>'
|
|
1921
|
+
f'<div class="stage"><img class="nw" '
|
|
1922
|
+
f'src="data:image/png;base64,{_png_b64(img)}" alt="{lbl}" loading="lazy">'
|
|
1923
|
+
f"{flip}</div>"
|
|
1924
|
+
"</figure>"
|
|
1925
|
+
)
|
|
1926
|
+
|
|
1927
|
+
sections.append(
|
|
1928
|
+
f'<section class="grp"><h2>{nm}'
|
|
1929
|
+
f'<span class="cnt">{len(variants)} bản</span></h2>'
|
|
1930
|
+
f'<div class="row">{"".join(cards)}</div></section>'
|
|
1931
|
+
)
|
|
1932
|
+
|
|
1933
|
+
doc = """<!doctype html><html lang="vi"><head><meta charset="utf-8">
|
|
1934
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
1935
|
+
<title>game-art — đối chiếu gốc / mới</title>
|
|
1936
|
+
<style>
|
|
1937
|
+
:root{--cell:__CELL__px;--custom:#3aa675;color-scheme:dark light}
|
|
1938
|
+
*{box-sizing:border-box}
|
|
1939
|
+
body{margin:0;background:#0d1117;color:#e6edf3;font:14px/1.5 system-ui,Segoe UI,sans-serif}
|
|
1940
|
+
header{position:sticky;top:0;z-index:5;background:#161b22e6;backdrop-filter:blur(8px);
|
|
1941
|
+
border-bottom:1px solid #30363d;padding:12px 20px;display:flex;gap:14px;align-items:center;flex-wrap:wrap}
|
|
1942
|
+
h1{font-size:16px;margin:0 12px 0 0}
|
|
1943
|
+
.seg{display:inline-flex;border:1px solid #30363d;border-radius:8px;overflow:hidden}
|
|
1944
|
+
.seg button{background:#0d1117;color:#adbac7;border:0;padding:6px 12px;cursor:pointer;font:inherit}
|
|
1945
|
+
.seg button+button{border-left:1px solid #30363d}
|
|
1946
|
+
.seg button.on{background:#1f6feb;color:#fff}
|
|
1947
|
+
label.ctl{display:inline-flex;gap:6px;align-items:center;color:#8b949e}
|
|
1948
|
+
input[type=range]{width:130px}
|
|
1949
|
+
input[type=color]{width:30px;height:26px;border:1px solid #30363d;border-radius:6px;background:none;padding:0}
|
|
1950
|
+
.hint{color:#8b949e;font-size:12px;margin-left:auto}
|
|
1951
|
+
.hint kbd{background:#21262d;border:1px solid #30363d;border-radius:4px;padding:1px 5px;font:inherit}
|
|
1952
|
+
main{padding:20px}
|
|
1953
|
+
.grp{margin:0 0 26px}
|
|
1954
|
+
.grp h2{font-size:14px;margin:0 0 10px;padding-bottom:7px;border-bottom:1px solid #30363d;
|
|
1955
|
+
display:flex;align-items:center;gap:10px;color:#e6edf3}
|
|
1956
|
+
.cnt{color:#8b949e;font-weight:400;font-size:12px}
|
|
1957
|
+
.row{display:grid;gap:14px;grid-template-columns:repeat(auto-fill,minmax(var(--cell),1fr))}
|
|
1958
|
+
.card{background:#161b22;border:1px solid #30363d;border-radius:10px;overflow:hidden}
|
|
1959
|
+
.card.is-orig{border-color:#8957e5}
|
|
1960
|
+
.card.can-flip{cursor:pointer;user-select:none}
|
|
1961
|
+
figcaption{display:flex;justify-content:space-between;align-items:center;gap:8px;
|
|
1962
|
+
padding:7px 10px;font-size:12px;border-bottom:1px solid #30363d}
|
|
1963
|
+
.lbl{font-weight:600;color:#e6edf3}
|
|
1964
|
+
.is-orig .lbl{color:#d2a8ff}
|
|
1965
|
+
.dim{color:#8b949e;font-variant-numeric:tabular-nums}
|
|
1966
|
+
.state{margin-left:auto;font-size:11px;color:#8b949e}
|
|
1967
|
+
.showing-orig .state{color:#d2a8ff;font-weight:600}
|
|
1968
|
+
.stage{position:relative;height:var(--cell);display:flex;align-items:center;justify-content:center;padding:8px}
|
|
1969
|
+
.stage img{max-width:100%;max-height:100%;object-fit:contain}
|
|
1970
|
+
.ov{position:absolute;inset:8px;margin:auto;display:none}
|
|
1971
|
+
.showing-orig .nw{visibility:hidden}
|
|
1972
|
+
.showing-orig .ov{display:block}
|
|
1973
|
+
[data-bg=light] .stage{background:#f2f2f2}
|
|
1974
|
+
[data-bg=dark] .stage{background:#181818}
|
|
1975
|
+
[data-bg=custom] .stage{background:var(--custom)}
|
|
1976
|
+
[data-bg=checker] .stage{background-color:#fff;background-image:
|
|
1977
|
+
linear-gradient(45deg,#c9c9c9 25%,transparent 25%,transparent 75%,#c9c9c9 75%),
|
|
1978
|
+
linear-gradient(45deg,#c9c9c9 25%,transparent 25%,transparent 75%,#c9c9c9 75%);
|
|
1979
|
+
background-size:22px 22px;background-position:0 0,11px 11px}
|
|
1980
|
+
</style></head>
|
|
1981
|
+
<body data-bg="checker">
|
|
1982
|
+
<header>
|
|
1983
|
+
<h1>\U0001f3ae game-art — đối chiếu gốc / mới</h1>
|
|
1984
|
+
<div class="seg" id="bgseg">
|
|
1985
|
+
<button data-b="checker" class="on">Caro</button>
|
|
1986
|
+
<button data-b="light">Sáng</button>
|
|
1987
|
+
<button data-b="dark">Tối</button>
|
|
1988
|
+
<button data-b="custom">Màu</button>
|
|
1989
|
+
</div>
|
|
1990
|
+
<label class="ctl">Nền <input type="color" id="pick" value="#3aa675"></label>
|
|
1991
|
+
<label class="ctl">Zoom <input type="range" id="zoom" min="140" max="560" value="__CELL__"></label>
|
|
1992
|
+
<div class="seg"><button id="allorig">Xem tất cả bản GỐC</button></div>
|
|
1993
|
+
<div class="hint"><kbd>nhấn giữ</kbd> lật về gốc · <kbd>nháy đúp</kbd> khoá</div>
|
|
1994
|
+
</header>
|
|
1995
|
+
<main>
|
|
1996
|
+
__SECTIONS__
|
|
1997
|
+
</main>
|
|
1998
|
+
<script>
|
|
1999
|
+
var ORIG=__ORIGS__;
|
|
2000
|
+
var body=document.body,root=document.documentElement;
|
|
2001
|
+
document.querySelectorAll('#bgseg button').forEach(function(b){
|
|
2002
|
+
b.onclick=function(){
|
|
2003
|
+
document.querySelectorAll('#bgseg button').forEach(function(x){x.classList.remove('on')});
|
|
2004
|
+
b.classList.add('on'); body.dataset.bg=b.dataset.b;
|
|
2005
|
+
};
|
|
2006
|
+
});
|
|
2007
|
+
var pick=document.getElementById('pick');
|
|
2008
|
+
pick.oninput=function(){
|
|
2009
|
+
root.style.setProperty('--custom',pick.value);
|
|
2010
|
+
body.dataset.bg='custom';
|
|
2011
|
+
document.querySelectorAll('#bgseg button').forEach(function(x){x.classList.toggle('on',x.dataset.b==='custom')});
|
|
2012
|
+
};
|
|
2013
|
+
document.getElementById('zoom').oninput=function(){root.style.setProperty('--cell',this.value+'px')};
|
|
2014
|
+
|
|
2015
|
+
// Ảnh gốc chỉ gán src lúc CẦN, từ bảng ORIG nhúng một lần mỗi nhóm — tránh lặp base64.
|
|
2016
|
+
function fill(card){
|
|
2017
|
+
var ov=card.querySelector('.ov');
|
|
2018
|
+
if(ov && !ov.getAttribute('src')) ov.src=ORIG[card.dataset.g];
|
|
2019
|
+
}
|
|
2020
|
+
function show(card,on){
|
|
2021
|
+
if(!card.classList.contains('can-flip')) return;
|
|
2022
|
+
fill(card);
|
|
2023
|
+
card.classList.toggle('showing-orig',on);
|
|
2024
|
+
var s=card.querySelector('.state'); if(s) s.textContent=on?'đang xem GỐC':'';
|
|
2025
|
+
}
|
|
2026
|
+
document.querySelectorAll('.card.can-flip').forEach(function(card){
|
|
2027
|
+
card.addEventListener('pointerdown',function(){show(card,true)});
|
|
2028
|
+
card.addEventListener('pointerup',function(){if(card.dataset.lock!=='1')show(card,false)});
|
|
2029
|
+
card.addEventListener('pointerleave',function(){if(card.dataset.lock!=='1')show(card,false)});
|
|
2030
|
+
card.addEventListener('dblclick',function(){
|
|
2031
|
+
card.dataset.lock=card.dataset.lock==='1'?'0':'1';
|
|
2032
|
+
show(card,card.dataset.lock==='1');
|
|
2033
|
+
});
|
|
2034
|
+
});
|
|
2035
|
+
var all=false,btn=document.getElementById('allorig');
|
|
2036
|
+
btn.onclick=function(){
|
|
2037
|
+
all=!all; btn.classList.toggle('on',all);
|
|
2038
|
+
btn.textContent=all?'Xem tất cả bản MỚI':'Xem tất cả bản GỐC';
|
|
2039
|
+
document.querySelectorAll('.card.can-flip').forEach(function(c){
|
|
2040
|
+
c.dataset.lock=all?'1':'0'; show(c,all);
|
|
2041
|
+
});
|
|
2042
|
+
};
|
|
2043
|
+
</script>
|
|
2044
|
+
</body></html>"""
|
|
2045
|
+
return (doc.replace("__CELL__", str(int(cell)))
|
|
2046
|
+
.replace("__SECTIONS__", "\n".join(sections))
|
|
2047
|
+
.replace("__ORIGS__", _json.dumps(
|
|
2048
|
+
{i: (f"data:image/png;base64,{b}" if b else "") for i, b in enumerate(origs)})))
|
|
2049
|
+
|
|
2050
|
+
|
|
2051
|
+
def web_sheet(cells: list[tuple[str, Image.Image]], cell: int = 240) -> str:
|
|
2052
|
+
"""Trang HTML DUYỆT BẰNG MẮT, tự chứa (nhúng base64 PNG) — thay contact sheet tĩnh.
|
|
2053
|
+
|
|
2054
|
+
Ưu điểm so với PNG: đổi nền TRỰC TIẾP (sáng/tối/caro/màu tùy chọn) để soi mép cutout
|
|
2055
|
+
và vùng bán trong suốt, kéo slider phóng to từng ảnh, không cần mở Photoshop. Một file
|
|
2056
|
+
HTML duy nhất, mở bằng trình duyệt nào cũng chạy, không phụ thuộc file ngoài.
|
|
2057
|
+
|
|
2058
|
+
`cells`: list (nhãn, ảnh RGBA) — vd [("original", src), ("v1", var1), ...].
|
|
2059
|
+
"""
|
|
2060
|
+
cards = []
|
|
2061
|
+
for label, img in cells:
|
|
2062
|
+
w, h = img.size
|
|
2063
|
+
lbl = _html.escape(str(label))[:40]
|
|
2064
|
+
cards.append(
|
|
2065
|
+
'<figure class="card">'
|
|
2066
|
+
f'<figcaption><span class="lbl">{lbl}</span>'
|
|
2067
|
+
f'<span class="dim">{w}×{h}</span></figcaption>'
|
|
2068
|
+
f'<div class="stage"><img src="data:image/png;base64,{_png_b64(img)}" '
|
|
2069
|
+
f'alt="{lbl}" loading="lazy"></div>'
|
|
2070
|
+
"</figure>"
|
|
2071
|
+
)
|
|
2072
|
+
grid = "\n".join(cards)
|
|
2073
|
+
|
|
2074
|
+
doc = """<!doctype html><html lang="vi"><head><meta charset="utf-8">
|
|
2075
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
2076
|
+
<title>game-art — review cutout</title>
|
|
2077
|
+
<style>
|
|
2078
|
+
:root{--cell:__CELL__px;--custom:#3aa675;color-scheme:dark light}
|
|
2079
|
+
*{box-sizing:border-box}
|
|
2080
|
+
body{margin:0;background:#0d1117;color:#e6edf3;font:14px/1.5 system-ui,Segoe UI,sans-serif}
|
|
2081
|
+
header{position:sticky;top:0;z-index:5;background:#161b22e6;backdrop-filter:blur(8px);
|
|
2082
|
+
border-bottom:1px solid #30363d;padding:12px 20px;display:flex;gap:14px;align-items:center;flex-wrap:wrap}
|
|
2083
|
+
h1{font-size:16px;margin:0 12px 0 0}
|
|
2084
|
+
.seg{display:inline-flex;border:1px solid #30363d;border-radius:8px;overflow:hidden}
|
|
2085
|
+
.seg button{background:#0d1117;color:#adbac7;border:0;padding:6px 12px;cursor:pointer;font:inherit}
|
|
2086
|
+
.seg button+button{border-left:1px solid #30363d}
|
|
2087
|
+
.seg button.on{background:#1f6feb;color:#fff}
|
|
2088
|
+
label.ctl{display:inline-flex;gap:6px;align-items:center;color:#8b949e}
|
|
2089
|
+
input[type=range]{width:130px}
|
|
2090
|
+
input[type=color]{width:30px;height:26px;border:1px solid #30363d;border-radius:6px;background:none;padding:0}
|
|
2091
|
+
main{padding:20px;display:grid;gap:16px;grid-template-columns:repeat(auto-fill,minmax(var(--cell),1fr))}
|
|
2092
|
+
.card{background:#161b22;border:1px solid #30363d;border-radius:10px;overflow:hidden}
|
|
2093
|
+
figcaption{display:flex;justify-content:space-between;align-items:center;padding:7px 10px;font-size:12px;border-bottom:1px solid #30363d}
|
|
2094
|
+
.lbl{font-weight:600;color:#e6edf3}
|
|
2095
|
+
.dim{color:#8b949e;font-variant-numeric:tabular-nums}
|
|
2096
|
+
.stage{height:var(--cell);display:flex;align-items:center;justify-content:center;padding:8px}
|
|
2097
|
+
.stage img{max-width:100%;max-height:100%;object-fit:contain;image-rendering:auto}
|
|
2098
|
+
[data-bg=light] .stage{background:#f2f2f2}
|
|
2099
|
+
[data-bg=dark] .stage{background:#181818}
|
|
2100
|
+
[data-bg=custom] .stage{background:var(--custom)}
|
|
2101
|
+
[data-bg=checker] .stage{background-color:#fff;background-image:
|
|
2102
|
+
linear-gradient(45deg,#c9c9c9 25%,transparent 25%,transparent 75%,#c9c9c9 75%),
|
|
2103
|
+
linear-gradient(45deg,#c9c9c9 25%,transparent 25%,transparent 75%,#c9c9c9 75%);
|
|
2104
|
+
background-size:22px 22px;background-position:0 0,11px 11px}
|
|
2105
|
+
</style></head>
|
|
2106
|
+
<body data-bg="checker">
|
|
2107
|
+
<header>
|
|
2108
|
+
<h1>\U0001f3ae game-art — review</h1>
|
|
2109
|
+
<div class="seg" id="bgseg">
|
|
2110
|
+
<button data-b="checker" class="on">Caro</button>
|
|
2111
|
+
<button data-b="light">Sáng</button>
|
|
2112
|
+
<button data-b="dark">Tối</button>
|
|
2113
|
+
<button data-b="custom">Màu</button>
|
|
2114
|
+
</div>
|
|
2115
|
+
<label class="ctl">Nền <input type="color" id="pick" value="#3aa675"></label>
|
|
2116
|
+
<label class="ctl">Zoom <input type="range" id="zoom" min="140" max="560" value="__CELL__"></label>
|
|
2117
|
+
</header>
|
|
2118
|
+
<main id="grid">
|
|
2119
|
+
__GRID__
|
|
2120
|
+
</main>
|
|
2121
|
+
<script>
|
|
2122
|
+
var body=document.body,root=document.documentElement;
|
|
2123
|
+
document.querySelectorAll('#bgseg button').forEach(function(b){
|
|
2124
|
+
b.onclick=function(){
|
|
2125
|
+
document.querySelectorAll('#bgseg button').forEach(function(x){x.classList.remove('on')});
|
|
2126
|
+
b.classList.add('on'); body.dataset.bg=b.dataset.b;
|
|
2127
|
+
};
|
|
2128
|
+
});
|
|
2129
|
+
var pick=document.getElementById('pick');
|
|
2130
|
+
pick.oninput=function(){
|
|
2131
|
+
root.style.setProperty('--custom',pick.value);
|
|
2132
|
+
body.dataset.bg='custom';
|
|
2133
|
+
document.querySelectorAll('#bgseg button').forEach(function(x){x.classList.toggle('on',x.dataset.b==='custom')});
|
|
2134
|
+
};
|
|
2135
|
+
document.getElementById('zoom').oninput=function(){root.style.setProperty('--cell',this.value+'px')};
|
|
2136
|
+
</script>
|
|
2137
|
+
</body></html>"""
|
|
2138
|
+
return doc.replace("__CELL__", str(int(cell))).replace("__GRID__", grid)
|