Create a pixmap which is the difference of two shapes - libgdx

I'd like to create an overlay in my libGDX game to be used during onboarding. Basically I'd like to overlay the entire screen with a 40% alpha rectangle (that's easy) but I want a cutout of a circle on top of the area of interest where the user should focus attention. Is there a way to create a texture which is the difference of two shapes or pixmaps?
Pixmap overlay = new Pixmap(screenWidth, screenHeight, Pixmap.Format.RGBA8888);
overlay.setColor(0, 0, 0, 0.4f);
overlay.fillRectangle(0, 0, screenWidth, screenHeight);
Pixmap overlayCutout = new Pixmap(screenWidth, screenHeight, Pixmap.Format.RGBA8888);
overlayCutout.setColor(Color.WHITE);
overlayCutout.fillCircle(focusAreaX, focusAreaY, radius);
Texture t = new Texture(overlay);
//subtract overlayCutout??
overlay.dispose();
overlayCutout.dispose();
batch.draw(t);

I'm not sure I completely got what you want to do, but I think the trick is to use only one Pixmap...
Pixmap overlay = new Pixmap(screenWidth, screenHeight, Pixmap.Format.RGBA8888);
overlay.setColor(0, 0, 0, 0.4f);
overlay.fillRectangle(0, 0, screenWidth, screenHeight);
// the trick is to "redraw" the inner circle with an "invisible" colour alpha=0
overlay.setBlending(Blending.None);
overlay.setColor(1, 1, 1, 0);
overlay.fillCircle(focusAreaX, focusAreaY, radius);
overlay.setBlending(Blending.SourceOver);
Texture t = new Texture(overlay);
overlay.dispose();
batch.draw(t);

Related

LibGDX FrameBuffer

I'm trying to make a game where you build a spaceship from parts, and fly it around and such.
I would like to create the ship from a series of components (from a TextureAtlas for instance). I'd like to draw the ship from it's component textures, and then save the drawn ship as one large Texture. (So i don't have to draw 50 component textures, just one ship texture. What would be the best way to go about this?
I've been trying to do so using a FrameBuffer. I've been able to draw the components to a Texture, and draw the texture to the screen, but no matter what I try the Texture ends up with a solid background the size of the frame buffer. It's like the clear command can't clear with transparency. Here's the drawing code I have at the moment. The ship is just a Sprite which i save the FrameBuffer texture to.
public void render(){
if (ship == null){
int screenwidth = Gdx.graphics.getWidth();
int screenheight = Gdx.graphics.getHeight();
SpriteBatch fb = new SpriteBatch();
FrameBuffer fbo = new FrameBuffer(Format.RGB888, screenwidth, screenheight, false);
fbo.begin();
fb.enableBlending();
Gdx.gl.glBlendFuncSeparate(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA, GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gdx.gl.glClearColor(1, 0, 1, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
fb.begin();
atlas.createSprite("0").draw(fb);
fb.end();
fbo.end();
ship = new Sprite(fbo.getColorBufferTexture());
ship.setPosition(0, -screenheight);
}
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.enableBlending();
batch.setBlendFunction(GL20.GL_ONE, GL20.GL_ZERO);
ship.draw(batch);
batch.end();
}
The problem here lies in this line:
FrameBuffer fbo = new FrameBuffer(Format.RGB888, screenwidth, screenheight, false);
specifically with Format.RGB888. This line is saying that your FrameBuffer should be Red (8 bits) followed by Green (8 bits) followed by Blue (8 bits). Notice however, that this format doesn't have any bits for Alpha (transparency). In order to get transparency out of your frame buffer, you probably instead want to use the Format.RGBA8888, which includes an additional 8 bits for Alpha.
Hope this helps.

LibGDX: transform Texture to TextureRegion

I am creating a TowerDefence Game in LibGDX and am now trying to replace the old Tile with a new StaticTiledMapTile.
But to create a StaticTiledMapTile I need a TextureRegion, not a Texture.
Now I'm trying to create a TextureRegion, which contains the whole Texture, but it doesn't seem to work. It always appears distorted.
I have tried the following:
TextureRegion region = new TextureRegion(new Texture("someImg.png"), 0, 0, 32, 32);
StaticTileMapTile tile = new StaticTiledMapTile(region);
getLayer().getCell(x,y).setTile(tile); //setting the new tile
In my opinion this should work (if the image, as it is, is 32px wide and 32px high).

Html5 Canvas - Shading an outside area

I need to shade an OUTSIDE area, ie the shapes I draw in the shader are drawn normally, and their inverse is then shaded. Its easiest to explain with an example, and noting the bit that is not working:
// canvasBackground is the actual background
// canvasBackgroundContext is its context
// To make it simple, I will fill it with green
canvasBackgroundContext.fillStyle = "#00FF00";
canvasBackgroundContext.clearRect(0, 0, canvasBackground.width, canvasBackground.height);
// I also have a the shader
// canvasShader and canvasShaderContext with same width and height as canvasBackground
canvasShaderContext.fillStyle = "rgba(0, 0, 0, 0.25)"; // Black but slightly transparent
canvasShaderContext.clearRect(0, 0, canvasShader.width, canvasShader.height);
// Everything so far is great - now the problem
// This is wrong, because when I create the area I want to create clear, it does not work
// because when you draw a shape it does not work like clearRect, as it does not set each pixel to a clear pixel
canvasShaderContext.fillStyle = "rgba(0, 0, 0, 0.0)";
// Create the only non shaded bits in the shader, overlapping rects
canvasShaderContext.fillRect(10, 10, 50, 50);
canvasShaderContext.fillRect(40, 40, 50, 50);
// So when I do this, it should shade the entire background except for the two 50x50 overlapping rects at 10,10 and 40,40
canvasBackgroundContext.drawImage(canvasShaderContext, 0, 0);
I don't want to go to a pixel by pixel basis using getImageData, as that is slow. There must be some way of doing this.
I am not sure I fully understand what you try to achieve, but how about adding a composite mode to this:
canvasShaderContext.fillRect(10, 10, 50, 50);
canvasShaderContext.fillRect(40, 40, 50, 50);
which results in:
/// store old mode whatever that is
var oldMode = canvasShaderContext.globalCompositeOperation;
/// this uses any shape that is drawn next to punch a hole
/// in destination (current context).
canvasShaderContext.globalCompositeOperation = 'destination-out';
/// now draw the holes
canvasShaderContext.fillRect(10, 10, 50, 50);
canvasShaderContext.fillRect(40, 40, 50, 50);
/// set back to old mode
canvasShaderContext.globalCompositeOperation = oldMode;
That will also clear the alpha bits.

Tweening Sprite size using GreenSock in AS3

I've seen examples where it's possible to tween a rectangle using scaleX, but I can't find anything that tweens a circle. (The "circle" that I'm drawing is actually a donut shape and I want the outside circle to be the one that is tweened).
var resizeVar:Number = 75;
myCircle.graphics.drawCircle((myCircle.width/2), (myCircle.height/2), resizeVar);
myCircle.graphics.drawCircle((myCircle.width/2), (myCircle.height/2), 75);
I tried doing it this way, but this throws lots of errors. I don't think it's possible this way:
TweenMax.to(myCircle, 2, {resizeVar:150, ease:SlowMo.ease.config(1, 0)});
Normally with display objects, it is done this way. It doesn't work with this "donut" though:
TweenMax.to(myRectangle, 2, {scaleX:1.5, scaleY:1.5 ease:SlowMo.ease.config(1, 0)});
So my question is, how can I tween the radius size of my outside circle?
EDIT: This is how the donut is being drawn, so the resizeVar needs to change from 75 to 150.
var myCircle:Sprite = new Sprite();
myCircle.graphics.beginFill(0xbbbbbb);
myCircle.graphics.drawCircle(0, 0, 150); // this is what should be tweening/scaling
myCircle.graphics.drawCircle(0, 0, 75); // this should stay the same
myCircle.graphics.endFill();
addChild(myCircle);
You should be able to tween the scaleX and scaleY properties of ANY displayObject:
var radius:Number = 75;
var myCircle:Sprite = new Sprite();
myCircle.graphics.beginFill(0);
myCircle.graphics.drawCircle(radius/2, radius/2, radius);
myCircle.graphics.endFill();
addChild(myCircle);
TweenMax.to(myCircle, 2, {scaleX:2, scaleY:2, ease:SlowMo.ease.config(1,0)});
EDIT
This is how you would scale just the outside of the donut:
var resizeObject:Object = { innerRadius:75, outerRadius:150 };
myCircle = new Sprite();
myCircle.graphics.beginFill(0xbbbbbb);
myCircle.graphics.drawCircle(0, 0, resizeObject.outerRadius);
myCircle.graphics.drawCircle(0, 0, resizeObject.innerRadius);
myCircle.graphics.endFill();
addChild(myCircle);
TweenMax.to(resizeObject, 2, {outerRadius:300, ease:SlowMo.ease.config(1,0), onUpdate:updateCircle, onUpdateParams:[resizeObject]});
function updateCircle(resizeObject:Object):void
{
myCircle.graphics.clear();
myCircle.graphics.beginFill(0xbbbbbb);
myCircle.graphics.drawCircle(0, 0, resizeObject.outerRadius);
myCircle.graphics.drawCircle(0, 0, resizeObject.innerRadius);
myCircle.graphics.endFill();
}
The reason it works with the rectangle is that you are changing the scale of the rectangle. When you change the scale Flash Player adjusts the scale of the display object containing your graphics.
However, with the circle, you are trying to change the radius of the circle. The radius is only used when you draw the circle with the drawCircle() method. One way to tween the radius is to use your tween to re-draw the circle many times (not that ideal).
To re-draw the circle with a new radius, you can use the onUpdate callback that TweenMax offers:
TweenMax.to(myCircle, 2, {resizeVar:150, onUpdate: onUpdateCallback, onUpdateParams: [resizeVar] });
function onUpdateCallback(radius):void
{
myCircle.graphics.drawCircle(myCircle.graphics.drawCircle((myCircle.width/2), (myCircle.height/2), radius);
}
[Edit]
Note, I've added some params that you need to pass to the onUpdateCallback() function. I've also modified the function to add a radius parameter, and then use the radius when drawing the circle.
In regards to "trying to change the outside circle of this donut", this may be more complex. You might need to draw both circles of the donut. You might need to also call graphics.clear() before you draw the circle.
However, perhaps the answer from #Marcela is better, just change the scaleX and scaleY of the object you've already drawn. But if you need to get to a specified radius, the only way to do that is by re-drawing the circle(s) on each interval of the tween.

circular colorTransform

Is there a way to apply a colorTransform to a BitmapData in a circle rather than in a rectangle?
Instead of erasing rectangular parts of an image by reducing the alpha channel as in the code below, I'd like to do it in circles.
_bitmap.colorTransform(new Rectangle(mouseX-d/2, mouseY-d/2, d, d),
new ColorTransform(1, 1, 1, .5, 0, 0, 0, 1));
I do have some code which loops through the pixels, extracts the alpha value and uses setPixel but it seams significantly slower than the colorTransform function.
Try creating a circle using the drawing API (flash.display.Graphics) and then drawing that onto the bitmap data with BlendMode.ERASE. That might solve your problem, if I understand it correctly.
var circle : Shape = new Shape;
circle.graphics.beginFill(0xffcc00, 1);
circle.graphics.drawEllipse(-50, -50, 100, 100);
// Create a transformation matrix for the draw() operation, with
// a translation matching the mouse position.
var mtx : Matrix = new Matrix();
mtx.translate(mouseX, mouseY);
// Draw circle at mouse position with the ERASE blend mode, to
// set affected pixels to alpha=0.
myBitmap.draw(circle, mtx, null, BlendMode.ERASE);
I'm not 100% sure that the ERASE blend mode works satisfyingly with the draw() command, but I can't see why it shouldn't. Please let me know how it works out!