1use crate::gpu::{drawable_image, needs_mipmaps, ISkiaGrContext};
2use crate::helpers::image_saving_helper;
3use crate::i_drawable_bitmap_impl::IDrawableBitmapImpl;
4use crate::locked_framebuffer::LockedFramebuffer;
5use crate::skia_sharp_extensions::{
6 to_alpha_format, to_pixel_format_opt, to_sk_alpha_type, to_sk_color_type_or_panic, to_sk_sampling_options_scaled,
7};
8use ferroui_base::media::imaging::{BitmapEncoderOptions, BitmapInterpolationMode};
9use ferroui_base::platform::{AlphaFormat, IBitmapImpl, ILockedFramebuffer, IReadableBitmapImpl, PixelFormat};
10use ferroui_base::{PixelSize, Vector};
11use skia_safe::canvas::SrcRectConstraint;
12use skia_safe::{
13 AlphaType, Bitmap, Canvas, Codec, ColorType, Data, Image, ImageInfo, Paint, Rect, SamplingOptions,
14};
15use std::any::Any;
16use std::io::{self, Read, Write};
17use std::rc::Rc;
18use std::sync::Mutex;
19
20pub(crate) fn load_error() -> io::Error {
21 io::Error::new(io::ErrorKind::InvalidData, "Unable to load bitmap from provided data")
22}
23
24fn read_stream(stream: &mut dyn Read) -> io::Result<Data> {
25 let mut bytes = Vec::new();
26 stream.read_to_end(&mut bytes)?;
27 Ok(Data::new_copy(&bytes))
28}
29
30fn decode_with_info(codec: &mut Codec, info: &ImageInfo) -> Option<Bitmap> {
32 let mut bitmap = Bitmap::new();
33 if !bitmap.try_alloc_pixels_info(info, None) {
34 return None;
35 }
36
37 let row_bytes = bitmap.row_bytes();
38 let mut pixmap = bitmap.peek_pixels()?;
39 let pixels = pixmap.bytes_mut()?;
40
41 match codec.get_pixels_with_options(info, pixels, row_bytes, None) {
42 skia_safe::codec::Result::Success | skia_safe::codec::Result::IncompleteInput => Some(bitmap),
43 _ => None,
44 }
45}
46
47pub(crate) fn decode_bitmap(stream: &mut dyn Read) -> io::Result<Bitmap> {
49 let data = read_stream(stream)?;
50 let mut codec = Codec::from_data(data).ok_or_else(load_error)?;
51
52 let mut info = codec.info();
53 if info.alpha_type() == AlphaType::Unpremul {
54 info = info.with_alpha_type(AlphaType::Premul);
55 }
56 let info = info.with_color_space(None);
57
58 decode_with_info(&mut codec, &info).ok_or_else(load_error)
59}
60
61pub(crate) fn decode_bitmap_to_size(
64 stream: &mut dyn Read,
65 decode_size: i32,
66 horizontal: bool,
67 interpolation_mode: BitmapInterpolationMode,
68) -> io::Result<Bitmap> {
69 let data = read_stream(stream)?;
70 let mut codec = Codec::from_data(data).ok_or_else(load_error)?;
71 let info = codec.info();
72
73 let supported_scale = codec.get_scaled_dimensions(if horizontal {
75 decode_size as f32 / info.width() as f32
76 } else {
77 decode_size as f32 / info.height() as f32
78 });
79
80 let nearest = ImageInfo::new_n32_premul((supported_scale.width, supported_scale.height), None);
82 let bitmap = decode_with_info(&mut codec, &nearest).ok_or_else(load_error)?;
83
84 let real_scale = if horizontal {
86 info.height() as f64 / info.width() as f64
87 } else {
88 info.width() as f64 / info.height() as f64
89 };
90
91 let desired = if horizontal {
92 ImageInfo::new_n32_premul((decode_size, (real_scale * decode_size as f64) as i32), None)
93 } else {
94 ImageInfo::new_n32_premul(((real_scale * decode_size as f64) as i32, decode_size), None)
95 };
96
97 if bitmap.width() != desired.width() || bitmap.height() != desired.height() {
98 let is_upscaling = desired.width() > bitmap.width() || desired.height() > bitmap.height();
99
100 let mut scaled = Bitmap::new();
101 if !scaled.try_alloc_pixels_info(&desired, None) {
102 return Err(load_error());
103 }
104
105 let mut dst = scaled.peek_pixels().ok_or_else(load_error)?;
106 let src = bitmap.peek_pixels().ok_or_else(load_error)?;
107 if !src.scale_pixels(&mut dst, to_sk_sampling_options_scaled(interpolation_mode, is_upscaling)) {
108 return Err(load_error());
109 }
110
111 return Ok(scaled);
112 }
113
114 Ok(bitmap)
115}
116
117pub(crate) struct SendBitmap(pub Bitmap);
119
120unsafe impl Send for SendBitmap {}
124
125impl std::ops::Deref for SendBitmap {
126 type Target = Bitmap;
127
128 fn deref(&self) -> &Bitmap {
129 &self.0
130 }
131}
132
133pub struct ImmutableBitmap {
135 image: Mutex<Option<Image>>,
136 bitmap: Mutex<Option<SendBitmap>>,
137 custom_image_dispose: Mutex<Option<Box<dyn FnOnce(Image) + Send>>>,
138 dpi: Vector,
139 pixel_size: PixelSize,
140}
141
142impl ImmutableBitmap {
143 fn from_bitmap(mut bitmap: Bitmap, pixel_size: Option<PixelSize>, dpi: Vector) -> io::Result<Self> {
144 bitmap.set_immutable();
145 let image = skia_safe::images::raster_from_bitmap(&bitmap).ok_or_else(load_error)?;
146 let pixel_size = pixel_size.unwrap_or_else(|| PixelSize::new(image.width(), image.height()));
147
148 Ok(Self {
149 image: Mutex::new(Some(image)),
150 bitmap: Mutex::new(Some(SendBitmap(bitmap))),
151 custom_image_dispose: Mutex::new(None),
152 dpi,
153 pixel_size,
154 })
155 }
156
157 pub fn from_stream(stream: &mut dyn Read) -> io::Result<Self> {
159 Self::from_bitmap(decode_bitmap(stream)?, None, Vector::new(96.0, 96.0))
161 }
162
163 pub fn from_image(image: Image, custom_image_dispose: Option<Box<dyn FnOnce(Image) + Send>>) -> Self {
167 let pixel_size = PixelSize::new(image.width(), image.height());
168
169 Self {
170 image: Mutex::new(Some(image)),
171 bitmap: Mutex::new(None),
172 custom_image_dispose: Mutex::new(custom_image_dispose),
173 dpi: Vector::new(96.0, 96.0),
174 pixel_size,
175 }
176 }
177
178 pub fn resized(
180 src: &ImmutableBitmap,
181 destination_size: PixelSize,
182 interpolation_mode: BitmapInterpolationMode,
183 ) -> io::Result<Self> {
184 let is_upscaling =
185 destination_size.width > src.pixel_size.width || destination_size.height > src.pixel_size.height;
186 let info = ImageInfo::new(
187 (destination_size.width, destination_size.height),
188 ColorType::BGRA8888,
189 AlphaType::Premul,
190 None,
191 );
192
193 let mut bitmap = Bitmap::new();
194 if !bitmap.try_alloc_pixels_info(&info, None) {
195 return Err(load_error());
196 }
197
198 let pixmap = bitmap.peek_pixels().ok_or_else(load_error)?;
199 if !src.image().scale_pixels(&pixmap, to_sk_sampling_options_scaled(interpolation_mode, is_upscaling), None)
200 {
201 return Err(load_error());
202 }
203
204 Self::from_bitmap(bitmap, None, Vector::new(96.0, 96.0))
205 }
206
207 pub fn from_stream_to_size(
210 stream: &mut dyn Read,
211 decode_size: i32,
212 horizontal: bool,
213 interpolation_mode: BitmapInterpolationMode,
214 ) -> io::Result<Self> {
215 let bitmap = decode_bitmap_to_size(stream, decode_size, horizontal, interpolation_mode)?;
216 Self::from_bitmap(bitmap, None, Vector::new(96.0, 96.0))
217 }
218
219 pub fn from_pixels(
225 size: PixelSize,
226 dpi: Vector,
227 stride: i32,
228 format: PixelFormat,
229 alpha_format: AlphaFormat,
230 data: &[u8],
231 ) -> io::Result<Self> {
232 let create_error =
233 || io::Error::new(io::ErrorKind::InvalidInput, "Unable to create bitmap from provided data");
234
235 let info = ImageInfo::new(
236 (size.width, size.height),
237 to_sk_color_type_or_panic(format),
238 to_sk_alpha_type(alpha_format),
239 None,
240 );
241
242 let mut bitmap = Bitmap::new();
243 if !bitmap.try_alloc_pixels_info(&info, None) {
244 return Err(create_error());
245 }
246
247 let row_bytes = bitmap.row_bytes();
248 let height = size.height.max(0) as usize;
249 let abs_stride = stride.unsigned_abs() as usize;
250 let copy_bytes = info.min_row_bytes().min(abs_stride);
251
252 if data.len() < abs_stride * height.saturating_sub(1) + if height > 0 { copy_bytes } else { 0 } {
253 return Err(create_error());
254 }
255
256 if height > 0 {
257 let mut pixmap = bitmap.peek_pixels().ok_or_else(create_error)?;
258 let pixels = pixmap.bytes_mut().ok_or_else(create_error)?;
259
260 for row in 0..height {
261 let source_row = if stride < 0 { height - 1 - row } else { row };
262 let source = &data[source_row * abs_stride..source_row * abs_stride + copy_bytes];
263 pixels[row * row_bytes..row * row_bytes + copy_bytes].copy_from_slice(source);
264 }
265 }
266
267 Self::from_bitmap(bitmap, Some(size), dpi)
268 }
269
270 pub fn image(&self) -> Image {
275 self.image.lock().unwrap().clone().expect("ImmutableBitmap has been disposed")
276 }
277}
278
279impl IBitmapImpl for ImmutableBitmap {
280 fn dpi(&self) -> Vector {
281 self.dpi
282 }
283
284 fn pixel_size(&self) -> PixelSize {
285 self.pixel_size
286 }
287
288 fn version(&self) -> i32 {
289 1
290 }
291
292 fn save(&self, stream: &mut dyn Write, options: &BitmapEncoderOptions) -> io::Result<()> {
293 image_saving_helper::save_image(&self.image(), stream, options)
294 }
295
296 fn dispose(&self) {
297 let image = self.image.lock().unwrap().take();
298 let custom_image_dispose = self.custom_image_dispose.lock().unwrap().take();
299 match (custom_image_dispose, image) {
300 (Some(custom_image_dispose), Some(image)) => custom_image_dispose(image),
301 (_, image) => drop(image),
302 }
303 self.bitmap.lock().unwrap().take();
304 }
305
306 fn as_any(&self) -> &dyn Any {
307 self
308 }
309
310 fn as_readable_bitmap(&self) -> Option<&dyn IReadableBitmapImpl> {
311 Some(self)
312 }
313}
314
315impl IDrawableBitmapImpl for ImmutableBitmap {
316 fn draw(
317 &self,
318 gr_context: Option<&dyn ISkiaGrContext>,
319 canvas: &Canvas,
320 source_rect: &Rect,
321 dest_rect: &Rect,
322 sampling_options: SamplingOptions,
323 paint: &Paint,
324 ) {
325 canvas.draw_image_rect_with_sampling_options(
326 drawable_image(gr_context, self.image(), needs_mipmaps(&sampling_options)),
327 Some((source_rect, SrcRectConstraint::Fast)),
328 dest_rect,
329 sampling_options,
330 paint,
331 );
332 }
333}
334
335impl IReadableBitmapImpl for ImmutableBitmap {
336 fn format(&self) -> Option<PixelFormat> {
337 self.bitmap.lock().unwrap().as_ref().and_then(|bitmap| to_pixel_format_opt(bitmap.color_type()))
338 }
339
340 fn alpha_format(&self) -> Option<AlphaFormat> {
341 self.bitmap.lock().unwrap().as_ref().map(|bitmap| to_alpha_format(bitmap.alpha_type()))
342 }
343
344 fn lock(&self) -> Rc<dyn ILockedFramebuffer> {
345 let bitmap = self.bitmap.lock().unwrap();
346 let bitmap = bitmap.as_ref().unwrap_or_else(|| panic!("A bitmap is needed for locking"));
347
348 let format = to_pixel_format_opt(bitmap.color_type())
349 .unwrap_or_else(|| panic!("Unsupported format {:?}", bitmap.color_type()));
350 let alpha_format = to_alpha_format(bitmap.alpha_type());
351
352 Rc::new(LockedFramebuffer::new(
353 bitmap.pixmap().addr() as *mut u8,
354 self.pixel_size,
355 bitmap.row_bytes() as i32,
356 self.dpi,
357 format,
358 alpha_format,
359 None,
360 ))
361 }
362}
363
364#[cfg(test)]
365mod tests {
366 use super::*;
367
368 fn constructor_from_pixels_copies_source_data(width: i32, height: i32, negative_stride: bool) {
369 let size = PixelSize::new(width, height);
370 let row_bytes = (width * 4) as usize;
371 let abs_stride = row_bytes;
372 let byte_size = abs_stride * height as usize;
373
374 let expected = |row: usize, x: usize| ((row * row_bytes + x) * 7 + 1) as u8;
376
377 let mut source = vec![0u8; byte_size];
380 for row in 0..height as usize {
381 let physical_row = if negative_stride { height as usize - 1 - row } else { row };
382 for x in 0..row_bytes {
383 source[physical_row * abs_stride + x] = expected(row, x);
384 }
385 }
386
387 let stride = if negative_stride { -(abs_stride as i32) } else { abs_stride as i32 };
388
389 let bitmap = ImmutableBitmap::from_pixels(
390 size,
391 Vector::new(96.0, 96.0),
392 stride,
393 PixelFormat::BGRA8888,
394 AlphaFormat::Premul,
395 &source,
396 )
397 .unwrap();
398
399 source.fill(0xCD);
402 drop(source);
403
404 assert_eq!(size, bitmap.pixel_size());
405
406 let locked = bitmap.lock();
407 assert_eq!(size, locked.size());
408
409 let locked_row_bytes = locked.row_bytes() as usize;
410 let dst = unsafe { std::slice::from_raw_parts(locked.address(), locked_row_bytes * height as usize) };
413
414 for row in 0..height as usize {
415 for x in 0..row_bytes {
416 assert_eq!(expected(row, x), dst[row * locked_row_bytes + x]);
417 }
418 }
419
420 locked.dispose();
421 bitmap.dispose();
422 }
423
424 #[test]
425 fn constructor_from_pixels_copies_source_data_cases() {
426 for (width, height) in [(1, 1), (3, 5), (64, 64)] {
427 constructor_from_pixels_copies_source_data(width, height, false);
428 constructor_from_pixels_copies_source_data(width, height, true);
429 }
430 }
431}