From 4efe3a911001e768408b021633473f2d279af34c Mon Sep 17 00:00:00 2001 From: Jose Falanga Date: Mon, 17 Aug 2026 01:51:54 -0300 Subject: [PATCH] Combine two screenshots side-by-side for banner layout --- core/boxart_itch.gd | 48 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/core/boxart_itch.gd b/core/boxart_itch.gd index b0bf9d3..f588e38 100644 --- a/core/boxart_itch.gd +++ b/core/boxart_itch.gd @@ -64,14 +64,27 @@ func get_boxart(item: LibraryItem, kind: LAYOUT) -> Texture2D: _save_enriched_meta(game_id, meta) var screenshots: Array = meta.get("screenshots", []) - var url := _url_for_layout(kind, cover_url, screenshots) - if url.is_empty(): - return null - var cache_flags := Cache.FLAGS.NONE if use_caching: cache_flags = Cache.FLAGS.LOAD | Cache.FLAGS.SAVE + # Banner: combine two screenshots side-by-side when available. + if kind == LAYOUT.BANNER and screenshots.size() >= 2: + logger.debug("Fetching itch.io banner (2 screenshots) for: " + item.name) + var tex_a := await http_image.fetch(screenshots[0], cache_flags) + var tex_b := await http_image.fetch(screenshots[1], cache_flags) + if tex_a != null and tex_b != null: + return _combine_side_by_side(tex_a, tex_b) + # Fall through to single-image path if either failed. + if tex_a != null: + return tex_a + if tex_b != null: + return tex_b + + var url := _url_for_layout(kind, cover_url, screenshots) + if url.is_empty(): + return null + logger.debug("Fetching itch.io box art for: " + item.name) var texture: Texture2D = await http_image.fetch(url, cache_flags) if texture == null: @@ -97,8 +110,7 @@ func _url_for_layout(kind: LAYOUT, cover_url: String, screenshots: Array) -> Str LAYOUT.GRID_LANDSCAPE: return screenshots[0] if not screenshots.is_empty() else cover_url LAYOUT.BANNER: - if screenshots.size() > 1: - return screenshots[1] + # Single screenshot fallback (two-screenshot case handled in get_boxart). return screenshots[0] if not screenshots.is_empty() else cover_url return "" @@ -110,6 +122,30 @@ func _cover_url(game: Dictionary) -> String: return game.get("coverUrl", "") +## Combines two textures side-by-side into a single banner image. +## Both images are scaled to the same height, then placed left and right. +func _combine_side_by_side(a: Texture2D, b: Texture2D) -> Texture2D: + var img_a := a.get_image() + var img_b := b.get_image() + if img_a == null or img_b == null: + return a + + # Scale both to the same height (use the taller one). + var target_h: int = max(img_a.get_height(), img_b.get_height()) + if img_a.get_height() != target_h: + var scale: float = float(target_h) / float(img_a.get_height()) + img_a.resize(int(img_a.get_width() * scale), target_h, Image.INTERPOLATE_BILINEAR) + if img_b.get_height() != target_h: + var scale: float = float(target_h) / float(img_b.get_height()) + img_b.resize(int(img_b.get_width() * scale), target_h, Image.INTERPOLATE_BILINEAR) + + var combined := Image.create(img_a.get_width() + img_b.get_width(), target_h, false, img_a.get_format()) + combined.blit_rect(img_a, Rect2i(0, 0, img_a.get_width(), target_h), Vector2i.ZERO) + combined.blit_rect(img_b, Rect2i(0, 0, img_b.get_width(), target_h), Vector2i(img_a.get_width(), 0)) + + return ImageTexture.create_from_image(combined) + + ## Scrapes the itch.io game page for screenshots. The itch.io API does not ## expose screenshots, so the page HTML is the only source. func _fetch_game_page_metadata(game: Dictionary) -> Dictionary: