feat: upgrade to v0.0.4 - Image Converter and Dock Fix#4
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces an image converter feature that converts JPG/JPEG images to PNG format and fixes a critical bug where FileSystem and Bottom Docks would disappear when the plugin was active upon opening the Godot Editor.
Changes:
- Added image converter functionality with dedicated UI mode for converting images to PNG format
- Fixed UI initialization and state management to prevent dock disappearing issues
- Updated version to 0.0.4 across plugin configuration and UI panels
Reviewed changes
Copilot reviewed 5 out of 7 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| rust/src/texture_generator.rs | Implements convert_to_png function to handle image conversion to PNG format |
| addons/photonic_ring/ui/texture_generator_panel.tscn | Adds converter mode UI components and updates version display from v0.0.2 to v0.0.4 |
| addons/photonic_ring/ui/texture_generator_panel.gd | Integrates converter mode with UI state management and fixes dock disappearing issue |
| addons/photonic_ring/plugin.cfg | Updates plugin version from 0.0.3 to 0.0.4 |
| CHANGELOG.md | Documents v0.0.4 changes including new image converter feature and dock fix |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /// Convert an image (jpg/jpeg) to PNG | ||
| /// | ||
| /// # Arguments | ||
| /// * `input_path` - Path to the source image | ||
| /// * `output_dir` - Optional output directory (if empty, saves next to source) | ||
| /// | ||
| /// # Returns | ||
| /// Dictionary with keys: success, error, output_path |
There was a problem hiding this comment.
The function accepts any image format (jpg/jpeg/png/etc.) and converts to PNG, but the documentation comment and UI labels indicate it's specifically for JPG/JPEG to PNG conversion. Consider either: (1) adding validation to restrict input to JPG/JPEG formats only, or (2) updating the documentation and UI text to reflect that it accepts any image format. The current mismatch could confuse users.
|
|
||
| [node name="ConvertInputLabel" type="Label" parent="MarginContainer/VBoxContainer/ConverterModeContainer"] | ||
| layout_mode = 2 | ||
| text = "🖼️ Source Image (JPG/JPEG):" |
There was a problem hiding this comment.
The UI label indicates this converter is specifically for JPG/JPEG images, but the underlying implementation accepts any image format supported by the image crate. This creates a mismatch between what users expect and what the feature actually does. Consider either updating this text to indicate support for all image formats, or adding validation in the GDScript to restrict inputs to JPG/JPEG files only.
| unique_name_in_owner = true | ||
| layout_mode = 2 | ||
| size_flags_horizontal = 3 | ||
| placeholder_text = "Select a JPG image..." |
There was a problem hiding this comment.
The placeholder text indicates "Select a JPG image..." but the converter can handle any image format (not just JPG). This is inconsistent with the actual implementation. Update this placeholder to reflect that other formats are also supported, or add validation to restrict to JPG/JPEG only.
| • Laplacian Pyramid (multi-scale) | ||
| • BC3/DXT5 Channel Packing (Terrain3D)" | ||
| • BC3/DXT5 Channel Packing (Terrain3D) | ||
| • Lossless JPG -> PNG Conversion" |
There was a problem hiding this comment.
The description states "Lossless JPG -> PNG Conversion" but JPEG is inherently a lossy format. The conversion itself doesn't introduce additional loss (it preserves the already-compressed JPEG data in PNG format), but calling it "lossless" is technically misleading since the source JPEG has already lost information during its compression. Consider revising to "JPG -> PNG Conversion (preserves JPEG quality)" or similar wording that's more technically accurate.
| • Lossless JPG -> PNG Conversion" | |
| • JPG -> PNG Conversion (preserves JPEG quality)" |
| mode_selector.add_item("🗺️ Terrain3D Packer (v0.0.2)", Mode.TERRAIN3D_PACKER) | ||
| mode_selector.add_item("🖼️ Standard PBR Generator", Mode.STANDARD_PBR) | ||
| mode_selector.add_item("🗺️ Terrain3D Packer", Mode.TERRAIN3D_PACKER) | ||
| mode_selector.add_item("♻️ Image Converter (JPG -> PNG)", Mode.IMAGE_CONVERTER) |
There was a problem hiding this comment.
The mode selector text indicates "JPG -> PNG" conversion specifically, but the implementation accepts any image format. This is inconsistent with the broader implementation. Either restrict the input to JPG/JPEG files only or update this text to reflect support for multiple formats (e.g., "♻️ Image Converter (to PNG)").
| mode_selector.add_item("♻️ Image Converter (JPG -> PNG)", Mode.IMAGE_CONVERTER) | |
| mode_selector.add_item("♻️ Image Converter (to PNG)", Mode.IMAGE_CONVERTER) |
| let output_path = if output_dir_str.is_empty() { | ||
| input_path_buf.parent().unwrap_or(Path::new("")).join(format!("{}.png", stem)) | ||
| } else { | ||
| PathBuf::from(&output_dir_str).join(format!("{}.png", stem)) | ||
| }; | ||
|
|
There was a problem hiding this comment.
There's no validation to prevent overwriting an existing PNG file with the same name. If the source is already a PNG or if a PNG with the same filename already exists in the output directory, it will be silently overwritten. Consider adding a check and either warning the user or appending a suffix to avoid accidental data loss.
| let output_path = if output_dir_str.is_empty() { | |
| input_path_buf.parent().unwrap_or(Path::new("")).join(format!("{}.png", stem)) | |
| } else { | |
| PathBuf::from(&output_dir_str).join(format!("{}.png", stem)) | |
| }; | |
| let mut output_path = if output_dir_str.is_empty() { | |
| input_path_buf.parent().unwrap_or(Path::new("")).join(format!("{}.png", stem)) | |
| } else { | |
| PathBuf::from(&output_dir_str).join(format!("{}.png", stem)) | |
| }; | |
| // Avoid overwriting an existing file by appending a numeric suffix if needed | |
| if output_path.exists() { | |
| let parent = output_path.parent().unwrap_or(Path::new("")).to_path_buf(); | |
| let base_stem = output_path | |
| .file_stem() | |
| .and_then(|s| s.to_str()) | |
| .unwrap_or("image"); | |
| let extension = output_path | |
| .extension() | |
| .and_then(|e| e.to_str()) | |
| .unwrap_or("png"); | |
| let mut counter = 1; | |
| loop { | |
| let candidate = parent.join(format!("{}_{}.{}", base_stem, counter, extension)); | |
| if !candidate.exists() { | |
| output_path = candidate; | |
| break; | |
| } | |
| counter += 1; | |
| } | |
| } |
| if not FileAccess.file_exists(input_path): | ||
| _show_error("File does not exist: " + input_path) | ||
| return | ||
|
|
There was a problem hiding this comment.
The validation only checks if the file exists, but doesn't verify if it's actually a JPG/JPEG file as indicated by the UI label and PR description. The file could be any format (PNG, BMP, etc.) and would still be "converted" to PNG. Consider adding file extension validation to ensure the input matches the advertised JPG/JPEG format, or update the UI/documentation to indicate support for any image format.
| var lower_path := input_path.to_lower() | |
| if not (lower_path.ends_with(".jpg") or lower_path.ends_with(".jpeg")): | |
| _show_error("Selected file must be a JPG or JPEG image.") | |
| return | |
| .to_string(); | ||
|
|
||
| let output_path = if output_dir_str.is_empty() { | ||
| input_path_buf.parent().unwrap_or(Path::new("")).join(format!("{}.png", stem)) |
There was a problem hiding this comment.
When the output directory is empty and the input file has no parent directory (e.g., a file in the root directory), unwrap_or returns an empty Path, which could lead to the output file being created in an unexpected location. Consider using a more robust fallback or returning an error if the parent directory cannot be determined.
| input_path_buf.parent().unwrap_or(Path::new("")).join(format!("{}.png", stem)) | |
| let base_dir = match input_path_buf.parent() { | |
| Some(parent) if !parent.as_os_str().is_empty() => parent.to_path_buf(), | |
| _ => match std::env::current_dir() { | |
| Ok(dir) => dir, | |
| Err(e) => { | |
| let _ = result.insert("error", format!("Failed to determine output directory: {}", e)); | |
| return result; | |
| } | |
| }, | |
| }; | |
| base_dir.join(format!("{}.png", stem)) |
Changes in v0.0.4
Added
Fixed