我试图在Flutter中画出与Image
的两个Canvas
之差,而我真正的意思是我只想画出不同之处(您可以在下面看到我希望看到的内容)。
它需要同步发生,即在paint
通话中!
在此答案中,两个图像分别称为imageA
和imageB
。
canvas.drawImage(
imageA,
offset,
Paint(),
);
canvas.drawImage(
imageB,
offset,
Paint()
..blendMode = BlendMode.difference,
);
imageA
和imageB
分别绘制。
imageA
与imageB
之间的差异。您可以看到我期望看到的 left ,以及使用 right 上面的代码得到的实际结果。
左侧图像中的灰色/白色背景颜色应该是透明,即在两个图像相同的情况下,alpha应该等于零。此外,expected
图像中红色正方形周围的黑色阴影是伪影,而不是我在应用程序中想要的。
我并不是说expected
的图像就是我期望从BlendMode.difference
获得的图像,但是我想知道如何获得expected
的输出,即我仅能实现绘制两个图像之间的区别。
这意味着我只想渲染imageB
中的与imageA
不同的像素,否则不渲染任何东西,即alpha值0。
我将再次尝试更清楚地解释它:
如果颜色相同,则绘制透明像素(删除源像素)。
如果颜色不同,请绘制目标像素。
答案 0 :(得分:2)
当前抖动不支持您要执行的操作。根据我的研究,Flutter在本机级别上处理画布油漆。您可以从以下代码片段中看到这一点:
/// Fills the canvas with the given [Paint].
///
/// To fill the canvas with a solid color and blend mode, consider
/// [drawColor] instead.
void drawPaint(Paint paint) {
assert(paint != null);
_drawPaint(paint._objects, paint._data);
}
void _drawPaint(List<dynamic> paintObjects, ByteData paintData) native 'Canvas_drawPaint';
这来自最新版本的flutter(1.9.2)的canvas实现。如果没有异步代码,您可以执行的任何像素操作都必须使用抖动BlendMode
或ImageFilter
来完成,它们缺乏个性化的遮罩支持。
要做您打算做的事情的唯一方法是实际实现本机代码,我真的不认为您想做。我花了最后6个小时来弄清楚官方flutter API文档中没有什么可以做您想要做的事情,但是问题是,flutter中的大多数事情都是旨在实现异步的。
如果您打算冒险以本机方式实现此功能,为什么不要求将其实现为官方Flutter github中的功能?我的意思是说,要做很多工作,不要让它浪费。
仅仅因为我不想空手而归,所以我用image lib编写了您想要的东西,但是使用了async:
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:image/image.dart' as im;
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MyApp();
}
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ByteBuffer imgBytes;
double canvasHeight = 500;
double canvasWidth = 300;
ui.PictureRecorder recorder1;
Canvas canvas1;
ui.PictureRecorder recorder2;
Canvas canvas2;
ui.Picture picture1;
ui.Picture picture2;
@override
void initState() {
// Canvas1
recorder1 = ui.PictureRecorder();
canvas1 = Canvas(recorder1,
Rect.fromPoints(Offset(0.0, 0.0), Offset(canvasWidth, canvasHeight)));
// Draw red background
var paint = Paint();
paint.style = PaintingStyle.fill;
paint.color = Colors.red;
canvas1.drawRect(Offset(0, 0) & Size(canvasWidth, canvasHeight), paint);
// Draw black square
paint = Paint();
paint.style = PaintingStyle.fill;
paint.color = Colors.black;
canvas1.drawRect(
Offset(canvasWidth * 0.5 / 4, canvasHeight / 2) &
Size(canvasHeight / 8, canvasHeight / 8),
paint);
picture1 = recorder1.endRecording();
// Canvas2
recorder2 = ui.PictureRecorder();
canvas2 = Canvas(recorder2,
Rect.fromPoints(Offset(0.0, 0.0), Offset(canvasWidth, canvasHeight)));
// Draw red background
paint = Paint();
paint.style = PaintingStyle.fill;
paint.color = Colors.red;
canvas2.drawRect(Offset(0, 0) & Size(canvasWidth, canvasHeight), paint);
// Draw blue square
paint = Paint();
paint.style = PaintingStyle.fill;
paint.color = Colors.blue;
canvas2.drawRect(
Offset(canvasWidth * 2.5 / 4, canvasHeight / 2) &
Size(canvasHeight / 8, canvasHeight / 8),
paint);
picture2 = recorder2.endRecording();
(() async {
ui.Image img1 =
await picture1.toImage(canvasWidth.toInt(), canvasHeight.toInt());
ByteData byteData1 =
await img1.toByteData(format: ui.ImageByteFormat.png);
im.Image decodedPng1 = im.decodePng(byteData1.buffer.asUint8List());
ui.Image img2 =
await picture2.toImage(canvasWidth.toInt(), canvasHeight.toInt());
ByteData byteData2 =
await img2.toByteData(format: ui.ImageByteFormat.png);
im.Image decodedPng2 = im.decodePng(byteData2.buffer.asUint8List());
for (int i = 0; i < canvasHeight; i += 1) {
for (int j = 0; j < canvasWidth; j += 1) {
int pixel1 = decodedPng1.getPixel(j, i);
int r1 = pixel1 & 0xff;
int g1 = (pixel1 >> 8) & 0xff;
int b1 = (pixel1 >> 16) & 0xff;
int a1 = (pixel1 >> 24) & 0xff;
int pixel2 = decodedPng2.getPixel(j, i);
int r2 = pixel2 & 0xff;
int g2 = (pixel2 >> 8) & 0xff;
int b2 = (pixel2 >> 16) & 0xff;
int a2 = (pixel2 >> 24) & 0xff;
if (r1 == r2 && g1 == g2 && b1 == b2 && a1 == a2) {
// Draw black transparent
decodedPng2.setPixel(j, i, 0);
} else {
// Leave as is
}
}
}
setState(() {
imgBytes = Uint8List.fromList(im.encodePng(decodedPng2)).buffer;
});
})();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Dummy'),
),
body: imgBytes != null
? Center(
child: Image.memory(
Uint8List.view(imgBytes),
width: canvasWidth,
height: canvasHeight,
),
)
: Container(),
),
);
}
}