@@ -20,6 +20,7 @@ import com.facebook.drawee.backends.pipeline.Fresco
2020import com.facebook.fbreact.specs.NativeImageLoaderAndroidSpec
2121import com.facebook.imagepipeline.common.RotationOptions
2222import com.facebook.imagepipeline.core.ImagePipeline
23+ import com.facebook.imagepipeline.image.CloseableImage
2324import com.facebook.imagepipeline.image.EncodedImage
2425import com.facebook.imagepipeline.request.ImageRequest
2526import com.facebook.imagepipeline.request.ImageRequestBuilder
@@ -93,6 +94,13 @@ internal class ImageLoaderModule : NativeImageLoaderAndroidSpec, LifecycleEventL
9394 resolveResourceSize(uriString, promise)
9495 return
9596 }
97+ // Fast path: data: URIs are not supported by fetchEncodedImage's producer
98+ // sequence (throws IllegalArgumentException). Route them through the decoded
99+ // image pipeline which handles data: URIs via DataFetchProducer.
100+ if (" data" == source.uri.scheme) {
101+ resolveDecodedImageSize(source, promise)
102+ return
103+ }
96104 val request: ImageRequest =
97105 ImageRequestBuilder .newBuilderWithSource(source.uri)
98106 .setRotationOptions(RotationOptions .disableRotation())
@@ -122,6 +130,11 @@ internal class ImageLoaderModule : NativeImageLoaderAndroidSpec, LifecycleEventL
122130 resolveResourceSize(uriString, promise)
123131 return
124132 }
133+ // Fast path: data: URIs are self-contained; headers are not applicable.
134+ if (" data" == source.uri.scheme) {
135+ resolveDecodedImageSize(source, promise)
136+ return
137+ }
125138 val imageRequestBuilder: ImageRequestBuilder =
126139 ImageRequestBuilder .newBuilderWithSource(source.uri)
127140 .setRotationOptions(RotationOptions .disableRotation())
@@ -217,6 +230,59 @@ internal class ImageLoaderModule : NativeImageLoaderAndroidSpec, LifecycleEventL
217230 )
218231 }
219232
233+ /* *
234+ * Resolve the size of a data: URI (or any URI unsupported by the encoded-image pipeline)
235+ * by decoding the image through Fresco's decoded-image pipeline, which routes through
236+ * DataFetchProducer and supports data: URIs.
237+ */
238+ private fun resolveDecodedImageSize (source : ImageSource , promise : Promise ) {
239+ val request: ImageRequest = ImageRequestBuilder .newBuilderWithSource(source.uri).build()
240+ val dataSource: DataSource <CloseableReference <CloseableImage >> =
241+ this .imagePipeline.fetchDecodedImage(request, this .callerContext)
242+ dataSource.subscribe(
243+ object : BaseDataSubscriber <CloseableReference <CloseableImage >>() {
244+ override fun onNewResultImpl (
245+ dataSource : DataSource <CloseableReference <CloseableImage >>
246+ ) {
247+ if (! dataSource.isFinished) {
248+ return
249+ }
250+ val ref = dataSource.result
251+ if (ref != null ) {
252+ try {
253+ val image = ref.get()
254+ val width = image.width
255+ val height = image.height
256+ if (width < 0 || height < 0 ) {
257+ promise.reject(ERROR_GET_SIZE_FAILURE , " Failed to get the size of the image" )
258+ return
259+ }
260+ promise.resolve(
261+ buildReadableMap {
262+ put(" width" , width)
263+ put(" height" , height)
264+ },
265+ )
266+ } catch (e: Exception ) {
267+ promise.reject(ERROR_GET_SIZE_FAILURE , e)
268+ } finally {
269+ CloseableReference .closeSafely(ref)
270+ }
271+ } else {
272+ promise.reject(ERROR_GET_SIZE_FAILURE , " Failed to get the size of the image" )
273+ }
274+ }
275+
276+ override fun onFailureImpl (
277+ dataSource : DataSource <CloseableReference <CloseableImage >>
278+ ) {
279+ promise.reject(ERROR_GET_SIZE_FAILURE , dataSource.failureCause)
280+ }
281+ },
282+ CallerThreadExecutor .getInstance(),
283+ )
284+ }
285+
220286 /* *
221287 * Prefetches the given image to the Fresco image disk cache.
222288 *
0 commit comments