makepad_widgets/
image_cache.rs1use crate::{makepad_draw::*};
2use std::collections::HashMap;
3use makepad_zune_jpeg::JpegDecoder;
4use makepad_zune_png::PngDecoder;
5
6
7#[derive(Live, LiveHook)]
8#[live_ignore]
9pub enum ImageFit{
10 #[pick] Stretch,
11 Horizontal,
12 Vertical,
13 Smallest,
14 Biggest
15}
16
17
18#[derive(Default, Clone)]
19pub struct ImageBuffer {
20 pub width: usize,
21 pub height: usize,
22 pub data: Vec<u32>,
23}
24
25impl ImageBuffer {
26 pub fn new(in_data: &[u8], width: usize,height: usize) -> Result<ImageBuffer, String> {
27 let mut out = Vec::new();
28 let pixels = width * height;
29 out.resize(pixels, 0u32);
30 if in_data.len() / pixels== 3{
32 for i in 0..pixels{
33 let r = in_data[i*3];
34 let g = in_data[i*3+1];
35 let b = in_data[i*3+2];
36 out[i] = 0xff000000 | ((r as u32)<<16) | ((g as u32)<<8) | ((b as u32)<<0);
37 }
38 }
39 else if in_data.len() / pixels == 4{
40 for i in 0..pixels{
41 let r = in_data[i*4];
42 let g = in_data[i*4+1];
43 let b = in_data[i*4+2];
44 let a = in_data[i*4+3];
45 out[i] = ((a as u32)<<24) | ((r as u32)<<16) | ((g as u32)<<8) | ((b as u32)<<0);
46 }
47 }
48 else{
49 return Err("ImageBuffer::new Image buffer pixel alignment not 3 or 4".to_string())
50 }
51 Ok(ImageBuffer {
52 width,
53 height,
54 data: out
55 })
56 }
57
58 pub fn into_new_texture(self, cx:&mut Cx)->Texture{
59 let texture = Texture::new(cx);
60 self.into_texture(cx, &texture);
61 texture
62 }
63
64 pub fn into_texture(mut self, cx:&mut Cx, texture:&Texture){
65 texture.set_desc(
66 cx,
67 TextureDesc {
68 format: TextureFormat::ImageBGRA,
69 width: Some(self.width),
70 height: Some(self.height),
71 },
72 );
73 texture.swap_image_u32(cx, &mut self.data);
74 }
75
76
77 pub fn from_png(
78 data: &[u8]
79 ) -> Result<Self, String> {
80 let mut decoder = PngDecoder::new(data);
81 match decoder.decode() {
82 Ok(image) => {
83 if let Some(data) = image.u8(){
84 let (width,height) = decoder.get_dimensions().unwrap();
85 ImageBuffer::new(&data, width as usize, height as usize)
86 }
87 else{
88 Err("Error decoding PNG: image data empty".to_string())
89 }
90 }
91 Err(err) => {
92 Err(format!("Error decoding PNG: {:?}", err))
93 }
94 }
95 }
96
97 pub fn from_jpg(
98 data: &[u8]
99 ) -> Result<Self, String> {
100 let mut decoder = JpegDecoder::new(&*data);
101 match decoder.decode() {
103 Ok(data) => {
104 let info = decoder.info().unwrap();
105 ImageBuffer::new(&data, info.width as usize, info.height as usize)
106 },
107 Err(err) => {
108 Err(format!("Error decoding JPG: {:?}", err))
109 }
110 }
111 }
112}
113
114pub struct ImageCache {
115 map: HashMap<String, Texture>,
116}
117
118impl ImageCache {
119 pub fn new() -> Self {
120 Self {
121 map: HashMap::new(),
122 }
123 }
124}
125
126pub trait ImageCacheImpl {
127 fn get_texture(&self) -> &Option<Texture>;
128 fn set_texture(&mut self, texture: Option<Texture>);
129
130 fn lazy_create_image_cache(&mut self,cx: &mut Cx) {
131 if !cx.has_global::<ImageCache>() {
132 cx.set_global(ImageCache::new());
133 }
134 }
135
136
137 fn load_png_from_data(&mut self, cx:&mut Cx, data:&[u8]){
138 match ImageBuffer::from_png(&*data){
139 Ok(data)=>{
140 if let Some(texture) = self.get_texture(){
141 data.into_texture(cx, texture);
142 }
143 else{
144 self.set_texture(Some(data.into_new_texture(cx)));
145 }
146 }
147 Err(err)=>{
148 error!("load_png_from_data: Cannot load png image from data {}", err);
149 }
150 }
151 }
152
153 fn load_jpg_from_data(&mut self, cx:&mut Cx, data:&[u8]){
154 match ImageBuffer::from_jpg(&*data){
155 Ok(data)=>{
156 if let Some(texture) = self.get_texture(){
157 data.into_texture(cx, texture);
158 }
159 else{
160 self.set_texture(Some(data.into_new_texture(cx)));
161 }
162 }
163 Err(err)=>{
164 error!("load_jpg_from_data: Cannot load png image from data {}", err);
165 }
166 }
167 }
168
169 fn load_image_dep_by_path(
170 &mut self,
171 cx: &mut Cx,
172 image_path: &str,
173 ) {
174 if let Some(texture) = cx.get_global::<ImageCache>().map.get(image_path){
175 self.set_texture(Some(texture.clone()));
176 }
177 else{
178 match cx.get_dependency(image_path) {
179 Ok(data) => {
180 if image_path.ends_with(".jpg") {
181 match ImageBuffer::from_jpg(&*data){
182 Ok(data)=>{
183 let texture = data.into_new_texture(cx);
184 cx.get_global::<ImageCache>().map.insert(image_path.to_string(), texture.clone());
185 self.set_texture(Some(texture));
186 }
187 Err(err)=>{
188 error!("load_image_dep_by_path: Cannot load jpeg image from path: {} {}",image_path, err);
189 }
190 }
191 } else if image_path.ends_with(".png") {
192 match ImageBuffer::from_png(&*data){
193 Ok(data)=>{
194 let texture = data.into_new_texture(cx);
195 cx.get_global::<ImageCache>().map.insert(image_path.to_string(), texture.clone());
196 self.set_texture(Some(texture));
197 }
198 Err(err)=>{
199 error!("load_image_dep_by_path: Cannot load png image from path: {} {}",image_path, err);
200 }
201 }
202 } else {
203 error!("load_image_dep_by_path: Image format not supported {}",image_path);
204 }
205 }
206 Err(err) => {
207 error!("load_image_dep_by_path: Resource not found {} {}",image_path, err);
208 }
209 }
210 }
211 }
212}