/* This file is part of icoformat, a Windows Icon (ICO) File Format plugin for Adobe Photoshop Copyright (C) 2002-2010 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 "icoformat.h" #include "ico_io.h" #include "ui.h" #include "png.h" OSErr readicon(FormatRecordPtr pb,ICONDIRENTRY *pide, ICONIMAGE *pii); OSErr readICO(FormatRecordPtr pb,ICONDIRENTRY *pide, ICONIMAGE *pii); OSErr readctable(FormatRecordPtr pb, ICONIMAGE *pii); BufferID outBufferID; // may be set by readpng unsigned char *outdata = NULL; // may be set by readpng int iconcount; StringPtr name; FILEREF file; OSErr readctable(FormatRecordPtr pb, ICONIMAGE *pii){ OSErr e = noErr; RGBQUAD *ctab,*p; long count; int i,ncols; if(pii->icHeader.biBitCount <= 8){ ncols = pii->icHeader.biClrUsed ? pii->icHeader.biClrUsed : 1 << pii->icHeader.biBitCount; if( (ctab = malloc(count = sizeof(RGBQUAD)*ncols)) ){ if( !(e = FSRead((FILEREF)pb->dataFork, &count, ctab)) ){ for(i = 0, p = ctab; i < ncols; ++i, ++p){ pb->redLUT[i] = p->rgbRed; pb->greenLUT[i] = p->rgbGreen; pb->blueLUT[i] = p->rgbBlue; } // pad out the rest of Photoshop's colour table with black for(; i < 0x100; ++i) pb->redLUT[i] = pb->greenLUT[i] = pb->blueLUT[i] = 0; #ifdef PS6 pb->lutCount = ncols; // PS 5.5 or later #endif } free(ctab); }else e = memFullErr; } return e; } // it's a traditional (pre-Vista) ICO with a BITMAPINFOHEADER OSErr readICO(FormatRecordPtr pb, ICONDIRENTRY *pide, ICONIMAGE *pii){ long count; int outrb,inrb,transrb,i,j,flipv,transmask,instep,pix,rowslop,transxor,mask,hasalpha; unsigned char *inbuf,*in,*out,*inrow,*outrow,*trans,*transrow; OSErr e = noErr; BITMAPINFOHEADER *hdr = &pii->icHeader; pb->depth = 8; // only 8 is valid hasalpha = hdr->biBitCount == 32; if(hdr->biBitCount <= 8){ if(hdr->biPlanes == 1) pb->imageMode = plugInModeIndexedColor; else return formatCannotRead; }else if(hdr->biBitCount == 16 || hdr->biBitCount == 24 || hdr->biBitCount == 32) pb->imageMode = plugInModeRGBColor; else return formatCannotRead; if( !(e = readctable(pb,pii)) ) { // normally, the ICO bitmaps are stored bottom row first // but check for a negative height, which would signal a top-down bitmap flipv = hdr->biHeight > 0; pb->imageSize.h = hdr->biWidth; pb->imageSize.v = hdr->biHeight/2; if(!hdr->biBitCount) return formatCannotRead; else if(hdr->biBitCount > 8){ // reading an RGB mode image pb->planes = 3 + hasalpha + 1; instep = hdr->biBitCount/8; // bytes per pixel rowslop = 0; }else{ // reading an Indexed mode image pb->planes = hdr->biPlanes + 1; instep = hdr->biPlanes; // bytes per packed pixel rowslop = 16/hdr->biBitCount; // end-of-row slop for unpacking image and transparency rows } /* adding 0.5 achieves rounding */ pb->imageHRes = hdr->biXPelsPerMeter ? (Fixed)(0.5+(hdr->biXPelsPerMeter / (39.37/65536.))) : DEFAULT_RES; pb->imageVRes = hdr->biYPelsPerMeter ? (Fixed)(0.5+(hdr->biYPelsPerMeter / (39.37/65536.))) : DEFAULT_RES; SETRECT(pb->theRect,0,0,pb->imageSize.h,pb->imageSize.v); pb->loPlane = 0; pb->hiPlane = pb->planes-1; pb->colBytes = pb->planes; pb->rowBytes = outrb = ((long)pb->imageSize.h*pb->depth*pb->planes+7)/8 + rowslop; // note extra slop needed for unpacking! pb->planeBytes = 1; // planes are interleaved // pb->planeBytes = outrb*(long)pb->imageSize.v; #ifdef PIFmtCanWriteTransparency if( pb->imageMode == plugInModeRGBColor && hasalpha ) pb->transparencyPlane = 3; // PS 6.0 or later #endif transxor = 0; // compute input row stride (XOR bitmap) inrb = ICONROWBYTES( (long)pb->imageSize.h*hdr->biBitCount*hdr->biPlanes ); // compute transparency row stride (AND bitmap), 1 plane of 1 bit mask transrb = ICONROWBYTES(pb->imageSize.h); // allocate a buffer for output data e = PS_BUFFER_ALLOC(outrb*(long)pb->imageSize.v, &outBufferID); if(e == noErr) { DBG("got output buffer"); pb->data = outdata = (unsigned char*)PS_BUFFER_LOCK(outBufferID, true); // tell Photoshop where it is DPRINTF("inrb=%d transrb=%d outrb=%d instep=%d rowslop=%d imageSize=%d,%d planes=%d loPlane=%d hiPlane=%d colBytes=%d rowBytes=%ld planeBytes=%ld data=%#lx", inrb,transrb,outrb,instep,rowslop, pb->imageSize.h,pb->imageSize.v,pb->planes,pb->loPlane,pb->hiPlane, pb->colBytes,pb->rowBytes,pb->planeBytes,pb->data); count = (inrb+transrb)*(long)pb->imageSize.v; // allocate buffer for reading if( (inbuf = malloc(count)) ){ DBG("got input buffer"); // now read all image data if( !(e = FSRead((FILEREF)pb->dataFork,&count,inbuf)) ){ outrow = outdata; if(flipv){ outrow += (long)(pb->imageSize.v-1)*outrb; outrb = -outrb; } for(j = pb->imageSize.v, inrow = inbuf, transrow = inbuf+inrb*(long)pb->imageSize.v ; j-- ; inrow += inrb, outrow += outrb, transrow += transrb) { //DPRINTF("row: j=%d inrow=%#lx trans=%#lx outrow=%#lx",j,inrow,trans,outrow); transmask = 0x80; #define TRANS_PIXEL \ *out++ = transxor ^ ((*trans & transmask) ? 0xff : 0); \ if(!(transmask >>= 1)){ \ ++trans; \ transmask = 0x80; \ } // process one row for(in = inrow, out = outrow, trans=transrow, i = 0; i < pb->imageSize.h; in += instep) { switch(hdr->biBitCount){ case 1: // unpack eight 1-bit pixels from input byte for(mask = 0x100; mask >>= 1;){ *out++ = (*in & mask) != 0; TRANS_PIXEL; } i += 8; break; case 4: // unpack two 4-bit pixels from input byte *out++ = *in >> 4; TRANS_PIXEL; *out++ = *in & 0xf; TRANS_PIXEL; i += 2; break; case 8: // copy one 8-bit pixel memcpy(out,in,instep); // only need memcpy if instep can be >1 !! out += instep; ++i; TRANS_PIXEL; break; case 16: // expand a 16-bit pixel into Photoshop's RGB planes pix = (in[1] << 8) | in[0]; *out++ = pix >> 10; *out++ = (pix >> 5) & 0x1f; *out++ = pix & 0x1f; ++i; TRANS_PIXEL; break; case 24: *out++ = in[2]; //R *out++ = in[1]; //G *out++ = in[0]; //B ++i; TRANS_PIXEL; break; case 32: *out++ = in[2]; //R *out++ = in[1]; //G *out++ = in[0]; //B *out++ = in[3]; //alpha ++i; TRANS_PIXEL; break; } } } } free(inbuf); } DBG("done loop"); } } return e; } #define IS_PNG(pii) ((pii)->icHeader.biSize == PNG_MAGIC) OSErr readicon(FormatRecordPtr pb, ICONDIRENTRY *pide, ICONIMAGE *pii){ return IS_PNG(pii) ? readPNG(pb, pide, pii) : readICO(pb, pide, pii); } OSErr getimageinfo(FILEREF f, ICONDIRENTRY *pide, ICONIMAGE *pii, int idx, char *desc){ OSErr e; *desc = 0; // set description to empty string (ordinary ICO) if( !(e = SetFPos(f, fsFromStart, FIRST_ICONDIRENTRY + SIZEOF_ICONDIRENTRY*idx)) && !(e = read_icondirentry(f, pide)) ){ DPRINTF("ICONDIRENTRY[%d]: bWidth=%d bHeight=%d bColorCount=%d wPlanes=%d wBitCount=%d dwBytesInRes=%ld dwImageOffset=%ld", idx,pide->bWidth,pide->bHeight,pide->bColorCount, pide->wPlanes,pide->wBitCount,pide->dwBytesInRes,pide->dwImageOffset); if( !(e = SetFPos(f, fsFromStart, pide->dwImageOffset)) && !(e = read_bitmapinfoheader(f, &pii->icHeader)) ){ if(IS_PNG(pii)){ // it's a PNG getPNGinfo(f, pide, pii, desc); }else{ // it's an ICO, with BITMAPINFOHEADER // position file past header if(pii->icHeader.biSize > SIZEOF_BMIHEADER) e = SetFPos(f, fsFromMark, pii->icHeader.biSize - SIZEOF_BMIHEADER); } DPRINTF("ICONIMAGE.BITMAPINFOHEADER: biSize=%ld biWidth=%ld biHeight=%ld biPlanes=%d biBitCount=%d biSizeImage=%ld (biCompression=%ld biClrUsed=%ld)", pii->icHeader.biSize,pii->icHeader.biWidth,pii->icHeader.biHeight, pii->icHeader.biPlanes,pii->icHeader.biBitCount,pii->icHeader.biSizeImage, pii->icHeader.biCompression,pii->icHeader.biClrUsed); } } return e; } OSErr read_start(FormatRecordPtr pb){ ICONDIR idir; OSErr e; ICONIMAGE ii; char desc[32]; outdata = NULL; file = (FILEREF)pb->dataFork; /* used in ui.c */ if( !(e = read_icondir((FILEREF)pb->dataFork, &idir)) ){ // sniff for filetypes sometimes renamed as ICO if( (idir.idReserved != 0x4d42 || warnformat(pb, pb->fileSpec->name, "BMP")) && (idir.idReserved != 0x4947 || warnformat(pb, pb->fileSpec->name, "GIF")) && (idir.idReserved != 0xd8ff || warnformat(pb, pb->fileSpec->name, "JPEG")) // relax the check on idType and just issue a warning if it's unexpected && (idir.idType == IDTYPE_ICON || idir.idType == IDTYPE_CUR || warntype(pb, pb->fileSpec->name, idir.idType)) && idir.idCount ) { whichicon = 0; #ifdef PS6 pb->imageIndex = whichicon+1; // PS 6.0 #endif name = pb->fileSpec->name; iconcount = idir.idCount; if(idir.idCount == 1 || pickicondialog(pb)){ if( !(e = getimageinfo((FILEREF)pb->dataFork, idir.idEntries, &ii, whichicon, desc)) ) e = readicon(pb, idir.idEntries, &ii); }else e = userCanceledErr; return e; } e = formatCannotRead; // icondir.idType != 1 or idir.idCount == 0 } return e; } OSErr read_continue(FormatRecordPtr pb){ pb->data = NULL; //SETRECT(pb->theRect,0,0,0,0); return noErr; } OSErr read_finish(FormatRecordPtr pb){ if(outdata) PS_BUFFER_FREE(outBufferID); return noErr; }