【问题标题】:How do I set LogicalScreenDescriptor and ImageDescriptor gif metadata如何设置 LogicalScreenDescriptor 和 ImageDescriptor gif 元数据
【发布时间】:2021-11-20 23:32:38
【问题描述】:

我想创建一个逻辑屏幕描述符大于我的 gif 图像序列中的任何图像的 GIF。 gif 中的每个图像都将修改其顶部和左侧偏移量。这是我的代码,看起来它应该可以工作,但它没有

void test() throws IOException {

    Image image1 = textToImage ("m",12.0 );
    Image image2 = textToImage("n", 24.0);

    Image[] images = {image2, image1};

    String[] imageTopOffset = {"6", "30"};
    String[] imageLeftOffset = {"6", "36"};

    ImageWriter iw = ImageIO.getImageWritersByFormatName("gif").next();
    ImageWriteParam params = iw.getDefaultWriteParam();
    int type = ((BufferedImage)getRenderedImage(image1)).getType();
    ImageTypeSpecifier imageTypeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(type);

    IIOMetadata metadata = iw.getDefaultImageMetadata(imageTypeSpecifier, params);
    IIOMetadataNode root = (IIOMetadataNode)metadata.getAsTree(metadata.getNativeMetadataFormatName());

    IIOMetadataNode lsdNode = getNode(root, "LogicalScreenDescriptor");
    lsdNode.setAttribute("logicalScreenHeight", "100");
    lsdNode.setAttribute("logicalScreenWidth", "75");

    IIOMetadataNode graphicsControlExtensionNode = getNode(root, "GraphicControlExtension");
    graphicsControlExtensionNode.setAttribute("disposalMethod", "none");
    graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
    graphicsControlExtensionNode.setAttribute("transparentColorFlag", "FALSE");
    graphicsControlExtensionNode.setAttribute("delayTime", "100");
    graphicsControlExtensionNode.setAttribute("transparentColorIndex", "0");

    IIOMetadataNode commentsNode = getNode(root, "CommentExtensions");
    commentsNode.setAttribute("CommentExtension", "Created by: http://example.com");

    IIOMetadataNode appExtensionsNode = getNode(root, "ApplicationExtensions");
    IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");
    child.setAttribute("applicationID", "NETSCAPE");
    child.setAttribute("authenticationCode", "2.0");
    boolean loop = true;
    int loopContinuously = loop ? 0 : 1;
    child.setUserObject(new byte[]{ 0x1, (byte) (loopContinuously & 0xFF), (byte) ((loopContinuously >> 8) & 0xFF)});
    appExtensionsNode.appendChild(child);

    ByteArrayOutputStream os = new ByteArrayOutputStream();
    ImageOutputStream ios = ImageIO.createImageOutputStream(os);
    iw.setOutput(ios);
    iw.prepareWriteSequence(metadata);
    int i = 0;
    for (Image image : images) {
        graphicsControlExtensionNode = getNode(root, "GraphicControlExtension");
        graphicsControlExtensionNode.setAttribute("delayTime", "50");
        IIOMetadataNode imageDescriptorNode = getNode(root, "ImageDescriptor");
        imageDescriptorNode.setAttribute("imageLeftPosition", imageLeftOffset[i]);
        imageDescriptorNode.setAttribute("imageTopPosition", imageTopOffset[i]);
        imageDescriptorNode.setAttribute("imageWidth", String.valueOf(image.getWidth()));
        imageDescriptorNode.setAttribute("imageHeight", String.valueOf(image.getHeight()));
        imageDescriptorNode.setAttribute("interlaceFlag", "FALSE");

        IIOImage ii = new IIOImage(getRenderedImage(image), null, metadata);
        iw.writeToSequence(ii, params);
        i++;
    }
    iw.endWriteSequence();
    ios.close();

    byte[] gifContent = os.toByteArray();
    os.close();
    File outputFile = new File("test.gif");
    try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
        outputStream.write(gifContent);
        outputStream.close();
    }
}


private WritableImage textToImage(String text, Double size) {

    Text t = new Text();
    t.setFont(getFont("Calibi",
            "NORMAL",
            "REGULAR",
            size));
    t.setStroke(Color.BLACK);
    t.setText(text);

    Scene scene = new Scene(new StackPane(t));
    return t.snapshot(null, null);
}

IIOMetadataNode getNode(IIOMetadataNode rootNode, String name) {
    NodeList childNodes  = rootNode.getChildNodes();
    for (int i=0; i<childNodes.getLength(); i++) {
        if (childNodes.item(i).getNodeName().equals(name) ) {
            return (IIOMetadataNode)childNodes.item(i);
        }
    }
    // no child node with the given name found, create one!
    IIOMetadataNode metadataNode = new IIOMetadataNode(name);
    rootNode.appendChild(metadataNode);
    return  metadataNode;
}

Font getFont(String fontname, String fontWeight, String fontPosture, double size) {
    FontPosture posture = FontPosture.valueOf(fontPosture);
    FontWeight weight = FontWeight.valueOf(fontWeight);
    Font font = Font.font (fontname, weight, posture, size);
    return font;
}

public RenderedImage getRenderedImage(Image image) {
    return SwingFXUtils.fromFXImage(image, null);
}

它生成的 gif 图像是序列中第一个图像的大小,即使我将 LogicalScreenDescriptor 设置为比写出的图像更大的大小。 gif 的实际大小是第一张图像的大小。另一个问题是 imageTopPosition 和 imageLeftPosition 没有被应用。

两张图片大小不同。生成两张图片,一张是字母m的12点图像,另一张是字母n的24点图像。

那么,如何制作更大的逻辑屏幕描述符以及如何更改图像描述符偏移量。尽管上面的代码看起来应该可以工作,但实际上并没有。我发现的大多数示例都假设 gif 中的所有图像都具有相同的大小,并且 gif 中后续图像的显示完全取代了之前的图像。

【问题讨论】:

  • 调试时,我查看了 iw.canReplaceStreamMetadata() 并得到了 false 的值。这似乎可以解释为什么设置 LogicalScreenDiscriptor 参数无效。事实上,当我转储我制作的 gif 文件时,我也找不到我添加的评论。因此,“canReplaceStreamMetadata”是错误的事实也可以解释这一点。我不明白的是我必须改变或做些什么才能替换streamMetadata。我需要不同的 GIF ImageWriter 吗?

标签: image image-processing javafx gif


【解决方案1】:

以下是我所做的编码更改,解决了 gif 无法正常显示的问题:

 void test() throws IOException {

    Image image1 = textToImage ("m",12.0 );
    Image image2 = textToImage("n", 24.0);

    Image[] images = {image2, image1};

    String[] imageTopOffset = {"6", "30"};
    String[] imageLeftOffset = {"6", "36"};

    ImageWriter iw = ImageIO.getImageWritersByMIMEType("image/gif").next();
    ImageWriteParam params = iw.getDefaultWriteParam();
    int type = ((BufferedImage)getRenderedImage(image1)).getType();
    ImageTypeSpecifier imageTypeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(type);

    IIOMetadata imageMetadata = iw.getDefaultImageMetadata(imageTypeSpecifier, params);
    IIOMetadata streamMetadata = iw.getDefaultStreamMetadata(params);
    IIOMetadataNode streamRoot = (IIOMetadataNode)streamMetadata.getAsTree(streamMetadata.getNativeMetadataFormatName());
    IIOMetadataNode imageRoot = (IIOMetadataNode)imageMetadata.getAsTree(imageMetadata.getNativeMetadataFormatName());

    ByteArrayOutputStream os = new ByteArrayOutputStream();
    ImageOutputStream ios = ImageIO.createImageOutputStream(os);
    iw.setOutput(ios);

    IIOMetadataNode lsdNode = getNode(streamRoot, "LogicalScreenDescriptor");
    lsdNode.setAttribute("logicalScreenHeight", "100");
    lsdNode.setAttribute("logicalScreenWidth", "75");
/*
 *      The following extension nodes may not be put in the streamMetadata. If you do add them
 *      to the streamMetadata you'll get any error when you prepareWriteSequence
 *
    IIOMetadataNode graphicsControlExtensionNode = getNode(streamRoot, "GraphicControlExtension");
    graphicsControlExtensionNode.setAttribute("disposalMethod", "none");
    graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
    graphicsControlExtensionNode.setAttribute("transparentColorFlag", "FALSE");
    graphicsControlExtensionNode.setAttribute("delayTime", "100");
    graphicsControlExtensionNode.setAttribute("transparentColorIndex", "0");

    IIOMetadataNode commentsNode = getNode(streamRoot, "CommentExtensions");
    commentsNode.setAttribute("CommentExtension", "Created by: http://example.com");

    IIOMetadataNode appExtensionsNode = getNode(streamRoot, "ApplicationExtensions");
    IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");
    child.setAttribute("applicationID", "NETSCAPE");
    child.setAttribute("authenticationCode", "2.0");
    boolean loop = true;
    int loopContinuously = loop ? 0 : 1;
    child.setUserObject(new byte[]{ 0x1, (byte) (loopContinuously & 0xFF), (byte) ((loopContinuously >> 8) & 0xFF)});
    appExtensionsNode.appendChild(child);

 */
    streamMetadata.setFromTree(streamMetadata.getNativeMetadataFormatName(), streamRoot);
    iw.prepareWriteSequence(streamMetadata);
    int i = 0;
    for (Image image : images) {
        IIOMetadataNode graphicsControlExtensionNode = getNode(imageRoot, "GraphicControlExtension");
        graphicsControlExtensionNode.setAttribute("delayTime", "50");
        IIOMetadataNode imageDescriptorNode = getNode(imageRoot, "ImageDescriptor");
        imageDescriptorNode.setAttribute("imageLeftPosition", imageLeftOffset[i]);
        imageDescriptorNode.setAttribute("imageTopPosition", imageTopOffset[i]);
        imageMetadata.setFromTree(imageMetadata.getNativeMetadataFormatName(),imageRoot);
        IIOImage ii = new IIOImage(getRenderedImage(image), null, imageMetadata);
        iw.writeToSequence(ii, params);
        i++;
    }
    iw.endWriteSequence();
    ios.close();

    byte[] gifContent = os.toByteArray();
    os.close();
    File outputFile = new File("test.gif");
    try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
        outputStream.write(gifContent);
        outputStream.close();
    }
}

prepareWriteSequence 命令不喜欢包含扩展 graphicsControlExtension 和 ApplicationExtensions 的 streamMetadata。我通过检查 GifImageWriter 的源代码发现了这一点。我提供给集合“imageWidth”和“imageHeight”的值也存在一些问题。不确定这些属性的值应该是什么样的。我只是通过不设置这些值来避免这个问题。

输出是一个 75x100 的 gif,其中一个 12pt 的字母 m 从顶部偏移 30,从左侧偏移 36,以及一个 24 点的字母 n,从顶部偏移 6,从左侧偏移 6。

【讨论】:

    猜你喜欢
    • 2012-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多