Documentation and cleanups to the RotSprite methods.

This commit is contained in:
fendrin 2013-09-18 02:36:30 +02:00
parent 80ca05e448
commit 85f84dd383
4 changed files with 126 additions and 85 deletions

View File

@ -189,7 +189,7 @@ surface rotate_modification::operator()(const surface& src) const
case 180: return rotate_180_surface(src);
case 270: return rotate_90_surface(src, false);
case 360: return src;
default: return rotate_any_surface(src, normalized, zoom_, offset_);
default: return rotate_any_surface(src, normalized, zoom_, offset_);
}
// Other values are not supported. Ignore them.
@ -644,11 +644,30 @@ REGISTER_MOD_PARSER(ROTATE, args)
std::vector<std::string> const& slice_params = utils::split(args, ',', utils::STRIP_SPACES);
const size_t s = slice_params.size();
int angle = (s > 0) ? lexical_cast_default<int>(slice_params[0]) : 90;
int zoom = (s > 1) ? lexical_cast_default<int>(slice_params[1]) : 16;
int offset = (s > 2) ? lexical_cast_default<int>(slice_params[2]) : 8;
return new rotate_modification(angle, zoom, offset);
switch (s) {
case 0:
return new rotate_modification();
break;
case 1:
return new rotate_modification(
lexical_cast_default<int>(slice_params[0]));
break;
case 2:
return new rotate_modification(
lexical_cast_default<int>(slice_params[0]),
lexical_cast_default<int>(slice_params[1]));
break;
case 3:
return new rotate_modification(
lexical_cast_default<int>(slice_params[0]),
lexical_cast_default<int>(slice_params[1]),
lexical_cast_default<int>(slice_params[2]));
break;
default:
return NULL;
break;
}
return NULL;
}
// Grayscale

View File

@ -184,9 +184,19 @@ class rotate_modification : public modification
public:
/**
* Constructor.
* @pre @zoom >= @offset Otherwise @return will have empty pixels.
* @pre @offset > 0 Otherwise the procedure will not return.
*
* @param degrees Amount of rotation (in degrees).
* Positive values are clockwise; negative are counter-clockwise.
* Only multiples of 90 are supported.
* @param zoom The zoom level to calculate the rotation from.
* Greater values result in better results and increased runtime.
* This parameter will be ignored if @degrees is a multiple of 90.
* @param offset Determines the step size of the scanning of the zoomed source.
* Different offsets can produce better results, try them out.
* Greater values result in decreased runtime.
* This parameter will be ignored if @degrees is a multiple of 90.
* If @offset is greater than @zoom the result will have empty pixels.
*/
rotate_modification(int degrees = 90, int zoom = 16, int offset = 8)
: degrees_(degrees), zoom_(zoom), offset_(offset)

View File

@ -1626,116 +1626,115 @@ surface blend_surface(
return optimize ? create_optimized_surface(nsurf) : nsurf;
}
surface rotate_any_surface(const surface& surf, float angle, int zoom, int offset)
/* Simplified RotSprite algorithm.
* http://en.wikipedia.org/wiki/Image_scaling#RotSprite
* Lifted from: http://github.com/salmonmoose/SpriteRotator
* 1) Zoom the source image by a certain factor.
* 2) Scan the zoomed source image at every step=offset and put it in the result. */
surface rotate_any_surface(const surface& surf, float angle, int zoom, int offset, bool optimize)
{
if (offset <= 0) offset = 1; //otherwise the for loops for scaling the image won't work.
surface src = scale_surface(surf, surf->w * zoom, surf->h * zoom, false);
//convert angle to radians
float radians = (angle * 2 * M_PI) / 360;
//calculate cosine and sine
float cosine = static_cast<float>(cos(radians));
float sine = static_cast<float>(sin(radians));
//calculate the size of the new image
float point_1x = (-src->h * sine);
float point_1y = ( src->h * cosine);
float point_2x = ( src->w * cosine-src->h * sine);
float point_2y = ( src->h * cosine+src->w * sine);
float point_3x = ( src->w * cosine);
float point_3y = ( src->w * sine);
float min_x = std::min(0.0F, std::min(point_1x, std::min(point_2x, point_3x)));
float min_y = std::min(0.0F, std::min(point_1y, std::min(point_2y, point_3y)));
float max_x = (angle > 90 && angle < 180) ? 0 : std::max(point_1x, std::max(point_2x, point_3x));
float max_y = (angle > 180 && angle < 270) ? 0 : std::max(point_1y, std::max(point_2y, point_3y));
//calculate the destination image width & height
int dest_width = static_cast<int>(ceil(abs(max_x) - min_x));
int dest_height = static_cast<int>(ceil(abs(max_y) - min_y));
surface dest(create_neutral_surface(dest_width / zoom, dest_height / zoom));
if(dest == NULL) {
std::cerr << "could not make neutral surface...\n";
return NULL;
}
int src_w, src_h, dst_w, dst_h;
float min_x, max_x, min_y, max_y, sine, cosine;
{
surface_lock dst_lock(dest);
// convert angle to radiant (angle * 2 * M_PI) / 360
const float radians = angle * M_PI / 180;
cosine = static_cast<float>(cos(radians));
sine = static_cast<float>(sin(radians));
// calculate the size of the dst image
src_w = surf->w * zoom;
src_h = surf->h * zoom;
/* See http://en.wikipedia.org/wiki/Rotation_(mathematics) */
const float point_1x = src_h * -sine;
const float point_1y = src_h * cosine;
const float point_2x = src_w * cosine - src_h * sine;
const float point_2y = src_h * cosine + src_w * sine;
const float point_3x = src_w * cosine;
const float point_3y = src_w * sine;
/* After the rotation, the new image has different dimensions.
* E.g.: The maximum height equals the former diagonal in case the angle is 45, 135, 225 or 315 degree.
* See http://en.wikipedia.org/wiki/File:Rotation_illustration2.svg to get the idea. */
min_x = std::min(0.0F, std::min(point_1x, std::min(point_2x, point_3x)));
min_y = std::min(0.0F, std::min(point_1y, std::min(point_2y, point_3y)));
max_x = (angle > 90 && angle < 180) ? 0 : std::max(point_1x, std::max(point_2x, point_3x));
max_y = (angle > 180 && angle < 270) ? 0 : std::max(point_1y, std::max(point_2y, point_3y));
dst_w = static_cast<int>(ceil(abs(max_x) - min_x)) / zoom;
dst_h = static_cast<int>(ceil(abs(max_y) - min_y)) / zoom;
}
surface dst(create_neutral_surface(dst_w, dst_h));
{
surface_lock dst_lock(dst);
const surface src = scale_surface(surf, src_w, src_h, false);
const_surface_lock src_lock(src);
float scale = 1.f / zoom;
for (int x = 0; x < dest_width; x += offset)
for (int y = 0; y < dest_height; y += offset) {
//calculate the new pixel we want to copy
const float source_x = ((x+min_x)*cosine + (y+min_y)*sine);
const float source_y = ((y+min_y)*cosine - (x+min_x)*sine);
//if the pixel exist on the src surface
if (source_x >= 0 && source_x < src->w && source_y >= 0 && source_y < src->h)
//get it from the src surface and place it on the dest surface
put_pixel( dest, dst_lock, x*scale , y*scale, //multiply with scale
get_pixel( src, src_lock, source_x, source_y ) );
const float scale = 1.f / zoom;
const int max_x = dst_w * zoom;
const int max_y = dst_h * zoom;
/* Loop through the zoomed src image,
* take every pixel in steps with offset distance and place it in the dst image. */
for (int x = 0; x < max_x; x += offset)
for (int y = 0; y < max_y; y += offset) {
// calculate the src pixel that fits in the dst
const float source_x = (x + min_x)*cosine + (y + min_y)*sine;
const float source_y = (y + min_y)*cosine - (x + min_x)*sine;
// if the pixel exists on the src surface
if (source_x >= 0 && source_x < src_w
&& source_y >= 0 && source_y < src_h)
// get it from the src surface and place it on the dst surface
put_pixel(dst, dst_lock, x*scale , y*scale, // multiply with scale
get_pixel(src, src_lock, source_x, source_y));
}
}
//return the rotated destination surface
return create_optimized_surface(dest);
return optimize ? create_optimized_surface(dst) : dst;
}
void put_pixel(surface& surf, surface_lock& surf_lock, int x, int y, Uint32 pixel)
void put_pixel(const surface& surf, surface_lock& surf_lock, int x, int y, Uint32 pixel)
{
int bpp = surf->format->BytesPerPixel;
/* p is the address to the pixel we want to set */
Uint8* p = reinterpret_cast<Uint8*>(surf_lock.pixels()) + y * surf->pitch + x * bpp;
const int bpp = surf->format->BytesPerPixel;
/* dst is the address to the pixel we want to set */
Uint8* const dst = reinterpret_cast<Uint8*>(surf_lock.pixels()) + y * surf->pitch + x * bpp;
switch (bpp) {
case 1:
*p = pixel;
*dst = pixel;
break;
case 2:
*reinterpret_cast<Uint16*>(p) = pixel;
*reinterpret_cast<Uint16*>(dst) = pixel;
break;
case 3:
if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
p[0] = (pixel >> 16) & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = pixel & 0xff;
dst[0] = (pixel >> 16) & 0xff;
dst[1] = (pixel >> 8) & 0xff;
dst[2] = pixel & 0xff;
} else {
p[0] = pixel & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = (pixel >> 16) & 0xff;
dst[0] = pixel & 0xff;
dst[1] = (pixel >> 8) & 0xff;
dst[2] = (pixel >> 16) & 0xff;
}
break;
case 4:
*reinterpret_cast<Uint32*>(p) = pixel;
*reinterpret_cast<Uint32*>(dst) = pixel;
break;
default:
break;
}
}
Uint32 get_pixel(const surface& surf, const_surface_lock& surf_lock, int x, int y)
Uint32 get_pixel(const surface& surf, const const_surface_lock& surf_lock, int x, int y)
{
int bpp = surf->format->BytesPerPixel;
//const_surface_lock lock(surf);
const int bpp = surf->format->BytesPerPixel;
/* p is the address to the pixel we want to retrieve */
const Uint8* p = reinterpret_cast<const Uint8*>(surf_lock.pixels()) + y * surf->pitch + x * bpp;
const Uint8* const src = reinterpret_cast<const Uint8*>(surf_lock.pixels()) + y * surf->pitch + x * bpp;
switch (bpp) {
case 1:
return *p;
return *src;
case 2:
return *reinterpret_cast<const Uint16*>(p);
return *reinterpret_cast<const Uint16*>(src);
case 3:
if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
return p[0] << 16 | p[1] << 8 | p[2];
return src[0] << 16 | src[1] << 8 | src[2];
else
return p[0] | p[1] << 8 | p[2] << 16;
return src[0] | src[1] << 8 | src[2] << 16;
break;
case 4:
return *reinterpret_cast<const Uint32*>(p);
return *reinterpret_cast<const Uint32*>(src);
default:
return 0;
}

View File

@ -337,6 +337,9 @@ surface blend_surface(
/**
* Rotates a surface by any degrees.
*
* @pre @zoom >= @offset Otherwise @return will have empty pixels.
* @pre @offset > 0 Otherwise the procedure will not return.
*
* @param surf The surface to rotate.
* @param angle The angle of rotation.
* @param zoom Which zoom level to use for calculating the result.
@ -345,7 +348,8 @@ surface blend_surface(
*
* @return The rotated surface.
*/
surface rotate_any_surface(const surface& surf, float angle, int zoom, int offset);
surface rotate_any_surface(const surface& surf, float angle,
int zoom, int offset, bool optimize=true);
/**
* Rotates a surface 180 degrees.
@ -437,8 +441,17 @@ private:
bool locked_;
};
void put_pixel(surface& surf, surface_lock& surf_lock, int x, int y, Uint32 pixel);
Uint32 get_pixel(const surface& surf, const_surface_lock& surf_lock, int x, int y);
/**
* Helper methods for setting/getting a single pixel in an image.
* Lifted from http://sdl.beuc.net/sdl.wiki/Pixel_Access
*
* @param surf The image to get or receive the pixel from.
* @param surf_lock The locked surface to make sure the pointers are valid.
* @x The position in the row of the pixel.
* @y The row of the pixel.
*/
void put_pixel(const surface& surf, surface_lock& surf_lock, int x, int y, Uint32 pixel);
Uint32 get_pixel(const surface& surf, const const_surface_lock& surf_lock, int x, int y);
struct surface_restorer
{