Skip to content

Instantly share code, notes, and snippets.

@roipeker
Last active August 27, 2024 07:51
Show Gist options
  • Save roipeker/9315aa25301f5c0362caaebd15876c2f to your computer and use it in GitHub Desktop.
Save roipeker/9315aa25301f5c0362caaebd15876c2f to your computer and use it in GitHub Desktop.
Basic image pixel color detection in Flutter (supports screenshots of the widget tree)
//////////////////////////////
//
// 2019, roipeker.com
// screencast - demo simple image:
// https://youtu.be/EJyRH4_pY8I
//
// screencast - demo snapshot:
// https://youtu.be/-LxPcL7T61E
//
//////////////////////////////
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:image/image.dart' as img;
import 'package:flutter/services.dart' show rootBundle;
class ColorPickerWidget extends StatefulWidget {
@override
_ColorPickerWidgetState createState() => _ColorPickerWidgetState();
}
class _ColorPickerWidgetState extends State<ColorPickerWidget> {
String imagePath = 'assets/images/santorini.jpg';
GlobalKey imageKey = GlobalKey();
GlobalKey paintKey = GlobalKey();
// CHANGE THIS FLAG TO TEST BASIC IMAGE, AND SNAPSHOT.
bool useSnapshot = true;
// based on useSnapshot=true ? paintKey : imageKey ;
// this key is used in this example to keep the code shorter.
GlobalKey currentKey;
final StreamController<Color> _stateController = StreamController<Color>();
img.Image photo;
@override
void initState() {
currentKey = useSnapshot ? paintKey : imageKey;
super.initState();
}
@override
Widget build(BuildContext context) {
final String title = useSnapshot ? "snapshot" : "basic";
return Scaffold(
appBar: AppBar(title: Text("Color picker $title")),
body: StreamBuilder(
initialData: Colors.green[500],
stream: _stateController.stream,
builder: (buildContext, snapshot) {
Color selectedColor = snapshot.data ?? Colors.green;
return Stack(
children: <Widget>[
RepaintBoundary(
key: paintKey,
child: GestureDetector(
onPanDown: (details) {
searchPixel(details.globalPosition);
},
onPanUpdate: (details) {
searchPixel(details.globalPosition);
},
child: Center(
child: Image.asset(
imagePath,
key: imageKey,
//color: Colors.red,
//colorBlendMode: BlendMode.hue,
//alignment: Alignment.bottomRight,
fit: BoxFit.none,
//scale: .8,
),
),
),
),
Container(
margin: EdgeInsets.all(70),
width: 50,
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: selectedColor,
border: Border.all(width: 2.0, color: Colors.white),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 4,
offset: Offset(0, 2))
]),
),
Positioned(
child: Text('${selectedColor}',
style: TextStyle(
color: Colors.white,
backgroundColor: Colors.black54)),
left: 114,
top: 95,
),
],
);
}),
);
}
void searchPixel(Offset globalPosition) async {
if (photo == null) {
await (useSnapshot ? loadSnapshotBytes() : loadImageBundleBytes());
}
_calculatePixel(globalPosition);
}
void _calculatePixel(Offset globalPosition) {
RenderBox box = currentKey.currentContext.findRenderObject();
Offset localPosition = box.globalToLocal(globalPosition);
double px = localPosition.dx;
double py = localPosition.dy;
if (!useSnapshot) {
double widgetScale = box.size.width / photo.width;
print(py);
px = (px / widgetScale);
py = (py / widgetScale);
}
int pixel32 = photo.getPixelSafe(px.toInt(), py.toInt());
int hex = abgrToArgb(pixel32);
_stateController.add(Color(hex));
}
Future<void> loadImageBundleBytes() async {
ByteData imageBytes = await rootBundle.load(imagePath);
setImageBytes(imageBytes);
}
Future<void> loadSnapshotBytes() async {
RenderRepaintBoundary boxPaint = paintKey.currentContext.findRenderObject();
ui.Image capture = await boxPaint.toImage();
ByteData imageBytes =
await capture.toByteData(format: ui.ImageByteFormat.png);
setImageBytes(imageBytes);
capture.dispose();
}
void setImageBytes(ByteData imageBytes) {
List<int> values = imageBytes.buffer.asUint8List();
photo = null;
photo = img.decodeImage(values);
}
}
// image lib uses uses KML color format, convert #AABBGGRR to regular #AARRGGBB
int abgrToArgb(int argbColor) {
int r = (argbColor >> 16) & 0xFF;
int b = argbColor & 0xFF;
return (argbColor & 0xFF00FF00) | (b << 16) | r;
}
@shubham64mehta
Copy link

will this code work ,if i want to extract the color from the images i am clicking using the camera

@gabrielrodrigodesbesell

will this code work ,if i want to extract the color from the images i am clicking using the camera

@shubham64mehta did you implement color detection with images provided from the camera, in real time?

@huihliu
Copy link

huihliu commented Nov 14, 2020

Thanks, this code helped me a lot. But I just found it does not work on flutter web support. Wonder if there is an alternative ?

@roipeker
Copy link
Author

@huihlui, it should work. image package is supported in web, and the rest is just Flutter's API.
last time i tried to access pixel data, I managed to do it from raw Bytes (without the need of image package).
check graphx readme, under DEMOS, color spectrum ⇢.
Just found the gists of that demo... check getPixelColor()

@rezaUniqe
Copy link

@roipeker,hi .toimage() is not supported in flutter web

@roipeker
Copy link
Author

Hi @roipeker, could you explain how this works? Or even just explain how I can refine (make smaller) the radius of the pointer which determines what area we get the colour for?

It's been a while since I did it, so not sure if what i'm saying is valid ... seems like basically, maps the global coordinates to the widget's local coordinates.... therefore any transform applied to the widget (like scaling it down to fit the screen) will modify the pixel coordinates. So, if you want more precision, make the Image widget bigger.

If you useSnapshot it will take the RepaintBoundary, so it will create a "screenshot" of the current child's render (so, the output image bytes will be as big as the widget). If you take an iPhoneX, and the Image widget takes the entire width of the screen, no paddings, that'd be a max of 375 pixels (at 1x) in the image width, that's using the RepaintBoundary approach ...

In the other case, where the image bytes are taken directly from the asset, this is where we need to map pointer (touch/mouse) coordinates to the image size coordinates, here you can gain in precision accuracy... but you will be constrained by the pointers/screen size relation to select a color.

So, only way to make a color picker more precise is to have a "zoom in" functionality in which case, both approaches will work pretty much the same.

Sorry for the long response...

@roipeker
Copy link
Author

@roipeker,hi .toimage() is not supported in flutter web

Probably not, the common flutter web output is pretty limited. Works on the wasm (skia) target though. Like this displacement map sample...

@leandroderezende
Copy link

@shubham64mehta and @gabrielrodrigodesbesell, did you get pick color pixel from camera?

I would like do to create a APP that turn on de camera, put a point on screen and, when i move de camera, it pick the color on this point in real time. I don't like to take a picture and save, because lose routine time.

Do anyone suggest a code?

Thanks.

@roipeker
Copy link
Author

@leandroderezende, live feed from camera usually uses a native Texture in flutter, so u don't have access to the Image memory on the Dart side, so I believe is currently, not impossible.

@leandroderezende
Copy link

@leandroderezende, live feed from camera usually uses a native Texture in flutter, so u don't have access to the Image memory on the Dart side, so I believe is currently, not impossible.

@roipeker, What do you think about take a screenshot and send the image to pick the pixel color? Could it be faster than salve this photoand after, read it? Tks...

@roipeker
Copy link
Author

You need an Image in Dart and covert it to ByteData with rawRgba format… knowing the width/height of the image, get the bytes as List and u will know how to loop through it. Each value in the list is a component for the color channel (from 0-255), list[0] red pixel 1, list[1] green pixel 1, list[2] blue pixel 1, list[3] alpha pixel 1… so each 4 values u can make a Color()… if u know the image width and height, u can directly access the indices that u have in the list like if it was a grid made of columns and rows … so basically make a method for getPixel(x,y) and calculate what would be the index in that cartesian coordinate.
I hope the explanation makes sense, as its the only approach on the Dart side I know of… otherwise u must get a package to do this on the native side (if theres any)

@leandroderezende
Copy link

You need an Image in Dart and covert it to ByteData with rawRgba format… knowing the width/height of the image, get the bytes as List and u will know how to loop through it. Each value in the list is a component for the color channel (from 0-255), list[0] red pixel 1, list[1] green pixel 1, list[2] blue pixel 1, list[3] alpha pixel 1… so each 4 values u can make a Color()… if u know the image width and height, u can directly access the indices that u have in the list like if it was a grid made of columns and rows … so basically make a method for getPixel(x,y) and calculate what would be the index in that cartesian coordinate.
I hope the explanation makes sense, as its the only approach on the Dart side I know of… otherwise u must get a package to do this on the native side (if theres any)

@roipeker, yes... your explanation makes sense!! I am trying time to work in this theme. Really, I am learn Flutter now. If I have some news, I post here. Thanks!

@roipeker
Copy link
Author

Here’s a quick sample of the approach i commented before:
https://gist.github.com/roipeker/98fadef42c470227f9e72ab0ea18ec74

Live demo: https://roi-gradient-picker.surge.sh/

@Ghanam330
Copy link

https://i.stack.imgur.com/miTWO.jpg*I Sorry can you help me? want to take a picture like this analysis paper, all the colors change. I want when I shoot the picture, I can see the degrees of each RGB color and compare them with my degrees, and according to each degree I look to diagnose a specific disease. The 10 colors that are in any picture I take are like this picture
thanks.

@roipeker
Copy link
Author

roipeker commented Mar 1, 2022

@Ghanam330 i doubt u can reliably do any diagnosis like that. The camera, environment and lighting conditions will change, even the surface where u show the color samples. I think some image analysis AI is more appropriate for the tasks to at least evaluate the false positives u might encounter.

@Ghanam330
Copy link

Ghanam330 commented Mar 1, 2022

@roipeker
I apologize for disturbing you.
I will go to something from AI, but what is the idea that I can link to, that I send the image, for example, to the API that does the analysis and then returns the data to me again.
And is there a way, for example, that I put it in the web view of the application?
Thanks for your reply, and sorry if I bothered you

@roipeker
Copy link
Author

roipeker commented Mar 2, 2022

@Ghanam330 to be honest, its beyond my knowledge. If you rely on some server implementation, i guess you will just post the image to some service and get some data results you can use. In any way, at Flutter level, bitmap manipulation is a no-go for now. No apis exposed natively from skia, and u have to rely on 3rd psrty packages like inage to do some pixel level manipulation, which is VERY slow. I think the ifea in your case is to find the boundries of the sample colors and detect a pattern, like the qr code does, so u can get the bounds of pixels relevant to color analisis. Then maybe apply some blur filter and level correction to normalize the noise, and maybe reduce the color palette of the images (like gif does)… at least that way u might have an easier way to compare colors. Reducing the image to a very small size can also average the dominant color. But sadly, I have no input for you beyond those ideas.

@gputhige
Copy link

I am trying to run this code but get these errors as given below. I have also checked on your youtube video on this and am not sure why these errors are throwing up. Not sure if its something to do with the Null check..

Line 47: Color selectedColor = snapshot.data ?? Colors.green;
Error: A value of type 'Object' can't be assigned to a variable of type 'Color'.
Try changing the type of the variable, or casting the right-hand type to 'Color'.dartinvalid_assignment

Line 109: RenderBox box = currentKey.currentContext.findRenderObject();
Error: A value of type 'RenderObject?' can't be assigned to a variable of type 'RenderBox'.
Try changing the type of the variable, or casting the right-hand type to 'RenderBox'.dart

Line 134: RenderRepaintBoundary boxPaint = paintKey.currentContext.findRenderObject();
Error: A value of type 'RenderObject?' can't be assigned to a variable of type 'RenderRepaintBoundary'.
Try changing the type of the variable, or casting the right-hand type to 'RenderRepaintBoundary'.

@khurram40
Copy link

Updated Code Without error.

import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:image/image.dart' as img;
import 'package:kaizen/constants/asset_paths.dart';

class ColorPickerWidget extends StatefulWidget {
@OverRide
_ColorPickerWidgetState createState() => _ColorPickerWidgetState();

}

class _ColorPickerWidgetState extends State {
String imagePath ="assests/images/your_image.png";
GlobalKey imageKey = GlobalKey();
GlobalKey paintKey = GlobalKey();

// CHANGE THIS FLAG TO TEST BASIC IMAGE, AND SNAPSHOT.
bool useSnapshot = true;

// based on useSnapshot=true ? paintKey : imageKey ;
// this key is used in this example to keep the code shorter.
late GlobalKey currentKey;

final StreamController _stateController = StreamController();
img.Image? photo;

@OverRide
void initState() {
currentKey = useSnapshot ? paintKey : imageKey;
super.initState();
}

@OverRide
Widget build(BuildContext context) {
final String title = useSnapshot ? "snapshot" : "basic";
return Scaffold(
appBar: AppBar(title: Text("Color picker $title")),
body: StreamBuilder(
initialData: Colors.green[500],
stream: _stateController.stream,
builder: (buildContext, snapshot) {
Color selectedColor = (snapshot.data ?? Colors.green) as Color;
return Stack(
children: [
RepaintBoundary(
key: paintKey,
child: GestureDetector(
onPanDown: (details) {
searchPixel(details.globalPosition);
},
onPanUpdate: (details) {
searchPixel(details.globalPosition);
},
child: Image.asset(
imagePath,
key: imageKey,
height: 500,
width: 500,
//color: Colors.red,
//colorBlendMode: BlendMode.hue,
//alignment: Alignment.bottomRight,
fit: BoxFit.cover,
//scale: .8,
),
),
),
Container(
margin: EdgeInsets.all(70),
width: 50,
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: selectedColor,
border: Border.all(width: 2.0, color: Colors.white),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 4,
offset: Offset(0, 2))
]),
),
Positioned(
child: Text('${selectedColor}',
style: TextStyle(
color: Colors.white,
backgroundColor: Colors.black54)),
left: 114,
top: 95,
),
],
);
}),
);
}

void searchPixel(Offset globalPosition) async {
if (photo == null) {
await (useSnapshot ? loadSnapshotBytes() : loadImageBundleBytes());
}
_calculatePixel(globalPosition);
}

void _calculatePixel(Offset globalPosition) {
RenderBox box = currentKey.currentContext?.findRenderObject() as RenderBox;
Offset localPosition = box.globalToLocal(globalPosition);

double px = localPosition.dx;
double py = localPosition.dy;

if (!useSnapshot) {
  double widgetScale = box.size.width / photo!.width;
  print(py);
  px = (px / widgetScale);
  py = (py / widgetScale);
}

int pixel32 = photo!.getPixelSafe(px.toInt(), py.toInt());
int hex = abgrToArgb(pixel32);

_stateController.add(Color(hex));

}

Future loadImageBundleBytes() async {
ByteData imageBytes = await rootBundle.load(imagePath);
setImageBytes(imageBytes);
}

Future loadSnapshotBytes() async {
RenderRepaintBoundary boxPaint =
paintKey.currentContext?.findRenderObject() as RenderRepaintBoundary;
ui.Image capture = await boxPaint.toImage();
ByteData? imageBytes =
await capture.toByteData(format: ui.ImageByteFormat.png);
setImageBytes(imageBytes!);
capture.dispose();
}

void setImageBytes(ByteData imageBytes) {
List values = imageBytes.buffer.asUint8List();

photo = img.decodeImage(values)!;

}
}

// image lib uses uses KML color format, convert #AABBGGRR to regular #AARRGGBB
int abgrToArgb(int argbColor) {
int r = (argbColor >> 16) & 0xFF;
int b = argbColor & 0xFF;
return (argbColor & 0xFF00FF00) | (b << 16) | r;
}

@gputhige
Copy link

Thanks.
Will try and revert.

@ch-moez
Copy link

ch-moez commented Dec 3, 2022

`//////////////////////////////
//
// 2019, roipeker.com
// screencast - demo simple image:
// https://youtu.be/EJyRH4_pY8I
//
// screencast - demo snapshot:
// https://youtu.be/-LxPcL7T61E
//
//////////////////////////////

import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:image/image.dart' as img;
import 'package:flutter/services.dart' show rootBundle;

void main() => runApp(const MaterialApp(home: MyApp()));

class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);

@OverRide
State createState() => _MyAppState();
}

class _MyAppState extends State {
String imagePath = 'assets/12.jpg';
GlobalKey imageKey = GlobalKey();
GlobalKey paintKey = GlobalKey();

// CHANGE THIS FLAG TO TEST BASIC IMAGE, AND SNAPSHOT.
bool useSnapshot = true;

// based on useSnapshot=true ? paintKey : imageKey ;
// this key is used in this example to keep the code shorter.
late GlobalKey currentKey;

final StreamController _stateController = StreamController();
//late img.Image photo ;
img.Image? photo;

@OverRide
void initState() {
currentKey = useSnapshot ? paintKey : imageKey;
super.initState();
}

@OverRide
Widget build(BuildContext context) {
final String title = useSnapshot ? "snapshot" : "basic";
return SafeArea(
child: Scaffold(
appBar: AppBar(title: Text("Color picker $title")),
body: StreamBuilder(
initialData: Colors.green[500],
stream: _stateController.stream,
builder: (buildContext, snapshot) {
Color selectedColor = snapshot.data as Color ?? Colors.green;
return Stack(
children: [
RepaintBoundary(
key: paintKey,
child: GestureDetector(
onPanDown: (details) {
searchPixel(details.globalPosition);
},
onPanUpdate: (details) {
searchPixel(details.globalPosition);
},
child: Center(
child: Image.asset(
imagePath,
key: imageKey,
//color: Colors.red,
//colorBlendMode: BlendMode.hue,
//alignment: Alignment.bottomRight,
fit: BoxFit.contain,
//scale: .8,
),
),
),
),
Container(
margin: const EdgeInsets.all(70),
width: 50,
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: selectedColor!,
border: Border.all(width: 2.0, color: Colors.white),
boxShadow: [
const BoxShadow(
color: Colors.black12,
blurRadius: 4,
offset: Offset(0, 2))
]),
),
Positioned(
child: Text('${selectedColor}',
style: const TextStyle(
color: Colors.white,
backgroundColor: Colors.black54)),
left: 114,
top: 95,
),
],
);
}),
),
);
}

void searchPixel(Offset globalPosition) async {
if (photo == null) {
await (useSnapshot ? loadSnapshotBytes() : loadImageBundleBytes());
}
_calculatePixel(globalPosition);
}

void _calculatePixel(Offset globalPosition) {
RenderBox box = currentKey.currentContext!.findRenderObject() as RenderBox;
Offset localPosition = box.globalToLocal(globalPosition);

double px = localPosition.dx;
double py = localPosition.dy;

if (!useSnapshot) {
  double widgetScale = box.size.width / photo!.width;
  print(py);
  px = (px / widgetScale);
  py = (py / widgetScale);
}

int pixel32 = photo!.getPixelSafe(px.toInt(), py.toInt());
int hex = abgrToArgb(pixel32);

_stateController.add(Color(hex));

}

Future loadImageBundleBytes() async {
ByteData imageBytes = await rootBundle.load(imagePath);
setImageBytes(imageBytes);
}

Future loadSnapshotBytes() async {
RenderRepaintBoundary boxPaint =
paintKey.currentContext!.findRenderObject() as RenderRepaintBoundary;
//RenderObject? boxPaint = paintKey.currentContext.findRenderObject();
ui.Image capture = await boxPaint.toImage();

ByteData? imageBytes =
    await capture.toByteData(format: ui.ImageByteFormat.png);
setImageBytes(imageBytes!);
capture.dispose();

}

void setImageBytes(ByteData imageBytes) {
List values = imageBytes.buffer.asUint8List();
photo;
photo = img.decodeImage(values)!;
}
}

// image lib uses uses KML color format, convert #AABBGGRR to regular #AARRGGBB
int abgrToArgb(int argbColor) {
int r = (argbColor >> 16) & 0xFF;
int b = argbColor & 0xFF;
return (argbColor & 0xFF00FF00) | (b << 16) | r;
}
`

@nmchien
Copy link

nmchien commented Dec 26, 2022

`///////////////////////////////////// 2019, roipeker.com // screencast - demo hình ảnh Đơn giản: // https://youtu.be/EJyRH4_pY8I // // ghi màn hình - ảnh chụp demo: // https://youtu.be/-LxPcL7T61E // ///////// / //////////////////

nhập 'phi tiêu: không đồng bộ'; nhập 'phi tiêu:typed_data'; nhập 'phi tiêu: ui' dưới dạng ui; nhập 'phi tiêu: toán học';

nhập 'gói: rung/liệu.dart'; nhập 'gói: rung/rendering.dart'; nhập 'gói: image/image.dart' dưới img; nhập 'gói: rung/services.dart' hiển thị rootBundle;

void main() => runApp(const MaterialApp(home: MyApp()));

Lớp MyApp mở rộng StatefulWidget { const MyApp({Key? key}): super(key: key);

@OverRide Trạng thái createState() => _MyAppState(); }

Lớp _MyAppState mở rộng Trạng thái { string imagePath = 'nội dung/12.jpg'; GlobalKey imageKey = GlobalKey(); GlobalKey paintKey = GlobalKey();

// THAY ĐỔI CỜ NÀY ĐỂ KIỂM TRA ẢNH CƠ BẢN VÀ ẢNH CHỤP. bool useSnapshot = true;

// dựa trên useSnapshot=true ? paintKey : imageKey ; // phím này đã được sử dụng trong ví dụ này để giữ mã ngắn hơn. GlobalKey currentKey lần lượt;

StreamController cuối cùng _stateController = StreamController(); //muộn img.Ảnh ; img.Hình ảnh? tấm hình;

@OverRide void initState() { currentKey = useSnapshot ? paintKey : imageKey; super.initState(); }

@OverRide Bản dựng tiện ích con (Bối cảnh BuildContext) { tiêu đề Chuỗi cuối cùng = useSnapshot? "ảnh chụp nhanh" : "cơ bản"; return SafeArea( con: Scaffold( appBar: AppBar(title: Text("Bộ chọn màu $title")), body: StreamBuilder( initData : Colors.green[500], stream: _stateController.stream, builder: (buildContext, snapshot ) { Color selectColor = snapshot.data as Color ?? Colors.green; return Stack( children: [ RepaintBoundary( key: paintKey, child: GestureDetector( onPanDown: (details) { searchPixel(details.globalPosition); }, onPanUpdate: ( chi tiết) ) { searchPixel(details.globalPosition); }, con: Center( con: Image.asset( imagePath, key: imageKey, //color: Colors.red, //colorBlendMode: BlendMode.hue, //alignment: Alignment.bottomRight, fit: BoxFit.contain, // scale: .8, ), ), ), ), Khu vực chứa( lề: const EdgeInsets.all(70), chiều rộng: 50, chiều cao: 50, trang trí: BoxDecoration( format: BoxShape.circle, color : đã chọnColor!, đường viền: Đường viền.tất cả(chiều rộng: 2.0, màu:Colors.white), boxShadow: [ const BoxShadow( color: Colors.black12, blurRadius: 4, offset: Offset(0, 2)) ]), ), Đã định vị( con: Văn bản('${selectedColor}', type: const TextStyle( color: Colors.white , backgroundColor: Colors.black54)), left: 114, top: 95, ), ], ); }), ), ); }

void searchPixel(Offset globalPosition) async { if (photo == null) { đang chờ (useSnapshot ? loadSnapshotBytes() : loadImageBundleBytes()); } _calculatePixel(globalPosition); }

void _calculatePixel(Offset globalPosition) { RenderBox box = currentKey.currentContext!.findRenderObject() làm RenderBox; Bù đắp localPosition = box.globalToLocal(globalPosition);

double px = localPosition.dx;
double py = localPosition.dy;

if (!useSnapshot) {
  double widgetScale = box.size.width / photo!.width;
  print(py);
  px = (px / widgetScale);
  py = (py / widgetScale);
}

int pixel32 = photo!.getPixelSafe(px.toInt(), py.toInt());
int hex = abgrToArgb(pixel32);

_stateController.add(Color(hex));

}

Tương lai loadImageBundleBytes() async { ByteData imageBytes = đang chờ rootBundle.load(imagePath); setImageBytes(imageBytes); }

Tương lai loadSnapshotBytes() async { RenderRepaintBoundary boxPaint = paintKey.currentContext!.findRenderObject() doing RenderRepaintBoundary; //RenderObject? boxPaint = paintKey.currentContext.findRenderObject(); ui.Chụp ảnh = đang chờ hộpPaint.toImage();

ByteData? imageBytes =
    await capture.toByteData(format: ui.ImageByteFormat.png);
setImageBytes(imageBytes!);
capture.dispose();

}

void setImageBytes(ByteData imageBytes) { Danh sách giá trị = imageBytes.buffer.asUint8List(); tấm hình; ảnh = img.decodeImage(giá trị)!; } }

// lib lib image sieu used used format KML color, convert #AABBGGRR to #AARRGGBB normal int abgrToArgb(int argbColor) { int r = (argbColor >> 16) & 0xFF; int b = argbColor & 0xFF; trả về (argbColor & 0xFF00FF00) | (b<<16) | r; } `

can you send me the image.dart file code? thanks

@ch-moez
Copy link

ch-moez commented Dec 26, 2022

import 'package:image/image.dart' as img;

@gyoussef55
Copy link

@roipeker I tried using the code and placed an image inside an InteractiveViewer widget. However, when I zoom in, the color I obtain by tapping on the image is not accurate.
What can I do ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment