diff --git a/src/css/ingame_hud/dialogs.scss b/src/css/ingame_hud/dialogs.scss index 35d9aeb0..b913ba35 100644 --- a/src/css/ingame_hud/dialogs.scss +++ b/src/css/ingame_hud/dialogs.scss @@ -226,6 +226,18 @@ } } + .checkBoxGridFormElem { + display: inline-grid; + grid-template-columns: 1fr; + @include S(margin, 10px, 0); + @include S(grid-row-gap, 10px); + + > .checkBoxFormElem { + margin: 0; + justify-content: space-between; + } + } + .enum { display: grid; grid-template-columns: auto 1fr auto; diff --git a/src/js/core/modal_dialog_forms.js b/src/js/core/modal_dialog_forms.js index 2f2401a9..b27204f3 100644 --- a/src/js/core/modal_dialog_forms.js +++ b/src/js/core/modal_dialog_forms.js @@ -140,7 +140,7 @@ export class FormElementCheckbox extends FormElement { getHtml() { return `
- ${this.label ? `` : ""} + ${this.label ? `` : ""}
@@ -170,6 +170,31 @@ export class FormElementCheckbox extends FormElement { focus(parent) {} } +export class FormElementCheckboxList extends FormElement { + constructor({ id, label = null, checkboxes = [] }) { + super(id, label); + this.checkboxes = checkboxes; + } + + getHtml() { + return ` +
+ ${this.checkboxes.map(checkbox => checkbox.getHtml()).join("\n")} +
+ `; + } + + bindEvents(parent, clickTrackers) { + this.checkboxes.forEach(checkbox => checkbox.bindEvents(parent, clickTrackers)); + } + + getValue() { + return this.checkboxes.map(checkbox => checkbox.getValue()); + } + + focus(parent) {} +} + export class FormElementItemChooser extends FormElement { /** * diff --git a/src/js/game/hud/parts/screenshot_exporter.js b/src/js/game/hud/parts/screenshot_exporter.js index 673dc9cd..0ec46069 100644 --- a/src/js/game/hud/parts/screenshot_exporter.js +++ b/src/js/game/hud/parts/screenshot_exporter.js @@ -9,12 +9,16 @@ import { StaticMapEntityComponent } from "../../components/static_map_entity"; import { KEYMAPPINGS } from "../../key_action_mapper"; import { BaseHUDPart } from "../base_hud_part"; import { DialogWithForm } from "../../../core/modal_dialog_elements"; -import { FormElementCheckbox, FormElementEnum } from "../../../core/modal_dialog_forms"; +import { + FormElementCheckbox, + FormElementCheckboxList, + FormElementEnum, +} from "../../../core/modal_dialog_forms"; import { ORIGINAL_SPRITE_SCALE } from "../../../core/sprites"; import { getDeviceDPI } from "../../../core/dpi_manager"; import { HUDMassSelector } from "./mass_selector"; import { clamp } from "../../../core/utils"; -import { CHUNK_OVERLAY_RES } from "../../map_chunk_view"; +import { CHUNK_OVERLAY_RES, MapChunkView } from "../../map_chunk_view"; const logger = createLogger("screenshot_exporter"); @@ -113,6 +117,16 @@ export class HUDScreenshotExporter extends BaseHUDPart { label: "Wires layer", defaultValue: this.root.currentLayer === "wires" ? true : false, }); + const backgroundInput = new FormElementCheckbox({ + id: "screenshotBackground", + // @TODO: translation (T.dialogs.exportScreenshotWarning.descBackground) + label: "Transparent background", + defaultValue: false, + }); + const checkboxInputs = new FormElementCheckboxList({ + id: "screenshotCheckboxes", + checkboxes: [overlayInput, layerInput, backgroundInput], + }); const dialog = new DialogWithForm({ app: this.root.app, title: T.dialogs.exportScreenshotWarning.title, @@ -128,7 +142,7 @@ export class HUDScreenshotExporter extends BaseHUDPart { .getKeyCodeString() + "" ), - formElements: [qualityInput, overlayInput, layerInput], + formElements: [qualityInput, checkboxInputs], buttons: ["cancel:good", "ok:bad"], }); @@ -145,6 +159,7 @@ export class HUDScreenshotExporter extends BaseHUDPart { qualityInput.getValue(), overlayInput.getValue(), layerInput.getValue(), + backgroundInput.getValue(), !!bounds, bounds ), @@ -157,10 +172,11 @@ export class HUDScreenshotExporter extends BaseHUDPart { * @param {number} targetResolution * @param {boolean} overlay * @param {boolean} wiresLayer + * @param {boolean} hideBackground * @param {boolean} allowBorder * @param {Rectangle?} tileBounds */ - doExport(targetResolution, overlay, wiresLayer, allowBorder, tileBounds) { + doExport(targetResolution, overlay, wiresLayer, hideBackground, allowBorder, tileBounds) { logger.log("Starting export ..."); const boundsSelected = !!tileBounds; @@ -296,9 +312,23 @@ export class HUDScreenshotExporter extends BaseHUDPart { this.root.signals.gameFrameStarted.dispatch(); if (overlay) { this.root; - this.root.map.drawOverlay(parameters); + if (hideBackground) { + this.root.map.drawVisibleChunks(parameters, MapChunkView.prototype.drawOverlayNoBackground); + } else { + this.root.map.drawOverlay(parameters); + } } else { - this.root.map.drawBackground(parameters); + if (hideBackground) { + this.root.map.drawVisibleChunks( + parameters, + /** @this {MapChunkView} */ function (parameters) { + this.root.systemMgr.systems.beltUnderlays.drawChunk(parameters, this); + this.root.systemMgr.systems.belt.drawChunk(parameters, this); + } + ); + } else { + this.root.map.drawBackground(parameters); + } this.root.systemMgr.systems.belt.drawBeltItems(parameters); this.root.map.drawForeground(parameters); this.root.systemMgr.systems.hub.draw(parameters); diff --git a/src/js/game/map_chunk_view.js b/src/js/game/map_chunk_view.js index 131ce37b..cc5c43d9 100644 --- a/src/js/game/map_chunk_view.js +++ b/src/js/game/map_chunk_view.js @@ -130,6 +130,40 @@ export class MapChunkView extends MapChunk { } } + /** + * Overlay with transparent background + * @param {DrawParameters} parameters + */ + drawOverlayNoBackground(parameters) { + const overlaySize = globalConfig.mapChunkSize * CHUNK_OVERLAY_RES; + const sprite = this.root.buffers.getForKey({ + key: "chunknobg@" + this.root.currentLayer, + subKey: this.renderKey, + w: overlaySize, + h: overlaySize, + dpi: 1, + redrawMethod: this.generateOverlayBufferNoBackground.bind(this), + }); + + const dims = globalConfig.mapChunkWorldSize; + const extrude = 0.05; + + // Draw chunk "pixel" art + parameters.context.imageSmoothingEnabled = false; + drawSpriteClipped({ + parameters, + sprite, + x: this.x * dims - extrude, + y: this.y * dims - extrude, + w: dims + 2 * extrude, + h: dims + 2 * extrude, + originalW: overlaySize, + originalH: overlaySize, + }); + + parameters.context.imageSmoothingEnabled = true; + } + /** * * @param {HTMLCanvasElement} canvas @@ -254,6 +288,94 @@ export class MapChunkView extends MapChunk { } } + /** + * + * @param {HTMLCanvasElement} canvas + * @param {CanvasRenderingContext2D} context + * @param {number} w + * @param {number} h + * @param {number} dpi + */ + generateOverlayBufferNoBackground(canvas, context, w, h, dpi) { + for (let x = 0; x < globalConfig.mapChunkSize; ++x) { + const upperArray = this.contents[x]; + for (let y = 0; y < globalConfig.mapChunkSize; ++y) { + const upperContent = upperArray[y]; + if (upperContent) { + const staticComp = upperContent.components.StaticMapEntity; + const data = getBuildingDataFromCode(staticComp.code); + const metaBuilding = data.metaInstance; + + const overlayMatrix = metaBuilding.getSpecialOverlayRenderMatrix( + staticComp.rotation, + data.rotationVariant, + data.variant, + upperContent + ); + + if (overlayMatrix) { + context.fillStyle = metaBuilding.getSilhouetteColor( + data.variant, + data.rotationVariant + ); + for (let dx = 0; dx < 3; ++dx) { + for (let dy = 0; dy < 3; ++dy) { + const isFilled = overlayMatrix[dx + dy * 3]; + if (isFilled) { + context.fillRect( + x * CHUNK_OVERLAY_RES + dx, + y * CHUNK_OVERLAY_RES + dy, + 1, + 1 + ); + } + } + } + + continue; + } else { + context.fillStyle = metaBuilding.getSilhouetteColor( + data.variant, + data.rotationVariant + ); + context.fillRect( + x * CHUNK_OVERLAY_RES, + y * CHUNK_OVERLAY_RES, + CHUNK_OVERLAY_RES, + CHUNK_OVERLAY_RES + ); + + continue; + } + } + } + } + + if (this.root.currentLayer === "wires") { + // Draw wires overlay + + context.fillStyle = THEME.map.wires.overlayColor; + context.fillRect(0, 0, w, h); + + for (let x = 0; x < globalConfig.mapChunkSize; ++x) { + const wiresArray = this.wireContents[x]; + for (let y = 0; y < globalConfig.mapChunkSize; ++y) { + const content = wiresArray[y]; + if (!content) { + continue; + } + MapChunkView.drawSingleWiresOverviewTile({ + context, + x: x * CHUNK_OVERLAY_RES, + y: y * CHUNK_OVERLAY_RES, + entity: content, + tileSizePixels: CHUNK_OVERLAY_RES, + }); + } + } + } + } + /** * @param {object} param0 * @param {CanvasRenderingContext2D} param0.context