We are currently migrating Bugzilla to GitHub issues.
Any changes made to the bug tracker now will be lost, so please do not post new bugs or make changes to them.
When we're done, all bug URLs will redirect to their equivalent location on the new bug tracker.

Bug 4313

Summary: Don't copy all right and down pixels in SoftwareRenderer with rotate and odd sizes
Product: SDL Reporter: Nik <truecat17>
Component: renderAssignee: Sam Lantinga <slouken>
Status: NEW --- QA Contact: Sam Lantinga <slouken>
Severity: normal    
Priority: P2 CC: sezeroz, sylvain.becker
Version: 2.0.8   
Hardware: x86_64   
OS: Linux   
Attachments: patch
test case

Description Nik 2018-10-14 00:30:54 UTC
#include <iostream>

#include <SDL2/SDL.h>


int main(int, char **) {
	//odd values:
	const size_t W = 3;
	const size_t H = 5;

	//any color
	const Uint32 DEFAULT_COLOR = (128 << 24) + (128 << 16) + (128 << 8) + 255;


	SDL_Surface *orig = SDL_CreateRGBSurfaceWithFormat(0, W, H, 32, SDL_PIXELFORMAT_RGBA32);
	Uint32 *pixels = (Uint32*)orig->pixels;
	for (size_t i = 0; i < W * H; ++i) {
		pixels[i] = DEFAULT_COLOR;
	}

	SDL_Surface *res = SDL_CreateRGBSurfaceWithFormat(0, W, H, 32, SDL_PIXELFORMAT_RGBA32);
	SDL_Renderer *renderer = SDL_CreateSoftwareRenderer(res);

	SDL_Texture *origTexture = SDL_CreateTextureFromSurface(renderer, orig);
	SDL_RenderCopyEx(renderer, origTexture, 0, 0, 180, 0, SDL_FLIP_NONE);

	pixels = (Uint32*)res->pixels;

	//all right pixels
	for (size_t y = 0; y < H; ++y) {
		Uint32 color = pixels[y * res->pitch + (W - 1) * 4];
		if (color != DEFAULT_COLOR) {
			std::cout << "Error on pixel <" << (W - 1) << ", " << y << ">\n";
		}
	}

	//and all down pixels
	for (size_t x = 0; x < W; ++x) {
		Uint32 color = pixels[(H - 1) * res->pitch + x * 4];
		if (color != DEFAULT_COLOR) {
			std::cout << "Error on pixel <" << x << ", " << (H - 1) << ">\n";
		}
	}

	return 0;
}
Comment 1 Sylvain 2018-10-15 11:29:16 UTC
your test case is incorrect because your look at the wrong position:
not 
  Uint32 color = pixels[y * res->pitch + (W - 1) * 4];
but		
  Uint32 color = pixels[y * (res->pitch / 4) + (W - 1)];

and not
  Uint32 color = pixels[(H - 1) * res->pitch + x * 4];
but
  Uint32 color = pixels[(H - 1) * (res->pitch / 4) + x];
  

And you also need to retrieve the rotated surface by calling: 
  SDL_RenderReadPixels(renderer, NULL, SDL_PIXELFORMAT_RGBA32, pixels, res->pitch);



There is one pixel shift, but as you said, this is because of odd width/height. 
According to SDL_render.h doc, if you don't provide the center, "rotation will be done around dstrect.w/2, dstrect.h/2).".
So I am not sure this is a bug.

if you want a perfect 180 rotation, you can use flip flags.
Comment 2 Nik 2018-10-15 16:31:52 UTC
>your test case is incorrect because
Oops, I made some stupid mistakes in first, sorry

>SDL_RenderReadPixels
SoftwareRenderer draw directly on surface, is not?
In any way, it string is not affects


Here is new version of test-code:

#include <iostream>

#include <SDL2/SDL.h>


int main(int, char **) {
	//odd values:
	const size_t W = 3;
	const size_t H = 5;


	SDL_Surface *orig = SDL_CreateRGBSurfaceWithFormat(0, W, H, 32, SDL_PIXELFORMAT_RGBA32);

	//any color
	const Uint32 DEFAULT_COLOR = SDL_MapRGBA(orig->format, 128, 128, 128, 255);


	Uint32 *pixels = (Uint32*)orig->pixels;
	for (size_t i = 0; i < W * H; ++i) {
		pixels[i] = DEFAULT_COLOR;
	}

	SDL_Surface *res = SDL_CreateRGBSurfaceWithFormat(0, W, H, 32, SDL_PIXELFORMAT_RGBA32);
	SDL_Renderer *renderer = SDL_CreateSoftwareRenderer(res);

	SDL_Texture *origTexture = SDL_CreateTextureFromSurface(renderer, orig);
	SDL_RenderCopyEx(renderer, origTexture, 0, 0, 180, 0, SDL_FLIP_NONE);

	pixels = (Uint32*)res->pixels;
	for (size_t y = 0; y < H; ++y) {
		for (size_t x = 0; x < W; ++x) {
			Uint32 color = pixels[y * W + x];
			std::cout << (color == DEFAULT_COLOR) << ' ';
		}
		std::cout << '\n';
	}

	return 0;
}
Comment 3 Nik 2018-10-15 16:39:38 UTC
>So I am not sure this is a bug.
But it's 100% bug, try rotate surface 1x1 - get pixel with value 0

>if you want a perfect 180 rotation, you can use flip flags.
It's workaround, I need rotate to 180 correctly
And 90 & 270 rotation incorrectly on odd size too
Comment 4 Sylvain 2018-10-15 20:50:33 UTC
Created attachment 3374 [details]
patch

You're right, no need of SDL_RenderReadPixels.

I can propose this patch that seems working but I haven't tested much outside of the test-case. Please give a try with various rotations to see if it holds.
Comment 5 Sylvain 2018-10-15 20:52:00 UTC
It fixes useless colorkey error message in SDL_rotate.c.
Comment 6 Nik 2018-10-17 20:02:53 UTC
It seems to be working
Comment 7 Sylvain 2018-10-17 20:14:37 UTC
Created attachment 3382 [details]
test case

careful on if you use head SDL sources, there is an issue with bug #4188 which break rotations 90 using RenderCopyEx.


Here's my test case. you can modify angle and also renderer (software,opengl,gles2) with the Hint.
And see the output. The output must be the same for all renderers.


The patch seems to fix 180 rotation offset, for all renderers (software, opengl, gles2).
For 90 rotation, there is still an offset, but it's the same for all 3 renderers.


Previously, there were also an offset for 180, the same for all 3 renderers.
Comment 8 Nik 2018-10-17 21:52:52 UTC
>The output must be the same for all renderers.
Yes, but it's very strange

If we set dstrect's x = y = 0, why appearance offset?
SDL rotate about center area with width = max(srcrect.w, dstrect.w) and height = max(srcrect.h, dstrect.h)?
Or what?
I don't understand for what need this strange behavior
Comment 9 Sylvain 2018-10-18 18:30:59 UTC
I agree the offset is strange, but consistent among renderers.
You could check on window to see how it behaves.

RenderCopyEx rotate and also can stretch.