@@ -2,16 +2,22 @@ import observable = require("data/observable");
2
2
import imagesource = require( "image-source" ) ;
3
3
import application = require( "application" ) ;
4
4
5
+ interface ArrayBufferStatic extends ArrayBufferConstructor {
6
+ from ( buffer : java . nio . ByteBuffer ) : ArrayBuffer ;
7
+ }
8
+
5
9
var Intent = android . content . Intent ;
6
10
var Activity = android . app . Activity ;
7
11
var MediaStore = android . provider . MediaStore ;
8
12
var BitmapFactory = android . graphics . BitmapFactory ;
13
+ var StaticArrayBuffer = < ArrayBufferStatic > ArrayBuffer ;
9
14
10
15
export class SelectedAsset extends observable . Observable {
11
16
private _uri : android . net . Uri ;
12
17
private _thumb : imagesource . ImageSource ;
13
18
private _thumbRequested : boolean ;
14
19
private _fileUri : string ;
20
+ private _data : ArrayBuffer ;
15
21
16
22
constructor ( uri : android . net . Uri ) {
17
23
super ( ) ;
@@ -23,6 +29,30 @@ export class SelectedAsset extends observable.Observable {
23
29
return Promise . reject ( new Error ( "Not implemented." ) ) ;
24
30
}
25
31
32
+ getImage ( options ?: { maxWidth : number , maxHeight : number } ) : Promise < imagesource . ImageSource > {
33
+ return new Promise < imagesource . ImageSource > ( ( resolve , reject ) => {
34
+ try {
35
+ resolve ( this . decodeUri ( this . _uri , options ) ) ;
36
+ } catch ( ex ) {
37
+ reject ( ex ) ;
38
+ }
39
+ } ) ;
40
+ }
41
+
42
+ getImageData ( ) : Promise < ArrayBuffer > {
43
+ return new Promise < ArrayBuffer > ( ( resolve , reject ) => {
44
+ try {
45
+ if ( ! this . _data ) {
46
+ var bb = this . getByteBuffer ( this . _uri ) ;
47
+ this . _data = StaticArrayBuffer . from ( bb ) ;
48
+ }
49
+ resolve ( this . _data ) ;
50
+ } catch ( ex ) {
51
+ reject ( ex ) ;
52
+ }
53
+ } ) ;
54
+ }
55
+
26
56
get thumb ( ) : imagesource . ImageSource {
27
57
if ( ! this . _thumbRequested ) {
28
58
this . decodeThumbUri ( ) ;
@@ -35,7 +65,7 @@ export class SelectedAsset extends observable.Observable {
35
65
}
36
66
37
67
get fileUri ( ) : string {
38
- if ( ! this . _fileUri ) {
68
+ if ( ! this . _fileUri ) {
39
69
this . _fileUri = this . _calculateFileUri ( ) ;
40
70
}
41
71
return this . _fileUri ;
@@ -44,7 +74,7 @@ export class SelectedAsset extends observable.Observable {
44
74
private _calculateFileUri ( ) : string {
45
75
var cursor : android . database . ICursor ;
46
76
var columns = [ MediaStore . MediaColumns . DATA ] ;
47
- if ( android . os . Build . VERSION . SDK_INT >= 19 ) {
77
+ if ( android . os . Build . VERSION . SDK_INT >= 19 ) {
48
78
var wholeID : string = ( < any > android . provider ) . DocumentsContract . getDocumentId ( this . _uri ) ;
49
79
var id = wholeID . split ( ":" ) [ 1 ] ;
50
80
cursor = this . getContentResolver ( ) . query ( android . provider . MediaStore . Images . Media . EXTERNAL_CONTENT_URI , columns , "_id=?" , [ id ] , null ) ;
@@ -87,34 +117,91 @@ export class SelectedAsset extends observable.Observable {
87
117
88
118
private decodeThumbUri ( ) : void {
89
119
// Decode image size
120
+ var REQUIRED_SIZE = {
121
+ maxWidth : 100 ,
122
+ maxHeight : 100
123
+ } ;
124
+
125
+ // Decode with scale
126
+ this . _thumb = this . decodeUri ( this . _uri , REQUIRED_SIZE ) ;
127
+ this . notifyPropertyChange ( "thumb" , this . _thumb ) ;
128
+ }
129
+
130
+ /**
131
+ * Discovers the sample size that a BitmapFactory.Options object should have
132
+ * to scale the retrieved image to the given max size.
133
+ * @param uri The URI of the image that should be scaled.
134
+ * @param options The options that should be used to produce the correct image scale.
135
+ */
136
+ private getSampleSize ( uri : android . net . Uri , options ?: { maxWidth : number , maxHeight : number } ) : number {
90
137
var boundsOptions = new BitmapFactory . Options ( ) ;
91
138
boundsOptions . inJustDecodeBounds = true ;
92
- BitmapFactory . decodeStream ( this . getContentResolver ( ) . openInputStream ( this . _uri ) , null , boundsOptions ) ;
93
-
94
- var REQUIRED_SIZE = 100 ;
139
+ BitmapFactory . decodeStream ( this . openInputStream ( uri ) , null , boundsOptions ) ;
95
140
96
141
// Find the correct scale value. It should be the power of 2.
97
142
var outWidth = boundsOptions . outWidth ;
98
143
var outHeight = boundsOptions . outHeight ;
99
144
var scale = 1 ;
100
- while ( true ) {
101
- if ( outWidth / 2 < REQUIRED_SIZE
102
- || outHeight / 2 < REQUIRED_SIZE ) {
103
- break ;
145
+ if ( options ) {
146
+ // TODO: Refactor to accomodate different scaling options
147
+ // Right now, it just selects the smallest of the two sizes
148
+ // and scales the image proportionally to that.
149
+ var targetSize = options . maxWidth < options . maxHeight ? options . maxWidth : options . maxHeight ;
150
+ while ( ! ( this . matchesSize ( targetSize , outWidth ) ||
151
+ this . matchesSize ( targetSize , outHeight ) ) ) {
152
+ outWidth /= 2 ;
153
+ outHeight /= 2 ;
154
+ scale *= 2 ;
104
155
}
105
- outWidth /= 2 ;
106
- outHeight /= 2 ;
107
- scale *= 2 ;
108
156
}
157
+ return scale ;
158
+ }
109
159
110
- // Decode with scale
160
+ private matchesSize ( targetSize : number , actualSize : number ) : boolean {
161
+ return targetSize && actualSize / 2 < targetSize ;
162
+ }
163
+
164
+ /**
165
+ * Decodes the given URI using the given options.
166
+ * @param uri The URI that should be decoded into an ImageSource.
167
+ * @param options The options that should be used to decode the image.
168
+ */
169
+ private decodeUri ( uri : android . net . Uri , options ?: { maxWidth : number , maxHeight : number } ) : imagesource . ImageSource {
111
170
var downsampleOptions = new BitmapFactory . Options ( ) ;
112
- downsampleOptions . inSampleSize = scale ;
113
- var bitmap = BitmapFactory . decodeStream ( this . getContentResolver ( ) . openInputStream ( this . _uri ) , null , downsampleOptions ) ;
171
+ downsampleOptions . inSampleSize = this . getSampleSize ( uri , options ) ;
172
+ var bitmap = BitmapFactory . decodeStream ( this . openInputStream ( uri ) , null , downsampleOptions ) ;
173
+ var image = new imagesource . ImageSource ( ) ;
174
+ image . setNativeSource ( bitmap ) ;
175
+ return image ;
176
+ }
114
177
115
- this . _thumb = new imagesource . ImageSource ( ) ;
116
- this . _thumb . setNativeSource ( bitmap ) ;
117
- this . notifyPropertyChange ( "thumb" , this . _thumb ) ;
178
+ /**
179
+ * Retrieves the raw data of the given file and exposes it as a byte buffer.
180
+ */
181
+ private getByteBuffer ( uri : android . net . Uri ) : java . nio . ByteBuffer {
182
+ var file : android . content . res . AssetFileDescriptor = null ;
183
+ try {
184
+ file = this . getContentResolver ( ) . openAssetFileDescriptor ( uri , "r" ) ;
185
+
186
+ // Determine how many bytes to allocate in memory based on the file length
187
+ var length : number = file . getLength ( ) ;
188
+ var buffer : java . nio . ByteBuffer = java . nio . ByteBuffer . allocateDirect ( length ) ;
189
+ var bytes = buffer . array ( ) ;
190
+ var stream = file . createInputStream ( ) ;
191
+
192
+ // Buffer the data in 4KiB amounts
193
+ var reader = new java . io . BufferedInputStream ( stream , 4096 ) ;
194
+ reader . read ( bytes , 0 , bytes . length ) ;
195
+ return buffer ;
196
+ } finally {
197
+ if ( file ) {
198
+ file . close ( ) ;
199
+ }
200
+ }
201
+ }
202
+
203
+ private openInputStream ( uri : android . net . Uri ) : java . io . InputStream {
204
+ return this . getContentResolver ( ) . openInputStream ( uri ) ;
118
205
}
119
206
120
207
private getContentResolver ( ) : android . content . ContentResolver {
@@ -179,7 +266,7 @@ export class ImagePicker {
179
266
resolve ( results ) ;
180
267
return ;
181
268
182
- } catch ( e ) {
269
+ } catch ( e ) {
183
270
application . android . off ( application . AndroidApplication . activityResultEvent , onResult ) ;
184
271
reject ( e ) ;
185
272
return ;
0 commit comments