2015-01-29 1 views
1

У меня есть следующие функции, чтобы спасти Linechart Image:Как сохранить диаграмму JAVA FX без использования Swing API?

@FXML 
    public void saveAsPng() { 
     String timeStamp = new SimpleDateFormat("HHmmss_yyyyMMdd").format(Calendar.getInstance().getTime());    
     chart.setAnimated(false);    
     System.out.println("Saving . . ."); 
     WritableImage image = chart.snapshot(new SnapshotParameters(), null); 
     File file = new File("chart"+timeStamp+".png"); 

     try { 
      ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", file); 
     } catch (IOException e) { 
      Logger.getLogger(SampleController.class.getName()).log(Level.SEVERE, null, e); 
      System.out.println("Error"); 
     }   
    } 

К сожалению, JDK для ARM поддержки доцент Swing API. я получаю сообщение об ошибке при использовании:

ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", file); 

У меня есть обработанное изображение в WritableImage изображения переменных. Есть ли другой способ сохранения диаграммы?

ответ

4

Предлагаемый подход

PNG довольно прост для кодирования. Google некоторые Java-реализации png и модифицируют их источник, чтобы использовать JavaFX PixelReader для ввода данных с пикселями, а не awt-изображений.

Вот пример проекта можно изменить:

http://catcode.com/pngencoder/

Просто убедитесь, что проверить все лицензионные требования для реализации вы изменить.

Пример решение

Я думал, что я дам этому попытку, и это в конечном итоге довольно просто реализовать (благодаря большому источнику кодирования PNG в catcode, который я нашел и связанный ранее).

В приведенном ниже кодексе нет абсолютно никаких утверждений, что он будет делать то, что вы хотите. Все, что я сделал, это вырвать ссылки AWT-изображения и пикселя-граббера и заменить их ссылками на изображения изображений JavaFX и пикселов. Но, похоже, он отлично работает для ограниченного тестового примера, который я пробовал.

Пример вывод

Изображения слева является оригинальным графиком. Изображение справа - экспортированная кодированная PNG диаграмма, загруженная обратно в JavaFX и отображаемая в ImageView.

Изображение слева немного острее, предположительно потому, что я занимался разработкой на сетчатке mac, и было бы необходимо сохранить изображение с разрешением в два пикселя, чтобы получить ту же точность в экспортированном изображении, что и источник ,

Корень сцены имеет фоновое изображение листьев. Для диаграммы используется полупрозрачный цветной фон, чтобы пропускать фон - это гарантирует, что альфа-кодирование работает для сгенерированного файла png.

pie charts

PngEncoderFX.java - Png кодер для JavaFX изображений

import javafx.scene.image.Image; 
import javafx.scene.image.PixelFormat; 
import javafx.scene.image.PixelReader; 

import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.util.zip.CRC32; 
import java.util.zip.Deflater; 
import java.util.zip.DeflaterOutputStream; 

/** 
* PngEncoder takes a Java Image object and creates a byte string which can be saved as a PNG file. 
* The Image is presumed to use the DirectColorModel. 
* 
* <p>Thanks to Jay Denny at KeyPoint Software 
* http://www.keypoint.com/ 
* who let me develop this code on company time.</p> 
* 
* <p>You may contact me with (probably very-much-needed) improvements, 
* comments, and bug fixes at:</p> 
* 
* <p><code>[email protected]</code></p> 
* 
* <p>This library is free software; you can redistribute it and/or 
* modify it under the terms of the GNU Lesser General Public 
* License as published by the Free Software Foundation; either 
* version 2.1 of the License, or (at your option) any later version.</p> 
* 
* <p>This library is distributed in the hope that it will be useful, 
* but WITHOUT ANY WARRANTY; without even the implied warranty of 
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
* Lesser General Public License for more details.</p> 
* 
* <p>You should have received a copy of the GNU Lesser General Public 
* License along with this library; if not, write to the Free Software 
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
* A copy of the GNU LGPL may be found at 
* <code>http://www.gnu.org/copyleft/lesser.html</code></p> 
* 
* @author J. David Eisenberg 
* @version 1.5, 19 Oct 2003 
* 
* CHANGES: 
* -------- 
* 30-Jav-2015 : Hacked source to work with JavaFX images instead of AWT images (by Jewelsea for StackOverflow). 
* 19-Nov-2002 : CODING STYLE CHANGES ONLY (by David Gilbert for Object Refinery Limited); 
* 19-Sep-2003 : Fix for platforms using EBCDIC (contributed by Paulo Soares); 
* 19-Oct-2003 : Change private fields to protected fields so that 
*    PngEncoderB can inherit them (JDE) 
*    Fixed bug with calculation of nRows 
*/ 

public class PngEncoderFX extends Object { 

    /** Constant specifying that alpha channel should be encoded. */ 
    public static final boolean ENCODE_ALPHA = true; 

    /** Constant specifying that alpha channel should not be encoded. */ 
    public static final boolean NO_ALPHA = false; 

    /** Constants for filter (NONE) */ 
    public static final int FILTER_NONE = 0; 

    /** Constants for filter (SUB) */ 
    public static final int FILTER_SUB = 1; 

    /** Constants for filter (UP) */ 
    public static final int FILTER_UP = 2; 

    /** Constants for filter (LAST) */ 
    public static final int FILTER_LAST = 2; 

    /** IHDR tag. */ 
    protected static final byte IHDR[] = {73, 72, 68, 82}; 

    /** IDAT tag. */ 
    protected static final byte IDAT[] = {73, 68, 65, 84}; 

    /** IEND tag. */ 
    protected static final byte IEND[] = {73, 69, 78, 68}; 

    /** The png bytes. */ 
    protected byte[] pngBytes; 

    /** The prior row. */ 
    protected byte[] priorRow; 

    /** The left bytes. */ 
    protected byte[] leftBytes; 

    /** The image. */ 
    protected Image image; 

    /** The width. */ 
    protected int width, height; 

    /** The byte position. */ 
    protected int bytePos, maxPos; 

    /** CRC. */ 
    protected CRC32 crc = new CRC32(); 

    /** The CRC value. */ 
    protected long crcValue; 

    /** Encode alpha? */ 
    protected boolean encodeAlpha; 

    /** The filter type. */ 
    protected int filter; 

    /** The bytes-per-pixel. */ 
    protected int bytesPerPixel; 

    /** The compression level. */ 
    protected int compressionLevel; 

    /** 
    * Class constructor 
    */ 
    public PngEncoderFX() { 
     this(null, false, FILTER_NONE, 0); 
    } 

    /** 
    * Class constructor specifying Image to encode, with no alpha channel encoding. 
    * 
    * @param image A Java Image object which uses the DirectColorModel 
    * @see java.awt.Image 
    */ 
    public PngEncoderFX(Image image) { 
     this(image, false, FILTER_NONE, 0); 
    } 

    /** 
    * Class constructor specifying Image to encode, and whether to encode alpha. 
    * 
    * @param image A Java Image object which uses the DirectColorModel 
    * @param encodeAlpha Encode the alpha channel? false=no; true=yes 
    * @see java.awt.Image 
    */ 
    public PngEncoderFX(Image image, boolean encodeAlpha) { 
     this(image, encodeAlpha, FILTER_NONE, 0); 
    } 

    /** 
    * Class constructor specifying Image to encode, whether to encode alpha, and filter to use. 
    * 
    * @param image A Java Image object which uses the DirectColorModel 
    * @param encodeAlpha Encode the alpha channel? false=no; true=yes 
    * @param whichFilter 0=none, 1=sub, 2=up 
    * @see java.awt.Image 
    */ 
    public PngEncoderFX(Image image, boolean encodeAlpha, int whichFilter) { 
     this(image, encodeAlpha, whichFilter, 0); 
    } 


    /** 
    * Class constructor specifying Image source to encode, whether to encode alpha, filter to use, 
    * and compression level. 
    * 
    * @param image A Java Image object 
    * @param encodeAlpha Encode the alpha channel? false=no; true=yes 
    * @param whichFilter 0=none, 1=sub, 2=up 
    * @param compLevel 0..9 
    * @see java.awt.Image 
    */ 
    public PngEncoderFX(Image image, boolean encodeAlpha, int whichFilter, int compLevel) { 
     this.image = image; 
     this.encodeAlpha = encodeAlpha; 
     setFilter(whichFilter); 
     if (compLevel >= 0 && compLevel <= 9) { 
      this.compressionLevel = compLevel; 
     } 
    } 

    /** 
    * Set the image to be encoded 
    * 
    * @param image A Java Image object which uses the DirectColorModel 
    * @see java.awt.Image 
    * @see java.awt.image.DirectColorModel 
    */ 
    public void setImage(Image image) { 
     this.image = image; 
     pngBytes = null; 
    } 

    /** 
    * Creates an array of bytes that is the PNG equivalent of the current image, specifying 
    * whether to encode alpha or not. 
    * 
    * @param encodeAlpha boolean false=no alpha, true=encode alpha 
    * @return an array of bytes, or null if there was a problem 
    */ 
    public byte[] pngEncode(boolean encodeAlpha) { 
     byte[] pngIdBytes = {-119, 80, 78, 71, 13, 10, 26, 10}; 

     if (image == null) { 
      return null; 
     } 
     width = (int) image.getWidth(); 
     height = (int) image.getHeight(); 

     /* 
     * start with an array that is big enough to hold all the pixels 
     * (plus filter bytes), and an extra 200 bytes for header info 
     */ 
     pngBytes = new byte[((width + 1) * height * 3) + 200]; 

     /* 
     * keep track of largest byte written to the array 
     */ 
     maxPos = 0; 

     bytePos = writeBytes(pngIdBytes, 0); 
     //hdrPos = bytePos; 
     writeHeader(); 
     //dataPos = bytePos; 
     if (writeImageData()) { 
      writeEnd(); 
      pngBytes = resizeByteArray(pngBytes, maxPos); 
     } 
     else { 
      pngBytes = null; 
     } 
     return pngBytes; 
    } 

    /** 
    * Creates an array of bytes that is the PNG equivalent of the current image. 
    * Alpha encoding is determined by its setting in the constructor. 
    * 
    * @return an array of bytes, or null if there was a problem 
    */ 
    public byte[] pngEncode() { 
     return pngEncode(encodeAlpha); 
    } 

    /** 
    * Set the alpha encoding on or off. 
    * 
    * @param encodeAlpha false=no, true=yes 
    */ 
    public void setEncodeAlpha(boolean encodeAlpha) { 
     this.encodeAlpha = encodeAlpha; 
    } 

    /** 
    * Retrieve alpha encoding status. 
    * 
    * @return boolean false=no, true=yes 
    */ 
    public boolean getEncodeAlpha() { 
     return encodeAlpha; 
    } 

    /** 
    * Set the filter to use 
    * 
    * @param whichFilter from constant list 
    */ 
    public void setFilter(int whichFilter) { 
     this.filter = FILTER_NONE; 
     if (whichFilter <= FILTER_LAST) { 
      this.filter = whichFilter; 
     } 
    } 

    /** 
    * Retrieve filtering scheme 
    * 
    * @return int (see constant list) 
    */ 
    public int getFilter() { 
     return filter; 
    } 

    /** 
    * Set the compression level to use 
    * 
    * @param level 0 through 9 
    */ 
    public void setCompressionLevel(int level) { 
     if (level >= 0 && level <= 9) { 
      this.compressionLevel = level; 
     } 
    } 

    /** 
    * Retrieve compression level 
    * 
    * @return int in range 0-9 
    */ 
    public int getCompressionLevel() { 
     return compressionLevel; 
    } 

    /** 
    * Increase or decrease the length of a byte array. 
    * 
    * @param array The original array. 
    * @param newLength The length you wish the new array to have. 
    * @return Array of newly desired length. If shorter than the 
    *   original, the trailing elements are truncated. 
    */ 
    protected byte[] resizeByteArray(byte[] array, int newLength) { 
     byte[] newArray = new byte[newLength]; 
     int  oldLength = array.length; 

     System.arraycopy(array, 0, newArray, 0, Math.min(oldLength, newLength)); 
     return newArray; 
    } 

    /** 
    * Write an array of bytes into the pngBytes array. 
    * Note: This routine has the side effect of updating 
    * maxPos, the largest element written in the array. 
    * The array is resized by 1000 bytes or the length 
    * of the data to be written, whichever is larger. 
    * 
    * @param data The data to be written into pngBytes. 
    * @param offset The starting point to write to. 
    * @return The next place to be written to in the pngBytes array. 
    */ 
    protected int writeBytes(byte[] data, int offset) { 
     maxPos = Math.max(maxPos, offset + data.length); 
     if (data.length + offset > pngBytes.length) { 
      pngBytes = resizeByteArray(pngBytes, pngBytes.length + Math.max(1000, data.length)); 
     } 
     System.arraycopy(data, 0, pngBytes, offset, data.length); 
     return offset + data.length; 
    } 

    /** 
    * Write an array of bytes into the pngBytes array, specifying number of bytes to write. 
    * Note: This routine has the side effect of updating 
    * maxPos, the largest element written in the array. 
    * The array is resized by 1000 bytes or the length 
    * of the data to be written, whichever is larger. 
    * 
    * @param data The data to be written into pngBytes. 
    * @param nBytes The number of bytes to be written. 
    * @param offset The starting point to write to. 
    * @return The next place to be written to in the pngBytes array. 
    */ 
    protected int writeBytes(byte[] data, int nBytes, int offset) { 
     maxPos = Math.max(maxPos, offset + nBytes); 
     if (nBytes + offset > pngBytes.length) { 
      pngBytes = resizeByteArray(pngBytes, pngBytes.length + Math.max(1000, nBytes)); 
     } 
     System.arraycopy(data, 0, pngBytes, offset, nBytes); 
     return offset + nBytes; 
    } 

    /** 
    * Write a two-byte integer into the pngBytes array at a given position. 
    * 
    * @param n The integer to be written into pngBytes. 
    * @param offset The starting point to write to. 
    * @return The next place to be written to in the pngBytes array. 
    */ 
    protected int writeInt2(int n, int offset) { 
     byte[] temp = {(byte) ((n >> 8) & 0xff), (byte) (n & 0xff)}; 
     return writeBytes(temp, offset); 
    } 

    /** 
    * Write a four-byte integer into the pngBytes array at a given position. 
    * 
    * @param n The integer to be written into pngBytes. 
    * @param offset The starting point to write to. 
    * @return The next place to be written to in the pngBytes array. 
    */ 
    protected int writeInt4(int n, int offset) { 
     byte[] temp = {(byte) ((n >> 24) & 0xff), 
         (byte) ((n >> 16) & 0xff), 
         (byte) ((n >> 8) & 0xff), 
         (byte) (n & 0xff)}; 
     return writeBytes(temp, offset); 
    } 

    /** 
    * Write a single byte into the pngBytes array at a given position. 
    * 
    * @param b The integer to be written into pngBytes. 
    * @param offset The starting point to write to. 
    * @return The next place to be written to in the pngBytes array. 
    */ 
    protected int writeByte(int b, int offset) { 
     byte[] temp = {(byte) b}; 
     return writeBytes(temp, offset); 
    } 

    /** 
    * Write a PNG "IHDR" chunk into the pngBytes array. 
    */ 
    protected void writeHeader() { 
     int startPos; 

     startPos = bytePos = writeInt4(13, bytePos); 
     bytePos = writeBytes(IHDR, bytePos); 
     width = (int) image.getWidth(); 
     height = (int) image.getHeight(); 
     bytePos = writeInt4(width, bytePos); 
     bytePos = writeInt4(height, bytePos); 
     bytePos = writeByte(8, bytePos); // bit depth 
     bytePos = writeByte((encodeAlpha) ? 6 : 2, bytePos); // direct model 
     bytePos = writeByte(0, bytePos); // compression method 
     bytePos = writeByte(0, bytePos); // filter method 
     bytePos = writeByte(0, bytePos); // no interlace 
     crc.reset(); 
     crc.update(pngBytes, startPos, bytePos - startPos); 
     crcValue = crc.getValue(); 
     bytePos = writeInt4((int) crcValue, bytePos); 
    } 

    /** 
    * Perform "sub" filtering on the given row. 
    * Uses temporary array leftBytes to store the original values 
    * of the previous pixels. The array is 16 bytes long, which 
    * will easily hold two-byte samples plus two-byte alpha. 
    * 
    * @param pixels The array holding the scan lines being built 
    * @param startPos Starting position within pixels of bytes to be filtered. 
    * @param width Width of a scanline in pixels. 
    */ 
    protected void filterSub(byte[] pixels, int startPos, int width) { 
     int i; 
     int offset = bytesPerPixel; 
     int actualStart = startPos + offset; 
     int nBytes = width * bytesPerPixel; 
     int leftInsert = offset; 
     int leftExtract = 0; 

     for (i = actualStart; i < startPos + nBytes; i++) { 
      leftBytes[leftInsert] = pixels[i]; 
      pixels[i] = (byte) ((pixels[i] - leftBytes[leftExtract]) % 256); 
      leftInsert = (leftInsert + 1) % 0x0f; 
      leftExtract = (leftExtract + 1) % 0x0f; 
     } 
    } 

    /** 
    * Perform "up" filtering on the given row. 
    * Side effect: refills the prior row with current row 
    * 
    * @param pixels The array holding the scan lines being built 
    * @param startPos Starting position within pixels of bytes to be filtered. 
    * @param width Width of a scanline in pixels. 
    */ 
    protected void filterUp(byte[] pixels, int startPos, int width) { 
     int  i, nBytes; 
     byte currentByte; 

     nBytes = width * bytesPerPixel; 

     for (i = 0; i < nBytes; i++) { 
      currentByte = pixels[startPos + i]; 
      pixels[startPos + i] = (byte) ((pixels[startPos + i] - priorRow[i]) % 256); 
      priorRow[i] = currentByte; 
     } 
    } 

    /** 
    * Write the image data into the pngBytes array. 
    * This will write one or more PNG "IDAT" chunks. In order 
    * to conserve memory, this method grabs as many rows as will 
    * fit into 32K bytes, or the whole image; whichever is less. 
    * 
    * 
    * @return true if no errors; false if error grabbing pixels 
    */ 
    protected boolean writeImageData() { 
     int rowsLeft = height; // number of rows remaining to write 
     int startRow = 0;  // starting row to process this time through 
     int nRows;    // how many rows to grab at a time 

     byte[] scanLines;  // the scan lines to be compressed 
     int scanPos;   // where we are in the scan lines 
     int startPos;   // where this line's actual pixels start (used for filtering) 

     byte[] compressedLines; // the resultant compressed lines 
     int nCompressed;  // how big is the compressed area? 

     //int depth;    // color depth (handle only 8 or 32) 

     PixelReader pg = image.getPixelReader(); 

     bytesPerPixel = (encodeAlpha) ? 4 : 3; 

     Deflater scrunch = new Deflater(compressionLevel); 
     ByteArrayOutputStream outBytes = new ByteArrayOutputStream(1024); 

     DeflaterOutputStream compBytes = new DeflaterOutputStream(outBytes, scrunch); 
     try { 
      while (rowsLeft > 0) { 
       nRows = Math.min(32767/(width * (bytesPerPixel + 1)), rowsLeft); 
       nRows = Math.max(nRows, 1); 

       int[] pixels = new int[width * nRows]; 

       pg.getPixels(0, startRow, width, nRows, PixelFormat.getIntArgbInstance(), pixels, 0, width); 

       /* 
       * Create a data chunk. scanLines adds "nRows" for 
       * the filter bytes. 
       */ 
       scanLines = new byte[width * nRows * bytesPerPixel + nRows]; 

       if (filter == FILTER_SUB) { 
        leftBytes = new byte[16]; 
       } 
       if (filter == FILTER_UP) { 
        priorRow = new byte[width * bytesPerPixel]; 
       } 

       scanPos = 0; 
       startPos = 1; 
       for (int i = 0; i < width * nRows; i++) { 
        if (i % width == 0) { 
         scanLines[scanPos++] = (byte) filter; 
         startPos = scanPos; 
        } 
        scanLines[scanPos++] = (byte) ((pixels[i] >> 16) & 0xff); 
        scanLines[scanPos++] = (byte) ((pixels[i] >> 8) & 0xff); 
        scanLines[scanPos++] = (byte) ((pixels[i]) & 0xff); 
        if (encodeAlpha) { 
         scanLines[scanPos++] = (byte) ((pixels[i] >> 24) & 0xff); 
        } 
        if ((i % width == width - 1) && (filter != FILTER_NONE)) { 
         if (filter == FILTER_SUB) { 
          filterSub(scanLines, startPos, width); 
         } 
         if (filter == FILTER_UP) { 
          filterUp(scanLines, startPos, width); 
         } 
        } 
       } 

       /* 
       * Write these lines to the output area 
       */ 
       compBytes.write(scanLines, 0, scanPos); 

       startRow += nRows; 
       rowsLeft -= nRows; 
      } 
      compBytes.close(); 

      /* 
      * Write the compressed bytes 
      */ 
      compressedLines = outBytes.toByteArray(); 
      nCompressed = compressedLines.length; 

      crc.reset(); 
      bytePos = writeInt4(nCompressed, bytePos); 
      bytePos = writeBytes(IDAT, bytePos); 
      crc.update(IDAT); 
      bytePos = writeBytes(compressedLines, nCompressed, bytePos); 
      crc.update(compressedLines, 0, nCompressed); 

      crcValue = crc.getValue(); 
      bytePos = writeInt4((int) crcValue, bytePos); 
      scrunch.finish(); 
      return true; 
     } 
     catch (IOException e) { 
      System.err.println(e.toString()); 
      return false; 
     } 
    } 

    /** 
    * Write a PNG "IEND" chunk into the pngBytes array. 
    */ 
    protected void writeEnd() { 
     bytePos = writeInt4(0, bytePos); 
     bytePos = writeBytes(IEND, bytePos); 
     crc.reset(); 
     crc.update(IEND); 
     crcValue = crc.getValue(); 
     bytePos = writeInt4((int) crcValue, bytePos); 
    } 

} 

PieChartPngEncoder.java - Test Harness

import javafx.application.Application; 
import javafx.collections.*; 
import javafx.geometry.*; 
import javafx.scene.*; 
import javafx.scene.control.Label; 
import javafx.scene.image.*; 
import javafx.scene.layout.*; 
import javafx.scene.paint.*; 
import javafx.stage.Stage; 
import javafx.scene.chart.*; 

import java.io.IOException; 
import java.nio.file.*; 
import java.util.logging.*; 

/** 
* Demonstrates encoding a JavaFX node to a PNG image using a custom PNG encoder and no AWT/Swing classes. 
*/ 
public class PieChartPngEncoder extends Application { 
    private static final Logger logger = Logger.getLogger(PieChartPngEncoder.class.getName()); 

    @Override public void start(Stage stage) throws IOException { 
     final PieChart chart = createChart(); 
     chart.getStylesheets().add(getClass().getResource(
       "chart.css" 
     ).toExternalForm()); 
     chart.getStyleClass().add("translucent-background"); 

     Path imagePath = Files.createTempFile("png-test", ".png"); 
     exportPngSnapshot(
       chart, 
       imagePath, 
       Color.TRANSPARENT 
     ); 

     Image chartImage = new Image(
       imagePath.toUri().toURL().toExternalForm() 
     ); 

     Label exportLocation = new Label("Exported to " + imagePath); 
     exportLocation.setStyle(""); 
     exportLocation.getStyleClass().add("overlay-label"); 

     VBox layout = new VBox(
       10, 
       new HBox(10, 
         chart, 
         new ImageView(chartImage) 
       ), 
       exportLocation 
     ); 
     layout.setAlignment(Pos.BASELINE_RIGHT); 
     layout.setPadding(new Insets(10)); 

     Scene scene = new Scene(layout); 
     scene.getStylesheets().add(getClass().getResource(
       "encoder-app.css" 
     ).toExternalForm()); 

     stage.setScene(scene); 
     stage.show(); 

     logger.log(Level.INFO, "Wrote: " + imagePath); 
    } 

    private PieChart createChart() { 
     ObservableList<PieChart.Data> pieChartData = 
       FXCollections.observableArrayList(
         new PieChart.Data("Grapefruit", 13), 
         new PieChart.Data("Oranges", 25), 
         new PieChart.Data("Plums", 10), 
         new PieChart.Data("Pears", 22), 
         new PieChart.Data("Apples", 30)); 
     final PieChart chart = new PieChart(pieChartData); 
     chart.setTitle("Imported Fruits"); 
     return chart; 
    } 

    private void exportPngSnapshot(
      Node node, 
      Path path, 
      Paint backgroundFill 
    ) throws IOException { 
     if (node.getScene() == null) { 
      Scene snapshotScene = new Scene(new Group(node)); 
     } 

     SnapshotParameters params = new SnapshotParameters(); 
     params.setFill(backgroundFill); 
     Image chartSnapshot = node.snapshot(params, null); 
     PngEncoderFX encoder = new PngEncoderFX(chartSnapshot, true); 
     byte[] bytes = encoder.pngEncode(); 
     Files.write(path, bytes); 
    } 

    public static void main(String[] args) { 
     launch(args); 
    } 
} 

кодер-приложение.CSS - образец стиля для тестового жгута

/** file: encoder-app.css **/ 

.root { 
    -fx-background-image: url(http://pixdaus.com/files/items/pics/7/92/275792_de0efe881e1a7e5da077a26eacfa5ed2_large.jpg); 
    -fx-background-size: cover; 
} 

.overlay-label { 
    -fx-background-color: rgba(0, 80, 0, 0.7); 
    -fx-text-fill: white; 
} 

chart.css - образец таблицы стилей для тестового жгута

/** file: chart.css **/ 
.translucent-background { 
    -fx-background-color: rgba(127, 127, 60, 0.7); 
} 
+0

Спасибо, я уверен, что это пригодится один день. – brian

+0

Большое спасибо. Надеюсь, он работает на платформе ARM – kingspp

Смежные вопросы