Category Archives: PooperPig

Anything to do with PooperPig that isn’t categorised elsewhere

Symbolic Linking for Cocos

I’ve read loads of times about being able to have one version of cocos2d-x installed for all my projects – but never really followed up – but cocos is so big, having several versions really is a pain, so I decided to do it.
And it is easy!

I have an existing project at

/Users/PooperPig/Documents/Projects/MyGame

Inside this project I have the cocos2d folder, containing all the cocos2d stuff, and all the other folders like Classes, Resources and all the proj.xxx folders.

First things first, I grab the latest and greatest version of cocos2d and put it in my Projects folder, in a folder called cocos2d-x-3.7.

In my case, this is what I did to get the latest (you can just download the zip if you want – I just did this bit in case I want to make changes and submit them back to cocos2d-x)

$ git clone git://github.com/cocos2d/cocos2d-x.git
$ cd cocos2d-x
$ git checkout v3
$ ./download-deps.py
$ git submodule update --init

Now I go to my game project folder, and delete the cocos2d folder! Ok – so I really just copied it somewhere just in case – but you can delete it safely – after all you can always download it again!

So now I need to create a symbolic link from my project folder, pointing to the cocos2d-x-3.7 folder.

ln -s /Users/PooperPig/Documents/Projects/cocos2d-x-3.7 cocos2d

That’s really it! I can open up my project in XCode and run it just like normal!

The obvious question now is “What do I do when I want to upgrade the version of Cocos2d-x?”

Easy peasy.

Once you have created your new cocos2d-x-3.n directory, change to the project directory and use

ln -nfs /Users/PooperPig/Documents/Projects/cocos2d-x-3.8 cocos2d

this command simply replaces the current symbolic link with a new one.

Now you can test your project – and if it doesn’t work, you can use the ln -nfs again to point it back to old version!

A Git by any other name

Ages ago I used Git for PooperPig – but had some problems & went back to SVN (which I had used previously, and so was familiar with).

Well, while faffing about recently, I wanted to go back to a previous version – and found it relatively complex in SVN with the support of XCode (i.e. there’s no support for going back at all!)

For this (and a couple of other) reasons, I have decided to try Git again.

So, I’m giving BitBucket a go.

It’s free (which is a good price) and they have all sorts of other tools (for cash) that I might decide to use…

So far it’s proving relatively easy – although I am really puzzled by the fact that can’t find any .svn files in my project – I wanted to remove them before pushing my project to git!

Right now, I”m watching the file upload to Bitbucket (the BitBucket site is really helpful, lots of tutorials and stuff to help you start – so I’m just following their instructions)

What will be interesting is how XCode handles it once it’s there!

Wish me luck.

simplicity is Always the best idea

After struggling with new Scenes, and trying to figure out how to cleanly handle having a pause screen, I decided to step back and look at it a different way.

What do I really want to achieve?

Show a new layer, with some options on it (like continue, restart, choose a level)

so why am I messing around with scenes? well, the reason was because I thought thats how it should be done. But why can’t I just shove a pause layer on top of the game?

That’s what I just did – and it works much better, with much less code.

the only thing it doesn’t do is transitions – which if I want them, I am sure I could fake up anyway!

So I introduced a pauseGame() function…

void GameController::pauseGame()
{
	stopAllFunctions();
	Globals::holdTouchFunctions = true;
	Globals::gamePaused = true;
	this->unscheduleUpdate();
}

stopAllFunctions is just a method that stops any of the user-functions (jumping, pooping etc.) that have started already; this is important if the user presses Fart and pause – or we will hear farting int eh pause screen!

holdTouchFunctions is just a flag to see if touches are going to be processed in the game. As I use areas of the screen for control, I need to disable this when paused to avoid the pig trying to jump if that area of the screen is touched.

gamePaused is a flag that I check in the main update() method – if true, I do nothing, so nothing gets updated! Simples!

I have a corresponding unPauseGame() method too – just sets the flags to false and calls scheduleUpdate.

I had a real problem when paused that, despite resetting all physical objects, my box2d object seemed to magically retain some residual velocity.
I gave up after a while and resorted to setting it back to zero in the update method – it’s only used in debug mode when I’m editing – so no real overhead.

A New Beginning

Of course I wrote PooperPig by starting with the fun bit – the game engine itself.

 

So when you start the game it just started playing.

Then I added a menu option, so I can choose levels, and that sort of stuff.

But I just kinda shoved it in there – and it was a bit of a hack.

Then I added a spawning sound to my entities. So now when I run the game I see the menu, but hear my spawning sound – because in the background it starts then pauses the game.

So now I’m changing tack, ripping it out and replacing it with a more sensible process.

Attempt 1: Coded and hacked my way through it without thinking too much- it all worked, but there were lots of small changes all through the code. It all worked more or less, but the terrain sprites suddenly had a black background.

Couldn’t figure it out quickly, so undid most of the changes and started again – with a more methodical approach (you know, like thinking first!)

We’ll see what happens!

 

… Well, what happened was it went horribly wrong!

Can;t figure out how to do the simplest thing:

Start the game, show a ‘pause’ screen.

Select a level in the pause screen

play that level

Press a pause button

display the pause screen

either select a level or un-pause

I’m tired and I have a cold. That’s my excuse!

Maybe tomorrow!

Spawn of Animation

Up until now I have had animations for the following:

  1.     Default ,
  2.     Dying
  3.     Dead 
  4.     Flying 
  5.     Spitting 
  6.     Eating 
  7.     Farting 
  8.     Pooping

and I decided to create another for Spawning.

I don’t use cocos2d-x own Animation classes – I use  my own, because, honestly, I didn’t think the cocos2d-x animation classes gave me enough control when I first looked at them; subsequently I think maybe they do, but I have my own class which is nicely flexible!

Adding a new type is a simple matter of adding an entry to a pList, together with a string representation – so I added spawning with string “Spawning”

Now I can call Animation::startAnimation(enum::Spawning) and it looks for an animation XXXXSpawning in the collection of animations for that particular Visible Object (another of my classes).

Encountered a couple of issues.

Firstly, if you die while farting(!) I needed to stop the farting animation; I’d just used showAnimation(enum::Default);

And in my reset function I call StopFarting();

So, at the start of a level, it showed the default animation instead of the spawning animation. easily fixed.

The other problem was with jumping. I have a flying animation shown when Poopy jumps. so when he lands, detected using a sensor, I changed the animation to default. Of course, this also stuffed up the spawning animation, as during spawning, Poopy would land, and the spawning animation would stop.

Again, not a difficult fix.

Now all I have to do is actually design a spawning animation! (for testing I used the dying animation, shown backward.)

These changes also gave me a new method, continuePreviousAnimation()

so now, if I am in the middle of one animation, and go off to do something else, instead of reverting back to the default animation, I can continue with the one I was showing before.

 

There’s a hole in the sky!

Well, I thought it was about time I started blogging again – if only to make me do more to PooperPig!

I just added the ability to change the surface (floor or roof) collision mask – this will let me set up places where certain entities can go through the floor or roof.

Why would I want to do that?

My idea here is that I can put some baddies above the roof. they will be activated when PooperPig comes close to them, and start following him (if they’re that sort of baddy) but can’t get to him.

Then, at some point, there’s a hole in the roof through which they can fall.

Depending on the roof height and textures, they may be invisible to the player, so the first they will know is when a bunch of baddies drop out of the sky 🙂

First try was sort of OK. It all works, but in testing it I changed the texture of the ‘sub-terrain’ above the roof – and what actually changed was the texture below the floor!

Time to put by debugging hat on!

That was easy!

Because roof and floor are so similar, but my C++ knowledge is so limited, I have a bunch of code for handling floor and roof that is similar but not quite identical.

Of course, I should have a single method so that changes to the calculation needn’t be done in two places – but it’s quite neet the way it works & I don’t need to change it at the moment. Perhaps one day!

What had happened was that in my ‘drawRoofSubterrain()’ method I had a call to the method ‘drawSubterrainFloorPortion();

Oops!

Now that’s fixed, it looks fine (i.e. the terrain above the roof changes where the ‘hole’ should appear. Now to add a baddy!

Baddy added. He falls through the hole, fine!

Unfortunately this has revealed a bug in my baddy code; the baddy accelerates toward Poopy – but if he misses (which he does if he’s stuck above the roof) he doesn’t turn around and come back for another try!

Aha! turns out that friction was set to 0 on the roof and on the baddy so applying the opposite force never changed his direction and wold at best have bought him to a stop!

Fixed that, and (having added friction) the baddy didn’t move at all!

Of course! the force being applied for chasing Poopy was too small.

Adjusted that and now all is well. Baddy chases Poopy, while stuck above the roof, until it comes across the hole, when it drops down.

Spreadsheet of Design Resolutions

I’m still playing with multi-resolution support using cocos2d-x

 

I set up a spreadsheet to help me look at the potential different device sizes, and what resource sizes would be suitable for each

 

in case it’s useful – https://docs.google.com/spreadsheets/d/11YZN4cajRCUktdq72HQbLZfc6vreBA78-KDjGvR46Jw/pubhtml?gid=0&single=true

On the left is a list of most of the device resolutions available

To the right there are then four areas, based on four resolutions of resources (sort of Low, medium High Res and Ultra high res)

It calculates the ratios etc. based on a fixed height – because that’s what I want – but you could change it to be based on width – or anything you like!

 

As it is set up, I have added a column against each device resolution, showing which of the four resources gives the best option for that size.

I think it is fairly self-explanatory – but then again I wrote it, so feel free to ask me anything!

 

Cocos2d-x v3.x Multi Resolution Support

How Cocos2d-x Multi-resolution support works

Introduction

Despite reading Wikis, experimenting and getting lots of help in the forums, my brain just wasn’t ‘getting’ how multi-resolution support works in cocos. I don’t know why – perhaps I am just getting too old. But the best way I have of learning something is to teach someone else – so this is my explanation – maybe it will help someone else; it will certainly help me!

The Problem

If I develop an App using cocos2d-x I might decide to design it for a resolution of 1024 x 768 – which is a common screen resolution on both tablets, phones and desktop computers of yore.   So I design all of my assets – sprites, background images etc. – to suit this resolution.   But what about all of the other devices out there that run at different resolutions?   There are two things I need to do:

  1. Provide assets for other resolutions
  2. Provide scaling

Providing assets for other resolutions is fine –up to a point. I might provide images for 2048 x 1536, 1024 x 768 and 512 x 394 – and that will give me better quality generally than scaling all of my assets at runtime. But that still doesn’t solve the issue if I want to run my app on a device that has a different resolution to any of those for which I have provided assets.   What I need to do (somehow) is to take my assets and scale them (up or down) so that they fit the screen resolution as well as possible.   I find real examples easiest to deal with – so here’s our issue – I’ll talk through the problems and the solution step by step.

Design size 1024 x 768

The design size is the size I am programming for initially. Knowing the design size, I will (more accurately, cocos2d-x will) be able to scale assets correctly to other resolutions.

Screen size 1024 x 768 and 640 x 512

This is the physical size of the screen I am going to get the application running on. I’m looking at two resolutions because it makes sense to first make sure my screen looks correct at exactly my design resolution, then to try it with other resolutions.

Resolution Policy

Cocos2d-X offers different ‘policies’ which determine exactly how it handles scaling your assets to fit. These are described in the code as follows:

// The entire application is visible in the specified area without trying to preserve the original aspect ratio.
// Distortion can occur, and the application may appear stretched or compressed.
    EXACT_FIT,
// The entire application fills the specified area, without distortion but possibly with some cropping,
// while maintaining the original aspect ratio of the application.
    NO_BORDER,
// The entire application is visible in the specified area without distortion while maintaining the original
// aspect ratio of the application. Borders can appear on two sides of the application.
    SHOW_ALL,
// The application takes the height of the design resolution size and modifies the width of the internal
// canvas so that it fits the aspect ratio of the device
// no distortion will occur however you must make sure your application works on different
// aspect ratios
    FIXED_HEIGHT,
// The application takes the width of the design resolution size and modifies the height of the internal
// canvas so that it fits the aspect ratio of the device
// no distortion will occur however you must make sure your application works on different
    // aspect ratios
    FIXED_WIDTH,

I’m going to choose FIXED_HEIGHT as I want there to be no gaps around the screen, but I don’t mind if the left or right of my background are cut off a little bit. What fixed height policy will do is to scale my images so that the height of the image fills the height of the device – so as long as the width of my image, after this scaling, is equal to or greater than the device width, all will look fine.

The Solution

Cocos2d-X will look at our design size (1024 x 768) and at our screen size. (640 x 512) These two rectangles have different aspect ratios.

1024 / 768 = 1.333

640 / 512 = 1.25

So the shape of each is different – but we definitely don’t want to ‘squish’ our graphics by scaling our assets differently in the X and Y direction.

To scale the design size to the screen size we could do one of two things…

a)    scale so that the height fits exactly in the new size
b)   scale so that the width fits exactly in the new size.

If we do a) then we would need to scale our design size by the ratio of the heights – i.e. 512 / 768 = 0.66667

If we do b) then we would need to scale our design size by the ratio of the widths – i.e. 640 / 1024 = 0.625

So, if we had an asset that was the size of our full screen (1024 x 768)   Scaling using option a) would give (1024 * 0.66667) x (768 * 0.66667) which = 682 x 512.

Scaling using option b) would give (1024 * 0.625) x (768 * 0.625) which = 640 x 480

You can see that option a) will give us an asset that is wider than our screen, and exactly the same height. So, if we used this, we would fill the screen, but lose the sides of our image.

Using option b) would give us an asset that is the same width as our screen, but smaller than its height – which means we would have borders at the top and bottom of the screen – which we don’t want (which is why we asked for the FIXED_HEIGHT policy!)   So cocos2d-x will use option a) because of the FIXED_HEIGHT policy.

Recall, we have a 1024 x 768 image being displayed on a 640 x 512 screen. The image is scaled to 682 x 512.

We will lose 21 pixels on the left and 21 pixels on the right of our image, but there will be no gaps.

We could try this the ‘other way around’. If we had an image 640 x 512 and went to display it on a 1024 x 768 screen, to scale the height we need to multiply 512 by 1.5 and scale the width by the same amount, 640 x 1.5 = 960.l

So our image would be scaled to 960 x 768 – and we’d have black bars to the left and the right!   Depending on what devices you are trying to support, and memory availability etc. you need to make a decision as to what images you will supply to support those resolutions.   For example, you could replace the 640 x 512 image with one at 683 x 512. Why 683? Because 683 * 1.5 = 1024.5 – so providing that image would scale up to fit a 1024 x 768 device. But remember it would also truncate pixels (43 of them) on the sides.

For the purposes of this article, let’s go back to using a design size of 1024 x 768 displaying on a 640 x 512 device

So far we have just been talking about the whole screen – but what happens when we display a sprite?   Think about scaling a large sprite that we are using as the ground in a game. At our design size of 1024 x 768 we will provide an asset that is 1024 pixels wide (so it covers the whole width of the screen) and is, say, 77 pixels high (which happens to be about 1/10 of the height of the design screen height, which makes my maths easier!

To make It sit at the bottom of the screen at 1024 x 768 we would set the anchorpoint to (0.5,0) and set the position to ((screenwidth / 2) , 0)   Cocos will take our 1024 x 77 sprite and scale it by .66667 giving us a sprite 682 x 51.   This will be positioned mid-screen on the x axis, and at the bottom on the y axis – exactly what we want.

Here’s my grass.png.

 

grass.png

It is 1024 x 77 pixels. I’ve surrounded and crossed it with red lines so it is easy to see which areas are shown when it is displayed   Here’s the program running at 1024 x 768.

screen1

You can see the entire border.

Running at 640 x 512

screen2

The image is central, scaled to the same proportion of the height, but the right and left edges of the grass image are truncated.   Here’s the code that allows us to do this:

Appdelegate.cpp

bool AppDelegate::applicationDidFinishLaunching() {
    // initialize director
    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();
    if(!glview) {
        //glview = GLViewImpl::create("My Game");
        // set the size of the screen we're testing on
        glview = GLViewImpl::createWithRect("MultiRes", Rect(0,0,640,512));
        director->setOpenGLView(glview);
    }
    
    // Tell cocos our design resolution and our resolution policy
    glview->setDesignResolutionSize(1024, 768, ResolutionPolicy::FIXED_HEIGHT);

    // turn on display FPS
    director->setDisplayStats(true);

    // set FPS. the default value is 1.0/60 if you don't call this
    director->setAnimationInterval(1.0 / 60);

    // create a scene. it's an autorelease object
    auto scene = HelloWorld::createScene();

    // run
    director->runWithScene(scene);

    return true;
}


 

HelloworldScene.cpp

bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    
    CCLOG("visibleSize is (%.2f, %.2f)", visibleSize.width, visibleSize.height);
    CCLOG("origin is (%.2f, %.2f)", origin.x, origin.y);
    
    // add our terrain sprite
    auto sprite = Sprite::create("grass.png");
    sprite->setAnchorPoint(Vec2(0.5,0));
    
    // position the sprite on the center of the screen
    sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, 0 + origin.y));

    // add the sprite as a child to this layer
    this->addChild(sprite, 0);
    
    return true;
}

In AppDelegate.cpp we make the changes in bold – this sets up the window on the Mac (or on Windows presumably) to have a content size of 640 x 512. This is a great way of testing different resolutions without running emulators or buying lots of devices – just set the window size to the resolution of the device you want to test for.

In HelloWorldScene.cpp I’ve replaced the init() method to make it simple. After getting the visibleSize and the origin, I CCLOG them – it’s interesting to look at the output and see if it’s what you expect.

When I run with a design resolution of 1024 x 768 and a screen resolution of 1024 x 768 then I expect the visibleSize to be 1024, 768 and the origin to be 0,0

When I run with a design resolution of 1024 x 768 and a screen resolution of 640 x 512 my visible size is (960 x 768) and my origin (32,0)

This puzzled me at first – what is that 960? Didn’t we calculate the scaled width to be 682 pixels?

The answer is that visible size and offset refer to the design size and not the visible size. So 960 pixels from my original image will be visible in 682 pixels of screen.   I’ll repeat that.   visibleSize is telling us that 960 pixels of our 1024 pixel design width will be visible – scaled to fit our physical screen – but we don’t care about that scaling, as cocos2d-x is going to take care of it for us.

The full 768 pixels height of our design will be visible. Origin is telling us that 32 pixels left and right are chopped off from our design (i.e. before scaling) – and no pixels are chopped off top and bottom.

For our grass, we used the offset to adjust the position of our sprite – and we need to remember to do this for any sprite where we want it to appear at the same relative location on the screen at different resolutions.

For example, if we had a player sprite positioned at (0,100), using the figures from above (design resolution 1024 x 768, screen resolution 640 x 512) the sprite would actually appear 32 pixels to the left of the screen – so it might be invisible! So we need to position it to (0 + origin.x, 100 + origin.y) and instead of (0,100) they now are positioned at (32 , 100).   The y coordinate is the same – cocos will scale the 100 appropriately to fit the screen size. The x coordinate has the effect of moving the sprite 32 pixels to the right at the design resolution before cocos scales it to be in the right position on the screen.

Assets

Now we know how to position stuff so it looks the same in different resolutions, we come to the scaling problem.   If I supply a sprite and allow it to be scaled to any resolution the game is run at, it can look pretty bad. An extreme example would be a sprite has single-pixel width vertical lines; during scaling down, these lines might disappear completely. During scaling up to a larger size, some lines may end up being 2 physical pixels wide, while others are a single pixel wide. (This honestly depends on the algorithm used by the software for scaling, and I haven’t investigated cocos2d-x scaling – but the premise is the same; scaling can make sprites look bad!)

The answer, of course, is to provide different sprite assets for different resolutions. Back in the day, the resolutions generally covered were standard iphone, ipad and hd iphone (480 x 320, 1024 x 768 and 960 x 640) and we could provide assets for all three. Cross platform resolutions just used the closest resolution assets available. But now there are 2048 x 1536 ipads, 1136 x 640 iphones and the iPhone 6 comes in 1334 x 750 and 1920 x 1080 flavours!   Providing assets for any or all of these resolutions is pretty much up to you as a developer – you may want to provide assets for all resolutions if the look of your sprites is really important – or fewer if you don’t mind losing some resolution buy scaling while saving the overall size of your application.

Cocos2d-x v3 has a great way of providing you with all the help you need to provide the assets for as many or as few resolutions as you want to support.   First of all we need to create the assets and add them as resources to our application; but add different resolution resources in different folders. (I’ve called my folders HD, SD and UHD for 1024 x 768, 480 x 320 and 1920 x 1080 based graphics – but you can call them whatever makes sense to you – and provide them at whatever resolutions you want.   Now, to tell cocos2d-x how to find the right resources   In Appdelegate.cpp I add the following…

// Information about resources
typedef struct tagResource
{
    cocos2d::Size size; // The size that this resource is designed for
    cocos2d::Size useIfScreenOverSize; // If teh screen size is more than this value, this resource is a valid choice
    char directory[100]; // The name of the directory containing resources of this type
} Resource;

// Define all our resource types and locations
static Resource largeResource  =  { Size(1920, 1080), Size(1024, 768), "UHD"};
static Resource mediumResource =  { Size(1024, 768), Size(750, 544),  "HD" };
static Resource smallResource  =  { Size(480, 320), Size(0, 0),   "SD" };

// Declare the resolution we designed the game at
static Size designResolutionSize = Size(1024, 768);

// Declare an array containing the resource descriptions, from largest to smallest
static std::array<Resource,3>  resources{{largeResource, mediumResource, smallResource}};


bool AppDelegate::applicationDidFinishLaunching() {
    // initialize director
    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();
    if(!glview) {
        glview = GLViewImpl::createWithRect("MultiRes", Rect(0,0,640,480));
        director->setOpenGLView(glview);
    }
    
    // Tell cocos our design resolution and our resolution policy
    glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::FIXED_HEIGHT);

    // The vector we will use to build a list of paths to search for resources
    std::vector searchPaths;
    
    // Get the actual screen size
    Size frameSize = glview->getFrameSize();
    
    CCLOG("FrameSize is (%.0f, %.0f)", frameSize.width,frameSize.height);
    
    // Define a silly scale factor so we know when we have calculated it
    float scaleFactor = -1;
    
    // Look through our resource definitions
    for (auto resource : resources)
    {
        // If the screen is wider or higher than the resolution of the resource...
        if (frameSize.width > resource.useIfScreenOverSize.width )//|| frameSize.width > resource.useIfScreenOverSize.width)
        {
            // Add this directory to the search path
            searchPaths.push_back(resource.directory);
            CCLOG("searching in %s", resource.directory);
            // If we haven't already determined the scale factor, calculated it based on this resources resolution
            if (scaleFactor == -1)
                scaleFactor = resource.size.height /designResolutionSize.height;
            break;
        }
    }
    
    
    director->setContentScaleFactor(scaleFactor);
    FileUtils::getInstance()->setSearchPaths(searchPaths);
    
    CCLOG("Scale Factor = %f", scaleFactor);
    
    // turn on display FPS
    director->setDisplayStats(true);

    // set FPS. the default value is 1.0/60 if you don't call this
    director->setAnimationInterval(1.0 / 60);

    // create a scene. it's an autorelease object
    auto scene = HelloWorld::createScene();

    // run
    director->runWithScene(scene);

    return true;
}



Hopefully there are enough code comments for this to make sense.   One thing to note in particular is the way I select which resolution(s) to use. Most code snippets you see simply take the resolution of the asset and compare with the resolution of the device – and that generally means you will scale assets down. But you might not want to!   (I confess, I haven’t experimented too much, but it strikes me that it might look better to scale a small asset up by a small amount than to scale a larger asset down by a large amount)   so I introduced the concept of the useIfScreenOverSize property. So I can say   “I have an asset created at 1024 x 768 and want to use it if the device size is greater than 750 x 544 – and a resolution lower than 750 x 544 will use the next lowest resolution asset.

Another point of note is that I ‘break’ after finding the folder I want to support the resolution. This isn’t strictly necessary, you can add as many folders to the search path as you like, and cocos will keep searching until it finds the file matching the name you requested.

This can be useful in keeping app sizes down. You may have a graphic resource where size really isn’t important (maybe a cloud in the sky, for example) so you provide that image only at a lower resolution. Adding the lower resolution folders to the search path allows cocos to find the resource, but it will only scale it as if it were designed at the design size you specified – but sometimes that’s all that’s required for purely decorative images.