c++初学者

专注技术开发

[转]PSD格式文件的读取

 PhotoShop,我想没有人会不知道吧。如今最新的版本是6.0,其图象文件*.PSD和5.5相比变化并不太大。以下我就介绍*.PSD文件的读取方法,并提供完整读取函数。其中:m_Rect为目标区域,m_lpDDS7为目标DirectDraw表面,m_pbAlphaMask为目标Aplha通告指针。Read16函数为从指定文件当前位置读取一个WORD,Read32函数为从指定文件当前位置读取一个DWORD。MAX_PSD_CHANNELS为24。以下就是*.PSD文件的读取方法,有兴趣的朋友可以继续深入研究,到时可别忘了发我一份。

  HRESULT LoadPSD( LPSTR strFilename ) // 读取PSD文件
  {
    DWORD dwWidth, dwHeight;
// 宽高
    long lSurfWidth = m_Rect.right - m_Rect.left;
    long lSurfHeight = m_Rect.bottom - m_Rect.top;
    WORD CompressionType;
// 压缩类型
    HDC hDC;
    FILE *fpPSD;
    WORD ChannelCount;
// 通道数

    // 打开PSD文件
    if ( ( fpPSD = fopen ( strFilename, "rb" ) ) == NULL ) {
      return E_FAIL;
    }

    // 头四个字节为"8BPS"
    char signature[5];
    signature[0] = fgetc( fpPSD );
    signature[1] = fgetc( fpPSD );
    signature[2] = fgetc( fpPSD );
    signature[3] = fgetc( fpPSD );
    signature[4] = '\0';
    if ( strcmp( signature,"8BPS" ) != 0 ) {
      return E_FAIL;
    }

    // 版本必须为1
    if ( Read16( fpPSD ) != 1 ) {
      return E_FAIL;
    }

    // 跳过一些数据 (总是0)
    Read32( fpPSD );
    Read16( fpPSD );

    // 读取通道数
    ChannelCount = Read16( fpPSD );

    // 确定至少有一个通道
    if ( ( ChannelCount < 0 ) || ( ChannelCount > MAX_PSD_CHANNELS ) ) {
      return E_FAIL;
    }

    // 读入宽和高
    dwHeight = Read32( fpPSD );
    dwWidth = Read32( fpPSD );
    if ( dwWidth != ( DWORD )lSurfWidth ||
dwHeight != ( DWORD )lSurfHeight ) {
      return E_FAIL;
    }

    // 只读入8位通道
    if ( Read16( fpPSD ) != 8 ) {
      return E_FAIL;
    }

    // 确定模式为RGB.
    // 可能值:
    // 0: 位图
    // 1: 灰阶
    // 2: 索引
    // 3: RGB
    // 4: CMYK
    // 7: Multichannel
    // 8: Duotone
    // 9: Lab

    if ( Read16( fpPSD ) != 3 ) {
      return E_FAIL;
    }

    // 跳过数据(如调色板)
    int ModeDataCount = Read32( fpPSD );
    if ( ModeDataCount )
      fseek( fpPSD, ModeDataCount, SEEK_CUR );

    // 跳过数据(如:pen tool paths, etc)
    int ResourceDataCount = Read32( fpPSD );
    if ( ResourceDataCount )
      fseek( fpPSD, ResourceDataCount, SEEK_CUR );

    // 条过保留数据
    int ReservedDataCount = Read32( fpPSD );
    if ( ReservedDataCount )
      fseek( fpPSD, ReservedDataCount, SEEK_CUR );

    // 0: 非压缩
    // 1: RLE压缩

    CompressionType = Read16( fpPSD );
    if ( CompressionType > 1 ) {
      return E_FAIL;
    }

    BYTE* PSDPixels = new BYTE[ ( lSurfWidth * lSurfHeight ) * 4 ];

    // 解包数据
    UnPackPSD( fpPSD, lSurfWidth, lSurfHeight, PSDPixels, ChannelCount, CompressionType );

    fclose( fpPSD );

    // 复制信息
    BITMAPINFO BitmapInfo;
    ZeroMemory( &BitmapInfo, sizeof( BitmapInfo ) );
    BitmapInfo.bmiHeader.biSize = sizeof( BitmapInfo.bmiHeader );
    BitmapInfo.bmiHeader.biWidth = lSurfWidth;
    BitmapInfo.bmiHeader.biHeight = -lSurfHeight;
    BitmapInfo.bmiHeader.biPlanes = 1;
    BitmapInfo.bmiHeader.biBitCount = 32;

    m_lpDDS7->GetDC( &hDC );

    int rc = StretchDIBits( hDC,
                0,
                0,
                lSurfWidth,
                lSurfHeight,
                0,
                0,
                lSurfWidth,
                lSurfHeight,
                PSDPixels,
                &BitmapInfo,
                DIB_RGB_COLORS,
                SRCCOPY );

    m_lpDDS7->ReleaseDC( hDC );

    if ( rc == GDI_ERROR ) {
      H_ARRAY_DELETE( PSDPixels );

  #ifdef _DEBUG
    g_pHERR->OutDebugMsg( 3, H2DSERR_INVALID_PSD );
  #endif
    return E_FAIL;

    }

    // 是否读取Alpha混合通道
    if( ChannelCount > 3 ) {
      m_pbAlphaMask = new BYTE[ lSurfWidth * lSurfHeight ];

    for ( int x = 0; x < lSurfWidth; x++ )
      for ( int y = 0; y < lSurfHeight; y++ ) {
        m_pbAlphaMask[ ( y * lSurfWidth ) + x ] =
                PSDPixels[ ( ( ( y * lSurfHeight ) + x ) * 4 ) + 3 ];
      }
    }
    else {
      m_pbAlphaMask = NULL;
    }

    H_ARRAY_DELETE( PSDPixels );

    return DD_OK;
  }

  // PSD文件解包
 
  void CHades2DSurface::UnPackPSD( FILE *fp,     // fp为PSD文件指针,
                   DWORD dwWidth,  
// dwWidth、dwHeight为宽高,
                   DWORD dwHeight,
                   BYTE* pixels,
   // pixels为解包目标指针,
                   WORD ChannelCnt,
  // ChannelCnt为通道数,
                   WORD Compression )
// Compression位压缩类型。
                
                
  {
    int Default[4] = { 0, 0, 0, 255 };
    int chn[4] = { 2, 1, 0, 3};
    int PixelCount = dwWidth * dwHeight;

    if ( Compression ) {
      fseek( fp, dwHeight * ChannelCnt * 2, SEEK_CUR );

      for ( int c = 0; c < 4; c++ ) {
        int pn = 0;
        int channel = chn[c];

        if ( channel >= ChannelCnt ) {
          for ( pn=0; pn < PixelCount ;pn++ ) {
            pixels[ ( pn * 4 ) + channel ] = Default[ channel ];
          }
        }
        else
// 非压缩
        {
          int count = 0;
          while( count < PixelCount ) {
            int len = fgetc( fp );
            if( len == 128 ) { }
            else if ( len < 128 ) // 非RLE
            {
              len++;
              count += len;
              while(len) {
                pixels[ ( pn * 4 ) + channel ] = fgetc( fp );
                pn++;
                len--;
              }
            }
             else if ( len > 128 )
// RLE打包
            {
              len ^= 0x0FF;
              len += 2;
              unsigned char val = fgetc( fp );
              count += len;
              while( len ) {
                pixels[ ( pn * 4 ) + channel ] = val;
                pn++;
                len--;
              }
            }
          }
        }
      }
    }
    else
    {
      for ( int c=0; c < 4; c++ ) {
        int channel = chn[c];
        if ( channel > ChannelCnt ) {
          for( int pn = 0; pn < PixelCount; pn++ ) {
            pixels[ ( pn * 4 ) + channel ] = Default[ channel ];
          }
        }
        else {
          for( int n = 0; n < PixelCount; n++ ) {
            pixels[ ( n * 4 ) + channel ] = fgetc( fp );
          }
        }
      }
    }
  }

posted on 2008-12-26 18:15 大海 阅读(1378) 评论(0)  编辑 收藏 引用 所属分类: 图像


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理