private static void DebugValidate(Bounds sourceBounds, XTexture2D referenceTexture)
    {
        Bounds referenceBounds = referenceTexture.Bounds;

        if (!referenceBounds.Contains(sourceBounds))
        {
            EmitOverlappingWarning(sourceBounds, referenceTexture);
        }

        if (sourceBounds.Right < sourceBounds.Left || sourceBounds.Bottom < sourceBounds.Top)
        {
            EmitInvertedWarning(sourceBounds, referenceTexture);
        }

        if (sourceBounds.Degenerate)
        {
            EmitDegenerateWarning(sourceBounds, referenceTexture);
        }
#if false
        if (source.Left < 0 || source.Top < 0 || source.Right >= reference.Width || source.Bottom >= reference.Height)
        {
            if (source.Right - reference.Width > 1 || source.Bottom - reference.Height > 1)
            {
                Debug.Warning($"Out of range source '{source}' for texture '{reference.SafeName()}' ({reference.Width}, {reference.Height})");
            }
        }
        if (source.Right < source.Left || source.Bottom < source.Top)
        {
            Debug.Warning($"Inverted range source '{source}' for texture '{reference.SafeName()}'");
        }
#endif
    }
Esempio n. 2
0
 internal Drawable(XTexture2D texture, Bounds?source = null, float rotation = 0.0f, int offset = 0)
 {
     Texture  = texture;
     Source   = source;
     Rotation = Math.Clamp(rotation, 0.0f, MathF.PI * 2.0f);
     Offset   = offset;
 }
 private static void EmitDegenerateWarning(Bounds sourceBounds, XTexture2D referenceTexture)
 {
     if (referenceTexture is not InternalTexture2D && referenceTexture.Meta().ShouldReportError(ReportOnceErrors.DegenerateSource))
     {
         Debug.Warning($"Degenerate sprite source '{sourceBounds}' for texture '{referenceTexture.NormalizedName()}' ({referenceTexture.Extent()})");
     }
 }
Esempio n. 4
0
        internal Initializer(XTexture2D reference, Bounds dimensions, uint expectedScale, TextureType textureType, bool animated)
        {
            Reference      = reference;
            BlendState     = DrawState.CurrentBlendState;
            SamplerState   = DrawState.CurrentSamplerState;
            ExpectedScale  = expectedScale;
            Bounds         = dimensions;
            IsPreview      = Configuration.Preview.Override.Instance is not null;
            Scaler         = Configuration.Preview.Override.Instance?.Scaler ?? Config.Resample.Scaler;
            ScalerGradient = Configuration.Preview.Override.Instance?.ScalerGradient ?? Config.Resample.ScalerGradient;

            TextureType = textureType;

            // Truncate the bounds so that it fits if it wouldn't otherwise fit
            if (!Bounds.ClampToChecked(Reference.Bounds, out var clampedBounds))
            {
                Debug.Warning($"SpriteInfo for '{reference.NormalizedName()}' bounds '{dimensions}' are not contained in reference bounds '{(Bounds)reference.Bounds}'");
                Bounds = clampedBounds;
            }

            var refMeta = reference.Meta();
            var refData = refMeta.CachedData;

            if (refData is null)
            {
                // TODO : Switch this around to use ReadOnlySequence so our hash is specific to the sprite
                refData = new byte[reference.SizeBytes()];
                Debug.Trace($"Reloading Texture Data (not in cache): {reference.NormalizedName(DrawingColor.LightYellow)}");
                reference.GetData(refData);
                reference.Meta().CachedRawData = refData;
                if (refMeta.IsCompressed)
                {
                    refData = null;                     // we can only use uncompressed data at this stage.
                }
                WasCached = false;
            }
            else if (refData == Texture2DMeta.BlockedSentinel)
            {
                refData   = null;
                WasCached = false;
            }
            else
            {
                WasCached = true;
            }

            ReferenceData = refData;

            Hash     = null;
            DataHash = null;
            if (Config.SuspendedCache.Enabled && animated)
            {
                (Hash, DataHash) = GetHash();
            }
        }
 private AnimatedTexture(
     XTexture2D texture,
     Vector2I size,
     Vector2I[] spriteOffsets,
     int ticksPerFrame,
     uint currentTick
     ) : base(texture)
 {
     Size          = size;
     SpriteOffsets = spriteOffsets;
     TicksPerFrame = ticksPerFrame;
     CurrentTick   = currentTick;
 }
Esempio n. 6
0
    private static unsafe void SetDataPurge <T>(
        XTexture2D texture,
        XRectangle?rect,
        ReadOnlySpan <T> data,
        int startIndex,
        int elementCount,
        bool animated
        ) where T : unmanaged
    {
        TextureCache.Remove(texture);

        if (!ManagedSpriteInstance.Validate(texture, clean: true))
        {
            return;
        }

        if (texture.Format.IsBlock())
        {
            ManagedSpriteInstance.FullPurge(texture, animated: animated);
            return;
        }

#if ASYNC_SETDATA
        var byteData = Cacheable(texture) ? GetByteArray(data, startIndex, elementCount, out int elementSize) : null;

        ThreadQueue.Queue((data) =>
                          Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;
                          ScaledTexture.Purge(
                              reference: texture,
                              bounds: rect,
                              data: new DataRef <byte>(
                                  data: data,
                                  offset: startIndex * elementSize,
                                  length: elementCount * elementSize
                                  )
                              ), byteData);
#else
        var byteData = Cacheable(texture) ? data : default;

        var span = byteData.IsEmpty ? default : byteData.Slice(startIndex, elementCount).AsBytes();

                   ManagedSpriteInstance.Purge(
                       reference: texture,
                       bounds: rect,
                       data: new(span),
                       animated: animated
                       );
#endif
    }
Esempio n. 7
0
    private static bool CheckIsDataChanged <T>(
        XTexture2D instance,
        int level,
        int arraySlice,
        XRectangle?inRect,
        T[] data,
        int startIndex,
        int elementCount
        ) where T : unmanaged
    {
        Bounds rect = inRect ?? instance.Bounds;

        if (instance.TryMeta(out var meta) && meta.CachedData is { } cachedData)
        {
            var dataSpan = data.AsReadOnlySpan().Cast <T, byte>();

            unsafe {
                var inSpan      = dataSpan;
                int inOffset    = 0;
                int inRowLength = rect.Width * sizeof(T);

                var cachedSpan = new Span2D <byte>(
                    array: cachedData,
                    offset: startIndex * sizeof(T),
                    width: rect.Width * sizeof(T),
                    height: rect.Height,
                    pitch: (instance.Width - rect.Width) * sizeof(T)
                    );

                for (int y = 0; y < rect.Height; ++y)
                {
                    var inSpanRow     = inSpan.SliceUnsafe(inOffset, inRowLength);
                    var cachedSpanRow = cachedSpan.GetRowSpan(y);
                    if (!inSpanRow.SequenceEqual(cachedSpanRow))
                    {
                        return(true);
                    }
                    inOffset += inRowLength;
                }

                return(false);
            }
        }

        return(true);
    }
    internal static Span <byte> Decode(ReadOnlySpan <byte> data, Vector2I size, SurfaceFormat format)
    {
        using var tempTexture = new DecodingTexture2D(DrawState.Device, size.Width, size.Height, false, format)
              {
                  Name = "Decode Texture"
              };
        tempTexture.SetData(data.ToArray());
        using var pngStream = new MemoryStream();
        tempTexture.SaveAsPng(pngStream, tempTexture.Width, tempTexture.Height);
        pngStream.Flush();
        using var pngTexture = XTexture2D.FromStream(DrawState.Device, pngStream);
        var resultData = GC.AllocateUninitializedArray <byte>(pngTexture.Area() * sizeof(uint), pinned: true);

        pngTexture.GetData(resultData);

        return(resultData.AsSpan());
    }
    internal static bool IsFont(XTexture2D texture, Vector2I spriteSize, Vector2I sheetSize)
    {
        switch (texture.Format)
        {
        case SurfaceFormat.Dxt1:
        case SurfaceFormat.Dxt1SRgb:
        case SurfaceFormat.Dxt1a:
        case SurfaceFormat.Dxt3:
        case SurfaceFormat.Dxt3SRgb:
        case SurfaceFormat.Dxt5:
        case SurfaceFormat.Dxt5SRgb:
            return(Math.Min(spriteSize.MinOf, sheetSize.MinOf) >= 1);

        default:
            return(false);
        }
    }
Esempio n. 10
0
 public static void OnConstructTexture2D(
     XTexture2D __instance,
     GraphicsDevice graphicsDevice,
     int width,
     int height,
     bool mipmap,
     SurfaceFormat format,
     ref SurfaceType type,
     bool shared,
     int arraySize,
     ref bool __state
     )
 {
     if (PlatformConstruct is not null && arraySize == 1 && type == SurfaceType.Texture)
     {
         type    = SurfaceType.SwapChainRenderTarget;
         __state = true;
     }
Esempio n. 11
0
    internal ManagedTexture2D(
        ReadOnlyPinnedSpan <byte> .FixedSpan data,
        ManagedSpriteInstance instance,
        XTexture2D reference,
        Vector2I dimensions,
        SurfaceFormat format,
        string?name = null
        ) : base(
            graphicsDevice: reference.GraphicsDevice.IsDisposed ? DrawState.Device : reference.GraphicsDevice,
            width: dimensions.Width,
            height: dimensions.Height,
            mipmap: UseMips,
            format: format,
            type: PTexture2D.PlatformConstruct is null ? SurfaceType.Texture : SurfaceType.SwapChainRenderTarget,     // this prevents the texture from being constructed immediately
            shared: UseShared,
            arraySize: 1
            )
    {
        if (PTexture2D.PlatformConstruct is not null && !GL.Texture2DExt.Construct(this, data, dimensions, UseMips, format, SurfaceType.Texture, UseShared))
        {
            PTexture2D.PlatformConstruct(this, dimensions.X, dimensions.Y, UseMips, format, SurfaceType.Texture, UseShared);
            SetData(data.Array);
        }

        Name = name ?? $"{reference.NormalizedName()} [internal managed <{format}>]";

        Reference      = reference.MakeWeak();
        SpriteInstance = instance;
        Dimensions     = dimensions - instance.BlockPadding;

        reference.Disposing += OnParentDispose;

        Interlocked.Add(ref TotalAllocatedSize, (ulong)this.SizeBytes());
        Interlocked.Increment(ref TotalManagedTextures);

        if (Configuration.Config.Garbage.ShouldCollectAccountOwnedTextures)
        {
            Garbage.MarkOwned(format, dimensions.Area);
            Marked = true;
        }
    }
Esempio n. 12
0
 private static bool Cacheable(XTexture2D texture) => texture.LevelCount <= 1;
Esempio n. 13
0
 internal Drawable(XTexture2D texture, Bounds?source, int rotationDegrees) : this(texture, source, DegreesToRadians(rotationDegrees))
 {
 }
Esempio n. 14
0
    public static bool PatchImage(IAssetDataForImage __instance, XTexture2D source, XRectangle?sourceArea, XRectangle?targetArea, PatchMode patchMode)
    {
        if (!Config.SMAPI.ApplyPatchEnabled)
        {
            return(true);
        }

        // get texture
        if (source is null)
        {
            throw new ArgumentNullException(nameof(source), "Can't patch from a null source texture.");
        }

        XTexture2D target = __instance.Data;

        // get areas
        sourceArea ??= new(0, 0, source.Width, source.Height);
        targetArea ??= new(0, 0, Math.Min(sourceArea.Value.Width, target.Width), Math.Min(sourceArea.Value.Height, target.Height));

        // validate
        if (!source.Bounds.Contains(sourceArea.Value))
        {
            throw new ArgumentOutOfRangeException(nameof(sourceArea), "The source area is outside the bounds of the source texture.");
        }
        if (!target.Bounds.Contains(targetArea.Value))
        {
            throw new ArgumentOutOfRangeException(nameof(targetArea), "The target area is outside the bounds of the target texture.");
        }
        if (sourceArea.Value.Size != targetArea.Value.Size)
        {
            throw new InvalidOperationException("The source and target areas must be the same size.");
        }

        if (GL.Texture2DExt.CopyTexture(source, sourceArea.Value, target, targetArea.Value, patchMode))
        {
            return(false);
        }

        // get source data
        int pixelCount = sourceArea.Value.Width * sourceArea.Value.Height;
        var sourceData = GC.AllocateUninitializedArray <XColor>(pixelCount);

        source.GetData(0, sourceArea, sourceData, 0, pixelCount);

        // merge data in overlay mode
        if (patchMode == PatchMode.Overlay)
        {
            // get target data
            var targetData = GC.AllocateUninitializedArray <XColor>(pixelCount);
            target.GetData(0, targetArea, targetData, 0, pixelCount);

            // merge pixels
            for (int i = 0; i < sourceData.Length; i++)
            {
                var above = sourceData[i];
                var below = targetData[i];

                // shortcut transparency
                if (above.A < MinOpacity)
                {
                    sourceData[i] = below;
                    continue;
                }
                if (below.A < MinOpacity)
                {
                    sourceData[i] = above;
                    continue;
                }

                // merge pixels
                // This performs a conventional alpha blend for the pixels, which are already
                // premultiplied by the content pipeline. The formula is derived from
                // https://shawnhargreaves.com/blog/premultiplied-alpha.html.
                float alphaBelow = 1 - (above.A / 255f);
                sourceData[i] = new XColor(
                    (int)(above.R + (below.R * alphaBelow)),               // r
                    (int)(above.G + (below.G * alphaBelow)),               // g
                    (int)(above.B + (below.B * alphaBelow)),               // b
                    Math.Max(above.A, below.A)                             // a
                    );
            }
        }

        // patch target texture
        target.SetData(0, targetArea, sourceData, 0, pixelCount);
        return(false);
    }
Esempio n. 15
0
 internal static void Unmark(XTexture2D texture)
 {
     texture.AssertNotNull();
     Unmark(texture.SizeBytesLong());
 }
Esempio n. 16
0
 private static bool GetIsSliced(Bounds bounds, XTexture2D reference, [NotNullWhen(true)] out Config.TextureRef?result)
 {
     return(reference.Meta().CheckSliced(bounds, out result));
 }
Esempio n. 17
0
 internal static Vector2I Extent(this XTexture2D texture) => new(texture.Width, texture.Height);
Esempio n. 18
0
 internal static void PurgeHash(XTexture2D reference)
 {
     reference.Meta().CachedRawData = null;
 }
Esempio n. 19
0
 internal static int Area(this XTexture2D texture) => texture.Width * texture.Height;
Esempio n. 20
0
 protected MetaTexture(XTexture2D?texture)
 {
     Texture = texture ?? ThrowNullReferenceException <XTexture2D?>(nameof(texture));
 }
 internal static bool IsWater(Bounds bounds, XTexture2D texture)
 {
     return(bounds.Right <= 640 && bounds.Top >= 2000 && bounds.Extent.MinOf >= WaterBlock && texture.NormalizedName() == @"LooseSprites\Cursors");
 }
 internal static void Validate(Bounds sourceBounds, XTexture2D referenceTexture)
 {
     DebugValidate(sourceBounds, referenceTexture);
 }
Esempio n. 23
0
 internal static Span <byte> Decode(XTexture2D texture, ReadOnlySpan <byte> data) => Decoder.MonoBlockDecoder.Decode(data, texture.Extent(), texture.Format);
Esempio n. 24
0
 internal DrawInfo(
     ManagedSpriteInstance?instance,
     XTexture2D texture,
     Bounds destination,
     Bounds source,
     in XColor color,
Esempio n. 25
0
 protected MetaTexture(XTexture2D?texture)
 {
     Texture = texture ?? throw new NullReferenceException(nameof(texture));
 }