Skip to content

Commit

Permalink
Merge pull request #54 from fabulous-dev/perf-opti-image
Browse files Browse the repository at this point in the history
Avoid allocating a new ImageSource instance on each update by using specialized attributes for each value type
  • Loading branch information
TimLariviere authored Jan 8, 2024
2 parents 4f61e1b + c667ab0 commit e40d296
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 6 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

_No unreleased changes_

## [8.0.4] - 2024-01-08

### Changed
- Avoid allocating a new ImageSource instance on each update by using specialized attributes for each value type by @TimLariviere (https://github.com/fabulous-dev/Fabulous.MauiControls/pull/54)

## [8.0.3] - 2024-01-03

### Fixed
Expand Down
29 changes: 23 additions & 6 deletions src/Fabulous.MauiControls/Views/Controls/Image.fs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,23 @@ module Image =

let Source = Attributes.defineBindableWithEquality<ImageSource> Image.SourceProperty

/// Performance optimization: avoid allocating a new ImageSource instance on each update
/// we store the user value (eg. string, Uri, Stream) and convert it to an ImageSource only when needed
let inline private defineSourceAttribute<'model when 'model: equality> ([<InlineIfLambda>] convertModelToValue: 'model -> ImageSource) =
Attributes.defineScalar<'model, 'model> Image.SourceProperty.PropertyName id ScalarAttributeComparers.equalityCompare (fun _ newValueOpt node ->
let target = node.Target :?> Image

match newValueOpt with
| ValueNone -> target.ClearValue(Image.SourceProperty)
| ValueSome v -> target.SetValue(Image.SourceProperty, convertModelToValue v))

let SourceFile = defineSourceAttribute<string> ImageSource.FromFile

let SourceUri = defineSourceAttribute<Uri> ImageSource.FromUri

let SourceStream =
defineSourceAttribute<Stream>(fun stream -> ImageSource.FromStream(fun () -> stream))

[<AutoOpen>]
module ImageBuilders =
type Fabulous.Maui.View with
Expand All @@ -42,35 +59,35 @@ module ImageBuilders =
/// <summary>Create an Image widget with a source</summary>
/// <param name="source">The image source</param>
static member inline Image<'msg>(source: string) =
View.Image<'msg>(ImageSource.FromFile(source))
WidgetBuilder<'msg, IFabImage>(Image.WidgetKey, Image.SourceFile.WithValue(source))

/// <summary>Create an Image widget with a source and an aspect</summary>
/// <param name="source">The image source</param>
/// <param name="aspect">The image aspect</param>
static member inline Image<'msg>(source: string, aspect: Aspect) =
View.Image<'msg>(ImageSource.FromFile(source), aspect)
WidgetBuilder<'msg, IFabImage>(Image.WidgetKey, Image.SourceFile.WithValue(source), Image.Aspect.WithValue(aspect))

/// <summary>Create an Image widget with a source</summary>
/// <param name="source">The image source</param>
static member inline Image<'msg>(source: Uri) =
View.Image<'msg>(ImageSource.FromUri(source))
WidgetBuilder<'msg, IFabImage>(Image.WidgetKey, Image.SourceUri.WithValue(source))

/// <summary>Create an Image widget with a source and an aspect</summary>
/// <param name="source">The image source</param>
/// <param name="aspect">The image aspect</param>
static member inline Image<'msg>(source: Uri, aspect: Aspect) =
View.Image<'msg>(ImageSource.FromUri(source), aspect)
WidgetBuilder<'msg, IFabImage>(Image.WidgetKey, Image.SourceUri.WithValue(source), Image.Aspect.WithValue(aspect))

/// <summary>Create an Image widget with a source</summary>
/// <param name="source">The image source</param>
static member inline Image<'msg>(source: Stream) =
View.Image<'msg>(ImageSource.FromStream(fun () -> source))
WidgetBuilder<'msg, IFabImage>(Image.WidgetKey, Image.SourceStream.WithValue(source))

/// <summary>Create an Image widget with a source and an aspect</summary>
/// <param name="source">The image source</param>
/// <param name="aspect">The image aspect</param>
static member inline Image<'msg>(source: Stream, aspect: Aspect) =
View.Image<'msg>(ImageSource.FromStream(fun () -> source), aspect)
WidgetBuilder<'msg, IFabImage>(Image.WidgetKey, Image.SourceStream.WithValue(source), Image.Aspect.WithValue(aspect))

[<Extension>]
type ImageModifiers =
Expand Down

0 comments on commit e40d296

Please sign in to comment.