resize large image
This commit is contained in:
parent
173c19d557
commit
fdbce75afd
|
@ -3,11 +3,13 @@ package niuhuan.pikapi
|
|||
import android.content.ContentValues
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Matrix
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.provider.MediaStore
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.Log
|
||||
import android.view.Display
|
||||
import android.view.KeyEvent
|
||||
|
@ -23,6 +25,7 @@ import kotlinx.coroutines.launch
|
|||
import kotlinx.coroutines.newSingleThreadContext
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import mobile.Mobile
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
|
@ -100,6 +103,7 @@ class MainActivity : FlutterActivity() {
|
|||
// 获取可以迁移数据地址
|
||||
"androidGetExtendDirs" -> androidGetExtendDirs()
|
||||
"androidSecureFlag" -> androidSecureFlag(call.argument("flag")!!)
|
||||
"convertToPNG" -> convertToPNG(call.argument("path")!!)
|
||||
else -> {
|
||||
notImplementedToken
|
||||
}
|
||||
|
@ -350,4 +354,36 @@ class MainActivity : FlutterActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun convertToPNG(path: String): ByteArray {
|
||||
BitmapFactory.decodeFile(path)?.let { bitmap ->
|
||||
val maxWidth =
|
||||
when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> windowManager.currentWindowMetrics.bounds.width()
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 -> {
|
||||
val displayMetrics = DisplayMetrics()
|
||||
windowManager.defaultDisplay.getRealMetrics(displayMetrics)
|
||||
displayMetrics.widthPixels
|
||||
}
|
||||
else -> throw Exception("not support")
|
||||
}
|
||||
if (bitmap.width > maxWidth) {
|
||||
val newHeight = maxWidth * bitmap.height / bitmap.width
|
||||
val newImage = Bitmap.createScaledBitmap(bitmap, maxWidth, newHeight, true)
|
||||
return compressBitMap(newImage)
|
||||
}
|
||||
return compressBitMap(bitmap)
|
||||
}
|
||||
throw Exception("error pic")
|
||||
}
|
||||
|
||||
private fun compressBitMap(bitmap: Bitmap): ByteArray {
|
||||
val bos = ByteArrayOutputStream()
|
||||
bos.use { bos ->
|
||||
Log.d("BITMAP", bitmap.width.toString())
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos)
|
||||
}
|
||||
return bos.toByteArray()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -95,23 +95,30 @@ List<T> filteredList<T>(List<T> list, bool Function(T) filter) {
|
|||
|
||||
/// 创建一个单选对话框, 用户取消选择返回null, 否则返回所选内容
|
||||
Future<T?> chooseListDialog<T>(
|
||||
BuildContext context,
|
||||
String title,
|
||||
List<T> items,
|
||||
) async {
|
||||
BuildContext context, String title, List<T> items,
|
||||
{String? tips}) async {
|
||||
List<Widget> widgets = [];
|
||||
if (tips != null) {
|
||||
widgets.add(
|
||||
Container(
|
||||
padding: EdgeInsets.fromLTRB(15, 5, 15, 15),
|
||||
child: Text(tips),
|
||||
)
|
||||
);
|
||||
}
|
||||
widgets.addAll(items.map((e) => SimpleDialogOption(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(e);
|
||||
},
|
||||
child: Text('$e'),
|
||||
)));
|
||||
|
||||
return showDialog<T>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return SimpleDialog(
|
||||
title: Text(title),
|
||||
children: items
|
||||
.map((e) => SimpleDialogOption(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(e);
|
||||
},
|
||||
child: Text('$e'),
|
||||
))
|
||||
.toList(),
|
||||
children: widgets,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:pikapi/basic/Entities.dart';
|
||||
|
@ -607,4 +608,9 @@ class Method {
|
|||
"comicId": comicId,
|
||||
});
|
||||
}
|
||||
|
||||
/// 转化为PNG
|
||||
Future<Uint8List> convertToPNG(String path) async {
|
||||
return await _channel.invokeMethod("convertToPNG", {"path": path});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../Common.dart';
|
||||
import '../Method.dart';
|
||||
|
||||
const _propertyName = "convertToPNG";
|
||||
var _convertToPNG = false;
|
||||
|
||||
Future initConvertToPNG() async {
|
||||
if (Platform.isAndroid) {
|
||||
_convertToPNG =
|
||||
(await method.loadProperty(_propertyName, "false")) == "true";
|
||||
}
|
||||
}
|
||||
|
||||
bool convertToPNG() {
|
||||
return _convertToPNG;
|
||||
}
|
||||
|
||||
Future<void> _chooseConvertToPNGSetting(BuildContext context) async {
|
||||
String? result = await chooseListDialog<String>(context, "超大图片缩放", ["是", "否"],
|
||||
tips: "会增加耗电\n可以解决部分漫画崩溃的问题");
|
||||
if (result != null) {
|
||||
var target = result == "是";
|
||||
await method.saveProperty(_propertyName, "$target");
|
||||
_convertToPNG = target;
|
||||
}
|
||||
}
|
||||
|
||||
Widget convertToPNGSetting() {
|
||||
if (Platform.isAndroid) {
|
||||
return StatefulBuilder(
|
||||
builder: (BuildContext context, void Function(void Function()) setState) {
|
||||
return ListTile(
|
||||
title: Text("读取到超大图片时进行缩放(防止崩溃)"),
|
||||
subtitle: Text(_convertToPNG ? "是" : "否"),
|
||||
onTap: () async {
|
||||
await _chooseConvertToPNGSetting(context);
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
return Container();
|
||||
}
|
|
@ -6,6 +6,7 @@ import 'package:pikapi/basic/config/AutoClean.dart';
|
|||
import 'package:pikapi/basic/config/AutoFullScreen.dart';
|
||||
import 'package:pikapi/basic/config/ChooserRoot.dart';
|
||||
import 'package:pikapi/basic/config/ContentFailedReloadAction.dart';
|
||||
import 'package:pikapi/basic/config/ConvertToPNG.dart';
|
||||
import 'package:pikapi/basic/config/DownloadAndExportPath.dart';
|
||||
import 'package:pikapi/basic/config/DownloadThreadCount.dart';
|
||||
import 'package:pikapi/basic/config/FullScreenAction.dart';
|
||||
|
@ -67,6 +68,7 @@ class _InitScreenState extends State<InitScreen> {
|
|||
await initDownloadAndExportPath();
|
||||
await initAndroidSecureFlag();
|
||||
await initDownloadThreadCount();
|
||||
await initConvertToPNG();
|
||||
// 登录, 如果token失效重新登录, 网络不好的时候可能需要1分钟
|
||||
if (await method.preLogin()) {
|
||||
// 如果token或username+password有效则直接进入登录好的界面
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'package:pikapi/basic/config/AutoClean.dart';
|
|||
import 'package:pikapi/basic/config/AutoFullScreen.dart';
|
||||
import 'package:pikapi/basic/config/ChooserRoot.dart';
|
||||
import 'package:pikapi/basic/config/ContentFailedReloadAction.dart';
|
||||
import 'package:pikapi/basic/config/ConvertToPNG.dart';
|
||||
import 'package:pikapi/basic/config/DownloadAndExportPath.dart';
|
||||
import 'package:pikapi/basic/config/DownloadThreadCount.dart';
|
||||
import 'package:pikapi/basic/config/FullScreenAction.dart';
|
||||
|
@ -37,6 +38,7 @@ class SettingsScreen extends StatelessWidget {
|
|||
NetworkSetting(),
|
||||
Divider(),
|
||||
qualitySetting(),
|
||||
convertToPNGSetting(),
|
||||
readerTypeSetting(),
|
||||
readerDirectionSetting(),
|
||||
autoFullScreenSetting(),
|
||||
|
|
|
@ -1,20 +1,31 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:pikapi/basic/Method.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:pikapi/basic/config/ConvertToPNG.dart';
|
||||
import 'dart:io';
|
||||
import 'dart:ui' as ui show Codec;
|
||||
|
||||
Future<Uint8List> _loadImageFile(String path) {
|
||||
if (convertToPNG()) {
|
||||
return method.convertToPNG(path);
|
||||
}
|
||||
return File(path).readAsBytes();
|
||||
}
|
||||
|
||||
// 从本地加载图片
|
||||
class ResourceFileImageProvider extends ImageProvider<ResourceFileImageProvider> {
|
||||
class ResourceFileImageProvider
|
||||
extends ImageProvider<ResourceFileImageProvider> {
|
||||
final String path;
|
||||
final double scale;
|
||||
|
||||
ResourceFileImageProvider(this.path, {this.scale = 1.0});
|
||||
|
||||
@override
|
||||
ImageStreamCompleter load(ResourceFileImageProvider key, DecoderCallback decode) {
|
||||
ImageStreamCompleter load(
|
||||
ResourceFileImageProvider key, DecoderCallback decode) {
|
||||
return MultiFrameImageStreamCompleter(
|
||||
codec: _loadAsync(key),
|
||||
scale: key.scale,
|
||||
|
@ -22,14 +33,15 @@ class ResourceFileImageProvider extends ImageProvider<ResourceFileImageProvider>
|
|||
}
|
||||
|
||||
@override
|
||||
Future<ResourceFileImageProvider> obtainKey(ImageConfiguration configuration) {
|
||||
Future<ResourceFileImageProvider> obtainKey(
|
||||
ImageConfiguration configuration) {
|
||||
return SynchronousFuture<ResourceFileImageProvider>(this);
|
||||
}
|
||||
|
||||
Future<ui.Codec> _loadAsync(ResourceFileImageProvider key) async {
|
||||
assert(key == this);
|
||||
return PaintingBinding.instance!
|
||||
.instantiateImageCodec(await File(path).readAsBytes());
|
||||
.instantiateImageCodec(await _loadImageFile(path));
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -96,7 +108,8 @@ class ResourceDownloadFileImageProvider
|
|||
}
|
||||
|
||||
// 从远端加载图片 暂时未使用 (现在都是先获取路径然后再通过file显示)
|
||||
class ResourceRemoteImageProvider extends ImageProvider<ResourceRemoteImageProvider> {
|
||||
class ResourceRemoteImageProvider
|
||||
extends ImageProvider<ResourceRemoteImageProvider> {
|
||||
final String fileServer;
|
||||
final String path;
|
||||
final double scale;
|
||||
|
@ -113,7 +126,8 @@ class ResourceRemoteImageProvider extends ImageProvider<ResourceRemoteImageProvi
|
|||
}
|
||||
|
||||
@override
|
||||
Future<ResourceRemoteImageProvider> obtainKey(ImageConfiguration configuration) {
|
||||
Future<ResourceRemoteImageProvider> obtainKey(
|
||||
ImageConfiguration configuration) {
|
||||
return SynchronousFuture<ResourceRemoteImageProvider>(this);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue