Page 1 of 2

Question about scaling sprites

Posted: October 8th, 2019, 5:05 am
by WilsonHuang
After learning Intermediate Tutorial 12 [Animated Sprite Character]. I want to add the scaling effect to the sprite, which you can enlarge or shrink sprites. My code seems okay when enlarge the sprites. However, I have invisible cover when I shrink the sprites. How do I fix it?

Here is my code:

Code: Select all

#include "Graphics.h"
#include "Colors.h"
#include <assert.h>
#include <math.h>
namespace SpriteEffect
{
	class Scale
	{
	public:
		Scale(float scale = 1.0f)
			:scale(scale)
		{
			assert(scale > 0.0f);
		}
		void operator()(Color sourceColor, int x, int y, Graphics& gfx)
		{
			if (sourceColor != chroma)
			{
				for (int dy = 0; dy < (int)ceil(scale); dy++)
				{
					for (int dx = 0; dx < (int)ceil(scale); dx++)
					{
						int resultX = (int)ceil(x * scale + dx);
						int resultY = (int)ceil(y * scale + dy);

						if (resultX < 0)
						{
							resultX = 0;
						}
						if (resultY < 0)
						{
							resultY = 0;
						}

						if (resultX >= gfx.ScreenWidth)
						{
							resultX = gfx.ScreenWidth - 1;
						}
						if (resultY >= gfx.ScreenHeight)
						{
							resultY = gfx.ScreenHeight - 1;
						}

						gfx.PutPixel(resultX, resultY, sourceColor);
					}
				}
			}
		}
	private:
		Color chroma = Colors::Magenta;
		float scale;
	};
}
Usage:

Code: Select all

gfx.DrawSprite(position.x, position.y, frames[currentFrame], surface, SpriteEffect::Scale(0.9f));
Invisible Cover.png
(9.22 KiB) Not downloaded yet

Re: Question about scaling sprites

Posted: October 9th, 2019, 5:14 am
by albinopapa

Code: Select all

scale = .9f;
for (int dy = 0; dy < (int)ceil(.9f); dy++)
{
	for (int dx = 0; dx < (int)ceil(.9f); dx++)
Does this help?
Basically, you are only iterating from 0 to 1 ( ceil( .9f ) = 1.f ).

Your code works for scaling up because the ratio for the sprite pixel to screen pixel is 2:1 or something like that, so looping 2x2 for a scale factor of 2.f would work. For minification, you'd need to skip over some sprite pixels for each screen pixel. In order for this to work using an effect, your effect class would need more information. Basically, while magnification works, scaling is more of a transformation than a visual effect.

The 3D Fundamentals tutorials covers transformations.

Re: Question about scaling sprites

Posted: October 9th, 2019, 7:17 pm
by AleksiyCODE
Well, i made a quick (shitty) animated sprite tester for my friend, and there is simple scaling built in (arrow keys change animation playback speed and size)
https://gitlab.com/AleksiyCODE/st
you may be able to get some ideas from there (but the code is shit)

Re: Question about scaling sprites

Posted: October 10th, 2019, 12:48 am
by WilsonHuang
AleksiyCODE wrote:
October 9th, 2019, 7:17 pm
Well, i made a quick (shitty) animated sprite tester for my friend, and there is simple scaling built in (arrow keys change animation playback speed and size)
https://gitlab.com/AleksiyCODE/st
you may be able to get some ideas from there (but the code is shit)
Thanks, I'll give it a look!

Re: Question about scaling sprites

Posted: October 10th, 2019, 1:59 am
by WilsonHuang
albinopapa wrote:
October 9th, 2019, 5:14 am

Code: Select all

scale = .9f;
for (int dy = 0; dy < (int)ceil(.9f); dy++)
{
	for (int dx = 0; dx < (int)ceil(.9f); dx++)
Does this help?
Basically, you are only iterating from 0 to 1 ( ceil( .9f ) = 1.f ).

Your code works for scaling up because the ratio for the sprite pixel to screen pixel is 2:1 or something like that, so looping 2x2 for a scale factor of 2.f would work. For minification, you'd need to skip over some sprite pixels for each screen pixel. In order for this to work using an effect, your effect class would need more information. Basically, while magnification works, scaling is more of a transformation than a visual effect.

The 3D Fundamentals tutorials covers transformations.
Thanks for reply.
I think I found the problem. The problem is in the DrawSprite function.
In DrawSprite function, there are code to check whether the sprite is outside the screen and clip the sprite.

Code: Select all

		//top-left
		if (x < clip.left)
		{
			source.left += clip.left - x;
			x = clip.left;
		}
		if (y < clip.top)
		{
			source.top += clip.top - y;
			y = clip.top;
		}

		//bottom-right
		if (x + source.GetWidth() > clip.right)
		{
			source.right -= x + source.GetWidth() - clip.right;
		}
		if (y + source.GetHeight() > clip.bottom)
		{
			source.bottom -= y + source.GetHeight() - clip.bottom;
		}
The original sprite is out of the screen and being clipped. This effects the scale down version (also be clipped)
It seems I need to do some modification to the DrawSprite function to make the transformation works.
Also, just as you said, " scaling is more of a transformation than a visual effect". I may move the transformation code to the other place.

BTW, which episode of 3D Fundamentals tutorials covers transformations? I'll take a look Thanks!

Re: Question about scaling sprites

Posted: October 10th, 2019, 7:19 am
by albinopapa
Well, whoops, it's the Advanced series.

Transformations/Scaling/Translation

Re: Question about scaling sprites

Posted: October 10th, 2019, 6:48 pm
by albinopapa
Here's my implementation. The clipping algorithm is slightly different than chili's current implementation, but this is how he taught it in the old series, not sure why he changed it.
Spoiler:

Code: Select all

	template<typename E>
	void DrawSprite( int x, int y, RectI srcRect, const RectI& clipRect, const Surface& s, E effect, float scale = 1.f )
	{
		auto dstLeft = std::max( -x, clipRect.left ) + x;
		auto dstTop = std::max( -y, clipRect.top ) + y;
		auto dstRight = std::min( clipRect.GetWidth() - x, int( srcRect.GetWidth() * scale ) ) + x;
		auto dstBottom = std::min( clipRect.GetHeight() - y, int( srcRect.GetHeight() * scale ) ) + y;

		auto const srcScale = 1.f / scale;
		for( int dy = dstTop; dy < dstBottom; dy++ )
		{
			auto const sy = int( ( dy - dstTop ) * srcScale );
			for( int dx = dstLeft; dx < dstRight; dx++ )
			{
				auto const sx = int( ( dx - dstLeft ) * srcScale );
				effect( dx, dy, s.GetPixel( sx, sy ), *this );
			}
		}
	}
I put the scale parameter at the end, so it could be defaulted. This makes it so changes don't have to be made for routines you've already written and don't need scaling.

NOTE: This is just the final DrawSprite function, you'll need to add the float scale parameter to each of the DrawSprite functions to get this to work.

NOTE2: Corrected code in this post.

Re: Question about scaling sprites

Posted: October 10th, 2019, 8:45 pm
by albinopapa
sprite_scaling.png
(66.31 KiB) Not downloaded yet
Here's a sample of the Link sprite scaled from 10% to 200% ( incremented by 10% each time ).

Re: Question about scaling sprites

Posted: October 10th, 2019, 8:48 pm
by albinopapa
A more generic solution would be to use the 3D Fundamentals framework and method. In that, you create a quadrangle, apply transforms to the vertices ( translation, rotation, scaling, etc... ) and interpolate between the vertices pixel to pixel.

Re: Question about scaling sprites

Posted: October 10th, 2019, 9:31 pm
by albinopapa
Well, there's an error in my code. It assumes a single sprite, not a sprite sheet.