# Cómo generar un thumbnail (miniatura)

> Redimensionar una imagen a una miniatura con resize(), eligiendo modo y calidad.

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

# Cómo generar un thumbnail (miniatura)

**Propósito.** Reducir una imagen grande a una miniatura persistente (una **nueva** `Image` con
píxeles realmente reducidos), en vez de solo escalar en el dibujo. Esto ahorra memoria de textura
y ancho de banda de GPU cuando muestras muchas miniaturas.

## APIs usadas
- `Image resize(int newWidth, int newHeight, ResizeMode mode, ResizeQuality quality) throws ImageException` — punto de entrada principal. Devuelve una **nueva** `Image`; la original no se modifica.
- `Image resize(int newWidth, int newHeight, ResizeMode mode)` — overload que usa `ResizeQuality.SMOOTH`.
- `Image resize(int newWidth, int newHeight)` — overload que usa `ResizeMode.STRETCH` + `ResizeQuality.SMOOTH`.
- `Image resizeWidth(int newWidth[, ResizeQuality])` / `Image resizeHeight(int newHeight[, ResizeQuality])` — redimensionan preservando la proporción a partir de un ancho o alto objetivo (ideal para thumbnails).

### Enums (verificados en `UIConstants`)
`ResizeMode { STRETCH, FIT, FILL }`
- `STRETCH` — fuerza exactamente `w`×`h` (puede distorsionar).
- `FIT` — mantiene la proporción y cabe **entera** dentro de `w`×`h` (letter-box; el resultado puede ser menor que el objetivo).
- `FILL` — mantiene la proporción y **llena** `w`×`h`, recortando el exceso por el centro.

`ResizeQuality { FAST, SMOOTH, HARDWARE }`
- `FAST` — muestreo rápido (nearest); úsalo para cachés en tiempo real.
- `SMOOTH` — remuestreo de alta calidad; úsalo para thumbnails que quedan guardados/mostrados.
- `HARDWARE` — mantiene los píxeles de origen y delega la escala a la GPU en el dibujo (no reduce la memoria de pixmap).

## Ejemplo completo (copiar, pegar y ejecutar)

```java
package com.exemplo;

import java.io.IOException;

import dev.crossfly.ui.*;                     // Window, ImageView, UIConstants.ImageSize
import dev.crossfly.ui.layout.*;             // VerticalLayout, LAY_AUTO
import dev.crossfly.ui.image.*;              // Image, ImageException
import dev.crossfly.ui.UIConstants.ResizeMode;
import dev.crossfly.ui.UIConstants.ResizeQuality;

/** Pantalla principal ejecutable. Apunta el launcher (classname) y corre: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      try {
         Image original = new Image("/sdcard/foto.jpg");

         // Miniatura 128x128 rellenando y recortando el excedente, con buena calidad:
         Image thumb = original.resize(128, 128, ResizeMode.FILL, ResizeQuality.SMOOTH);

         // Alternativa preservando la proporción a partir del ancho (sin recortar):
         Image thumbW = original.resizeWidth(128, ResizeQuality.SMOOTH);

         add(new ImageView(thumb, UIConstants.ImageSize.ORIGINAL), LAY_AUTO);
         add(new ImageView(thumbW, UIConstants.ImageSize.ORIGINAL), LAY_AUTO);
      } catch (ImageException | IOException e) {
         // archivo no encontrado o fallo en el resize (ej.: "Out of memory")
      }
   }
}
```

> El fragmento de abajo muestra **solo el núcleo**. Para ejecutarlo, colócalo en el esqueleto `MainWindow` de arriba.

## Fragmento mínimo

```java
// verificado en la fuente: Image.java resize(...) línea 870; enums en UIConstants (795, 806)
import dev.crossfly.ui.image.*;   // Image, ImageException
import dev.crossfly.ui.UIConstants.ResizeMode;
import dev.crossfly.ui.UIConstants.ResizeQuality;

Image original = new Image("/sdcard/foto.jpg");

// Miniatura 128x128 rellenando y recortando el excedente, con buena calidad:
Image thumb = original.resize(128, 128, ResizeMode.FILL, ResizeQuality.SMOOTH);

// Alternativa preservando la proporción a partir del ancho (sin recortar):
Image thumbW = original.resizeWidth(128, ResizeQuality.SMOOTH);
```

> Nota de implementación (fuente): con `ResizeMode.FIT`/`FILL`, el `resize` primero escala uniformemente
> y, en el caso `FILL`, recorta por el centro vía `getArea(...)`. Con `STRETCH` llama directamente al
> escalado exacto. `resize` lanza `ImageException` (ej.: "Out of memory" en imágenes muy grandes)
> e `IllegalArgumentException` si `newWidth`/`newHeight` ≤ 0.

> No confundas `resize(...)` (crea píxeles nuevos) con `renderScaleX/renderScaleY` y
> `setRenderScale(...)`, que solo escalan **en el dibujo** vía GPU sin alterar el pixmap.

## Profundiza
- [Imágenes](/docs/ui/images-and-graphics/images) — diferencia entre resize destructivo y render-scale.
- Receta hermana: [salvar-imagem.md](/docs/cookbook/images/salvar-imagem) — graba el thumbnail en disco después de generarlo.
