1#![no_std]
20#![forbid(unsafe_code)]
21
22#[cfg(jflib_module)]
24extern crate modrt as rt;
25
26extern crate alloc;
27
28pub mod blend;
29pub mod blendmode;
30pub mod dash;
31pub mod flood;
32pub mod gradient;
33pub mod path;
34mod panel;
35pub mod pathdata;
36pub mod raster;
37pub mod shapes;
38pub mod straight;
39pub mod stroke;
40pub mod tmask;
41pub mod xform;
42
43use alloc::vec;
44use alloc::vec::Vec;
45
46pub use rt::fx::div255;
47
48pub const FMT_XRGB: u8 = 0; pub const FMT_PARGB: u8 = 1; pub const FMT_A8: u8 = 2; pub const FMT_ARGB: u8 = 3; pub const SFF_OWNED: u8 = 1;
53pub const SFF_OPAQUE: u8 = 2; pub const FX_ONE: i32 = 256; pub const RULE_NONZERO: u32 = 0;
56pub const RULE_EVENODD: u32 = 1;
57pub const JOIN_MITER: u32 = 0; pub const JOIN_ROUND: u32 = 1;
59pub const JOIN_BEVEL: u32 = 2;
60pub const CAP_BUTT: u32 = 0;
61pub const CAP_ROUND: u32 = 1;
62pub const CAP_SQUARE: u32 = 2;
63pub const ERR_FAIL: i32 = -1;
64pub const ERR_NOMEM: i32 = -8;
65pub const ERR_LIMIT: i32 = -15;
66const SURF_MAX: i32 = 65536; #[derive(Default)]
70pub struct Surface {
71 pub px: Vec<u8>,
72 pub w: i32,
73 pub h: i32,
74 pub stride: i32,
75 pub fmt: u8,
76 pub flags: u8,
77 pub opac: u8, pub blend: u8, }
80
81pub fn try_zeroed(n: usize) -> Option<Vec<u8>> {
85 let mut probe: Vec<u8> = Vec::new();
86 probe.try_reserve_exact(n).ok()?;
87 drop(probe);
88 Some(vec![0u8; n])
89}
90
91impl Surface {
92 pub fn new(w: i32, h: i32, fmt: u8) -> Surface {
96 let Some((stride, bytes)) = Surface::layout(w, h, fmt) else {
97 return Surface { px: Vec::new(), w: 0, h: 0, stride: 0, fmt, flags: 0, opac: 255, blend: 0 };
98 };
99 Surface { px: vec![0u8; bytes], w, h, stride, fmt, flags: SFF_OWNED, opac: 255, blend: 0 }
100 }
101
102 pub fn try_new(w: i32, h: i32, fmt: u8) -> Option<Surface> {
106 let (stride, bytes) = Surface::layout(w, h, fmt)?;
107 Some(Surface { px: try_zeroed(bytes)?, w, h, stride, fmt, flags: SFF_OWNED, opac: 255, blend: 0 })
108 }
109
110 fn layout(w: i32, h: i32, fmt: u8) -> Option<(i32, usize)> {
112 if !(0..=SURF_MAX).contains(&w) || !(0..=SURF_MAX).contains(&h) {
113 return None;
114 }
115 let row = if fmt == FMT_A8 { w } else { w * 4 };
116 let stride = (row + 63) & !63;
117 let bytes = (stride as usize).checked_mul(h as usize).filter(|&n| n <= isize::MAX as usize)?;
118 Some((stride, bytes.max(64)))
119 }
120
121 pub fn new_a8_packed(w: i32, h: i32) -> Surface {
123 Surface { px: vec![0u8; (w * h) as usize], w, h, stride: w, fmt: FMT_A8, flags: SFF_OWNED, opac: 255, blend: 0 }
124 }
125
126 pub fn resize(&mut self, w: i32, h: i32) {
128 *self = Surface::new(w, h, self.fmt);
129 }
130
131 pub fn bpp(&self) -> usize {
133 if self.fmt == FMT_A8 { 1 } else { 4 }
134 }
135}
136
137#[derive(Clone, Copy)]
139struct Saved {
140 w: i32,
141 h: i32,
142 clip: [i32; 4],
143 csp: usize,
144 ox: i32,
145 oy: i32,
146 opac: u32,
147 row0: i32,
148}
149
150pub struct Dc {
153 pub clip: [i32; 4],
154 clip_stack: Vec<[i32; 4]>,
155 targets: Vec<Saved>,
156 pub ox: i32,
157 pub oy: i32,
158 pub w: i32,
159 pub h: i32,
160 pub color: u32,
161 pub opac: u32,
162 pub pcol: [u32; 4], pub pcol32: u32,
164 pub ia: u32, pub row0: i32,
169}
170
171impl Dc {
172 pub fn new(w: i32, h: i32) -> Dc {
174 let mut dc = Dc {
175 clip: [0, 0, w, h],
176 clip_stack: Vec::new(),
177 targets: Vec::new(),
178 ox: 0,
179 oy: 0,
180 w,
181 h,
182 color: 0,
183 opac: 255,
184 pcol: [0; 4],
185 pcol32: 0,
186 ia: 255,
187 row0: 0,
188 };
189 dc.set_color(0xFF00_0000);
190 dc
191 }
192
193 pub fn begin(&mut self, sf: &Surface) {
196 self.begin_rows(sf, 0, sf.h);
197 }
198
199 pub fn begin_rows(&mut self, sf: &Surface, row0: i32, h: i32) {
203 self.targets.push(Saved {
204 w: self.w,
205 h: self.h,
206 clip: self.clip,
207 csp: self.clip_stack.len(),
208 ox: self.ox,
209 oy: self.oy,
210 opac: self.opac,
211 row0: self.row0,
212 });
213 self.w = sf.w;
214 self.h = h;
215 self.ox = 0;
216 self.oy = 0;
217 self.row0 = row0;
218 let y1 = row0.saturating_add(sf.h).min(h);
219 self.clip = [0, row0.clamp(0, y1.max(0)), sf.w, y1.max(0)];
220 }
221
222 pub fn end(&mut self) {
224 let Some(s) = self.targets.pop() else { return };
225 self.w = s.w;
226 self.h = s.h;
227 self.clip = s.clip;
228 self.clip_stack.truncate(s.csp);
229 self.ox = s.ox;
230 self.oy = s.oy;
231 self.row0 = s.row0;
232 self.clamp_clip();
233 self.set_opacity(s.opac);
234 }
235
236 pub fn set_color(&mut self, argb: u32) {
238 self.color = argb;
239 let a = div255((argb >> 24) * self.opac);
240 self.ia = 255 - a;
241 self.pcol[3] = a;
242 let mut packed = a << 24;
243 for ch in 0..3 {
244 let c = (argb >> (ch * 8)) & 255;
245 let p = div255(c * a);
246 self.pcol[ch] = p;
247 packed |= p << (ch * 8);
248 }
249 self.pcol32 = packed;
250 }
251
252 pub fn get_color(&self) -> u32 {
253 self.color
254 }
255
256 pub fn set_opacity(&mut self, op: u32) {
258 self.opac = op & 255;
259 let c = self.color;
260 self.set_color(c);
261 }
262
263 pub fn origin(&mut self, x: i32, y: i32) {
264 self.ox = x;
265 self.oy = y;
266 }
267
268 pub fn translate(&mut self, dx: i32, dy: i32) {
269 self.ox = self.ox.wrapping_add(dx);
270 self.oy = self.oy.wrapping_add(dy);
271 }
272
273 pub fn clip_push(&mut self, x: i32, y: i32, w: i32, h: i32) {
275 self.clip_stack.push(self.clip);
276 let x = x.wrapping_add(self.ox);
277 let y = y.wrapping_add(self.oy);
278 let x0 = x.max(self.clip[0]);
279 let y0 = y.max(self.clip[1]);
280 let x1 = x.wrapping_add(w).min(self.clip[2]).max(x0); let y1 = y.wrapping_add(h).min(self.clip[3]).max(y0);
282 self.clip = [x0, y0, x1, y1];
283 }
284
285 pub fn clip_pop(&mut self) {
286 if let Some(c) = self.clip_stack.pop() {
287 self.clip = c;
288 self.clamp_clip();
289 }
290 }
291
292 fn clamp_clip(&mut self) {
294 let x0 = self.clip[0].max(0).min(self.w);
295 let x1 = self.clip[2].min(self.w).max(x0);
296 let y0 = self.clip[1].max(0).min(self.h);
297 let y1 = self.clip[3].min(self.h).max(y0);
298 self.clip = [x0, y0, x1, y1];
299 }
300
301 pub fn clip_rect(&self, x: i32, y: i32, w: i32, h: i32) -> Option<[i32; 4]> {
303 let x = x.wrapping_add(self.ox);
304 let y = y.wrapping_add(self.oy);
305 let x0 = x.max(self.clip[0]);
306 let y0 = y.max(self.clip[1]);
307 let x1 = x.wrapping_add(w).min(self.clip[2]);
308 let y1 = y.wrapping_add(h).min(self.clip[3]);
309 if x1 <= x0 || y1 <= y0 { None } else { Some([x0, y0, x1, y1]) }
310 }
311
312 pub fn fill(&self, sf: &mut Surface, x: i32, y: i32, w: i32, h: i32) {
314 let Some([x0, y0, x1, y1]) = self.clip_rect(x, y, w, h) else { return };
315 let stride = sf.stride as usize;
316 if sf.fmt == FMT_A8 {
317 let a = (self.pcol32 >> 24) as u8;
318 for yy in y0..y1 {
319 let o = (yy - self.row0) as usize * stride;
320 sf.px[o + x0 as usize..o + x1 as usize].fill(a);
321 }
322 return;
323 }
324 if self.ia != 0 && self.pcol32 == 0 {
325 return; }
327 for yy in y0..y1 {
328 let o = (yy - self.row0) as usize * stride + x0 as usize * 4;
329 let row = &mut sf.px[o..o + (x1 - x0) as usize * 4];
330 if self.ia == 0 {
331 blend::fill_solid(row, self.pcol32);
332 } else {
333 blend::fill_over(row, &self.pcol, self.ia);
334 }
335 }
336 }
337
338 pub fn clear(&self, sf: &mut Surface, argb: u32) {
340 let [x0, y0, x1, y1] = self.clip;
341 if x1 <= x0 || y1 <= y0 {
342 return;
343 }
344 let a = argb >> 24;
345 let mut c = argb;
346 if a != 255 {
347 c = argb & 0xFF00_0000;
348 for ch in 0..3 {
349 c |= div255(((argb >> (ch * 8)) & 255) * a) << (ch * 8);
350 }
351 }
352 let stride = sf.stride as usize;
353 for yy in y0..y1 {
354 let o = (yy - self.row0) as usize * stride;
355 if sf.fmt == FMT_A8 {
356 sf.px[o + x0 as usize..o + x1 as usize].fill((c >> 24) as u8);
357 } else {
358 blend::fill_solid(&mut sf.px[o + x0 as usize * 4..o + x1 as usize * 4], c);
359 }
360 }
361 }
362
363 pub fn hline(&self, sf: &mut Surface, x: i32, y: i32, w: i32) {
364 self.fill(sf, x, y, w, 1);
365 }
366
367 pub fn vline(&self, sf: &mut Surface, x: i32, y: i32, h: i32) {
368 self.fill(sf, x, y, 1, h);
369 }
370
371 pub fn frame(&self, sf: &mut Surface, x: i32, y: i32, w: i32, h: i32) {
373 if w <= 0 || h <= 0 {
374 return;
375 }
376 self.hline(sf, x, y, w);
377 if h == 1 {
378 return;
379 }
380 self.hline(sf, x, y + h - 1, w);
381 if h == 2 {
382 return;
383 }
384 self.vline(sf, x, y + 1, h - 2);
385 if w == 1 {
386 return;
387 }
388 self.vline(sf, x + w - 1, y + 1, h - 2);
389 }
390
391 pub fn blit(&self, sf: &mut Surface, src: &Surface, x: i32, y: i32) {
393 if sf.fmt == FMT_A8 || src.fmt == FMT_A8 {
394 return;
395 }
396 let Some([x0, y0, x1, y1]) = self.clip_rect(x, y, src.w, src.h) else { return };
397 let sx = (x0 - x.wrapping_add(self.ox)) as usize;
398 let sy = (y0 - y.wrapping_add(self.oy)) as usize;
399 let n = (x1 - x0) as usize;
400 for r in 0..(y1 - y0) as usize {
401 let d = ((y0 - self.row0) as usize + r) * sf.stride as usize + x0 as usize * 4;
402 let s = (sy + r) * src.stride as usize + sx * 4;
403 let dst = &mut sf.px[d..d + n * 4];
404 let srow = &src.px[s..s + n * 4];
405 if src.fmt == FMT_XRGB {
406 dst.copy_from_slice(srow);
407 } else {
408 blend::blit_over(dst, srow, self.opac);
409 }
410 }
411 }
412
413 pub fn blit_scaled(&self, sf: &mut Surface, src: &Surface, x: i32, y: i32, w: i32, h: i32) {
415 if w == src.w && h == src.h {
416 self.blit(sf, src, x, y);
417 return;
418 }
419 if w <= 0 || h <= 0 || src.w <= 0 || src.h <= 0 || sf.fmt == FMT_A8 || src.fmt == FMT_A8 {
420 return;
421 }
422 let xa = x.wrapping_add(self.ox);
423 let ya = y.wrapping_add(self.oy);
424 let Some([x0, y0, x1, y1]) = self.clip_rect(x, y, w, h) else { return };
425 let vw = (x1 - x0) as usize;
426 let xstep = ((src.w as u64) << 16) / w as u64;
427 let ystep = ((src.h as u64) << 16) / h as u64;
428 let mut row = vec![0u8; vw * 4];
429 for r in 0..(y1 - y0) {
430 let sy = (((y0 + r - ya) as u32 as u64 * ystep + (ystep >> 1)) >> 16) as usize;
431 let srow = sy * src.stride as usize;
432 let mut fx = (x0 - xa) as u32 as u64 * xstep + (xstep >> 1);
433 for i in 0..vw {
434 let s = srow + (fx >> 16) as usize * 4;
435 row[i * 4..i * 4 + 4].copy_from_slice(&src.px[s..s + 4]);
436 fx += xstep;
437 }
438 let d = (y0 + r - self.row0) as usize * sf.stride as usize + x0 as usize * 4;
439 let dst = &mut sf.px[d..d + vw * 4];
440 if src.fmt == FMT_XRGB {
441 dst.copy_from_slice(&row);
442 } else {
443 blend::blit_over(dst, &row, self.opac);
444 }
445 }
446 }
447}