绘制从远程服务器下载的图像 [英] Drawing over an image downloaded from remote server

查看:90
本文介绍了绘制从远程服务器下载的图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要执行以下操作:


  1. 从服务器下载PNG资源

  2. 根据状态在该图像上绘制几个矩形,用不同的颜色

  3. 在可缩放图像视图中显示该图像

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

I have a working code in an Android app using Canvas, but I cannot figure out how to do that with Flutter.

以下是下载资源的代码:

Here is the code that downloads the resource:

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,但找不到从画布上的文件绘制图像并将其转换为Image的方法,因为无法从文件中提取宽度和高度。

What should I do next? I tried using PictureRecorder and Canvas, but I cannot find a way to draw image from the file on those canvas and then convert it to Image because I cannot extract width and height from the file.

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

Below is the Android code equivalent that I would like to implement in Flutter.

// 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);


推荐答案

我终于设法解决了这个问题!

I finally managed to solve the issue!

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

I created a renderer that creates a composite image (background from the remote resource and adds rectangles in the foreground).

渲染器:

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:

And to supply the image to the ZoomableImage I created a custom 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函数)。

If anybody knows a better way to convert an Image to Codec or to even skip this step, please comment (MapRenderer.renderMap function).

这篇关于绘制从远程服务器下载的图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆