Referencia de la API: Surface
La clase Surface es el destino de todos los comandos de dibujo. Gestiona la memoria de píxeles (en CPU o GPU) y proporciona el Canvas que usas para dibujar.
Descripción general
Un Surface es responsable de:
- Contener los datos de píxeles (o gestionar la textura de la GPU).
- Proporcionar una interfaz
Canvaspara dibujar en esos datos. - Capturar el contenido actual en una
Image.
Crear una Surface
1. Surface Raster (CPU)
La superficie más simple. Los píxeles residen en la memoria estándar del sistema (RAM). Es la mejor opción para generar imágenes, renderizado en el servidor o pruebas.
// Superficie estándar RGBA de 32 bits
Surface raster = Surface.makeRasterN32Premul(800, 600);
// Con ImageInfo personalizada (ej., color F16 para HDR)
ImageInfo info = new ImageInfo(800, 600, ColorType.RGBA_F16, AlphaType.PREMUL);
Surface hdrSurface = Surface.makeRaster(info);2. Surface de GPU (Render Target)
Se utiliza para renderizado acelerado por hardware. Necesitas un DirectContext (contexto OpenGL/Metal/Vulkan).
DirectContext context = ...; // Tu contexto de GPU
// Crear una nueva textura en la GPU gestionada por Skia
Surface gpuSurface = Surface.makeRenderTarget(
context,
false, // ¿Con presupuesto? (¿Debería Skia contar esto contra su límite de caché?)
ImageInfo.makeN32Premul(800, 600)
);3. Envolver Texturas OpenGL/Metal Existentes
Si estás integrando Skija en un motor de juego o sistema de ventanas existente (como LWJGL o JWM), la ventana suele proporcionar un ID de "framebuffer" o "textura". Lo envuelves para que Skija pueda dibujar directamente en la pantalla.
// Ejemplo OpenGL
int framebufferId = 0; // Búfer de pantalla por defecto
BackendRenderTarget renderTarget = BackendRenderTarget.makeGL(
800, 600, // Ancho, Alto
0, // Recuento de muestras (0 para sin MSAA)
8, // Bits de stencil
framebufferId,
BackendRenderTarget.FRAMEBUFFER_FORMAT_GR_GL_RGBA8
);
Surface screenSurface = Surface.wrapBackendRenderTarget(
context,
renderTarget,
SurfaceOrigin.BOTTOM_LEFT, // Las coordenadas de OpenGL comienzan en la parte inferior izquierda
ColorType.RGBA_8888,
ColorSpace.getSRGB(),
null // SurfaceProps
);4. Envolver Píxeles Raster (Interoperabilidad)
Si tienes un ByteBuffer o un puntero de otra biblioteca (como un decodificador de fotogramas de video), puedes envolverlo directamente sin copiar.
long pixelPtr = ...; // Puntero nativo a la memoria
int rowBytes = width * 4; // Bytes por fila
Surface wrap = Surface.wrapPixels(
ImageInfo.makeN32Premul(width, height),
pixelPtr,
rowBytes
);5. Surface Nula
Crea una superficie que no hace nada. Útil para medir o probar sin asignar memoria.
Surface nullSurface = Surface.makeNull(100, 100);Crear Instantáneas (Image)
Crear una Image inmutable a partir de una Surface es una operación económica (Copy-on-Write).
// ¡Esto no copia los píxeles inmediatamente!
// Efectivamente "bifurca" la superficie. Los dibujos futuros en 'surface' no afectarán a 'snapshot'.
Image snapshot = surface.makeImageSnapshot();
// Ahora puedes usar 'snapshot' para dibujar en otra superficie o guardar en disco.Interactuar con el Contenido
// Obtener el canvas para dibujar
Canvas canvas = surface.getCanvas();
canvas.drawCircle(50, 50, 20, paint);
// Leer píxeles de vuelta a un bitmap
Bitmap bitmap = new Bitmap();
bitmap.allocPixels(ImageInfo.makeN32Premul(100, 100));
if (surface.readPixels(bitmap, 0, 0)) {
// Píxeles leídos con éxito
}
// Escribir píxeles desde un bitmap en la superficie
surface.writePixels(bitmap, 10, 10);
// Enviar comandos a la GPU (importante para superficies de GPU)
surface.flush();getCanvas(): Devuelve el canvas para dibujar.readPixels(bitmap, x, y): Lee píxeles desde la GPU/CPU hacia un bitmap.writePixels(bitmap, x, y): Escribe píxeles desde un bitmap en la superficie.flush(): Asegura que todos los comandos pendientes de la GPU se envíen al controlador.notifyContentWillChange(): Llama a esto si modificas la memoria de píxeles subyacente directamente (omitir el Canvas).getRecordingContext(): Devuelve elDirectContextque respalda esta superficie (si lo hay).