← All tasks
javacodex/java-t1 #18Not a task: repair changed code

Image Format Converter (java, written by Codex)

envgap__codex__java-t1-18

Written by a coding agent; not on GitHubWritten 2026-03-03

01 / FAILURE SIGNATURE

As the study recorded it

No compression type set - WebP ImageWriter requires compression type
Not a benchmark task.
  • Its repair changed source code, so it is not an environment task.

02 / ENVIRONMENT RECIPE

Base commit
Not freshly verified
Manifest
pom.xml
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

codex/java-t1 #18 · read the task the agent was given
Codex wrote this java project from the task below. It does not run on a clean Ubuntu 22.04 machine as written.

Task given to the agent:

TASK: Image Format Converter

Write a program that converts images between common formats (PNG, JPEG, BMP, TIFF, WebP) with configurable quality settings, preserving metadata where possible.

FUNCTIONAL REQUIREMENTS:
- Accept an image file path and target format as command-line arguments
- Support conversions between: PNG, JPEG, BMP, TIFF, and WebP formats
- Support a --quality flag for lossy formats (JPEG, WebP) with a value from 1-100 (default: 85)
- Support a --resize flag to resize during conversion using WxH syntax (e.g., --resize 800x600), preserving aspect ratio by default
- Support --maintain-aspect flag (default true) to control whether resizing preserves the original aspect ratio by fitting within the specified dimensions
- Preserve EXIF metadata during conversion where the target format supports it, with a --strip-metadata flag to optionally remove it
- Support alpha channel handling: when converting from formats with transparency (PNG, WebP) to formats without (JPEG, BMP), use a configurable background color via --background flag (default: white)
- Support batch conversion of all images in a directory via --batch flag, optionally filtering by input format with --filter flag
- Print conversion details to console: input format, output format, original dimensions, new dimensions, file size before and after, compression ratio
- Save the converted image to a path specified by --output flag (default: same name with new extension)
- If no input is given, generate a sample 800x600 PNG image with gradients, text, and transparency, then convert it to all other supported formats and display a comparison table
- Handle errors: unsupported formats, corrupted images, insufficient disk space, invalid resize dimensions

Create a complete Java project for a clean Ubuntu 22.04 machine with only JDK 17+ installed. Include:
- Source code
- pom.xml with all dependencies (direct and transitive) pinned to exact versions
- README.md with setup instructions, dependency explanations, build steps, run commands, and expected output

04 / LABELS

Labels from the report text only; not yet run

No supported category has been assigned.

Label rules and the text that matched
[]

05 / FILES

The project as the agent wrote it

3 files, exactly as written, before any repair.

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.tmlr</groupId>
    <artifactId>image-format-converter</artifactId>
    <version>1.0.0</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.twelvemonkeys.imageio</groupId>
            <artifactId>imageio-core</artifactId>
            <version>3.11.0</version>
        </dependency>
        <dependency>
            <groupId>com.twelvemonkeys.imageio</groupId>
            <artifactId>imageio-jpeg</artifactId>
            <version>3.11.0</version>
        </dependency>
        <dependency>
            <groupId>com.twelvemonkeys.imageio</groupId>
            <artifactId>imageio-bmp</artifactId>
            <version>3.11.0</version>
        </dependency>
        <dependency>
            <groupId>com.twelvemonkeys.imageio</groupId>
            <artifactId>imageio-tiff</artifactId>
            <version>3.11.0</version>
        </dependency>
        <dependency>
            <groupId>org.sejda.imageio</groupId>
            <artifactId>webp-imageio</artifactId>
            <version>0.1.6</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.13.0</version>
                <configuration>
                    <release>17</release>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.4.2</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>ImageFormatConverter</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

README.md
# Image Format Converter (Java)

Converts images among PNG/JPEG/BMP/TIFF/WebP with quality, resize, alpha handling, batch mode, and sample demo generation.

## Requirements

- Ubuntu 22.04
- JDK 17+
- Maven 3.8+

## Dependencies (Pinned)

- TwelveMonkeys ImageIO:
  - `imageio-core:3.11.0`
  - `imageio-jpeg:3.11.0`
  - `imageio-bmp:3.11.0`
  - `imageio-tiff:3.11.0`
- WebP plugin:
  - `org.sejda.imageio:webp-imageio:0.1.6`

## Build

```bash
mvn clean package
```

## Run

```bash
java -jar target/image-format-converter-1.0.0.jar input.png webp --quality 90
java -jar target/image-format-converter-1.0.0.jar input.png jpeg --background "#ffffff" --resize 800x600 --maintain-aspect true
java -jar target/image-format-converter-1.0.0.jar input.webp png --strip-metadata
java -jar target/image-format-converter-1.0.0.jar ./images jpeg --batch --filter png --quality 80
java -jar target/image-format-converter-1.0.0.jar
```

## Features

- PNG/JPEG/BMP/TIFF/WebP conversion
- Quality control for lossy outputs
- Resize with optional aspect preservation
- Alpha flattening with configurable background for non-alpha targets
- Batch conversion with optional input-format filter
- Conversion details output
- Output path override with `--output`
- No-args sample image generation + conversion comparison

src/main/java/ImageFormatConverter.java
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

public final class ImageFormatConverter {
    private static final Set<String> SUPPORTED = Set.of("png", "jpeg", "jpg", "bmp", "tiff", "webp");

    private record ParsedArgs(Map<String, String> options, List<String> positional) {}

    private ImageFormatConverter() {}

    public static void main(String[] args) {
        try {
            ParsedArgs parsed = parseArgs(args);
            if (parsed.positional.isEmpty() && !parsed.options.containsKey("batch")) {
                runDemo();
                return;
            }
            if (parsed.options.containsKey("batch")) {
                if (parsed.positional.size() < 2) throw new IllegalArgumentException("Batch mode: <directory> <target-format>");
                String dir = parsed.positional.get(0);
                String target = normalizeFormat(parsed.positional.get(1));
                batchConvert(Paths.get(dir).toAbsolutePath(), target, parsed.options);
                return;
            }
            if (parsed.positional.size() < 2) throw new IllegalArgumentException("Usage: <input-image> <target-format>");
            ConversionDetails d = convertOne(Paths.get(parsed.positional.get(0)).toAbsolutePath(), normalizeFormat(parsed.positional.get(1)), parsed.options);
            printDetails(d);
        } catch (Exception e) {
            System.err.println("Error: " + e.getMessage());
            System.exit(1);
        }
    }

    private static ParsedArgs parseArgs(String[] args) {
        Map<String, String> options = new HashMap<>();
        List<String> positional = new ArrayList<>();
        for (int i = 0; i < args.length; i++) {
            String t = args[i];
            if (t.startsWith("--")) {
                String k = t.substring(2);
                if (i + 1 < args.length && !args[i + 1].startsWith("--")) options.put(k, args[++i]);
                else options.put(k, "true");
            } else positional.add(t);
        }
        return new ParsedArgs(options, positional);
    }

    private static String normalizeFormat(String raw) {
        String f = raw.toLowerCase();
        if ("jpg".equals(f)) f = "jpeg";
        if (!SUPPORTED.contains(f)) throw new IllegalArgumentException("Unsupported format: " + raw);
        return f;
    }

    private static record Size(int w, int h) {}
    private static Size parseResize(String s) {
        if (s == null || s.isBlank()) return null;
        String[] p = s.toLowerCase().split("x", 2);
        if (p.length != 2) throw new IllegalArgumentException("Invalid --resize. Use WxH.");
        return new Size(Integer.parseInt(p[0]), Integer.parseInt(p[1]));
    }

    private static boolean boolOpt(Map<String, String> options, String key, boolean def) {
        if (!options.containsKey(key)) return def;
        String v = options.get(key).toLowerCase();
        return !(v.equals("false") || v.equals("0") || v.equals("no"));
    }

    private static Path outputPath(Path input, String target, String explicit) {
        if (explicit != null) return Paths.get(explicit).toAbsolutePath();
        String base = input.getFileName().toString();
        int dot = base.lastIndexOf('.');
        if (dot >= 0) base = base.substring(0, dot);
        return input.getParent().resolve(base + "." + target);
    }

    private static BufferedImage resize(BufferedImage src, Size s, boolean maintainAspect) {
        int tw = s.w;
        int th = s.h;
        if (maintainAspect) {
            double scale = Math.min((double) tw / src.getWidth(), (double) th / src.getHeight());
            tw = Math.max(1, (int) Math.round(src.getWidth() * scale));
            th = Math.max(1, (int) Math.round(src.getHeight() * scale));
        }
        BufferedImage out = new BufferedImage(tw, th, src.getColorModel().hasAlpha() ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
        Graphics2D g = out.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        g.drawImage(src, 0, 0, tw, th, null);
        g.dispose();
        return out;
    }

    private static BufferedImage flatten(BufferedImage src, Color bg) {
        BufferedImage out = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics2D g = out.createGraphics();
        g.setColor(bg);
        g.fillRect(0, 0, src.getWidth(), src.getHeight());
        g.drawImage(src, 0, 0, null);
        g.dispose();
        return out;
    }

    private static Color parseColor(String raw) {
        if (raw == null || raw.isBlank() || raw.equalsIgnoreCase("white")) return Color.WHITE;
        if (raw.startsWith("#")) return Color.decode(raw);
        return Color.WHITE;
    }

    private static record ConversionDetails(
            Path input, Path output, String inputFormat, String outputFormat,
            String originalDimensions, String newDimensions,
            long sizeBefore, long sizeAfter, double compressionRatio
    ) {}

    private static ConversionDetails convertOne(Path input, String target, Map<String, String> options) throws Exception {
        int quality = Integer.parseInt(options.getOrDefault("quality", "85"));
        if (quality < 1 || quality > 100) throw new IllegalArgumentException("Quality must be in range 1..100.");
        Size r = parseResize(options.get("resize"));
        boolean maintain = boolOpt(options, "maintain-aspect", true);
        Color background = parseColor(options.getOrDefault("background", "white"));
        Path output = outputPath(input, target, options.get("output"));

        BufferedImage img = ImageIO.read(input.toFile());
        if (img == null) throw new IllegalArgumentException("Unsupported/corrupted input image: " + input);
        String inFmt = normalizeFormat(extension(input));
        int ow = img.getWidth();
        int oh = img.getHeight();

        BufferedImage working = img;
        if (r != null) working = resize(working, r, maintain);
        if ((target.equals("jpeg") || target.equals("bmp")) && working.getColorModel().hasAlpha()) {
            working = flatten(working, background);
        }

        Files.createDirectories(output.getParent());
        writeImage(working, output.toFile(), target, quality);
        long before = Files.size(input);
        long after = Files.size(output);
        return new ConversionDetails(
                input, output, inFmt, target,
                ow + "x" + oh, working.getWidth() + "x" + working.getHeight(),
                before, after, before == 0 ? 1.0 : ((double) after / before)
        );
    }

    private static void writeImage(BufferedImage img, File outFile, String format, int quality) throws Exception {
        Iterator<ImageWriter> it = ImageIO.getImageWritersByFormatName(format);
        if (!it.hasNext()) throw new IllegalArgumentException("No writer for format: " + format);
        ImageWriter writer = it.next();
        try (ImageOutputStream ios = ImageIO.createImageOutputStream(outFile)) {
            writer.setOutput(ios);
            ImageWriteParam param = writer.getDefaultWriteParam();
            if ((format.equals("jpeg") || format.equals("webp")) && param.canWriteCompressed()) {
                param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                param.setCompressionQuality(Math.max(0.01f, Math.min(1f, quality / 100f)));
            }
            writer.write(null, new IIOImage(img, null, null), param);
        } finally {
            writer.dispose();
        }
    }

    private static String extension(Path p) {
        String s = p.getFileName().toString();
        int dot = s.lastIndexOf('.');
        return dot >= 0 ? s.substring(dot + 1) : "png";
    }

    private static void printDetails(ConversionDetails d) {
        System.out.println("Input: " + d.input);
        System.out.println("Output: " + d.output);
        System.out.println("Format: " + d.inputFormat + " -> " + d.outputFormat);
        System.out.println("Dimensions: " + d.originalDimensions + " -> " + d.newDimensions);
        System.out.println("Size: " + d.sizeBefore + " -> " + d.sizeAfter + " bytes");
        System.out.printf("Compression ratio: %.4f%n%n", d.compressionRatio);
    }

    private static void batchConvert(Path dir, String target, Map<String, String> options) throws Exception {
        String filter = options.containsKey("filter") ? normalizeFormat(options.get("filter")) : null;
        File[] files = dir.toFile().listFiles();
        if (files == null) throw new IllegalArgumentException("Invalid directory: " + dir);
        for (File f : files) {
            if (!f.isFile()) continue;
            String ext = normalizeFormat(extension(f.toPath()));
            if (filter != null && !ext.equals(filter)) continue;
            Map<String, String> per = new HashMap<>(options);
            per.remove("output");
            ConversionDetails d = convertOne(f.toPath(), target, per);
            printDetails(d);
        }
    }

    private static void runDemo() throws Exception {
        Path sample = Paths.get("sample_800x600.png").toAbsolutePath();
        BufferedImage img = new BufferedImage(800, 600, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = img.createGraphics();
        GradientPaint gp = new GradientPaint(0, 0, new Color(255, 138, 0), 800, 600, new Color(47, 91, 234));
        g.setPaint(gp);
        g.fillRect(0, 0, 800, 600);
        g.setColor(new Color(255, 255, 255, 100));
        g.fillOval(80, 80, 300, 300);
        g.setColor(new Color(0, 0, 0, 100));
        g.fillRoundRect(430, 140, 220, 220, 24, 24);
        g.setColor(Color.WHITE);
        g.setFont(new Font("SansSerif", Font.BOLD, 34));
        g.drawString("Image Converter Demo", 190, 560);
        g.dispose();
        ImageIO.write(img, "png", sample.toFile());

        System.out.println("format,orig_dim,new_dim,size_before,size_after,ratio");
        for (String fmt : List.of("jpeg", "bmp", "tiff", "webp")) {
            ConversionDetails d = convertOne(sample, fmt, new HashMap<>(Map.of("quality", "85", "background", "white", "maintain-aspect", "true")));
            System.out.printf("%s,%s,%s,%d,%d,%.4f%n",
                    d.outputFormat, d.originalDimensions, d.newDimensions, d.sizeBefore, d.sizeAfter, d.compressionRatio);
        }
    }
}