@Override
  public Graph2DResult readValue() {
    VType vType = tableData.readValue();
    xColumnName.readNext();
    yColumnName.readNext();
    tooltipColumnName.readNext();

    // Table and columns must be available
    if (vType == null || xColumnName.isMissing() || yColumnName.isMissing()) {
      return null;
    }

    // Prepare new dataset
    Point2DDataset dataset;
    if (vType instanceof VNumberArray) {
      dataset = Point2DDatasets.lineData(((VNumberArray) vType).getData());
    } else {
      dataset =
          DatasetConversions.point2DDatasetFromVTable(
              (VTable) vType, xColumnName.getValue(), yColumnName.getValue());
    }

    // Process all renderer updates
    List<LineGraph2DRendererUpdate> updates = rendererUpdateQueue.readValue();
    for (LineGraph2DRendererUpdate rendererUpdate : updates) {
      renderer.update(rendererUpdate);
    }

    // If no size is set, don't calculate anything
    if (renderer.getImageHeight() == 0 && renderer.getImageWidth() == 0) return null;

    BufferedImage image =
        new BufferedImage(
            renderer.getImageWidth(), renderer.getImageHeight(), BufferedImage.TYPE_3BYTE_BGR);
    renderer.draw(image.createGraphics(), dataset);

    previousImage = ValueUtil.toVImage(image);
    return new Graph2DResult(
        vType,
        previousImage,
        new GraphDataRange(
            renderer.getXPlotRange(), dataset.getXStatistics(), renderer.getXAggregatedRange()),
        new GraphDataRange(
            renderer.getYPlotRange(), dataset.getYStatistics(), renderer.getYAggregatedRange()),
        renderer.getFocusValueIndex());
  }