示例#1
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
    }
示例#2
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);
    }
示例#3
0
 public static bool OnDraw(XSpriteBatch __instance, XTexture2D?texture, XVector2 position, XRectangle?sourceRectangle, XColor color)
 {
     return(ForwardDraw(
                @this: __instance,
                texture: texture,
                position: position,
                sourceRectangle: sourceRectangle,
                color: color
                ));
 }
示例#4
0
 public static bool OnDraw(XSpriteBatch __instance, XTexture2D?texture, XVector2 position, XRectangle?sourceRectangle, XColor color, float rotation, XVector2 origin, float scale, SpriteEffects effects, float layerDepth)
 {
     return(ForwardDraw(
                @this: __instance,
                texture: texture,
                position: position,
                sourceRectangle: sourceRectangle,
                color: color,
                rotation: rotation,
                origin: origin,
                scale: new XVector2(scale),
                effects: effects,
                layerDepth: layerDepth
                ));
 }
示例#5
0
    public static bool OnDraw(XSpriteBatch __instance, ref XTexture2D?texture, ref XVector2 position, ref XRectangle?sourceRectangle, ref XColor color, float rotation, ref XVector2 origin, ref XVector2 scale, SpriteEffects effects, float layerDepth)
    {
        if (!Config.IsEnabled)
        {
            return(true);
        }

        return(__instance.OnDraw(
                   texture: ref texture,
                   position: ref position,
                   source: ref sourceRectangle,
                   color: ref color,
                   rotation: rotation,
                   origin: ref origin,
                   scale: ref scale,
                   effects: effects,
                   layerDepth: ref layerDepth
                   ));
    }
示例#6
0
 public static bool OnDraw(XSpriteBatch __instance, XTexture2D?texture, XRectangle destinationRectangle, XRectangle?sourceRectangle, XColor color)
 {
     return(ForwardDraw(
                @this: __instance,
                texture: texture,
                destinationRectangle: destinationRectangle,
                sourceRectangle: sourceRectangle,
                color: color
                ));
 }
示例#7
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);
    }
示例#8
0
    internal static bool OnDraw(
        this XSpriteBatch @this,
        ref XTexture2D?texture,
        ref XVector2 position,
        ref XRectangle?source,
        ref XColor color,
        float rotation,
        ref XVector2 origin,
        ref XVector2 scale,
        SpriteEffects effects,
        ref float layerDepth
        )
    {
        if (texture is null)
        {
            return(false);
        }

        GetDrawParameters(
            texture: texture,
            source: source,
            bounds: out var sourceRectangle,
            scaleFactor: out var scaleFactor
            );

        var originalSourceRect = sourceRectangle;

        ManagedSpriteInstance?spriteInstance;
        ManagedTexture2D?     resampledTexture;

        if (texture is ManagedTexture2D texture2D)
        {
            resampledTexture = texture2D;
            spriteInstance   = resampledTexture.SpriteInstance;
            sourceRectangle  = resampledTexture.Dimensions;
        }
        else if (texture.FetchScaledTexture(
                     expectedScale: EstimateScale(scale, scaleFactor),
                     source: ref sourceRectangle,
                     spriteInstance: out spriteInstance,
                     create: true
                     ))
        {
            spriteInstance.UpdateReferenceFrame();
            resampledTexture = spriteInstance.Texture !;
        }
        else
        {
            resampledTexture = null;
        }

        if (spriteInstance is null || resampledTexture is null)
        {
            return(Continue);
        }

        if (originalSourceRect.X < 0)
        {
            position.X -= originalSourceRect.X * scale.X;
        }
        if (originalSourceRect.Y < 0)
        {
            position.Y -= originalSourceRect.Y * scale.Y;
        }

        var adjustedScale    = (Vector2F)scale / spriteInstance.Scale;
        var adjustedPosition = position;
        var adjustedOrigin   = (Vector2F)origin;

        if (spriteInstance.TexType == TextureType.SlicedImage)
        {
            sourceRectangle = source ?? resampledTexture.Bounds;
            if (source is not null)
            {
                sourceRectangle = new Bounds(
                    (Vector2I)source.Value.Location - spriteInstance.OriginalSourceRectangle.Offset,
                    source.Value.Size
                    );
                sourceRectangle.Offset = (sourceRectangle.OffsetF * spriteInstance.Scale).NearestInt();
                sourceRectangle.Extent = (sourceRectangle.ExtentF * spriteInstance.Scale).NearestInt();
            }
        }

        if (!spriteInstance.Padding.IsZero)
        {
            var paddingX = spriteInstance.Padding.X;
            var paddingY = spriteInstance.Padding.Y;

            if (effects.HasFlag(SpriteEffects.FlipHorizontally))
            {
                paddingX = (paddingX.Y, paddingX.X);
            }

            if (effects.HasFlag(SpriteEffects.FlipVertically))
            {
                paddingY = (paddingY.Y, paddingY.X);
            }

            var padding = new PaddingQuad(paddingX, paddingY);

            var textureSize = new Vector2F(sourceRectangle.Extent);
            var innerSize   = (Vector2F)spriteInstance.UnpaddedSize;

            // This is the scale factor to bring the inner size to the draw size.
            var innerRatio = textureSize / innerSize;             // spriteInstance.InnerRatio;

            // Scale the... scale by the scale factor.
            adjustedScale *= innerRatio;

            adjustedOrigin *= spriteInstance.Scale;
            adjustedOrigin /= innerRatio;
            adjustedOrigin += (Vector2F)padding.Offset;
        }
        else
        {
            adjustedOrigin *= spriteInstance.Scale;
        }

        if (source.HasValue)
        {
            sourceRectangle.Invert.X = source.Value.Width < 0;
            sourceRectangle.Invert.Y = source.Value.Height < 0;
        }

        if (Debug.Mode.RegisterDrawForSelect(
                instance: spriteInstance,
                texture: texture,
                originalPosition: position,
                originalSource: source,
                position: adjustedPosition,
                source: sourceRectangle,
                color: color,
                rotation: rotation,
                originalOrigin: origin,
                origin: adjustedOrigin,
                scale: adjustedScale,
                effects: effects,
                layerDepth: layerDepth
                ))
        {
            color = XColor.Red;
        }

        texture  = resampledTexture;
        source   = sourceRectangle;
        origin   = adjustedOrigin;
        scale    = adjustedScale;
        position = adjustedPosition;
        return(Continue);
    }
示例#9
0
    // Takes the arguments, and checks to see if the texture is padded. If it is, it is forwarded to the correct draw call, avoiding
    // intervening mods altering the arguments first.
    internal static bool OnDrawFirst(
        this XSpriteBatch @this,
        ref XTexture2D?texture,
        ref XRectangle destination,
        ref XRectangle?source,
        XColor color,
        float rotation,
        ref XVector2 origin,
        ref SpriteEffects effects,
        float layerDepth,
        ref ManagedTexture2D?__state
        )
    {
        if (texture is null)
        {
            return(false);
        }

        using var watchdogScoped = WatchDog.WatchDog.ScopedWorkingState;

        /*
         * if (destination.Width < 0 || destination.Height < 0) {
         *      Debug.Trace("destination invert");
         * }
         * if (source is XRectangle sourceRect && (sourceRect.Width < 0 || sourceRect.Height < 0)) {
         *      Debug.Trace("source invert");
         * }
         */

        GetDrawParameters(
            texture: texture,
            source: source,
            bounds: out var sourceRectangle,
            scaleFactor: out var scaleFactor
            );

        var referenceRectangle = sourceRectangle;

        Bounds destinationBounds = destination;

        var expectedScale2D = destinationBounds.ExtentF / sourceRectangle.ExtentF;
        var expectedScale   = EstimateScale(expectedScale2D, scaleFactor);

        if (!texture.FetchScaledTexture(
                expectedScale: expectedScale,
                source: ref sourceRectangle,
                spriteInstance: out var spriteInstance,
                create: true
                ))
        {
            return(Continue);
        }
        spriteInstance.UpdateReferenceFrame();

        if (referenceRectangle.X < 0)
        {
            destinationBounds.Left -= referenceRectangle.X;
        }
        if (referenceRectangle.Y < 0)
        {
            destinationBounds.Top -= referenceRectangle.Y;
        }

        var resampledTexture = spriteInstance.Texture !;

        if (!spriteInstance.Padding.IsZero)
        {
            // Convert the draw into the other draw style. This has to be done because the padding potentially has
            // subpixel accuracy when scaled to the destination rectangle.

            var originalSize    = referenceRectangle.ExtentF;
            var destinationSize = destinationBounds.ExtentF;
            var newScale        = destinationSize / originalSize;
            var newPosition     = destinationBounds.OffsetF;

            if ((destinationBounds.Invert.X || destinationBounds.Invert.Y) && DrawState.CurrentRasterizerState.CullMode == CullMode.CullCounterClockwiseFace)
            {
                // Winding order is invalid
                return(Stop);
            }
            if (destinationBounds.Invert.X)
            {
                effects ^= SpriteEffects.FlipHorizontally;
            }
            if (destinationBounds.Invert.Y)
            {
                effects ^= SpriteEffects.FlipVertically;
            }

            // TODO handle culling here for inverted sprites?

            @this.Draw(
                texture: resampledTexture,
                position: newPosition,
                sourceRectangle: sourceRectangle,
                color: color,
                rotation: rotation,
                origin: origin,
                scale: newScale,
                effects: effects,
                layerDepth: layerDepth
                );
            return(Stop);
        }
        __state = resampledTexture;
        return(Continue);
    }
示例#10
0
    internal static bool OnDraw(
        this XSpriteBatch @this,
        ref XTexture2D?texture,
        ref XRectangle destination,
        ref XRectangle?source,
        ref XColor color,
        float rotation,
        ref XVector2 origin,
        ref SpriteEffects effects,
        ref float layerDepth,
        ref ManagedTexture2D?__state
        )
    {
        if (texture is null)
        {
            return(false);
        }

        Bounds sourceRectangle;
        ManagedSpriteInstance?spriteInstance;
        ManagedTexture2D      resampledTexture;

        Bounds destinationBounds = destination;

        var referenceSource = source.GetValueOrDefault();

        if (__state is null)
        {
            GetDrawParameters(
                texture: texture,
                source: source,
                bounds: out sourceRectangle,
                scaleFactor: out var scaleFactor
                );

            var expectedScale2D = new Vector2F(destinationBounds.Extent) / new Vector2F(sourceRectangle.Extent);
            var expectedScale   = EstimateScale(expectedScale2D, scaleFactor);

            if (!texture.FetchScaledTexture(
                    expectedScale: expectedScale,
                    source: ref sourceRectangle,
                    spriteInstance: out spriteInstance
                    ))
            {
                return(Continue);
            }

            if (spriteInstance.TexType == TextureType.SlicedImage)
            {
                sourceRectangle = source ?? spriteInstance.Texture !.Bounds;
            }

            spriteInstance.UpdateReferenceFrame();

            resampledTexture = spriteInstance.Texture !;
        }
        else
        {
            resampledTexture = __state;
            spriteInstance   = resampledTexture.SpriteInstance;
            sourceRectangle  = resampledTexture.Dimensions;
            if (spriteInstance.TexType == TextureType.SlicedImage)
            {
                sourceRectangle = source ?? resampledTexture.Bounds;
                if (source.HasValue)
                {
                    sourceRectangle = new Bounds(
                        (Vector2I)source.Value.Location - spriteInstance.OriginalSourceRectangle.Offset,
                        source.Value.Size
                        );
                    sourceRectangle.Offset = (sourceRectangle.OffsetF * spriteInstance.Scale).NearestInt();
                    sourceRectangle.Extent = (sourceRectangle.ExtentF * spriteInstance.Scale).NearestInt();
                }
            }
        }

        if (referenceSource.X < 0)
        {
            destination.X -= referenceSource.X;
        }
        if (referenceSource.Y < 0)
        {
            destination.Y -= referenceSource.Y;
        }

        var scaledOrigin = (Vector2F)origin * spriteInstance.Scale;

        if (source.HasValue)
        {
            sourceRectangle.Invert.X = source.Value.Width < 0;
            sourceRectangle.Invert.Y = source.Value.Height < 0;
        }

        if (Debug.Mode.RegisterDrawForSelect(
                instance: spriteInstance,
                texture: texture,
                originalDestination: destinationBounds,
                destination: destination,
                source: sourceRectangle,
                color: color,
                rotation: rotation,
                originalOrigin: origin,
                origin: scaledOrigin,
                effects: effects,
                layerDepth: layerDepth
                ))
        {
            color = XColor.Red;
        }

        source  = sourceRectangle;
        origin  = scaledOrigin;
        texture = resampledTexture;

        return(Continue);
    }