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;
}
}
}