当前位置: 首页 > 知识库问答 >
问题:

创建分层Tif与Java在Photoshop中使用

拓拔意
2023-03-14

我感兴趣的是用Java创建一个分层tif,使Photoshop能够识别层。我能够创建多页tif,但Photoshop无法将这些页面识别为层。不过,Acrobat可以查看这些页面。谁知道Photoshop是如何存储tif层数据的,以及如何用Java生成这些数据?

谢谢

共有2个答案

后安民
2023-03-14

我开始了一个基于TinyTIFF的解决方案,这个SO问题的答案来自@haraldK,TIFF规范和Photoshop TIFF规范。这是编写TIFF的最简单方法。我输入了编写Photoshop部分的代码,但还没有完成。

请注意,Photoshop使用TIFF图像作为“预览”图像,类似于PSD文件最末端的展平合成图像。Photoshop TIFF部分包含所有层的像素数据(同样类似于PSD)。Adobe以这种方式使用TIFF是相当肮脏的。您也可以只使用(同样糟糕的)PSD格式,因为将PSD数据粉碎为TIFF格式只会增加复杂性,而没有任何好处。这就是为什么我没有完成下面的代码。如果你完成了,请把它贴在这里。

输出类来自Kryo。pixmap.getPixels()是每个像素4个字节,RGBA。

/* Copyright (c) 2008-2015 Jan W. Krieger (<jan@jkrieger.de>, <j.krieger@dkfz.de>), German Cancer Research Center (DKFZ) & IWR, University of Heidelberg
 * Copyright (c) 2018, Nathan Sweet, Esoteric Software LLC
 * All rights reserved.
 * 
 * This software is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
 * License (LGPL) as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later
 * version. This program 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 General Public License for more details. You
 * should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */

public class TiffWriter {
    private Output out;
    private int width, height;

    private int ifdCount, ifdLastOffset, ifdData, headerStart;
    private Output header;

    public void start (OutputStream output, int width, int height) throws IOException {
        this.out = new Output(output);
        this.width = width;
        this.height = height;

        out.writeByte('M'); // Big endian.
        out.writeByte('M');
        out.writeShort(42); // Magic number.
        ifdLastOffset = out.total();
        out.writeInt(8); // Offset of first IFD.
    }

    public void frame (Pixmap pixmap, String name, int frame, int endFrame) throws IOException {
        ByteBuffer pixels = pixmap.getPixels();

        headerStart = out.total();
        ifdData = 2 + TIFF_HEADER_MAX_ENTRIES * 12;
        ifdCount = 0;
        header = new Output(TIFF_HEADER_SIZE + 2);
        header.setPosition(2);

        writeLongIFD(TIFF_FIELD_IMAGEWIDTH, width);
        writeLongIFD(TIFF_FIELD_IMAGELENGTH, height);
        writeShortIFD(TIFF_FIELD_BITSPERSAMPLE, 8, 8, 8);
        writeShortIFD(TIFF_FIELD_COMPRESSION, COMPRESSION_NO);
        writeShortIFD(TIFF_FIELD_PHOTOMETRICINTERPRETATION, PHOTOMETRIC_INTERPRETATION_RGB);
        writeLongIFD(TIFF_FIELD_STRIPOFFSETS, headerStart + 2 + TIFF_HEADER_SIZE);
        writeShortIFD(TIFF_FIELD_SAMPLESPERPIXEL, 4);
        writeLongIFD(TIFF_FIELD_ROWSPERSTRIP, height);
        writeLongIFD(TIFF_FIELD_STRIPBYTECOUNTS, width * height);
        writeRationalIFD(TIFF_FIELD_XRESOLUTION, 720000, 10000);
        writeRationalIFD(TIFF_FIELD_YRESOLUTION, 720000, 10000);
        writeShortIFD(TIFF_FIELD_PLANARCONFIG, PLANAR_CONFIGURATION_CHUNKY);
        writeShortIFD(TIFF_FIELD_RESOLUTIONUNIT, RESOLUTION_UNIT_INCH);
        writeShortIFD(TIFF_FIELD_EXTRASAMPLES, 1); // Adds alpha to last samples per pixel.
        // writeIFDEntrySHORT(TIFF_FIELD_SAMPLEFORMAT, SAMPLE_FORMAT_FLOAT);

        // Photoshop layer entry.
        ifdCount++;
        header.writeShort(TIFF_FIELD_PHOTOSHOP_IMAGESOURCEDATA);
        header.writeShort(TIFF_TYPE_UNDEFINED);
        int sizePosition = header.position();
        header.writeInt(0); // Size in bytes.
        header.writeInt(ifdData + headerStart);
        int pos = header.position();
        header.setPosition(ifdData);
        writeString(header, "Adobe Photoshop Document Data Block");
        // Unfinished!
        int size = header.position() - ifdData;
        ifdData = header.position();
        header.setPosition(sizePosition);
        header.writeInt(size);
        header.setPosition(pos);

        if (ifdCount > TIFF_HEADER_MAX_ENTRIES) throw new RuntimeException();

        header.setPosition(0);
        header.writeShort(ifdCount);

        header.setPosition(2 + ifdCount * 12); // header start + 12 bytes per IFD entry
        header.writeInt(headerStart + 2 + TIFF_HEADER_SIZE + width * height);

        out.writeBytes(header.getBuffer(), 0, TIFF_HEADER_SIZE + 2);

        ifdLastOffset = headerStart + 2 + ifdCount * 12;

        pixels.position(0);
        for (int i = 0, n = width * height * 4; i < n; i += 4) {
            byte a = pixels.get(i + 3);
            float pma = (a & 0xff) / 255f;
            out.writeByte((byte)((pixels.get(i) & 0xff) * pma));
            out.writeByte((byte)((pixels.get(i + 1) & 0xff) * pma));
            out.writeByte((byte)((pixels.get(i + 2) & 0xff) * pma));
            out.writeByte(a);
        }
        pixels.position(0);
    }

    public void end () throws IOException {
        out.close();

        // Erase last IFD offset.
        RandomAccessFile file = new RandomAccessFile("test.tif", "rw");
        file.seek(ifdLastOffset);
        file.write((byte)0);
        file.write((byte)0);
        file.write((byte)0);
        file.write((byte)0);
        file.close();
    }

    public void close () throws IOException {
        end();
    }

    private void writeString (Output output, String value) {
        for (int i = 0, n = value.length(); i < n; i++)
            output.writeByte(value.charAt(i));
        output.writeByte(0);
    }

    private void writeLongIFD (int tag, int data) {
        ifdCount++;
        header.writeShort(tag);
        header.writeShort(TIFF_TYPE_LONG);
        header.writeInt(1);
        header.writeInt(data);
    }

    private void writeShortIFD (int tag, int data) {
        ifdCount++;
        header.writeShort(tag);
        header.writeShort(TIFF_TYPE_SHORT);
        header.writeInt(1);
        header.writeShort(data);
        header.writeShort(0); // Pad bytes.
    }

    private void writeShortIFD (int tag, int... data) {
        ifdCount++;
        header.writeShort(tag);
        header.writeShort(TIFF_TYPE_SHORT);
        header.writeInt(data.length);
        if (data.length == 1)
            header.writeInt(data[0]);
        else {
            header.writeInt(ifdData + headerStart);
            int pos = header.position();
            header.setPosition(ifdData);
            for (int value : data)
                header.writeShort(value);
            ifdData = header.position();
            header.setPosition(pos);
        }
    }

    private void writeRationalIFD (int tag, int numerator, int denominator) {
        ifdCount++;
        header.writeShort(tag);
        header.writeShort(TIFF_TYPE_RATIONAL);
        header.writeInt(1);
        header.writeInt(ifdData + headerStart);
        int pos = header.position();
        header.setPosition(ifdData);
        header.writeInt(numerator);
        header.writeInt(denominator);
        ifdData = header.position();
        header.setPosition(pos);
    }

    static private final int TIFF_HEADER_SIZE = 510;
    static private final int TIFF_HEADER_MAX_ENTRIES = 16;

    static private final int TIFF_FIELD_IMAGEWIDTH = 256;
    static private final int TIFF_FIELD_IMAGELENGTH = 257;
    static private final int TIFF_FIELD_BITSPERSAMPLE = 258;
    static private final int TIFF_FIELD_COMPRESSION = 259;
    static private final int TIFF_FIELD_PHOTOMETRICINTERPRETATION = 262;
    static private final int TIFF_FIELD_IMAGEDESCRIPTION = 270;
    static private final int TIFF_FIELD_STRIPOFFSETS = 273;
    static private final int TIFF_FIELD_SAMPLESPERPIXEL = 277;
    static private final int TIFF_FIELD_ROWSPERSTRIP = 278;
    static private final int TIFF_FIELD_STRIPBYTECOUNTS = 279;
    static private final int TIFF_FIELD_XRESOLUTION = 282;
    static private final int TIFF_FIELD_YRESOLUTION = 283;
    static private final int TIFF_FIELD_PLANARCONFIG = 284;
    static private final int TIFF_FIELD_RESOLUTIONUNIT = 296;
    static private final int TIFF_FIELD_EXTRASAMPLES = 338;
    static private final int TIFF_FIELD_SAMPLEFORMAT = 339;
    static private final int TIFF_FIELD_PHOTOSHOP_IMAGESOURCEDATA = 37724;

    static private final int TIFF_TYPE_BYTE = 1;
    static private final int TIFF_TYPE_ASCII = 2;
    static private final int TIFF_TYPE_SHORT = 3;
    static private final int TIFF_TYPE_LONG = 4;
    static private final int TIFF_TYPE_RATIONAL = 5;
    static private final int TIFF_TYPE_UNDEFINED = 7;

    static private final int SAMPLE_FORMAT_UNSIGNED_INT = 1;
    static private final int SAMPLE_FORMAT_SIGNED_INT = 2;
    static private final int SAMPLE_FORMAT_FLOAT = 3;
    static private final int SAMPLE_FORMAT_UNDEFINED = 4;

    static private final int COMPRESSION_NO = 1;
    static private final int COMPRESSION_CCITT_HUFFMAN = 2;
    static private final int COMPRESSION_T4 = 3;
    static private final int COMPRESSION_T6 = 4;
    static private final int COMPRESSION_LZW = 5;
    static private final int COMPRESSION_JPEG_OLD = 6;
    static private final int COMPRESSION_JPEG_NEW = 7;
    static private final int COMPRESSION_DEFLATE = 8;

    static private final int PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO = 0;
    static private final int PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO = 1;
    static private final int PHOTOMETRIC_INTERPRETATION_RGB = 2;
    static private final int PHOTOMETRIC_INTERPRETATION_PALETTE = 3;
    static private final int PHOTOMETRIC_INTERPRETATION_TRANSPARENCY = 4;

    static private final int PLANAR_CONFIGURATION_CHUNKY = 1;
    static private final int PLANAR_CONFIGURATION_PLANAR = 2;

    static private final int RESOLUTION_UNIT_NO = 1;
    static private final int RESOLUTION_UNIT_INCH = 2;
    static private final int RESOLUTION_UNIT_CENTIMETER = 3;

    static public void main (String[] args) throws Exception {
        FileOutputStream output = new FileOutputStream("test.tif");
        TiffWriter writer = new TiffWriter();
        writer.start(output, imageWidth, imageHeight);
        for (int i = 0; i < 16; i++) {
            Pixmap pixmap = new Pixmap(...);
            writer.frame(pixmap, "run", i, 16);
        }
        writer.end();
        writer.close();
    }
}
田瀚
2023-03-14

我为我的TIFF ImageIO插件研究了这一点,据我所知,Photoshop在TIFF中存储层信息的方式是完全专有的,不使用标准TIFF机制,例如使用链接或嵌套IFD(330/SubIFD)或文件类型(254/NewSubFileType)的多页文档等。

相反,它将图层信息与图层图像数据一起存储在Photoshop特定的TIFF标签中;37724/ImageSourceData,其类型为UNDEFINED(或“仅字节”)。幸运的是,此标签的内容记录在Adobe Photoshop®TIFF技术说明中。

此标记的内容始终以0结尾的字符串“Adobe Photoshop Document Data Block”开头。其余内容是各种Photoshop资源,由Photoshop 4字节资源标识符8BIM标识,然后是每个资源的4字节资源键和4字节长度。

关于Photoshop层,该块中有趣的资源是用资源键Layr标识的资源。这与Photoshop文件格式的图层和遮罩信息部分中记录的结构相同。

我确实在PSD ImageIO插件中有读取这两种结构的代码,这可能值得一看,但它还不支持编写。

当您可以写入Photoshop TIFF标记的内容时,您应该能够将其作为TIFF IIOMetadata的一部分传递给TIFF ImageWriter,写入器将与您传递的任何其他元数据和像素数据一起写入。

所以,正如你所看到的,这一切(大部分)都是有记录的,并且在Java肯定是可行的,但仍然不是完全微不足道的。

 类似资料:
  • 问题内容: 我对用Java创建分层的tif感兴趣,以使Photoshop能够识别这些层。我能够创建多页tif,但是Photoshop无法将页面识别为图层。尽管可以使用Acrobat查看页面。有人知道Photoshop如何存储tif图层数据以及如何用Java生成吗? 谢谢。 问题答案: 我已经为TIFF ImageIO插件进行了研究,据我了解,Photoshop在TIFF中存储图层信息的方式是完全专

  • 我有一个火花数据框,我需要写入MongoDB。我想知道如何在mongoDB中将数据框的一些列写成嵌套/分层JSON。假设数据框有6列,col1,col2,…… col5,col6我想要col1,col2,col3作为第一层次结构,其余列col4到col6作为第二层次结构。像这样的东西, 我如何在pyspark中实现这一点?

  • JPEG、PNG 或 GIF 图像资源可以从 PSD 文件图层或图层组中的内容生成。将受支持的图像格式扩展添加到图层名称或图层组名称时,会自动生成资源。(可选)您也可以指定所生成图像资源的品质和大小参数。 从 PSD 文件生成图像资源对于多设备 Web 设计来说尤其有用。 视频:在 Photoshop 中生成 Web 资源视频:在 Photoshop 中生成 Web 资源这段视频会向您介绍如何通过

  • 创建图层和组 新图层将出现在“图层”面板中选定图层的上方,或出现在选定组内。 创建新图层或组 执行下列操作之一: 要使用默认选项创建新图层或组,请单击“图层”面板中的“创建新图层”按钮 或“新建组”按钮 。 选取“图层”>“新建”>“图层”或选取“图层”>“新建”>“组”。 从“图层”面板菜单中选择“新建图层”或“新建组”。 按住 Alt 键 (Windows) 或 Option 键 (Mac O

  • 本文向大家介绍MongoDB 中创建分层JSON,包括了MongoDB 中创建分层JSON的使用技巧和注意事项,需要的朋友参考一下 使用以下语法在MongoDB中创建分层JSON- 让我们创建一个包含文档的集合- 在find()方法的帮助下显示集合中的所有文档- 这将产生以下输出-

  • Adobe Photoshop 中的文字由基于矢量的文字轮廓(即以数学方式定义的形状)组成,这些形状描述字样的字母、数字和符号。许多字样可用于一种以上的格式,最常用的格式有 Type 1(又称 PostScript 字体)、TrueType、OpenType、New CID 和 CID 无保护(仅限于日语)。 Photoshop 保留基于矢量的文字轮廓,并在您缩放文字、调整文字大小、存储 PDF