decoding: RGBA_F16 to ARGB_8888 conversion
This commit is contained in:
parent
b224709c5d
commit
f02108fbcd
16 changed files with 91 additions and 40 deletions
|
@ -19,6 +19,7 @@ import androidx.annotation.RequiresApi
|
|||
import androidx.core.content.pm.ShortcutInfoCompat
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import androidx.core.net.toUri
|
||||
import app.loup.streams_channel.StreamsChannel
|
||||
import deckers.thibault.aves.channel.AvesByteSendingMethodCodec
|
||||
import deckers.thibault.aves.channel.calls.AccessibilityHandler
|
||||
|
@ -69,7 +70,6 @@ import kotlinx.coroutines.SupervisorJob
|
|||
import kotlinx.coroutines.launch
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import androidx.core.net.toUri
|
||||
|
||||
// `FlutterFragmentActivity` because of local auth plugin
|
||||
open class MainActivity : FlutterFragmentActivity() {
|
||||
|
|
|
@ -2,12 +2,12 @@ package deckers.thibault.aves
|
|||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import deckers.thibault.aves.channel.calls.AppAdapterHandler
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.utils.getParcelableExtraCompat
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import androidx.core.net.toUri
|
||||
|
||||
class WallpaperActivity : MainActivity() {
|
||||
private var originalIntent: String? = null
|
||||
|
|
|
@ -38,7 +38,7 @@ import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
|
|||
import deckers.thibault.aves.channel.calls.Coresult.Companion.safeSuspend
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.utils.BitmapUtils
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getDecodedBytes
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getRawBytes
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
import deckers.thibault.aves.utils.anyCauseIs
|
||||
import deckers.thibault.aves.utils.getApplicationInfoCompat
|
||||
|
@ -175,7 +175,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
|
|||
|
||||
try {
|
||||
val bitmap = withContext(Dispatchers.IO) { target.get() }
|
||||
bytes = bitmap?.getDecodedBytes(recycle = false)
|
||||
bytes = bitmap?.getRawBytes(recycle = false)
|
||||
} catch (e: Exception) {
|
||||
Log.w(LOG_TAG, "failed to decode app icon for packageName=$packageName", e)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import deckers.thibault.aves.model.FieldMap
|
|||
import deckers.thibault.aves.model.provider.ImageProvider
|
||||
import deckers.thibault.aves.model.provider.ImageProviderFactory.getProvider
|
||||
import deckers.thibault.aves.utils.BitmapUtils
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getDecodedBytes
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getRawBytes
|
||||
import deckers.thibault.aves.utils.FileUtils.transferFrom
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
import deckers.thibault.aves.utils.MimeTypes
|
||||
|
@ -74,7 +74,7 @@ class EmbeddedDataHandler(private val context: Context) : MethodCallHandler {
|
|||
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
|
||||
exif.thumbnailBitmap?.let { bitmap ->
|
||||
TransformationUtils.rotateImageExif(BitmapUtils.getBitmapPool(context), bitmap, orientation)?.let {
|
||||
it.getDecodedBytes(recycle = false)?.let { bytes -> thumbnails.add(bytes) }
|
||||
it.getRawBytes(recycle = false)?.let { bytes -> thumbnails.add(bytes) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import deckers.thibault.aves.decoder.AvesAppGlideModule
|
|||
import deckers.thibault.aves.decoder.MultiPageImage
|
||||
import deckers.thibault.aves.utils.BitmapRegionDecoderCompat
|
||||
import deckers.thibault.aves.utils.BitmapUtils
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getDecodedBytes
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getRawBytes
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
import deckers.thibault.aves.utils.MathUtils
|
||||
import deckers.thibault.aves.utils.MemoryUtils
|
||||
|
@ -132,7 +132,7 @@ class RegionFetcher internal constructor(
|
|||
bitmap = decoder.decodeRegion(effectiveRect, options)
|
||||
}
|
||||
|
||||
val bytes = bitmap?.getDecodedBytes(recycle = true)
|
||||
val bytes = bitmap?.getRawBytes(recycle = true)
|
||||
if (bytes != null) {
|
||||
result.success(bytes)
|
||||
} else {
|
||||
|
|
|
@ -14,7 +14,7 @@ import com.caverock.androidsvg.SVGParseException
|
|||
import deckers.thibault.aves.metadata.SVGParserBufferedInputStream
|
||||
import deckers.thibault.aves.metadata.SvgHelper.normalizeSize
|
||||
import deckers.thibault.aves.utils.BitmapUtils
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getDecodedBytes
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getRawBytes
|
||||
import deckers.thibault.aves.utils.MemoryUtils
|
||||
import deckers.thibault.aves.utils.StorageUtils
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
|
@ -109,7 +109,7 @@ class SvgRegionFetcher internal constructor(
|
|||
svg.renderToCanvas(canvas, renderOptions)
|
||||
|
||||
bitmap = Bitmap.createBitmap(bitmap, bleedX, bleedY, targetBitmapWidth, targetBitmapHeight)
|
||||
val bytes = bitmap.getDecodedBytes(recycle = true)
|
||||
val bytes = bitmap.getRawBytes(recycle = true)
|
||||
result.success(bytes)
|
||||
} catch (e: Exception) {
|
||||
result.error("fetch-read-exception", "failed to initialize region decoder for uri=$uri regionRect=$regionRect", e.message)
|
||||
|
|
|
@ -16,7 +16,7 @@ import com.bumptech.glide.signature.ObjectKey
|
|||
import deckers.thibault.aves.decoder.AvesAppGlideModule
|
||||
import deckers.thibault.aves.decoder.MultiPageImage
|
||||
import deckers.thibault.aves.utils.BitmapUtils.applyExifOrientation
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getDecodedBytes
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getRawBytes
|
||||
import deckers.thibault.aves.utils.MimeTypes
|
||||
import deckers.thibault.aves.utils.MimeTypes.SVG
|
||||
import deckers.thibault.aves.utils.MimeTypes.isVideo
|
||||
|
@ -77,7 +77,7 @@ class ThumbnailFetcher internal constructor(
|
|||
}
|
||||
}
|
||||
|
||||
val bytes = bitmap?.getDecodedBytes(recycle = false)
|
||||
val bytes = bitmap?.getRawBytes(recycle = false)
|
||||
if (bytes != null) {
|
||||
result.success(bytes)
|
||||
} else {
|
||||
|
|
|
@ -3,7 +3,7 @@ package deckers.thibault.aves.channel.calls.fetchers
|
|||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.net.Uri
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getDecodedBytes
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getRawBytes
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import org.beyka.tiffbitmapfactory.DecodeArea
|
||||
import org.beyka.tiffbitmapfactory.TiffBitmapFactory
|
||||
|
@ -32,7 +32,7 @@ class TiffRegionFetcher internal constructor(
|
|||
inDecodeArea = DecodeArea(regionRect.left, regionRect.top, regionRect.width(), regionRect.height())
|
||||
}
|
||||
val bitmap = TiffBitmapFactory.decodeFileDescriptor(fd, options)
|
||||
val bytes = bitmap?.getDecodedBytes(recycle = true)
|
||||
val bytes = bitmap?.getRawBytes(recycle = true)
|
||||
if (bytes != null) {
|
||||
result.success(bytes)
|
||||
} else {
|
||||
|
|
|
@ -9,8 +9,8 @@ import androidx.core.net.toUri
|
|||
import com.bumptech.glide.Glide
|
||||
import deckers.thibault.aves.decoder.AvesAppGlideModule
|
||||
import deckers.thibault.aves.utils.BitmapUtils.applyExifOrientation
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getDecodedBytes
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getEncodedBytes
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getRawBytes
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
import deckers.thibault.aves.utils.MemoryUtils
|
||||
import deckers.thibault.aves.utils.MimeTypes
|
||||
|
@ -155,7 +155,7 @@ class ImageByteStreamHandler(private val context: Context, private val arguments
|
|||
if (bitmap != null) {
|
||||
val recycle = false
|
||||
val bytes = if (decoded) {
|
||||
bitmap.getDecodedBytes(recycle)
|
||||
bitmap.getRawBytes(recycle)
|
||||
} else {
|
||||
bitmap.getEncodedBytes(canHaveAlpha = MimeTypes.canHaveAlpha(mimeType), recycle = recycle)
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ class ImageByteStreamHandler(private val context: Context, private val arguments
|
|||
if (bitmap != null) {
|
||||
val recycle = false
|
||||
val bytes = if (decoded) {
|
||||
bitmap.getDecodedBytes(recycle)
|
||||
bitmap.getRawBytes(recycle)
|
||||
} else {
|
||||
bitmap.getEncodedBytes(canHaveAlpha = false, recycle = false)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.net.Uri
|
||||
import androidx.core.graphics.createBitmap
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.Priority
|
||||
import com.bumptech.glide.Registry
|
||||
|
@ -22,7 +23,6 @@ import deckers.thibault.aves.metadata.SVGParserBufferedInputStream
|
|||
import deckers.thibault.aves.metadata.SvgHelper.normalizeSize
|
||||
import deckers.thibault.aves.utils.StorageUtils
|
||||
import kotlin.math.ceil
|
||||
import androidx.core.graphics.createBitmap
|
||||
|
||||
@GlideModule
|
||||
class SvgGlideModule : LibraryGlideModule() {
|
||||
|
|
|
@ -3,6 +3,7 @@ package deckers.thibault.aves.decoder
|
|||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import androidx.core.graphics.scale
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.Priority
|
||||
import com.bumptech.glide.Registry
|
||||
|
@ -17,7 +18,6 @@ import com.bumptech.glide.load.model.MultiModelLoaderFactory
|
|||
import com.bumptech.glide.module.LibraryGlideModule
|
||||
import com.bumptech.glide.signature.ObjectKey
|
||||
import org.beyka.tiffbitmapfactory.TiffBitmapFactory
|
||||
import androidx.core.graphics.scale
|
||||
|
||||
@GlideModule
|
||||
class TiffGlideModule : LibraryGlideModule() {
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.content.Context
|
|||
import android.graphics.BitmapFactory
|
||||
import android.media.MediaMetadataRetriever
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import com.drew.metadata.avi.AviDirectory
|
||||
import com.drew.metadata.exif.ExifIFD0Directory
|
||||
import com.drew.metadata.jpeg.JpegDirectory
|
||||
|
@ -29,7 +30,6 @@ import deckers.thibault.aves.utils.UriUtils.tryParseId
|
|||
import org.beyka.tiffbitmapfactory.TiffBitmapFactory
|
||||
import java.io.IOException
|
||||
import androidx.exifinterface.media.ExifInterfaceFork as ExifInterface
|
||||
import androidx.core.net.toUri
|
||||
|
||||
class SourceEntry {
|
||||
private val origin: Int
|
||||
|
|
|
@ -11,6 +11,7 @@ import android.net.Uri
|
|||
import android.os.Binder
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.core.net.toUri
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.FutureTarget
|
||||
import com.commonsware.cwac.document.DocumentFileCompat
|
||||
|
@ -32,6 +33,7 @@ import deckers.thibault.aves.metadata.PixyMetaHelper.xmpDocString
|
|||
import deckers.thibault.aves.metadata.metadataextractor.Helper
|
||||
import deckers.thibault.aves.metadata.xmp.GoogleXMP
|
||||
import deckers.thibault.aves.model.AvesEntry
|
||||
import deckers.thibault.aves.model.EntryFields
|
||||
import deckers.thibault.aves.model.ExifOrientationOp
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.model.NameConflictResolution
|
||||
|
@ -63,8 +65,6 @@ import java.util.Date
|
|||
import java.util.TimeZone
|
||||
import kotlin.math.absoluteValue
|
||||
import androidx.exifinterface.media.ExifInterfaceFork as ExifInterface
|
||||
import androidx.core.net.toUri
|
||||
import deckers.thibault.aves.model.EntryFields
|
||||
|
||||
abstract class ImageProvider {
|
||||
open fun fetchSingle(context: Context, uri: Uri, sourceMimeType: String?, allowUnsized: Boolean, callback: ImageOpCallback) {
|
||||
|
|
|
@ -4,9 +4,9 @@ import android.content.Context
|
|||
import android.graphics.Bitmap
|
||||
import android.graphics.ColorSpace
|
||||
import android.os.Build
|
||||
import android.util.Half
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.graphics.createBitmap
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.resource.bitmap.TransformationUtils
|
||||
import deckers.thibault.aves.metadata.Metadata.getExifCode
|
||||
|
@ -30,6 +30,8 @@ object BitmapUtils {
|
|||
private const val MAX_8_BITS_FLOAT = 0xff.toFloat()
|
||||
private const val MAX_10_BITS_FLOAT = 0x3ff.toFloat()
|
||||
|
||||
private const val RAW_BYTES_TRAILER_LENGTH = INT_BYTE_SIZE * 2
|
||||
|
||||
// bytes per pixel with different bitmap config
|
||||
private const val BPP_ALPHA_8 = 1
|
||||
private const val BPP_RGB_565 = 2
|
||||
|
@ -59,19 +61,15 @@ object BitmapUtils {
|
|||
return pixelCount * getBytePerPixel(config)
|
||||
}
|
||||
|
||||
fun Bitmap.getDecodedBytes(recycle: Boolean): ByteArray? {
|
||||
fun Bitmap.getRawBytes(recycle: Boolean): ByteArray? {
|
||||
if (!MemoryUtils.canAllocate(byteCount)) {
|
||||
throw Exception("bitmap buffer is $byteCount bytes, which cannot be allocated to a new byte array")
|
||||
}
|
||||
|
||||
try {
|
||||
val bytes = ByteBuffer.allocate(byteCount + INT_BYTE_SIZE * 2).apply {
|
||||
// `ByteBuffer` initial order is always `BIG_ENDIAN`
|
||||
var bytes = ByteBuffer.allocate(byteCount + RAW_BYTES_TRAILER_LENGTH).apply {
|
||||
copyPixelsToBuffer(this)
|
||||
// append bitmap size for use by the caller
|
||||
putInt(width)
|
||||
putInt(height)
|
||||
|
||||
rewind()
|
||||
}.array()
|
||||
|
||||
// convert pixel format and color space, if necessary
|
||||
|
@ -81,10 +79,14 @@ object BitmapUtils {
|
|||
val connector = ColorSpace.connect(srcColorSpace, dstColorSpace)
|
||||
if (config == Bitmap.Config.ARGB_8888) {
|
||||
if (srcColorSpace != dstColorSpace) {
|
||||
argb8888toArgb8888(bytes, connector, end = byteCount)
|
||||
argb8888ToArgb8888(bytes, connector, end = byteCount)
|
||||
}
|
||||
} else if (config == Bitmap.Config.RGBA_F16) {
|
||||
rgbaf16ToArgb8888(bytes, connector, end = byteCount)
|
||||
val newConfigByteCount = byteCount / (BPP_RGBA_F16 / BPP_ARGB_8888)
|
||||
bytes = bytes.sliceArray(0..<newConfigByteCount + RAW_BYTES_TRAILER_LENGTH)
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && config == Bitmap.Config.RGBA_1010102) {
|
||||
rgba1010102toArgb8888(bytes, connector, end = byteCount)
|
||||
rgba1010102ToArgb8888(bytes, connector, end = byteCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -92,6 +94,14 @@ object BitmapUtils {
|
|||
// should not be called before accessing color space or other properties
|
||||
if (recycle) this.recycle()
|
||||
|
||||
// append bitmap size for use by the caller to interpret the raw bytes
|
||||
val trailerOffset = bytes.size - RAW_BYTES_TRAILER_LENGTH
|
||||
bytes = ByteBuffer.wrap(bytes).apply {
|
||||
position(trailerOffset)
|
||||
putInt(width)
|
||||
putInt(height)
|
||||
}.array()
|
||||
|
||||
return bytes
|
||||
} catch (e: Exception) {
|
||||
Log.e(LOG_TAG, "failed to get bytes from bitmap", e)
|
||||
|
@ -111,8 +121,6 @@ object BitmapUtils {
|
|||
}
|
||||
}
|
||||
try {
|
||||
// the Bitmap raw bytes are not decodable by Flutter
|
||||
// we need to format them (compress, or add a BMP header) before sending them
|
||||
// `Bitmap.CompressFormat.PNG` is slower than `JPEG`, but it allows transparency
|
||||
// the BMP format allows an alpha channel, but Android decoding seems to ignore it
|
||||
if (canHaveAlpha && hasAlpha()) {
|
||||
|
@ -139,8 +147,10 @@ object BitmapUtils {
|
|||
return null
|
||||
}
|
||||
|
||||
// convert bytes, without reallocation:
|
||||
// - from original color space to sRGB.
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private fun argb8888toArgb8888(bytes: ByteArray, connector: ColorSpace.Connector, start: Int = 0, end: Int = bytes.size) {
|
||||
private fun argb8888ToArgb8888(bytes: ByteArray, connector: ColorSpace.Connector, start: Int = 0, end: Int = bytes.size) {
|
||||
// unpacking from ARGB_8888 and packing to ARGB_8888
|
||||
// stored as [3,2,1,0] -> [AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR]
|
||||
for (i in start..<end step BPP_ARGB_8888) {
|
||||
|
@ -162,11 +172,51 @@ object BitmapUtils {
|
|||
}
|
||||
}
|
||||
|
||||
// convert bytes, without reallocation:
|
||||
// - from config RGBA_F16 to ARGB_8888,
|
||||
// - from original color space to sRGB.
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private fun rgbaf16ToArgb8888(bytes: ByteArray, connector: ColorSpace.Connector, start: Int = 0, end: Int = bytes.size) {
|
||||
val indexDivider = BPP_RGBA_F16 / BPP_ARGB_8888
|
||||
for (i in start..<end step BPP_RGBA_F16) {
|
||||
// unpacking from RGBA_F16
|
||||
// stored as [7,6,5,4,3,2,1,0] -> [AAAAAAAA AAAAAAAA BBBBBBBB BBBBBBBB GGGGGGGG GGGGGGGG RRRRRRRR RRRRRRRR]
|
||||
val i7 = bytes[i + 7].toInt()
|
||||
val i6 = bytes[i + 6].toInt()
|
||||
val i5 = bytes[i + 5].toInt()
|
||||
val i4 = bytes[i + 4].toInt()
|
||||
val i3 = bytes[i + 3].toInt()
|
||||
val i2 = bytes[i + 2].toInt()
|
||||
val i1 = bytes[i + 1].toInt()
|
||||
val i0 = bytes[i].toInt()
|
||||
|
||||
val hA = Half((((i7 and 0xff) shl 8) or (i6 and 0xff)).toShort())
|
||||
val hB = Half((((i5 and 0xff) shl 8) or (i4 and 0xff)).toShort())
|
||||
val hG = Half((((i3 and 0xff) shl 8) or (i2 and 0xff)).toShort())
|
||||
val hR = Half((((i1 and 0xff) shl 8) or (i0 and 0xff)).toShort())
|
||||
|
||||
// components as floats in sRGB
|
||||
val srgbFloats = connector.transform(hR.toFloat(), hG.toFloat(), hB.toFloat())
|
||||
val srgbR = (srgbFloats[0] * 255.0f + 0.5f).toInt()
|
||||
val srgbG = (srgbFloats[1] * 255.0f + 0.5f).toInt()
|
||||
val srgbB = (srgbFloats[2] * 255.0f + 0.5f).toInt()
|
||||
val alpha = (hA.toFloat() * 255.0f + 0.5f).toInt()
|
||||
|
||||
// packing to ARGB_8888
|
||||
// stored as [3,2,1,0] -> [AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR]
|
||||
val dstI = i / indexDivider
|
||||
bytes[dstI + 3] = alpha.toByte()
|
||||
bytes[dstI + 2] = srgbB.toByte()
|
||||
bytes[dstI + 1] = srgbG.toByte()
|
||||
bytes[dstI] = srgbR.toByte()
|
||||
}
|
||||
}
|
||||
|
||||
// convert bytes, without reallocation:
|
||||
// - from config RGBA_1010102 to ARGB_8888,
|
||||
// - from original color space to sRGB.
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private fun rgba1010102toArgb8888(bytes: ByteArray, connector: ColorSpace.Connector, start: Int = 0, end: Int = bytes.size) {
|
||||
private fun rgba1010102ToArgb8888(bytes: ByteArray, connector: ColorSpace.Connector, start: Int = 0, end: Int = bytes.size) {
|
||||
val alphaFactor = 255.0f / MAX_2_BITS_FLOAT
|
||||
|
||||
for (i in start..<end step BPP_RGBA_1010102) {
|
||||
|
|
|
@ -15,6 +15,8 @@ import android.provider.DocumentsContract
|
|||
import android.provider.MediaStore
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.text.isDigitsOnly
|
||||
import com.commonsware.cwac.document.DocumentFileCompat
|
||||
import deckers.thibault.aves.model.provider.ImageProvider
|
||||
import deckers.thibault.aves.utils.FileUtils.transferFrom
|
||||
|
@ -29,8 +31,6 @@ import java.io.InputStream
|
|||
import java.io.OutputStream
|
||||
import java.util.Locale
|
||||
import java.util.regex.Pattern
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.text.isDigitsOnly
|
||||
|
||||
object StorageUtils {
|
||||
private val LOG_TAG = LogUtils.createTag<StorageUtils>()
|
||||
|
|
|
@ -7,9 +7,10 @@ import 'package:flutter/services.dart';
|
|||
class InteropDecoding {
|
||||
static Future<ui.ImageDescriptor?> bytesToCodec(Uint8List bytes) async {
|
||||
const trailerLength = 4 * 2;
|
||||
if (bytes.length < trailerLength) return null;
|
||||
final byteCount = bytes.length;
|
||||
if (byteCount < trailerLength) return null;
|
||||
|
||||
final trailerOffset = bytes.length - trailerLength;
|
||||
final trailerOffset = byteCount - trailerLength;
|
||||
final trailer = ByteData.sublistView(bytes, trailerOffset);
|
||||
final bitmapWidth = trailer.getUint32(0);
|
||||
final bitmapHeight = trailer.getUint32(4);
|
||||
|
|
Loading…
Reference in a new issue