2014年6月9日 星期一

Mipmap 建立函數

可以用不限長寬的圖片來產生 Mipmap 所須各圖層的圖片。

MipmapCreate() 的第一個參數 Mipmap 用於回傳 Mipmap 所有圖層的陣列,格式為
{第 0 層圖片的指標, 第 1 層圖片的指標, ... 第 LevelNum-1 層圖片的指標}

第二個參數 WidthHeight 用於回傳各層圖片的長寬,格式為
{
第 0 層圖片的 Width, 第 0 層圖片的 Height,
...
第 LevelNum-1 層圖片的 Width, 第 LevelNum-1 層圖片的 Height
}

第三個參數 LevelNum 用於回傳建立的層數


MipmapDelete() 用於正確的刪除 MipmapCreate() 建立的所有資料


// 由 MipmapCreate() 叫用
// 產生各層的縮小圖片,直至 1*1
void MipmapCreateX(unsigned char **Mipmaps, unsigned int *WidthHeight,
                   const unsigned int LevelNum, const unsigned char *SrcMap,
                   const unsigned int SrcWidth, const unsigned int SrcHeight,
                   const unsigned int bpp)
{
  unsigned int Level = 1; // 從第 2 層開始建立,第一層為 0
  const unsigned char *PreSrcMap = SrcMap;
  unsigned int PreSrcWidth = SrcWidth;
  unsigned int PreSrcHeight = SrcHeight;

  while(Level < LevelNum)
  {
    unsigned int Width = PreSrcWidth / 2 + PreSrcWidth % 2;
    unsigned int Height = PreSrcHeight / 2 + PreSrcHeight % 2;
    unsigned int NumberOfPixels = Width * Height;
    unsigned int n=0, X=0, Y=0;

    WidthHeight[Level * 2] = Width;
    WidthHeight[Level * 2 + 1] = Height;

    unsigned char *Pixels = new unsigned char[NumberOfPixels * bpp];
    Mipmaps[Level] = Pixels;

    while(n < NumberOfPixels)
    {
      unsigned char *p = Pixels + n*bpp;
      const unsigned char *s = PreSrcMap + (Y*PreSrcWidth + X)*bpp;
      if(Y < PreSrcHeight-1 && X < PreSrcWidth-1)
      {
        for(unsigned int b = 0; b < bpp; ++b)
          p[b] = s[b]/4 + s[b+bpp]/4 + s[b+PreSrcWidth*bpp]/4 +
          s[b+PreSrcWidth*bpp+bpp]/4;
        X+=2;
        if(X == PreSrcWidth)
        {
          X = 0; Y+=2;
        }
      }
      else if(X < PreSrcWidth-1)
      {
        for(unsigned int b = 0; b < bpp; ++b)
          p[b] = s[b]/2 + s[b+bpp]/2;
        X+=2;
      }
      else if(Y < PreSrcHeight-1)
      {
        for(unsigned int b = 0; b < bpp; ++b)
          p[b] = s[b]/2 + s[b+PreSrcWidth*bpp]/2;
        X=0;
        Y+=2;
      }
      else // if(Y == PreSrcHeight-1 && X == PreSrcWidth-1)
      {
        for(unsigned int b = 0; b < bpp; ++b)
          p[b] = s[b];
      }
      ++n;
    }

    ++Level;
    PreSrcMap = Pixels;
    PreSrcWidth = Width;
    PreSrcHeight = Height;
  }
}

/*
  用 max(Width,Height) 來算
  2^(n+1) 比 2^n 多一層
  2^n + 1 比 2^n 多一層
  所以 2^n + 1 至 2^(n+1) 比 2^n 多一層
*/
unsigned int CalculateLevelNum(unsigned int len)
{
  bool b = false;
  unsigned int LevelNum = 1;
  unsigned int Len = len;
  unsigned int t;
  while((t = Len >> 1))
  {
    ++LevelNum;
    b = (Len&1)? true:b;
    Len = t;
  }

  return (b)?LevelNum+1:LevelNum;
}

// 長寬皆不能為 0,否則回覆 false
// SrcMap 須指向以 byte 為單位,為每個像素做分色的圖片
// bpp 表示每個像素的 byte 數,比如 rgba 為 4,rgb 為 3
bool MipmapCreate(unsigned char ***Mipmap, unsigned int **WidthHeight,
                  unsigned int *LevelNum,
                  const unsigned char *SrcMap, const unsigned int SrcWidth,
                  const unsigned int SrcHeight, const unsigned int bpp)
{
  if(SrcWidth == 0 || SrcHeight == 0)
    return false;

  if(SrcWidth > SrcHeight)
    *LevelNum = CalculateLevelNum(SrcWidth);
  else
    *LevelNum = CalculateLevelNum(SrcHeight);

  unsigned int NumberOfPixels = SrcWidth * SrcHeight;

  *Mipmap = new unsigned char *[*LevelNum];
  *WidthHeight = new unsigned int[(*LevelNum) * 2];

  (*WidthHeight)[0] = SrcWidth;
  (*WidthHeight)[1] = SrcHeight;

  unsigned char *Pixels = new unsigned char[NumberOfPixels * bpp];
  (*Mipmap)[0] = Pixels;

  // 先複製第 1 層
  for(unsigned int n=0, X=0, Y=0; n < NumberOfPixels; ++n)
  {
    unsigned char *p = Pixels + n*bpp;
    const unsigned char *s = SrcMap + (X + Y * SrcWidth) * bpp;

    for(unsigned int b = 0; b < bpp; ++b)
      p[b] = s[b];

    if(++X == SrcWidth)
    {
      X = 0;
      ++Y;
    }
  }

  MipmapCreateX(*Mipmap,*WidthHeight,*LevelNum,Pixels,SrcWidth,SrcHeight,bpp);

  return true;
}


void MipmapDelete(unsigned char **Mipmap, unsigned int *WidthHeight,
                   unsigned int LevelNum)
{
  delete [] WidthHeight;

  for(unsigned int n = 0; n < LevelNum; ++n)
    delete [] Mipmap[n];

  delete [] Mipmap;
}

int main()
{
  unsigned int bpp = 4;
  unsigned int MapWidth = 3;
  unsigned int MapHeight = 2;
  unsigned char *Map = new unsigned char[MapWidth*MapHeight*bpp]; // 實務上應載入圖檔

  unsigned char **Mipmap;
  unsigned int *WidthHeight;
  unsigned int LevelNum;

  if(MipmapCreate(&Mipmap, &WidthHeight, &LevelNum, Map, MapWidth, MapHeight, bpp))
    MipmapDelete(Mipmap, WidthHeight, LevelNum);

  delete [] Map;

  return 0;
}

-------------------------------------------------------------------
上面的程式碼發現在 OpenGL 和 D3D 皆不能用,只好修一下,若 Ext 指定為 false,將以 OpenGL 和 D3D 的方式做縮減,但這方法每次縮小若 n%2 不等於 0,會捨棄最後的邊

// 由 MipmapCreate() 叫用
// 產生各層的縮小圖片,直至 1*1
static void MipmapCreateX(unsigned char **Mipmaps, unsigned int *WidthHeight, 
                          const unsigned int LevelNum,
                          const unsigned char *SrcMap, const unsigned int SrcWidth, 
                          const unsigned int SrcHeight, const unsigned int bpp)
{
  unsigned int Level = 1; // 從第 2 層開始建立,第一層為 0
  const unsigned char *PreSrcMap = SrcMap;
  unsigned int PreSrcWidth = SrcWidth;
  unsigned int PreSrcHeight = SrcHeight;

  while(Level < LevelNum)
  {
    unsigned int Width = PreSrcWidth / 2 + (PreSrcWidth & 1);
    unsigned int Height = PreSrcHeight / 2 + (PreSrcHeight & 1);
    unsigned int NumberOfPixels = Width * Height;
    unsigned int n=0, X=0, Y=0;

    WidthHeight[Level * 2] = Width;
    WidthHeight[Level * 2 + 1] = Height;

    unsigned char *Pixels = new unsigned char[NumberOfPixels * bpp];
    Mipmaps[Level] = Pixels;

    while(n < NumberOfPixels)
    {
      unsigned char *p = Pixels + n*bpp;
      const unsigned char *s = PreSrcMap + (Y*PreSrcWidth + X)*bpp;
      if(Y < PreSrcHeight-1 && X < PreSrcWidth-1)
      {
        for(unsigned int b = 0; b < bpp; ++b)
          p[b] = s[b]/4 + s[b+bpp]/4 + s[b+PreSrcWidth*bpp]/4 + 
                  s[b+PreSrcWidth*bpp+bpp]/4;
        X+=2;
        if(X == PreSrcWidth)
        {
          X = 0; Y+=2;
        }
      }
      else if(X < PreSrcWidth-1)
      {
        for(unsigned int b = 0; b < bpp; ++b)
          p[b] = s[b]/2 + s[b+bpp]/2;
        X+=2;
      }
      else if(Y < PreSrcHeight-1)
      {
        for(unsigned int b = 0; b < bpp; ++b)
          p[b] = s[b]/2 + s[b+PreSrcWidth*bpp]/2;
        X=0;
        Y+=2;
      }
      else // if(Y == PreSrcHeight-1 && X == PreSrcWidth-1)
      {
        for(unsigned int b = 0; b < bpp; ++b)
          p[b] = s[b];
      }
      ++n;
    }

    ++Level;
    PreSrcMap = Pixels;
    PreSrcWidth = Width;
    PreSrcHeight = Height;
  }
}

// 由 MipmapCreate() 叫用
// 產生各層的縮小圖片,直至 1*1
static void MipmapCreateX2(unsigned char **Mipmaps, unsigned int *WidthHeight, 
                           const unsigned int LevelNum,
                           const unsigned char *SrcMap, const unsigned int SrcWidth, 
                           const unsigned int SrcHeight, const unsigned int bpp)
{
  unsigned int Level = 1; // 從第 2 層開始建立,第一層為 0
  const unsigned char *PreSrcMap = SrcMap;
  unsigned int PreSrcWidth = SrcWidth;
  unsigned int PreSrcHeight = SrcHeight;

  while(Level < LevelNum)
  {
    bool Xodd = PreSrcWidth & 1;
    // bool Yodd = PreSrcHeight & 1;
    unsigned int Width = (PreSrcWidth>1)? PreSrcWidth/2 : 1;
    unsigned int Height = (PreSrcHeight>1)? PreSrcHeight/2 : 1;
    unsigned int NumberOfPixels = Width * Height;
    unsigned int n=0, X=0, Y=0;

    WidthHeight[Level * 2] = Width;
    WidthHeight[Level * 2 + 1] = Height;

    unsigned char *Pixels = new unsigned char[NumberOfPixels * bpp];
    Mipmaps[Level] = Pixels;

    while(n < NumberOfPixels)
    {
      unsigned char *p = Pixels + n*bpp;
      const unsigned char *s = PreSrcMap + (Y*PreSrcWidth + X)*bpp;
      if(Y < PreSrcHeight-1 && X < PreSrcWidth-1)
      {
        for(unsigned int b = 0; b < bpp; ++b)
          p[b] = s[b]/4 + s[b+bpp]/4 + s[b+PreSrcWidth*bpp]/4 + 
                 s[b+PreSrcWidth*bpp+bpp]/4;
        X+=2;
        if(X == PreSrcWidth || (Xodd && X == PreSrcWidth-1))
        {
          X = 0; Y+=2;
        }
      }
      else if(X < PreSrcWidth-1)
      {
        for(unsigned int b = 0; b < bpp; ++b)
          p[b] = s[b]/2 + s[b+bpp]/2;
        X+=2;
      }
      else // if(Y < PreSrcHeight-1)
      {
        for(unsigned int b = 0; b < bpp; ++b)
          p[b] = s[b]/2 + s[b+PreSrcWidth*bpp]/2;
        //X=0;
        Y+=2;
      }
      ++n;
    }

    ++Level;
    PreSrcMap = Pixels;
    PreSrcWidth = Width;
    PreSrcHeight = Height;
  }
}

/*
  用 max(Width,Height) 來算有多少層

  log2() 為 2 的對數
  int() 為取整數部份

  if Ext == true
    2^(n+1) 比 2^n 多一層
    2^n + 1 比 2^n 多一層
    所以 2^n + 1 至 2^(n+1) 比 2^n 多一層
int(log2(len))+1 層 (log2(len) 無小數) 或 int(log2(len))+1+1 層 (log2(len) 有小數) if Ext == false 只計算 int(log2(len))+1 層 */ unsigned int CalculateLevelNum(const unsigned int len, const bool Ext) { bool b = false; unsigned int LevelNum = 1; unsigned int Len = len; unsigned int t; while((t = Len >> 1)) { ++LevelNum; b = (Ext && Len&1)? true:b; Len = t; } return (b)?LevelNum+1:LevelNum; } // 長寬皆不能為 0,否則回覆 false // SrcMap 須指向以 byte 為單位,為每個像素做分色的圖片 // bpp 表示每個像素的 byte 數,比如 rgba 為 4,rgb 為 3 bool MipmapCreate(unsigned char ***Mipmap, unsigned int **WidthHeight, unsigned int *LevelNum, const unsigned char *SrcMap, const unsigned int SrcWidth, const unsigned int SrcHeight, const unsigned int bpp, const bool Ext = false) { if(SrcWidth == 0 || SrcHeight == 0) return false; if(SrcWidth > SrcHeight) *LevelNum = CalculateLevelNum(SrcWidth, Ext); else *LevelNum = CalculateLevelNum(SrcHeight, Ext); unsigned int NumberOfPixels = SrcWidth * SrcHeight; *Mipmap = new unsigned char *[*LevelNum]; *WidthHeight = new unsigned int[(*LevelNum) * 2]; (*WidthHeight)[0] = SrcWidth; (*WidthHeight)[1] = SrcHeight; unsigned char *Pixels = new unsigned char[NumberOfPixels * bpp]; (*Mipmap)[0] = Pixels; // 先複製第 1 層 for(unsigned int n=0, X=0, Y=0; n < NumberOfPixels; ++n) { unsigned char *p = Pixels + n*bpp; const unsigned char *s = SrcMap + (X + Y * SrcWidth) * bpp; for(unsigned int b = 0; b < bpp; ++b) p[b] = s[b]; if(++X == SrcWidth) { X = 0; ++Y; } } if(Ext) MipmapCreateX(*Mipmap,*WidthHeight,*LevelNum,Pixels,SrcWidth,SrcHeight,bpp); else MipmapCreateX2(*Mipmap,*WidthHeight,*LevelNum,Pixels,SrcWidth,SrcHeight,bpp); return true; } // 用於正確的刪除 MipmapCreate() 建立的所有資料 void MipmapDelete(unsigned char **Mipmap, unsigned int *WidthHeight, unsigned int LevelNum) { delete [] WidthHeight; for(unsigned int n = 0; n < LevelNum; ++n) delete [] Mipmap[n]; delete [] Mipmap; }




沒有留言:

張貼留言