@@ -2,41 +2,30 @@ import Foundation
22import ImageFormats
33
44/// A view that displays an image.
5- public struct Image : View {
6- private var image : ImageFormats . Image < RGBA > ?
5+ public struct Image : TypeSafeView , View {
76 private var isResizable = false
7+ private var source : Source
88
9- public var body : some View {
10- if let image {
11- _Image ( image, resizable: isResizable)
12- }
9+ enum Source : Equatable {
10+ case url( URL , useFileExtension: Bool )
11+ case image( ImageFormats . Image < RGBA > )
1312 }
1413
14+ public var body = EmptyView ( )
15+
1516 /// Displays an image file. `png`, `jpg`, and `webp` are supported.
1617 /// - Parameters:
1718 /// - url: The url of the file to display.
1819 /// - useFileExtension: If `true`, the file extension is used to determine the file type,
1920 /// otherwise the first few ('magic') bytes of the file are used.
2021 public init ( _ url: URL , useFileExtension: Bool = true ) {
21- guard let data = try ? Data ( contentsOf: url) else {
22- return
23- }
24-
25- let bytes = Array ( data)
26- if useFileExtension {
27- image = try ? ImageFormats . Image< RGBA> . load(
28- from: bytes,
29- usingFileExtension: url. pathExtension
30- )
31- } else {
32- image = try ? ImageFormats . Image< RGBA> . load( from: bytes)
33- }
22+ source = . url( url, useFileExtension: useFileExtension)
3423 }
3524
3625 /// Displays an image from raw pixel data.
3726 /// - Parameter image: The image data to display.
3827 public init ( _ image: ImageFormats . Image < RGBA > ) {
39- self . image = image
28+ source = . image( image )
4029 }
4130
4231 /// Makes the image resize to fit the available space.
@@ -45,59 +34,127 @@ public struct Image: View {
4534 image. isResizable = true
4635 return image
4736 }
48- }
4937
50- /// An internal implementation detail of ``Image``. Implements displaying of raw pixel data.
51- struct _Image : ElementaryView , View {
52- private var image : ImageFormats . Image < RGBA >
53- private var resizable : Bool
38+ init ( _ source: Source , resizable: Bool ) {
39+ self . source = source
40+ self . isResizable = resizable
41+ }
42+
43+ func layoutableChildren< Backend: AppBackend > (
44+ backend: Backend ,
45+ children: _ImageChildren
46+ ) -> [ LayoutSystem . LayoutableChild ] {
47+ [ ]
48+ }
5449
55- init ( _ image: ImageFormats . Image < RGBA > , resizable: Bool ) {
56- self . image = image
57- self . resizable = resizable
50+ func children< Backend: AppBackend > (
51+ backend: Backend ,
52+ snapshots: [ ViewGraphSnapshotter . NodeSnapshot ] ? ,
53+ environment: Environment
54+ ) -> _ImageChildren {
55+ _ImageChildren ( backend: backend)
5856 }
5957
6058 func asWidget< Backend: AppBackend > (
59+ _ children: _ImageChildren ,
6160 backend: Backend
6261 ) -> Backend . Widget {
63- return backend . createImageView ( )
62+ children . container . into ( )
6463 }
6564
6665 func update< Backend: AppBackend > (
6766 _ widget: Backend . Widget ,
67+ children: _ImageChildren ,
6868 proposedSize: SIMD2 < Int > ,
6969 environment: Environment ,
7070 backend: Backend ,
7171 dryRun: Bool
7272 ) -> ViewSize {
73- if !dryRun {
74- backend. updateImageView (
75- widget,
76- rgbaData: image. data,
77- width: image. width,
78- height: image. height
79- )
73+ let image : ImageFormats . Image < RGBA > ?
74+ if source != children. cachedImageSource {
75+ switch source {
76+ case . url( let url, let useFileExtension) :
77+ if let data = try ? Data ( contentsOf: url) {
78+ let bytes = Array ( data)
79+ if useFileExtension {
80+ image = try ? ImageFormats . Image< RGBA> . load(
81+ from: bytes,
82+ usingFileExtension: url. pathExtension
83+ )
84+ } else {
85+ image = try ? ImageFormats . Image< RGBA> . load( from: bytes)
86+ }
87+ } else {
88+ image = nil
89+ }
90+ case . image( let sourceImage) :
91+ image = sourceImage
92+ }
93+
94+ children. cachedImageSource = source
95+ children. cachedImage = image
96+ children. imageChanged = true
97+ } else {
98+ image = children. cachedImage
8099 }
81100
82- let idealSize = SIMD2 ( image. width, image. height)
101+ let idealSize = SIMD2 ( image? . width ?? 0 , image? . height ?? 0 )
83102 let size : ViewSize
84- if resizable {
103+ if isResizable {
85104 size = ViewSize (
86- size: proposedSize,
105+ size: image == nil ? . zero : proposedSize,
87106 idealSize: idealSize,
88107 minimumWidth: 0 ,
89108 minimumHeight: 0 ,
90- maximumWidth: nil ,
91- maximumHeight: nil
109+ maximumWidth: image == nil ? 0 : nil ,
110+ maximumHeight: image == nil ? 0 : nil
92111 )
93112 } else {
94113 size = ViewSize ( fixedSize: idealSize)
95114 }
96115
116+ if !dryRun && children. imageChanged {
117+ children. imageChanged = false
118+ if let image {
119+ backend. updateImageView (
120+ children. imageWidget. into ( ) ,
121+ rgbaData: image. data,
122+ width: image. width,
123+ height: image. height
124+ )
125+ if children. isContainerEmpty {
126+ backend. addChild ( children. imageWidget. into ( ) , to: children. container. into ( ) )
127+ backend. setPosition ( ofChildAt: 0 , in: children. container. into ( ) , to: . zero)
128+ }
129+ children. isContainerEmpty = false
130+ } else {
131+ backend. removeAllChildren ( of: children. container. into ( ) )
132+ children. isContainerEmpty = true
133+ }
134+ }
135+
97136 if !dryRun {
98- backend. setSize ( of: widget, to: size. size)
137+ backend. setSize ( of: children. container. into ( ) , to: size. size)
138+ backend. setSize ( of: children. imageWidget. into ( ) , to: size. size)
99139 }
100140
101141 return size
102142 }
103143}
144+
145+ class _ImageChildren : ViewGraphNodeChildren {
146+ var cachedImageSource : Image . Source ? = nil
147+ var cachedImage : ImageFormats . Image < RGBA > ? = nil
148+ var container : AnyWidget
149+ var imageWidget : AnyWidget
150+ var imageChanged = false
151+ var isContainerEmpty = true
152+
153+ init < Backend: AppBackend > ( backend: Backend ) {
154+ container = AnyWidget ( backend. createContainer ( ) )
155+ imageWidget = AnyWidget ( backend. createImageView ( ) )
156+ }
157+
158+ var widgets : [ AnyWidget ] = [ ]
159+ var erasedNodes : [ ErasedViewGraphNode ] = [ ]
160+ }
0 commit comments