]>
Commit | Line | Data |
---|---|---|
5641dccf | 1 | use nihav_core::frame::{FrameType, NAVideoBuffer}; |
b4d5b851 KS |
2 | use nihav_codec_support::codecs::MV; |
3 | use nihav_codec_support::codecs::blockdsp::edge_emu; | |
47527732 KS |
4 | use super::rv3040::{RV34DSP, RV34MBInfo}; |
5 | ||
6 | fn clip8(a: i16) -> u8 { | |
7 | if a < 0 { 0 } | |
8 | else if a > 255 { 255 } | |
9 | else { a as u8 } | |
10 | } | |
11 | ||
12 | fn rv3_filter_h(dst: &mut [u8], mut didx: usize, dstride: usize, src: &[u8], mut sidx: usize, sstride: usize, bsize: usize, c1: i16, c2: i16) { | |
13 | for _ in 0..bsize { | |
14 | for x in 0..bsize { | |
15 | dst[didx + x] = clip8((-((src[sidx + x - 1] as i16) + (src[sidx + x + 2] as i16)) + (src[sidx + x + 0] as i16) * c1 + (src[sidx + x + 1] as i16) * c2 + 8) >> 4); | |
16 | } | |
17 | sidx += sstride; | |
18 | didx += dstride; | |
19 | } | |
20 | } | |
21 | ||
22 | fn rv3_filter_v(dst: &mut [u8], mut didx: usize, dstride: usize, src: &[u8], mut sidx: usize, sstride: usize, bsize: usize, c1: i16, c2: i16) { | |
23 | for _ in 0..bsize { | |
24 | for x in 0..bsize { | |
25 | dst[didx + x] = clip8((-((src[sidx + x - 1 * sstride] as i16) + (src[sidx + x + 2 * sstride] as i16)) + (src[sidx + x + 0 * sstride] as i16) * c1 + (src[sidx + x + 1 * sstride] as i16) * c2 + 8) >> 4); | |
26 | } | |
27 | sidx += sstride; | |
28 | didx += dstride; | |
29 | } | |
30 | } | |
31 | ||
32 | macro_rules! mc_matrix { | |
33 | ($s: ident, $o: expr, $c1: expr) => ( | |
34 | ($c1 * 6) * ($s[$o] as i32) + ($c1 * 9) * ($s[$o + 1] as i32) + ($c1) * ($s[$o + 2] as i32) | |
35 | ); | |
36 | ($s: ident, $o: expr, $c1: expr, $d1: expr, $d2: expr) => ( | |
e07387c7 | 37 | -($c1) * ($s[$o - 1] as i32) + ($c1 * $d1) * ($s[$o] as i32) + ($c1 * $d2) * ($s[$o + 1] as i32) + -($c1) * ($s[$o + 2] as i32) |
47527732 KS |
38 | ); |
39 | ($s: ident, $o: expr, $ss: expr, $c1: expr, $c2: expr, $d1: expr, $d2: expr) => ( | |
40 | ((mc_matrix!($s, $o - $ss, -1, $d1, $d2) + | |
41 | mc_matrix!($s, $o , $c1, $d1, $d2) + | |
42 | mc_matrix!($s, $o + $ss, $c2, $d1, $d2) + | |
43 | mc_matrix!($s, $o + 2 * $ss, -1, $d1, $d2) + 128) >> 8) as i16 | |
44 | ); | |
45 | (m22; $s: ident, $o: expr, $ss: expr) => ( | |
46 | ((mc_matrix!($s, $o + 0 * $ss, 6) + | |
47 | mc_matrix!($s, $o + 1 * $ss, 9) + | |
48 | mc_matrix!($s, $o + 2 * $ss, 1) + 128) >> 8) as i16 | |
49 | ); | |
50 | } | |
51 | ||
52 | macro_rules! mc_func { | |
53 | (copy; $name: ident, $size: expr) => ( | |
54 | fn $name (dst: &mut [u8], mut didx: usize, dstride: usize, src: &[u8], mut sidx: usize, sstride: usize) { | |
55 | for _ in 0..$size { | |
56 | let d = &mut dst[didx..][..$size]; | |
57 | let s = &src[sidx..][..$size]; | |
58 | for x in 0..$size { d[x] = s[x]; } | |
59 | didx += dstride; | |
60 | sidx += sstride; | |
61 | } | |
62 | } | |
63 | ); | |
64 | (hor; $name: ident, $c1: expr, $c2: expr, $size: expr) => ( | |
65 | fn $name (dst: &mut [u8], didx: usize, dstride: usize, src: &[u8], sidx: usize, sstride: usize) { | |
66 | rv3_filter_h(dst, didx, dstride, src, sidx, sstride, $size, $c1, $c2); | |
67 | } | |
68 | ); | |
69 | (ver; $name: ident, $c1: expr, $c2: expr, $size: expr) => ( | |
70 | fn $name (dst: &mut [u8], didx: usize, dstride: usize, src: &[u8], sidx: usize, sstride: usize) { | |
71 | rv3_filter_v(dst, didx, dstride, src, sidx, sstride, $size, $c1, $c2); | |
72 | } | |
73 | ); | |
74 | (m11; $name: ident, $size: expr) => ( | |
75 | fn $name (dst: &mut [u8], mut didx: usize, dstride: usize, src: &[u8], mut sidx: usize, sstride: usize) { | |
76 | for _ in 0..$size { | |
77 | for x in 0..$size { | |
78 | dst[didx + x] = clip8(mc_matrix!(src, sidx + x, sstride, 12, 6, 12, 6)); | |
79 | } | |
80 | didx += dstride; | |
81 | sidx += sstride; | |
82 | } | |
83 | } | |
84 | ); | |
85 | (m12; $name: ident, $size: expr) => ( | |
86 | fn $name (dst: &mut [u8], mut didx: usize, dstride: usize, src: &[u8], mut sidx: usize, sstride: usize) { | |
87 | for _ in 0..$size { | |
88 | for x in 0..$size { | |
89 | dst[didx + x] = clip8(mc_matrix!(src, sidx + x, sstride, 6, 12, 12, 6)); | |
90 | } | |
91 | didx += dstride; | |
92 | sidx += sstride; | |
93 | } | |
94 | } | |
95 | ); | |
96 | (m21; $name: ident, $size: expr) => ( | |
97 | fn $name (dst: &mut [u8], mut didx: usize, dstride: usize, src: &[u8], mut sidx: usize, sstride: usize) { | |
98 | for _ in 0..$size { | |
99 | for x in 0..$size { | |
100 | dst[didx + x] = clip8(mc_matrix!(src, sidx + x, sstride, 12, 6, 6, 12)); | |
101 | } | |
102 | didx += dstride; | |
103 | sidx += sstride; | |
104 | } | |
105 | } | |
106 | ); | |
107 | (m22; $name: ident, $size: expr) => ( | |
108 | fn $name (dst: &mut [u8], mut didx: usize, dstride: usize, src: &[u8], mut sidx: usize, sstride: usize) { | |
109 | for _ in 0..$size { | |
110 | for x in 0..$size { | |
111 | dst[didx + x] = clip8(mc_matrix!(m22; src, sidx + x, sstride)); | |
112 | } | |
113 | didx += dstride; | |
114 | sidx += sstride; | |
115 | } | |
116 | } | |
117 | ); | |
118 | } | |
119 | mc_func!(copy; copy_16, 16); | |
120 | mc_func!(copy; copy_8, 8); | |
121 | mc_func!(hor; luma_mc_10_16, 12, 6, 16); | |
122 | mc_func!(hor; luma_mc_20_16, 6, 12, 16); | |
123 | mc_func!(hor; luma_mc_10_8, 12, 6, 8); | |
124 | mc_func!(hor; luma_mc_20_8, 6, 12, 8); | |
125 | mc_func!(ver; luma_mc_01_16, 12, 6, 16); | |
126 | mc_func!(ver; luma_mc_02_16, 6, 12, 16); | |
127 | mc_func!(ver; luma_mc_01_8, 12, 6, 8); | |
128 | mc_func!(ver; luma_mc_02_8, 6, 12, 8); | |
129 | mc_func!(m11; luma_mc_11_16, 16); | |
130 | mc_func!(m11; luma_mc_11_8, 8); | |
131 | mc_func!(m21; luma_mc_21_16, 16); | |
132 | mc_func!(m21; luma_mc_21_8, 8); | |
133 | mc_func!(m12; luma_mc_12_16, 16); | |
134 | mc_func!(m12; luma_mc_12_8, 8); | |
135 | mc_func!(m22; luma_mc_22_16, 16); | |
136 | mc_func!(m22; luma_mc_22_8, 8); | |
137 | ||
138 | const RV30_CHROMA_FRAC1: [u16; 3] = [ 8, 5, 3 ]; | |
139 | const RV30_CHROMA_FRAC2: [u16; 3] = [ 0, 3, 5 ]; | |
140 | fn rv30_chroma_mc(dst: &mut [u8], mut didx: usize, dstride: usize, src: &[u8], mut sidx: usize, sstride: usize, size: usize, x: usize, y: usize) { | |
141 | if (x == 0) && (y == 0) { | |
142 | for _ in 0..size { | |
fa57381e | 143 | dst[didx..][..size].copy_from_slice(&src[sidx..][..size]); |
47527732 KS |
144 | didx += dstride; |
145 | sidx += sstride; | |
146 | } | |
147 | return; | |
148 | } | |
149 | let a = RV30_CHROMA_FRAC1[x] * RV30_CHROMA_FRAC1[y]; | |
150 | let b = RV30_CHROMA_FRAC2[x] * RV30_CHROMA_FRAC1[y]; | |
151 | let c = RV30_CHROMA_FRAC1[x] * RV30_CHROMA_FRAC2[y]; | |
152 | let d = RV30_CHROMA_FRAC2[x] * RV30_CHROMA_FRAC2[y]; | |
153 | for _ in 0..size { | |
154 | for x in 0..size { | |
155 | dst[didx + x] = ((a * (src[sidx + x] as u16) | |
156 | + b * (src[sidx + x + 1] as u16) | |
157 | + c * (src[sidx + x + sstride] as u16) | |
158 | + d * (src[sidx + x + 1 + sstride] as u16) + 32) >> 6) as u8; | |
159 | } | |
160 | didx += dstride; | |
161 | sidx += sstride; | |
162 | } | |
163 | } | |
164 | ||
fa57381e | 165 | #[allow(clippy::type_complexity)] |
47527732 KS |
166 | pub struct RV30DSP { |
167 | luma_mc: [[fn (&mut [u8], usize, usize, &[u8], usize, usize); 9]; 2], | |
168 | } | |
169 | ||
170 | impl RV30DSP { | |
171 | pub fn new() -> Self { | |
172 | RV30DSP { | |
173 | luma_mc: [ | |
174 | [ copy_16, luma_mc_10_16, luma_mc_20_16, | |
175 | luma_mc_01_16, luma_mc_11_16, luma_mc_21_16, | |
176 | luma_mc_02_16, luma_mc_12_16, luma_mc_22_16 ], | |
177 | [ copy_8, luma_mc_10_8, luma_mc_20_8, | |
178 | luma_mc_01_8, luma_mc_11_8, luma_mc_21_8, | |
179 | luma_mc_02_8, luma_mc_12_8, luma_mc_22_8 ] ], | |
180 | } | |
181 | } | |
182 | } | |
183 | ||
184 | macro_rules! el { | |
185 | ($src: ident, $o: expr) => ($src[$o] as i16); | |
186 | } | |
187 | ||
188 | fn clip_symm(a: i16, lim: i16) -> i16 { | |
189 | if a < -lim { | |
190 | -lim | |
191 | } else if a > lim { | |
192 | lim | |
193 | } else { | |
194 | a | |
195 | } | |
196 | } | |
197 | ||
198 | const RV30_LOOP_FILTER_STRENGTH: [i16; 32] = [ | |
199 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, | |
200 | 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5 | |
201 | ]; | |
202 | ||
203 | macro_rules! test_bit { | |
204 | ($pat: expr, $x: expr) => ( (($pat >> $x) & 1) != 0 ) | |
205 | } | |
206 | ||
207 | fn rv30_loop_filter4(pix: &mut [u8], mut off: usize, step: usize, stride: usize, lim: i16) { | |
208 | for _ in 0..4 { | |
209 | let a = el!(pix, off - 2*step); | |
210 | let b = el!(pix, off - step); | |
211 | let c = el!(pix, off); | |
212 | let d = el!(pix, off + step); | |
213 | let diff0 = ((a - d) - (b - c) * 4) >> 3; | |
214 | let diff = clip_symm(diff0, lim); | |
215 | pix[off - step] = clip8(b + diff); | |
216 | pix[off ] = clip8(c - diff); | |
217 | off += stride; | |
218 | } | |
219 | } | |
220 | ||
221 | fn rv30_div_mv(mv: i16) -> (i16, usize) { | |
222 | let i = mv / 3; | |
223 | let f = mv - i * 3; | |
224 | if f < 0 { | |
225 | (i - 1, (f + 3) as usize) | |
226 | } else { | |
227 | (i, f as usize) | |
228 | } | |
229 | } | |
230 | ||
231 | fn check_pos(x: usize, y: usize, size: usize, w: usize, h: usize, dx: i16, dy: i16, e0: isize, e1: isize, e2: isize, e3: isize) -> bool { | |
232 | let xn = (x as isize) + (dx as isize); | |
233 | let yn = (y as isize) + (dy as isize); | |
234 | ||
235 | (xn - e0 >= 0) && (xn + (size as isize) + e1 <= (w as isize)) && (yn - e2 >= 0) && (yn + (size as isize) + e3 <= (h as isize)) | |
236 | } | |
237 | ||
238 | const RV30_EDGE1: [isize; 3] = [ 0, 1, 1 ]; | |
239 | const RV30_EDGE2: [isize; 3] = [ 0, 2, 2 ]; | |
240 | ||
241 | impl RV34DSP for RV30DSP { | |
b7c882c1 | 242 | #[allow(clippy::cognitive_complexity)] |
16cbd8c0 | 243 | fn loop_filter(&self, frame: &mut NAVideoBuffer<u8>, _ftype: FrameType, mbinfo: &[RV34MBInfo], mb_w: usize, _mb_h: usize, row: usize) { |
47527732 KS |
244 | let mut offs: [usize; 3] = [0; 3]; |
245 | let mut stride: [usize; 3] = [0; 3]; | |
246 | ||
247 | for comp in 0..3 { | |
248 | stride[comp] = frame.get_stride(comp); | |
249 | let start = if comp == 0 { row * 16 } else { row * 8 }; | |
250 | offs[comp] = frame.get_offset(comp) + start * stride[comp]; | |
251 | } | |
252 | ||
1a967e6b | 253 | let data = frame.get_data_mut().unwrap(); |
47527732 KS |
254 | let dst: &mut [u8] = data.as_mut_slice(); |
255 | ||
256 | // vertical filter | |
257 | let mut left_cbp = 0; | |
258 | let mut left_lim = 0; | |
259 | let mut left_dbk = 0; | |
260 | let mut mb_pos: usize = row * mb_w; | |
261 | for mb_x in 0..mb_w { | |
262 | let cur_lim = RV30_LOOP_FILTER_STRENGTH[mbinfo[mb_pos].q as usize]; | |
263 | let cur_dbk = mbinfo[mb_pos].deblock; | |
264 | let cur_cbp = mbinfo[mb_pos].cbp_c; | |
265 | let xstart = if mb_x == 0 { 1 } else { 0 }; | |
266 | for y in 0..4 { | |
267 | let yoff = offs[0] + mb_x * 16 + y * 4 * stride[0]; | |
268 | for x in xstart..4 { | |
269 | let cs = x + y*4; | |
270 | let loc_lim; | |
271 | ||
272 | if test_bit!(cur_dbk, cs) { | |
273 | loc_lim = cur_lim; | |
274 | } else if (x == 0) && test_bit!(left_dbk, cs + 3) { | |
275 | loc_lim = left_lim; | |
276 | } else if (x != 0) && test_bit!(cur_dbk, cs - 1) { | |
277 | loc_lim = cur_lim; | |
278 | } else { | |
279 | loc_lim = 0; | |
280 | } | |
281 | if loc_lim != 0 { | |
282 | rv30_loop_filter4(dst, yoff + x * 4, 1, stride[0], loc_lim); | |
283 | } | |
284 | } | |
285 | } | |
286 | ||
287 | for comp in 1..3 { | |
288 | for y in 0..2 { | |
289 | let coff = offs[comp] + mb_x * 8 + y * 4 * stride[comp]; | |
290 | for x in xstart..2 { | |
291 | let cs = x + y * 2 + (comp - 1) * 4; | |
292 | let loc_lim; | |
293 | ||
294 | if test_bit!(cur_cbp, cs) { | |
295 | loc_lim = cur_lim; | |
296 | } else if (x == 0) && test_bit!(left_cbp, cs + 1) { | |
297 | loc_lim = left_lim; | |
298 | } else if (x != 0) && test_bit!(cur_cbp, cs - 1) { | |
299 | loc_lim = cur_lim; | |
300 | } else { | |
301 | loc_lim = 0; | |
302 | } | |
303 | if loc_lim != 0 { | |
304 | rv30_loop_filter4(dst, coff + x * 4, 1, stride[comp], loc_lim); | |
305 | } | |
306 | } | |
307 | } | |
308 | } | |
309 | ||
310 | left_lim = cur_lim; | |
311 | left_dbk = cur_dbk; | |
312 | left_cbp = cur_cbp; | |
313 | mb_pos += 1; | |
314 | } | |
315 | ||
316 | // horizontal filter | |
317 | let mut mb_pos: usize = row * mb_w; | |
318 | for mb_x in 0..mb_w { | |
319 | let cur_lim = RV30_LOOP_FILTER_STRENGTH[mbinfo[mb_pos].q as usize]; | |
320 | let cur_dbk = mbinfo[mb_pos].deblock; | |
321 | let cur_cbp = mbinfo[mb_pos].cbp_c; | |
322 | let ystart = if row == 0 { 1 } else { 0 }; | |
323 | let top_lim; | |
324 | let top_dbk; | |
325 | let top_cbp; | |
326 | if row > 0 { | |
327 | top_lim = RV30_LOOP_FILTER_STRENGTH[mbinfo[mb_pos - mb_w].q as usize]; | |
328 | top_dbk = mbinfo[mb_pos - mb_w].deblock; | |
329 | top_cbp = mbinfo[mb_pos - mb_w].cbp_c; | |
330 | } else { | |
331 | top_lim = 0; | |
332 | top_dbk = 0; | |
333 | top_cbp = 0; | |
334 | } | |
335 | for y in ystart..4 { | |
336 | let yoff = offs[0] + mb_x * 16 + y * 4 * stride[0]; | |
337 | for x in 0..4 { | |
338 | let cs = x + y*4; | |
339 | let loc_lim; | |
340 | ||
341 | if test_bit!(cur_dbk, cs) { | |
342 | loc_lim = cur_lim; | |
343 | } else if (y == 0) && test_bit!(top_dbk, cs + 12) { | |
344 | loc_lim = top_lim; | |
345 | } else if (y != 0) && test_bit!(cur_dbk, cs - 4) { | |
346 | loc_lim = cur_lim; | |
347 | } else { | |
348 | loc_lim = 0; | |
349 | } | |
350 | if loc_lim != 0 { | |
351 | rv30_loop_filter4(dst, yoff + x * 4, stride[0], 1, loc_lim); | |
352 | } | |
353 | } | |
354 | } | |
355 | ||
356 | for comp in 1..3 { | |
357 | for y in ystart..2 { | |
358 | let coff = offs[comp] + mb_x * 8 + y * 4 * stride[comp]; | |
359 | for x in 0..2 { | |
360 | let cs = x + y * 2 + (comp - 1) * 4; | |
361 | let loc_lim; | |
362 | ||
363 | if test_bit!(cur_cbp, cs) { | |
364 | loc_lim = cur_lim; | |
365 | } else if (y == 0) && test_bit!(top_cbp, cs + 2) { | |
366 | loc_lim = top_lim; | |
367 | } else if (y != 0) && test_bit!(cur_cbp, cs - 2) { | |
368 | loc_lim = cur_lim; | |
369 | } else { | |
370 | loc_lim = 0; | |
371 | } | |
372 | if loc_lim != 0 { | |
373 | rv30_loop_filter4(dst, coff + x * 4, stride[comp], 1, loc_lim); | |
374 | } | |
375 | } | |
376 | } | |
377 | } | |
378 | ||
379 | mb_pos += 1; | |
380 | } | |
381 | } | |
382 | fn do_luma_mc(&self, frame: &mut NAVideoBuffer<u8>, prev_frame: &NAVideoBuffer<u8>, x: usize, y: usize, mv: MV, use16: bool, avg: bool) { | |
383 | let size: usize = if use16 { 16 } else { 8 }; | |
384 | let dstride = frame.get_stride(0); | |
385 | let doffset = frame.get_offset(0) + (if !avg { x + y * dstride } else { 0 }); | |
1a967e6b | 386 | let data = frame.get_data_mut().unwrap(); |
47527732 KS |
387 | let dst: &mut [u8] = data.as_mut_slice(); |
388 | ||
389 | let (w_, h_) = prev_frame.get_dimensions(0); | |
390 | let w = (w_ + 15) & !15; | |
391 | let h = (h_ + 15) & !15; | |
392 | ||
393 | let (dx, cx) = rv30_div_mv(mv.x); | |
394 | let (dy, cy) = rv30_div_mv(mv.y); | |
395 | let mode = cx + cy * 3; | |
396 | ||
397 | if check_pos(x, y, size, w, h, dx, dy, RV30_EDGE1[cx], RV30_EDGE2[cx], RV30_EDGE1[cy], RV30_EDGE2[cy]) { | |
398 | let sstride = prev_frame.get_stride(0); | |
399 | let mut soffset = prev_frame.get_offset(0) + x + y * sstride; | |
400 | let data = prev_frame.get_data(); | |
401 | let src: &[u8] = data.as_slice(); | |
402 | soffset = ((soffset as isize) + (dx as isize) + (dy as isize) * (sstride as isize)) as usize; | |
403 | self.luma_mc[if use16 { 0 } else { 1 }][mode](dst, doffset, dstride, src, soffset, sstride); | |
404 | } else { | |
405 | let mut ebuf: [u8; 32*20] = [0; 32*20]; | |
86081fed | 406 | edge_emu(prev_frame, (x as isize) + (dx as isize) - 1, (y as isize) + (dy as isize) - 1, 16+3, 16+3, &mut ebuf, 32, 0, 4); |
47527732 KS |
407 | self.luma_mc[if use16 { 0 } else { 1 }][mode](dst, doffset, dstride, &ebuf, 32 + 1, 32); |
408 | } | |
409 | } | |
410 | fn do_chroma_mc(&self, frame: &mut NAVideoBuffer<u8>, prev_frame: &NAVideoBuffer<u8>, x: usize, y: usize, comp: usize, mv: MV, use8: bool, avg: bool) { | |
411 | let size: usize = if use8 { 8 } else { 4 }; | |
412 | let dstride = frame.get_stride(comp); | |
413 | let doffset = frame.get_offset(comp) + (if !avg { x + y * dstride } else { 0 }); | |
1a967e6b | 414 | let data = frame.get_data_mut().unwrap(); |
47527732 KS |
415 | let dst: &mut [u8] = data.as_mut_slice(); |
416 | ||
417 | let (w_, h_) = prev_frame.get_dimensions(comp); | |
418 | let w = (w_ + 7) & !7; | |
419 | let h = (h_ + 7) & !7; | |
420 | ||
421 | let (dx, cx) = rv30_div_mv(mv.x / 2); | |
422 | let (dy, cy) = rv30_div_mv(mv.y / 2); | |
423 | ||
424 | if check_pos(x, y, size, w, h, dx, dy, 0, 1, 0, 1) { | |
425 | let sstride = prev_frame.get_stride(comp); | |
426 | let mut soffset = prev_frame.get_offset(comp) + x + y * sstride; | |
427 | let data = prev_frame.get_data(); | |
428 | let src: &[u8] = data.as_slice(); | |
429 | soffset = ((soffset as isize) + (dx as isize) + (dy as isize) * (sstride as isize)) as usize; | |
430 | rv30_chroma_mc(dst, doffset, dstride, src, soffset, sstride, size, cx, cy); | |
431 | } else { | |
432 | let mut ebuf: [u8; 16*10] = [0; 16*10]; | |
86081fed | 433 | edge_emu(prev_frame, (x as isize) + (dx as isize), (y as isize) + (dy as isize), 8+1, 8+1, &mut ebuf, 16, comp, 4); |
47527732 KS |
434 | rv30_chroma_mc(dst, doffset, dstride, &ebuf, 0, 16, size, cx, cy); |
435 | } | |
436 | } | |
437 | } |