gpt4 book ai didi

flutter - 如何实现 Flood-fill 算法?

转载 作者:行者123 更新时间:2023-12-03 16:51:45 31 4
gpt4 key购买 nike

我正在开发一个 Paint 应用程序,其中我正在实现类似于 MS Paint 应用程序的 BucketFill 功能。
我已经使用几种 FloodFill 算法对其进行了编码,但是填充颜色过程花费了太多时间。
我不太确定其背后的原因可能是由于低缓存内存、糟糕的算法,或者可能需要花费大量时间来计算偏移量。
有人可以帮助我了解您在 Flutter/Dart 方面的知识吗?
尝试的算法:

  • 基于递归的方法(4 或 8 连接方法)
  • 基于队列的方法
  • 基于堆栈的方法
  • 基于队列线性的方法

  • 最重要的是,性能非常慢
    import 'dart:collection';
    import 'dart:math';
    import 'package:flutter/material.dart';
    import 'dart:ui';
    import 'dart:async';
    import 'package:flutter/rendering.dart';
    import 'dart:typed_data';
    import 'dart:ui' as ui;

    class BucketFill {
    List<Offset> _points = <Offset>[];
    Uint32List words;
    static int width;
    Color oldColor, pixel;
    int imageWidth;
    int imageHeight;

    Future<List> capturePng(GlobalKey key, Offset offset) async {
    RenderRepaintBoundary boundary = key.currentContext.findRenderObject();
    ui.Image image = await boundary.toImage();
    final rgbaImageData =
    await image.toByteData(format: ui.ImageByteFormat.rawRgba);
    imageHeight = image.height;
    imageWidth = image.width;
    words = Uint32List.view(rgbaImageData.buffer, rgbaImageData.offsetInBytes,
    rgbaImageData.lengthInBytes ~/ Uint32List.bytesPerElement);
    oldColor = _getColor(words, offset.dx, offset.dy);
    return _floodfill(oldColor, offset.dx, offset.dy);
    }

    Color _getColor(Uint32List words, double x1, double y1) {
    int x = x1.toInt();
    int y = y1.toInt();
    var offset = x + y * imageWidth;
    return Color(words[offset]);
    }

    // Queue based approach.
    List<Offset> _floodfill(Color oldColor, double x, double y) {
    Queue<Offset> queue = new Queue();
    Offset temp;
    queue.add(Offset(x, y));
    _points = List.from(_points)..add(queue.first);
    while (queue.isNotEmpty) {
    temp = queue.first;
    queue.removeFirst();
    if (_shouldFillColor(temp.dx + 2, temp.dy)) {
    queue.add(Offset(temp.dx + 2, temp.dy));
    _points.add(Offset(temp.dx + 2, temp.dy));
    }
    if (_shouldFillColor(temp.dx - 2, temp.dy)) {
    queue.add(Offset(temp.dx - 2, temp.dy));
    _points.add(Offset(temp.dx - 2, temp.dy));
    }
    if (_shouldFillColor(temp.dx, temp.dy + 2)) {
    queue.add(Offset(temp.dx, temp.dy + 2));
    _points.add(Offset(temp.dx, temp.dy + 2));
    }
    if (_shouldFillColor(temp.dx, temp.dy - 2)) {
    queue.add(Offset(temp.dx, temp.dy - 2));
    _points.add(Offset(temp.dx, temp.dy - 2));
    }
    }
    _points.add(null);
    return _points;
    }

    bool _shouldFillColor(double x, double y) {
    return (x >= 0 &&
    x < imageWidth &&
    y >= 0 &&
    y < imageHeight &&
    !_points.contains(Offset(x, y)))
    ? _getColor(words, x, y) == oldColor
    : false;
    }
    }

    最佳答案

    我最近刚刚为此做了一个名为 floodfill_image 的包。
    基本上我使用 Queue-Linear Flood Fill Algorithm by J. Dunlap并将其移植到 Flutter 中。
    这是我用于 Flutter 的源代码:

    //Original algorithm by J. Dunlap queuelinearfloodfill.aspx
    //Java port by Owen Kaluza
    //Android port by Darrin Smith (Standard Android)
    //Flutter port by Garlen Javier
    import 'dart:async';
    import 'dart:collection';
    import 'dart:ui';
    import 'package:image/image.dart' as img;
    class QueueLinearFloodFiller {
    img.Image image;
    int _width = 0;
    int _height = 0;
    int _cachedWidth = -1;
    int _cachedHeight = -1;
    int _fillColor = 0;
    int _tolerance = 8;
    List<int> _startColor = [0, 0, 0, 0];
    List<bool> _pixelsChecked;
    Queue<_FloodFillRange> _ranges;
    QueueLinearFloodFiller(img.Image imgVal, int newColor) {
    image = imgVal;
    _width = image.width;
    _height = image.height;
    setFillColor(newColor);
    }
    void resize(Size size) {
    if (_cachedWidth != size.width.toInt() ||
    _cachedHeight != size.height.toInt()) {
    image = img.copyResize(image,
    width: size.width.toInt(), height: size.height.toInt());
    _width = image.width;
    _height = image.height;
    _cachedWidth = _width;
    _cachedHeight = _height;
    }
    }
    void setTargetColor(int targetColor) {
    _startColor[0] = img.getRed(targetColor);
    _startColor[1] = img.getGreen(targetColor);
    _startColor[2] = img.getBlue(targetColor);
    _startColor[3] = img.getAlpha(targetColor);
    }
    void setTolerance(int value) {
    _tolerance = value.clamp(0, 100);
    }
    int getFillColor() {
    return _fillColor;
    }
    void setFillColor(int value) {
    _fillColor = value;
    }
    void _prepare() {
    // Called before starting flood-fill
    _pixelsChecked = List<bool>.filled(_width * _height, false);
    _ranges = Queue<_FloodFillRange>();
    }
    // Fills the specified point on the bitmap with the currently selected fill
    // color.
    // int x, int y: The starting coords for the fill
    Future<void> floodFill(int x, int y) async {
    // Setup
    _prepare();
    if (_startColor[0] == 0) {
    // ***Get starting color.
    int startPixel = image.getPixelSafe(x, y);
    _startColor[0] = img.getRed(startPixel);
    _startColor[1] = img.getGreen(startPixel);
    _startColor[2] = img.getBlue(startPixel);
    }
    // ***Do first call to floodfill.
    _linearFill(x, y);
    // ***Call floodfill routine while floodfill _ranges still exist on the
    // queue
    _FloodFillRange range;
    while (_ranges.length > 0) {
    // **Get Next Range Off the Queue
    range = _ranges.removeFirst();
    // **Check Above and Below Each Pixel in the Floodfill Range
    int downPxIdx = (_width * (range.y + 1)) + range.startX;
    int upPxIdx = (_width * (range.y - 1)) + range.startX;
    int upY = range.y - 1; // so we can pass the y coord by ref
    int downY = range.y + 1;
    for (int i = range.startX; i <= range.endX; i++) {
    // *Start Fill Upwards
    // if we're not above the top of the bitmap and the pixel above
    // this one is within the color tolerance
    if (range.y > 0 && (!_pixelsChecked[upPxIdx]) && _checkPixel(i, upY)) {
    _linearFill(i, upY);
    }
    // *Start Fill Downwards
    // if we're not below the bottom of the bitmap and the pixel
    // below this one is within the color tolerance
    if (range.y < (_height - 1) &&
    (!_pixelsChecked[downPxIdx]) &&
    _checkPixel(i, downY)) {
    _linearFill(i, downY);
    }
    downPxIdx++;
    upPxIdx++;
    }
    }
    }
    // Finds the furthermost left and right boundaries of the fill area
    // on a given y coordinate, starting from a given x coordinate, filling as
    // it goes.
    // Adds the resulting horizontal range to the queue of floodfill _ranges,
    // to be processed in the main loop.
    //
    // int x, int y: The starting coords
    void _linearFill(int x, int y) {
    // ***Find Left Edge of Color Area
    int lFillLoc = x; // the location to check/fill on the left
    int pxIdx = (_width * y) + x;
    while (true) {
    // **fill with the color
    //pixels[pxIdx] = _fillColor;
    image.setPixelSafe(lFillLoc, y, _fillColor);
    // **indicate that this pixel has already been checked and filled
    _pixelsChecked[pxIdx] = true;
    // **de-increment
    lFillLoc--; // de-increment counter
    pxIdx--; // de-increment pixel index
    // **exit loop if we're at edge of bitmap or color area
    if (lFillLoc < 0 ||
    (_pixelsChecked[pxIdx]) ||
    !_checkPixel(lFillLoc, y)) {
    break;
    }
    }
    lFillLoc++;
    // ***Find Right Edge of Color Area
    int rFillLoc = x; // the location to check/fill on the left
    pxIdx = (_width * y) + x;
    while (true) {
    // **fill with the color
    image.setPixelSafe(rFillLoc, y, _fillColor);
    // **indicate that this pixel has already been checked and filled
    _pixelsChecked[pxIdx] = true;
    // **increment
    rFillLoc++; // increment counter
    pxIdx++; // increment pixel index
    // **exit loop if we're at edge of bitmap or color area
    if (rFillLoc >= _width ||
    _pixelsChecked[pxIdx] ||
    !_checkPixel(rFillLoc, y)) {
    break;
    }
    }
    rFillLoc--;
    // add range to queue
    _FloodFillRange r = new _FloodFillRange(lFillLoc, rFillLoc, y);
    _ranges.add(r);
    }
    // Sees if a pixel is within the color tolerance range.
    bool _checkPixel(int x, int y) {
    int pixelColor = image.getPixelSafe(x, y);
    int red = img.getRed(pixelColor);
    int green = img.getGreen(pixelColor);
    int blue = img.getBlue(pixelColor);
    int alpha = img.getAlpha(pixelColor);
    return (red >= (_startColor[0] - _tolerance) &&
    red <= (_startColor[0] + _tolerance) &&
    green >= (_startColor[1] - _tolerance) &&
    green <= (_startColor[1] + _tolerance) &&
    blue >= (_startColor[2] - _tolerance) &&
    blue <= (_startColor[2] + _tolerance) &&
    alpha >= (_startColor[3] - _tolerance) &&
    alpha <= (_startColor[3] + _tolerance));
    }
    }
    // Represents a linear range to be filled and branched from.
    class _FloodFillRange {
    int startX;
    int endX;
    int y;
    _FloodFillRange(int startX, int endX, int yPos) {
    this.startX = startX;
    this.endX = endX;
    this.y = yPos;
    }
    }
    这是一个示例 gif,它也将显示 Flutter 中此算法的速度。
    enter image description here
    如果你想了解更多关于我移植它的步骤,你可以访问我的博客 https://www.meekcode.com/blog/flood-fill-in-flutter

    关于flutter - 如何实现 Flood-fill 算法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52837641/

    31 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com