1 // Copyright © 2024 Collabora, Ltd.
2 // SPDX-License-Identifier: MIT
3
4 use crate::format::Format;
5 use crate::image::SampleLayout;
6 use crate::tiling::{GOBType, Tiling};
7 use crate::Minify;
8
9 use std::ops::Add;
10
11 pub mod units {
12 #[derive(Clone, Debug, Copy, PartialEq)]
13 pub struct Elements {}
14
15 #[derive(Clone, Debug, Copy, PartialEq)]
16 pub struct Pixels {}
17
18 #[derive(Clone, Debug, Copy, PartialEq)]
19 pub struct Samples {}
20
21 #[derive(Clone, Debug, Copy, PartialEq)]
22 pub struct Bytes {}
23
24 #[derive(Clone, Debug, Copy, PartialEq)]
25 pub struct Tiles {}
26
27 #[derive(Clone, Debug, Copy, PartialEq)]
28 pub struct GOBs {}
29 }
30
31 #[derive(Clone, Debug, Copy, PartialEq, Default)]
32 #[repr(C)]
33 pub struct Extent4D<U> {
34 pub width: u32,
35 pub height: u32,
36 pub depth: u32,
37 pub array_len: u32,
38 phantom: std::marker::PhantomData<U>,
39 }
40
41 impl<U> Extent4D<U> {
new( width: u32, height: u32, depth: u32, array_len: u32, ) -> Extent4D<U>42 pub const fn new(
43 width: u32,
44 height: u32,
45 depth: u32,
46 array_len: u32,
47 ) -> Extent4D<U> {
48 Extent4D {
49 width,
50 height,
51 depth,
52 array_len,
53 phantom: std::marker::PhantomData,
54 }
55 }
56
align(self, alignment: &Self) -> Self57 pub fn align(self, alignment: &Self) -> Self {
58 Self {
59 width: self.width.next_multiple_of(alignment.width),
60 height: self.height.next_multiple_of(alignment.height),
61 depth: self.depth.next_multiple_of(alignment.depth),
62 array_len: self.array_len.next_multiple_of(alignment.array_len),
63 phantom: std::marker::PhantomData,
64 }
65 }
66
67 #[allow(dead_code)]
is_aligned_to(&self, alignment: Extent4D<U>) -> bool68 pub fn is_aligned_to(&self, alignment: Extent4D<U>) -> bool {
69 (self.width % alignment.width) == 0
70 && (self.height % alignment.height) == 0
71 && (self.depth % alignment.depth) == 0
72 && (self.array_len % alignment.array_len) == 0
73 }
74
mul<V>(self, other: Extent4D<V>) -> Extent4D<V>75 fn mul<V>(self, other: Extent4D<V>) -> Extent4D<V> {
76 Extent4D {
77 width: self.width * other.width,
78 height: self.height * other.height,
79 depth: self.depth * other.depth,
80 array_len: self.array_len * other.array_len,
81 phantom: std::marker::PhantomData,
82 }
83 }
84
div_ceil<V>(self, other: Self) -> Extent4D<V>85 fn div_ceil<V>(self, other: Self) -> Extent4D<V> {
86 Extent4D {
87 width: self.width.div_ceil(other.width),
88 height: self.height.div_ceil(other.height),
89 depth: self.depth.div_ceil(other.depth),
90 array_len: self.array_len.div_ceil(other.array_len),
91 phantom: std::marker::PhantomData,
92 }
93 }
94
cast_units<V>(self) -> Extent4D<V>95 pub fn cast_units<V>(self) -> Extent4D<V> {
96 Extent4D {
97 width: self.width,
98 height: self.height,
99 depth: self.depth,
100 array_len: self.array_len,
101 phantom: std::marker::PhantomData,
102 }
103 }
104 }
105
106 impl Extent4D<units::Pixels> {
to_sa( self, sample_layout: SampleLayout, ) -> Extent4D<units::Samples>107 pub fn to_sa(
108 self,
109 sample_layout: SampleLayout,
110 ) -> Extent4D<units::Samples> {
111 self.mul(sample_layout.px_extent_sa())
112 }
113
to_el( self, format: Format, sample_layout: SampleLayout, ) -> Extent4D<units::Elements>114 pub fn to_el(
115 self,
116 format: Format,
117 sample_layout: SampleLayout,
118 ) -> Extent4D<units::Elements> {
119 self.to_sa(sample_layout).div_ceil(format.el_extent_sa())
120 }
121
to_B( self, format: Format, sample_layout: SampleLayout, ) -> Extent4D<units::Bytes>122 pub fn to_B(
123 self,
124 format: Format,
125 sample_layout: SampleLayout,
126 ) -> Extent4D<units::Bytes> {
127 self.to_el(format, sample_layout).to_B(format)
128 }
129
to_tl( self, tiling: &Tiling, format: Format, sample_layout: SampleLayout, ) -> Extent4D<units::Tiles>130 pub fn to_tl(
131 self,
132 tiling: &Tiling,
133 format: Format,
134 sample_layout: SampleLayout,
135 ) -> Extent4D<units::Tiles> {
136 let tl_extent_B = tiling.extent_B();
137 self.to_B(format, sample_layout).div_ceil(tl_extent_B)
138 }
139 }
140
141 #[no_mangle]
nil_extent4d_px_to_el( extent_px: Extent4D<units::Pixels>, format: Format, sample_layout: SampleLayout, ) -> Extent4D<units::Elements>142 pub extern "C" fn nil_extent4d_px_to_el(
143 extent_px: Extent4D<units::Pixels>,
144 format: Format,
145 sample_layout: SampleLayout,
146 ) -> Extent4D<units::Elements> {
147 extent_px.to_el(format, sample_layout)
148 }
149
150 #[no_mangle]
nil_extent4d_px_to_tl( extent_px: Extent4D<units::Pixels>, tiling: &Tiling, format: Format, sample_layout: SampleLayout, ) -> Extent4D<units::Tiles>151 pub extern "C" fn nil_extent4d_px_to_tl(
152 extent_px: Extent4D<units::Pixels>,
153 tiling: &Tiling,
154 format: Format,
155 sample_layout: SampleLayout,
156 ) -> Extent4D<units::Tiles> {
157 extent_px.to_tl(tiling, format, sample_layout)
158 }
159
160 #[no_mangle]
nil_extent4d_px_to_B( extent_px: Extent4D<units::Pixels>, format: Format, sample_layout: SampleLayout, ) -> Extent4D<units::Bytes>161 pub extern "C" fn nil_extent4d_px_to_B(
162 extent_px: Extent4D<units::Pixels>,
163 format: Format,
164 sample_layout: SampleLayout,
165 ) -> Extent4D<units::Bytes> {
166 extent_px.to_B(format, sample_layout)
167 }
168
169 impl Extent4D<units::Samples> {
to_px(self, sample_layout: SampleLayout) -> Extent4D<units::Pixels>170 pub fn to_px(self, sample_layout: SampleLayout) -> Extent4D<units::Pixels> {
171 self.div_ceil(sample_layout.px_extent_sa())
172 }
173 }
174
175 impl Extent4D<units::Elements> {
to_B(self, format: Format) -> Extent4D<units::Bytes>176 pub fn to_B(self, format: Format) -> Extent4D<units::Bytes> {
177 Extent4D {
178 width: self.width * format.el_size_B(),
179 ..self.cast_units()
180 }
181 }
182
to_sa(self, format: Format) -> Extent4D<units::Samples>183 pub fn to_sa(self, format: Format) -> Extent4D<units::Samples> {
184 self.mul(format.el_extent_sa())
185 }
186 }
187
188 impl Extent4D<units::Bytes> {
size_B(&self) -> u64189 pub fn size_B(&self) -> u64 {
190 // size_B of something with layers doesn't make sense because we can't
191 // know the array stride based only on the other dimensions.
192 assert!(self.array_len == 1);
193 u64::from(self.width) * u64::from(self.height) * u64::from(self.depth)
194 }
195
to_GOB(self, gob_type: GOBType) -> Extent4D<units::GOBs>196 pub fn to_GOB(self, gob_type: GOBType) -> Extent4D<units::GOBs> {
197 self.div_ceil(gob_type.extent_B())
198 }
199 }
200
201 #[derive(Clone, Debug, Copy, PartialEq)]
202 #[repr(C)]
203 pub struct Offset4D<U> {
204 pub x: u32,
205 pub y: u32,
206 pub z: u32,
207 pub a: u32,
208 phantom: std::marker::PhantomData<U>,
209 }
210
211 impl<U> Offset4D<U> {
new(x: u32, y: u32, z: u32, a: u32) -> Offset4D<U>212 pub const fn new(x: u32, y: u32, z: u32, a: u32) -> Offset4D<U> {
213 Offset4D {
214 x,
215 y,
216 z,
217 a,
218 phantom: std::marker::PhantomData,
219 }
220 }
221
222 #[allow(dead_code)]
is_aligned_to(&self, alignment: Extent4D<U>) -> bool223 pub fn is_aligned_to(&self, alignment: Extent4D<U>) -> bool {
224 (self.x % alignment.width) == 0
225 && (self.y % alignment.height) == 0
226 && (self.z % alignment.depth) == 0
227 && (self.a % alignment.array_len) == 0
228 }
229
div_floor<V>(self, other: Extent4D<U>) -> Offset4D<V>230 fn div_floor<V>(self, other: Extent4D<U>) -> Offset4D<V> {
231 Offset4D {
232 x: self.x / other.width,
233 y: self.y / other.height,
234 z: self.z / other.depth,
235 a: self.a / other.array_len,
236 phantom: std::marker::PhantomData,
237 }
238 }
239
mul<V>(self, other: Extent4D<V>) -> Offset4D<V>240 fn mul<V>(self, other: Extent4D<V>) -> Offset4D<V> {
241 Offset4D {
242 x: self.x * other.width,
243 y: self.y * other.height,
244 z: self.z * other.depth,
245 a: self.a * other.array_len,
246 phantom: std::marker::PhantomData,
247 }
248 }
249
cast_units<V>(self) -> Offset4D<V>250 fn cast_units<V>(self) -> Offset4D<V> {
251 Offset4D {
252 x: self.x,
253 y: self.y,
254 z: self.z,
255 a: self.a,
256 phantom: std::marker::PhantomData,
257 }
258 }
259 }
260
261 impl<U> Add<Extent4D<U>> for Offset4D<U> {
262 type Output = Offset4D<U>;
263
add(self, rhs: Extent4D<U>) -> Offset4D<U>264 fn add(self, rhs: Extent4D<U>) -> Offset4D<U> {
265 Self {
266 x: self.x + rhs.width,
267 y: self.y + rhs.height,
268 z: self.z + rhs.depth,
269 a: self.a + rhs.array_len,
270 phantom: std::marker::PhantomData,
271 }
272 }
273 }
274
275 impl Offset4D<units::Pixels> {
to_el( self, format: Format, sample_layout: SampleLayout, ) -> Offset4D<units::Elements>276 pub fn to_el(
277 self,
278 format: Format,
279 sample_layout: SampleLayout,
280 ) -> Offset4D<units::Elements> {
281 self.mul(sample_layout.px_extent_sa())
282 .div_floor(format.el_extent_sa())
283 }
284
to_B( self, format: Format, sample_layout: SampleLayout, ) -> Offset4D<units::Bytes>285 pub fn to_B(
286 self,
287 format: Format,
288 sample_layout: SampleLayout,
289 ) -> Offset4D<units::Bytes> {
290 self.to_el(format, sample_layout).to_B(format)
291 }
292
to_tl( self, tiling: &Tiling, format: Format, sample_layout: SampleLayout, ) -> Offset4D<units::Tiles>293 pub fn to_tl(
294 self,
295 tiling: &Tiling,
296 format: Format,
297 sample_layout: SampleLayout,
298 ) -> Offset4D<units::Tiles> {
299 self.to_B(format, sample_layout)
300 .div_floor(tiling.extent_B())
301 }
302 }
303
304 #[no_mangle]
nil_offset4d_px_to_el( offset: Offset4D<units::Pixels>, format: Format, sample_layout: SampleLayout, ) -> Offset4D<units::Elements>305 pub extern "C" fn nil_offset4d_px_to_el(
306 offset: Offset4D<units::Pixels>,
307 format: Format,
308 sample_layout: SampleLayout,
309 ) -> Offset4D<units::Elements> {
310 offset.to_el(format, sample_layout)
311 }
312
313 #[no_mangle]
nil_offset4d_px_to_tl( offset: Offset4D<units::Pixels>, tiling: &Tiling, format: Format, sample_layout: SampleLayout, ) -> Offset4D<units::Tiles>314 pub extern "C" fn nil_offset4d_px_to_tl(
315 offset: Offset4D<units::Pixels>,
316 tiling: &Tiling,
317 format: Format,
318 sample_layout: SampleLayout,
319 ) -> Offset4D<units::Tiles> {
320 offset.to_tl(tiling, format, sample_layout)
321 }
322
323 #[no_mangle]
nil_offset4d_px_to_B( offset: Offset4D<units::Pixels>, format: Format, sample_layout: SampleLayout, ) -> Offset4D<units::Bytes>324 pub extern "C" fn nil_offset4d_px_to_B(
325 offset: Offset4D<units::Pixels>,
326 format: Format,
327 sample_layout: SampleLayout,
328 ) -> Offset4D<units::Bytes> {
329 offset.to_B(format, sample_layout)
330 }
331
332 impl Offset4D<units::Elements> {
to_B(self, format: Format) -> Offset4D<units::Bytes>333 pub fn to_B(self, format: Format) -> Offset4D<units::Bytes> {
334 Offset4D {
335 x: self.x * format.el_size_B(),
336 ..self.cast_units()
337 }
338 }
339 }
340
341 impl Minify<u32> for Extent4D<units::Pixels> {
minify(self, level: u32) -> Self342 fn minify(self, level: u32) -> Self {
343 Self {
344 width: self.width.minify(level),
345 height: self.height.minify(level),
346 depth: self.depth.minify(level),
347 ..self
348 }
349 }
350 }
351