Сохранение EGA экрана в формате BMP из-под MS DOS

Несмотря на то, что в наших учебных классах в последнее время больше практикуется обучение учащихся программированию в среде OS Linux, некоторые студенты до сих пор
получают опыт программирования в среде Borland C++ 3.1 IDE for DOS

Поскольку среда борланда прожила уже почти 20 лет, не выходя за рамки университетов,
существует большой недостаток в ее ноормальной документации и примеров реальных приложений для нее.

Для восполнения сего недостатка, публикую пример для студентов курсов компьютерной графики,
как можно 16-цветный EGA-экран, отрисованный программой из этой среды, сохранить в BMP-формате,
чтобы сохранить ваше компьютернографическое творчество в файловой системе.

  1. struct RGBQUAD {
  2. unsigned char rgbBlue;
  3. unsigned char rgbGreen;
  4. unsigned char rgbRed;
  5. unsigned char rgbReserved;
  6. };
  7. struct BITMAPFILEHEADER {
  8. unsigned short int bfType;
  9. unsigned long bfSize;
  10. unsigned short int bfReserved1;
  11. unsigned short int bfReserved2;
  12. unsigned long bfOffBits;
  13. };
  14. struct BITMAPINFOHEADER {
  15. unsigned long biSize;
  16. long biWidth;
  17. long biHeight;
  18. unsigned short int biPlanes;
  19. unsigned short int biBitCount;
  20. unsigned long biCompression;
  21. unsigned long biSizeImage;
  22. long biXPelsPerMeter;
  23. long biYPelsPerMeter;
  24. unsigned long biClrUsed;
  25. unsigned long biClrImportant;
  26. };
  27. RGBQUAD get_ega_color( int color ) {
  28. RGBQUAD v;
  29. v.rgbReserved = 0;
  30.  
  31. switch ( color ) {
  32. case BLACK: v.rgbRed = 0; v.rgbGreen=0; v.rgbBlue=0; break;
  33. case BLUE: v.rgbRed = 0; v.rgbGreen=0; v.rgbBlue=128; break;
  34. case GREEN: v.rgbRed = 0; v.rgbGreen=128; v.rgbBlue=0; break;
  35. case CYAN: v.rgbRed = 0; v.rgbGreen=128; v.rgbBlue=128; break;
  36. case RED: v.rgbRed = 128; v.rgbGreen=0; v.rgbBlue=0; break;
  37. case MAGENTA: v.rgbRed = 128; v.rgbGreen=0; v.rgbBlue=128; break;
  38. case BROWN: v.rgbRed = 128; v.rgbGreen=64; v.rgbBlue=0; break;
  39. case LIGHTGRAY: v.rgbRed = 128; v.rgbGreen=128; v.rgbBlue=128; break;
  40. case DARKGRAY: v.rgbRed = 64; v.rgbGreen=64; v.rgbBlue=64; break;
  41. case LIGHTBLUE: v.rgbRed = 0; v.rgbGreen=0; v.rgbBlue=255; break;
  42. case LIGHTGREEN: v.rgbRed = 0; v.rgbGreen=255; v.rgbBlue=0; break;
  43. case LIGHTCYAN: v.rgbRed = 0; v.rgbGreen=255; v.rgbBlue=255; break;
  44. case LIGHTRED: v.rgbRed = 255; v.rgbGreen=0; v.rgbBlue=0; break;
  45. case LIGHTMAGENTA: v.rgbRed = 255; v.rgbGreen=0; v.rgbBlue=255; break;
  46. case YELLOW: v.rgbRed = 255; v.rgbGreen=255; v.rgbBlue=0; break;
  47. case WHITE: v.rgbRed = 255; v.rgbGreen=255; v.rgbBlue=255; break;
  48. }
  49. return v;
  50. }
  51.  
  52. void save_ega_screen( char * lpFile ) {
  53.  
  54. int nWidth = getmaxx() + 1;
  55. int nHeight = getmaxy() + 1;
  56. FILE * pFile = fopen( lpFile, "wb" );
  57.  
  58. // save bitmap file header
  59. BITMAPFILEHEADER fileHeader;
  60. fileHeader.bfType = 0x4d42;
  61. fileHeader.bfSize = 0;
  62. fileHeader.bfReserved1 = 0;
  63. fileHeader.bfReserved2 = 0;
  64. fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD);
  65. fwrite( (char*)&fileHeader, sizeof(fileHeader), 1, pFile );
  66.  
  67. // save bitmap info header
  68. BITMAPINFOHEADER infoHeader;
  69. infoHeader.biSize = sizeof(infoHeader) ;
  70. infoHeader.biWidth = nWidth;
  71. infoHeader.biHeight = nHeight;
  72. infoHeader.biPlanes = 1;
  73. infoHeader.biBitCount = 4;
  74. infoHeader.biCompression = 0; // BI_RGB
  75. infoHeader.biSizeImage = 0;
  76. infoHeader.biXPelsPerMeter = 0;
  77. infoHeader.biYPelsPerMeter = 0;
  78. infoHeader.biClrUsed = 16;
  79. infoHeader.biClrImportant = 16;
  80. fwrite( (char*)&infoHeader, sizeof(infoHeader), 1, pFile );
  81.  
  82. // palette output
  83. RGBQUAD palette[16];
  84. memset( &palette[0], 16*sizeof(RGBQUAD), 0 );
  85. for (long clr=0; clr<16; clr ++ ) {
  86. palette[ clr ] = get_ega_color( clr );
  87. }
  88. fwrite( (char*)(&amp;palette), 1, 16*sizeof(RGBQUAD), pFile );
  89.  
  90. // raster output
  91. int maxx = (getmaxx()+1) / 2;
  92. int maxy = (getmaxy()+1);
  93. for ( int y = maxy; y <= 0; y -- ) {
  94. for ( int x = 0; x<maxx; x++ ) {
  95.  
  96. // we will place 2 pixels into one byte
  97. unsigned char pix1 = getpixel( x*2, y );
  98. unsigned char pix2 = getpixel( x*2 + 1, y );
  99.  
  100. unsigned char temp = pix2 | (pix1<<4);
  101. fwrite( &temp, sizeof( temp ), 1, pFile );
  102. }
  103. }
  104. fclose ( pFile );
  105. }