10
10
using Bumptech . Glide . Request ;
11
11
using Bumptech . Glide . Request . Target ;
12
12
using Microsoft . Extensions . Logging ;
13
+ using Microsoft . Extensions . Options ;
13
14
using Microsoft . Maui . Controls . PlatformConfiguration ;
14
15
using Microsoft . Maui . Platform ;
15
16
using Path = System . IO . Path ;
@@ -18,78 +19,140 @@ namespace AuroraControls;
18
19
19
20
internal partial class NoCacheFileImageSourceService
20
21
{
22
+ private static readonly BitmapFactory . Options _bitmapFactoryOptions =
23
+ new ( )
24
+ {
25
+ InSampleSize = 1 ,
26
+ InPreferredConfig = Bitmap . Config . Argb8888 , // Uses less memory than ARGB_8888
27
+ InDither = false ,
28
+ InTempStorage = new byte [ 32 * 1024 ] , // 32KB buffer for decoding
29
+ } ;
30
+
21
31
public override async Task < IImageSourceServiceResult ? > LoadDrawableAsync ( IImageSource imageSource , ImageView imageView ,
22
32
CancellationToken cancellationToken = default )
23
33
{
24
34
var fileImageSource = ( INoCacheFileImageSource ) imageSource ;
25
35
26
- if ( ! fileImageSource . IsEmpty )
36
+ if ( fileImageSource . IsEmpty )
27
37
{
28
- var file = fileImageSource . File ;
38
+ return null ;
39
+ }
29
40
30
- try
41
+ var file = fileImageSource . File ;
42
+
43
+ try
44
+ {
45
+ if ( ! Path . IsPathRooted ( file ) || ! File . Exists ( file ) )
31
46
{
32
- if ( ! Path . IsPathRooted ( file ) || ! File . Exists ( file ) )
47
+ var id = imageView . Context ? . GetDrawableId ( file ) ?? - 1 ;
48
+ if ( id > 0 )
33
49
{
34
- var id = imageView . Context ? . GetDrawableId ( file ) ?? - 1 ;
35
- if ( id > 0 )
36
- {
37
- imageView . SetImageResource ( id ) ;
38
- return new ImageSourceServiceLoadResult ( ) ;
39
- }
50
+ imageView . SetImageResource ( id ) ;
51
+ return new ImageSourceServiceLoadResult ( ) ;
40
52
}
41
-
42
- using var pathBitmap = await BitmapFactory . DecodeFileAsync ( file ) ;
43
- using var pathDrawable = new BitmapDrawable ( Platform . AppContext . Resources , pathBitmap ) ;
44
- imageView . SetImageDrawable ( pathDrawable ) ;
45
-
46
- return new ImageSourceServiceLoadResult ( ) ;
47
53
}
48
- catch ( Exception ex )
54
+
55
+ var pathDrawable = CreateDrawableModern ( file , imageView . Context ! ) ;
56
+ if ( pathDrawable != null )
49
57
{
50
- Logger ? . LogWarning ( ex , "Unable to load image file '{File}'." , file ) ;
51
- throw ;
58
+ imageView . SetImageDrawable ( pathDrawable ) ;
59
+ return new ImageSourceServiceLoadResult ( ( ) => pathDrawable . Dispose ( ) ) ;
52
60
}
53
- }
54
61
55
- return null ;
62
+ return null ;
63
+ }
64
+ catch ( Exception ex )
65
+ {
66
+ this . Logger ? . LogWarning ( ex , "Unable to load image file '{File}'." , file ) ;
67
+ throw ;
68
+ }
56
69
}
57
70
58
71
public override async Task < IImageSourceServiceResult < Drawable > ? > GetDrawableAsync ( IImageSource imageSource , Context context ,
59
72
CancellationToken cancellationToken = default )
60
73
{
61
74
var fileImageSource = ( INoCacheFileImageSource ) imageSource ;
62
- if ( ! fileImageSource . IsEmpty )
75
+ if ( fileImageSource . IsEmpty )
63
76
{
64
- var file = fileImageSource . File ;
77
+ return null ;
78
+ }
79
+
80
+ var file = fileImageSource . File ;
65
81
66
- try
82
+ try
83
+ {
84
+ if ( ! Path . IsPathRooted ( file ) || ! File . Exists ( file ) )
67
85
{
68
- if ( ! Path . IsPathRooted ( file ) || ! File . Exists ( file ) )
86
+ var id = context ? . GetDrawableId ( file ) ?? - 1 ;
87
+ if ( id > 0 )
69
88
{
70
- var id = context ? . GetDrawableId ( file ) ?? - 1 ;
71
- if ( id > 0 )
89
+ var d = context ? . GetDrawable ( id ) ;
90
+ if ( d is not null )
72
91
{
73
- var d = context ? . GetDrawable ( id ) ;
74
- if ( d is not null )
75
- {
76
- return new ImageSourceServiceResult ( d ) ;
77
- }
92
+ return new ImageSourceServiceResult ( d ) ;
78
93
}
79
94
}
95
+ }
80
96
81
- var pathBitmap = BitmapFactory . DecodeFile ( file ) ;
82
- var pathDrawable = new BitmapDrawable ( Platform . AppContext . Resources , pathBitmap ) ;
83
- return new ImageSourceServiceResult ( pathDrawable ) ;
97
+ var pathDrawable = CreateDrawableModern ( file , context ! ) ;
98
+ if ( pathDrawable != null )
99
+ {
100
+ return new ImageSourceServiceResult ( pathDrawable , ( ) => pathDrawable . Dispose ( ) ) ;
101
+ }
102
+
103
+ return null ;
104
+ }
105
+ catch ( Exception ex )
106
+ {
107
+ this . Logger ? . LogWarning ( ex , "Unable to load image file '{File}'." , file ) ;
108
+ throw ;
109
+ }
110
+ }
111
+
112
+ private static Drawable ? CreateDrawableModern ( string file , Context context )
113
+ {
114
+ try
115
+ {
116
+ if ( Android . OS . Build . VERSION . SdkInt >= Android . OS . BuildVersionCodes . P )
117
+ {
118
+ // Use modern ImageDecoder for Android API 28+
119
+ var source = ImageDecoder . CreateSource ( new Java . IO . File ( file ) ) ;
120
+ var bitmap = ImageDecoder . DecodeBitmap (
121
+ source ,
122
+ new ImageDecoderOnHeaderDecodedListener (
123
+ decoder =>
124
+ {
125
+ decoder . SetTargetColorSpace ( ColorSpace . Get ( ColorSpace . Named . Srgb ) ! ) ;
126
+ decoder . MemorySizePolicy = ImageDecoderMemoryPolicy . Default ;
127
+ } ) ) ;
128
+ return new BitmapDrawable ( context . Resources , bitmap ) ;
84
129
}
85
- catch ( Exception ex )
130
+ else
86
131
{
87
- Logger ? . LogWarning ( ex , "Unable to load image file '{File}'." , file ) ;
88
- throw ;
132
+ // Fallback to optimized BitmapFactory for older devices
133
+ var bitmap = BitmapFactory . DecodeFile ( file , _bitmapFactoryOptions ) ;
134
+ return new BitmapDrawable ( context . Resources , bitmap ) ;
89
135
}
90
136
}
137
+ catch
138
+ {
139
+ return null ;
140
+ }
141
+ }
142
+
143
+ private class ImageDecoderOnHeaderDecodedListener : Java . Lang . Object , ImageDecoder . IOnHeaderDecodedListener
144
+ {
145
+ private readonly Action < ImageDecoder > _onHeaderDecoded ;
146
+
147
+ public ImageDecoderOnHeaderDecodedListener ( Action < ImageDecoder > onHeaderDecoded )
148
+ {
149
+ _onHeaderDecoded = onHeaderDecoded ;
150
+ }
91
151
92
- return null ;
152
+ public void OnHeaderDecoded ( ImageDecoder decoder , ImageDecoder . ImageInfo info , ImageDecoder . Source source )
153
+ {
154
+ _onHeaderDecoded ( decoder ) ;
155
+ }
93
156
}
94
157
}
95
158
0 commit comments