Get the FREE Ultimate OpenClaw Setup Guide →

libgdx-framebuffer-pixmap

npx machina-cli add skill kyu-n/gdx-claude-skills/libgdx-framebuffer-pixmap --openclaw
Files (1)
SKILL.md
15.2 KB

libGDX 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);
  • hasDepth adds a 16-bit depth renderbuffer (GL_DEPTH_COMPONENT16).
  • hasStencil adds 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()

MethodBinds FBOSets ViewportSaves Previous State
begin()YesYes (to FBO dimensions)No
end()Unbinds to defaultYes (to back buffer)N/A
bind()YesNoNo
unbind() (static)Unbinds to defaultNoN/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

MethodReturnsNotes
getColorBufferTexture()TextureFirst color attachment. Owned by FBO — do NOT dispose separately.
getTextureAttachments()Array<Texture>All color attachments (for MRT).
getWidth()intFBO width in pixels.
getHeight()intFBO height in pixels.
getFramebufferHandle()intGL handle. Note: lowercase 'b' in "buffer".
dispose()voidDisposes FBO, all attachments, AND the color texture(s).
transfer(destination)voidBlit to another FBO (same dimensions required). GL30+.
transfer(destination, copyBits)voidBlit 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):

MethodParameters
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:

  • createFromFrameBuffer is static. Uses glReadPixels with bottom-left origin.
  • The returned Pixmap is Y-flipped vs screen appearance. Use PixmapIO.writePNG with flipY=true or manually flip.
  • Always produces RGBA8888 format.
  • 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

BehaviorDesktopAndroidiOS
FBO managed (recreated)N/A (no context loss)Yes — contents lost, structure rebuiltGL context preserved
Default FBO handle00Non-zero (auto-detected)
FloatFrameBufferGL 3.0+GL ES 3.0+GL ES 3.0+
createFromFrameBufferWorksWorksWorks
Max FBO sizeGPU-dependentOften 4096×4096GPU-dependent
Float texture filteringLinear worksNearest onlyNearest only

Common Mistakes

  1. Drawing FBO texture without flipping — FBO textures are Y-flipped. Use TextureRegion.flip(false, true) or the image renders upside-down.
  2. Disposing the FBO's color texturegetColorBufferTexture() returns a texture owned by the FBO. Disposing it separately corrupts the FBO. Only call fbo.dispose().
  3. Nesting FBO begin/endend() always unbinds to the default framebuffer, not the previously bound FBO. Structure FBO passes sequentially, never nested.
  4. Forgetting to clear after begin() — FBO retains previous frame content. Call ScreenUtils.clear() after fbo.begin().
  5. 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.
  6. Creating Pixmap in render loop without disposing — Pixmap allocates native memory. Creating one per frame without dispose() causes rapid memory exhaustion.
  7. Inventing fbo.getTexture() — The method is getColorBufferTexture(). There is no getTexture().
  8. Assuming createFromFrameBuffer uses screen coordinates — It uses OpenGL bottom-left origin and the result is Y-flipped. Use PixmapIO.writePNG with flipY=true for correct orientation.
  9. Calling scaleEffect on wrong class — Pixmap has no scale method. Use drawPixmap() with different src/dst rectangles for scaling.
  10. 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.
  11. Using 3-param writePNGPixmapIO.writePNG(file, pixmap, compression) does not exist. Use the 4-param version: writePNG(file, pixmap, compression, flipY).
  12. Setting Pixmap blending to SourceOver when writing alpha — Default blending (SourceOver) composites alpha. Set Blending.None when 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

  1. Step 1: Create an FBO with desired format and size (pixels): FrameBuffer fbo = new FrameBuffer(Pixmap.Format.RGBA8888, width, height, hasDepth);
  2. Step 2: fbo.begin(); ScreenUtils.clear(0, 0, 0, 1); // render scene here; fbo.end();
  3. 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.

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers