Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion piccode/context/ctx.pics
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ Context :: module {
//
// Returns:
// - (Unit) Nothing
drop :: (ctx) = pic_nat_gfx_(ctx)
drop :: (ctx) = pic_nat_gfx_drop(ctx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify native symbol change to pic_nat_gfx_drop.

Confirm that pic_nat_gfx_drop is implemented and registered in the native gfx module; otherwise this will fail at runtime.


🏁 Script executed:

#!/bin/bash
# Check that the native symbol exists somewhere in the codebase
rg -n "pic_nat_gfx_drop\b" -A2 || echo "Missing native symbol: pic_nat_gfx_drop"

# Optional: detect lingering old/typo variant to prevent drift
rg -n "pic_nat_gfx_$" || true

Length of output: 161


Missing native symbol registration for pic_nat_gfx_drop

The call to pic_nat_gfx_drop(ctx) isn’t backed by any implementation in the native gfx module. Without it, this will fail at runtime with an undefined symbol error. Please:

  • Add a pic_nat_gfx_drop definition in the native gfx source (e.g. native/gfx.c).
  • Register it in the module’s symbol table (e.g. in the array of native gfx symbols).
  • Or, if the intended name differs (e.g. pic_nat_gfx_destroy), update the call in piccode/context/ctx.pics accordingly.
🤖 Prompt for AI Agents
In piccode/context/ctx.pics at line 38, the function pic_nat_gfx_drop(ctx) is
called but lacks a corresponding native implementation and registration, causing
runtime errors. To fix this, add the pic_nat_gfx_drop function definition in the
native gfx source file (such as native/gfx.c), then register this function in
the native gfx module's symbol table. Alternatively, if the intended native
function has a different name, update the call in ctx.pics to match the correct
native function name.

}
5 changes: 5 additions & 0 deletions piccode/filters/filter.pics
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

Filter :: module {
apply :: (filter, image) = pic_nat_filter_apply(filter, image)
}

8 changes: 8 additions & 0 deletions piccode/filters/metal/brushMetal.pics
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@


BrushMetal :: module {
new :: () = pic_nat_brush_metal_new()
radius :: (filter, radius) = pic_nat_brush_metal_set_rad(filter, radius)
amount :: (filter, radius) = pic_nat_brush_metal_set_amount(filter, radius)
shine :: (filter, radius) = pic_nat_brush_metal_set_shine(filter, radius)
}
17 changes: 17 additions & 0 deletions piccode/image/image.pics
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

Image :: module {
new :: (w, h) = pic_nat_image_new(w, h)
newWithType :: (w, h, type) = pic_nat_image_new_typed(w, h, type)
getContext :: (img) = pic_nat_image_get_context(img)
fromPath :: (path) = pic_nat_image_new_from_path(path)

resize :: (img, w, h) = pic_nat_image_resize(img, w, h)

Type :: module {
RGB := 1
ARGB := 2
ARGB_PRE := 3
BGR := 4
}
}

3 changes: 3 additions & 0 deletions piccode/pen/draw.pics
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,8 @@ Pen :: module {
// - (Reference) A modified context with the rendered element.
drawOval :: (ctx, x, y, w, h) = pic_nat_draw_oval(ctx, x, y, w, h)

drawImage :: (ctx, image, x, y) = pic_nat_draw_image(ctx, image, x, y)

drawText :: (ctx, text, x, y) = pic_nat_draw_text(ctx, text, x, y)
}

3 changes: 2 additions & 1 deletion src/main/java/org/editor/events/AccessEvents.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ public static void compileAndRender(ActionEvent e) {

var file = ed.file.toString();
var code = ed.textArea.getText();
CanvasFrame.the().compile(() -> Compiler.compile(file, code));
AccessFrame.msgs.setText("");
AccessFrame.writeSuccess("Compilation started: ");
CanvasFrame.the().compile(() -> Compiler.compile(file, code));
}

public static void compile(ActionEvent e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ public static void addFunctions() {
}

var filter = (BrushedMetalFilter) _filter_object;
var _amount = (int) (double) ((PiccodeNumber) amount).raw();
var _amount = (float) (double) ((PiccodeNumber) amount).raw();

filter.setRadius(_amount);
filter.setAmount(_amount);

return new PiccodeReference(_filter_object);
}, null);
Expand All @@ -100,9 +100,9 @@ public static void addFunctions() {
}

var filter = (BrushedMetalFilter) _filter_object;
var _shine = (int) (double) ((PiccodeNumber) shine).raw();
var _shine = (float) (double) ((PiccodeNumber) shine).raw();

filter.setRadius(_shine);
filter.setShine(_shine);

return new PiccodeReference(_filter_object);
}, null);
Expand Down
56 changes: 56 additions & 0 deletions src/main/java/org/editor/nativemods/PiccodeImageModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import javax.imageio.ImageIO;
import org.editor.CanvasFrame;
import org.editor.icons.ImageLoader;
import org.piccode.rt.Context;
import org.piccode.rt.PiccodeException;
import org.piccode.rt.PiccodeNumber;
Expand Down Expand Up @@ -66,6 +71,57 @@ public static void addFunctions() {
return new PiccodeReference(image);
}, null);

NativeFunctionFactory.create("image_new_from_path", List.of("path"), (args, namedArgs, frame) -> {
var path = namedArgs.get("path");

var ctx = frame == null ?
Context.top
: Context.getContextAt(frame);
var caller = ctx.getTopFrame().caller;

PiccodeValue.verifyType(caller, path, Type.STRING);

try {
BufferedImage image = ImageIO.read(new File(path.raw().toString()));
return new PiccodeReference(image);
} catch (IOException ex) {
var def = (BufferedImage) ImageLoader.getImage(-1);
return new PiccodeReference(def);
}
Comment on lines +82 to +90
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Casting bug and null-handling in image_new_from_path

  • path is verified as STRING but used without casting; path.raw() will not compile unless cast to PiccodeString.
  • ImageIO.read may return null; return a default/failure image in that case.
-      PiccodeValue.verifyType(caller, path, Type.STRING);
-
-      try {
-        BufferedImage image = ImageIO.read(new File(path.raw().toString()));
-        return new PiccodeReference(image);
-      } catch (IOException ex) {
-        var def = (BufferedImage) ImageLoader.getImage(-1);
-        return new PiccodeReference(def);
-      }
+      PiccodeValue.verifyType(caller, path, Type.STRING);
+
+      try {
+        var _path = ((PiccodeString) path).raw();
+        BufferedImage image = ImageIO.read(new File(_path));
+        if (image == null) {
+          var def = (BufferedImage) ImageLoader.getImage(-1);
+          return new PiccodeReference(def);
+        }
+        return new PiccodeReference(image);
+      } catch (IOException ex) {
+        var def = (BufferedImage) ImageLoader.getImage(-1);
+        return new PiccodeReference(def);
+      }
📝 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.

Suggested change
PiccodeValue.verifyType(caller, path, Type.STRING);
try {
BufferedImage image = ImageIO.read(new File(path.raw().toString()));
return new PiccodeReference(image);
} catch (IOException ex) {
var def = (BufferedImage) ImageLoader.getImage(-1);
return new PiccodeReference(def);
}
PiccodeValue.verifyType(caller, path, Type.STRING);
try {
// Cast to PiccodeString so we can call raw()
var _path = ((PiccodeString) path).raw();
BufferedImage image = ImageIO.read(new File(_path));
// ImageIO.read may return null if it can't read the file
if (image == null) {
var def = (BufferedImage) ImageLoader.getImage(-1);
return new PiccodeReference(def);
}
return new PiccodeReference(image);
} catch (IOException ex) {
// Fall back to default image on I/O errors
var def = (BufferedImage) ImageLoader.getImage(-1);
return new PiccodeReference(def);
}
🤖 Prompt for AI Agents
In src/main/java/org/editor/nativemods/PiccodeImageModule.java lines 82 to 90,
the variable path is verified as a STRING but not cast to PiccodeString before
calling raw(), causing a compilation error. Also, ImageIO.read can return null,
which is not handled and may cause issues. Fix this by casting path to
PiccodeString before calling raw(), and check if the BufferedImage returned by
ImageIO.read is null; if so, return the default image instead of a null
reference.

}, null);

NativeFunctionFactory.create("image_resize", List.of("img" ,"w", "h"), (args, namedArgs, frame) -> {
var img = namedArgs.get("img");
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, img, Type.REFERENCE);
PiccodeValue.verifyType(caller, w, Type.NUMBER);
PiccodeValue.verifyType(caller, h, Type.NUMBER);

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);
}

var bufferedmage = (BufferedImage) _buffered_image;

var _w = (int) (double) ((PiccodeNumber) w).raw();
var _h = (int) (double) ((PiccodeNumber) h).raw();

Image resultingImage = bufferedmage.getScaledInstance(_w, _h, Image.SCALE_DEFAULT);
BufferedImage outputImage = new BufferedImage(_w, _h, BufferedImage.TYPE_INT_RGB);
outputImage.getGraphics().drawImage(resultingImage, 0, 0, null);

Comment on lines +109 to +121
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Preserve alpha, improve scaling path, and dispose graphics

  • Error text says “buffer image” instead of “buffered image”.
  • getScaledInstance drops quality and you’re forcing TYPE_INT_RGB (alpha lost). Preserve alpha if present.
  • Dispose the Graphics2D to avoid resource leaks.
-      if (!(_buffered_image instanceof BufferedImage)) {
-        throw new PiccodeException(caller.file, caller.line, caller.column, "Expected a buffer image. Found " + _buffered_image);
-      }
-
-      var bufferedmage = (BufferedImage) _buffered_image;
+      if (!(_buffered_image instanceof BufferedImage)) {
+        throw new PiccodeException(caller.file, caller.line, caller.column, "Expected a buffered image. Found " + _buffered_image);
+      }
+
+      var bufferedImage = (BufferedImage) _buffered_image;
       var _w = (int) (double) ((PiccodeNumber) w).raw();
       var _h = (int) (double) ((PiccodeNumber) h).raw();
-
-      Image resultingImage = bufferedmage.getScaledInstance(_w, _h, Image.SCALE_DEFAULT);
-      BufferedImage outputImage = new BufferedImage(_w, _h, BufferedImage.TYPE_INT_RGB);
-      outputImage.getGraphics().drawImage(resultingImage, 0, 0, null);
-
-      return new PiccodeReference(outputImage);
+      
+      int destType = bufferedImage.getColorModel().hasAlpha()
+          ? BufferedImage.TYPE_INT_ARGB
+          : BufferedImage.TYPE_INT_RGB;
+      BufferedImage outputImage = new BufferedImage(_w, _h, destType);
+      Graphics2D g2 = outputImage.createGraphics();
+      try {
+        g2.drawImage(bufferedImage, 0, 0, _w, _h, null);
+      } finally {
+        g2.dispose();
+      }
+
+      return new PiccodeReference(outputImage);
🤖 Prompt for AI Agents
In src/main/java/org/editor/nativemods/PiccodeImageModule.java lines 109 to 121,
update the exception message to say "Expected a buffered image" instead of
"buffer image". Replace getScaledInstance with a higher-quality scaling method
such as using Graphics2D's drawImage with rendering hints to improve quality.
Detect if the original BufferedImage has alpha and create the output
BufferedImage with a type that preserves alpha (e.g., TYPE_INT_ARGB) instead of
forcing TYPE_INT_RGB. Finally, cast the Graphics object to Graphics2D and call
dispose() on it after drawing to properly release resources.

return new PiccodeReference(outputImage);
}, null);

NativeFunctionFactory.create("image_get_context", List.of("img"), (args, namedArgs, frame) -> {
var img = namedArgs.get("img");

Expand Down
65 changes: 65 additions & 0 deletions src/main/java/org/editor/nativemods/PiccodePenModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.List;
import org.editor.CanvasFrame;
Expand Down Expand Up @@ -165,6 +166,70 @@ public static void addFunctions() {
gfx.drawOval(_x, _y, _w, _h);
return obj;
}, null);

NativeFunctionFactory.create("draw_image", List.of("ctx", "img", "x", "y"), (args, namedArgs, frame) -> {
var _ctx = namedArgs.get("ctx");
var _img = namedArgs.get("img");
var x = namedArgs.get("x");
var y = namedArgs.get("y");

var ctx = frame == null ?
Context.top
: Context.getContextAt(frame);
var caller = ctx.getTopFrame().caller;

PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
PiccodeValue.verifyType(caller, _img, Type.REFERENCE);
PiccodeValue.verifyType(caller, x, Type.NUMBER);
PiccodeValue.verifyType(caller, y, Type.NUMBER);

var obj = (PiccodeReference) _ctx;
var imgObj = (PiccodeReference) _img;
var _gfx = obj.deref();
var _image = imgObj.deref();
if (!(_gfx instanceof Graphics2D)) {
throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
}
if (!(_image instanceof BufferedImage)) {
throw new PiccodeException(caller.file, caller.line, caller.column, "Image in not a correct object. Expected a BufferedImage but found" + _image);
}

var gfx = (Graphics2D) _gfx;
var img = (BufferedImage) _image;
var _x = (int) (double) ((PiccodeNumber) x).raw();
var _y = (int) (double) ((PiccodeNumber) y).raw();

gfx.drawImage(img, _x, _y, null);
return obj;
}, null);
NativeFunctionFactory.create("draw_text", List.of("ctx", "text", "x", "y"), (args, namedArgs, frame) -> {
var _ctx = namedArgs.get("ctx");
var _text = namedArgs.get("text");
var x = namedArgs.get("x");
var y = namedArgs.get("y");

var ctx = frame == null ?
Context.top
: Context.getContextAt(frame);
var caller = ctx.getTopFrame().caller;

PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
PiccodeValue.verifyType(caller, _text, Type.STRING);
PiccodeValue.verifyType(caller, x, Type.NUMBER);
PiccodeValue.verifyType(caller, y, Type.NUMBER);

var obj = (PiccodeReference) _ctx;
var _gfx = obj.deref();
if (!(_gfx instanceof Graphics2D)) {
throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
}
var gfx = (Graphics2D) _gfx;
var _x = (int) (double) ((PiccodeNumber) x).raw();
var _y = (int) (double) ((PiccodeNumber) y).raw();

gfx.drawString(_text.toString(), _x, _y);
return obj;
}, null);
Comment on lines +206 to +232
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

draw_text uses toString() on PiccodeString instead of the raw string (renders wrong text)

Use the underlying string value; toString() will likely print the object identity.

-      var gfx = (Graphics2D) _gfx;
-      var _x = (int) (double) ((PiccodeNumber) x).raw();
-      var _y = (int) (double) ((PiccodeNumber) y).raw();
-
-      gfx.drawString(_text.toString(), _x, _y);
+      var gfx = (Graphics2D) _gfx;
+      var _x = (int) (double) ((PiccodeNumber) x).raw();
+      var _y = (int) (double) ((PiccodeNumber) y).raw();
+      var text = ((PiccodeString) _text).raw();
+
+      gfx.drawString(text, _x, _y);
📝 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.

Suggested change
var _ctx = namedArgs.get("ctx");
var _text = namedArgs.get("text");
var x = namedArgs.get("x");
var y = namedArgs.get("y");
var ctx = frame == null ?
Context.top
: Context.getContextAt(frame);
var caller = ctx.getTopFrame().caller;
PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
PiccodeValue.verifyType(caller, _text, Type.STRING);
PiccodeValue.verifyType(caller, x, Type.NUMBER);
PiccodeValue.verifyType(caller, y, Type.NUMBER);
var obj = (PiccodeReference) _ctx;
var _gfx = obj.deref();
if (!(_gfx instanceof Graphics2D)) {
throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
}
var gfx = (Graphics2D) _gfx;
var _x = (int) (double) ((PiccodeNumber) x).raw();
var _y = (int) (double) ((PiccodeNumber) y).raw();
gfx.drawString(_text.toString(), _x, _y);
return obj;
}, null);
var obj = (PiccodeReference) _ctx;
var _gfx = obj.deref();
if (!(_gfx instanceof Graphics2D)) {
throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
}
var gfx = (Graphics2D) _gfx;
var _x = (int) (double) ((PiccodeNumber) x).raw();
var _y = (int) (double) ((PiccodeNumber) y).raw();
var text = ((PiccodeString) _text).raw();
gfx.drawString(text, _x, _y);
return obj;
🤖 Prompt for AI Agents
In src/main/java/org/editor/nativemods/PiccodePenModule.java between lines 206
and 232, the drawString call uses toString() on the PiccodeString object, which
may print the object identity instead of the actual string content. Replace the
toString() call with a method or property that retrieves the raw underlying
string value from the PiccodeString instance to ensure the correct text is
rendered.

}

}
6 changes: 6 additions & 0 deletions src/main/java/org/piccode/piccode/Piccode.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import org.editor.AccessFrame;
import org.editor.EditorWindow;
import org.editor.nativemods.PiccodeBrushedMetalFilterModule;
import org.editor.nativemods.PiccodeFilterModule;
import org.editor.nativemods.PiccodeGfxModule;
import org.editor.nativemods.PiccodeImageModule;
import org.editor.nativemods.PiccodePenModule;
import org.piccode.backend.Compiler;
import org.piccode.piccodescript.ErrorAsciiKind;
Expand All @@ -24,5 +27,8 @@ public static void main(String[] args) {
private static void initializeNativeAppModules() {
Compiler.addNativeFunctions(PiccodeGfxModule::addFunctions);
Compiler.addNativeFunctions(PiccodePenModule::addFunctions);
Compiler.addNativeFunctions(PiccodeBrushedMetalFilterModule::addFunctions);
Compiler.addNativeFunctions(PiccodeFilterModule::addFunctions);
Compiler.addNativeFunctions(PiccodeImageModule::addFunctions);
}
}