Load image file as pixel by Android uri

This commit is contained in:
Ming Ming 2022-07-13 03:43:27 +08:00
parent f903fd8e6f
commit 8af5b648b5
7 changed files with 179 additions and 0 deletions

View file

@ -0,0 +1,65 @@
package com.nkming.nc_photos.plugin
import android.content.Context
import android.net.Uri
import com.nkming.nc_photos.plugin.image_processor.Rgba8Image
import com.nkming.nc_photos.plugin.image_processor.TfLiteHelper
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
class ImageLoaderChannelHandler(context: Context) :
MethodChannel.MethodCallHandler {
companion object {
const val METHOD_CHANNEL = "${K.LIB_ID}/image_loader_method"
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"loadUri" -> {
try {
loadUri(
call.argument("fileUri")!!,
call.argument("maxWidth")!!,
call.argument("maxHeight")!!,
call.argument("resizeMethod")!!,
call.argument("isAllowSwapSide")!!,
call.argument("shouldUpscale")!!,
result
)
} catch (e: Throwable) {
result.error("systemException", e.toString(), null)
}
}
else -> result.notImplemented()
}
}
/**
* Load and resize an image pointed by a uri
*
* @param fileUri
* @param maxWidth
* @param maxHeight
* @param resizeMethod
* @param isAllowSwapSide
* @param shouldUpscale
* @param result
*/
private fun loadUri(
fileUri: String, maxWidth: Int, maxHeight: Int, resizeMethod: Int,
isAllowSwapSide: Boolean, shouldUpscale: Boolean,
result: MethodChannel.Result
) {
val image = BitmapUtil.loadImage(
context, Uri.parse(fileUri), maxWidth, maxHeight,
BitmapResizeMethod.values()[resizeMethod], isAllowSwapSide,
shouldUpscale
).use {
Rgba8Image(TfLiteHelper.bitmapToRgba8Array(it), it.width, it.height)
}
result.success(image.toJson())
}
private val context = context
}

View file

@ -113,6 +113,14 @@ class NcPhotosPlugin : FlutterPlugin, ActivityAware,
PreferenceChannelHandler.METHOD_CHANNEL
)
preferenceMethodChannel.setMethodCallHandler(preferenceChannelHandler)
val imageLoaderChannelHandler =
ImageLoaderChannelHandler(flutterPluginBinding.applicationContext)
imageLoaderMethodChannel = MethodChannel(
flutterPluginBinding.binaryMessenger,
ImageLoaderChannelHandler.METHOD_CHANNEL
)
imageLoaderMethodChannel.setMethodCallHandler(imageLoaderChannelHandler)
}
override fun onDetachedFromEngine(
@ -131,6 +139,7 @@ class NcPhotosPlugin : FlutterPlugin, ActivityAware,
permissionMethodChannel.setMethodCallHandler(null)
logcatMethodChannel.setMethodCallHandler(null)
preferenceMethodChannel.setMethodCallHandler(null)
imageLoaderMethodChannel.setMethodCallHandler(null)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
@ -219,6 +228,7 @@ class NcPhotosPlugin : FlutterPlugin, ActivityAware,
private lateinit var permissionMethodChannel: MethodChannel
private lateinit var logcatMethodChannel: MethodChannel
private lateinit var preferenceMethodChannel: MethodChannel
private lateinit var imageLoaderMethodChannel: MethodChannel
private lateinit var lockChannelHandler: LockChannelHandler
private lateinit var mediaStoreChannelHandler: MediaStoreChannelHandler

View file

@ -0,0 +1,33 @@
package com.nkming.nc_photos.plugin.image_processor
import android.graphics.Bitmap
import java.nio.ByteBuffer
/**
* Container of pixel data stored in RGBA format
*/
class Rgba8Image(val pixel: ByteArray, val width: Int, val height: Int) {
companion object {
fun fromJson(json: Map<String, Any>) = Rgba8Image(
json["pixel"] as ByteArray, json["width"] as Int,
json["height"] as Int
)
}
fun toJson() = mapOf<String, Any>(
"pixel" to pixel,
"width" to width,
"height" to height,
)
fun toBitmap(): Bitmap {
return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
.apply {
copyPixelsFromBuffer(ByteBuffer.wrap(pixel))
}
}
init {
assert(pixel.size == width * height * 4)
}
}

View file

@ -1,6 +1,7 @@
package com.nkming.nc_photos.plugin.image_processor
import android.graphics.Bitmap
import java.nio.ByteBuffer
import java.nio.IntBuffer
interface TfLiteHelper {
@ -25,6 +26,18 @@ interface TfLiteHelper {
return rgb8
}
/**
* Convert an ARGB_8888 Android bitmap to a RGBA byte array
*
* @param bitmap
* @return
*/
fun bitmapToRgba8Array(bitmap: Bitmap): ByteArray {
val buffer = ByteBuffer.allocate(bitmap.width * bitmap.height * 4)
bitmap.copyPixelsToBuffer(buffer)
return buffer.array()
}
/**
* Convert a RGB8 byte array to an ARGB_8888 Android bitmap
*

View file

@ -2,6 +2,8 @@ library nc_photos_plugin;
export 'src/content_uri.dart';
export 'src/exception.dart';
export 'src/image.dart';
export 'src/image_loader.dart';
export 'src/image_processor.dart';
export 'src/lock.dart';
export 'src/logcat.dart';

22
plugin/lib/src/image.dart Normal file
View file

@ -0,0 +1,22 @@
import 'dart:typed_data';
/// Container of pixel data stored in RGBA format
class Rgba8Image {
const Rgba8Image(this.pixel, this.width, this.height);
factory Rgba8Image.fromJson(Map<String, dynamic> json) => Rgba8Image(
json["pixel"],
json["width"],
json["height"],
);
Map<String, dynamic> toJson() => {
"pixel": pixel,
"width": width,
"height": height,
};
final Uint8List pixel;
final int width;
final int height;
}

View file

@ -0,0 +1,34 @@
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:nc_photos_plugin/src/image.dart';
import 'package:nc_photos_plugin/src/k.dart' as k;
enum ImageLoaderResizeMethod {
fit,
fill,
}
class ImageLoader {
static Future<Rgba8Image> loadUri(
String fileUri,
int maxWidth,
int maxHeight,
ImageLoaderResizeMethod resizeMethod, {
bool isAllowSwapSide = false,
bool shouldUpscale = false,
}) async {
final result =
await _methodChannel.invokeMethod<Map>("loadUri", <String, dynamic>{
"fileUri": fileUri,
"maxWidth": maxWidth,
"maxHeight": maxHeight,
"resizeMethod": resizeMethod.index,
"isAllowSwapSide": isAllowSwapSide,
"shouldUpscale": shouldUpscale,
});
return Rgba8Image.fromJson(result!.cast<String, dynamic>());
}
static const _methodChannel = MethodChannel("${k.libId}/image_loader_method");
}