home > 投稿 > PsdReader.js
2011/09/04

PsdReader.js


PSDから画像を取得。Chrome、FireFoxのみ。

FileReader メモ
・Safari とスマホに FileReader がない。 File API も最小限。
・FireFox は File API に lastModifiedDate がない。

//こんな感じで使う。

var reader		= new FileReader(file);
var uploader	= new FileUploader();
var psd;
reader.onload	= function(e){
	psd 		= new PsdReader(e.target.result);
	var base64	= psd.getDataURL().split(',').pop()
	uploader.upload(base64);
}
reader.readAsBinaryString(file);

PsdReader

function PsdReader(file){this.initialize.apply(this, arguments);}
PsdReader.prototype = {
	//classPhpPsdReader.php
	//http://www.phpclasses.org/browse/file/17603.html
	initialize:function(str){
		var s = this.data = new BinaryStream(str);
		this.info	= {};
		if(this.data.read(4)=='8BPS'){
			$log('psd');
			//Photoshop PSD 解析例
			//http://hp.vector.co.jp/authors/VA032610/operation/sample/PSD.htm
			this.info.versionId 		= this.data.getUShort();
			this.data.seek(6,'SEEK_CUR');
			this.info.channels			= this.data.getUShort();
			this.info.height			= this.data.getULong();
			this.info.width				= this.data.getULong();
			this.info.colorDepth		= this.data.getUShort();//色深度
			this.info.colorMode			= this.data.getUShort();//カラーモード
			this.info.colorModeData		= this.data.getSection(false);
			this.info.imageResources	= this.data.getSection(false);
			this.info.layerMaskData		= this.data.getSection(false);
			this.info.compressionType	= this.data.getUShort();
			this.info.oneColorChannelPixelBytes	= this.info.colorDepth/8;
			this.colorBytesLength		= this.info.height*this.info.width*this.info.oneColorChannelPixelBytes;
			
			if (this.info.colorMode==2) {
				this.info.error = 'images with indexed colours are not supported yet';
				return false;
			}
			$log(this.info);
		}else{
			this.info.error = 'invalid or unsupported psd';
			return false;
		}
	},
	
	getDataURL:function(){
		if(!this.hasOwnProperty('canvas')) this.draw();
		return this.canvas.toDataURL();
	},
	
	getImageData:function(){
		if(!this.hasOwnProperty('imageData')) this.draw();
		return this.imageData;
	},
	
	draw:function(){
		switch(this.info.compressionType) {
			case 1	: var s = this.unpackData();break;
			default	: var s = this.data;
		}
		this.canvas			= document.createElement('canvas');
		this.canvas.width	= this.info.width;
		this.canvas.height	= this.info.height;
		this.context		= this.canvas.getContext('2d');
		this.imageData		= this.context.getImageData(0,0,this.info.width,this.info.height); 
		//$('#Container').append(this.canvas);
		stopwatch.push(new Date());
		this.setImageDataByRGB8(s);
		this.context.putImageData(this.imageData,0,0);
	},
	
	setImageDataByRGB8:function(obj){
		var length = this.info.width*this.info.height;
        for(var channel	=0;channel<3;channel++){
        	var index	= channel;
        	var start	= obj.pointer + this.colorBytesLength*channel;
        	var end		= start + length;
			for(var p=start;p<end;p++){
				this.imageData.data[index] = obj.data.charCodeAt(p);
				index += 4;
			}
		}
		index = 3;
		for(var p=0;p<length;p++){
			this.imageData.data[index] = 0xff;
			index += 4;
		}
	},
	
	unpackData:function(){
		this.info.scanLinesByteCounts = [];
		for (var i=0; i<this.info.height*this.info.channels; i++){ 
			this.info.scanLinesByteCounts.push(this.data.getUShort());
		}
		var s = '';
		for (var i=0; i<this.info.scanLinesByteCounts.length; i++) {
			s += this.getPackedBitsDecoded(this.data.read(this.info.scanLinesByteCounts[i]));
		}
		stopwatch.push(new Date());
		return new BinaryStream(s);
	},
	
	getPackedBitsDecoded:function(s){
		var p = 0;
		var returnString = '';
		while (1) {
			if (p<s.length){
				var c = s.charCodeAt(p)
				var headerByteValue = this.toSignedChar(c);
			}else{
				return returnString;
			}
			p++;
			if (headerByteValue >= 0) {
				for (i=0; i <= headerByteValue; i++) {
					returnString += s.charAt(p);
					p++;
				}
			} else {
				if (headerByteValue != -128) {
					var copyByte = s.charAt(p);
					p++;
					for (i=0; i < (1-headerByteValue); i++) {
						returnString += copyByte;
					}
				}
			}
		}
	},
	toSignedChar:function(c){
		c = c & 0xff;
		if (c<128) return c;
		else return c-256;
	}
}

BinaryStream

function BinaryStream(){this.initialize.apply(this, arguments);}
BinaryStream.prototype = {
	initialize:function(string){
		this.data = string;
		this.pointer = 0;
	},
	getULong:function(){
		var s = this.read(4);
		return (((((s.charCodeAt(0)<<8) + s.charCodeAt(1))<<8) + s.charCodeAt(2))<<8) +s.charCodeAt(3);
	},
	getUShort:function(){
		var s = this.read(2);
		return (s.charCodeAt(0) << 8) + s.charCodeAt(1);
	},
	getUChar:function(){
		var s = this.read(1);
		return s.charCodeAt(0);
	},
	getSection:function(isReturn){
		var sectionLength = this.getULong();
		if(isReturn===false){
			this.seek(sectionLength,'SEEK_CUR');
			return null;
		}else{
			return this.read(sectionLength);
		}
	},
	read:function(bytes){
		var p = this.pointer + bytes;
		var s = this.data.slice(this.pointer,p);
		this.pointer = p;
		return s;
	},
	_getInteger:function(byteCount) {
		switch (byteCount) {
			case 4	:return this.getULong();break;
			case 2	:return this.getUShort();break;
			default	:return this.getUChar();
				//return hexdec($this->_hexReverse(bin2hex(fread($this->fp,$byteCount))));
		}
	},
	tell:function()		{return this.pointer;},
	seek:function(offset,whence){
		switch(whence){
			case 'SEEK_CUR'	:this.pointer += offset;break;
			default			:this.pointer  = offset;
		}
	}
}

トラックバックURL

http://faces2.bascule.co.jp/mt/mt-tb.cgi/610

コメントを投稿

(コメントには承認が必要になることがあります。承認されるまではコメントは表示されません。)