Flutter位图绘制插件bitmap_canvas的使用

Flutter位图绘制插件bitmap_canvas的使用

简介

bitmap_canvas 是一个用于在 Flutter 中绘制像素级图像的插件。它提供了简单易用的 API,允许开发者通过 Dart 语言直接操作像素,并且还提供了用于展示这些绘制结果的小部件。

Bitmap Canvas

Render bitmap pixels with canvas-style APIs.


项目背景

此项目是 Flutter Bounty Hunters 的概念验证项目。如果需要更多功能,可以通过资助里程碑来支持项目的进一步开发!


插件简介

bitmap_canvas 是一个包,它通过 Dart 提供了易于使用的像素绘制 API,并且包含了一些小部件,可以轻松地展示这些绘制的结果。

Bitmap Canvas 动画示例


在实际项目中的应用

bitmap_canvasflutter_processing 的渲染器。


示例代码

绘制静态噪点动画

以下是一个绘制静态噪点的示例代码,每个像素都有随机亮度:

Widget build(BuildContext context) {
  // BitmapPaint 类似于 CustomPaint,但你可以绘制单个像素。
  return BitmapPaint(
    size: const Size(100, 100), // 设置画布大小
    painter: BitmapPainter.fromCallback((bitmapContext) async {
      final canvas = bitmapContext.canvas; // 获取画布对象
      final size = bitmapContext.size; // 获取画布尺寸
      final random = Random(); // 创建随机数生成器
      
      await canvas.startBitmapTransaction(); // 开始事务处理

      // 遍历画布的所有像素并设置颜色
      for (int x = 0; x < size.width; x += 1) {
        for (int y = 0; y < size.height; y += 1) {
          // 设置单个像素的颜色
          canvas.set(
            x: x, 
            y: y, 
            color: HSVColor.fromAHSV(1.0, 0, 0, random.nextDouble()).toColor()
          );
        }
      }

      await canvas.endBitmapTransaction(); // 结束事务处理
    }),
  );
}

为什么需要在 Flutter 中使用位图画布?

Flutter 基于 SKIA 构建,SKIA 是一个可移植的渲染系统,支持硬件加速的着色器。你可能会想,如果我们想绘制单个像素,为什么不直接使用着色器呢?软件渲染不是更慢吗?

以下是选择软件渲染(即用 Dart 绘制像素)而非着色器的一些原因:

  1. 学习成本更低:相比于像 GLSL 这样的着色器语言,用 Dart 绘制像素更容易学习。
  2. 着色器无法实现所有类型的像素绘制:例如,任何依赖其他像素值的像素绘制方式在着色器中都不支持。
  3. Flutter 不完全支持自定义着色器:这意味着大多数像素绘制行为无法通过着色器在 Flutter 中实现。

完整示例代码

以下是一个完整的示例代码,展示了如何使用 bitmap_canvas 来绘制不同的效果:

import 'package:bitmap_canvas/bitmap_canvas.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        body: Center(
          child: Wrap(
            spacing: 48,
            runSpacing: 48,
            children: [
              // 使用 NoiseBitmapPainter 绘制噪点
              BitmapCanvasDemo(
                bitmapPainter: NoiseBitmapPainter(),
              ),
              // 使用 FlowFieldPainter 绘制流场效果
              BitmapCanvasDemo(
                bitmapPainter: FlowFieldPainter(),
              ),
              // 使用 MetaBallsPainter 绘制元球效果
              BitmapCanvasDemo(
                bitmapPainter: MetaBallsPainter(),
              ),
              // 使用 PerlinNoisePainter 绘制柏林噪声效果
              BitmapCanvasDemo(
                bitmapPainter: PerlinNoisePainter(),
              ),
            ],
          ),
        ),
      ),
    ),
  );
}

// 自定义噪点绘制器
class NoiseBitmapPainter extends BitmapPainter {
  [@override](/user/override)
  void paint(BitmapCanvas canvas) {
    final random = Random();
    for (int x = 0; x < canvas.width; x++) {
      for (int y = 0; y < canvas.height; y++) {
        canvas.setPixel(x, y, Color.fromRGBO(random.nextInt(256), random.nextInt(256), random.nextInt(256), 1));
      }
    }
  }
}

// 自定义流场绘制器
class FlowFieldPainter extends BitmapPainter {
  [@override](/user/override)
  void paint(BitmapCanvas canvas) {
    for (int x = 0; x < canvas.width; x++) {
      for (int y = 0; y < canvas.height; y++) {
        double angle = (x + y) / 10;
        int r = (canvas.width / 2 + x * cos(angle)).toInt();
        int g = (canvas.height / 2 + y * sin(angle)).toInt();
        int b = 0;
        canvas.setPixel(x, y, Color.fromRGBO(r % 256, g % 256, b % 256, 1));
      }
    }
  }
}

// 元球绘制器
class MetaBallsPainter extends BitmapPainter {
  [@override](/user/override)
  void paint(BitmapCanvas canvas) {
    List<Offset> balls = [
      Offset(50, 50),
      Offset(150, 150),
    ];

    for (int x = 0; x < canvas.width; x++) {
      for (int y = 0; y < canvas.height; y++) {
        double sum = 0;
        for (var ball in balls) {
          double distance = (Offset(x.toDouble(), y.toDouble()) - ball).distance;
          sum += 1 / (distance + 1);
        }
        if (sum > 1) {
          canvas.setPixel(x, y, Colors.white);
        } else {
          canvas.setPixel(x, y, Colors.black);
        }
      }
    }
  }
}

// 柏林噪声绘制器
class PerlinNoisePainter extends BitmapPainter {
  [@override](/user/override)
  void paint(BitmapCanvas canvas) {
    for (int x = 0; x < canvas.width; x++) {
      for (int y = 0; y < canvas.height; y++) {
        double noiseValue = noise(x / 50, y / 50);
        int gray = (noiseValue * 255).toInt();
        canvas.setPixel(x, y, Color(gray));
      }
    }
  }
}

更多关于Flutter位图绘制插件bitmap_canvas的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter位图绘制插件bitmap_canvas的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


bitmap_canvas 是一个用于在 Flutter 中进行位图绘制的插件。它允许你在内存中创建一个位图,并在其上绘制各种图形、文本和图像。这个插件非常适合需要高性能绘制的场景,比如游戏、图像处理等。

安装

首先,你需要在 pubspec.yaml 文件中添加 bitmap_canvas 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  bitmap_canvas: ^0.0.1  # 请检查最新版本

然后运行 flutter pub get 来安装依赖。

基本用法

  1. 创建位图

    你可以使用 BitmapCanvas 类来创建一个位图。首先,你需要指定位图的宽度和高度。

    import 'package:bitmap_canvas/bitmap_canvas.dart';
    
    void main() {
      final bitmap = BitmapCanvas(800, 600);
    }
    
  2. 绘制图形

    你可以在位图上绘制各种图形,比如矩形、圆形、线条等。

    bitmap.drawRect(Rect.fromLTWH(100, 100, 200, 200), Paint()..color = Colors.red);
    bitmap.drawCircle(Offset(400, 300), 100, Paint()..color = Colors.blue);
    bitmap.drawLine(Offset(0, 0), Offset(800, 600), Paint()..color = Colors.green);
    
  3. 绘制文本

    你也可以在位图上绘制文本。

    bitmap.drawText('Hello, Flutter!', Offset(200, 200), TextStyle(color: Colors.black, fontSize: 24));
    
  4. 绘制图像

    你可以在位图上绘制其他图像。

    final image = await loadImageFromAsset('assets/image.png');
    bitmap.drawImage(image, Offset(300, 300));
    
  5. 获取位图数据

    绘制完成后,你可以获取位图的像素数据,或者将其转换为 Image 对象以便在 Flutter 中显示。

    final pixels = bitmap.getPixels();
    final image = await bitmap.toImage();
    
  6. 显示位图

    你可以使用 Image.memoryImage.asset 来显示位图。

    final image = await bitmap.toImage();
    final imageWidget = Image(image: image);
    

完整示例

以下是一个完整的示例,展示了如何使用 bitmap_canvas 插件创建一个位图,并在 Flutter 中显示它。

import 'package:flutter/material.dart';
import 'package:bitmap_canvas/bitmap_canvas.dart';

void main() async {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Bitmap Canvas Example')),
        body: Center(
          child: FutureBuilder(
            future: createBitmap(),
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.done) {
                return Image(image: snapshot.data);
              } else {
                return CircularProgressIndicator();
              }
            },
          ),
        ),
      ),
    );
  }

  Future<Image> createBitmap() async {
    final bitmap = BitmapCanvas(800, 600);

    // 绘制矩形
    bitmap.drawRect(Rect.fromLTWH(100, 100, 200, 200), Paint()..color = Colors.red);

    // 绘制圆形
    bitmap.drawCircle(Offset(400, 300), 100, Paint()..color = Colors.blue);

    // 绘制线条
    bitmap.drawLine(Offset(0, 0), Offset(800, 600), Paint()..color = Colors.green);

    // 绘制文本
    bitmap.drawText('Hello, Flutter!', Offset(200, 200), TextStyle(color: Colors.black, fontSize: 24));

    // 获取位图图像
    final image = await bitmap.toImage();
    return image;
  }
}
回到顶部