単色のビットマップファイルを16色に変更するためには

解決


ほほ  2009-01-14 00:26:59  No: 69429

お世話になります。

avlファイルより読み込んで
白黒ビットマップを作成するプログラムがあります。

先日、これまでの図形に新しい情報を追加することが出来たのですが
単色なので、これまでの部分と
追加した部分の見分けがつかないので
色分けすることで見やすく変更したいです。

単純に biBitcount=1 のものを biBitcount=4に変更したのですが
出来上がったファイルを開こうとすると
エラー(ピクチャが不正です。)が上がってしまいます。

他に変更が必要な箇所がありましたら
教えていただきたいです。

宜しくお願いします。


aetos  2009-01-14 03:55:22  No: 69430

> 他に変更が必要な箇所がありましたら
> 教えていただきたいです。

パレットと全てのビットデータを差し替える必要があります。
入出力ともにビットマップファイルなら、ファイルサイズが変わりますので、ファイルヘッダも変えなければなりません。


ほほ  2009-01-16 07:41:20  No: 69431

/*******************************************************************/
/*  << メモリ内部で図形を作成しビットマップファイルに保存 >   */
/*******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

#define    FIG_TMP_BUFF_SIZE  0x4000

int    HorizonBytes;
int    XmaxRange;
int    YmaxRange;
int    FigureBuffType=1;
unsigned char  FigTmpBuff[FIG_TMP_BUFF_SIZE];
unsigned char  *FigureBuff;
unsigned char  ShiftTable[]    ={0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
unsigned char  LeftMaskTable[] ={0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01};
unsigned char  RightMaskTable[]={0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF};
unsigned char  ReverseTable[]={
  0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
  0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
  0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
  0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
  0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
  0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
  0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
  0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
  0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
  0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
  0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
  0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
  0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
  0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
  0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
  0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
};
unsigned char  BitMapFont8x8[][8]={
  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},  
  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
          /* など */
};  

int  figinit(int, int);
void  figfree(void);
void  figreset(void);
int  putbmp(char *);
void  swapif(int *, int *);
int  checkrange(int, int);
void  hvcheckrange(int, int *, int *, int *);
void  putpixel(int, int);
void  hline(int, int, int);
void  vline(int, int, int);
void  line(int, int, int, int);

/*----- 図形作成用メモリ確保 -----*/
int figinit(int xdot, int ydot)
{

  HorizonBytes=((((xdot+7)>>3)+3)/4)*4;
  XmaxRange=xdot-1;
  YmaxRange=ydot-1;
  if(FIG_TMP_BUFF_SIZE>=HorizonBytes*ydot) {
    FigureBuff=FigTmpBuff;
    return 0;
  }
  else {
    if((unsigned char *)NULL!=(FigureBuff=(unsigned char *)calloc(HorizonBytes*ydot, sizeof(unsigned char)))) {
      FigureBuffType=0;
      return 0;
    }
    else
      return 1;
  }
}

/*----- 図形作成用メモリ解放 -----*/
void figfree()
{
  if(!FigureBuffType)
    free(FigureBuff);
}

/*----- 図形作成用メモリ内容クリア -----*/
void figreset()
{
  memset(FigureBuff, 0x00, HorizonBytes*(YmaxRange+1));
}

/*----- BMPファイル作成 -----*/
int putbmp(char *filename)
{
  #define  BF_OFF_BITS    (54+4*2)

  FILE    *fw;
  unsigned char  buff[BF_OFF_BITS];
  if((FILE *)NULL==(fw=fopen(filename, "wb")))
    return 1;
  buff[0]='B';              /* bfType  */
  buff[1]='M';
                  /* bfSize  */
  *(unsigned long *)(buff+2)=((unsigned long)HorizonBytes)*((unsigned long)YmaxRange+1L)+(unsigned long)BF_OFF_BITS;
  buff[6]=buff[7]=buff[8]=buff[9]=0;
  *(unsigned long *)(buff+10)=(unsigned long)BF_OFF_BITS;    /* bfOffBits  */
  *(unsigned long *)(buff+14)=40L;                    /* biSize  */
  *(long *)(buff+18)=(long)XmaxRange+1L;        /* biWidth  */
  *(long *)(buff+22)=(long)YmaxRange+1L;        /* biHeight  */
  *(int *)(buff+26)=1;
  *(int *)(buff+28)=1;            /* biBitCount  */
  *(unsigned long *)(buff+30)=*(unsigned long *)(buff+34)=0L;
  *(long *)(buff+38)=*(long *)(buff+42)=0L;
  *(unsigned long *)(buff+46)=*(unsigned long *)(buff+50)=0L;
  buff[54]=0xFF;
  buff[55]=0xFF;
  buff[56]=0xFF;
  buff[57]=0;
  buff[58]=0x00;
  buff[59]=0x00;
  buff[60]=0x00;
  buff[61]=0;
  if(BF_OFF_BITS!=fwrite(buff, sizeof(char), BF_OFF_BITS, fw)) {
    fclose(fw);
    unlink(filename);
    return 2;
  }
  if((unsigned)(HorizonBytes*(YmaxRange+1))==fwrite(FigureBuff, sizeof(char), HorizonBytes*(YmaxRange+1), fw)) {
    fclose(fw);
    return 0;
  }
  else {
    fclose(fw);
    unlink(filename);
    return 2;
  }
}

/*----- 2値を小さい順に並べる -----*/
void swapif(int *a, int *b)
{
  if(*a>*b) {
    *b^=*a;
    *a^=*b;
    *b^=*a;
  }
}

/*----- 座標が画面の範囲かチェック -----*/
int checkrange(int px, int py)
{
  return (px>=0)&&(px<=XmaxRange)&&(py>=0)&&(py<=YmaxRange);
}

/*----- 水平線及び垂直線の座標が画面の範囲かチェック -----*/
void hvcheckrange(int mode, int *x1, int *y1, int *xy)
{
  if(*x1<0)
    *x1=0;
  else if(*x1>XmaxRange)
    *x1=XmaxRange;

  if(*y1<0)
    *y1=0;
  else if(*y1>YmaxRange)
    *y1=YmaxRange;

  if(!mode) {
    if(*xy<0)
      *xy=0;
    else if(*xy>XmaxRange)
      *xy=XmaxRange;
  }
  else {
    if(*xy<0)
      *xy=0;
    else if(*xy>YmaxRange)
      *xy=YmaxRange;
  }
}

/*----- 点を打つ -----*/
void putpixel(int px, int py)
{
  if(checkrange(px, py))
    *(FigureBuff+py*HorizonBytes+(px>>3))|=(0x80>>(px&0x07));
}

/*----- 水平線を引く -----*/
void hline(int sx, int sy, int ex)
{
  register  i, dx;
  int    x1, x2;
  unsigned char  maskL, maskR;
  unsigned char  *aplane;

  swapif(&sx, &ex);
  hvcheckrange(0, &sx, &sy, &ex);
  x1=sx>>3;
  x2=ex>>3;
  dx=x2-x1;
  maskL=LeftMaskTable[sx&0x07];
  maskR=RightMaskTable[ex&0x07];
  aplane=FigureBuff+sy*HorizonBytes+x1;
  if(0==dx)
    *aplane|=maskL&maskR;
  else {
    *aplane|=maskL;
    aplane++;
    for(i=1; i<dx; i++)
      *aplane++=0xFF;
    *aplane|=maskR;
  }
}

/*----- 垂直線を引く -----*/
void vline(int sx, int sy, int ey)
{
  register  py;
  unsigned char  src;
  unsigned char  *aplane;

  swapif(&sy, &ey);
  hvcheckrange(1, &sx, &sy, &ey);
  src=ShiftTable[sx&0x07];
  aplane=FigureBuff+sy*HorizonBytes+(sx>>3);
  for(py=sy; py<=ey; py++) {
    *aplane|=src;
    aplane+=HorizonBytes;
  }
}

/*----- 直線を引く -----*/
void line(int x1, int y1, int x2, int y2)
{
  register  i, s;
  int    px, py, sx, sy, dx, dy;

  if(x1==x2)
    vline(x1, y1, y2);
  else if(y1==y2)
    hline(x1, y1, x2);
  else {
    px=x1;
    py=y1;
    sx=(x2>px)?1:-1;
    sy=(y2>py)?1:-1;
    dx=abs(x2-px);
    dy=abs(y2-py);
    if(dx>dy) {
      s=dx>>1;
      for(i=0; i<=dx; i++) {
        putpixel(px, py);
        s+=dy;
        if(s>dx) {
          s-=dx;
          py+=sy;
        }
        px+=sx;
      }
    } 
    else {
      s=dy>>1;
      for(i=0; i<=dy; i++) {
        putpixel(px, py);
        s+=dx;
        if(s>dy) {
          s-=dy;
          px+=sx;
        }
        py+=sy;
      }
    }
  }
}

見づらいかもしれませんが、すみません。

分からないことがいくつかあります。
①unsigned char  ReverseTable[]=  定義の意味と、配列の値について
②src=ShiftTable[sx&0x07];    &0x07について
③*(FigureBuff+py*HorizonBytes+(px>>3))|=(0x80>>(px&0x07));  
  シフトするのはどんな意味があるのでしょうか?

④16色に変換することが出来そうでしょうか?


aetos  2009-01-17 01:04:16  No: 69432

申し訳ないですが、長い上に、単にビットマップを作成するのみならずいろいろ計算しているようですし、適切な構造体も使っていないので、まったく読む気がしません。
従いまして、ご質問には全て「わかりません」と回答させて頂きます。

最も単純なケースとして、真っ白な 2bpp のビットマップを、(見た目は同じだとしても)4bpp に変換する方法くらいなら教えて差し上げられますが、その変換と無関係のプログラムの仕様に関してはご容赦願います。


ほほ  2009-01-17 05:50:36  No: 69433

>ご質問には全て「わかりません」と回答させて頂きます。
了解です。

>最も単純なケースとして、真っ白な 2bpp のビットマップを、(見た目は同>じだとしても)4bpp に変換する方法くらいなら教えて差し上げられ…

web上で調べてみても『これ!』と思える参考になるも把握できず
ポピュラーな2bpp のビットマップの作成方法もよく分からない状態です。

自分が投稿したソースと関係性がなくても
是非 理解したいので
もし、参考になるソースがありましたら
どうぞ教えていただけないでしょうか。


みい  2009-01-17 18:32:32  No: 69434

「ビットマップ ファイル ヘッダー」で検索かけてみてください。
ビットマップファイルの構造や作り方を書いてあるところがいくつも検索に引っかかるはずです。
http://www.geocities.co.jp/Playtown-Knight/6845/sd_doc/format_windib.html
とか。
いくつか見ていって理解しやすいところを見つけてみてください
(感性や個人レベルの問題もあり万人に分かりやすい説明は難しいと思いますので、ご自分に最適は自分で見つけるしかないと思います)。


wclrp ( 'o')  2009-01-18 23:44:24  No: 69435

水平線を引くとかあるけどさ
ファイル保存または読み込むときにだけ4bits/pixelに変換して
それ以外は8bits/pixelが楽なんじゃない。

>(3) *(FigureBuff+py*HorizonBytes+(px>>3))|=(0x80>>(px&0x07));  
1bit/pixelのときの処理だね。
1バイトで8ピクセルを表現できるから
そのまま書き変えると隣の7ピクセルも書き変えてしまうので。

1bit/pixelでは問題ないのかもしれないが
4bits/pixelだと変えないとだな。
|つまりORだから0を1に変えることはできても1を0に変えることはできないので。
でも元画像が透けて見えるような効果があるからあえてその方がいい場合もある。

少なくともビット演算はよく理解していないといけない。

グローバル変数使いまくりの関数は
違うサイズの画像を二つ使用する必要が出たときとかに対応できないから
よくない設計だな。


wclrp ( 'o')  2009-01-18 23:52:21  No: 69436

>(2) src=ShiftTable[sx&0x07];    &0x07について

プログラム全部見るの大変なので一部分しか見てないから
間違えているかもしれない念のため。

FigureBuff配列の1要素つまり1バイトが8ピクセル分あるから
何番目(何ビット目)を1に変えるか示すためでしょ。

例えば
y = 0
x = 9
を1に変えるには、
FigureBuff[1]の左から2番目のビットを1に変え、
ほかのビットは変えないということ。

sx & 0x07は、8で割った余りと同じ結果になることを利用している。


wclrp ( 'o')  2009-01-19 00:01:42  No: 69437

>(1) unsigned char ReverseTable[]=  定義の意味と、配列の値について

たぶん仕事かなんかだと思うけどそのソースの出所に聞いたら。

Reverse
逆、反対、裏、嘔吐

Table
机、卓、食卓、表

>定義の意味
大変だからLUTでしょ。
1bit/pixel用だから使えないな。

>値
0,1,2,3,4,...,255を2進数で左右逆に書いてあるものだよ。


ほほ  2009-01-20 00:53:44  No: 69438

■16色に変更するためにBITMAPFILEHEADERの構造のようなものを
    変更してファイルを作ることが出来るのですが、開くことが出来ません。
    
   ①*(int *)(buff+28)=1;            /* biBitCount(1)  */
     → *(int *)(buff+28)=4;

                       /* biCompression */          /* biSizelmage */
   ②*(unsigned long *)(buff+30)=*(unsigned long *)(buff+34)=0L; 
     →*(unsigned long *)(buff+30)=0L;                
       *(unsigned long *)(buff+34)=(unsigned long)(XmaxRange+1L)*(unsigned long)(YmaxRange+1L);
    
   ③(long *)(buff+38)=*(long *)(buff+42)=0L;  
     →*(long *)(buff+38)=(long)3780L;        /* biXPixPerMeter  */
       *(long *)(buff+42)=(long)3780L;        /* biYPixPerMeter  */
       (値の求め方はよく分からないのですが…)

  ■パレット部を16色追加すると、ファイル作成すらできません。
  buff[54]=0x00;  /* 0:黒 */
  buff[55]=0x00;
  buff[56]=0x00;
  buff[57]=0x00;
  buff[58]=0x00;   /* 1:茶  */
  buff[59]=0x00;
  buff[60]=0x80;
  buff[61]=0x00;
  buff[62]=0x00;   /* 2:緑  */
  buff[63]=0x80;
  buff[64]=0x00;
  buff[65]=0x00;
  buff[66]=0x00;   /* 3:ウグイス ? */
  buff[67]=0x80;
  buff[68]=0x80;
  buff[69]=0x00;
  buff[70]=0x80;   /* 4:青  */
  buff[71]=0x00;
  buff[72]=0x00;
  buff[73]=0x00;
  buff[74]=0x80;   /* 5:紫  */
  buff[75]=0x00;
  buff[76]=0x80;
  buff[77]=0x00;
  buff[78]=0x80;   /* 6:水色  */
  buff[79]=0x80;
  buff[80]=0x00;
  buff[81]=0x00;
  buff[82]=0xC0;   /* 7:薄グレー  */
  buff[83]=0xC0;
  buff[84]=0xC0;
  buff[85]=0x00;
  buff[86]=0x80;   /* 8:グレー  */
  buff[87]=0x80;
  buff[88]=0x80;
  buff[89]=0x00;
  buff[90]=0x00;   /* 9:赤  */
  buff[91]=0x00;
  buff[92]=0xFF;
  buff[93]=0x00;
  buff[94]=0x00;   /* 10:黄緑  */
  buff[95]=0xFF;
  buff[96]=0x00;
  buff[97]=0x00;
  buff[98]=0x00;   /* 11:黄  */
  buff[99]=0xFF;
  buff[100]=0xFF;
  buff[101]=0x00;
  buff[102]=0xFF;   /* 12:青  */
  buff[103]=0x00;
  buff[104]=0x00;
  buff[105]=0x00;
  buff[106]=0xFF;   /* 13:ピンク  */
  buff[107]=0x00;
  buff[108]=0xFF;
  buff[109]=0x00;
  buff[110]=0xFF;   /* 14:水色2  */
  buff[111]=0xFF;
  buff[112]=0x00;
  buff[113]=0x00;
  buff[114]=0xFF;   /* 15:白(透明)  */
  buff[115]=0xFF;
  buff[116]=0xFF;
  buff[117]=0x00;

    設定方法に問題があるのでしょうか?
    それとも、他の記述部を修正しないと16色のビットマップを作成することが出来ないのでしょうか。
    長くなりましてすみません。


wclrp ( 'o')  2009-01-20 06:45:03  No: 69439

面倒だから実際に動くか確認しないで回答する。
間違っているかもしれない。

構造体じゃなくて*(int *)(buff+28)とか変なやり方するの?
28であっているかとか俺は調べないよ。

XmaxRangeとか意味分かんないし。

グローバル変数にしなけれないけないようなものではないのに
グローバル変数を大量に使っていて
こんなプログラム作るならいなくなってくれないかって言いたいレベル。

1ピクセル1ビットだったのが1ピクセル4ビットになるので
ビットマップのバイト数も変わるはず。
ビットマップは横1行が4バイトの倍数になるように右に0〜3バイトの余分を追加する。

>パレット部を16色追加すると、ファイル作成すらできません。
意味わからん。
fwriteがエラーになるの?
むろん、その分buffのバイトサイズが増えているよね。

> buff[54]=0x00;  /* 0:黒 */
こういうのは
buff[sizeof(BITMAPFILEHEADER)+〜]=
こういう感じにした方がいい。


wclrp ( 'o')  2009-01-20 07:00:27  No: 69440

>#define BF_OFF_BITS (54+4*2)
16色に変更した?

>HorizonBytes=((((xdot+7)>>3)+3)/4)*4;
4bppに変更した?

>単純に biBitcount=1 のものを biBitcount=4に変更したのですが
これが目的だよな。
ということは一時的に2種類のビットマップが存在するんだよな。
HorizonBytesみたいにバイト数を示すものは
たとえ幅と高さが同じサイズでもbppが違えば別の値になるから
やっぱグローバル変数を多用している時点でこのプログラムまずいな。

やたらと引数が増えて困るとか言うなら
構造体にしてそのポインタを引数で渡せばいいことだし。


ほほ  2009-01-21 00:24:34  No: 69441

>構造体じゃなくて*(int *)(buff+28)とか変なやり方するの?
>28であっているかとか俺は調べないよ。
 →構造体を使えるように調べてみます。

>XmaxRangeと(YmaxRange)か意味分かんないし。
 →作成するビットマップの外枠のサイズで、固定で"195"が入ります。

>1ピクセル1ビットだったのが1ピクセル4ビットになるので
>ビットマップのバイト数も変わるはず。
>ビットマップは横1行が4バイトの倍数になるように右に0〜3バイトの余分を追加する。
 *(long *)(buff+18)=(long)XmaxRange+1L;      
 →*(long *)(buff+18)=((long)XmaxRange+1L)*4+3;  
 1Lの L はどんな使われかたですか?

>> buff[54]=0x00;  /* 0:黒 */
>こういうのは
>buff[sizeof(BITMAPFILEHEADER)+〜]=
>こういう感じにした方がいい。
→buff[sizeof(BITMAPFILEHEADER)+1]=0x00;
  こんな感じになるのですか?

>>#define BF_OFF_BITS (54+4*2)
>16色に変更した?
 →#define BF_OFF_BITS (54+4*16)

>>HorizonBytes=((((xdot+7)>>3)+3)/4)*4;
>4bppに変更した?
xdot=194なので 
194+7=201
201は2進数で11001001になって、3ビットシフトして 00011001を10進数に戻すと25
HorizonBytes=28になるのですが、3ビットシフト意味はどんな感じなのですか?

>>(3) *(FigureBuff+py*HorizonBytes+(px>>3))|=(0x80>>(px&0x07));  
>1bit/pixelのときの処理だね。
>1バイトで8ピクセルを表現できるから
>そのまま書き変えると隣の7ピクセルも書き変えてしまうので。

>1bit/pixelでは問題ないのかもしれないが
>4bits/pixelだと変えないとだな。
>|つまりORだから0を1に変えることはできても1を0に変えることはできないので。
>でも元画像が透けて見えるような効果があるからあえてその方がいい場合もある。
→ここのORはどのビットとどのビットのになるのでしょう。
  
>やたらと引数が増えて困るとか言うなら
>構造体にしてそのポインタを引数で渡せばいいことだし。
→どのように記述したらよいのでしょう?

分からないことばかりで、質問ばかりですが
どうぞ宜しくお願いいたします。


gak  2009-01-21 02:03:06  No: 69442

今回の場合、まず BMP の仕様を理解しない事には前に進めないと思う。
それ無くして、回答者の人達が呉れている情報を理解する事はかなり困難。

↓に(一般的形式の)2色DIB を 16色DIB に変換する処理を載せる。
BMP の仕様と睨めっこして、何をやっているか処理を追いかけてみると良いかと。
そうこうしている内に多分解ってくる。

void* cnv_2to16(void* src) {
    BITMAPINFOHEADER *src_bih, *dst_bih;
    RGBQUAD          *src_pal, *dst_pal;
    char *src_bits,  *dst_bits;
    int              src_rowbyte, dst_rowbyte;
    void             *dst;
    int              pal, x, y, src_pos, dst_pos;

    src_bih = (BITMAPINFOHEADER*)src;
    assert(src_bih->biBitCount == 1);

    src_rowbyte = ((1 * src_bih->biWidth + 31) / 32) * 4;
    dst_rowbyte = ((4 * src_bih->biWidth + 31) / 32) * 4;
    dst = malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 16 + dst_rowbyte * src_bih->biHeight);
    if (dst == NULL) {
        return NULL;
    }

    dst_bih = (BITMAPINFOHEADER*)dst;
    memcpy(dst_bih, src_bih, sizeof(BITMAPINFOHEADER));
    dst_bih->biSize = sizeof(BITMAPINFOHEADER);
    dst_bih->biBitCount = 4;
    dst_bih->biSizeImage = dst_bih->biClrUsed = dst_bih->biClrImportant = 0;

    src_pal = (RGBQUAD*)((char*)src_bih + src_bih->biSize);
    dst_pal = (RGBQUAD*)((char*)dst_bih + dst_bih->biSize);
    for (pal=0; pal < 2; ++pal) {
        dst_pal[pal] = src_pal[pal];
    }
    for (; pal < 16; ++pal) {
        /* palette の空き領域に適当な色をセット */
    }

    src_bits = (char*)src_pal + sizeof(RGBQUAD) * 2;
    dst_bits = (char*)dst_pal + sizeof(RGBQUAD) * 16;
    memset(dst_bits, 0, dst_rowbyte * src_bih->biHeight);
    for (y=0; y < dst_bih->biHeight; ++y) {
        for (x=0; x < dst_bih->biWidth; ++x) {
            src_pos = src_rowbyte * y + (x / 8);
            dst_pos = dst_rowbyte * y + (x / 2);
            dst_bits[dst_pos] |= ((src_bits[src_pos] >> (7 - (x % 8))) & 0x01) << (4 * (1 - (x % 2)));
        }
    }
    return dst;
}

BITMAPINFOHEADER
http://msdn.microsoft.com/ja-jp/library/cc352308.aspx
RGBQUAD
http://msdn.microsoft.com/ja-jp/library/ms532316(en-us).aspx


ほほ  2009-01-23 12:05:37  No: 69443

・BITMAPFILEHEADER    
・BITMAPINFOHEADER    
・RGBQUAD            

の構造体を使うようにして
biBitCount = 4 に変えても
これまで同様のbmpファイルを作成することができたのですが

色(パレット)を選択する方法までは分からない状態です。
例えば

/*----- 点を打つ -----*/
void putpixel(int px, int py)
{
  if(checkrange(px, py))
    *(FigureBuff+py*HorizonBytes+(4*px>>3))|=(0x80>>(4*px&0x07));

この場合はどのように記述したらよいのでしょうか?


ほほ  2009-01-23 13:12:27  No: 69444

いろいろ画像を変えてbmpファイルを作っていると

>たとえ幅と高さが同じサイズでもbppが違えば別の値になるから
>やっぱグローバル変数を多用している時点でこのプログラムまずいな。

>やたらと引数が増えて困るとか言うなら
>構造体にしてそのポインタを引数で渡せばいいことだし。

この言葉を実感する状況があるようです。
構造体にしてそのポインタを引数で渡すとは
どんな感じになるのでしょうか?


wclrp ( 'o')  2009-01-23 16:58:15  No: 69445

実際にプログラムをコンパイルしたわけではないのでスペルミスとかあるかも。

・BITMAPFILEHEADER    
・BITMAPINFOHEADER    
・RGBQUAD
がどういうのか忘れたので詳しくは示せない。

typedef unsigned char * BYTE; // BYTEがunsigned char *と同じ意味。
int putpixel(BITMAPINFOHEADER const * pInfo, BYTE * pFigureBuff, int x, int y, int colorIndex);
こんな感じかな。
pInfo:
    以下の条件を満たすこと
    4bppで必要な情報入り
    BITMAPINFOHEADERの次の位置に16色のRGBQUAD付き
    putpixel関数はpInfoの内容を変えないのでconst付き
colorIndex:
    モノクロではないので色を指定できる
BITMAPFILEHEADERはファイル保存するときだけ必要なので略

成功のときreturn 0とか、エラーのときreturn 0以外とか決めておく。

使用方法

BITMAPINFOHEADER BitmapInfo;
BYTE FigureBuff[略];
int ret;

BitmapInfo.biSize = 〜
BitmapInfo.〜 = 〜
BitmapInfo.〜 = 〜
BitmapInfo.〜 = 〜
BitmapInfo.〜 = 〜
BitmapInfoには妥当な値が入っている。
FigureBuffには画像データが入っている。

ret = putpixel(&pInfo, FigureBuff, 5, 10, 12);


wclrp ( 'o')  2009-01-23 17:16:04  No: 69446

面倒だから実際に動くか確認しないで回答する。
間違っているかもしれない。

そういえばビットマップって上下逆さになっているんだよな。
面倒なので上下逆さにしてません。

int putPixel(BITMAPINFOHEADER const * pInfo, BYTE * pFigureBuff, int x, int y, int colorIndex)
{
    int HorizonBytes;
    BYTE * pData;

    /* エラーチェック */
    if(pInfo == NULL) return -1;
    if(x < 0 || x >= pInfo->biWidth) return -1;
    if(略) return -1;
    if(colorIndex< 0 || colorIndex>= 16) return -1;
    if(pInfo->biBitCount != 4) return -1;
    if(略) return -1;

    HorizonBytes = 略
    pData = pFigureBuff + HorizonBytes * y + x/2;
インデント面倒だな
x/2は、1バイトに2ピクセルだから。
if(x&1) {
xが奇数ならpDataの下位4ビットをかき変える
上位4ビットは元の値のまま
*pData = (*pData & 0xf0) | colorIndex;
}
else {
*pData = (*pData & 0x0f) | (colorIndex << 4);
}
return 0;
}

pInfo->biBitCountの値を見て1bppと4bppの両方に対応すればいいね。


wclrp ( 'o')  2009-01-23 17:19:08  No: 69447

ミス
ret = putPixel(&BitmapInfo, FigureBuff, 5, 10, 12);

大文字小文字を取り入れた putPixel とした方が見やすいと思う。


ほほ  2009-01-24 01:14:20  No: 69448

ある図形に追加する

このような四角形の線に

┌────┐
│        │
└────┘

①/*----- 水平線を引く -----*/
void hline(int sx, int sy, int ex)

②/*----- 垂直線を引く -----*/
void vline(int sx, int sy, int ey)

③/*----- 直線を引く -----*/
void line(int x1, int y1, int x2, int y2)

これらを使って(①、②、③)  ねらって色を付けたいのですが
可能でしょうか?


wclrp ( 'o')  2009-01-24 04:57:21  No: 69449

斜めの線とかは難しいが水平線とかならできる。
それより、MFC/C++/WinAPIなどは使えない理由あるの?

Windowsのビットマップへ変換し
GDIの機能で描画して
ファイルに保存するときに4bitビットマップファイルに変換すればいい。

変換はちと難しいかも知れんが
GDIなら文字も描けるし円も描ける。

俺の頭の中に全部プログラムが入っているわけじゃないので
変換については色々ネットを検索してみてくれ。


wclrp ( 'o')  2009-01-24 05:20:30  No: 69450

点が描ければ線も描ける。
ただし遅いよ。
速くしたければputPixelを呼ぶのでなくputPixelの中の処理をかいて
無駄な部分な減らすなど効率いい処理に改造すればいい。

int putPixel(BITMAPINFOHEADER const * pInfo, BYTE * pFigureBuff, int x, int y, int colorIndex);

/*----- 水平線を引く -----*/
void hline(BITMAPINFOHEADER const * pInfo, BYTE * pFigureBuff, int sx, int sy, int ex, int colorIndex)
{
if(sx <= ex)
{
for( ; sx <= ex; ++sx)
{
putPixel(pInfo, pFigureBuff, sx, sy, colorIndex);
}
}
else
{

}
}


ほほ  2009-01-26 01:04:57  No: 69451

ビットマップファイルに数字を書き込む部分があるのですが
書き込む文字幅(X方向)が狭く、4bitに対応できていない状態です。

/*---- グラフィックフォント書き込み (8x8) ----*/
             x=75,  y=70   int=0    *str=42.5 
void bitfonf(int x, int y, int dir, char *str)
    unsigned char  *aplane;
    register  i;
    register  shift;
    unsigned char  or[8];
    unsigned char  pattern[8];
   
    for(i=0; i<8; i++)
      or[i]=0x00;
      switch(dir) {
        case  0:  y-=7;
        shift=x%8;
          aplane=FigureBuff+y*HorizonBytes+(4*x>>3);
    hile(*str) {
  
    bit_font_operation(dir, *str++, pattern);
          for(i=0; i<8; i++) {
             *(unsignedchar*)(aplane+HorizonBytes*i)|=or[i]|(pattern[i]>>shift);
             or[i]=pattern[i]<<(8-shift);
          } 
           aplane++;
          }
          for(i=0; i<8; i++)
           *(unsigned char *)(aplane+HorizonBytes*i)|=(or[i]); 
           break;

          後略

/*----- 8×8ビットパターン回転処理 -----*/
void bit_font_operation(int mode, int code, unsigned char *pattern)
{
    register  i;
    register  j;

    switch(mode) {
      case 0:
       for(i=0; i<8; i++)
          pattern[i]=BitMapFont8x8[code][i];
          break;

          後略

unsigned char  BitMapFont8x8[][8]={
  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},  /*--    --*/
                        中略
  {0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00},  /*-- .  --*/
  {0x00, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00},  /*-- /  --*/
  {0x00, 0x7C, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0x7C},  /*-- 0  --*/
  {0x00, 0x7E, 0x18, 0x18, 0x18, 0x78, 0x38, 0x18},  /*-- 1  --*/
  {0x00, 0xFE, 0xC0, 0x70, 0x1C, 0x06, 0xC6, 0x7C},  /*-- 2  --*/
  {0x00, 0x7C, 0xC6, 0x06, 0x1C, 0x06, 0xC6, 0x7C},  /*-- 3  --*/
  {0x00, 0x0C, 0xFE, 0xCC, 0x6C, 0x3C, 0x1C, 0x0C},  /*-- 4  --*/
  {0x00, 0x78, 0xCC, 0x06, 0x0C, 0xF8, 0xC0, 0xFE},  /*-- 5  --*/
  {0x00, 0x7C, 0xC6, 0xC6, 0xFC, 0x60, 0x30, 0x1C},  /*-- 6  --*/
  {0x00, 0x30, 0x30, 0x30, 0x18, 0x0C, 0xC6, 0xFE},  /*-- 7  --*/
  {0x00, 0x7C, 0xC6, 0xC6, 0x7C, 0xC6, 0xC6, 0x7C},  /*-- 8  --*/
  {0x00, 0x78, 0x0C, 0x06, 0x7E, 0xC6, 0xC6, 0x7C},  /*-- 9  --*/
                      中略
  {0x00, 0x15, 0x0A, 0x15, 0x0A, 0x15, 0x0A, 0x15}  /*--    --*/
};

【4の場合】

□□□□□□□□
□□□□■■□□
■■■■■■■□
■■□□■■□□
□■■□■■□□
□□■■■■□□
□□□■■■□□
□□□□■■□□
①②③④⑤⑥⑦⑧

これまでの、縦・横に線を引く場合は
*(unsignedchar*)(aplane+HorizonBytes*i)|=or[i]|(pattern[i]>>shift);
      
|= 以後を4倍すると良かったのですが
現在は、X方向の書きこみ領域が①〜③位で、4の文字が圧縮されて狭くなっています)
X方向の書きこみ領域(①〜⑧まで)を増やすにはどうしたら良いのでしょうか?
(X方向は問題なく出来ます)

長くなりましてすみません。


wclrp ( 'o')  2009-01-26 07:06:15  No: 69452

>aplane=FigureBuff+y*HorizonBytes+(4*x>>3);

答えは同じで間違ってはいないけど
4bppは1バイトが2ピクセルだから
aplane=FigureBuff+y*HorizonBytes+(x>>1);
でいいんじゃないの。
4倍して8で割る(>>3)んだったら2で割ればいいでしょ。
そもそも8で割るのは1bppで1バイトが8ピクセルだったときの処理なんだし。


wclrp ( 'o')  2009-01-26 07:24:39  No: 69453

俺が書いたputPixelは参考にならなかったのか。
そりゃ実際に動くか確認してないけどさ。

>for(i=0; i<8; i++) {
> *(unsignedchar*)(aplane+HorizonBytes*i)|=or[i]|(pattern[i]>>shift);
> or[i]=pattern[i]<<(8-shift);
>}

このiは縦座標のループなのか。
良くループにiを使うけど、最近俺は可読性が下がるのでやめた方がいいなって思っている。
ループがネストすると無条件でi,j,kって変数使ったりもする習慣だから変えるのは難しいところでもあるけど。

俺がputPixelで
xが奇数なら下位4ビットを変えて上位4ビットは元の値のまま
って説明したのに。
それと
>少なくともビット演算はよく理解していないといけない。
とも説明している。

左辺 = 右辺
の左辺が4bppで右辺が1bppであることを理解していれば難しくないはずなんだけどなあ。

>*(unsignedchar*)(aplane+HorizonBytes*i)|=or[i]|(pattern[i]>>shift);
putPixelを作っておおけば
putPixel(x+ix, y-7+iy, (pattern[iy]&bits[ix])?color:0);
みたいにもすませられるのに。


wclrp ( 'o')  2009-01-26 07:41:27  No: 69454

>*(unsignedchar*)(aplane+HorizonBytes*i)|=or[i]|(pattern[i]>>shift);

or[i]がなんのためのものか不明。

4bppなんだから16色あるので色をの指定が無いのがおかしい。

>shift=x%8;

xって書き込み位置だよね?
xが4だったら(pattern[i]>>4)ってことになるけど意味不明。
pattern[i]の途中から書き始めることになってしまうよ。

aplaneはもともとunsigned char *型なので(unsigned char *)にキャストしなくていい。

一般的には
  縦ループ {
    横ループ {
      処理
    }
  }
だな。


wclrp ( 'o')  2009-01-26 08:22:30  No: 69455

実際にプログラム作らず勘で答えているのでとんでもない間違えしているかもしれない。

orの意味がわかった。
shiftの意味もわかった。
酷過ぎ。
これ1bppのときのプログラムのまま変えていないんだな。

アルゴリズムが1bppのままだとは理解するまでに余計な手間とらされたよ。

>4bitに対応できていない状態です

理解せず適当に4倍してみたらそれっぽくなっただけで全くもって酷過ぎ。
これは対応していないようなもの。
変更箇所が多すぎて回答面倒だな。

まあ誰かが作った酷いプログラムを引き継いでいるだけなんだろうけど。

俺がputPixelで左辺の書き方示したのに事実上無視している状態だし。

右辺はBitMapFont8x8が1bppのデータだから4bppのデータに変えないといけない。

# 左辺のx軸はx+0〜x+7で、右辺のx軸は0〜7の変化。

左辺のx軸が偶数なら上位4ビットをかき変え...略
これから書き変える部分を0にしてそうではない部分を元のままの値にしておく
unsigned char mask4[2] = {0x0f,0xf0};
*(FigureBuff + x + ix + HorizonBytes * (y - 7 + iy)) &= mask4[(x + ix) % 2];
たぶんこんな感じ。
動作確認してないから正しいか判らない。

patternは1bppだから4bppに変換する。
色は背景色0,前景色15とすると、
unsigned char mask1[8] = {128,64,32,16,8,4,2,1}
unsigned char color;
if( (pattern[iy] & mask1[ix%8]) == 0 ) color = 0;
else color = 15;

左辺のx軸が偶数なら上位4ビットをかき変え...なので
右辺のx軸が偶数なら上位4ビットへ移動させる。
if( (ix % 2) == 0 ) color <<= 4;

これから書き変える部分を0にしておいたので
ビット論理和すればそこだけ書き変えたことになる。
*(FigureBuff + x + ix + HorizonBytes * (y - 7 + iy)) |= color;

たぶんこんな感じ。
動作確認してないから正しいか判らない。


ほほ  2009-01-26 09:32:03  No: 69456

なんとなく書換えしていたら上手くいってしまったので...
putPixel、
挑戦してみます。すみません。

BitMapFont8x8[][8]=より値を持ってくる場合
ixとiyの求め方はあるのでしょうか?


wclrp ( 'o')  2009-01-26 10:19:46  No: 69457

BitMapFont8x8[code][iy]の上位からixビット目


ほほ  2009-01-29 06:16:05  No: 69458

ixとiyの記述はどんな感じになるのですか?


wclrp ( 'o')  2009-01-29 09:16:12  No: 69459

ビット演算(二進数、シフト、論理和、論理積)、
4bppは1ピクセル4ビットなので、1バイトに2ピクセル入る。
4bppは1ピクセル4ビットなので、1バイトに2ピクセル入る。

これがどんな感じか二進数や
■□□■□■■□みたいな絵でイメージできるようになってほしい。

作ってみた。動作確認もした。
ここに記載すべきなんだが長いので別の場所に置く。
http://wclrp.blog90.fc2.com/blog-entry-85.html
本来は色付きの見やすいものにしようと思ったけど失敗。
C++である。
一部機能を簡略している。
エラーチェックを省略。省略していいというわけではない。
cnv_2to16をほぼそのまま利用した。


ほほ  2009-01-29 13:12:16  No: 69460

なんとか数字を描けています。
あと1歩です!


ほほ  2009-01-30 06:07:34  No: 69461

ようやく 出来ました。

wclrp ( 'o') 様

あんぽんたんな自分に
何から、何まで教えていただき
感謝の気持ちでいっぱいです。

まだ、疑問なことがあるのですが
先に、解決マークを付けさせていただきます。

本当にありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加