Flutter字体渲染插件freetype_dart的使用

发布于 1周前 作者 zlyuanteng 来自 Flutter

Flutter字体渲染插件freetype_dart的使用

在本篇文档中,我们将探讨如何使用freetype_dart插件来渲染字体。freetype_dart是一个将FreeType库绑定到Dart语言的库,允许开发者直接在Dart环境中使用FreeType的强大功能。

示例代码

示例1:渲染单个字符

import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:freetype_dart/src/errors.dart';
import 'package:freetype_dart/src/extensions/extensions.dart';
import 'package:freetype_dart/src/generated_bindings.dart';
import 'dart:io';
import 'package:image/image.dart' as img;

void main(List<String> args) {
  final dylib = DynamicLibrary.open(
      Platform.isWindows ? 'libfreetype-6.dll' : 'libfreetype.so.6');

  final ft = FreetypeBinding(dylib);

  final library = calloc<FT_Library>();

  var err = ft.FT_Init_FreeType(library);
  if (err != FT_Err_Ok) {
    print('err on Init FreeType');
  }

  final face = calloc<FT_Face>();
  err = ft.FT_New_Face(library.value, "VeraMono.ttf".toCharP(), 0, face);

  if (err == FT_Err_Unknown_File_Format) {
    print("Font format is unsupported");
  } else if (err == 1) {
    print("Font file is missing or corrupted");
  }

  // 确保加载了unicode字符映射
  err = ft.FT_Select_Charmap(face.value, ft_encoding_unicode);
  print("FT_Select_Charmap $err");
  
  // 选择一个字符进行渲染
  var character = 'A';
  int glyph_index = ft.FT_Get_Char_Index(face.value, character.codeUnitAt(0));
  if (glyph_index == 0) {
    print('código de caractere indefinido');
  }
  print("glyph_index $glyph_index");

  err = ft.FT_Set_Pixel_Sizes(face.value, 0, 240); // 设置字体大小为24像素
  print("FT_Set_Pixel_Sizes $err");
  
  err = ft.FT_Load_Glyph(face.value, glyph_index, FT_LOAD_DEFAULT);
  print("FT_Load_Glyph $err");

  // 渲染字形到位图
  err = ft.FT_Render_Glyph(
      face.value.ref.glyph, FT_Render_Mode_.FT_RENDER_MODE_NORMAL);
  print("FT_Render_Glyph $err");
  FT_Bitmap bitmap = face.value.ref.glyph.ref.bitmap;

  print(
      "bitmap width ${bitmap.width} rows ${bitmap.rows} mode ${bitmap.pixel_mode} ${bitmap.pitch}");

  var format = img.Format.uint8;

  final buffer = bitmap.buffer
      .cast<Int8>()
      .asTypedList((bitmap.pitch * bitmap.rows).toInt());

  // 创建一个256x256的8位(默认)RGB(默认)图像。
  final image = img.Image.fromBytes(
      width: bitmap.width,
      height: bitmap.rows,
      bytes: buffer.buffer,
      numChannels: 1,
      format: format);

  // 将结果编码为PNG图像格式。
  final png = img.encodePng(image);
  // 将PNG格式的数据写入文件。
  File('image.png').writeAsBytesSync(png);

  ft.FT_Done_Face(face.value);
  ft.FT_Done_FreeType(library.value);
}

结果

结果

示例2:渲染文本

import 'dart:math';
import 'package:freetype_dart/src/wrapper/freetype.dart';
import 'dart:io';
import 'package:image/image.dart' as img;

void main(List<String> args) {
  createPreview('VeraMono.ttf', 'VeraMono.png');
}

void createPreview(String fontFileName, String outputFileName,
    {int width = 640,
    int height = 480,
    int fontSize = 24,
    img.Color? backgroundColor}) {
  final image = img.Image(width: width, height: height);
  image.clear(backgroundColor ?? img.ColorRgb8(255, 255, 255));

  final ft = Freetype();
  final face = ft.newFace(fontFileName);
  face.setCharSize(fontSize * 64, 0, 100, 0);

  final text = face.familyName;
  final numChars = text.length;

  var targetHeight = height;
  final pen = Vector(50 * 64, (targetHeight - 100) * 64);

  final angle = (0.0 / 360) * 3.14159 * 2;

  final matrix = Matrix(0, 0, 0, 0);
  matrix.xx = (cos(angle) * 0x10000).toInt();
  matrix.xy = (-sin(angle) * 0x10000).toInt();
  matrix.yx = (sin(angle) * 0x10000).toInt();
  matrix.yy = (cos(angle) * 0x10000).toInt();

  for (var n = 0; n < numChars; n++) {
    // 设置变换
    face.setTransform(matrix, pen);
    // 加载字形图像到槽中(清除之前的)
    final char = text.codeUnitAt(n);
    final isLoad = face.loadChar(char, LoadFlag.RENDER);
    if (isLoad == false) continue; // 忽略错误
    final glyph = face.glyph;
    final bitmap = glyph.bitmap();
    final x = glyph.bitmapLeft;
    final y = targetHeight - glyph.bitmapTop;
    drawBitmap(image, bitmap, x, y);
    // 增加笔画位置
    pen.x += glyph.advance.x;
    pen.y += glyph.advance.y;
  }

  final png = img.encodePng(image);
  File(outputFileName).writeAsBytesSync(png);

  face.free();
  ft.free();
}

void drawBitmap(img.Image imageCtx, Bitmap bitmap, int x, int y) {
  final w = bitmap.width;
  final xMax = w;
  final yMax = bitmap.rows;
  final data = bitmap.buffer;

  for (var p = 0; p < xMax; p++) {
    for (var q = 0; q < yMax; q++) {
      var i = x + p;
      var j = y + q;
      if (i < 0 || j < 0 || i >= imageCtx.width || j >= imageCtx.height) {
        continue;
      }
      final idx = q * w + p;
      // if (idx > data.length - 1) {
      //   continue;
      // }
      final val = data[idx];
      final cor = img.ColorRgba8(84, 13, 110, val);
      img.drawPixel(imageCtx, i, j, cor, maskChannel: img.Channel.luminance);
    }
  }
}

更多关于Flutter字体渲染插件freetype_dart的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter字体渲染插件freetype_dart的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用freetype_dart插件进行字体渲染的示例代码。freetype_dart是一个Flutter插件,它使用FreeType库来渲染字体。请注意,由于freetype_dart可能不是官方维护的插件,具体的使用方法和API可能会有所不同,因此以下代码仅供参考,具体实现可能需要调整。

首先,确保在你的pubspec.yaml文件中添加freetype_dart依赖:

dependencies:
  flutter:
    sdk: flutter
  freetype_dart: ^x.y.z  # 请替换为实际的版本号

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

接下来,在你的Flutter项目中创建一个自定义的Widget来使用freetype_dart进行字体渲染。以下是一个基本的示例:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Freetype Dart Demo',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Freetype Dart Demo'),
        ),
        body: Center(
          child: CustomFontWidget(),
        ),
      ),
    );
  }
}

class CustomFontWidget extends StatefulWidget {
  @override
  _CustomFontWidgetState createState() => _CustomFontWidgetState();
}

class _CustomFontWidgetState extends State<CustomFontWidget> {
  FreetypeDart? freetype;

  @override
  void initState() {
    super.initState();
    // 初始化FreetypeDart实例
    _initFreetype();
  }

  Future<void> _initFreetype() async {
    try {
      // 加载字体文件(请确保字体文件路径正确)
      final Uint8List fontData = await rootBundle.load('assets/fonts/your_font.ttf');
      freetype = await FreetypeDart.init(fontData);
      // 设置字体大小
      freetype!.setSize(24);
    } catch (e) {
      print('Failed to initialize FreetypeDart: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    if (freetype == null) {
      return CircularProgressIndicator(); // 加载中
    }

    // 渲染文本
    final String text = "Hello, Freetype Dart!";
    final Uint8List glyphData = freetype!.renderText(text);

    // 将glyphData转换为ImageProvider以在Flutter中显示
    final ImageProvider imageProvider = MemoryImage(glyphData.buffer.asUint8List());

    return Image(
      image: imageProvider,
      width: freetype!.advanceWidth(text) * 1.0, // 根据字体宽度调整
      height: freetype!.height.toDouble(), // 根据字体高度调整
    );
  }
}

请注意以下几点:

  1. 字体文件:确保你有一个字体文件(如your_font.ttf)放在assets/fonts/目录下,并在pubspec.yaml中声明它:

    flutter:
      assets:
        - assets/fonts/your_font.ttf
    
  2. 渲染逻辑:上述示例中的渲染逻辑非常基础,只是将渲染的glyph数据转换为ImageProvider并在Image组件中显示。实际项目中,你可能需要更复杂的布局和渲染逻辑。

  3. 错误处理:示例中包含基本的错误处理,但在生产代码中,你可能需要更详细的错误处理和用户反馈。

  4. 性能优化:由于freetype_dart直接在Dart层进行字体渲染,可能会比Flutter内置的文本渲染性能差一些。如果性能是关键因素,你可能需要考虑其他方法或优化策略。

  5. 插件版本:由于插件的API和可用性可能会变化,请确保查阅最新的freetype_dart文档和示例。

希望这个示例能帮助你开始在Flutter项目中使用freetype_dart进行字体渲染!

回到顶部