绘制从远程服务器下载的图像

2024-01-28

我需要执行以下操作:

  1. 从服务器下载 PNG 资源
  2. 根据状态在该图像上绘制几个具有不同颜色的矩形
  3. 在可缩放图像视图中显示该图像

我在使用 Canvas 的 Android 应用程序中有一个工作代码,但我不知道如何使用 Flutter 来做到这一点。

这是下载资源的代码:

static Future<File> getImageFromUrl(String url) async {
final directory = await getApplicationDocumentsDirectory();
final file = File("$directory/${_getSHA(url)}.png");

if (await file.exists()) {
  // Returns the cached file
} else {
  final response = await http.get(url);

  if (response.statusCode >= 200 && response.statusCode < 300) {
    await file.writeAsBytes(response.bodyBytes);
  } else {
    return null;
  }
}
return file;
}

接下来我应该做什么?我尝试使用 PictureRecorder 和 Canvas,但我找不到从这些画布上的文件中绘制图像然后将其转换为图像的方法,因为我无法从文件中提取宽度和高度。

编辑: 下面是我想在 Flutter 中实现的等效 Android 代码。

// Here we have a bitmap from a file
    Bitmap mapBitmap = getBitmap();

    Canvas mapCanvas = new Canvas(mapBitmap);

    mapDrawable.setBounds(0, 0, mapCanvas.getWidth(), mapCanvas.getHeight());
    mapDrawable.draw(mapCanvas);

    canvasWidth = mapCanvas.getWidth();
    canvasHeight = mapCanvas.getHeight();

    Paint paint = new Paint();
    for (java.util.Map.Entry<String, MapObject> entry : this.mapObjects.entrySet()) {
        MapObject mapObject = entry.getValue();
        paint.setColor(getContext().getResources().getColor(mapObject.getBackgroundColor()));
        paint.setAlpha(100);
        mapCanvas.drawRect((int) (mapObject.getPosX() * scaleX),
                (int) (mapObject.getPosY() * scaleY),
                (int) ((mapObject.getPosX() + mapObject.getWidth()) * scaleX),
                (int) ((mapObject.getPosY() + mapObject.getHeight()) * scaleY),
                paint);
    }

    photoView.setImageBitmap(mapBitmap);

我终于成功解决了这个问题!

我创建了一个渲染器,用于创建合成图像(来自远程资源的背景并在前景中添加矩形)。

渲染器:

class MapRenderer {
  ui.Image _mapBackgroundImage;

  Future<ui.Codec> renderMap(String url, List<Sensor> sensors) async {
    await _loadMapBackground(url);
    var renderedMapImage = await _updateSensors(sensors);
    var byteD = await renderedMapImage.toByteData(
        format: ui.ImageByteFormat.png);
    return ui.instantiateImageCodec(Uint8List.view(byteD.buffer));
  }


  Future<ui.Image> _updateSensors(List<Sensor> sensors) async {
    ui.PictureRecorder recorder = ui.PictureRecorder();
    Canvas c = Canvas(recorder);

    var paint = ui.Paint();
    c.drawImage(_mapBackgroundImage, ui.Offset(0.0, 0.0), paint);

    for (Sensor s in sensors) {
      paint.color = (s.availability ? CustomColors.npSensorFree : CustomColors
          .npSensorOccupied);
      c.drawRect(
        ui.Rect.fromPoints(ui.Offset(s.posX, s.posY),
            ui.Offset(s.posX + s.width, s.posY + s.height)),
        paint,
      );
    }

    return recorder
        .endRecording()
        .toImage(_mapBackgroundImage.width, _mapBackgroundImage.height);
  }

  Future<void> _loadMapBackground(String url) async {
    var imageBytes = await _getLocalCopyOrLoadFromUrl(url);

    if (imageBytes != null) {
      _mapBackgroundImage = await _getImageFromBytes(imageBytes);
    } else {
      return null;
    }
  }

  Future<ui.Image> _getImageFromBytes(Uint8List bytes) async {
    var imageCodec = await ui.instantiateImageCodec(bytes);
    var frame = await imageCodec.getNextFrame();
    return frame.image;
  }

  Future<Uint8List> _getLocalCopyOrLoadFromUrl(String url) async {
    final directory = await getApplicationDocumentsDirectory();
    final file = File("${directory.path}/${_getSHA(url)}.png");

    if (await file.exists()) {
      return await file.readAsBytes();
    } else {
      Uint8List resourceBytes = await _loadFromUrl(url);

      if (resourceBytes != null) {
        await file.writeAsBytes(resourceBytes);
        return resourceBytes;
      } else {
        return null;
      }
    }
  }

  Future<Uint8List> _loadFromUrl(String url) async {
    final response = await http.get(url);

    if (response.statusCode >= 200 && response.statusCode < 300) {
      return response.bodyBytes;
    } else {
      return null;
    }
  }

  String _getSHA(String sth) {
    var bytes = utf8.encode(sth);
    var digest = sha1.convert(bytes);

    return digest.toString();
  }

  void dispose() {
    _mapBackgroundImage.dispose();
  }
}

为了向 ZoomableImage 提供图像,我创建了一个自定义 ImageProvider:

class MapImageProvider extends ImageProvider<MapImageProvider> {
  final String url;
  final List<Sensor> sensors;

  final MapRenderer mapRenderer = MapRenderer();

  MapImageProvider(this.url, this.sensors);

  @override
  ImageStreamCompleter load(MapImageProvider key) {   
    return MultiFrameImageStreamCompleter(
        codec: _loadAsync(key),
        scale: 1.0,
        informationCollector: (StringBuffer information) {
          information.writeln('Image provider: $this');
          information.write('Image key: $key');
        });
  }

  Future<ui.Codec> _loadAsync(MapImageProvider key) async {
    assert(key == this);

    return await mapRenderer.renderMap(url, sensors);
  }

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is MapImageProvider &&
          runtimeType == other.runtimeType &&
          url == other.url;

  @override
  int get hashCode => url.hashCode;

  @override
  String toString() => '$runtimeType("$url")';  

  @override
  Future<MapImageProvider> obtainKey(ImageConfiguration configuration) {
    return SynchronousFuture<MapImageProvider>(this);
  }
}

如果有人知道将图像转换为编解码器的更好方法,甚至跳过此步骤,请发表评论(MapRenderer.renderMap 函数)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

绘制从远程服务器下载的图像 的相关文章

随机推荐