Hi, After, um ... four (?) hours of hacking, I'm proud to present you my new SDL surface patch. I tryed to use second form of CL_SurfaceTarget::draw() : virtual void draw( CL_Surface_Generic *attributes, CL_Surface_DrawParams1 *params1, CL_GraphicContext *gc);
I choosed this one because it supports rotation & translation hotspot. The main difficulty was to find the correct arguments from attributes and params1 ! I found a bug in CL_SurfaceTarget : last_angle was'n saved (so the old optimisation hack wasn't usefull :-P). The main difficulty was to understand that params1.destX[0] can be the bottom right corner ! So I use min/max functions to find the top left and bottom right corner. With this I can correctly calculate destination size and position. The second difficulty was to extract one frame from a SDL_Surface. After one half hour I understood that alpha was set to zero or something like that (try without "SDL_SetAlpha (surface, 0, 255);" line in surface_sdl.cpp ;-)). I had a very mad performance problem : it was because "frame extractor" was called on every draw(). It's now "cached" in CL_SurfaceSDL::extract structure. Other problem : a sprite contains many frames, but only CL_SpriteGeneric contains correct informations (angle, scale, etc.). So I had to hack CL_Surface code to syncronise the frame with the correct values :-/ // Stupid patch because of Sprite class impl->angle=params2->rotate_angle; impl->scale_x=params2->scale_x; impl->scale_y=params2->scale_y; // End of stupid patch I think that another solution should be used here. --- I don't think that it's really a great idea to have so much CL_SurfaceTarget::draw() functions. It's really not clear ! I was very difficult for me to find correct values because some are lost because two draw() calls (DrawParams2 -> DrawParams1 : I lost angle and scale_x/y arguments, which _should_ be in arguments, but they are not always correct :-/). --- The patch is relative from ClanLib version "CVS 2004-08-10". I can understand that you really hate space instead <tab>, and that you doesn't want to install SDL just to try my patch, but please read it ! I need some comments ! Excume me for the space but I'm too tired to run some regexp after 2pm :-P --- I remove CL_SurfaceTarget::calc_hotspot from .h file and move it to .cpp file. I think that g++ is intelligent enough to read my "inline" keyword, no ? I also replace calc_hotspot_x/y #define by function. --- resourcedata_sprite_description.cpp isn't perfect. I found a mistake : when "size" attribute (<sprite><image><grid size="..." (...)/>), the current code doesn't check if both parts are corrects. So, you can have only a correct width :-( And width & height are not set to 1 by default (which is said is the documentation) .... Ok, here you have another patch. But I think that the code should contains other bugs. Bye, Haypo
--- cl_old/Sources/Display/surface.cpp 2004-05-06 14:36:01.000000000 +0200 +++ cl_cvs/Sources/Display/surface.cpp 2004-09-23 02:08:40.000000000 +0200 @@ -212,6 +212,13 @@ CL_GraphicContext *gc) { if (gc == 0) gc = CL_Display::get_current_window()->get_gc(); + + // Stupid patch because of Sprite class + impl->angle=params2->rotate_angle; + impl->scale_x=params2->scale_x; + impl->scale_y=params2->scale_y; + // End of stupid patch + impl->target->draw( impl, params2, --- cl_old/Sources/Display/sprite.cpp 2004-05-11 23:25:35.000000000 +0200 +++ cl_cvs/Sources/Display/sprite.cpp 2004-09-23 02:06:18.000000000 +0200 @@ -508,8 +508,10 @@ static CL_Surface_DrawParams2 params2; params2.srcX = frame.position.left; params2.srcY = frame.position.top; + params2.srcWidth = frame.position.get_width(); params2.srcHeight = frame.position.get_height(); + params2.destX = x; params2.destY = y; params2.destZ = 0.0; @@ -540,44 +542,7 @@ void CL_Sprite::draw( const CL_Rect &dest, CL_GraphicContext *gc) -{ - if(impl->finished == false || impl->show_on_finish != show_blank) - { - CL_Sprite_Generic::SpriteFrame &frame = impl->frames[impl->current_frame]; - - if (gc == 0) gc = CL_Display::get_current_window()->get_gc(); - - static CL_Surface_DrawParams2 params2; - params2.srcX = frame.position.left; - params2.srcY = frame.position.top; - params2.srcWidth = frame.position.get_width(); - params2.srcHeight = frame.position.get_height(); - params2.destX = dest.left; - params2.destY = dest.top; - params2.destZ = 0.0; - params2.red = impl->red; - params2.green = impl->green; - params2.blue = impl->blue; - params2.alpha = impl->alpha; - params2.blend_src = impl->blend_src; - params2.blend_dest = impl->blend_dest; - params2.scale_x = dest.get_width()/float(frame.position.get_width()); - params2.scale_y = dest.get_height()/float(frame.position.get_height()); - params2.translate_origin = impl->translation_origin; - params2.translate_x = impl->translation_hotspot.x + frame.offset.x; - params2.translate_y = impl->translation_hotspot.y + frame.offset.y; - params2.rotate_angle = impl->angle - impl->base_angle; - params2.rotate_pitch = impl->angle_pitch; - params2.rotate_yaw = impl->angle_yaw; - params2.rotate_origin = impl->rotation_origin; - params2.rotate_x = impl->rotation_hotspot.x + frame.offset.x; - params2.rotate_y = impl->rotation_hotspot.y + frame.offset.y; - - frame.surface.draw( - ¶ms2, - gc); - } -} +{ this->draw(dest.left, dest.top, gc); } float CL_Sprite::update(float time_elapsed) { --- cl_old/Sources/Display/surface_target.cpp 2004-05-22 18:23:25.000000000 +0200 +++ cl_cvs/Sources/Display/surface_target.cpp 2004-09-23 00:55:36.000000000 +0200 @@ -23,6 +23,49 @@ #include "surface_target.h" #include "API/Display/surface.h" +#if defined(INLINE_CALC_ROTATE_XY) + +#define calc_rotate_x(px,py,rotation_hotspot_x,rotation_hotspot_y,rotate_x_x,rotate_y_x) \ + (rotation_hotspot_x + (px-rotation_hotspot_x) * rotate_x_x + (py-rotation_hotspot_y) * rotate_y_x) + +#define calc_rotate_y(px,py,rotation_hotspot_x,rotation_hotspot_y,rotate_x_y,rotate_y_y) \ + (rotation_hotspot_y + (px-rotation_hotspot_x) * rotate_x_y + (py-rotation_hotspot_y) * rotate_y_y) + +#else + +double calc_rotate_x( +const double &px, +const double &py, +const double &rotation_hotspot_x, +const double &rotation_hotspot_y, +const double &rotate_x_x, +const double &rotate_y_x) +{ + return + rotation_hotspot_x + +(px-rotation_hotspot_x) * rotate_x_x + + (py-rotation_hotspot_y) * rotate_y_x + ; +} + +double calc_rotate_y( +const double &px, +const double &py, +const double &rotation_hotspot_x, +const double &rotation_hotspot_y, +const double &rotate_x_y, +const double &rotate_y_y) +{ + return + rotation_hotspot_y + +(px-rotation_hotspot_x) * rotate_x_y + +(py-rotation_hotspot_y) * rotate_y_y + ; +} + +#endif + + ///////////////////////////////////////////////////////////////////////////// // CL_Surface_Target construction: @@ -110,6 +153,17 @@ { static CL_Surface_DrawParams1 params1; + // Calculate final source rectangle points for render: + + params1.srcX[0] = params2->srcX; + params1.srcY[0] = params2->srcY; + params1.srcX[1] = params2->srcX+params2->srcWidth; + params1.srcY[1] = params2->srcY; + params1.srcX[2] = params2->srcX+params2->srcWidth; + params1.srcY[2] = params2->srcY+params2->srcHeight; + params1.srcX[3] = params2->srcX; + params1.srcY[3] = params2->srcY+params2->srcHeight; + // Calculate unit vectors for rotated surface: // (cached for speed reasons) static double vect_rotate_x[2] = { 1.0, 0.0 }; @@ -118,6 +172,7 @@ if (last_angle != params2->rotate_angle) { + last_angle = params2->rotate_angle; if (params2->rotate_angle == 0.0) { vect_rotate_x[0] = 1.0; @@ -151,22 +206,11 @@ double angle_rad = 3.1415926f*params2->rotate_angle/180; vect_rotate_x[0] = cos(angle_rad); vect_rotate_x[1] = sin(angle_rad); - vect_rotate_y[0] = cos(3.1415926f/2+angle_rad); - vect_rotate_y[1] = sin(3.1415926f/2+angle_rad); + vect_rotate_y[0] = -sin(angle_rad); // cos(x+pi/2) + vect_rotate_y[1] = cos(angle_rad); //sin(x+pi/2) } } - // Calculate final source rectangle points for render: - - params1.srcX[0] = params2->srcX; - params1.srcY[0] = params2->srcY; - params1.srcX[1] = params2->srcX+params2->srcWidth; - params1.srcY[1] = params2->srcY; - params1.srcX[2] = params2->srcX+params2->srcWidth; - params1.srcY[2] = params2->srcY+params2->srcHeight; - params1.srcX[3] = params2->srcX; - params1.srcY[3] = params2->srcY+params2->srcHeight; - // Calculate final destination rectangle points for surface rectangle: if (params2->rotate_angle == 0.0) @@ -186,7 +230,9 @@ params1.destX[0] = calc_rotate_x(t_params1->pixDestX, t_params1->pixDestY, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[0], vect_rotate_y[0]); params1.destY[0] = calc_rotate_y(t_params1->pixDestX, t_params1->pixDestY, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[1], vect_rotate_y[1]); params1.destX[1] = calc_rotate_x(t_params1->pixDestX+t_params1->destWidth, t_params1->pixDestY, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[0], vect_rotate_y[0]); + params1.destY[1] = calc_rotate_y(t_params1->pixDestX+t_params1->destWidth, t_params1->pixDestY, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[1], vect_rotate_y[1]); + params1.destX[2] = calc_rotate_x(t_params1->pixDestX+t_params1->destWidth, t_params1->pixDestY+t_params1->destHeight, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[0], vect_rotate_y[0]); params1.destY[2] = calc_rotate_y(t_params1->pixDestX+t_params1->destWidth, t_params1->pixDestY+t_params1->destHeight, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[1], vect_rotate_y[1]); params1.destX[3] = calc_rotate_x(t_params1->pixDestX, t_params1->pixDestY+t_params1->destHeight, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[0], vect_rotate_y[0]); @@ -251,5 +297,42 @@ if (ref_count == 0) delete this; } +CL_Pointf CL_Surface_Target::calc_hotspot +(CL_Origin origin, float hotspot_x, float hotspot_y, + float size_width, float size_height) +{ + switch(origin) + { + case origin_top_left: + default: + return CL_Pointf(-hotspot_x, hotspot_y); + break; + case origin_top_center: + return CL_Pointf(size_width / 2 - hotspot_x, -hotspot_y); + break; + case origin_top_right: + return CL_Pointf(size_width - hotspot_x, -hotspot_y); + break; + case origin_center_left: + return CL_Pointf(-hotspot_x, size_height / 2 - hotspot_y); + break; + case origin_center: + return CL_Pointf(size_width / 2 - hotspot_x, size_height / 2 - hotspot_y); + break; + case origin_center_right: + return CL_Pointf(size_width - hotspot_x, size_height / 2 - hotspot_y); + break; + case origin_bottom_left: + return CL_Pointf(-hotspot_x, size_height - hotspot_y); + break; + case origin_bottom_center: + return CL_Pointf(size_width / 2 - hotspot_x, size_height - hotspot_y); + break; + case origin_bottom_right: + return CL_Pointf(size_width - hotspot_x, size_height - hotspot_y); + break; + } +} + ///////////////////////////////////////////////////////////////////////////// // Implementation: --- cl_old/Sources/Display/surface_target.h 2004-04-24 02:09:49.000000000 +0200 +++ cl_cvs/Sources/Display/surface_target.h 2004-09-22 21:26:21.000000000 +0200 @@ -98,77 +98,10 @@ void release_ref(); // inlined this function for performance reasons. - CL_Pointf calc_hotspot(CL_Origin origin, float hotspot_x, float hotspot_y, float size_width, float size_height) - { - switch(origin) - { - case origin_top_left: - default: - return CL_Pointf(-hotspot_x, hotspot_y); - break; - case origin_top_center: - return CL_Pointf(size_width / 2 - hotspot_x, -hotspot_y); - break; - case origin_top_right: - return CL_Pointf(size_width - hotspot_x, -hotspot_y); - break; - case origin_center_left: - return CL_Pointf(-hotspot_x, size_height / 2 - hotspot_y); - break; - case origin_center: - return CL_Pointf(size_width / 2 - hotspot_x, size_height / 2 - hotspot_y); - break; - case origin_center_right: - return CL_Pointf(size_width - hotspot_x, size_height / 2 - hotspot_y); - break; - case origin_bottom_left: - return CL_Pointf(-hotspot_x, size_height - hotspot_y); - break; - case origin_bottom_center: - return CL_Pointf(size_width / 2 - hotspot_x, size_height - hotspot_y); - break; - case origin_bottom_right: - return CL_Pointf(size_width - hotspot_x, size_height - hotspot_y); - break; - } - } + inline CL_Pointf CL_Surface_Target::calc_hotspot + (CL_Origin origin, float hotspot_x, float hotspot_y, + float size_width, float size_height); - #define calc_rotate_x(px,py,rotation_hotspot_x,rotation_hotspot_y,rotate_x_x,rotate_y_x) \ - (rotation_hotspot_x + (px-rotation_hotspot_x) * rotate_x_x + (py-rotation_hotspot_y) * rotate_y_x) - - #define calc_rotate_y(px,py,rotation_hotspot_x,rotation_hotspot_y,rotate_x_y,rotate_y_y) \ - (rotation_hotspot_y + (px-rotation_hotspot_x) * rotate_x_y + (py-rotation_hotspot_y) * rotate_y_y) - -/* - - double calc_rotate_x( - const double &px, - const double &py, - const double &rotation_hotspot_x, - const double &rotation_hotspot_y, - const double &rotate_x_x, - const double &rotate_y_x) - { - return - rotation_hotspot_x + - (px-rotation_hotspot_x) * rotate_x_x + - (py-rotation_hotspot_y) * rotate_y_x; - } - - double calc_rotate_y( - const double &px, - const double &py, - const double &rotation_hotspot_x, - const double &rotation_hotspot_y, - const double &rotate_x_y, - const double &rotate_y_y) - { - return - rotation_hotspot_y + - (px-rotation_hotspot_x) * rotate_x_y + - (py-rotation_hotspot_y) * rotate_y_y; - } -*/ //! Implementation: private: int ref_count; --- cl_old/Sources/SDL/surface_sdl.cpp 2004-05-06 23:15:20.000000000 +0200 +++ cl_cvs/Sources/SDL/surface_sdl.cpp 2004-09-23 01:50:40.000000000 +0200 @@ -43,6 +43,8 @@ bool delete_provider, int flag) { + extract.surface = NULL; + // TODO: Use flag surface = 0; SDL_Surface *tmp = 0; @@ -160,8 +162,8 @@ CL_Surface_SDL::~CL_Surface_SDL() { - if(surface != 0) - SDL_FreeSurface(surface); + SDL_FreeSurface(extract.surface); + if(surface != 0) SDL_FreeSurface(surface); } ///////////////////////////////////////////////////////////////////////////// @@ -241,57 +243,95 @@ { } -void CL_Surface_SDL::draw( - CL_Surface_Generic *attributes, - CL_Surface_DrawParams1 *params1, - CL_GraphicContext *gc) +double min_double (double a,double b,double c, double d) { - // todo: move SDL code from other draws to this one. + double min=a; + if (b<min) min=b; + if (c<min) min=c; + if (d<min) min=d; + return min; } +double max_double (double a,double b,double c, double d) +{ + double max=a; + if (max<b) max=b; + if (max<c) max=c; + if (max<d) max=d; + return max; +} + void CL_Surface_SDL::draw( CL_Surface_Generic *attributes, - CL_Surface_DrawParams2 *params2, - CL_Surface_TargetDrawParams1 *t_params1, - CL_GraphicContext *context) + CL_Surface_DrawParams1 *params1, + CL_GraphicContext *gc) { - SDL_Rect source = {params2->srcX, params2->srcY, params2->srcWidth, params2->srcHeight}; + Sint16 min_src_x = params1->srcX[0]; + Sint16 max_src_x = params1->srcX[1]; + Sint16 src_width = max_src_x-min_src_x; + + Sint16 min_src_y = params1->srcY[0]; + Sint16 max_src_y = params1->srcY[2]; + Sint16 src_height = max_src_y-min_src_y; + + SDL_Rect source = { + min_src_x, + min_src_y, + src_width, + src_height}; + + // Extract the current frame of the whole texture + if ((extract.surface == NULL) + || (extract.rect.x != source.x) + || (extract.rect.y != source.y) + || (extract.rect.w != source.w) + || (extract.rect.h != source.h)) { + SDL_FreeSurface (extract.surface); + extract.rect = source; + SDL_Rect milieu = {0, 0, source.w, source.h}; + extract.surface = SDL_CreateRGBSurface + (SDL_SWSURFACE,milieu.w,milieu.h,32, + surface->format->Rmask, surface->format->Gmask, + surface->format->Bmask, surface->format->Amask); + SDL_SetAlpha (surface, 0, 255); + SDL_BlitSurface (surface, &source, extract.surface, &milieu); + } - SDL_Rect dst = { - static_cast<Sint16>(t_params1->pixDestX + context->get_modelview().get_origin_x()), - static_cast<Sint16>(t_params1->pixDestY + context->get_modelview().get_origin_y()), - static_cast<Sint16>(t_params1->destWidth), - static_cast<Sint16>(t_params1->destHeight) - }; + SDL_Surface *out = extract.surface; + bool free_out=false; - if (t_params1->destWidth == params2->srcWidth && t_params1->destHeight == params2->srcHeight) - { - if(surface->format->BitsPerPixel > 8) - { - Uint8 a = (Uint8)(attributes->alpha * 255); - SDL_SetAlpha(surface,SDL_RLEACCEL|SDL_SRCALPHA,a); - } - SDL_BlitSurface(surface,&source,SDL_GetVideoSurface(),&dst); + // Need rotation or scale ? + if ((attributes->angle != 0) || (attributes->scale_x != 1.0)) { + SDL_Surface *rotozoomed = rotozoomSurface + (extract.surface, -attributes->angle,attributes->scale_x, SMOOTHING_ON); + if(rotozoomed->format->BitsPerPixel > 8) { + Uint8 a = (Uint8)(attributes->alpha * 255); + SDL_SetAlpha(rotozoomed ,SDL_RLEACCEL|SDL_SRCALPHA,a); + } + free_out = true; + out = rotozoomed; } - else - { -#ifdef HAVE_LIBSDL_GFX - SDL_Surface *zoom = zoomSurface (surface, t_params1->destWidth/params2->srcWidth, t_params1->destHeight/params2->srcHeight, SMOOTHING_ON); - if(zoom->format->BitsPerPixel > 8) - { - Uint8 a = (Uint8)(attributes->alpha * 255); - SDL_SetAlpha(zoom ,SDL_RLEACCEL|SDL_SRCALPHA,a); - } - SDL_BlitSurface(zoom,&source,SDL_GetVideoSurface(),&dst); + // Calculate destination rectangle + float min_dest_x = min_double(params1->destX[0],params1->destX[1],params1->destX[2],params1->destX[3]); + float max_dest_x = max_double(params1->destX[0],params1->destX[1],params1->destX[2],params1->destX[3]); + float dest_width = max_dest_x-min_dest_x; + + float min_dest_y = min_double(params1->destY[0],params1->destY[1],params1->destY[2],params1->destY[3]); + float max_dest_y = max_double(params1->destY[0],params1->destY[1],params1->destY[2],params1->destY[3]); + float dest_height = max_dest_y-min_dest_y; + SDL_Rect dst = { + static_cast<Sint16>(min_dest_x+gc->get_modelview().get_origin_x()), + static_cast<Sint16>(min_dest_y+gc->get_modelview().get_origin_y()), + static_cast<Sint16>(dest_width), + static_cast<Sint16>(dest_height)}; + + // Blit the surface to screen + source.w = out->w; + source.h = out->h; + SDL_BlitSurface(out,NULL,SDL_GetVideoSurface(),&dst); - SDL_FreeSurface(zoom); -#else - CL_Log::log("debug", - "Error: clanSDL not compiled with SDL_gfx library, zooming not possible.\n" - "Error: see: http://www.ferzkopp.net/~aschiffler/Software/SDL_gfx-2.0/\n"); -#endif - } + if (free_out) SDL_FreeSurface(out); } ///////////////////////////////////////////////////////////////////////////// --- cl_old/Sources/SDL/surface_sdl.h 2004-04-24 02:09:49.000000000 +0200 +++ cl_cvs/Sources/SDL/surface_sdl.h 2004-09-23 01:50:30.000000000 +0200 @@ -69,18 +69,15 @@ CL_Surface_DrawParams1 *params1, CL_GraphicContext *gc); - //: Draw surface on screen. - virtual void draw( - CL_Surface_Generic *attributes, - CL_Surface_DrawParams2 *params2, - CL_Surface_TargetDrawParams1 *t_params1, - CL_GraphicContext *context); - //! Implementation: private: CL_Sizef screen_size; SDL_Surface *surface; + struct { + SDL_Surface *surface; + SDL_Rect rect; + } extract; }; #endif
--- cl_old/Sources/Display/resourcedata_sprite_description.cpp 2004-05-11 23:21:33.000000000 +0200 +++ cl_cvs/Sources/Display/resourcedata_sprite_description.cpp 2004-09-23 01:23:54.000000000 +0200 @@ -119,12 +119,14 @@ int yspacing = 0; std::vector<std::string> image_size = CL_String::tokenize(grid_element.get_attribute("size"), ","); - int width = 0; - if (image_size.size() > 0) + int width = 1; + int height = 1; + if (image_size.size() == 2) { width = atoi(image_size[0].c_str()); - int height = 0; - if (image_size.size() > 1) height = atoi(image_size[1].c_str()); + } else { + throw CL_Error("Resource '" + resource.get_name() + "' has incorrect array attribute, must be \"X,Y\"!"); + } if (grid_element.has_attribute("pos")) {