/* This file is part of "psdrecover", a File Format plugin for Adobe Photoshop Copyright (C) 2007-2011 Toby Thain, toby@telegraphics.com.au This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "psdrecover.h" #include "ui.h" #define MAXCH 64 extern long maxData; struct psd_header header; unsigned char *indata, *rledata; int rb, chunk, top, map[MAXCH], chindex; struct channel_info merged_chan[MAXCH]; unsigned modechannels[16] = {1,1,1,3,4,3,3,0,1,3,1,3,3,4,0,1}; // 0->multichannel struct layer_info *layer; uint32_t hres, vres; // set during dopsd() processing of image resources // Image data is returned channel by channel (non-interleaved), // one chunk at a time. // This function uses the following globals: // chindex : index of Photoshop image channel (0 .. planes-1) // indata : buffer for image data returned to Photoshop // top : index of chunk's top row // rb : row byte count expected by Photoshop // rledata : pre-allocated row decompression buffer // layer : pointer to layer info, or NULL if merged image is being processed // map : if layer, array mapping Photoshop channel indexes to PSD channel index // merged_chan : array of channel information for merged image static void getchannel(FormatRecordPtr pb){ int row; unsigned char *inrow; //{char s[0x100];sprintf(s,"getchannel(): chindex=%d top=%d",chindex,top);dbg(s);} pb->loPlane = pb->hiPlane = chindex; pb->data = indata; pb->theRect.left = 0; pb->theRect.right = pb->imageSize.h; pb->theRect.top = top; pb->theRect.bottom = top + chunk; if(pb->theRect.bottom > pb->imageSize.v) pb->theRect.bottom = pb->imageSize.v; // loop for each row of this chunk for(row = top, inrow = indata; row < pb->theRect.bottom; ++row, inrow += rb){ // locate, read, and decompress a single image row readunpackrow((FILEREF)pb->dataFork, // pointer to channel information; this is re-mapped for layers, // but for merged data, just follows channel order in file: whichlayer ? &layer->chan[map[chindex]] : &merged_chan[chindex], row, // row index in channel inrow, // where to store decompressed row data rledata); // decompression buffer } } static OSErr read_colour_table(FormatRecordPtr pb, FILEREF f){ FILEPOS chpos; FILECOUNT count; unsigned n; OSErr e; if(!(e = GETFPOS(f, &chpos)) && !(e = SETFPOS(f, fsFromStart, header.colormodepos))) { n = get4B(f)/3; if(n > 256) n = 256; pb->lutCount = count = n; (e = FSREAD(f, &count, pb->redLUT)) || (e = FSREAD(f, &count, pb->greenLUT)) || (e = FSREAD(f, &count, pb->blueLUT)) || (e = SETFPOS(f, fsFromStart, chpos)); } return e; } OSErr read_start(FormatRecordPtr pb, intptr_t *data){ FILEREF f = (FILEREF)pb->dataFork; int ch, i; OSErr e; char s[0x100]; hres = vres = 72L<<16; // default res indata = rledata = NULL; if(dopsd(f, "", &header)){ whichlayer = 0; // default to opening merged image if(!header.nlayers || picklayerdialog(pb)){ // set up parameter block to describe returned image pb->imageMode = header.mode; pb->depth = header.depth; if(whichlayer){ layer = &header.linfo[whichlayer-1]; pb->imageSize.h = layer->right - layer->left; pb->imageSize.v = layer->bottom - layer->top; pb->planes = layer->channels; for(ch = 0; ch < layer->channels; ++ch){ if(layer->chan[ch].id >= 0 && layer->chan[ch].id < layer->channels){ // a positive channel ID corresponds to an image channel //sprintf(s, "map[%d]=%d", layer->chan[ch].id, ch); dbg(s); map[layer->chan[ch].id] = ch; } else if(layer->chan[ch].id != TRANS_CHAN_ID){ // ignore layer masks, user layer masks, and any other IDs --pb->planes; //sprintf(s, "skipping channel %d (id = %d)", ch, layer->chan[ch].id); dbg(s); } } } else{ // read merged data, not a layer pb->imageSize.h = header.cols; pb->imageSize.v = header.rows; pb->planes = header.channels; if((e = SETFPOS(f, fsFromStart, header.lmistart + header.lmilen))) return e; } rb = ((long)pb->imageSize.h*pb->depth + 7) >> 3; chunk = maxData/rb; if(chunk < 1) chunk = 1; else if(chunk > pb->imageSize.v) chunk = pb->imageSize.v; if(!(indata = checkmalloc(chunk*rb)) || !(rledata = checkmalloc(2*rb))) return memFullErr; pb->rowBytes = rb; pb->colBytes = pb->depth == 16 ? 2 : 1; //pb->planeBytes ignored if one plane pb->imageHRes = hres; pb->imageVRes = vres; if(header.mode == plugInModeIndexedColor) if( (e = read_colour_table(pb, f)) ) return e; if(whichlayer){ // If processing a layer, and a channel was identified // as a transparency channel, map this to the last channel // returned to Photoshop. (This cannot be an image channel // because the layer's channel count already included it.) if(layer->chindex[TRANS_CHAN_ID] != -1){ pb->transparencyPlane = pb->planes-1; map[pb->planes-1] = layer->chindex[TRANS_CHAN_ID]; //sprintf(s, "map[%d]=%d transparencyPlane=%d", pb->planes-1, map[pb->planes-1], pb->transparencyPlane); dbg(s); } } else{ // If processing the merged image data, transparency/alpha // for the image, if present (per flag), is the first non-image channel. if(header.mergedalpha){ pb->transparencyPlane = modechannels[header.mode]; //sprintf(s, "transparencyPlane=%d", pb->transparencyPlane); dbg(s); } } // fill matrix with file positions of every row if(whichlayer){ for(i = 0; i <= (whichlayer - 1); ++i) for(ch = 0; ch < header.linfo[i].channels; ++ch) dochannel(f, &header.linfo[i], header.linfo[i].chan + ch, 1/*count*/, &header); } else{ dochannel(f, NULL, merged_chan, header.channels, &header); } chindex = top = 0; getchannel(pb); return noErr; } return userCanceledErr; } return formatCannotRead; } OSErr read_continue(FormatRecordPtr pb, intptr_t *data){ top += chunk; if(top >= pb->imageSize.v){ top = 0; ++chindex; } if(chindex < pb->planes) getchannel(pb); else pb->data = NULL; // finished return noErr; } OSErr read_finish(FormatRecordPtr pb, intptr_t *data){ int i, ch; free(indata); free(rledata); // free channel row pointers allocated by dochannel() for(i = 0; i < header.nlayers; ++i){ if(header.linfo[i].chan){ for(ch = 0; ch < header.linfo[i].channels; ++ch) if(header.linfo[i].chan[ch].rowpos) // we expect some of these to be NULL free(header.linfo[i].chan[ch].rowpos); free(header.linfo[i].chan); free(header.linfo[i].chindex-3); // hack :( free(header.linfo[i].name); free(header.linfo[i].nameno); } } if(whichlayer == 0){ for(ch = 0; ch < header.channels; ++ch) free(merged_chan[ch].rowpos); } free(header.linfo); return noErr; }