/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ package com.facebook.react.views.imagehelper import com.facebook.imagepipeline.core.ImagePipelineFactory import com.facebook.react.modules.fresco.ImageCacheControl import kotlin.math.abs /** Helper class for dealing with multisource images. */ internal object MultiSourceHelper { @JvmStatic fun getBestSourceForSize(width: Int, height: Int, sources: List): MultiSourceResult = getBestSourceForSize(width, height, sources, 1.0) /** * Chooses the image source with the size closest to the target image size. * * @param width the width of the view that will be used to display this image * @param height the height of the view that will be used to display this image * @param sources the list of potential image sources to choose from * @param multiplier the area of the view will be multiplied by this number before calculating the * best source; this is useful if the image will be displayed bigger than the view (e.g. zoomed) */ @JvmStatic fun getBestSourceForSize( width: Int, height: Int, sources: List, multiplier: Double, ): MultiSourceResult { // no sources if (sources.isEmpty()) { return MultiSourceResult(null, null) } // single source if (sources.size == 1) { return MultiSourceResult(sources[0], null) } // For multiple sources, we first need the view's size in order to determine the best source to // load. If we haven't been measured yet, return null and wait for onSizeChanged. if (width <= 0 || height <= 0) { return MultiSourceResult(null, null) } val imagePipeline = ImagePipelineFactory.getInstance().imagePipeline var best: ImageSource? = null var bestCached: ImageSource? = null val viewArea = width * height * multiplier var bestPrecision = Double.MAX_VALUE var bestCachePrecision = Double.MAX_VALUE for (source in sources) { val precision = abs(1.0 - source.size / viewArea) if (precision < bestPrecision) { bestPrecision = precision best = source } if ( precision < bestCachePrecision && source.cacheControl != ImageCacheControl.RELOAD && (imagePipeline.isInBitmapMemoryCache(source.uri) || // TODO: T206445115 isInDiskCacheSync is a blocking operation, we should move this // to // a separate thread imagePipeline.isInDiskCacheSync(source.uri)) ) { bestCachePrecision = precision bestCached = source } } if (bestCached != null && best != null && bestCached.source == best.source) { bestCached = null } return MultiSourceResult(best, bestCached) } class MultiSourceResult( /** * Get the best result overall (closest in size to the view's size). Can be null if there were * no sources to choose from, or if there were more than 1 sources but width/height were 0. */ @JvmField val bestResult: ImageSource?, /** * Get the best result (closest in size to the view's size) that is also in cache. If this * would be the same as the source from [getBestResult], this will return `null` instead. */ @JvmField val bestResultInCache: ImageSource?, ) }