最近在做一个java的图片管理器的课程设计,加入了一个读取psd格式的功能,之所以说加入,是因为jdk本身自带的ImageIO_API只持支持jpg、bmp、png、gif这几种格式的图片格式而已。

之前查过像是imagemagick之类的工具,我发现它们功能太多太强大了,而我想要做的只是读入psd格式文件,让其转化成BufferImage,顺带读取出图片的一点信息,仅此而已,所以我认为没有必要动用到这种航母级的工具。

主要要做的,是找出psd格式文件里面需要用到的字节,不需要用到的,例如图层路径等等的信息,则直接跳过。

下面是psd格式说明文档(4.0.1版本)里面的内容,可以看到它分为5个部分:

我用到的主要是第一部分的file header和最后一部分的image data,前者保存了图片的一些基本信息(长宽,通道数目等等),后者就是图片像素信息。

我把读取psd格式这一功能封装在一个叫PsdReader的类里面,首先是打开文件,我显式使用了FileChannel和MappedByteBuffer加快读取速度,起初用RandomAccessFile也是可以达到同样目的,但是效率过低。构造方法如下:

private BufferedImage img = null;//最终获得的目标图片
private int[] pixels;
private RandomAccessFile raf;
private int[] byteArray;
private int[][][] channelColor;//每条通道的颜色,0号位是red,1号位是green,2号位是blue,如果是4通道,alpha直接跳过
private int[][] numOfBytePerLine;//每行的字节数,扫描图片信息的时候是逐行进行的,因为rle压缩的关系,每行的字节数都不一定相同
private short numOfChannel;//通道的数目,如果是带有透明度,则是4通道,否则通常是3通道
private int height;
private int width;
private short isRle;//压缩方式,是0则没有压缩,是1则是rle压缩
private MappedByteBuffer mbbi;
public PsdReader(File file) {FileChannel fc = null;try {this.raf = new RandomAccessFile(file, "r");fc = raf.getChannel();long size = fc.size();this.mbbi = fc.map(FileChannel.MapMode.READ_ONLY, 0, size);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}this.readFile();//关键,读取图片文件中的信息img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);  pixels = new int[width*height];  this.initPixels(pixels);this.setRGB(img, 0, 0, width, height, pixels);try {fc.close();this.raf.close();} catch (IOException e) {e.printStackTrace();}}
private void readFile() {try {//-------第一部分:文件头------------------//通道数量this.mbbi.position(0x0c);//因为一开始的4个字节,是8bps(一个符号,我理解成这个标志代表此文件是psd格式文件,接着是两个字节的版本信息,接着是6个字节的保留位,从0x0c开始的两个字节,便是通道数目,之后亦是这样读取想要的信息,便不再累述,详细可参考下面给出的psd格式文档里面的信息表numOfChannel = this.mbbi.getShort();//图像高度height = this.mbbi.getInt();//图像宽度width = this.mbbi.getInt();//图像深度(每个通道的颜色位数)short depth = this.mbbi.getShort();//是rgb模式则type=3short type = this.mbbi.getShort();//--------第二部分:色彩模式信息,这部分的长度通常为0----int lenOfColorModel = this.mbbi.getInt();this.mbbi.position(lenOfColorModel+this.mbbi.position());//--------第三部分:图像资源数据------------------int lenOfImageResourceBlock = this.mbbi.getInt();//System.out.println("lenOfImageResourceBlock="+lenOfImageResourceBlock);this.mbbi.position(lenOfImageResourceBlock+this.mbbi.position());//--------第四部分:图层与蒙版信息----------------int lenOfLayerInfo = this.mbbi.getInt();this.mbbi.position(lenOfLayerInfo+this.mbbi.position());//--------第五部分:图像数据--------------------isRle = this.mbbi.getShort();} catch (Exception e1) {e1.printStackTrace();}

拿到位于文件最后的image data之后,便开始主要操作了:解压。

从上图可以看到,image data是以两个字节的compression开始的,如果图片未经过压缩,则这两个字节是00,否则是01,用editplus等工具的16进制查看器里面可以查看出来,起初我就是这样查找它的压缩规律的,后来发现这样做太愚蠢了。

因为psd格式文档里面明确写道:

由此看出,压缩方式是固定的packBits压缩方式,百度一下可以找到不少的关于此种压缩的解压和编码方式,这里就不累述了,下面是我用java实现的解压方法:

private void unpackbits(int lenOfInput, int[] channelColor) {short n = 0;int last = 0;while(lenOfInput>0){try {
//          n = raf.readByte();n = this.mbbi.get();lenOfInput--;} catch (Exception e) {e.printStackTrace();}if(0<=n && n<=127) {int repeatTime = n;++repeatTime;for(int t=0; t<repeatTime; t++) {try {
//                  channelColor[last+t] = raf.readUnsignedByte();int ti = this.mbbi.get();if(ti<0) { ti += 256; }channelColor[last+t] = ti;lenOfInput--;} catch (Exception e) {e.printStackTrace();}}last += repeatTime;}else if(-1>=n && n>=-127) {int val = 0;int repeatTime = -n;++repeatTime;try {
//              val = raf.readUnsignedByte();int ti = this.mbbi.get();if(ti<0) { ti += 256; }val = ti;//System.out.println(val);lenOfInput--;} catch (Exception e) {e.printStackTrace();}for(int t=0; t<repeatTime; t++) {channelColor[last+t] = val;}last += repeatTime;}else if(n==-128) {//noop}}}

如果图片是不经过压缩的,那就省略解压这一步,直接将像素数据塞进数组里面就行了。

下面是我写的整个PsdReader的完整代码:

package com.zer0w0.entity;import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;public class PsdReader {private BufferedImage img = null;private int[] pixels;private RandomAccessFile raf;private int[] byteArray;//用来接住unsignedByte,byte不存作负数(否则抛异常,说越过颜色范围)private int[][][] channelColor;private int[][] numOfBytePerLine;
//  private final static int RED = 0;
//  private final static int GREEN = 1;
//  private final static int BLUE = 2;private short numOfChannel;private int height;private int width;private short isRle;private MappedByteBuffer mbbi;public PsdReader(File file) {FileChannel fc = null;try {this.raf = new RandomAccessFile(file, "r");fc = raf.getChannel();long size = fc.size();this.mbbi = fc.map(FileChannel.MapMode.READ_ONLY, 0, size);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}this.readFile();img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);  pixels = new int[width*height];  this.initPixels(pixels);this.setRGB(img, 0, 0, width, height, pixels);try {fc.close();this.raf.close();} catch (IOException e) {e.printStackTrace();}}public BufferedImage getImg() {return img;}private void initPixels(int[] pixels) {int index = 0;int a = 255;for(int h=0; h<this.height; h++) {for(int w=0; w<this.width; w++) {int r = this.channelColor[0][h][w];int g = this.channelColor[1][h][w];int b = this.channelColor[2][h][w];if(this.numOfChannel>3) {a = this.channelColor[3][h][w];}pixels[index] = (a<<24) | (r<<16)| (g<<8) | b;index++;}}}private void setRGB( BufferedImage image, int x, int y, int width, int height, int[] pixels ) {  int type = image.getType();  if ( type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB )  image.getRaster().setDataElements( x, y, width, height, pixels );  else  image.setRGB( x, y, width, height, pixels, 0, width );  }private void readFile() {try {//-------第一部分:文件头------------------//通道数量
//          this.raf.seek(0x0c);this.mbbi.position(0x0c);
//          numOfChannel = this.raf.readShort();numOfChannel = this.mbbi.getShort();//System.out.println("numOfChannel="+numOfChannel);//图像高度
//          height = this.raf.readInt();height = this.mbbi.getInt();//System.out.println("height="+height);//图像宽度
//          width = this.raf.readInt();width = this.mbbi.getInt();//System.out.println("width="+width);//图像深度(每个通道的颜色位数)
//          short depth = this.raf.readShort();short depth = this.mbbi.getShort();//System.out.println("depth="+depth);//是rgb模式则type=3
//          short type = this.raf.readShort();short type = this.mbbi.getShort();//System.out.println("type="+type);//--------第二部分:色彩模式信息,这部分的长度通常为0----
//          int lenOfColorModel = raf.readInt();int lenOfColorModel = this.mbbi.getInt();//System.out.println("lenOfColorModel="+lenOfColorModel);
//          this.raf.seek(lenOfColorModel+this.raf.getFilePointer());//长度信息占4个字节,但是不用加,下同this.mbbi.position(lenOfColorModel+this.mbbi.position());//--------第三部分:图像资源数据------------------
//          int lenOfImageResourceBlock = raf.readInt();int lenOfImageResourceBlock = this.mbbi.getInt();//System.out.println("lenOfImageResourceBlock="+lenOfImageResourceBlock);
//          this.raf.seek(lenOfImageResourceBlock+this.raf.getFilePointer());this.mbbi.position(lenOfImageResourceBlock+this.mbbi.position());//--------第四部分:图层与蒙版信息----------------
//          int lenOfLayerInfo = raf.readInt();int lenOfLayerInfo = this.mbbi.getInt();//System.out.println("lenOfLayer="+lenOfLayerInfo);
//          this.raf.seek(lenOfLayerInfo+raf.getFilePointer());this.mbbi.position(lenOfLayerInfo+this.mbbi.position());//--------第五部分:图像数据--------------------
//          isRle = raf.readShort();isRle = this.mbbi.getShort();//System.out.println("isRle="+isRle);
//          //System.out.println("nowPosition="+this.raf.getFilePointer());//System.out.println("nowPosition="+this.mbbi.position());} catch (Exception e1) {e1.printStackTrace();}this.channelColor = new int[numOfChannel][height][width];if(isRle==1){this.numOfBytePerLine = new int[numOfChannel][height];for(int i=0; i<numOfChannel; i++) {for(int j=0; j<height; j++) {try {//TODO
//                  this.numOfBytePerLine[i][j] = this.raf.readUnsignedShort();int ti = this.mbbi.getShort();if(ti<0) { ti += 65536; }this.numOfBytePerLine[i][j] = ti;} catch (Exception e) {e.printStackTrace();}}}for(int c=0; c<numOfChannel; c++) {for(int h=0; h<height; h++) {this.unpackbits(numOfBytePerLine[c][h],channelColor[c][h]);}}}else if(isRle==0) {for(int c=0; c<numOfChannel; c++) {for(int h=0; h<height; h++) {for(int w=0; w<width; w++) {try {
//                          this.channelColor[c][h][w] = this.raf.readUnsignedByte();int ti = this.mbbi.get();if(ti<0) { ti += 256; }this.channelColor[c][h][w] = ti;} catch (Exception e) {e.printStackTrace();}}}}}}private void unpackbits(int lenOfInput, int[] channelColor) {short n = 0;int last = 0;while(lenOfInput>0){try {
//          n = raf.readByte();n = this.mbbi.get();lenOfInput--;} catch (Exception e) {e.printStackTrace();}if(0<=n && n<=127) {int repeatTime = n;++repeatTime;for(int t=0; t<repeatTime; t++) {try {
//                  channelColor[last+t] = raf.readUnsignedByte();int ti = this.mbbi.get();if(ti<0) { ti += 256; }channelColor[last+t] = ti;lenOfInput--;} catch (Exception e) {e.printStackTrace();}}last += repeatTime;}else if(-1>=n && n>=-127) {int val = 0;int repeatTime = -n;++repeatTime;try {
//              val = raf.readUnsignedByte();int ti = this.mbbi.get();if(ti<0) { ti += 256; }val = ti;//System.out.println(val);lenOfInput--;} catch (Exception e) {e.printStackTrace();}for(int t=0; t<repeatTime; t++) {channelColor[last+t] = val;}last += repeatTime;}else if(n==-128) {//noop}}}
}

使用new PsdReader(new File(psd文件的路径)).getImg()便可以拿到这个psd文件的BufferedImage了。以上,转载请注明出处。

java实现psd格式图片读入相关推荐

  1. java解析webp格式图片宽高;java解析webp图片转png格式

    java解析webp格式图片宽高:java解析webp图片转png格式 package 你的包名:***.***.***.***;import java.io.FileInputStream; imp ...

  2. java根据jpg格式图片或视频文件生成gif动图

    1. 多张jpg图片制作GIF <dependency>   <groupId>com.madgag</groupId>   <artifactId>a ...

  3. linux上打开psd格式图片--gimp

    psd格式,是Adobe Photoshop的图片格式,在windows上打开编辑当然没有问题,但是在linux上怎么打开呢,对于我这个linux菜鸟来说,还真是个问题.之前只熟悉png格式的图片,知 ...

  4. java获取webp格式图片宽和高 以及普通文件的宽和高 图片宽高

    try {// ImageIO.read报错的解决方法String fileType = ImgeMimeTypeUtils.getMimeType(facePathFile.getAbsoluteP ...

  5. psd格式图片一键切图

    转载于:https://www.cnblogs.com/yaomengli/p/7052788.html

  6. PSD格式怎么转换成JPG?这几种方法轻易转换图片格式

    PSD格式图片通常比较大,转换成JPG格式可以大大减小文件大小,从而节省存储空间和下载时间.此外,大多数图像查看器和编辑器都支持JPG格式,因此转换成JPG格式可以方便地共享和传输图片.如果您需要将图 ...

  7. 计算机中psd是什么文件格式,教你psd格式用什么打开

    现在的文件格式五花八门,在下载文件的时候难免会遇到一些特殊的文件格式,有些小伙伴就遇到了psd格式的文件,但是不知如何打开.今天,我就给大家分享一下如何打开psd格式文件的方法. psd是图像处理软件 ...

  8. 1分钟学会在Java中将PSD转换为PNG等图片格式,你可能差一个它

    PSD格式由Adobe Photoshop使用,以保存与图形设计有关的数据.PSD文件可以由集体形成图形的单个或多个图层组成.但是,将PSD图像直接查看或嵌入到Web或桌面应用程序中是不可行的. 为了 ...

  9. java如何把png转换成jpg_Java实现将png格式图片转换成jpg格式图片的方法【测试可用】...

    Java实现将png格式图片转换成jpg格式图片的方法[测试可用] 发布于 2020-4-9| 复制链接 摘记: 本文实例讲述了Java实现将png格式图片转换成jpg格式图片的方法.分享给大家供大家 ...

  10. 使用java将word文档docx,doc(包含图形,文本框)完美转换成所有格式图片(pdf,png,gif,jpeg等等)

    使用java将word文档docx,doc(包含图形,文本框,图片等)完美转换成所有格式图片(pdf,png,gif,jpeg等等)下文中附带代码,效果图等 思路 使用到的包 实现代码 效果图: 思路 ...

最新文章

  1. Foundations of Qt Development 学习笔记 Part1 Tips1-50
  2. java word openoffice_java 使用openoffice 转换文档,成.pdf,实现在线预览效果
  3. Hadoop Yarn常用参数配置项整理
  4. 基于vue自动化表单实践
  5. 《解密小米之互联网下的商业奇迹》
  6. SEO 百度后台主动推送链接
  7. c++ 线性回归_模型之母:简单线性回归的代码实现
  8. 均值(信息学奥赛一本通-T1060)
  9. 斗地主AI算法——第二章の数据结构
  10. linux怎样安装麒麟双系统,U盘启动中标麒麟V6双系统安装教程
  11. QCIF、CIF、DCIF、D1分辨率
  12. python爬虫实例项目大全-GitHub 上有哪些优秀的 Python 爬虫项目?
  13. 二维图像(数组)的fftshift
  14. 单维度量表验证性因子分析_探索性因子分析(EFA)和验证性因子分析(CFA)
  15. 【Fate/kaleid liner 魔法少女☆伊莉雅】系列中实践的、新世代的动画摄影工作流...
  16. 兴趣专业测试软件,测试你的专业兴趣是什么
  17. iOS 实现3Dtouch
  18. 十个全网最具创意的聊天机器人:漫威和联合国儿童基金会都在尝试使用聊天机器人...
  19. 多线程应用_左圆右方
  20. 一文学会炫酷图表利器pyecharts!

热门文章

  1. 微信小程序简单爱心点赞动画
  2. 浅谈学习的深度和广度
  3. 基于视觉的移动平台运动目标检测
  4. PC-DMIS 2019 CAD 模型坐标系的转换
  5. 实验6 地理数据可视化
  6. Wireshark 301: Spying on what people are downloading (Part 1 of 2)
  7. 新商业模式的“分歧者”——汇新云
  8. 应广单片机长按开关机_单键实现单片机开关机设计案例
  9. 上海十大it外包公司
  10. nvme linux raid,牛气!博通的NVMe RAID卡