Image resizing is nearly universal in any GUI framework. In fact, one of the first things you learn when starting out in web development is how to scale images using CSS or HTML's img attributes. But how does this work?
When I tell the computer to scale a 500x500 img to 100x50, or the reverse, how does the computer know which pixels to draw from the original image? Lastly, is it reasonably easy for me to write my own "image transformer" in another programming language without significant drops in performance?
Based on a bit of research, I can conclude that most web browser will use nearest neighbor or linear interpolation for image resizing. I've written a concept nearest neighbor algorithm that successfully resizes images, albeit VERY SLOWLY.
using System;
using System.Drawing;
using System.Timers;
namespace Image_Resize
{
class ImageResizer
{
public static Image Resize(Image baseImage, int newHeight, int newWidth)
{
var baseBitmap = new Bitmap(baseImage);
int baseHeight = baseBitmap.Height;
int baseWidth = baseBitmap.Width;
//Nearest neighbor interpolation converts pixels in the resized image to pixels closest to the old image. We have a 2x2 image, and want to make it a 9x9.
//Step 1. Take a 9x9 image and shrink it back to old value. To do this, divide the new width by old width (i.e. 9/2 = 4.5)
float widthRatio = (float)baseWidth/newWidth;
float heightRatio = (float)baseHeight/newHeight;
//Step 2. Perform an integer comparison for each pixel in old I believe. If we have a pixel in the new located at (4,5), then the proportional will be
//(.8888, 1.11111) which SHOULD GO DOWN to (0,1) coordinates on a 2x2. Seems counter intuitive, but imagining a 2x2 grid, (4.5) is on the left-bottom coordinate
//so it makes sense the to be on the (0,1) pixel.
var watch = new System.Diagnostics.Stopwatch();
watch.Start();
Bitmap resized = new Bitmap(newWidth, newHeight);
int oldX = 0; int oldY = 0;
for (int i = 0; i < newWidth; i++)
{
oldX = (int)(i*widthRatio);
for (int j = 0; j < newHeight; j++)
{
oldY = (int)(j*heightRatio);
Color newColor = baseBitmap.GetPixel(oldX,oldY);
resized.SetPixel(i,j, newColor);
}
}
//This works, but is 100x slower than standard library methods due to GetPixel() and SetPixel() methods. The average time to set a 1920x1080 image is a second.
watch.Stop();
Console.WriteLine("Resizing the image took " + watch.Elapsed.TotalMilliseconds + "ms.");
return resized;
}
}
class Program
{
static void Main(string[] args)
{
var img = Image.FromFile(#"C:\Users\kpsin\Pictures\codeimage.jpg");
img = ImageResizer.Resize(img, 1000, 1500);
img.Save(#"C:\Users\kpsin\Pictures\codeimage1.jpg");
}
}
}
I do hope that someone else can come along and provide either a) a faster algorithm for nearest neighbor because I'm overlooking something silly, or b) another way that image scalers work that I'm not aware of. Otherwise, question... answered?
Related
I am having some problems implementing fixed timestep with graphic interpolation in my game.
Here is part of the render method:
#Override
public void render(float delta)
{
double newTime = TimeUtils.millis() / 1000.0;
double frameTime = Math.min(newTime - currentTime, 0.25);
accumulator += frameTime;
currentTime = newTime;
while (accumulator >= step)
{
updateObjects(step);
accumulator -= step;
}
double alpha = accumulator / step;
interpolateObjects((float)alpha);
}
Here is updateObjects:
for (int i = 0; i < world.level.gameObjects.size(); i++)
{
GameObject go = world.level.gameObjects.get(i);
go.prevPosition.set(go.position);//save previous position
go.update(delta);
}
interpolateObjects:
for (int i = 0; i < world.level.gameObjects.size(); i++)
{
GameObject go = world.level.gameObjects.get(i);
go.position.lerp(go.prevPosition, alpha);
}
And then objects are rendered using position
As far as i can tell this should work, but it doesn't.
On high fps (200-400) everything is too slow, movement isn't even visible, i can just see that position is changing by 0.0001 or something like that
On low fps (10-20), movement is visible but again objects are very slow...
If i disable interpolation, than everything works as it should (on any fps), but then everything is jittery.
So the problem is somewhere in interpolation.
Your interpolation go.position.lerp(go.prevPosition, alpha) is set up to assume that prevPosition was last updated at an exact multiple of step, but then when you update prevPosition like this go.prevPosition.set(go.position) you are destroying that contract on the first update of the frame. It also looks like you are lerping backwards (from position to the previous position).
I think you need a third vector so the last interpolated value is guaranteed not to influence your fixed time updates. Here I'll call it interpPosition, and it will be used for drawing instead of position.
You actually seem to technically be extrapolating (not interpolating) the value, since you are not updating ahead of time and your alpha is calculated from time left in the accumulator. If you want to linearly extrapolate from the last two positions calculated, you can do it like this (note the 1+alpha to extrapolate):
for (int i = 0; i < world.level.gameObjects.size(); i++)
{
GameObject go = world.level.gameObjects.get(i);
interpPosition.set(go.prevPosition).lerp(go.position, 1 + alpha);
}
Depending on the speed of your simulation (and how fast objects can accelerate), this might still look jerky. I think a smoother, but computationally slower way to do this would be to do a fully calculated update using alpha instead of step time, and storing that in the interpPosition vector. But only do that if necessary.
I want to move a sprite (which happens to be a Rectangle) from any position of the screen and make it stop at exactly the touched position of the screen. Now, I can stop my sprite already, but not at the exact touched position. I cannot find a good way of doing this without sacrificing either accuracy or risking the sprite to not stop at all.
Naturally - the problem arises because the current position is Float, so that Vector will never (or extremely rarely) have the exact same coordinates as the touch point (which is an int).
In the code below, I stop my sprite by simply checking the distance between the current position and the target position (i.e. the touched position Vector3), like so if (touch.dst(currentPsition.x, currentPosition.y, 0) < 4).
For example, if the sprite is at position (5,5) and I touch the screen at (100,100), it will stop at like (98.5352,96.8283).
My question is, how do I stop the sprite at exactly the touch position, without having to approximate?
void updateMotion() {
if (moveT) {
movement.set(velocity).scl(Gdx.graphics.getDeltaTime());
this.setPosition(currentPosition.add(movement));
if (touch.dst(currentPosition.x, currentPosition.y, 0) < 4)
moveT = false;
}
}
public void setMoveToTouchPosition(boolean moveT) {
this.moveT = moveT;
this.touch = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
GameScreen.getCamera().unproject(touch);
currentPosition = new Vector2(this.x, this.y);
direction.set(new Vector2(touch.x, touch.y)).sub(currentPosition).nor();
velocity = new Vector2(direction).scl(speed);
}
Of course sprite can't move smoothly to touch position and then stop in exactly the same position because of many reasons. Just change this
if (touch.dst(currentPosition.x, currentPosition.y, 0) < 4)
moveT = false;
to this
if (touch.dst(currentPosition.x, currentPosition.y, 0) < 2) {
currentPosition.x = touch.x;
currentPosition.y = touch.y;
moveT = false;
}
A quick yet acceptable solution to this could be the use of the Rectangle class. Considering you make a Rectangle surrounding the moving entity and constantly update it's bounds based on its current location, it's texture width, and it's texture height. You could stop it when it overlaps with the "target position". If you do this you guarantee yourself that it will stop exactly at that position. For example:
Texture entityTexture = new Texture("assets/image.png");
Rectangle entityBounds = new Rectangle();
entityBounds.set((currentPosition.x, currentPosition.y, entityTexture .getWidth(), entityTexture .getHeight()));
Rectangle targetBounds = new Rectangle();
targetBounds.set((targetPosition.x, targetPosition.y, 1, 1)); // make width and height 1 by 1 for max accuracy
public void update(){
// update bounds based on new position
entityBounds.set((currentPosition.x, currentPosition.y, entityTexture.getWidth(), entityTexture.getHeight()));
targetBounds.set((targetPosition.x, targetPosition.y, 1, 1));
if(entityBounds.overlaps(targetBounds)){
// do something
}
}
I'm trying to make a game that will take what users draw on their screens and create a sprite with a physics body. I looked around and I saw a tutorial that demonstrated this. Unfortunately the tutorial was made in Cocos2d-x v2.
http://build-failed.blogspot.com/2012/08/freehand-drawing-with-cocos2d-x-and.html
That is the tutorial that I am referring to.
http://www.cocos2d-x.org/wiki/Render_To_Texture
I tried to use that cocos2d-x tutorial to help me, but it has been labeled outdated (I tried it anyway and it didn't work). Is it still possible to allow the user to draw through this method? Or do I need to find another method to allow the user to draw the sprites? Any suggestions would be helpful. Thanks.
void GameScene::onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *event)
{
Point start = touch->getLocation();
start = Director::getInstance()->convertToGL(start);
Point end = touch->getPreviousLocation();
end = Director::getInstance()->convertToGL(end);
target->begin();
float distance = start.getDistance(end);
for (int i = 0; i < distance; i++)
{
float difx = end.x - start.x;
float dify = end.y - start.y;
float delta = (float) i / distance;
brush->setPosition(Point(start.x + (difx * delta), start.y + (dify * delta)));
brush->visit();
}
target->end();
}
void GameScene::onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *event)
{
myObjectSprite = Sprite::createWithTexture(target->getSprite()->getTexture());
myObjectSprite->setPosition(Point(visibleSize.width / 2, visibleSize.height / 2));
this->addChild(myObjectSprite);
}
This is what I gathered from those links. I can draw but there are a few problems.
The first attempt to draw always reflects horizontally over the center of the screen.
After a few drawings the fps begins to drop, and the app begins to take up a lot of memory and CPU usage.
The for loop in the onTouchMoved method is used because the onTouchEnded method isn't called fast enough, so there is often a large gap between each point that the onTouchMethod obtains. The loop is used to draw the brush sprites in a line between each point to prevent the gaps. However, for some reason it is not drawing in between the gaps.
Additionally, in the first link the person uses b2PolygonShape and b2FixtureDef from box2d. What are the new names of the classes in cocos2d-x v3?
EDIT I have had a rethink: I am going to use a much easier to implement tree structure with a grid so that each block can have up to 4 neighbours.
I am trying to attach blocks on to the stalk (series of blocks) of this flower. Green blocks are the existing stalk and the blue block is the one attached to the mouse.
The block attached to the mouse will correctly snap to the nearest edges (I'm allowing diagonals for now) however the blocks will also be able to go 'inside' the stalk (image 2) by snapping to a block above or below.
My question is, how can I stop this? I considered
Iterating the list again but ignoring the block I just tried to attach to; I think this will just result in the same problem really by attaching to another block 'inside' the stalk.
Find which block I am intersecting and get another connection point from it, repeat until there are no intersections; This seems the better option but could be very messy when intersecting more than 1 block at a time
I should note that I plan on having a smoother snap, not just arbitrarily to an edge, so a grid is pretty much out of the question. I am pretty confident there must be an elegant solution, I'm just not seeing it!
Here is my snapping code as it currently stands
var mousePos:Point = new Point(mouseX, mouseY);// new Point(e.stageX, e.stageY);
var nearestPoint:Point = null;
var nearestDistance:Number = 0;
for (var i:int = 0; i < mPlant.length; ++i) {
var part:PlantPart = mPlant[i];
if (part is Stalk) {
var connectionPoint:Point = (part as Stalk).getNearestConnectionPoint(mousePos);
var distance:Number = Point.distance(mousePos, connectionPoint);
if (nearestPoint == null || distance < nearestDistance) {
nearestPoint = connectionPoint;
nearestDistance = distance;
}
}
}
if (nearestPoint != null) {
mMousePointer.x = nearestPoint.x;
mMousePointer.y = nearestPoint.y;
}
You can always pre-calculate a bounding Rectangle for the stalk, and then do the snapping check against that Rectangle.
The Rectangle can be calculated using something like this (un-tested code, though)
var _bounding:Rectangle = new Rectangle(int.MAX_VALUE, int.MAX_VALUE,0,0);
for each( var part:PlantPart in mPlant)
{
if(part is Stalk)
{
_bounding.width = part.width; // Width will always be the same
_bounding.height = Math.max( _bounding.height, part.y );
_bounding.x = Math.min( _bounding.x, part.x );
_bounding.y = Math.min( _bounding.y, part.y );
}
}
I try to draw simple text in my android game on libgdx, but it's look sharp. How to make text look smooth in different resolutions? My Code:
private BitmapFont font;
font = new BitmapFont();
font.scale((ppuX*0.02f));
font.draw(spb, "Score:", width/2-ppuX*2f, height-0.5f*ppuY);
font.getRegion().getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
This gets the texture used in a BitmapFont and changes its filtering to bilinear, allowing higher resulting image quality while both up- and downscaling it at the cost of slightly slower (the difference is usually not noticeable) GPU rendering.
One solution is to use the FreeType extension to libgdx, as described here. This allows you to generate a bitmap font on the fly from a .ttf font. Typically you would do this at startup time once you know the target resolution.
Here's an example:
int viewportHeight;
BitmapFont titleFont;
BitmapFont textFont;
private void createFonts() {
FileHandle fontFile = Gdx.files.internal("data/Roboto-Bold.ttf");
FreeTypeFontGenerator generator = new FreeTypeFontGenerator(fontFile);
FreeTypeFontParameter parameter = new FreeTypeFontParameter();
parameter.size = 12;
textFont = generator.generateFont(parameter);
parameter.size = 24;
titleFont = generator.generateFont(parameter);
generator.dispose();
}
You should definitly have a quick look on custom font shaders and/or DistanceField-Fonts. They're easy to understand and similarly easy to implement:
https://github.com/libgdx/libgdx/wiki/Distance-field-fonts
DistanceFieldFonts stay smooth, even when you upscale them:
Create a .fnt file using hiero which is provided by libgdx website.
Set the size of font to 150; it will create a .fnt file and a .png file.
Copy both files into your assets folder.
Now declare the font:
BitmapFont font;
Now in create method:
font = new BitmapFont(Gdx.files.internal("data/100.fnt"), false); // 100 is the font name you can give your font any name
In render:
font.setscale(.2f);
font.draw(batch, "whatever you want to write", x,y);
In general you don't get sharp text because you are designing your game for a certain resolution and when you move to a different device, Libgdx scales everything to match the new resolution. Even with linear filtering scaling is bad on text because round corners are easily distorted. In a perfect world you would create the content dynamically at runtime according to the number of pixels available to you and not a single automatic scale would be used.
This is the approach I'm using: Building everything for small screen (480 x 320), and when you open it on a bigger resolution, I load the BitmapFont with a higher size and apply and inverse scale to the one that Libgdx will later do automatically.
Here's an example to make things clearer:
public static float SCALE;
public static final int VIRTUAL_WIDTH = 320;
public static final int VIRTUAL_HEIGHT = 480;
public void loadFont(){
// how much bigger is the real device screen, compared to the defined viewport
Screen.SCALE = 1.0f * Gdx.graphics.getWidth() / Screen.VIRTUAL_WIDTH ;
// prevents unwanted downscale on devices with resolution SMALLER than 320x480
if (Screen.SCALE<1)
Screen.SCALE = 1;
FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal("data/Roboto-Regular.ttf"));
// 12 is the size i want to give for the font on all devices
// bigger font textures = better results
labelFont = generator.generateFont((int) (12 * SCALE));
// aplly the inverse scale of what Libgdx will do at runtime
labelFont.setScale((float) (1.0 / SCALE));
// the resulting font scale is: 1.0 / SCALE * SCALE = 1
//Apply Linear filtering; best choice to keep everything looking sharp
labelFont.getRegion().getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
}
Bitmap fonts are textures and if you want to make smaller textures look smoother when you are resizing them to bigger sizes you need to make sure you use the right texture filter.
This blog post deals with such issues
With many things deprecated after the update, this is what's working for me:
public void regenerateFonts(OrthographicCamera cam, Game game) {
int size = 18;
if (cam != null && game != null) {
// camera and game are provided, recalculate sizes
float ratioX = cam.viewportWidth / game.getW();
float ratioY = cam.viewportHeight / game.getH();
System.out.println("Ratio: [" + ratioX + ":" + ratioY + "]");
size *= ratioY;
}
// font parameters for this size
FreeTypeFontParameter params = new FreeTypeFontParameter();
params.flip = true; // if your cam is flipped
params.characters = LETTERS; // your String containing all letters you need
params.size = size;
params.magFilter = TextureFilter.Linear; // used for resizing quality
params.minFilter = TextureFilter.Linear; // also
// Lato Light generator
FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal("fonts/Lato-Light.ttf"));
// make the font
fontLatoLight = generator.generateFont(params);
generator.dispose(); // dispose to avoid memory leaks
}
And when you want to render it on the screen:
// text rendering
fontLatoLight.setColor(Color.WHITE); // set color here (has other overloads too)
fontLatoLight.draw(batch, "Hello World!", xCoord, yCoord);
My Solution for smooth text with Libgdx
I use BitmapFont and I generate 3 different size same fonts using Hiero tool
example Arial 16 , Arial 32, Arial 64
I put them in my assets file and use (load) only one of them depeding on the size of screen
if(Gdx.graphics.getWidth() < (480*3)/2)
{
textGametFont = BitmapFont(Gdx.files.internal(nameFont+16+".fnt"),
Gdx.files.internal(nameFont+16+".png"), false);
}else
{
if(Gdx.graphics.getWidth() < (3*920)/2)
{
textGametFont = new BitmapFont(Gdx.files.internal(nameFont+32+".fnt"),
Gdx.files.internal(nameFont+32+".png"), false);
}else
{
textGametFont = new BitmapFont(Gdx.files.internal(nameFont+64+".fnt"),
Gdx.files.internal(nameFont+64+".png"), false);
}
}
then I use this line of code to higher result quality of down and up Scaling
textGametFont.getRegion().getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
scale the image
to handle the size of the font for all type of resolution of device I use those two functions
public static float xTrans(float x)
{
return x*Gdx.graphics.width/(YourModel.SCREEN_WIDTH);
}
public static float yTrans(float y)
{
return y*Gdx.graphics.height/YourModel.SCREEN_Height;
}
the model screen resolution that i use is
SCREEN_WIDTH = 480
SCREEN_HEIGHT = 320
Set the scale to the font
textGametFont.setScale((xtrans(yourScale)+ ytrans(yourScale))/2f);
and finally draw your text
textGametFont.draw(batch, "WINNER !!", xTrans(250), yTrans(236));
Hope this was clear and helpful !!!
private BitmapFont font;
font = new BitmapFont();
font.scale((ppuX*0.02f));
font.draw(spb, "Score:", width/2-ppuX*2f, height-0.5f*ppuY);
Check out [this](http://www.badlogicgames.com/wordpress/?p=2300) blog post.
??? This just explains how to use the .scale() method which I'm stating is deprecated in the current release.
In scene2d, if you want apply antialiasing to all your labels, put this on constructor of your first screen:
skin.getFont("default-font").getRegion().getTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
This is the first screen in my game:
...
public class MainMenuScreen implements Screen {
public MainMenuScreen() {
...
skin.getFont("default-font").getRegion().getTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
}
}
Font name is in ui.json file, check for BitmapFont and Label$LabelStyle section:
"com.badlogic.gdx.graphics.g2d.BitmapFont": {
"default-font": {
"file": "default.fnt"
}
},
"com.badlogic.gdx.scenes.scene2d.ui.Label$LabelStyle": {
"default": {
"font": "default-font",
"fontColor": "white",
}
},