-
Notifications
You must be signed in to change notification settings - Fork 0
Fix the rampant memory usage that was caused by compiling per… #36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Caution Review failedThe pull request is closed. WalkthroughThis update refactors the rendering logic in Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant AccessEvents
participant CanvasFrame
participant Compiler
User->>AccessEvents: compileAndRender(file, code)
AccessEvents->>CanvasFrame: the().compile(() -> Compiler.compile(file, code))
CanvasFrame->>CanvasFrame: Create offscreen image, run lambda
CanvasFrame->>CanvasFrame: Store render image
CanvasFrame->>User: (On repaint) paintComponent draws render image
sequenceDiagram
participant PiccodeRuntime
participant PiccodeImageModule
participant PiccodeFilterModule
participant PiccodeBrushedMetalFilterModule
participant JavaImageAPI
PiccodeRuntime->>PiccodeImageModule: image_new(w, h)
PiccodeImageModule->>JavaImageAPI: new BufferedImage(w, h)
JavaImageAPI-->>PiccodeImageModule: BufferedImage
PiccodeImageModule-->>PiccodeRuntime: PiccodeReference(BufferedImage)
PiccodeRuntime->>PiccodeFilterModule: filter_apply(filter, image)
PiccodeFilterModule->>JavaImageAPI: filter.filter(image)
JavaImageAPI-->>PiccodeFilterModule: Filtered BufferedImage
PiccodeFilterModule-->>PiccodeRuntime: PiccodeReference(Filtered BufferedImage)
PiccodeRuntime->>PiccodeBrushedMetalFilterModule: brush_metal_new()
PiccodeBrushedMetalFilterModule->>JavaImageAPI: new BrushedMetalFilter()
JavaImageAPI-->>PiccodeBrushedMetalFilterModule: BrushedMetalFilter
PiccodeBrushedMetalFilterModule-->>PiccodeRuntime: PiccodeReference(BrushedMetalFilter)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
Note 🔌 MCP (Model Context Protocol) integration is now available in Early Access!Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context. 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🧹 Nitpick comments (3)
src/main/java/org/editor/events/AccessEvents.java (1)
26-26: Nit: message typo"Cannot compiler unsaved code" → "Cannot compile unsaved code".
-EditorWindow.current_file.setText("Cannot compiler unsaved code"); +EditorWindow.current_file.setText("Cannot compile unsaved code");Also applies to: 43-43
src/main/java/org/editor/nativemods/PiccodeFilterModule.java (1)
41-43: Clarify error message and type nameMinor: "Expected a buffer image" → "Expected a BufferedImage" for clarity.
-throw new PiccodeException(caller.file, caller.line, caller.column, "Expected a buffer image. Found " + _buffered_image); +throw new PiccodeException(caller.file, caller.line, caller.column, "Expected a BufferedImage. Found " + _buffered_image);src/main/java/org/editor/nativemods/PiccodeImageModule.java (1)
69-88: Typo fix and message clarity; document disposal of the returned Graphics2D
- Rename local var for readability.
- Message: "BufferedImage".
- Consider a note in docs that callers must dispose contexts via
gfx_drop.var _buffered_image = ((PiccodeReference)img).deref(); if (!(_buffered_image instanceof BufferedImage)) { - throw new PiccodeException(caller.file, caller.line, caller.column, "Expected a buffer image. Found " + _buffered_image); + throw new PiccodeException(caller.file, caller.line, caller.column, "Expected a BufferedImage. Found " + _buffered_image); } -var bufferedmage = (BufferedImage) _buffered_image; -var gfx = bufferedmage.createGraphics(); +var bufferedImage = (BufferedImage) _buffered_image; +var gfx = bufferedImage.createGraphics(); return new PiccodeReference(gfx);Please ensure documentation or examples mention disposing the returned context: use the existing
gfx_dropnative function to avoid leaks.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/main/java/org/editor/CanvasFrame.java(6 hunks)src/main/java/org/editor/events/AccessEvents.java(1 hunks)src/main/java/org/editor/nativemods/PiccodeBrushedMetalFilterModule.java(1 hunks)src/main/java/org/editor/nativemods/PiccodeFilterModule.java(1 hunks)src/main/java/org/editor/nativemods/PiccodeImageModule.java(1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: hexaredecimal
PR: Glimmr-Lang/PicassoCode#26
File: tests/test_readme_validation.py:233-236
Timestamp: 2025-08-06T23:34:19.770Z
Learning: The PicassoCode project README has specific formatting requirements including: app icon at "./src/main/resources/applogo/appicon.png" with 40% width, repository URL "gitgithub.com:hexaredecimal/Piccode.git", JAR file "Piccode-1.0-SNAPSHOT-jar-with-dependencies.jar", and elaborate ASCII art with "Creativity + Logic + Math" tagline in the license section.
📚 Learning: 2025-05-14T10:49:23.453Z
Learnt from: hexaredecimal
PR: Glimmr-Lang/PicassoCode#1
File: src/main/java/org/editor/EditorWindow.java:204-209
Timestamp: 2025-05-14T10:49:23.453Z
Learning: The VLDocking library (com.vlsolutions.swing.docking) expects dockable components to be owned by the frame while being managed by the docking desktop. Unlike standard Swing components that can only have one parent, the DockingDesktop acts as a manager for dockable components rather than being their parent in the component hierarchy.
Applied to files:
src/main/java/org/editor/CanvasFrame.java
📚 Learning: 2025-05-14T10:49:23.453Z
Learnt from: hexaredecimal
PR: Glimmr-Lang/PicassoCode#1
File: src/main/java/org/editor/EditorWindow.java:204-209
Timestamp: 2025-05-14T10:49:23.453Z
Learning: The VLDocking library (com.vlsolutions.swing.docking) has a distinct component management model where UI components are added to both the frame's content pane AND registered with the DockingDesktop. Unlike standard Swing where components can only have one parent, VLDocking expects dockables to be owned by the frame while being managed by the docking desktop for layout, docking states, and behaviors.
Applied to files:
src/main/java/org/editor/CanvasFrame.java
📚 Learning: 2025-08-06T23:34:19.770Z
Learnt from: hexaredecimal
PR: Glimmr-Lang/PicassoCode#26
File: tests/test_readme_validation.py:233-236
Timestamp: 2025-08-06T23:34:19.770Z
Learning: The PicassoCode project README has specific formatting requirements including: app icon at "./src/main/resources/applogo/appicon.png" with 40% width, repository URL "gitgithub.com:hexaredecimal/Piccode.git", JAR file "Piccode-1.0-SNAPSHOT-jar-with-dependencies.jar", and elaborate ASCII art with "Creativity + Logic + Math" tagline in the license section.
Applied to files:
src/main/java/org/editor/nativemods/PiccodeImageModule.java
🧬 Code Graph Analysis (3)
src/main/java/org/editor/events/AccessEvents.java (2)
src/main/java/org/editor/CanvasFrame.java (1)
CanvasFrame(43-348)src/main/java/org/editor/AccessFrame.java (1)
AccessFrame(40-279)
src/main/java/org/editor/CanvasFrame.java (1)
src/main/java/org/editor/nativemods/PiccodeGfxModule.java (3)
PiccodeGfxModule(23-106)args(31-63)args(26-29)
src/main/java/org/editor/nativemods/PiccodeImageModule.java (2)
src/main/java/org/editor/nativemods/PiccodePenModule.java (6)
PiccodePenModule(23-170)addFunctions(25-168)args(61-93)args(95-133)args(135-167)args(27-59)src/main/java/org/editor/nativemods/PiccodeGfxModule.java (4)
addFunctions(25-104)PiccodeGfxModule(23-106)args(31-63)args(64-83)
🪛 GitHub Actions: Java CI with Maven
src/main/java/org/editor/nativemods/PiccodeBrushedMetalFilterModule.java
[error] 3-3: Compilation error: package com.jhlabs.image does not exist.
[error] 28-102: Compilation error: cannot find symbol class BrushedMetalFilter in multiple locations within the file.
🔇 Additional comments (6)
src/main/java/org/editor/CanvasFrame.java (3)
78-78: Good initializationPriming the offscreen buffer once in the constructor via
compile(() -> null)is a sensible default.
116-118: Offscreen buffer draw is correctRendering the cached
renderimage onto the panel decouples painting from compilation and is the right direction for performance.
299-301: Good: disposing grid graphicsExplicitly disposing the grid Graphics2D is correct and prevents native resource leaks.
src/main/java/org/editor/nativemods/PiccodeFilterModule.java (1)
49-52: Filter application flow looks goodType verification and
BufferedImageOp.filterusage are correct. Returning a new reference to the filtered image is appropriate.src/main/java/org/editor/nativemods/PiccodeBrushedMetalFilterModule.java (2)
28-30: Creation function looks fineCreating and returning a
BrushedMetalFilterwrapped inPiccodeReferenceis correct.
32-56: Radius setter is correctType checks and
setRadiususage look good.
| public void compile(Supplier<PiccodeValue> fx) { | ||
| int width = getWidth(); | ||
| int height = getHeight(); | ||
| if (width <= 0 || height <= 0) { | ||
| return; | ||
| } | ||
|
|
||
| render = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); | ||
|
|
||
| // Get the Graphics2D object | ||
| gfx = render.createGraphics(); | ||
|
|
||
| // Enable transparency by drawing a fully transparent background | ||
| gfx.setComposite(AlphaComposite.Clear); | ||
| gfx.fillRect(0, 0, width, height); | ||
|
|
||
| gfx.setColor(Color.BLACK); | ||
| // Switch back to normal composite mode for drawing | ||
| gfx.setComposite(AlphaComposite.SrcOver); | ||
|
|
||
| fx.get(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Dispose previous gfx and reuse buffers; avoid leaks and allocations; repaint after compile
The current compile always allocates a new BufferedImage, reassigns gfx, and never disposes the previous Graphics2D. This leaks native resources and increases GC pressure. Also, if the component size hasn't changed, you can reuse the existing buffer.
Proposed fix:
- Dispose the previous
gfxbefore creating a new one (or reuse existing when size unchanged). - Reuse
renderif width/height are unchanged and just clear it. - Set useful rendering hints on
gfx. - Trigger a repaint after updating the offscreen buffer.
public void compile(Supplier<PiccodeValue> fx) {
int width = getWidth();
int height = getHeight();
if (width <= 0 || height <= 0) {
return;
}
-
- render = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
-
- // Get the Graphics2D object
- gfx = render.createGraphics();
-
- // Enable transparency by drawing a fully transparent background
- gfx.setComposite(AlphaComposite.Clear);
- gfx.fillRect(0, 0, width, height);
-
- gfx.setColor(Color.BLACK);
- // Switch back to normal composite mode for drawing
- gfx.setComposite(AlphaComposite.SrcOver);
-
- fx.get();
+ // Reuse buffer if size unchanged; otherwise dispose and recreate
+ boolean recreate = render == null || render.getWidth() != width || render.getHeight() != height;
+ if (recreate) {
+ if (gfx != null) {
+ try { gfx.dispose(); } catch (Exception ignore) {}
+ }
+ render = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ gfx = render.createGraphics();
+ // Optional: quality hints for higher fidelity drawing to the offscreen buffer
+ gfx.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ gfx.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
+ gfx.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+ }
+
+ // Clear buffer to full transparency
+ gfx.setComposite(AlphaComposite.Clear);
+ gfx.fillRect(0, 0, width, height);
+ gfx.setComposite(AlphaComposite.SrcOver);
+ gfx.setColor(Color.BLACK);
+
+ // Execute supplied drawing/compilation logic
+ fx.get();
+
+ // Ensure the latest image is presented
+ repaint(getVisibleRect());
}Note: If you plan to allow long-running drawing from background threads into gfx, consider guarding gfx and render with a lock and using a snapshot for painting to avoid tearing/races.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public void compile(Supplier<PiccodeValue> fx) { | |
| int width = getWidth(); | |
| int height = getHeight(); | |
| if (width <= 0 || height <= 0) { | |
| return; | |
| } | |
| render = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); | |
| // Get the Graphics2D object | |
| gfx = render.createGraphics(); | |
| // Enable transparency by drawing a fully transparent background | |
| gfx.setComposite(AlphaComposite.Clear); | |
| gfx.fillRect(0, 0, width, height); | |
| gfx.setColor(Color.BLACK); | |
| // Switch back to normal composite mode for drawing | |
| gfx.setComposite(AlphaComposite.SrcOver); | |
| fx.get(); | |
| } | |
| public void compile(Supplier<PiccodeValue> fx) { | |
| int width = getWidth(); | |
| int height = getHeight(); | |
| if (width <= 0 || height <= 0) { | |
| return; | |
| } | |
| // Reuse buffer if size unchanged; otherwise dispose and recreate | |
| boolean recreate = render == null | |
| || render.getWidth() != width | |
| || render.getHeight() != height; | |
| if (recreate) { | |
| if (gfx != null) { | |
| try { | |
| gfx.dispose(); | |
| } catch (Exception ignore) { | |
| } | |
| } | |
| render = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); | |
| gfx = render.createGraphics(); | |
| // Optional: quality hints for higher-fidelity drawing to the offscreen buffer | |
| gfx.setRenderingHint(RenderingHints.KEY_ANTIALIASING, | |
| RenderingHints.VALUE_ANTIALIAS_ON); | |
| gfx.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, | |
| RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); | |
| gfx.setRenderingHint(RenderingHints.KEY_RENDERING, | |
| RenderingHints.VALUE_RENDER_QUALITY); | |
| } | |
| // Clear buffer to full transparency | |
| gfx.setComposite(AlphaComposite.Clear); | |
| gfx.fillRect(0, 0, width, height); | |
| gfx.setComposite(AlphaComposite.SrcOver); | |
| gfx.setColor(Color.BLACK); | |
| // Execute supplied drawing/compilation logic | |
| fx.get(); | |
| // Ensure the latest image is presented | |
| repaint(getVisibleRect()); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/org/editor/CanvasFrame.java lines 302 to 323, the compile
method leaks native resources by not disposing the previous gfx Graphics2D
object and always allocating a new BufferedImage even if the size is unchanged.
Fix this by checking if the current render buffer matches the component size; if
so, reuse it by clearing its content instead of creating a new one. If a new
BufferedImage is needed, dispose the old gfx before creating a new one. Also,
set appropriate rendering hints on gfx for better quality and call repaint()
after updating the buffer to refresh the display.
| CanvasFrame.file = file; | ||
| CanvasFrame.code = code; | ||
| CanvasFrame.start = true; | ||
| CanvasFrame.the().compile(() -> Compiler.compile(file, code)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Avoid blocking the EDT during compilation
Compiler.compile(file, code) likely does non-trivial work. Invoking it inside compile(...) from an ActionEvent runs on the Swing EDT and can freeze the UI. Consider splitting CanvasFrame.compile into:
- a lightweight EDT setup that (re)creates the offscreen buffer/graphics, then
- executing
fx.get()on a background worker, drawing into the offscreenGraphics2D, and finallyrepaint()on EDT.
At minimum, ensure heavy parts of fx.get() aren't executed on the EDT.
🏁 Script executed:
#!/bin/bash
set -e
# Locate CanvasFrame source file
file=$(rg -l "class CanvasFrame" src | head -n1)
echo "CanvasFrame file: $file"
# Show occurrences of compile( within CanvasFrame, with context
rg -n "compile(" -C3 "$file"Length of output: 363
🏁 Script executed:
#!/bin/bash
set -e
# Inspect all compile( calls in CanvasFrame.java using fixed-string search to avoid regex errors
file="src/main/java/org/editor/CanvasFrame.java"
echo "CanvasFrame file: $file"
rg -n -F 'compile(' -C3 "$file"Length of output: 702
🏁 Script executed:
#!/bin/bash
set -e
file="src/main/java/org/editor/CanvasFrame.java"
echo "Extracting `compile(Supplier<PiccodeValue> fx)` implementation:"
sed -n '300,360p' "$file"Length of output: 1572
Offload heavy compilation from the Swing EDT
Currently, CanvasFrame.compile(Supplier<PiccodeValue> fx) (src/main/java/org/editor/CanvasFrame.java around line 302) does all setup and then calls fx.get() on the EDT. When fx.get() wraps Compiler.compile(file, code), the UI will freeze until compilation finishes.
To fix:
- In
compile(...), perform only the offscreen-buffer creation andGraphics2Dsetup on the EDT. - Move the blocking
fx.get()call into a background worker (e.g.,SwingWorkeror anExecutorService). - In the worker’s completion callback, invoke
repaint(getVisibleRect())on the EDT to update the display.
Example sketch using SwingWorker:
public void compileAsync(Supplier<PiccodeValue> fx) {
int w = getWidth(), h = getHeight();
if (w <= 0 || h <= 0) return;
// EDT: prepare offscreen buffer
render = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
gfx = render.createGraphics();
gfx.setComposite(AlphaComposite.Clear);
gfx.fillRect(0, 0, w, h);
gfx.setComposite(AlphaComposite.SrcOver);
gfx.setColor(Color.BLACK);
// Background: run heavy work
new SwingWorker<PiccodeValue, Void>() {
@Override
protected PiccodeValue doInBackground() {
return fx.get(); // compiles off EDT
}
@Override
protected void done() {
// EDT: repaint with new contents
try { get(); } catch (Exception ignored) {}
repaint(getVisibleRect());
}
}.execute();
}Then update your call in AccessEvents to use compileAsync(...) instead of compile(...).
🤖 Prompt for AI Agents
In src/main/java/org/editor/events/AccessEvents.java at line 32, the call to
CanvasFrame.the().compile(...) runs the heavy compilation on the Swing EDT,
causing UI freezes. To fix this, update CanvasFrame to add a
compileAsync(Supplier<PiccodeValue>) method that does offscreen buffer setup on
the EDT, then runs fx.get() in a SwingWorker background thread, and repaints on
completion. Then change this line to call compileAsync(...) instead of
compile(...), ensuring compilation happens off the EDT and the UI remains
responsive.
| @@ -0,0 +1,112 @@ | |||
| package org.editor.nativemods; | |||
|
|
|||
| import com.jhlabs.image.BrushedMetalFilter; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Build failing: missing JHLabs Filters dependency
CI error: “package com.jhlabs.image does not exist”. Add the JHLabs Filters library to your build (Maven/Gradle) or vendor the jar.
If using Maven, add the dependency to pom.xml once confirmed, e.g.:
<dependency>
<groupId><!-- com.jhlabs or correct groupId --></groupId>
<artifactId><!-- filters or correct artifactId --></artifactId>
<version><!-- latest stable version --></version>
</dependency>🌐 Web query:
What are the correct Maven coordinates (groupId/artifactId/version) for the JHLabs image filters containing com.jhlabs.image.BrushedMetalFilter?
💡 Result:
The correct Maven coordinates for the JHLabs image filters library containing com.jhlabs.image.BrushedMetalFilter are:
- groupId: com.jhlabs
- artifactId: filters
- version: 2.0.235-1
A typical Maven dependency block would look like:
<dependency>
<groupId>com.jhlabs</groupId>
<artifactId>filters</artifactId>
<version>2.0.235-1</version>
</dependency>This version is the most current and recommended for use from Maven Central, according to multiple authoritative sources[4][6][10]. The class com.jhlabs.image.BrushedMetalFilter is included in this artifact[5].
Alternative version:
- Slightly older versions, such as 2.0.235, may also contain the filter but 2.0.235-1 is newer and preferred[6][10].
Notes:
- The library is licensed under the Apache 2.0 License[10].
- The official homepage for more information is [http://www.jhlabs.com/ip/filters/][10].
- No additional compile dependencies are required for basic usage[10].
Citations:
- 1: https://central.sonatype.com/artifact/com.jhlabs/filters/2.0.235
- 2: https://jar-download.com/artifacts/com.jhlabs/filters/2.0.235-1/source-code/com/jhlabs/image/ShadeFilter.java
- 3: https://maven.apache.org/guides/mini/guide-naming-conventions.html
- 4: https://central.sonatype.com/artifact/com.jhlabs/filters
- 5: https://blog.csdn.net/weixin_34217656/article/details/114119117
- 6: https://mvnrepository.com/artifact/com.jhlabs/filters
- 7: https://jar-download.com/artifacts/com.jhlabs/filters/2.0.235-1/source-code
- 8: http://www.jhlabs.com/ip/filters/
- 9: https://mvnrepository.com/artifact/com.jhlabs
- 10: https://mvnrepository.com/artifact/com.jhlabs/filters/2.0.235-1
🏁 Script executed:
#!/bin/bash
# Locate all usages of BrushedMetalFilter to inspect setter methods
rg -n 'BrushedMetalFilter' -n src/main/javaLength of output: 1195
🏁 Script executed:
#!/bin/bash
# Inspect all setter calls on the BrushedMetalFilter instance
rg -n "filter\.set" -n src/main/java/org/editor/nativemods/PiccodeBrushedMetalFilterModule.javaLength of output: 195
🌐 Web query:
List the setter methods available on com.jhlabs.image.BrushedMetalFilter in version 2.0.235-1
💡 Result:
The setter methods available on com.jhlabs.image.BrushedMetalFilter in version 2.0.235-1 are not listed explicitly in the provided search results, but essential parameters and likely corresponding setters are documented in the JHLabs Brushed Metal Filter documentation[5]. The filter has the following configurable properties:
- Color (the color of the metal)
- Radius (the "radius" of the blur)
- Amount (the amount of noise to add, range 0-1)
- Monochrome (true if the noise should be monochrome)
- Shine (the amount of shine to add, range 0-1)
Based on standard JavaBeans conventions, the corresponding setter methods are highly likely to be:
setColor(int color)setRadius(float radius)setAmount(float amount)setMonochrome(boolean monochrome)setShine(float shine)
This inference is grounded in the fact that these parameters are explicitly documented as the adjustable parameters for the filter in the official documentation[5], and the naming pattern is consistent with other JHLabs filters[1].
Summary Table of Expected Setter Methods
| Property | Setter Method | Parameter Type |
|---|---|---|
| Color | setColor | int |
| Radius | setRadius | float |
| Amount | setAmount | float |
| Monochrome | setMonochrome | boolean |
| Shine | setShine | float |
If you require the precise method signatures from the actual source, examining the source code for version 2.0.235-1 directly would guarantee accuracy[2]. However, all available documentation and usage examples point to the existence of these methods with the names and types shown above[5][1].
Citations:
- 1: http://www.jhlabs.com/ip/filters/
- 2: https://jar-download.com/artifacts/com.jhlabs/filters/2.0.235-1/source-code
- 3: https://www.youtube.com/watch?v=htKdKASIIfw
- 4: https://central.sonatype.com/artifact/com.jhlabs/filters/2.0.235
- 5: http://www.jhlabs.com/ip/brushed_metal.html
- 6: https://mvnrepository.com/artifact/com.jhlabs/filters/2.0.235-1
- 7: https://mvnrepository.com/artifact/com.jhlabs/filters
Add JHLabs Filters dependency & correct BrushedMetalFilter setters
Ensure you’ve added the JHLabs Filters library so the import resolves, and fix two incorrect setter calls for amount and shine.
• In your build (Maven), add to pom.xml:
<dependency>
<groupId>com.jhlabs</groupId>
<artifactId>filters</artifactId>
<version>2.0.235-1</version>
</dependency>• In PiccodeBrushedMetalFilterModule.java, update the two misplaced setRadius calls:
– At line 79:
- filter.setRadius(_amount);
+ filter.setAmount(_amount);– At line 105:
- filter.setRadius(_shine);
+ filter.setShine(_shine);The original filter.setRadius(radius) (line 53) is correct; only the amount and shine setters were misplaced.
🧰 Tools
🪛 GitHub Actions: Java CI with Maven
[error] 3-3: Compilation error: package com.jhlabs.image does not exist.
🤖 Prompt for AI Agents
In src/main/java/org/editor/nativemods/PiccodeBrushedMetalFilterModule.java at
line 3, ensure the JHLabs Filters library is added to your Maven pom.xml as a
dependency with groupId com.jhlabs, artifactId filters, and version 2.0.235-1 to
resolve the import. Then, in the same file around lines 79 and 105, replace the
incorrect calls to filter.setRadius with the correct setters filter.setAmount
and filter.setShine respectively, keeping the original filter.setRadius call at
line 53 unchanged.
| NativeFunctionFactory.create("brush_metal_set_amount", List.of("filter", "amount"), (args, namedArgs, frame) -> { | ||
| var _filter = namedArgs.get("filter"); | ||
| var amount = namedArgs.get("amount"); | ||
|
|
||
| var ctx = frame == null | ||
| ? Context.top | ||
| : Context.getContextAt(frame); | ||
| var caller = ctx.getTopFrame().caller; | ||
|
|
||
| PiccodeValue.verifyType(caller, _filter, Type.REFERENCE); | ||
| PiccodeValue.verifyType(caller, amount, Type.NUMBER); | ||
|
|
||
| var obj = (PiccodeReference) _filter; | ||
| var _filter_object = obj.deref(); | ||
| if (!(_filter_object instanceof BrushedMetalFilter)) { | ||
| throw new PiccodeException(caller.file, caller.line, caller.column, "Filter is not a correct object."); | ||
| } | ||
|
|
||
| var filter = (BrushedMetalFilter) _filter_object; | ||
| var _amount = (int) (double) ((PiccodeNumber) amount).raw(); | ||
|
|
||
| filter.setRadius(_amount); | ||
|
|
||
| return new PiccodeReference(_filter_object); | ||
| }, null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Bug: using setRadius for 'amount'; use the correct setter and type
brush_metal_set_amount calls filter.setRadius(_amount). That’s incorrect. Use the actual amount setter (likely setAmount) and match parameter type (amount is typically float/double in JHLabs).
- var _amount = (int) (double) ((PiccodeNumber) amount).raw();
-
- filter.setRadius(_amount);
+ var _amount = (float) (double) ((PiccodeNumber) amount).raw();
+ filter.setAmount(_amount);Please confirm the exact setter signature for BrushedMetalFilter amount and adjust the cast accordingly.
🌐 Web query:
What is the method signature of setAmount in com.jhlabs.image.BrushedMetalFilter?
💡 Result:
The BrushedMetalFilter class from the JH Labs Java Image Filters library includes a method named setAmount. This method allows you to adjust the intensity of the brushed metal effect applied to an image. The method signature is:
public void setAmount(float amount)Here, amount is a floating-point value that specifies the strength of the effect. A higher value results in a more pronounced brushed metal appearance.
For more details on the BrushedMetalFilter class and its methods, you can refer to the JH Labs Java Image Filters repository on GitHub.
Use setAmount(float) instead of setRadius(int) in brush_metal_set_amount
We’ve confirmed that BrushedMetalFilter#setAmount is declared as:
public void setAmount(float amount)Update the code in src/main/java/org/editor/nativemods/PiccodeBrushedMetalFilterModule.java (lines 58–82) accordingly:
- var _amount = (int) (double) ((PiccodeNumber) amount).raw();
- filter.setRadius(_amount);
+ var _amount = (float) (double) ((PiccodeNumber) amount).raw();
+ filter.setAmount(_amount);🤖 Prompt for AI Agents
In src/main/java/org/editor/nativemods/PiccodeBrushedMetalFilterModule.java
between lines 58 and 82, the method brush_metal_set_amount incorrectly calls
setRadius(int) on BrushedMetalFilter. Replace this call with setAmount(float) by
converting the amount argument to a float instead of an int, and then invoke
filter.setAmount with that float value.
| NativeFunctionFactory.create("brush_metal_set_shine", List.of("filter", "shine"), (args, namedArgs, frame) -> { | ||
| var _filter = namedArgs.get("filter"); | ||
| var shine = namedArgs.get("shine"); | ||
|
|
||
| var ctx = frame == null | ||
| ? Context.top | ||
| : Context.getContextAt(frame); | ||
| var caller = ctx.getTopFrame().caller; | ||
|
|
||
| PiccodeValue.verifyType(caller, _filter, Type.REFERENCE); | ||
| PiccodeValue.verifyType(caller, shine, Type.NUMBER); | ||
|
|
||
| var obj = (PiccodeReference) _filter; | ||
| var _filter_object = obj.deref(); | ||
| if (!(_filter_object instanceof BrushedMetalFilter)) { | ||
| throw new PiccodeException(caller.file, caller.line, caller.column, "Filter is not a correct object."); | ||
| } | ||
|
|
||
| var filter = (BrushedMetalFilter) _filter_object; | ||
| var _shine = (int) (double) ((PiccodeNumber) shine).raw(); | ||
|
|
||
| filter.setRadius(_shine); | ||
|
|
||
| return new PiccodeReference(_filter_object); | ||
| }, null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Bug: using setRadius for 'shine'; use the correct setter and type
brush_metal_set_shine also calls setRadius. Use the shine setter (likely setShine) with the correct type (likely float).
- var _shine = (int) (double) ((PiccodeNumber) shine).raw();
-
- filter.setRadius(_shine);
+ var _shine = (float) (double) ((PiccodeNumber) shine).raw();
+ filter.setShine(_shine);Please verify BrushedMetalFilter API for the correct setter name and parameter type.
🏁 Script executed:
#!/bin/bash
# Search for the BrushedMetalFilter class to inspect its API
rg "class BrushedMetalFilter" -A 100
# Check for any setShine methods in the codebase
rg "setShine" -n
# Confirm existing setRadius usages on BrushedMetalFilter
rg "setRadius" -nLength of output: 390
🌐 Web query:
com.jhlabs.image.BrushedMetalFilter Javadoc methods list
💡 Result:
The com.jhlabs.image.BrushedMetalFilter class provides image processing functionality to generate a brushed metal texture. While direct, complete Javadoc for its methods is not available in the provided search results, typical JHLabs image filters—including BrushedMetalFilter—expose a consistent set of methods based on Java image filter conventions and documentation[1][6].
Here is the most likely list of methods (based on JHLabs filter conventions and third-party documentation):
BrushedMetalFilter Methods (typical):
-
filter(BufferedImage src, BufferedImage dest)
Applies the filter to a source image and produces a destination image. -
setColor(Color color)
Sets the base color of the brushed metal texture. -
getColor()
Returns the current base color setting. -
setBrushLength(float length)
Sets the length of the brush strokes in the texture. -
getBrushLength()
Returns the currently set brush length. -
setAmount(float amount)
Sets the amount of noise or intensity in the texture effect. -
getAmount()
Returns the current noise/intensity amount. -
setMonochrome(boolean mono)
Enables or disables a monochrome effect for the brushed metal. -
isMonochrome()
Returns if the monochrome effect is enabled. -
setShine(float shine)
Adjusts how shiny the metal effect appears. -
getShine()
Returns the currently set shine value. -
Other inherited or utility methods from
BufferedImageOp(e.g.,createCompatibleDestImage,getBounds2D,getPoint2D) are typically present in JHLabs filters[1].
Context and Details:
- JHLabs filters commonly use
setXXX()and correspondinggetXXX()methods for each configurable parameter[1]. - The main processing is typically performed by the
filter()method, which takes input and outputBufferedImageinstances and returns the result[1]. - Parameters like color, brush length, amount, monochrome, and shine allow precise control over the metal texture, as described in official documentation and articles on JHLabs[6].
If you need method signatures or more technical documentation, access to the actual source code (e.g., via GitHub or the official JHLabs site) is recommended. However, the methods listed above reflect the standard configuration pattern for this class within the JHLabs filter library[1][6].
Citations:
- 1: http://www.jhlabs.com/ip/filters/
- 2: https://show.docjava.com/book/cgij/jdoc/index.html?gnu%2Fio%2FCommPortIdentifierInterface.html
- 3: https://www.docjava.com/book/cgij/jdoc/com/jhlabs/image/package-frame.html
- 4: https://show.docjava.com/book/cgij/jdoc/index.html?j3d%2Fexamples%2Fappearance%2Ftexture%2Fnoise%2FImprovedNoise.html
- 5: https://www.oracle.com/hk/technical-resources/articles/java/javadoc-tool.html
- 6: http://www.jhlabs.com/ip/filters/BrushedMetalFilter.html
- 7: https://github.com/ajmas/JH-Labs-Java-Image-Filters/blob/master/src/com/jhlabs/image/BlockFilter.java
Replace setRadius with setShine(float) in the shine setter
Based on the JHLabs BrushedMetalFilter API, setShine(float) is the correct method and it accepts a float, not an int.
• File: src/main/java/org/editor/nativemods/PiccodeBrushedMetalFilterModule.java
Lines: 84–108 (inside the "brush_metal_set_shine" factory)
- var _shine = (int) (double) ((PiccodeNumber) shine).raw();
- filter.setRadius(_shine);
+ var _shine = (float) ((PiccodeNumber) shine).raw();
+ filter.setShine(_shine);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| NativeFunctionFactory.create("brush_metal_set_shine", List.of("filter", "shine"), (args, namedArgs, frame) -> { | |
| var _filter = namedArgs.get("filter"); | |
| var shine = namedArgs.get("shine"); | |
| var ctx = frame == null | |
| ? Context.top | |
| : Context.getContextAt(frame); | |
| var caller = ctx.getTopFrame().caller; | |
| PiccodeValue.verifyType(caller, _filter, Type.REFERENCE); | |
| PiccodeValue.verifyType(caller, shine, Type.NUMBER); | |
| var obj = (PiccodeReference) _filter; | |
| var _filter_object = obj.deref(); | |
| if (!(_filter_object instanceof BrushedMetalFilter)) { | |
| throw new PiccodeException(caller.file, caller.line, caller.column, "Filter is not a correct object."); | |
| } | |
| var filter = (BrushedMetalFilter) _filter_object; | |
| var _shine = (int) (double) ((PiccodeNumber) shine).raw(); | |
| filter.setRadius(_shine); | |
| return new PiccodeReference(_filter_object); | |
| }, null); | |
| NativeFunctionFactory.create("brush_metal_set_shine", List.of("filter", "shine"), (args, namedArgs, frame) -> { | |
| var _filter = namedArgs.get("filter"); | |
| var shine = namedArgs.get("shine"); | |
| var ctx = frame == null | |
| ? Context.top | |
| : Context.getContextAt(frame); | |
| var caller = ctx.getTopFrame().caller; | |
| PiccodeValue.verifyType(caller, _filter, Type.REFERENCE); | |
| PiccodeValue.verifyType(caller, shine, Type.NUMBER); | |
| var obj = (PiccodeReference) _filter; | |
| var _filter_object = obj.deref(); | |
| if (!(_filter_object instanceof BrushedMetalFilter)) { | |
| throw new PiccodeException(caller.file, caller.line, caller.column, "Filter is not a correct object."); | |
| } | |
| var filter = (BrushedMetalFilter) _filter_object; | |
| var _shine = (float) ((PiccodeNumber) shine).raw(); | |
| filter.setShine(_shine); | |
| return new PiccodeReference(_filter_object); | |
| }, null); |
🤖 Prompt for AI Agents
In src/main/java/org/editor/nativemods/PiccodeBrushedMetalFilterModule.java
lines 84 to 108, replace the call to filter.setRadius(_shine) with
filter.setShine(_shine) and ensure _shine is converted to a float instead of an
int. This aligns with the JHLabs BrushedMetalFilter API which requires
setShine(float) for setting the shine value.
| NativeFunctionFactory.create("image_new", List.of("w", "h"), (args, namedArgs, frame) -> { | ||
| var w = namedArgs.get("w"); | ||
| var h = namedArgs.get("h"); | ||
|
|
||
| var ctx = frame == null ? | ||
| Context.top | ||
| : Context.getContextAt(frame); | ||
| var caller = ctx.getTopFrame().caller; | ||
|
|
||
| PiccodeValue.verifyType(caller, w, Type.NUMBER); | ||
| PiccodeValue.verifyType(caller, h, Type.NUMBER); | ||
|
|
||
| var _w = (int) (double) ((PiccodeNumber) w).raw(); | ||
| var _h = (int) (double) ((PiccodeNumber) h).raw(); | ||
|
|
||
| var image = new BufferedImage(_w, _h, BufferedImage.TYPE_INT_ARGB); | ||
| return new PiccodeReference(image); | ||
| }, null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Validate image dimensions and surface IllegalArgumentException as PiccodeException
Creating a BufferedImage with non-positive dimensions throws IllegalArgumentException. Validate inputs and raise a PiccodeException with caller info for better diagnostics.
PiccodeValue.verifyType(caller, w, Type.NUMBER);
PiccodeValue.verifyType(caller, h, Type.NUMBER);
var _w = (int) (double) ((PiccodeNumber) w).raw();
var _h = (int) (double) ((PiccodeNumber) h).raw();
-var image = new BufferedImage(_w, _h, BufferedImage.TYPE_INT_ARGB);
+if (_w <= 0 || _h <= 0) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Image dimensions must be > 0. Got: " + _w + "x" + _h);
+}
+BufferedImage image;
+try {
+ image = new BufferedImage(_w, _h, BufferedImage.TYPE_INT_ARGB);
+} catch (IllegalArgumentException iae) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Invalid image dimensions: " + _w + "x" + _h);
+}
return new PiccodeReference(image);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| NativeFunctionFactory.create("image_new", List.of("w", "h"), (args, namedArgs, frame) -> { | |
| var w = namedArgs.get("w"); | |
| var h = namedArgs.get("h"); | |
| var ctx = frame == null ? | |
| Context.top | |
| : Context.getContextAt(frame); | |
| var caller = ctx.getTopFrame().caller; | |
| PiccodeValue.verifyType(caller, w, Type.NUMBER); | |
| PiccodeValue.verifyType(caller, h, Type.NUMBER); | |
| var _w = (int) (double) ((PiccodeNumber) w).raw(); | |
| var _h = (int) (double) ((PiccodeNumber) h).raw(); | |
| var image = new BufferedImage(_w, _h, BufferedImage.TYPE_INT_ARGB); | |
| return new PiccodeReference(image); | |
| }, null); | |
| NativeFunctionFactory.create("image_new", List.of("w", "h"), (args, namedArgs, frame) -> { | |
| var w = namedArgs.get("w"); | |
| var h = namedArgs.get("h"); | |
| var ctx = frame == null | |
| ? Context.top | |
| : Context.getContextAt(frame); | |
| var caller = ctx.getTopFrame().caller; | |
| PiccodeValue.verifyType(caller, w, Type.NUMBER); | |
| PiccodeValue.verifyType(caller, h, Type.NUMBER); | |
| var _w = (int) (double) ((PiccodeNumber) w).raw(); | |
| var _h = (int) (double) ((PiccodeNumber) h).raw(); | |
| if (_w <= 0 || _h <= 0) { | |
| throw new PiccodeException( | |
| caller.file, caller.line, caller.column, | |
| "Image dimensions must be > 0. Got: " + _w + "x" + _h | |
| ); | |
| } | |
| BufferedImage image; | |
| try { | |
| image = new BufferedImage(_w, _h, BufferedImage.TYPE_INT_ARGB); | |
| } catch (IllegalArgumentException iae) { | |
| throw new PiccodeException( | |
| caller.file, caller.line, caller.column, | |
| "Invalid image dimensions: " + _w + "x" + _h | |
| ); | |
| } | |
| return new PiccodeReference(image); | |
| }, null); |
🤖 Prompt for AI Agents
In src/main/java/org/editor/nativemods/PiccodeImageModule.java around lines 28
to 45, the code creates a BufferedImage without validating that width and height
are positive, which can throw IllegalArgumentException. Add checks after
extracting _w and _h to verify they are greater than zero. If not, throw a new
PiccodeException including the caller information and a clear message about
invalid image dimensions to improve error handling and diagnostics.
| NativeFunctionFactory.create("image_new_typed", List.of("w", "h", "type"), (args, namedArgs, frame) -> { | ||
| var w = namedArgs.get("w"); | ||
| var h = namedArgs.get("h"); | ||
| var type = namedArgs.get("type"); | ||
|
|
||
| var ctx = frame == null ? | ||
| Context.top | ||
| : Context.getContextAt(frame); | ||
| var caller = ctx.getTopFrame().caller; | ||
|
|
||
| PiccodeValue.verifyType(caller, w, Type.NUMBER); | ||
| PiccodeValue.verifyType(caller, h, Type.NUMBER); | ||
| PiccodeValue.verifyType(caller, type, Type.NUMBER); | ||
|
|
||
| var _w = (int) (double) ((PiccodeNumber) w).raw(); | ||
| var _h = (int) (double) ((PiccodeNumber) h).raw(); | ||
| var _type = (int) (double) ((PiccodeNumber) type).raw(); | ||
|
|
||
| var image = new BufferedImage(_w, _h, _type); | ||
| return new PiccodeReference(image); | ||
| }, null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Same validation for typed image creation
Apply the same dimension validation and exception mapping for image_new_typed.
var _w = (int) (double) ((PiccodeNumber) w).raw();
var _h = (int) (double) ((PiccodeNumber) h).raw();
var _type = (int) (double) ((PiccodeNumber) type).raw();
-var image = new BufferedImage(_w, _h, _type);
+if (_w <= 0 || _h <= 0) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Image dimensions must be > 0. Got: " + _w + "x" + _h);
+}
+BufferedImage image;
+try {
+ image = new BufferedImage(_w, _h, _type);
+} catch (IllegalArgumentException iae) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Invalid image args: " + _w + "x" + _h + ", type=" + _type);
+}
return new PiccodeReference(image);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| NativeFunctionFactory.create("image_new_typed", List.of("w", "h", "type"), (args, namedArgs, frame) -> { | |
| var w = namedArgs.get("w"); | |
| var h = namedArgs.get("h"); | |
| var type = namedArgs.get("type"); | |
| var ctx = frame == null ? | |
| Context.top | |
| : Context.getContextAt(frame); | |
| var caller = ctx.getTopFrame().caller; | |
| PiccodeValue.verifyType(caller, w, Type.NUMBER); | |
| PiccodeValue.verifyType(caller, h, Type.NUMBER); | |
| PiccodeValue.verifyType(caller, type, Type.NUMBER); | |
| var _w = (int) (double) ((PiccodeNumber) w).raw(); | |
| var _h = (int) (double) ((PiccodeNumber) h).raw(); | |
| var _type = (int) (double) ((PiccodeNumber) type).raw(); | |
| var image = new BufferedImage(_w, _h, _type); | |
| return new PiccodeReference(image); | |
| }, null); | |
| NativeFunctionFactory.create("image_new_typed", List.of("w", "h", "type"), (args, namedArgs, frame) -> { | |
| var w = namedArgs.get("w"); | |
| var h = namedArgs.get("h"); | |
| var type = namedArgs.get("type"); | |
| var ctx = frame == null ? | |
| Context.top | |
| : Context.getContextAt(frame); | |
| var caller = ctx.getTopFrame().caller; | |
| PiccodeValue.verifyType(caller, w, Type.NUMBER); | |
| PiccodeValue.verifyType(caller, h, Type.NUMBER); | |
| PiccodeValue.verifyType(caller, type, Type.NUMBER); | |
| var _w = (int) (double) ((PiccodeNumber) w).raw(); | |
| var _h = (int) (double) ((PiccodeNumber) h).raw(); | |
| var _type = (int) (double) ((PiccodeNumber) type).raw(); | |
| if (_w <= 0 || _h <= 0) { | |
| throw new PiccodeException(caller.file, caller.line, caller.column, | |
| "Image dimensions must be > 0. Got: " + _w + "x" + _h); | |
| } | |
| BufferedImage image; | |
| try { | |
| image = new BufferedImage(_w, _h, _type); | |
| } catch (IllegalArgumentException iae) { | |
| throw new PiccodeException(caller.file, caller.line, caller.column, | |
| "Invalid image args: " + _w + "x" + _h + ", type=" + _type); | |
| } | |
| return new PiccodeReference(image); | |
| }, null); |
🤖 Prompt for AI Agents
In src/main/java/org/editor/nativemods/PiccodeImageModule.java lines 47 to 67,
the image_new_typed function lacks dimension validation and exception mapping
present in other image creation functions. Add checks to ensure width and height
are positive integers and wrap the BufferedImage creation in a try-catch block
to map exceptions to Piccode exceptions, providing consistent validation and
error handling.
Before we were compiling the source code each frame in order to draw on the screen. Now I simply render to a bufferedImage that is the same size as the render window and then draw that image on top of the grid.
Summary by CodeRabbit
New Features
Refactor
Bug Fixes