public void DrawSprite(SurfaceSprite surface, int xScreen, int yScreen, bool isHorizontalFlipped, bool isVerticalFlipped, int xSprite = 0, int ySprite = 0, int widthSprite = int.MaxValue, int heightSprite = int.MaxValue) { if (widthSprite == int.MaxValue) { widthSprite = surface.Width; } if (heightSprite == int.MaxValue) { heightSprite = surface.Height; } if (this.BoundsClipped.Width <= 0 || this.BoundsClipped.Height <= 0) { return; } SurfaceRectangle rectScreen = new SurfaceRectangle(); int destinationAddress = 0; int sourceAddress = 0; // pas de clipping: La destination ne bouge pas (sauf exception des xscreen/yScreen negatif et la source bouge if (HaveClip == false) { // La lecture des pixels commence en négatif (en gros on devrait afficher du transparent) // Pour afficher ces pixels qui n'existent pas on compense en poussant la position à l'écran et en diminuant la taille if (ySprite < 0) { yScreen += -ySprite; heightSprite += ySprite; ySprite = 0; } if (xSprite < 0) { xScreen += -xSprite; widthSprite += xSprite; xSprite = 0; } // on genere le rectangle affichable de la source. Il prend en compte son clipping à lui (souvent inutile) mais pas celui de l'ecran. var rectSprite = surface.GetVisibleRectangle(xSprite, ySprite, widthSprite, heightSprite, false, false); if (rectSprite.isVisible == false) { return; } // on genere le rectangle affichable de l'ecran en prenant la taille de celui du rectangle de la source rectScreen = this.GetVisibleRectangle(xScreen, yScreen, rectSprite.width, rectSprite.height, isHorizontalFlipped, isVerticalFlipped); if (rectScreen.isVisible == false) { return; } sourceAddress = rectSprite.address; // On sort de l'ecran mais le rectangle est visible donc on recalcule l'adresse de départ en ajoutant les lignes perdues if (yScreen < 0) { if (isVerticalFlipped == false) { sourceAddress += (surface.Width * -yScreen); } else { if (heightSprite > BoundsClipped.Height) // cas ou la taille affichable est plus petite que le sprite et que l'on est flipH { var sourceOffsetY = ((yScreen + heightSprite) - BoundsClipped.Height); if (sourceOffsetY >= 0) { sourceAddress += surface.Width * sourceOffsetY; } } // on avance la source du nombre de pixel caché par le bord de l'ecran } } else if (yScreen + heightSprite >= BoundsClipped.Height) { if (isVerticalFlipped == true) { var sourceOffsetY = ((yScreen + heightSprite) - BoundsClipped.Height); sourceAddress += surface.Width * sourceOffsetY; // on avance la source du nombre de pixel caché par le bord de l'ecran } } if (xScreen < 0) { if (isHorizontalFlipped == false) { sourceAddress += -xScreen; // on avance la source du nombre de pixel caché par le bord de l'ecran } else { if (widthSprite > BoundsClipped.Width) // cas ou la taille affichable est plus petite que le sprite et que l'on est flipH { var sourceOffsetX = ((xScreen + widthSprite) - BoundsClipped.Width); if (sourceOffsetX >= 0) { sourceAddress += sourceOffsetX; } } // on avance la source du nombre de pixel caché par le bord de l'ecran } // en mode flip Horizontal on a pas besoin d'avancer car la source address et la derniere pixel de la ligne } else if (xScreen + widthSprite >= this.BoundsClipped.Width) { if (isHorizontalFlipped == true) { var sourceOffsetX = ((xScreen + widthSprite) - BoundsClipped.Width); sourceAddress += sourceOffsetX; // on avance la source du nombre de pixel caché par le bord de l'ecran } } destinationAddress = rectScreen.address; } // ** Gestion du clipping : ici le mouvement se fait du coté de la source et la destination (écran) reste fixe ** else { // on genere le rectangle affichable de la source (il prend en compte son clipping à lui (souvent inutile) et le clipping de l'écran mais qui bouge en xscreen et yscreen ) var sourceClipped = new Rectangle(this.BoundsClipped.X - xScreen + xSprite, this.BoundsClipped.Y - yScreen + ySprite, this.BoundsClipped.Width, this.BoundsClipped.Height); var rectSprite = surface.GetVisibleRectangle(xSprite, ySprite, widthSprite, heightSprite, false, false, sourceClipped); if (rectSprite.isVisible == false) { return; } // on genere le rectangle affichable de l'ecran en prenant la taille de celui du rectangle de la source rectScreen = this.GetVisibleRectangle(this.BoundsClipped.X, this.BoundsClipped.Y, rectSprite.width, rectSprite.height, isHorizontalFlipped, isVerticalFlipped); if (rectScreen.isVisible == false) { return; } sourceAddress = rectSprite.address; destinationAddress = rectScreen.address; // compensation sur la destinationAddress if (isHorizontalFlipped == false) { //if (rectSprite.x == 0) //{ if (xScreen + widthSprite > BoundsClipped.Right) { destinationAddress += (this.BoundsClipped.Width - rectSprite.width); } else if (xScreen > BoundsClipped.X) { destinationAddress += xScreen - BoundsClipped.X; } //} } else { if (rectSprite.x == xSprite) { if (xScreen + widthSprite > BoundsClipped.Right) { sourceAddress += ((xScreen + widthSprite) - this.BoundsClipped.Right); destinationAddress += (this.BoundsClipped.Width - rectSprite.width); } else if (xScreen > BoundsClipped.X) { destinationAddress += xScreen - BoundsClipped.X; } } else { if (xScreen < BoundsClipped.X) { if (xScreen + widthSprite > BoundsClipped.Right) { // fn de ligne - distance entre xScreen et le x de la zone de clipping sourceAddress += ((xScreen + widthSprite) - this.BoundsClipped.Right) - (BoundsClipped.X - xScreen); } else { sourceAddress += -rectSprite.x + xSprite; } } } } // Vertical if (isVerticalFlipped == false) { //if (rectSprite.y == 0) //{ if (yScreen + heightSprite > BoundsClipped.Bottom) { destinationAddress += (this.BoundsClipped.Height - rectSprite.height) * this.Width; } else if (yScreen > BoundsClipped.Y) { destinationAddress += (yScreen - BoundsClipped.Y) * this.Width; } //} } else { if (rectSprite.y == ySprite) { if (yScreen + heightSprite > BoundsClipped.Bottom) { sourceAddress += ((yScreen + heightSprite) - this.BoundsClipped.Bottom) * surface.Width; destinationAddress += (this.BoundsClipped.Height - rectSprite.height) * this.Width; } else if (yScreen > BoundsClipped.Y) { destinationAddress += (yScreen - BoundsClipped.Y) * this.Width; } } else { if (yScreen < BoundsClipped.Y) { // Peut-on virer ce if ? et laisser seulement le else // s'il n'arrive jamais dans le point d'arret c'est le cas if (yScreen + heightSprite > BoundsClipped.Bottom) { // fn de ligne - distance entre xScreen et le x de la zone de clipping sourceAddress += (((yScreen + heightSprite) - this.BoundsClipped.Bottom) - (BoundsClipped.Y - yScreen)) * surface.Width; } else { sourceAddress += (-rectSprite.y + ySprite) * surface.Width; } } } } } // Affichage var strideDestination = rectScreen.stride; var strideSource = surface.Width - Math.Min(surface.Width, rectScreen.width); var heightScreen = rectScreen.height; var widthScreen = rectScreen.width; var destinationPixels = this.Pixels; var sourcePixels = surface.Pixels; int destinationDirection = rectScreen.direction; //try //{ for (int iy = 0; iy < heightScreen; iy++) { for (int ix = 0; ix < widthScreen; ix++) { destinationPixels[destinationAddress].AlphaBlend(sourcePixels[sourceAddress], this.Opacity); // sinon c'est invisible sourceAddress++; destinationAddress += destinationDirection; } destinationAddress += strideDestination; sourceAddress += strideSource; } //} //catch (Exception ex) //{ //} //if (HaveClip) //{ // // test : En Green affiche la source qui sera envoyé à destination // DrawRectangle(rectScreen.x, rectScreen.y, rectScreen.width, rectScreen.height, Argb32.Green, false); //} }
/// <summary> /// Obtenir un rectangle à l'interieur de l'ecran (prend en compte le clipping de la surface + un clipping externe eventuellement) /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <param name="width"></param> /// <param name="height"></param> /// <returns></returns> private SurfaceRectangle GetVisibleRectangle(int x, int y, int width, int height, bool isFlipHorizontal, bool isFlipVertical, Rectangle?externalBoundsClipped = null) { SurfaceRectangle rectVisibility = new SurfaceRectangle(); int direction; int stride; var rectRequested = new Rectangle(x, y, width, height); // taille demandé par le client rectRequested.Intersect(this.BoundsClipped); // BoundsClipped est deja l'intersection entre clip et l'ecran // Rajoute la possibilité d'ajouté un clip externe (par exemple celui d'une destination sur une source) if (externalBoundsClipped != null) { rectRequested.Intersect(externalBoundsClipped.Value); } // L'interscetion entre la taille demandée et le clipping n'existe pas if (rectRequested.Width == 0 || rectRequested.Height == 0) { return(rectVisibility); } int position = this.GetPosition(rectRequested.X, rectRequested.Y); if (isFlipHorizontal == false) { stride = this.Width - rectRequested.Width; direction = 1; } else { position += (rectRequested.Width - 1); direction = -1; stride = this.Width + rectRequested.Width; } if (isFlipVertical == true) { position += (this.Width * (rectRequested.Height - 1)); if (isFlipHorizontal == false) { stride = -this.Width - rectRequested.Width; } else { stride = -this.Width + rectRequested.Width; } } if (position == -1) { return(rectVisibility); } rectVisibility.direction = direction; rectVisibility.position = position; rectVisibility.address = this.Address + position; rectVisibility.stride = stride; rectVisibility.width = rectRequested.Width; rectVisibility.height = rectRequested.Height; rectVisibility.isVisible = true; rectVisibility.x = rectRequested.X; rectVisibility.y = rectRequested.Y; return(rectVisibility); }