A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System;
5using osu.Framework.Graphics.Primitives;
6using osuTK;
7using osu.Framework.Graphics.Colour;
8using osu.Framework.Graphics.OpenGL.Vertices;
9using osu.Framework.Graphics.Textures;
10using osuTK.Graphics.ES30;
11
12namespace osu.Framework.Graphics.OpenGL.Textures
13{
14 internal class TextureGLSub : TextureGL
15 {
16 private readonly TextureGL parent;
17 private RectangleI bounds;
18
19 public override RectangleI Bounds => bounds;
20
21 public override TextureGL Native => parent.Native;
22
23 public override int TextureId => parent.TextureId;
24 public override bool Loaded => parent.Loaded;
25
26 internal override bool IsQueuedForUpload
27 {
28 get => parent.IsQueuedForUpload;
29 set => parent.IsQueuedForUpload = value;
30 }
31
32 public TextureGLSub(RectangleI bounds, TextureGL parent, WrapMode wrapModeS = WrapMode.None, WrapMode wrapModeT = WrapMode.None)
33 : base(wrapModeS, wrapModeT)
34 {
35 // If GLWrapper is not initialized at this point, it means we do not have OpenGL available
36 // and thus will never draw anything. In this case it is fine if the parent texture is null.
37 if (GLWrapper.IsInitialized && parent == null)
38 throw new InvalidOperationException("May not construct a subtexture without a parent texture to refer to.");
39
40 this.bounds = bounds;
41 this.parent = parent;
42 }
43
44 public override int Height
45 {
46 get => bounds.Height;
47 set => bounds.Height = value;
48 }
49
50 public override int Width
51 {
52 get => bounds.Width;
53 set => bounds.Width = value;
54 }
55
56 private RectangleF boundsInParent(RectangleF? textureRect)
57 {
58 RectangleF actualBounds = bounds;
59
60 if (textureRect.HasValue)
61 {
62 RectangleF localBounds = textureRect.Value;
63 actualBounds.X += localBounds.X;
64 actualBounds.Y += localBounds.Y;
65 actualBounds.Width = localBounds.Width;
66 actualBounds.Height = localBounds.Height;
67 }
68
69 return actualBounds;
70 }
71
72 public override RectangleF GetTextureRect(RectangleF? textureRect) => parent.GetTextureRect(boundsInParent(textureRect));
73
74 internal override void DrawTriangle(Triangle vertexTriangle, ColourInfo drawColour, RectangleF? textureRect = null, Action<TexturedVertex2D> vertexAction = null,
75 Vector2? inflationPercentage = null, RectangleF? textureCoords = null)
76 {
77 parent.DrawTriangle(vertexTriangle, drawColour, boundsInParent(textureRect), vertexAction, inflationPercentage, boundsInParent(textureCoords));
78 }
79
80 internal override void DrawQuad(Quad vertexQuad, ColourInfo drawColour, RectangleF? textureRect = null, Action<TexturedVertex2D> vertexAction = null, Vector2? inflationPercentage = null,
81 Vector2? blendRangeOverride = null, RectangleF? textureCoords = null)
82 {
83 parent.DrawQuad(vertexQuad, drawColour, boundsInParent(textureRect), vertexAction, inflationPercentage: inflationPercentage, blendRangeOverride: blendRangeOverride, boundsInParent(textureCoords));
84 }
85
86 internal override bool Bind(TextureUnit unit, WrapMode wrapModeS, WrapMode wrapModeT)
87 {
88 if (!Available)
89 throw new ObjectDisposedException(ToString(), "Can not bind disposed sub textures.");
90
91 Upload();
92
93 return parent.Bind(unit, wrapModeS, wrapModeT);
94 }
95
96 internal override bool Upload() => false;
97
98 internal override void FlushUploads()
99 {
100 }
101
102 internal override void SetData(ITextureUpload upload, WrapMode wrapModeS, WrapMode wrapModeT, Opacity? uploadOpacity)
103 {
104 if (upload.Bounds.Width > bounds.Width || upload.Bounds.Height > bounds.Height)
105 {
106 throw new ArgumentOutOfRangeException(
107 nameof(upload),
108 $"Texture is too small to fit the requested upload. Texture size is {bounds.Width} x {bounds.Height}, upload size is {upload.Bounds.Width} x {upload.Bounds.Height}.");
109 }
110
111 if (upload.Bounds.IsEmpty)
112 upload.Bounds = bounds;
113 else
114 {
115 var adjustedBounds = upload.Bounds;
116
117 adjustedBounds.X += bounds.X;
118 adjustedBounds.Y += bounds.Y;
119
120 upload.Bounds = adjustedBounds;
121 }
122
123 UpdateOpacity(upload, ref uploadOpacity);
124 parent?.SetData(upload, wrapModeS, wrapModeT, uploadOpacity);
125 }
126 }
127}