Blur the screen in LibGDX
Asked Answered
D

2

14

Im trying to create a background effect like this for my pause menu. My current idea is to take a screenshot on pause, save it, open it, Gaussian blur it, then render it to the screen and render the menu ontop. The only problem is I don't know how to save the screenshot effectively.

I've tried using batch.setColor(0,0,0,0.7f); to render a faded image ontop on the background but it didn't give me the blur effect i was looking for, rather just a tint as I assumed it would.

Examples/documentation greatly appreciated.

EDIT: found this code

package com.me.mygdxgame;

import java.awt.Point;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.PixelInterleavedSampleModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import com.badlogic.gdx.Application.ApplicationType;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.utils.ScreenUtils;

public class ScreenShot {

    private static final int[] RGBA_OFFSETS = { 0, 1, 2, 3 };
    private static final int[] RGB_OFFSETS = { 0, 1, 2 };

    public static void saveScreenshot(String baseName) throws IOException {
        File createTempFile = File.createTempFile(baseName, ".png");
        saveScreenshot(createTempFile);
    }

    public static void saveScreenshot(File file) throws IOException {
        saveScreenshot(file, false);
    }

    public static void saveScreenshot(File file, boolean hasAlpha) throws IOException {
        if (Gdx.app.getType() == ApplicationType.Android)
            return;

        byte[] screenshotPixels = ScreenUtils.getFrameBufferPixels(true);

        int width = Gdx.graphics.getWidth();
        int height = Gdx.graphics.getHeight();

        saveScreenshot(file, screenshotPixels, width, height, hasAlpha);
    }

    public static void saveScreenshot(File file, byte[] pixels, int width, int height, boolean hasAlpha) throws IOException {
        DataBufferByte dataBuffer = new DataBufferByte(pixels, pixels.length);

        PixelInterleavedSampleModel sampleModel = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, width, height, 4, 4 * width, getOffsets(hasAlpha));

        WritableRaster raster = Raster.createWritableRaster(sampleModel, dataBuffer, new Point(0, 0));

        BufferedImage img = new BufferedImage(getColorModel(hasAlpha), raster, false, null);

        ImageIO.write(img, "png", file);
    }

    private static ColorModel getColorModel(boolean alpha) {
        if (alpha)
            return new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] { 8, 8, 8, 8 }, true, false, ComponentColorModel.TRANSLUCENT, DataBuffer.TYPE_BYTE);
        return new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] { 8, 8, 8 }, false, false, ComponentColorModel.OPAQUE, DataBuffer.TYPE_BYTE);
    }

    private static int[] getOffsets(boolean alpha) {
        if (alpha)
            return RGBA_OFFSETS;
        return RGB_OFFSETS;
    }

}

but it gives me this error when I try saveScreenshot("output");(runnign desktop version, if that makes a difference. Haven't tested on an android)

Exception in thread "LWJGL Application" com.badlogic.gdx.utils.GdxRuntimeException: java.awt.image.RasterFormatException: Incorrect scanline stride: 3200
    at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:111)
Caused by: java.awt.image.RasterFormatException: Incorrect scanline stride: 3200

error line: ImageIO.write(img, "png", file);

Dayak answered 30/9, 2013 at 1:14 Comment(1)
How do you make Gaussian blur on image?Mascarenas
C
15

I'm late to the party but this is EXACTLY what you're looking for:

https://github.com/mattdesl/lwjgl-basics/wiki/ShaderLesson5

Render to a field buffer object using a shader to create the blur, then draw it from the frame buffer onto the screen.

Caulis answered 6/1, 2014 at 22:29 Comment(8)
no idea how i get this working with libgdx for me it just changes colorsMonochromatism
@Monochromatism check which shader programs you're running then, your pixel shader must be wrongCaulis
Copy pasted the the fragment shader and it was using the fragment shader aswell because changing things in the shader changes what was rendered. Anyways I found a workaround by doing the blur with BlurUtils (even if it is propably less performant)Monochromatism
@Monochromatism Hi. In the BlurUtils it says width and height must be square, by any chance you know how to use it on rectanglular textures?Pang
@JayN where did you read that it needs to be square? for clearification i'm using this for blurring my image. currently i'm using it on a resolution of 480x271 and it works fine for me.Monochromatism
@Monochromatism I'm using the generateBlurredMipmaps() function, in that function there's a line that if width != height then it simply returns, and if I comment out that line, it just draws blank white screen for me.Pang
@JayN as far as i know textures for mip mapping always needs to be square because the smallest image in the mip map is 1x1 or 2x2. maybe using the normal blur function fits better for your purpose.Monochromatism
@Monochromatism I've tried the normal one too, but I can't find a way to do the transition from normal image to a blurred one with that method.Pang
D
0

I have created my own implementation of gaussian blur for libgdx. Maybe it will be useful for someone. Just add the following class to your project:

package com.mygdx.game;

import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Color;
import java.lang.Math;

class Blur {
    private static double matrix [];
    private static int radius;

    public static void setMatrix(int radius, double sigma) {
        Blur.matrix = new double[radius+1];
        Blur.radius = radius;
        for(int i=0; i<=radius; i++)
            matrix[i] = gauss((double)i, sigma);
        double sum = 0;
        for(int i=-radius; i<=radius; i++)
            sum += matrix[Math.abs(i)];
        for(int i=0; i<=radius; i++)
            matrix[i]/=sum;
    }

    private static double gauss(double x, double sigma) {
        return 1/(sigma*Math.sqrt(2*Math.PI))*Math.exp(-(x*x)/(2*sigma*sigma));
    }

    public static Pixmap blur(Pixmap pixmap, boolean vertical) {
        int width = pixmap.getWidth();
        int height = pixmap.getHeight();
        Pixmap result = new Pixmap(width,height,Format.RGBA8888);

        for(int y=0; y<height; y++)
            for(int x=0; x<width; x++){
                float r=0,g=0,b=0,a=0;
                for(int i=-radius; i<=radius; i++) {
                    int px=x, py=y;
                    if(vertical)
                        py=mirrorTheEdge(py+i,height);
                    else 
                        px=mirrorTheEdge(px+i,width);
                    Color color = new Color(pixmap.getPixel(px,py));
                    float weight = (float)matrix[Math.abs(i)];
                    r+=color.r*weight;
                    g+=color.g*weight;
                    b+=color.b*weight;
                    a+=color.a*weight;
                }
                result.setColor(new Color(r,g,b,a));
                result.drawPixel(x,y);
            }

        return result;
    }

    private static int mirrorTheEdge(int p, int limit) {
        if(p<0) 
            p=p*-1-1;
        if(p>=limit) 
            p=limit*2-p+1;
        return p;
    }
}

And here is an example of use:

package com.mygdx.game;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.utils.ScreenUtils;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.files.FileHandle;

public class MyGdxGame extends ApplicationAdapter {
    SpriteBatch batch;
    Texture img;
    
    @Override
    public void create () {
        batch = new SpriteBatch();

        Pixmap pixmap = new Pixmap(new FileHandle("badlogic.jpg")); // Creating pixmap from file
        Blur.setMatrix(15,7.5); // Setting the blur strength
        pixmap = Blur.blur(pixmap,false); // Blurring the pixmap horizontally
        pixmap = Blur.blur(pixmap,true); // Blurring the pixmap vertically
        //pixmap = Blur.blur(Blur.blur(pixmap,false),true); // same in one line

        img = new Texture(pixmap);
    }

    @Override
    public void render () {
        ScreenUtils.clear(1, 0, 0, 1);
        batch.begin();
        batch.draw(img, 0, 0);
        batch.end();
    }
    
    @Override
    public void dispose () {
        batch.dispose();
        img.dispose();
    }
}

Basically, anything that can be transformed into a pixmap can be blurred. To set the strength of the blur, use Blur.setMatrix(radius,sigma). I don't understand it very well, but the higher these two values are, the blurrier the image. If you want to optimize this, remember that the lower the radius, the better. That's all.

Durware answered 26/7, 2023 at 1:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.