ferroui_base/media/imaging/
bitmap.rs1use crate::media::imaging::bitmap_memory::BitmapMemory;
2use crate::media::imaging::pixel_format_transcoder::PixelFormatTranscoder;
3use crate::media::imaging::{BitmapEncoderOptions, BitmapInterpolationMode, IBitmap};
4use crate::media::{DrawingContext, IImage, IImageBrushSource};
5use crate::platform::{self, AlphaFormat, IBitmapImpl, ILockedFramebuffer, PixelFormat};
6use crate::utilities::{RefCountable, RefCounted};
7use crate::{PixelRect, PixelSize, Rect, Size, Vector};
8use std::any::Any;
9use std::io::{Read, Write};
10
11pub struct Bitmap {
18 is_transcoded: bool,
19 platform_impl: RefCounted<crate::platform::SharedBitmapImpl>,
20}
21
22pub(crate) fn create_bitmap_ref(platform_impl: std::sync::Arc<crate::platform::SharedBitmapImpl>) -> RefCounted<crate::platform::SharedBitmapImpl> {
25 let item = platform_impl.clone();
26 RefCountable::create(platform_impl, move || item.dispose())
27}
28
29impl Bitmap {
30 pub fn decode_to_width(
36 stream: &mut dyn Read,
37 width: i32,
38 interpolation_mode: BitmapInterpolationMode,
39 ) -> std::io::Result<Bitmap> {
40 Ok(Bitmap::from_impl(platform::render_interface().load_bitmap_to_width(stream, width, interpolation_mode)?))
41 }
42
43 pub fn decode_to_height(
46 stream: &mut dyn Read,
47 height: i32,
48 interpolation_mode: BitmapInterpolationMode,
49 ) -> std::io::Result<Bitmap> {
50 Ok(Bitmap::from_impl(platform::render_interface().load_bitmap_to_height(stream, height, interpolation_mode)?))
51 }
52
53 pub fn create_scaled_bitmap(
56 &self,
57 destination_size: PixelSize,
58 interpolation_mode: BitmapInterpolationMode,
59 ) -> Bitmap {
60 Bitmap::from_impl(platform::render_interface().resize_bitmap(
61 &*self.platform_impl.item(),
62 destination_size,
63 interpolation_mode,
64 ))
65 }
66
67 pub fn from_file(file_name: &str) -> std::io::Result<Bitmap> {
69 Ok(Bitmap::from_impl(platform::render_interface().load_bitmap_from_file(file_name)?))
70 }
71
72 pub fn from_stream(stream: &mut dyn Read) -> std::io::Result<Bitmap> {
74 Ok(Bitmap::from_impl(platform::render_interface().load_bitmap(stream)?))
75 }
76
77 pub fn from_ref(platform_impl: &RefCounted<crate::platform::SharedBitmapImpl>) -> Bitmap {
80 Bitmap { is_transcoded: false, platform_impl: platform_impl.clone_ref() }
81 }
82
83 pub fn from_impl(platform_impl: std::sync::Arc<crate::platform::SharedBitmapImpl>) -> Bitmap {
85 Bitmap { is_transcoded: false, platform_impl: create_bitmap_ref(platform_impl) }
86 }
87
88 pub fn from_pixels(
93 format: PixelFormat,
94 alpha_format: AlphaFormat,
95 data: &[u8],
96 size: PixelSize,
97 dpi: Vector,
98 stride: i32,
99 ) -> Bitmap {
100 let factory = platform::render_interface();
101 if factory.is_supported_bitmap_pixel_format(format) {
102 Bitmap::from_impl(factory.load_bitmap_from_pixels(format, alpha_format, data, size, dpi, stride))
103 } else {
104 let mut transcoded = BitmapMemory::new(PixelFormat::RGBA8888, AlphaFormat::Unpremul, size);
105 let transcoded_alpha_format = if format.has_alpha() { alpha_format } else { AlphaFormat::Opaque };
106 let (row_bytes, transcoded_format) = (transcoded.row_bytes(), transcoded.format());
107
108 PixelFormatTranscoder::transcode(
109 data,
110 size,
111 stride,
112 format,
113 alpha_format,
114 transcoded.data_mut(),
115 row_bytes,
116 transcoded_format,
117 transcoded_alpha_format,
118 );
119
120 let platform_impl = factory.load_bitmap_from_pixels(
121 PixelFormat::RGBA8888,
122 transcoded_alpha_format,
123 transcoded.data(),
124 size,
125 dpi,
126 row_bytes,
127 );
128 transcoded.dispose();
129
130 Bitmap { is_transcoded: true, platform_impl: create_bitmap_ref(platform_impl) }
131 }
132 }
133
134 pub fn dispose(&self) {
136 self.platform_impl.dispose();
137 }
138
139 pub fn dpi(&self) -> Vector {
141 self.platform_impl.item().dpi()
142 }
143
144 pub fn size(&self) -> Size {
146 self.platform_impl.item().pixel_size().to_size_with_dpi_vector(self.dpi())
147 }
148
149 pub fn pixel_size(&self) -> PixelSize {
151 self.platform_impl.item().pixel_size()
152 }
153
154 pub fn platform_impl(&self) -> &RefCounted<crate::platform::SharedBitmapImpl> {
156 &self.platform_impl
157 }
158
159 pub fn save_to_file(&self, file_name: &str, options: &BitmapEncoderOptions) -> std::io::Result<()> {
161 let mut stream = std::fs::File::create(file_name)?;
162 self.save(&mut stream, options)
163 }
164
165 pub fn save(&self, stream: &mut dyn Write, options: &BitmapEncoderOptions) -> std::io::Result<()> {
167 self.platform_impl.item().save(stream, options)
168 }
169
170 pub fn format(&self) -> Option<PixelFormat> {
172 self.platform_impl.item().as_readable_bitmap().and_then(|readable| readable.format())
173 }
174
175 pub fn alpha_format(&self) -> Option<AlphaFormat> {
177 self.platform_impl.item().as_readable_bitmap().and_then(|readable| readable.alpha_format())
178 }
179
180 fn validate_source_rect(&self, mut source_rect: PixelRect) -> PixelRect {
181 let pixel_size = self.pixel_size();
182
183 if (source_rect.width <= 0 || source_rect.height <= 0) && (source_rect.x != 0 || source_rect.y != 0) {
184 panic!("source_rect is out of range: {source_rect}");
185 }
186
187 if source_rect.x < 0 || source_rect.y < 0 {
188 panic!("source_rect is out of range: {source_rect}");
189 }
190
191 if source_rect.width <= 0 {
192 source_rect = source_rect.with_width(pixel_size.width);
193 }
194 if source_rect.height <= 0 {
195 source_rect = source_rect.with_height(pixel_size.height);
196 }
197
198 if source_rect.right() > pixel_size.width || source_rect.bottom() > pixel_size.height {
199 panic!("source_rect is out of range: {source_rect}");
200 }
201
202 source_rect
203 }
204
205 pub(crate) fn copy_pixels_between(
212 source_rect: PixelRect,
213 source: &[u8],
214 source_row_bytes: i32,
215 source_format: PixelFormat,
216 buffer: &mut [u8],
217 stride: i32,
218 ) {
219 let bits_per_pixel = source_format.bits_per_pixel() as i32;
220 let min_stride = source_rect
221 .width
222 .checked_mul(bits_per_pixel)
223 .and_then(|bits| bits.checked_add(7))
224 .expect("arithmetic overflow")
225 / 8;
226 if stride < min_stride {
227 panic!("stride is out of range: {stride}");
228 }
229
230 let min_buffer_size = stride as i64 * source_rect.height as i64;
233 if min_buffer_size > buffer.len() as i64 {
234 panic!("buffer size is out of range: {}", buffer.len());
235 }
236
237 let offset_x =
238 source_rect.x.checked_mul(bits_per_pixel).and_then(|bits| bits.checked_add(7)).expect("arithmetic overflow")
239 / 8;
240
241 if offset_x == 0 && source_row_bytes == stride && stride == min_stride {
247 let start = (source_row_bytes as i64 * source_rect.y as i64) as usize;
248 let length = min_buffer_size as usize;
249 buffer[..length].copy_from_slice(&source[start..start + length]);
250 return;
251 }
252
253 let min_stride = min_stride as usize;
254 for y in 0..source_rect.height {
255 let src_address =
256 (source_row_bytes as i64 * (source_rect.y + y) as i64 + offset_x as i64) as usize;
257 let dst_address = (stride as i64 * y as i64) as usize;
258 buffer[dst_address..dst_address + min_stride]
259 .copy_from_slice(&source[src_address..src_address + min_stride]);
260 }
261 }
262
263 pub(crate) fn copy_pixels_core(
264 &self,
265 source_rect: PixelRect,
266 buffer: &mut [u8],
267 stride: i32,
268 fb: &dyn ILockedFramebuffer,
269 ) {
270 let source_rect = self.validate_source_rect(source_rect);
271 let (row_bytes, format) = (fb.row_bytes(), fb.format());
272 fb.with_data(&mut |source| {
273 Self::copy_pixels_between(source_rect, source, row_bytes, format, buffer, stride);
274 });
275 }
276
277 pub fn copy_pixels(&self, source_rect: PixelRect, buffer: &mut [u8], stride: i32) {
285 let item = self.platform_impl.item();
286 let readable = match (self.format(), item.as_readable_bitmap()) {
287 (Some(format), Some(readable)) if Some(format) == readable.format() => readable,
288 _ => panic!("CopyPixels is not supported for this bitmap type"),
289 };
290
291 if self.is_transcoded {
292 panic!("CopyPixels is not supported for transcoded bitmaps");
293 }
294
295 let fb = readable.lock();
296 self.copy_pixels_core(source_rect, buffer, stride, &*fb);
297 fb.dispose();
298 }
299
300 pub fn copy_pixels_to_framebuffer(&self, buffer: &dyn ILockedFramebuffer) {
305 let item = self.platform_impl.item();
306 let readable = match item.as_readable_bitmap() {
307 Some(readable) if readable.format().is_some() && readable.alpha_format().is_some() => readable,
308 _ => panic!("CopyPixels is not supported for this bitmap type"),
311 };
312
313 if Some(buffer.format()) != readable.format() || Some(buffer.alpha_format()) != readable.alpha_format() {
314 let fb = readable.lock();
315 let (size, row_bytes, format, alpha_format) = (fb.size(), fb.row_bytes(), fb.format(), fb.alpha_format());
316 let (dest_row_bytes, dest_format, dest_alpha_format) =
317 (buffer.row_bytes(), buffer.format(), buffer.alpha_format());
318 fb.with_data(&mut |source| {
319 buffer.with_data(&mut |dest| {
320 PixelFormatTranscoder::transcode(
321 source,
322 size,
323 row_bytes,
324 format,
325 alpha_format,
326 dest,
327 dest_row_bytes,
328 dest_format,
329 dest_alpha_format,
330 );
331 });
332 });
333 fb.dispose();
334 } else {
335 let fb = readable.lock();
336 let (source_rect, stride) = (PixelRect::from_size(fb.size()), fb.row_bytes());
337 let buffer_size = (buffer.row_bytes() as i64 * buffer.size().height as i64).max(0) as usize;
338 buffer.with_data(&mut |dest| {
339 let length = buffer_size.min(dest.len());
340 self.copy_pixels_core(source_rect, &mut dest[..length], stride, &*fb);
341 });
342 fb.dispose();
343 }
344 }
345}
346
347impl IImage for Bitmap {
348 fn size(&self) -> Size {
349 Bitmap::size(self)
350 }
351
352 fn draw(&self, context: &mut DrawingContext<'_>, source_rect: Rect, dest_rect: Rect) {
353 context.draw_bitmap(&self.platform_impl, 1.0, source_rect, dest_rect);
354 }
355
356 fn as_any(&self) -> &dyn Any {
357 self
358 }
359
360 fn as_bitmap(&self) -> Option<&dyn IBitmap> {
361 Some(self)
362 }
363}
364
365impl IBitmap for Bitmap {
366 fn dpi(&self) -> Vector {
367 Bitmap::dpi(self)
368 }
369
370 fn pixel_size(&self) -> PixelSize {
371 Bitmap::pixel_size(self)
372 }
373
374 fn platform_impl(&self) -> &RefCounted<crate::platform::SharedBitmapImpl> {
375 &self.platform_impl
376 }
377
378 fn save(&self, stream: &mut dyn Write, options: &BitmapEncoderOptions) -> std::io::Result<()> {
379 Bitmap::save(self, stream, options)
380 }
381
382 fn dispose(&self) {
383 Bitmap::dispose(self)
384 }
385}
386
387impl IImageBrushSource for Bitmap {
388 fn bitmap(&self) -> Option<&RefCounted<crate::platform::SharedBitmapImpl>> {
389 if !self.platform_impl.is_alive() {
390 return None;
391 }
392 Some(&self.platform_impl)
393 }
394}
395
396impl PartialEq for Bitmap {
399 fn eq(&self, other: &Self) -> bool {
400 std::ptr::eq(self, other)
401 }
402}