protected static Opacity ComputeOpacity(ITextureUpload upload) { // TODO: Investigate performance issues and revert functionality once we are sure there is no overhead. // see https://github.com/ppy/osu/issues/9307 return(Opacity.Mixed); // ReadOnlySpan<Rgba32> data = upload.Data; // // if (data.Length == 0) // return Opacity.Transparent; // // int firstPixelValue = data[0].A; // // // Check if the first pixel has partial transparency (neither fully-opaque nor fully-transparent). // if (firstPixelValue != 0 && firstPixelValue != 255) // return Opacity.Mixed; // // // The first pixel is GUARANTEED to be either fully-opaque or fully-transparent. // // Now we need to go through the rest of the image and check that every other pixel matches this value. // for (int i = 1; i < data.Length; i++) // { // if (data[i].A != firstPixelValue) // return Opacity.Mixed; // } // // return firstPixelValue == 0 ? Opacity.Transparent : Opacity.Opaque; }
internal override void SetData(ITextureUpload upload, WrapMode wrapModeS, WrapMode wrapModeT, Opacity?uploadOpacity) { // Can only perform padding when the bounds are a sub-part of the texture RectangleI middleBounds = upload.Bounds; if (middleBounds.IsEmpty || middleBounds.Width * middleBounds.Height > upload.Data.Length) { // For a texture atlas, we don't care about opacity, so we avoid // any computations related to it by assuming it to be mixed. base.SetData(upload, wrapModeS, wrapModeT, Opacity.Mixed); return; } int actualPadding = padding / (1 << upload.Level); if (wrapModeS != WrapMode.None && wrapModeT != WrapMode.None) { uploadCornerPadding(upload, middleBounds, actualPadding); } if (wrapModeS != WrapMode.None) { uploadHorizontalPadding(upload, middleBounds, actualPadding); } if (wrapModeT != WrapMode.None) { uploadVerticalPadding(upload, middleBounds, actualPadding); } // Upload the middle part of the texture // For a texture atlas, we don't care about opacity, so we avoid // any computations related to it by assuming it to be mixed. base.SetData(upload, wrapModeS, wrapModeT, Opacity.Mixed); }
protected static Opacity ComputeOpacity(ITextureUpload upload) { // TODO: Investigate performance issues and revert functionality once we are sure there is no overhead. // see https://github.com/ppy/osu/issues/9307 return(Opacity.Mixed); // if (upload.Data.Length == 0) // return Opacity.Transparent; // // bool isTransparent = true; // bool isOpaque = true; // // for (int i = 0; i < upload.Data.Length; ++i) // { // isTransparent &= upload.Data[i].A == 0; // isOpaque &= upload.Data[i].A == 255; // // if (!isTransparent && !isOpaque) // return Opacity.Mixed; // } // // if (isTransparent) // return Opacity.Transparent; // // return Opacity.Opaque; }
protected override void DoUpload(ITextureUpload upload, IntPtr dataPointer) { if (!(upload is VideoTextureUpload videoUpload)) { return; } // Do we need to generate a new texture? if (textureIds == null) { Debug.Assert(memoryLease == null); memoryLease = NativeMemoryTracker.AddMemory(this, Width * Height * 3 / 2); textureIds = new int[3]; GL.GenTextures(textureIds.Length, textureIds); for (int i = 0; i < textureIds.Length; i++) { GLWrapper.BindTexture(textureIds[i]); if (i == 0) { int width = videoUpload.Frame->width; int height = videoUpload.Frame->height; GL.TexImage2D(TextureTarget2d.Texture2D, 0, TextureComponentCount.R8, width, height, 0, PixelFormat.Red, PixelType.UnsignedByte, IntPtr.Zero); textureSize += width * height; } else { int width = (videoUpload.Frame->width + 1) / 2; int height = (videoUpload.Frame->height + 1) / 2; GL.TexImage2D(TextureTarget2d.Texture2D, 0, TextureComponentCount.R8, width, height, 0, PixelFormat.Red, PixelType.UnsignedByte, IntPtr.Zero); textureSize += width * height; } GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge); } } for (int i = 0; i < textureIds.Length; i++) { GLWrapper.BindTexture(textureIds[i]); GL.PixelStore(PixelStoreParameter.UnpackRowLength, videoUpload.Frame->linesize[(uint)i]); GL.TexSubImage2D(TextureTarget2d.Texture2D, 0, 0, 0, videoUpload.Frame->width / (i > 0 ? 2 : 1), videoUpload.Frame->height / (i > 0 ? 2 : 1), PixelFormat.Red, PixelType.UnsignedByte, (IntPtr)videoUpload.Frame->data[(uint)i]); } GL.PixelStore(PixelStoreParameter.UnpackRowLength, 0); UploadComplete = true; }
public Layer(SKBitmap image) { RelativeSizeAxes = Axes.Both; InternalChild = Sprite = new Sprite() { RelativeSizeAxes = Axes.Both }; IsVisible.ValueChanged += e => Alpha = e.NewValue ? 1 : 0; Image = null !; Canvas = null !; Upload = null !; SetImage(image); }
private void uploadHorizontalPadding(ITextureUpload upload, RectangleI middleBounds, int actualPadding) { RectangleI[] sideBoundsArray = { new RectangleI(middleBounds.X - actualPadding, middleBounds.Y, actualPadding, middleBounds.Height).Intersect(atlasBounds), // Left new RectangleI(middleBounds.X + middleBounds.Width, middleBounds.Y, actualPadding, middleBounds.Height).Intersect(atlasBounds), // Right }; int[] sideIndices = { 0, // Left middleBounds.Width - 1, // Right }; for (int i = 0; i < 2; ++i) { RectangleI sideBounds = sideBoundsArray[i]; if (!sideBounds.IsEmpty) { bool allTransparentBlack = true; int index = sideIndices[i]; var sideUpload = new MemoryAllocatorTextureUpload(sideBounds.Width, sideBounds.Height) { Bounds = sideBounds }; int stride = middleBounds.Width; for (int y = 0; y < sideBounds.Height; ++y) { for (int x = 0; x < sideBounds.Width; ++x) { Rgba32 pixel = upload.Data[index + y * stride]; allTransparentBlack &= pixel == transparent_black; sideUpload.RawData[y * sideBounds.Width + x] = pixel; } } // Only upload padding if the border isn't completely transparent. if (!allTransparentBlack) { // For a texture atlas, we don't care about opacity, so we avoid // any computations related to it by assuming it to be mixed. base.SetData(sideUpload, WrapMode.None, WrapMode.None, Opacity.Mixed); } } } }
internal override void SetData(ITextureUpload upload, WrapMode wrapModeS, WrapMode wrapModeT, Opacity?uploadOpacity) { if (uploadOpacity != null && uploadOpacity != Opacity.Opaque) { throw new InvalidOperationException("Video texture uploads must always be opaque"); } UploadComplete = false; // We do not support videos with transparency at this point, // so the upload's opacity as well as the texture's opacity // is always opaque. base.SetData(upload, wrapModeS, wrapModeT, Opacity = Opacity.Opaque); }
protected void UpdateOpacity(ITextureUpload upload, ref Opacity?uploadOpacity) { // Compute opacity if it doesn't have a value yet uploadOpacity ??= ComputeOpacity(upload); // Update the texture's opacity depending on the upload's opacity. // If the upload covers the entire bounds of the texture, it fully // determines the texture's opacity. Otherwise, it can only turn // the texture's opacity into a mixed state (if it disagrees with // the texture's existing opacity). if (upload.Bounds == Bounds && upload.Level == 0) { Opacity = uploadOpacity.Value; } else if (uploadOpacity.Value != Opacity) { Opacity = Opacity.Mixed; } }
private void uploadCornerPadding(ITextureUpload upload, RectangleI middleBounds, int actualPadding) { RectangleI[] cornerBoundsArray = { new RectangleI(middleBounds.X - actualPadding, middleBounds.Y - actualPadding, actualPadding, actualPadding).Intersect(atlasBounds), // TopLeft new RectangleI(middleBounds.X + middleBounds.Width, middleBounds.Y - actualPadding, actualPadding, actualPadding).Intersect(atlasBounds), // TopRight new RectangleI(middleBounds.X - actualPadding, middleBounds.Y + middleBounds.Height, actualPadding, actualPadding).Intersect(atlasBounds), // BottomLeft new RectangleI(middleBounds.X + middleBounds.Width, middleBounds.Y + middleBounds.Height, actualPadding, actualPadding).Intersect(atlasBounds), // BottomRight }; int[] cornerIndices = { 0, // TopLeft middleBounds.Width - 1, // TopRight (middleBounds.Height - 1) * middleBounds.Width, // BottomLeft (middleBounds.Height - 1) * middleBounds.Width + middleBounds.Width - 1, // BottomRight }; for (int i = 0; i < 4; ++i) { RectangleI cornerBounds = cornerBoundsArray[i]; int nCornerPixels = cornerBounds.Width * cornerBounds.Height; Rgba32 cornerPixel = upload.Data[cornerIndices[i]]; // Only upload if we have a non-zero size and if the colour isn't already transparent black if (nCornerPixels > 0 && cornerPixel != transparent_black) { var cornerUpload = new MemoryAllocatorTextureUpload(cornerBounds.Width, cornerBounds.Height) { Bounds = cornerBounds }; for (int j = 0; j < nCornerPixels; ++j) { cornerUpload.RawData[j] = cornerPixel; } // For a texture atlas, we don't care about opacity, so we avoid // any computations related to it by assuming it to be mixed. base.SetData(cornerUpload, WrapMode.None, WrapMode.None, Opacity.Mixed); } } }
/// <summary> /// Sets the pixel data of this <see cref="TextureGLAtlas"/>. /// </summary> /// <param name="upload">The <see cref="ITextureUpload"/> containing the data.</param> /// <param name="wrapModeS">The texture wrap mode in horizontal direction.</param> /// <param name="wrapModeT">The texture wrap mode in vertical direction.</param> /// <param name="uploadOpacity">Whether the upload is opaque, transparent, or a mix of both..</param> internal abstract void SetData(ITextureUpload upload, WrapMode wrapModeS, WrapMode wrapModeT, Opacity?uploadOpacity);
/// <summary> /// Sets the pixel data of this <see cref="TextureGL"/>. /// </summary> /// <param name="upload">The <see cref="ITextureUpload"/> containing the data.</param> public void SetData(ITextureUpload upload) => SetData(upload, WrapModeS, WrapModeT, null);
public abstract void SetData(ITextureUpload upload);
public override void SetData(ITextureUpload upload) { UploadComplete = false; base.SetData(upload); }