我感兴趣的是用Java创建一个分层tif,使Photoshop能够识别层。我能够创建多页tif,但Photoshop无法将这些页面识别为层。不过,Acrobat可以查看这些页面。谁知道Photoshop是如何存储tif层数据的,以及如何用Java生成这些数据?
谢谢
我开始了一个基于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();
}
}
我为我的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