Skip to content

Commit

Permalink
resize large image
Browse files Browse the repository at this point in the history
  • Loading branch information
niuhuan committed Nov 6, 2021
1 parent 173c19d commit fdbce75
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 18 deletions.
36 changes: 36 additions & 0 deletions android/app/src/main/kotlin/niuhuan/pikapi/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -100,6 +103,7 @@ class MainActivity : FlutterActivity() {
// 获取可以迁移数据地址
"androidGetExtendDirs" -> androidGetExtendDirs()
"androidSecureFlag" -> androidSecureFlag(call.argument("flag")!!)
"convertToPNG" -> convertToPNG(call.argument("path")!!)
else -> {
notImplementedToken
}
Expand Down Expand Up @@ -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()
}


}
31 changes: 19 additions & 12 deletions lib/basic/Common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
},
);
Expand Down
6 changes: 6 additions & 0 deletions lib/basic/Method.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:typed_data';

import 'package:flutter/services.dart';
import 'package:pikapi/basic/Entities.dart';
Expand Down Expand Up @@ -607,4 +608,9 @@ class Method {
"comicId": comicId,
});
}

/// 转化为PNG
Future<Uint8List> convertToPNG(String path) async {
return await _channel.invokeMethod("convertToPNG", {"path": path});
}
}
48 changes: 48 additions & 0 deletions lib/basic/config/ConvertToPNG.dart
Original file line number Diff line number Diff line change
@@ -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();
}
2 changes: 2 additions & 0 deletions lib/screens/InitScreen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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有效则直接进入登录好的界面
Expand Down
2 changes: 2 additions & 0 deletions lib/screens/SettingsScreen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -37,6 +38,7 @@ class SettingsScreen extends StatelessWidget {
NetworkSetting(),
Divider(),
qualitySetting(),
convertToPNGSetting(),
readerTypeSetting(),
readerDirectionSetting(),
autoFullScreenSetting(),
Expand Down
26 changes: 20 additions & 6 deletions lib/screens/components/Images.dart
Original file line number Diff line number Diff line change
@@ -1,35 +1,47 @@
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,
);
}

@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
Expand Down Expand Up @@ -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;
Expand All @@ -113,7 +126,8 @@ class ResourceRemoteImageProvider extends ImageProvider<ResourceRemoteImageProvi
}

@override
Future<ResourceRemoteImageProvider> obtainKey(ImageConfiguration configuration) {
Future<ResourceRemoteImageProvider> obtainKey(
ImageConfiguration configuration) {
return SynchronousFuture<ResourceRemoteImageProvider>(this);
}

Expand Down

0 comments on commit fdbce75

Please sign in to comment.