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