CSDN - 文檔中心 - 其他開發(fā)語言 閱讀:42 ??評論: 0 ?? 參與評論


標(biāo)題 ? 一種位圖縮放的快速算法&PSD格式的開發(fā)&PSD格式文件的讀取???? 選擇自 byxdaz 的 Blog
關(guān)鍵字 ? 一種位圖縮放的快速算法&PSD格式的開發(fā)&PSD格式文件的讀取
出處 ?

一種位圖縮放的快速算法&PSD格式的開發(fā)&PSD格式文件的讀取

一種位圖縮放的快速算法?

  給定一個位圖,如何將它縮放至任意尺寸?很明顯,唯一的方法是:放大時,在像素中間添加一些重復(fù)像 素,使圖像拉寬;縮小時,把部分像素刪除掉,使圖像收縮。但是如何確定哪些像素該重復(fù),哪些像素該刪除呢?下面的方法是我自已想的,如果你有更好的方法, 請告訴我。我們只考慮水平方向(垂直方向跟水平方向是同樣的道理)。下面先從簡單的例子來說,最后再推出一個通用的算法:

若要放大1倍,應(yīng)將每一個像素都重復(fù)一次,N個像素變成了2N個像素,圖像放大為2倍。這個不難;

若要縮小1/2,應(yīng)該每隔一個像素刪除一個像素,2N個像素變成了N個像素,圖像縮小一半。這個也不難;

若要放大1.5倍,怎么辦?假設(shè)原有2N個像素,現(xiàn)在欲變成3N個像素,須添加N個像素,所以應(yīng)對原圖每隔一個像素添加一個重復(fù)像素:?

若要縮小1/3,就是C的逆過程:每隔兩個像素刪除一個像素。?
   上面四個例子都是比較容易的特例。現(xiàn)在來考慮通用的算法。在四個例子的縮放過程可以這樣理解。假設(shè)欲將長度為N1的像素列變成長度為N2的像素列,首 先,設(shè)立兩個指針,一個作為源指針,指向原來的像素列,讀取源像素,另一個作為目的指針,指向變換后的像素列,寫入讀取到的像素。然后,以拉伸后像素列的 長度為循環(huán)次數(shù),循環(huán)N2次,每次循環(huán)中由源指針處COPY一個像素到目的指針處,然后目的指針加一,源指針根據(jù)每次循環(huán)的不同需要增加一定步長(放大時 步長是零或一,縮小時步長大于等于一)。
  算法的框架解決了,但是中心內(nèi)容仍沒有解決:如何確定每次循環(huán)里源指針增加的步長?或者說,每次循環(huán) 里如何更新源指針的位置?容易看出,通過浮點運算很容易解決這個問題:設(shè)立一個初值為零的浮點變量,每次循環(huán)中,把這個浮點變量加上N1/N2,并將其結(jié) 果的整數(shù)部分作為源指針距離起始位置的偏移量;這樣,經(jīng)過N2次循環(huán),浮點變量的值恰好達到N1,源指針也恰好“走”到原像素列的末尾。
  這個 方法可行,但是太慢。如果能將浮點運算轉(zhuǎn)化成整數(shù)運算就快多了。那么如何轉(zhuǎn)化呢?我們可以設(shè)立一個值域為N1*N2的整數(shù)計數(shù)器,每次循環(huán)遞增N1,并且 規(guī)定,計數(shù)器每增加N2,源指針就前進一個像素。這樣,經(jīng)過N2次循環(huán),計數(shù)器共增加了N1*N2,源指針則增加了N1個單元,恰好“走”完全程。實際編 程中,我們是用一個值域為N2的整數(shù)計數(shù)器,超出值域部分取模處理。算法大致如下:

  void?StrechPixels(int?N1,?int?N2,?PIXEL?src_pixels[],?PIXEL?dest_pixels[])
  {
    ASSERT(N1?<=?N2);  //?N1?must?<=?N2
    int?p1?=?0,?count?=?0;
    for?(int?p2?=?0;?p2?<?N2;?p2++)
    {
      dest_pixels[p2]?=?src_pixels[p1];
      count?+=?N1;
      while?(count?>=?N2)
      {
        count?-=?N2;
        p1++;
      }
    }
  }

   上面算法只是水平縮放單行像素,對垂直方向也采用同樣的算法,便實現(xiàn)了任意比例的位圖縮放。經(jīng)過以上算法的處理,放大時圖像出現(xiàn)馬賽克,縮小時圖像出現(xiàn) 閃礫。若要獲得高質(zhì)量的縮放圖形,須采用插值、過濾等技術(shù)。但是因為這些技術(shù)所需計算量太大,在游戲中通常靠硬件加速來實現(xiàn),不宜軟件解決。??


================================================================
PSD格式的開發(fā)?



   我在做游戲時,因為要用到將PSD格式轉(zhuǎn)換成BMP或者JPG格式的程序,而且,在轉(zhuǎn)換時,要將PSD中的空格轉(zhuǎn)成游戲中約定的透明色,這樣的程序,只 能自己去寫了。所以,我在網(wǎng)上搜了一陣子,找到了“中國游戲開發(fā)者”的網(wǎng)站,看到了一篇關(guān)于PSD格式的文章(這也是我開始向這個網(wǎng)站投稿的原因,也許這 就叫緣)。
  本來是想偷點懶,省去了自己研究之苦,可以抄一抄別人現(xiàn)在的代碼,再自己改改,又能省時間,又能學(xué)到東西,何樂而不為呢?可是,在 抄下這篇文章的代碼之后,發(fā)現(xiàn)其運行居然是不能通過的。看來天下沒有免費的午餐,我還是得自己研究。一個多小時的苦戰(zhàn)之后,終于發(fā)現(xiàn)了問題所在,也許這并 不是一個問題,只是對于沒有這個經(jīng)驗人來說,這確實是個大問題。我現(xiàn)在將這篇文章的一些地方進行改正,望各位朋友在開發(fā)PSD格式的讀取問題上,不再有麻 煩。原文《PSD格式文件的讀取》在這里http: //cgd.pages.com.cn/cgd/develop/effect/200109/ReadPSD.htm,各位可以看看。我只將我的改正部 分寫在下面:?

  1)文件頭是4個字節(jié),只能讀4個字節(jié)。
  2)Photoshop的PSD格式用的是LIT格式存儲。

   這個LIT格式我以前只是聽說過,沒想到會被PSD用上。這個格式是將數(shù)據(jù)的高低位碼交換了的,如果直接用ReadFile或者fread函數(shù)將其讀出 來,它的高低位碼是被交換了的。例如:640的16進制值是1E0,用DWORD方式存在硬盤里是0001E000,用讀文件的函數(shù)讀出來以后,將變成: 00E00100。所以,其高低位碼被交換了,解決的方法是用轉(zhuǎn)換函數(shù),代碼如下:

  WORD?WORDBIGtoLIT(WORD?code) //?字型的處理
  {
    return?((a?>>?8?&?0xFF)?|?((a?&?0x00FF)?<<?8); //?把高低位碼再交換過來
  }

  DWORD?DWORDBIGtoLIT(DWORD?code) //?雙字型的處理
  {
    WORD?HiCode,?LowCode;

    HiCode?=?code?&?0xFFFF0000;
    LowCode?=?code?&?0x0000FFFF;
    HiCode?=?((HiCode?>>?8)?&?0xFF)?|?((HiCode?&?0x00FF)?<<?8);
    LowCode?((LowCode?>>?8)?&?0xFF)?|?((LowCode?&?0x00FF)?<<?8);
    return?MAKELONG((WORD)(LowCode?<<?16),?(WORD)HiCode);
  }

  當(dāng)然,也可以定義成宏形式,如下:

  #define?BIG2LIT(a)?(WORD((a?>>?8?&?0xFF)?|?((a?&?0x00FF)?<<?8)))
  #define?DWORDBIG2LIT(b)?MAKELONG(BIG2LIT(HIWORD(b)),?BIG2LIT(LOWORD(b)))

  看起來簡單一些,哈哈……。其它的,原文沒有什么錯誤,不過,我建議大家還是最好自己去解決問題,呵呵,因為有一句話說得很好:老師能教你讀書寫字,但是不能教你做天下文章。?


================================================================
PSD格式文件的讀取?


   PhotoShop,我想沒有人會不知道吧。如今最新的版本是6.0,其圖象文件*.PSD和5.5相比變化并不太大。以下我就介紹*.PSD文件的讀 取方法,并提供完整讀取函數(shù)。其中:m_Rect為目標(biāo)區(qū)域,m_lpDDS7為目標(biāo)DirectDraw表面,m_pbAlphaMask為目標(biāo) Aplha通告指針。Read16函數(shù)為從指定文件當(dāng)前位置讀取一個WORD,Read32函數(shù)為從指定文件當(dāng)前位置讀取一個DWORD。 MAX_PSD_CHANNELS為24。以下就是*.PSD文件的讀取方法,有興趣的朋友可以繼續(xù)深入研究,到時可別忘了發(fā)我一份。

  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;?//?通道數(shù)

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

    //?頭四個字節(jié)為"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;
    }

    //?跳過一些數(shù)據(jù)?(總是0)
    Read32(?fpPSD?);
    Read16(?fpPSD?);

    //?讀取通道數(shù)
    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;
    }

    //?跳過數(shù)據(jù)(如調(diào)色板)
    int?ModeDataCount?=?Read32(?fpPSD?);
    if?(?ModeDataCount?)
      fseek(?fpPSD,?ModeDataCount,?SEEK_CUR?);

    //?跳過數(shù)據(jù)(如:pen?tool?paths,?etc)
    int?ResourceDataCount?=?Read32(?fpPSD?);
    if?(?ResourceDataCount?)
      fseek(?fpPSD,?ResourceDataCount,?SEEK_CUR?);

    //?條過保留數(shù)據(jù)
    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?];

    //?解包數(shù)據(jù)
    UnPackPSD(?fpPSD,?lSurfWidth,?lSurfHeight,?PSDPixels,?ChannelCount,?CompressionType?);

    fclose(?fpPSD?);

    //?復(fù)制信息
    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為解包目標(biāo)指針,
                  ?WORD?ChannelCnt, ?//?ChannelCnt為通道數(shù),
                  ?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?);
          }
        }
      }
    }
  }??