1 using Microsoft.Xna.Framework;
2 using Microsoft.Xna.Framework.Graphics;
6 using System.Collections.Generic;
7 using System.Threading.Tasks;
11 public partial class Sprite
18 List<Sprite> retVal =
null;
21 retVal = list.Select(wRef =>
23 if (wRef.TryGetTarget(out
Sprite spr))
28 }).Where(s => s !=
null).ToList();
34 private static readonly List<WeakReference<Sprite>> list =
new List<WeakReference<Sprite>>();
36 static partial
void AddToList(
Sprite elem)
40 list.Add(
new WeakReference<Sprite>(elem));
44 static partial
void RemoveFromList(
Sprite sprite)
48 list.RemoveAll(wRef => !wRef.TryGetTarget(out
Sprite s) || s == sprite);
52 private class TextureRefCounter
58 private readonly
static Dictionary<Identifier, TextureRefCounter> textureRefCounts =
new Dictionary<Identifier, TextureRefCounter>();
60 private bool cannotBeLoaded;
64 protected Texture2D
texture {
get;
private set; }
74 private string disposeStackTrace;
78 get {
return texture !=
null && !cannotBeLoaded; }
88 public Sprite(Texture2D
texture, Rectangle? sourceRectangle, Vector2? newOffset,
float newRotation = 0.0f,
string path =
null)
92 offset = newOffset ?? Vector2.Zero;
93 size =
new Vector2(sourceRect.Width, sourceRect.Height);
99 if (!
string.IsNullOrEmpty(path))
101 Identifier fullPath = Path.GetFullPath(path).CleanUpPathCrossPlatform(correctFilenameCase:
false).ToIdentifier();
104 if (!textureRefCounts.TryAdd(fullPath,
new TextureRefCounter { RefCount = 1, Texture = texture }))
106 textureRefCounts[fullPath].RefCount++;
112 partial
void LoadTexture(ref Vector4 sourceVector, ref
bool shouldReturn)
122 if (sourceVector.Z == 0.0f) sourceVector.Z =
texture.Width;
123 if (sourceVector.W == 0.0f) sourceVector.W =
texture.Height;
138 Vector4 sourceVector = Vector4.Zero;
140 int maxLoadRetries = File.Exists(
FilePath) ? 3 : 0;
141 for (
int i = 0; i <= maxLoadRetries; i++)
145 LoadTexture(ref sourceVector, ref temp2);
147 catch (System.IO.IOException)
149 if (i == maxLoadRetries || !File.Exists(
FilePath)) {
throw; }
150 DebugConsole.NewMessage(
"Loading sprite \"" +
FilePath +
"\" failed, retrying in 250 ms...");
151 System.Threading.Thread.Sleep(500);
155 if (sourceRect.Width == 0 && sourceRect.Height == 0)
157 sourceRect =
new Rectangle((
int)sourceVector.X, (
int)sourceVector.Y, (
int)sourceVector.Z, (
int)sourceVector.W);
159 size.X *= sourceRect.Width;
160 size.Y *= sourceRect.Height;
165 cannotBeLoaded =
true;
174 DebugConsole.ThrowError(
"Sprite: Failed to reload the texture, texture is null.");
180 if (textureRefCounts.ContainsKey(pathKey))
182 textureRefCounts[pathKey].Texture =
texture;
186 if (sprite.texture == oldTexture)
193 partial
void CalculateSourceRect()
200 if (
string.IsNullOrWhiteSpace(file))
203 CrossThread.RequestExecutionOnMainThread(() =>
209 Identifier fullPath = Path.GetFullPath(file).CleanUpPathCrossPlatform(correctFilenameCase:
false).ToIdentifier();
212 if (textureRefCounts.ContainsKey(fullPath))
214 textureRefCounts[fullPath].RefCount++;
215 return textureRefCounts[fullPath].Texture;
219 if (File.Exists(file))
221 if (!ToolBox.IsProperFilenameCase(file))
224 DebugConsole.ThrowError(
"Texture file \"" + file +
"\" has incorrect case!", contentPackage: contentPackage);
228 Texture2D newTexture = TextureLoader.FromFile(file, compress, contentPackage: contentPackage);
231 if (!textureRefCounts.TryAdd(fullPath,
232 new TextureRefCounter { RefCount = 1, Texture = newTexture }))
234 CrossThread.RequestExecutionOnMainThread(() => newTexture.Dispose());
235 textureRefCounts[fullPath].RefCount++;
236 return textureRefCounts[fullPath].Texture;
243 DebugConsole.ThrowError($
"Sprite \"{file}\" not found!", contentPackage: contentPackage);
244 DebugConsole.Log(Environment.StackTrace.CleanupStackTrace());
250 public void Draw(ISpriteBatch spriteBatch, Vector2 pos,
float rotate = 0.0f,
float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None)
252 this.
Draw(spriteBatch, pos, Color.White, rotate, scale, spriteEffect);
255 public void Draw(ISpriteBatch spriteBatch, Vector2 pos, Color color,
float rotate = 0.0f,
float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None,
float?
depth =
null)
257 this.
Draw(spriteBatch, pos, color, this.
origin, rotate,
new Vector2(scale, scale), spriteEffect,
depth);
260 public void Draw(ISpriteBatch spriteBatch, Vector2 pos, Color color, Vector2
origin,
float rotate = 0.0f,
float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None,
float?
depth =
null)
262 this.
Draw(spriteBatch, pos, color,
origin, rotate,
new Vector2(scale, scale), spriteEffect,
depth);
265 public virtual void Draw(ISpriteBatch spriteBatch, Vector2 pos, Color color, Vector2
origin,
float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None,
float?
depth =
null)
267 if (
Texture ==
null) {
return; }
275 public void DrawSilhouette(SpriteBatch spriteBatch, Vector2 pos, Vector2
origin,
float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None,
float?
depth =
null)
277 if (
Texture ==
null) {
return; }
278 for (
int x = -1; x <= 1; x += 2)
280 for (
int y = -1; y <= 1; y += 2)
282 spriteBatch.Draw(
texture, pos +
offset +
new Vector2(x, y), sourceRect, Color.Black,
rotation + rotate,
origin, scale, spriteEffect, (
depth ??
this.depth) + 0.01f);
298 Vector2? startOffset =
null,
299 Vector2? textureScale =
null,
301 SpriteEffects? spriteEffects =
null)
303 if (
Texture ==
null) {
return; }
307 bool flipHorizontal = spriteEffects.Value.HasFlag(SpriteEffects.FlipHorizontally);
308 bool flipVertical = spriteEffects.Value.HasFlag(SpriteEffects.FlipVertically);
311 if (flipHorizontal != flipVertical) { addedRotation = -addedRotation; }
313 Vector2 advanceX = addedRotation == 0.0f ? Vector2.UnitX :
new Vector2((
float)Math.Cos(addedRotation), (
float)Math.Sin(addedRotation));
314 Vector2 advanceY =
new Vector2(-advanceX.Y, advanceX.X);
317 Vector2 drawOffset = startOffset ?? Vector2.Zero;
318 Vector2 scale = textureScale ?? Vector2.One;
319 Color drawColor = color ?? Color.White;
320 Vector2 transformedOrigin =
origin ?? Vector2.Zero;
322 transformedOrigin = advanceX * transformedOrigin.X + advanceY * transformedOrigin.Y;
324 void drawSection(Vector2 slicePos,
Rectangle sliceRect)
326 Vector2 transformedPos = slicePos;
330 transformedPos.X = targetSize.X - transformedPos.X - sliceRect.Width * scale.X;
334 transformedPos.Y = targetSize.Y - transformedPos.Y - sliceRect.Height * scale.Y;
337 transformedPos = advanceX * transformedPos.X + advanceY * transformedPos.Y;
338 transformedPos += position - transformedOrigin;
341 position: transformedPos,
342 sourceRectangle: sliceRect,
348 layerDepth:
depth ??
this.depth);
352 drawOffset.X = (drawOffset.X / scale.X) % sourceRect.Width;
353 drawOffset.Y = (drawOffset.Y / scale.Y) % sourceRect.Height;
356 int xTiles = (int)Math.Ceiling((targetSize.X + drawOffset.X * scale.X) / (sourceRect.Width * scale.X));
358 int yTiles = (int)Math.Ceiling((targetSize.Y + drawOffset.Y * scale.Y) / (sourceRect.Height * scale.Y));
361 Vector2 currDrawPosition = -drawOffset;
366 for (
int x = 0; x < xTiles; x++)
368 texPerspective.X = sourceRect.X;
369 texPerspective.Width = sourceRect.Width;
370 texPerspective.Height = sourceRect.Height;
373 if (currDrawPosition.X < 0)
375 float diff = -currDrawPosition.X;
376 currDrawPosition.X += diff;
377 texPerspective.Width -= (int)diff;
379 texPerspective.X += (int)diff;
384 int diff = (int)(((currDrawPosition.X + texPerspective.Width * scale.X) - targetSize.X) / scale.X);
385 texPerspective.Width -= diff;
388 currDrawPosition.Y = -drawOffset.Y;
390 for (
int y = 0; y < yTiles; y++)
392 texPerspective.Y = sourceRect.Y;
393 texPerspective.Height = sourceRect.Height;
396 if (currDrawPosition.Y < 0f)
398 float diff = -currDrawPosition.Y;
399 currDrawPosition.Y += diff;
400 texPerspective.Height -= (int)diff;
401 texPerspective.Y += (int)diff;
407 int diff = (int)(((currDrawPosition.Y + texPerspective.Height * scale.Y) - targetSize.Y) / scale.Y);
408 texPerspective.Height -= diff;
411 drawSection(currDrawPosition, texPerspective);
413 currDrawPosition.Y += texPerspective.Height * scale.Y;
416 currDrawPosition.X += texPerspective.Width * scale.X;
420 partial
void DisposeTexture()
422 disposeStackTrace = Environment.StackTrace;
431 if (!pathKey.IsEmpty && textureRefCounts.ContainsKey(pathKey))
433 textureRefCounts[pathKey].RefCount--;
434 if (textureRefCounts[pathKey].RefCount <= 0)
436 textureRefCounts.Remove(pathKey);
449 CrossThread.RequestExecutionOnMainThread(() =>
static ContentPath FromRaw(string? rawValue)
static readonly ContentPath Empty
ContentPackage? ContentPackage
Vector2 GetAttributeVector2(string key, in Vector2 def)
static GraphicsDeviceManager GraphicsDeviceManager
virtual void Draw(ISpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect=SpriteEffects.None, float? depth=null)
Vector2 RelativeOrigin
0 - 1
void DrawSilhouette(SpriteBatch spriteBatch, Vector2 pos, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect=SpriteEffects.None, float? depth=null)
Creates a silhouette for the sprite (or outline if the sprite is rendered on top of it)
void Draw(ISpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate=0.0f, float scale=1.0f, SpriteEffects spriteEffect=SpriteEffects.None, float? depth=null)
async Task LazyLoadAsync()
void EnsureLazyLoaded(bool isAsync=false)
static readonly Version LastBrokenTiledSpriteGameVersion
Last version of the game that had broken handling of sprites that were scaled, flipped and offset
volatile bool loadingAsync
void Draw(ISpriteBatch spriteBatch, Vector2 pos, Color color, float rotate=0.0f, float scale=1.0f, SpriteEffects spriteEffect=SpriteEffects.None, float? depth=null)
void Draw(ISpriteBatch spriteBatch, Vector2 pos, float rotate=0.0f, float scale=1.0f, SpriteEffects spriteEffect=SpriteEffects.None)
Sprite(Texture2D texture, Rectangle? sourceRectangle, Vector2? newOffset, float newRotation=0.0f, string path=null)
Sprite(ContentXElement element, string path="", string file="", bool lazyLoad=false, float sourceRectScale=1)
ContentXElement SourceElement
Reference to the xml element from where the sprite was created. Can be null if the sprite was not def...
static IEnumerable< Sprite > LoadedSprites
void DrawTiled(ISpriteBatch spriteBatch, Vector2 position, Vector2 targetSize, float rotation=0f, Vector2? origin=null, Color? color=null, Vector2? startOffset=null, Vector2? textureScale=null, float? depth=null, SpriteEffects? spriteEffects=null)
static Texture2D LoadTexture(string file, bool compress=true, ContentPackage contentPackage=null)