i am working on a canvas drawing module in to it i need to erase user drawing
basic funda is
adding image in canvas
user can draw on image using pencil (i.e black color) now user can erase this black color on erase function (i.e clear user drawing )
my try is :
case PaintMode.eraser:
print('eraser**** _image Painter!!!*'); // print(_painter.color)
// canvas.saveLayer(Offset.zero & size, Paint());1
var _painterTemp = _painter!
// ..color = Colors.transparent.withOpacity(0.8)//also tried it
..strokeCap = StrokeCap.round
..blendMode = BlendMode.clear;
for (var i = 0; i < _offset!.length - 1; i++) {
if (_offset[i] != null && _offset[i + 1] != null) {
final _path = Path()
..moveTo(_offset[i]!.dx, _offset[i]!.dy)
..lineTo(_offset[i + 1]!.dx, _offset[i + 1]!.dy);
} else if (_offset[i] != null && _offset[i + 1] == null) {
canvas.drawPoints(PointMode.points, [_offset[i]!], _painterTemp);
}
}
// canvas.restore();//1
break
but i am getting black lines only
took reference from
https://github.com/yellowQ-software/yellowQ-Flutter-Image-Painter
edit-----
i've edited like
case PaintMode.eraser:
print('eraser**** _image Painter!!!*');
var _painterTemp = _painter!
..color = Colors.transparent
..blendMode = BlendMode.clear; //srcOver
for (var i = 0; i < _offset!.length - 1; i++) {
if (_offset[i] != null && _offset[i + 1] != null) {
final _path = Path()
..moveTo(_offset[i]!.dx, _offset[i]!.dy)
..lineTo(_offset[i + 1]!.dx, _offset[i + 1]!.dy);
canvas.drawPath(_path, _painter);
canvas.drawPath(_path, _painterTemp);
canvas.saveLayer(Offset.zero & size, Paint());
} else if (_offset[i] != null && _offset[i + 1] == null) {
canvas.drawPoints(PointMode.points, [_offset[i]!], _painter);
canvas.drawPoints(PointMode.points, [_offset[i]!], _painterTemp);
}
// canvas.saveLayer(Offset.zero & size, Paint()); //1
}
// canvas.restore();//1
break;
but not working but getting custom painter called canvas.save() or canvas.saveLayer() at least 773 more times than it called canvas.restore().
any help?
Related
I am trying to add skin tone to an image in flutter canvas.
I've used the following code before to apply chroma by altering pixels when I load the image:
static Future<ui.Image> applyChromaToImage(ui.Image image, String pathForImage, RGBPixel chromaToApply, {RGBPixel previousChromaColor}) async
{
List<ChromaPointRange> chromaPoints = processChromaImageBytes(
image, imgBytes);
for(ChromaPointRange rnge in chromaPoints)
{
for(int y = rnge.yValStart; y <= rnge.yValEnd; y++)
{
RGBPixel currentPixel = RGBPixel.generatePixelFromImagePos(imgBytes, image.width, rnge.xVal, y);
//replace current pixel with skin tone
RGBPixel newPixl = currentPixel.mergeSkinTonePixel(chromaToApply, previousChromaColor);
imgBytes.setUint32((y * image.width + rnge.xVal) * 4, newPixl.getHex());
}
}
final Completer<ui.Image> imageCompleter = new Completer();
//looks the endian format doesn't get set right here
ui.PixelFormat formatToUse = Endian.host == Endian.little ? ui.PixelFormat.rgba8888 : ui.PixelFormat.bgra8888;
ui.decodeImageFromPixels(
imgBytes.buffer.asUint8List(),
image.width,
image.height,
formatToUse,
(ui.Image result) {
imageCompleter.complete(result);
// use your result image
},
);
//return image;
return await imageCompleter.future;
}
static List<ChromaPointRange> processChromaImageBytes(ui.Image image, ByteData imgBytes)
{
List<ChromaPointRange> chromaPoints = [];
ChromaPointRange currentPoints = null;
for(int x = 0; x < image.width; x = x + 1)
{
for(int y = 0; y < image.height; y = y + 1)
{
RGBPixel currentPixel = RGBPixel.generatePixelFromImagePos(imgBytes, image.width, x, y);
if(currentPixel.isSkinTonePixel())
{
if(currentPoints == null)
{
currentPoints = ChromaPointRange.fromEmpty();
currentPoints.xVal = x;
currentPoints.yValStart = y;
}
}
else if(currentPoints != null)
{
currentPoints.yValEnd = y - 1;
chromaPoints.add(currentPoints);
currentPoints = null;
}
}
if(currentPoints != null)
{
currentPoints.yValEnd = image.height - 1;
chromaPoints.add(currentPoints);
currentPoints = null;
}
}
return chromaPoints;
}
which basically checks every pixel in the image to see if it's within a range of the target color ( with is RGB 0, 255, 0), then adjusts the pixel if it is. This works, but takes a really long time ~ 3 seconds for a 1920 x 1080 image.
The end result is that I want to paint the image to a canvas with a skin tone applied. I've tried a different strategy, by painting the color underneath the image, and then trying to mask out that color from the image using canvas color filters. This is 1000% faster, but doesn't quite work.
Here is the code:
renderSprite(Canvas canvasToRender, Offset offsetToRender)
{
Paint imgPaint = new Paint();
if(chromaToApply != null)
{
Paint chromaPaint = new Paint();
chromaPaint.colorFilter = ColorFilter.mode(Color.fromRGBO(chromaToApply.redVal, chromaToApply.greenVal, chromaToApply.blueVal, 1), BlendMode.modulate);
canvasToRender.drawImage(spriteImage, offsetToRender, chromaPaint);
imgPaint.colorFilter = ColorFilter.mode(Color.fromRGBO(0, 255, 0, 1), BlendMode.dstOut);
}
if(spriteImage != null)
canvasToRender.drawImage(spriteImage, offsetToRender, imgPaint);
}
Here is the image that is painted underneath
Here is the image that is painted ontop
So I'm trying to mask out the green so the tan skin tone shows through on specific parts of the image.
I can't seem to find any combination of ColorFilter or anything else that will mask out the green color for me from the canvas. Any suggestions?
I am developing a flutter application. In that am using Paint to draw lines. In free hand line the point header is correctly displaying when we draw a straight line. But when we rotate the line, the point header position got misplaced.
case PaintMode.freeStyleArrow:
for (var i = 0; i < _offset!.length - 1; i++) {
if (_offset[i] != null && _offset[i + 1] != null) {
final _path = Path()
..moveTo(_offset[i]!.dx, _offset[i]!.dy)
..lineTo(_offset[i + 1]!.dx, _offset[i + 1]!.dy);
canvas.drawPath(_path, _painter!..strokeCap = StrokeCap.round);
paintsFreestyleArrow(canvas,_offset[0]!, _offset[_offset.length - 2]!, _painter, size);
}
else if (_offset[i] != null && _offset[i + 1] == null) {
canvas.drawPoints(PointMode.points, [_offset[i]!],
_painter!);
}}
break;
roadTopStartX = Random.Range(5, 10); // Path's x position which creation begins
roadTopStartY = Random.Range(8, 12); // Path's y position which creation begins
roadTopLength = Random.Range(4, 9); // Path's length
for (int i = 0; i < roadTopLength; i++)
{
GameObject tile = GameObject.Find("Tile" + (roadTopStartX + i) + " " + roadTopStartY); //I created grid whose tile's name Tile X Y like Tile 0 0
GameObject road = Instantiate(roadPrefab, tile.transform.position, tile.transform.rotation);
road.name = "Road"+ " " + (roadTopStartX + i) + " " + roadTopStartY;
roads.Add(road);
}
It is how i create random path on 2d grid, do you know better solution because when thinks become more complex gameobject.find becomes suffer for me
i found a more reliable solution: (thanks to derHugo)
public GameObject findTile(int x,int y)
{
GameObject findTilex = GameObject.Find("Tile" + (tileX + x) + " " +
(tileY + y));
return findTilex;
}
Example how i get neighbour tiles
public void getNeighbours()
{
if (findTile(0, 1) != null)
{
upper = findTile(0, 1);
}
if (findTile(0, -1) != null)
{
below = findTile(0, -1);
}
if (findTile(1, 0) != null)
{
right = findTile(1, 0);
}
if (findTile(-1, 0) != null)
{
left = findTile(-1, 0);
}
if (findTile(1, 1) != null)
{
rightCrossTop = findTile(1, 1);
}
if (findTile(1, -1) != null)
{
rightCrossUnder = findTile(1, -1);
}
if (findTile(-1, 1) != null)
{
leftCrossTop = findTile(-1, 1);
}
if (findTile(-1, -1) != null)
{
leftCrossBottom = findTile(-1, -1);
}
}
I have created a custom paint drawer, which works well, but I liked to create custom brush types like a calligraphy brush, crayon brush, etc...
class DrawingPainter extends CustomPainter {
DrawingPainter({this.pointsList}) : super();
List<DrawingPoints> pointsList;
#override
void paint(Canvas canvas, Size size) {
for (int i = 0; i < pointsList.length - 1; i++) {
if (pointsList[i] != null) {
canvas.saveLayer(Offset.zero & size, Paint());
if (shouldDrawLine(i)) {
canvas.drawLine(pointsList[i].points, pointsList[i + 1].points, pointsList[i].paint);
} else if (shouldDrawPoint(i)) {
canvas.drawPoints(dartUI.PointMode.polygon, [pointsList[i].points,pointsList[i].points], pointsList[i].paint);
}
canvas.restore();
}
}
}
bool shouldDrawPoint(int i) =>
pointsList[i] != null && pointsList.length > i + 1 && pointsList[i + 1] == null;
bool shouldDrawLine(int i) =>
pointsList[i] != null && pointsList.length > i + 1 && pointsList[i + 1] != null;
#override
bool shouldRepaint(DrawingPainter oldDelegate) => true;
}
this is my code for the drawing. How can I modify the brush drawing pattern like those types of brushes?
I'm trying to process the camera image input in Flutter but I can't seem to get it to work.
I have a listener on the camera feed that launch a function that is supposed to process the images btu everytime I end up doing anything in the function the program interface freeze. My guess is that I have too many frames to process and thus the program freezes but I have no idea how to ignore old frames.
Here is my code so far
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:image/image.dart' as imglib;
import 'package:flutter/scheduler.dart';
import 'user_data_container.dart';
class Camera extends StatefulWidget {
Camera();
#override
_CameraState createState() => new _CameraState();
}
class _CameraState extends State<Camera> {
CameraController controller;
bool isDetecting = false;
double redavg;
imglib.Image last_image;
int count = 0;
Image _image_display;
#override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) {
_initializeApp();
});
}
void _initializeApp() async {
var cameras = UserDataContainer.of(context).data.cameras;
if (cameras == null || cameras.length < 1) {
print('No camera is found');
} else {
controller = new CameraController(
cameras[0],
ResolutionPreset.high,
);
controller.initialize().then((_) {
if (!mounted) {
return;
}
setState(() {});
controller.startImageStream((CameraImage img) async {
print(count.toString() + "Stream. detecting: " + isDetecting.toString());
if (!isDetecting) {
isDetecting = true;
int startTime = new DateTime.now().millisecondsSinceEpoch;
_processCameraImage(img);
// int endTime = new DateTime.now().millisecondsSinceEpoch;
// print("Detection done in " + (endTime - startTime).toString());
// isDetecting = false;
}
else{
print("It's detecting");
}
});
});
}
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
if (controller == null || !controller.value.isInitialized) {
return Container();
}
return
Scaffold(
body: Column(
children: <Widget>[
Text("red: "+ redavg.toString() + " "+ count.toString()),
Expanded(child: last_image == null ? Container() : _image_display),
],
),
);
// );
}
void _processCameraImage(CameraImage image) async {
count = count +1;
print("p: " + image.planes.length.toString());
// if (isDetecting) {
// print("Already detecting;");
// return;
// }
// else{
// isDetecting = true;
try {
await processFrame(image);
} catch (e) {
// await handleExepction(e)
} finally {
print("Done detecting :)");
// isDetecting = false;
}
// }
}
void processFrame(CameraImage image) async {
print ("convert");
imglib.Image convertedImage = await _convertCameraImage(image);
last_image = convertedImage;
imglib.PngEncoder pngEncoder = new imglib.PngEncoder(level: 0, filter: 0);
// Convert to png
List<int> png = pngEncoder.encodeImage(last_image);
_image_display = Image.memory(png);
// var colors = colorAverage(convertedImage);
setState(() {
// redavg = colors[2];
// print(colors.toString());
});
}
imglib.Image _convertCameraImage(CameraImage image) {
int width = image.width;
int height = image.height;
var img = imglib.Image(image.planes[0].bytesPerRow, height); // Create Image buffer
const int hexFF = 0xFF000000;
final int uvyButtonStride = image.planes[1].bytesPerRow;
final int uvPixelStride = image.planes[1].bytesPerPixel;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
final int uvIndex =
uvPixelStride * (x / 2).floor() + uvyButtonStride * (y / 2).floor();
final int index = y * width + x;
final yp = image.planes[0].bytes[index];
final up = image.planes[1].bytes[uvIndex];
final vp = image.planes[2].bytes[uvIndex];
// Calculate pixel color
int r = (yp + vp * 1436 / 1024 - 179).round().clamp(0, 255);
int g = (yp - up * 46549 / 131072 + 44 - vp * 93604 / 131072 + 91)
.round()
.clamp(0, 255);
int b = (yp + up * 1814 / 1024 - 227).round().clamp(0, 255);
// color: 0x FF FF FF FF
// A B G R
img.data[index] = hexFF | (b << 16) | (g << 8) | r;
}
}
// Rotate 90 degrees to upright
// var img1 = imglib.copyRotate(img, 90);
imglib.PngEncoder pngEncoder = new imglib.PngEncoder(level: 0, filter: 0);
// Convert to png
List<int> png = pngEncoder.encodeImage(img);
_image_display = Image.memory(png);
return img;
}
}