libgdx-framebuffer-pixmap
npx machina-cli add skill kyu-n/gdx-claude-skills/libgdx-framebuffer-pixmap --openclawlibGDX FrameBuffer & Pixmap
Quick reference for render-to-texture (FBO) and CPU-side pixel manipulation. Covers FrameBuffer, GLFrameBuffer, FloatFrameBuffer, Pixmap, PixmapIO, and Color int conversions.
FrameBuffer (FBO)
com.badlogic.gdx.graphics.glutils.FrameBuffer extends GLFrameBuffer<Texture>, implements Disposable.
Construction
// Standard — width/height in PIXELS, not world units
FrameBuffer fbo = new FrameBuffer(Pixmap.Format.RGBA8888, width, height, hasDepth);
FrameBuffer fbo = new FrameBuffer(Pixmap.Format.RGBA8888, width, height, hasDepth, hasStencil);
// Typical: match screen resolution
FrameBuffer fbo = new FrameBuffer(Pixmap.Format.RGBA8888,
Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight(), false);
hasDepthadds a 16-bit depth renderbuffer (GL_DEPTH_COMPONENT16).hasStenciladds an 8-bit stencil renderbuffer (GL_STENCIL_INDEX8).- Width/height are pixel dimensions. Do NOT pass world units or viewport units.
begin() / end() Pattern
fbo.begin();
// Binds FBO + sets glViewport to FBO dimensions.
// Does NOT save previous framebuffer or viewport state.
ScreenUtils.clear(0, 0, 0, 1); // MUST clear — previous frame persists otherwise
batch.setProjectionMatrix(camera.combined);
batch.begin();
// ... render scene into FBO ...
batch.end();
fbo.end();
// Unbinds FBO (binds default framebuffer) + restores viewport to back buffer size.
end(int x, int y, int width, int height) — restores a specific viewport instead of auto-detecting back buffer size.
Gotchas:
begin()does NOT save previous state.end()ALWAYS unbinds to the default framebuffer. There is no stack.- You MUST clear after
begin()— the FBO retains content from the previous frame. - The camera/projection matrix used inside the FBO should match the FBO's dimensions, not the screen.
Drawing the FBO Texture to Screen
The FBO texture is vertically flipped (OpenGL renders Y-up but textures sample Y-down). You MUST flip it:
// Approach 1: TextureRegion with flip (recommended)
TextureRegion fboRegion = new TextureRegion(fbo.getColorBufferTexture());
fboRegion.flip(false, true); // create once, reuse
// Draw to screen
batch.setProjectionMatrix(screenCamera.combined);
batch.begin();
batch.draw(fboRegion, 0, 0, screenWidth, screenHeight);
batch.end();
Standard Post-Processing Pattern
// 1. Render scene into FBO
fbo.begin();
ScreenUtils.clear(0, 0, 0, 1);
batch.setProjectionMatrix(gameCamera.combined);
batch.begin();
// ... draw game world ...
batch.end();
fbo.end();
// 2. Draw FBO texture to screen (with optional shader)
batch.setProjectionMatrix(screenCamera.combined);
batch.setShader(postProcessShader); // optional: blur, vignette, etc.
batch.begin();
batch.draw(fboRegion, 0, 0, screenWidth, screenHeight);
batch.end();
batch.setShader(null);
bind() vs begin()
| Method | Binds FBO | Sets Viewport | Saves Previous State |
|---|---|---|---|
begin() | Yes | Yes (to FBO dimensions) | No |
end() | Unbinds to default | Yes (to back buffer) | N/A |
bind() | Yes | No | No |
unbind() (static) | Unbinds to default | No | N/A |
Use bind()/unbind() when you manage the viewport yourself. Use begin()/end() for standard rendering.
FBO Nesting — Does NOT Work
// BROKEN — do NOT do this:
fbo1.begin();
fbo2.begin();
fbo2.end(); // unbinds to DEFAULT framebuffer, NOT fbo1
// rendering here goes to screen, not fbo1!
fbo1.end();
end() always unbinds to the default framebuffer. There is no stack or save/restore mechanism. Structure FBO passes sequentially:
// CORRECT — sequential passes:
fbo1.begin();
// render scene
fbo1.end();
fbo2.begin();
// render fbo1's texture with shader
fbo2.end();
Key Methods
| Method | Returns | Notes |
|---|---|---|
getColorBufferTexture() | Texture | First color attachment. Owned by FBO — do NOT dispose separately. |
getTextureAttachments() | Array<Texture> | All color attachments (for MRT). |
getWidth() | int | FBO width in pixels. |
getHeight() | int | FBO height in pixels. |
getFramebufferHandle() | int | GL handle. Note: lowercase 'b' in "buffer". |
dispose() | void | Disposes FBO, all attachments, AND the color texture(s). |
transfer(destination) | void | Blit to another FBO (same dimensions required). GL30+. |
transfer(destination, copyBits) | void | Blit with explicit buffer bits mask. GL30+. |
DO NOT use fbo.getColorBufferTexture().dispose() — the FBO owns this texture. Disposing it separately corrupts the FBO.
FrameBufferBuilder (Advanced)
For custom FBO configurations (multiple color attachments, specific GL formats):
FrameBuffer fbo = new GLFrameBuffer.FrameBufferBuilder(width, height)
.addBasicColorTextureAttachment(Pixmap.Format.RGBA8888)
.addBasicDepthRenderBuffer()
.build();
// With explicit GL formats:
FrameBuffer fbo = new GLFrameBuffer.FrameBufferBuilder(width, height)
.addColorTextureAttachment(GL30.GL_RGBA8, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE)
.addDepthTextureAttachment(GL30.GL_DEPTH_COMPONENT24, GL20.GL_UNSIGNED_INT)
.build();
Builder class is GLFrameBuffer.FrameBufferBuilder (NOT GLFrameBuffer.GLFrameBufferBuilder — that's the abstract base).
Builder methods (all return builder for chaining):
| Method | Parameters |
|---|---|
addBasicColorTextureAttachment | (Pixmap.Format) |
addColorTextureAttachment | (int internalFormat, int format, int type) |
addFloatAttachment | (int internalFormat, int format, int type, boolean gpuOnly) |
addDepthTextureAttachment | (int internalFormat, int type) |
addStencilTextureAttachment | (int internalFormat, int type) |
addBasicDepthRenderBuffer | () — uses GL_DEPTH_COMPONENT16 |
addBasicStencilRenderBuffer | () — uses GL_STENCIL_INDEX8 |
addBasicStencilDepthPackedRenderBuffer | () — uses GL_DEPTH24_STENCIL8 |
addDepthRenderBuffer | (int internalFormat) |
addStencilRenderBuffer | (int internalFormat) |
addColorRenderBuffer | (int internalFormat) |
FloatFrameBuffer
com.badlogic.gdx.graphics.glutils.FloatFrameBuffer extends FrameBuffer. Uses GL30.GL_RGBA32F — for HDR rendering, deferred shading. Requires GL ES 3.0 / GL 3.0+.
FloatFrameBuffer hdrFbo = new FloatFrameBuffer(width, height, hasDepth);
On mobile (non-Desktop), texture filter is forced to Nearest (linear filtering of float textures is not supported on GL ES).
Builder variant: GLFrameBuffer.FloatFrameBufferBuilder.
Managed State
FBOs are managed — libGDX automatically recreates GL objects after context loss (Android). However, the rendered contents are lost. Only the FBO structure (handle, attachments) is rebuilt.
Pixmap
com.badlogic.gdx.graphics.Pixmap implements Disposable. CPU-side pixel buffer — all operations are software-rendered, no GL.
Coordinate system: Y-down, top-left origin — unlike libGDX's default Y-up for rendering.
Construction
Pixmap pm = new Pixmap(256, 256, Pixmap.Format.RGBA8888); // blank
Pixmap pm = new Pixmap(Gdx.files.internal("image.png")); // from file (PNG, JPEG, BMP)
Pixmap pm = new Pixmap(encodedBytes, offset, len); // byte[] of encoded image data
Pixmap pm = new Pixmap(encodedByteBuffer); // ByteBuffer of encoded image data
Pixmap.Format
Alpha, Intensity, LuminanceAlpha, RGB565, RGBA4444, RGB888, RGBA8888
Use RGBA8888 as the safe default. Other formats save memory but limit functionality.
Drawing (CPU-side, no GL)
pm.setColor(1, 0, 0, 1); // set draw color (float RGBA)
pm.setColor(Color.RED); // set draw color (Color object)
pm.setColor(0xFF0000FF); // set draw color (packed RGBA8888 int)
pm.fill(); // fill entire pixmap with current color
pm.drawPixel(x, y); // single pixel with current color
pm.drawPixel(x, y, 0xFF0000FF); // single pixel with explicit RGBA8888 color
pm.drawLine(x1, y1, x2, y2);
pm.drawRectangle(x, y, w, h); // outline
pm.fillRectangle(x, y, w, h); // filled
pm.drawCircle(x, y, radius); // outline
pm.fillCircle(x, y, radius); // filled
pm.fillTriangle(x1, y1, x2, y2, x3, y3); // filled only — NO drawTriangle() exists
There are NO drawTriangle, drawArc, drawPolygon, drawEllipse, or path methods.
Pixmap-to-Pixmap Blitting
pm.drawPixmap(src, x, y); // draw entire src at (x,y)
pm.drawPixmap(src, x, y, srcX, srcY, srcW, srcH); // draw region of src, no scaling
pm.drawPixmap(src, srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH); // with scaling
Pixel Access
int rgba = pm.getPixel(x, y); // packed RGBA8888 int regardless of pixmap format
Color c = new Color();
Color.rgba8888ToColor(c, rgba); // unpack to Color (mutates c, returns void)
int packed = Color.rgba8888(c); // pack Color to RGBA8888 int
ByteBuffer pixels = pm.getPixels(); // direct ByteBuffer of raw pixel data
pm.setPixels(byteBuffer); // replace pixel data from ByteBuffer
Settings
pm.setBlending(Pixmap.Blending.None); // overwrite mode (no alpha blending)
pm.setBlending(Pixmap.Blending.SourceOver); // alpha blending (default)
pm.setFilter(Pixmap.Filter.NearestNeighbour); // for drawPixmap scaling
pm.setFilter(Pixmap.Filter.BiLinear); // for drawPixmap scaling
Set blending to None when writing opaque content or when you need to write specific alpha values.
Creating Texture from Pixmap
Texture tex = new Texture(pm); // copies pixel data — Pixmap can be disposed after
pm.dispose();
The Texture copies the data. The Pixmap and Texture have independent lifetimes.
Screen Capture
// Reads from currently bound framebuffer (default or FBO)
// Coordinates use OpenGL BOTTOM-LEFT origin
Pixmap screenshot = Pixmap.createFromFrameBuffer(0, 0,
Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight());
// Result is Y-FLIPPED relative to screen — must flip for correct orientation:
PixmapIO.writePNG(Gdx.files.local("screenshot.png"), screenshot, 6, true); // flipY=true
screenshot.dispose();
Gotchas:
createFromFrameBufferis static. UsesglReadPixelswith bottom-left origin.- The returned Pixmap is Y-flipped vs screen appearance. Use
PixmapIO.writePNGwithflipY=trueor manually flip. - Always produces
RGBA8888format. - Works with FBOs too — reads from whatever framebuffer is currently bound.
Disposal
pm.dispose(); // MUST call — native heap memory
pm.isDisposed(); // check if already disposed
Properties
pm.getWidth(); // int — original dimensions
pm.getHeight(); // int
pm.getFormat(); // Pixmap.Format
pm.getGLFormat(); // int — GL format constant
pm.getGLType(); // int — GL type constant
Async Download
Pixmap.downloadFromUrl("https://example.com/image.png",
new Pixmap.DownloadPixmapResponseListener() {
public void downloadComplete(Pixmap pixmap) { /* use pixmap, must dispose */ }
public void downloadFailed(Throwable t) { /* handle error */ }
});
PixmapIO
com.badlogic.gdx.graphics.PixmapIO — static utility for saving Pixmaps to files.
PixmapIO.writePNG(fileHandle, pixmap); // default compression, no flip
PixmapIO.writePNG(fileHandle, pixmap, compression, flipY); // compression: 0–9, flipY: boolean
There is NO 3-parameter writePNG(file, pixmap, compression) overload — the extended version requires all 4 params.
CIM format (libGDX custom compressed):
PixmapIO.writeCIM(fileHandle, pixmap);
Pixmap pm = PixmapIO.readCIM(fileHandle);
Reusable PNG writer (avoids buffer reallocation):
PixmapIO.PNG writer = new PixmapIO.PNG();
writer.setFlipY(true); // default: true
writer.setCompression(6); // deflate level
writer.write(fileHandle, pixmap);
writer.dispose(); // reusable, dispose when done with all writes
Platform Differences
| Behavior | Desktop | Android | iOS |
|---|---|---|---|
| FBO managed (recreated) | N/A (no context loss) | Yes — contents lost, structure rebuilt | GL context preserved |
| Default FBO handle | 0 | 0 | Non-zero (auto-detected) |
| FloatFrameBuffer | GL 3.0+ | GL ES 3.0+ | GL ES 3.0+ |
createFromFrameBuffer | Works | Works | Works |
| Max FBO size | GPU-dependent | Often 4096×4096 | GPU-dependent |
| Float texture filtering | Linear works | Nearest only | Nearest only |
Common Mistakes
- Drawing FBO texture without flipping — FBO textures are Y-flipped. Use
TextureRegion.flip(false, true)or the image renders upside-down. - Disposing the FBO's color texture —
getColorBufferTexture()returns a texture owned by the FBO. Disposing it separately corrupts the FBO. Only callfbo.dispose(). - Nesting FBO begin/end —
end()always unbinds to the default framebuffer, not the previously bound FBO. Structure FBO passes sequentially, never nested. - Forgetting to clear after begin() — FBO retains previous frame content. Call
ScreenUtils.clear()afterfbo.begin(). - Using world units for FBO dimensions — FBO constructor takes pixel dimensions. Use
Gdx.graphics.getBackBufferWidth/Height()or a fixed pixel resolution, not camera viewport units. - Creating Pixmap in render loop without disposing — Pixmap allocates native memory. Creating one per frame without
dispose()causes rapid memory exhaustion. - Inventing
fbo.getTexture()— The method isgetColorBufferTexture(). There is nogetTexture(). - Assuming
createFromFrameBufferuses screen coordinates — It uses OpenGL bottom-left origin and the result is Y-flipped. UsePixmapIO.writePNGwithflipY=truefor correct orientation. - Calling
scaleEffecton wrong class — Pixmap has no scale method. UsedrawPixmap()with different src/dst rectangles for scaling. - Forgetting Pixmap is Y-down — Pixmap uses top-left origin (Y increases downward), unlike libGDX's rendering coordinate system (Y-up). Pixel coordinates from
getPixel()/drawPixel()use this Y-down system. - Using 3-param
writePNG—PixmapIO.writePNG(file, pixmap, compression)does not exist. Use the 4-param version:writePNG(file, pixmap, compression, flipY). - Setting Pixmap blending to SourceOver when writing alpha — Default blending (
SourceOver) composites alpha. SetBlending.Nonewhen you need to write specific alpha values or overwrite pixels completely.
Source
git clone https://github.com/kyu-n/gdx-claude-skills/blob/master/skills/libgdx-framebuffer-pixmap/SKILL.mdView on GitHub Overview
Provides a quick reference for render-to-texture with FrameBuffer, GLFrameBuffer, and CPU-side pixel manipulation via Pixmap. It covers creating FBOs, drawing the FBO texture to the screen (with Y-axis flip), and CPU-side work with PixmapIO, plus common pitfalls like upside-down renders and FBO nesting.
How This Skill Works
In LibGDX, create an FBO with pixel-precise width/height and optional depth/stencil buffers. Bind it with begin(), render your scene, then end() to switch back to the default framebuffer. When presenting the FBO, its texture is vertically flipped, so flip the TextureRegion (or adjust coords) before drawing; for post-processing, render the FBO texture with an optional shader. Use begin()/end() for standard rendering and bind()/unbind() when you manage the viewport yourself; nesting FBOs with begin() inside another begin() is not supported.
When to Use It
- Render-to-texture or post-processing pipelines using FrameBuffer, GLFrameBuffer, FloatFrameBuffer, or Pixmap-based workflows.
- CPU-side texture generation, Pixmap manipulation, or screen capture via PixmapIO.
- Debugging upside-down FBO rendering, texture orientation issues, or Y-axis mismatches.
- Diagnosing FBO nesting problems and blend state corruption caused by chained post-processing passes.
- Pixel-level manipulation or readback workflows where you need to access or save FBO content.
Quick Start
- Step 1: Create an FBO with desired format and size (pixels): FrameBuffer fbo = new FrameBuffer(Pixmap.Format.RGBA8888, width, height, hasDepth);
- Step 2: fbo.begin(); ScreenUtils.clear(0, 0, 0, 1); // render scene here; fbo.end();
- Step 3: TextureRegion fboRegion = new TextureRegion(fbo.getColorBufferTexture()); fboRegion.flip(false, true); // draw to screen with optional shader
Best Practices
- Always clear after fbo.begin() to avoid carrying content from the previous frame.
- Ensure width/height are pixel dimensions (not world/viewport units) when constructing FrameBuffer.
- When drawing the FBO texture, flip it vertically (TextureRegion.flip(false, true)) before rendering.
- Use begin()/end() for standard rendering; use bind()/unbind() only when you manage the viewport manually.
- Beware of FBO nesting; do not nest begin() calls, and verify you end() to the correct framebuffer to avoid rendering to the wrong target.
Example Use Cases
- Post-process a scene with a blur shader by rendering into an FBO and drawing the FBO texture with the shader applied.
- Capture the current frame to a PNG using Pixmap/PixmapIO after rendering to an FBO.
- Read back pixels into a Pixmap, modify colors on the CPU, and re-upload as a texture for dynamic effects.
- Diagnose and fix an upside-down render by flipping the FBO texture when drawing to screen.
- Avoid FBO nesting issues by structuring rendering passes to render into separate FBOs without overlapping begin() calls.