# How to save an image as PNG or JPEG

> Write an Image to disk (timestamped file) or to an OutputStream, as PNG or JPEG.

Source: https://crossfly.dev/docs/cookbook/images/salvar-imagem — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# How to save an image as PNG or JPEG

**Purpose.** Persist an `Image` to disk. CrossFly offers two levels: *convenience*
(saves a file named by timestamp and returns the path) and *full control* (you provide the
`OutputStream`).

### Convenience (saves the file automatically)
- `String toPng(String... prefix)` — saves a PNG named `<prefix><timestamp>.png`. On desktop it writes to `Device.homeFolder`; on mobile it writes to `/sdcard/`. Returns the saved path, or `null` on error (the error is handled internally via `Debug.handle`). The `prefix` is **optional**.
- `String toJpg(int... quality)` — saves a JPEG named by timestamp. `quality` is **optional** (default `90`); `100` = no compression, `85` = good compression, `60` = heavy compression but with noise. Returns the path, or `null` on error.

### Full control (you provide the stream)
- `void toPng(OutputStream s) throws ImageException, IOException` — writes the PNG to the stream. The PNG is 24 BPP, or 32 BPP if the image has an alpha channel.
- `void toJpg(OutputStream s, int quality) throws ImageException, IOException` — writes the JPEG to the stream with the given quality (0–100). **The stream is closed** by the call.

> Watch out for the overloads: `toPng(String...)`/`toJpg(int...)` **save a file** and swallow exceptions
> (return `null`). Whereas `toPng(OutputStream)`/`toJpg(OutputStream,int)` **propagate**
> `ImageException`/`IOException`. Choose according to whether or not you want to handle the error.

## Complete example (copy, paste and run)

```java
package com.exemplo;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import dev.crossfly.ui.*;                     // Window
import dev.crossfly.ui.layout.*;             // VerticalLayout
import dev.crossfly.ui.image.*;              // Image, ImageException
import dev.crossfly.util.*;                  // Debug

/** Runnable main screen. Point the launcher (classname) and run: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      try {
         Image img = new Image("/sdcard/foto.jpg");   // your already-loaded or generated image

         // 1) Convenience: file with timestamp; returns the path
         String pngPath = img.toPng("thumb_");   // e.g.: .../thumb_20260706153000.png
         String jpgPath = img.toJpg(85);         // quality 85
         Debug.log("PNG: " + pngPath + " | JPG: " + jpgPath);

         // 2) Full control: you decide the destination and handle errors
         try (OutputStream out = new FileOutputStream("/sdcard/saida.png")) {
            img.toPng(out);                      // stream is closed by the call; propagates exceptions
         } catch (ImageException | IOException e) {
            // handle write/encoding failure
         }
      } catch (ImageException | IOException e) {
         // failed to load the source image
      }
   }
}
```

> The snippet below shows **only the core**. To run it, place it inside the `MainWindow` skeleton above.

## Minimal snippet

```java
// verified in source: Image.java toPng/toJpg — lines 800, 811, 1454, 1479
import dev.crossfly.ui.image.*;   // Image, ImageException

Image img = /* your already-loaded or generated image */;

// 1) Convenience: file with timestamp; returns the path
String pngPath = img.toPng("thumb_");   // e.g.: .../thumb_20260706153000.png
String jpgPath = img.toJpg(85);         // quality 85

// 2) Full control: you decide the destination and handle errors
try (java.io.OutputStream out = new java.io.FileOutputStream("/sdcard/saida.png")) {
   img.toPng(out);                      // stream is closed by the call; propagates exceptions
} catch (ImageException | java.io.IOException e) {
   // handle write/encoding failure
}
```

## Dive deeper
- Sibling recipe: [thumbnail.md](/docs/cookbook/images/thumbnail) — generate the thumbnail and then save it with this recipe.
- Sibling recipe: [screenshot.md](/docs/cookbook/images/screenshot) — capture the screen and save it with `toPng()`.
