青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Que's C++ Studio

大道至簡
posts - 3, comments - 8, trackbacks - 0, articles - 0
BMP 和 JPEG
作者: 闕榮文
時間: 2016.5.29
1. 什么是 BMP
BMP 格式是最簡單,最直觀的位圖數據格式.它的思想非常樸素:
用若干個位來保存一個像素的信息,由若干個像素組成一個像素流來表達一張圖片.
通常我們會用1位(可以保存2種顏色,0 表示一種顏色,1表示另一種,不一定是黑白,也可以是藍綠,總之是2種),4位 - 16種顏色,8位 - 256種顏色,16位 - 65535種顏色,24位 - 2^24種顏色,32位 - 前24位和24位位圖一樣可以保存2^24種顏色,最后8位用來保存這個像素的灰度(也就是這個像素的明暗程度),可以表示256種灰度.注意,目前的顯示器在硬件上通常只能支持24位色,而且 libjpeg 也只能處理最多24位色的像素流.
對于24位以下的BMP,可以引入一個"調色板"來增強圖片的表達能力,以8位,256色位圖作為例子:
在8位位圖中,每個像素的信息用一個字節存儲,那么 DIB 數據就是一個 BYTE dibBuffer[] 數組, dibBuffer[0] 表示第一個像素的顏色,以此類推. 我們知道顏色是由RGB 3個分量組合而成的,編程中用 RGBQUAD 結構表示,那么8位除以3,每個分量只能用2位來存儲(不使用編碼壓縮RLE的前提下),實際能夠表現的顏色非常有限.現在我們引入一個長度為256的 RGBQUAD 類型的數組: RGBQUAD colorTable[256],我們在 DBI 數組 dibBuffer[] 中不再直接存放的每個像素的顏色值,而是存放該顏色值在 colorTable 中的索引,這樣就可以充分利用 dibBuffer 中的每一位的存儲空間.這個 "colorTable" 就是 Windows 中調色板的概念. 知道了這些,就可以理解為什么24位及以上色深的位圖不需要調色板了.

2.1. BMP 文件格式和DIB
DIB就是"設備無關位圖"的意思,我們可以理解為一個像素數組,這是編程時我們需要處理的數據,非常簡單,就是一個定長數組,如果是一個24位的DIB數據,那么在編程時就可以認為是一個 BYTE dibBuffer[], dibBuffer[0],dibBuffer[1],dibBuffer[2]表示第一個像素的 RGB 值(實際上是 BGR), dibBuffer[3],[4],[5] 表示第二個像素的 RGB 值,以此類推.當然我們不能直接把這個 dibBuffer 數組寫到磁盤作為 BMP 文件,缺少圖片的調色板,寬,高等信息,所以我們需要一個特定的格式來存儲 DIB 像素流.
BMP文件格式就是把DIB像素流存儲到磁盤是需要遵循的相關約定. 關于BMP文件格式的詳細說明在網上可以找到很多,比如這篇說的就很清楚: http://blog.csdn.net/lanbing510/article/details/8176231
從編程的角度來看,一個BMP文件是可以表述為以下結構:
typedef struct tagBITMAP_FILE
{
      BITMAPFILEHEADER bitmapheader;
      BITMAPINFOHEADER bitmapinfoheader;
      PALETTEENTRY palette[n]; // 調色板數據(可選,由BITMAPFILEHEADER::bOffBits計算 n 的值)
      UCHAR *dibBuffer;   // DIB 數據數組
} BITMAP_FILE;
BITMAPFILEHEADER, BITMAPINFOHEADER, PALETTEENTRY 結構的詳細信息可以在 MSDN 中查到.
用自然語言簡單描述一下:
文件頭 - 固定長度,表示這個文件是一個 BMP 文件,版本號,文件長度等,最重要的時文件頭結構中的 bfOffBits 字段,它表示 DIB 像素流數據在文件中的偏移位置,編程時,我們打開一個 BMP 文件,先讀取固定長度的文件頭,在根據這個字段就可以構造前面說的調色板數組 RGBQUAD colorTable[] 和 DIB 數組 BYTE dibBuffer[] 了.
BMP信息頭 - 固定長度,存儲位圖的寬高等信息,需要注意的字段 biHeight, 如果它是正數則表示像素流的信息是倒序存儲的,即位圖的底下一行的像素存儲在前;如果它是負數則表示像素流的信息是正序存儲的,位圖的第一行像素存儲在 dibBuffer 開頭.
調色板數組 - 可選,用文件頭中的偏移地址減去文件頭和信息頭的長度就是調色板數組的長度.
DIB像素流 - 就是 dibBuffer[] 數組.
特別要注意的一點是,在實際編程中, 24位 DIB 數據的存放順序是 BGR 即 dibBuffer[0] 存放的是最后一行的第一個像素的 B 分量, dibBuffer[1] 是 G 分量, dibBuffer[2] 是 R 分量, 而 JPG 壓縮時要求輸入順序是 RGB, 所以把 dibBuffer 提供給 JPEG 壓縮器前需要處理一下, dibBuffer[i] 和 dibBuffer[i + 2] 交換,否則得到的 JPG 圖像顏色是不對的.

2.2. DDB
DDB 是"設備相關位圖"的意思,把 DIB 數據寫入設備之后,設備在內部會把 DIB 數據處理為內部數據格式, Windows GDI 中用 HBITMAP 表述一個 DDB,我們只需要調用相關的 API 就可以了,具體細節不用理會.

3. 什么是 JPEG
JPEG是 DIB 數據的一種編碼規則,前面我們提到 BMP 文件,直接把 DIB 數組 dibBuffer[] 直接寫到文件中,所以BMP文件是原始的,無損失的保存了內存中的圖像數據.如果用某種算法把 dibBuffer 數組編碼壓縮,那么我們也許就沒必要把整個 dibBuffer (通常是一個很大的數組) 直接寫入文件中,從而大大節省磁盤空間. JPEG 就是這樣一種算法.

4. libjpeg
C語言實現的 JPEG 庫,官網地址: http://www.ijg.org/

4.1 編譯
我寫這篇文章的時候 JPEG 庫的版本是 jpeg-9b,從官網上下載源碼 jpegsr9b.zip 解壓后,啟動Visual Studio,進入命令行模式,切換到 jpeg 源碼目錄,輸入: nmake /f makefile.vc 就會看到 jpeg.sln - VS工程文件出現了,用Visual Studio 打開編譯即可.
如果執行 nmake 命令時提示找不到 win32.mak,就編輯一下 makefile.vc 把第12行 !include <win32.mak> 注釋掉就可以,其實 nmake /f makefile.vc 并不是真正編譯,這是重命名了幾個文件而已.
編譯完成后得到: jpeg.lib 這就是你需要的庫文件了,再把 jconfig.h, jerror.h, jinclude.h, jmorecfg.h, jpeglib.h 復制到你的工程中就算配置完成了.

4.2 example.c
libjpeg 的使用實例在源碼包中的 example.c 文件里, 我們只要把 write_JPEG_file / read_JPEG_file 兩個函數看明白就可以應付大多數應用.
 
4.3 內存 JPG 壓縮解壓縮及其它
example.c 中的實例是使用文件io的, 用 jpeg_mem_src / jpeg_mem_dest 函數代替 jpeg_stdio_src / jpeg_stdio_dest 就可以實現內存io.
JPEG庫是不知道調色板之類的東西的,它只是很單純的把輸入的 DIB 像素流壓縮輸出為一個更短的輸出數據流.所以對于包含了調色板的 BMP 文件,由于 DIB 數組內保存的是調色板的索引號而并不是顏色值,在提交給 JPEG 庫之前需要根據調色板查表構造一個真正的包含顏色信息的 DIB 像素流,這樣 JPEG 庫才能正常工作.

=======================================
我之前的的博客地址是 http://blog.csdn.net/querw 還有一些我以前寫的文章.實在受不了他們網站的各種問題,決定改變陣地.

====================================================================================================
附錄: 截取windows桌面,并保存為 .jpg 文件
  1 #include "stdafx.h"
  2 #include <tchar.h>
  3 
  4 extern "C"
  5 {
  6     #include "jpeglib.h"
  7 }
  8 
  9 int save_screen_to_jpeg(const char* filename, int quality)
 10 {
 11     /*
 12     * 把屏幕內容保存為一個 HBITMAP DDB
 13     */
 14     HDC hScrnDC = CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
 15     HDC hMemDC = CreateCompatibleDC(hScrnDC);
 16 
 17     // 獲取屏幕分辨率
 18     int xScrn = GetDeviceCaps(hScrnDC, HORZRES);
 19     int yScrn = GetDeviceCaps(hScrnDC, VERTRES);
 20 
 21     // 創建位圖,并選中
 22     HBITMAP hScrnBmp = CreateCompatibleBitmap(hScrnDC, xScrn, yScrn);
 23     SelectObject(hMemDC, hScrnBmp);
 24 
 25     // 復制屏幕內容
 26     BitBlt(hMemDC, 00, xScrn, yScrn, hScrnDC, 00, SRCCOPY);
 27 
 28     // 現在得到了一個 HBITMAP DDB - hScrnBmp
 29 
 30     /*
 31     * 通過 hScrnBmp DDB 取得 DIB 數據
 32     */
 33     // 獲取色深 JPG 只能處理 24 位色,所以不管當前系統設置的色深是多少,我們都要求 GetDIBits 函數返回 24 位的 DIB 數據,同時也不需要調色板
 34     //int colorDeepBits = GetDeviceCaps(hScrnBmp, BITSPIXEL);
 35     //if(colorDeepBits > 24) colorDeepBits = 24;
 36     int colorDeepBits = 24;
 37 
 38     // 每行像素占用的字節數,每行要對齊4字節.
 39     int imageRowSize = (xScrn * colorDeepBits + 31/ 32 * 4
 40 
 41     // 分配 DIB 數組
 42     unsigned char* dibBuffer = new unsigned char[imageRowSize * yScrn];
 43     assert(dibBuffer);
 44     memset(dibBuffer, 0, imageRowSize * yScrn);    // 清零是個好習慣    
 45 
 46     // 填充 BMP 信息頭
 47     BITMAPINFO bmi = {0};
 48     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
 49     bmi.bmiHeader.biWidth = xScrn;
 50     bmi.bmiHeader.biHeight = yScrn * -1;        // JPG 壓縮需要正序的 DIB 像素流,所以要負數.
 51     bmi.bmiHeader.biPlanes = 1;
 52     bmi.bmiHeader.biBitCount = colorDeepBits;
 53     bmi.bmiHeader.biCompression = BI_RGB;
 54 
 55     // 獲取 DIB 像素數組(DIB_RGB_COLORS 表示獲取 RGB 值而不是調色板索引,當然24位位圖也沒有調色板)
 56     int gdiRet = GetDIBits(hMemDC, hScrnBmp, 0, yScrn, dibBuffer, &bmi, DIB_RGB_COLORS);
 57     assert(gdiRet == yScrn);
 58     assert(bmi.bmiHeader.biSizeImage == imageRowSize * yScrn);
 59 
 60     // DIB 數據已經獲取,所有的 GDI 對象可以釋放了.
 61     DeleteDC(hScrnDC);
 62     DeleteDC(hMemDC);
 63     DeleteObject(hScrnBmp);
 64 
 65     /*
 66     * 把 DIB 數據壓縮為 JPG 數據,用 example.c 中的代碼
 67     */
 68 
 69     // DIB 中顏色的存放順序是 BGR, 而 JPG 要求的順序是 RGB, 所以要交換 R 和 B.
 70     // 由于有行對齊因素,所以逐行處理
 71     for(int row = 0; row < yScrn; ++row)
 72     {
 73         unsigned char* rowData = dibBuffer + imageRowSize * row;
 74         for(int col = 0; col < xScrn * 3; col += 3)
 75         {
 76             unsigned char swap = rowData[col];
 77             rowData[col] = rowData[col + 2];
 78             rowData[col + 2= swap;
 79         }
 80     }
 81 
 82     //把位圖數據壓縮為 jpeg
 83     struct jpeg_compress_struct cinfo;
 84     struct jpeg_error_mgr jerr;
 85     FILE * outfile;        /* target file */
 86     JSAMPROW row_pointer[1];    /* pointer to JSAMPLE row[s] */
 87     int row_stride;        /* physical row width in image buffer */
 88     int image_width = xScrn;
 89     int image_height = yScrn;
 90     JSAMPLE* image_buffer = dibBuffer;                    // DIB buffer
 91     int image_buffer_len = imageRowSize * image_height;    // DIB buffer 長度
 92 
 93     if(fopen_s(&outfile, filename, "wb"))
 94     //if ((outfile = fopen_s(filename, "wb")) == NULL) 
 95     {
 96         fprintf(stderr, "can't open %s\n", filename);
 97         assert(0);
 98     }
 99     else
100     {
101         /* Step 1: allocate and initialize JPEG compression object */
102         cinfo.err = jpeg_std_error(&jerr);
103 
104         /* Now we can initialize the JPEG compression object. */
105         jpeg_create_compress(&cinfo);
106 
107         /* Step 2: specify data destination (eg, a file) */
108         /* Note: steps 2 and 3 can be done in either order. */
109         jpeg_stdio_dest(&cinfo, outfile);
110 
111         /* Step 3: set parameters for compression */
112 
113         /* First we supply a description of the input image.
114          * Four fields of the cinfo struct must be filled in:
115          */
116         cinfo.image_width = image_width;     /* image width and height, in pixels */
117         cinfo.image_height = image_height;
118         cinfo.input_components = 3;        /* # of color components per pixel */ // 因為DIB數據是24位的,所以每個像素占用3個字節
119         cinfo.in_color_space = JCS_RGB;     /* colorspace of input image */
120         /* Now use the library's routine to set default compression parameters.
121          * (You must set at least cinfo.in_color_space before calling this,
122          * since the defaults depend on the source color space.)
123          */
124         jpeg_set_defaults(&cinfo);
125         /* Now you can set any non-default parameters you wish to.
126          * Here we just illustrate the use of quality (quantization table) scaling:
127          */
128         jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
129 
130         /* Step 4: Start compressor */
131 
132         /* TRUE ensures that we will write a complete interchange-JPEG file.
133          * Pass TRUE unless you are very sure of what you're doing.
134          */
135         jpeg_start_compress(&cinfo, TRUE);
136 
137         /* Step 5: while (scan lines remain to be written) */
138         /*           jpeg_write_scanlines(); */
139 
140         /* Here we use the library's state variable cinfo.next_scanline as the
141          * loop counter, so that we don't have to keep track ourselves.
142          * To keep things simple, we pass one scanline per call; you can pass
143          * more if you wish, though.
144          */
145         row_stride = imageRowSize;
146         while (cinfo.next_scanline < cinfo.image_height) 
147         {
148             /* jpeg_write_scanlines expects an array of pointers to scanlines.
149              * Here the array is only one element long, but you could pass
150              * more than one scanline at a time if that's more convenient.
151              */
152             row_pointer[0= &image_buffer[cinfo.next_scanline * row_stride];
153             //row_pointer[0] = &image_buffer[image_buffer_len - (cinfo.next_scanline + 1) * row_stride];
154             (void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
155         }
156 
157         /* Step 6: Finish compression */
158         jpeg_finish_compress(&cinfo);
159 
160         /* After finish_compress, we can close the output file. */
161         fclose(outfile);
162         
163         /* Step 7: release JPEG compression object */
164         /* This is an important step since it will release a good deal of memory. */
165         jpeg_destroy_compress(&cinfo);
166     }
167 
168     // 釋放 DIB 數組
169     delete []dibBuffer;
170     return 0;
171 }
172 

Feedback

# re: BMP和JPEG - 附截屏保存為JPG源碼  回復  更多評論   

2016-06-13 10:07 by demo
多謝 馬克下
現在成功截圖并在內存中壓縮成jpeg
但是我想在dc中顯示 如何將內存中jpeg再轉回HBitmap呢?
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            奶水喷射视频一区| 久久精品女人| 欧美高清视频一区| 老司机午夜免费精品视频| 黄色在线一区| 亚洲成色最大综合在线| 久久爱www| 香蕉av福利精品导航| 国产亚洲制服色| 牛夜精品久久久久久久99黑人| 欧美一区二区私人影院日本| 国产区精品视频| 麻豆成人精品| 欧美巨乳在线观看| 欧美一区日本一区韩国一区| 久久精品中文字幕一区| 日韩视频一区二区三区在线播放免费观看 | 国产无一区二区| 久久尤物视频| 欧美另类在线观看| 小嫩嫩精品导航| 欧美一区二区在线免费观看| 亚洲国产日韩欧美| 亚洲一区中文字幕在线观看| 在线观看av不卡| 在线视频精品一区| 亚洲国产精品va| 一本一本久久| 亚洲国产精品久久| 亚洲综合色网站| 亚洲狼人精品一区二区三区| 先锋影音一区二区三区| 一本色道88久久加勒比精品| 欧美一区二区视频免费观看| 中国亚洲黄色| 免费久久99精品国产| 久久精品国产77777蜜臀| 欧美—级a级欧美特级ar全黄| 国内精品久久久久久久果冻传媒| 日韩视频中文| 久久精品国产在热久久| 欧美激情一区在线| 国产一区二区中文| 日韩视频在线观看| 国内精品久久久久影院色 | 亚洲永久免费av| 久久亚洲影院| 久久久国产91| 国产精品亚洲成人| 一本久久精品一区二区| 99精品视频免费观看视频| 久久免费视频在线| 午夜视频在线观看一区二区三区| 亚洲午夜精品视频| 一区二区免费在线播放| 老司机精品视频网站| 久久久欧美一区二区| 国产拍揄自揄精品视频麻豆| 9l视频自拍蝌蚪9l视频成人| 99re视频这里只有精品| 欧美大片在线观看一区二区| 欧美成人在线免费观看| 在线日本高清免费不卡| 久久午夜电影网| 久久亚洲国产成人| 一区二区三区中文在线观看| 久久精品一区二区国产| 免费看黄裸体一级大秀欧美| 亚洲大胆人体视频| 欧美jjzz| 夜夜嗨av一区二区三区四区 | 国产乱码精品一区二区三区五月婷| 性色av一区二区三区红粉影视| 亚久久调教视频| 久久久国产精品一区二区中文| 久久久噜噜噜久久久| 久久久久久成人| 黄色精品免费| 欧美69视频| 亚洲免费观看高清完整版在线观看熊 | 欧美日韩精品一本二本三本| 亚洲另类一区二区| 亚洲欧美日韩在线播放| 国产精品女人久久久久久| 午夜精品久久久久久久久久久| 亚洲高清自拍| 欧美成人蜜桃| 亚洲视频一区二区免费在线观看| 一区二区在线观看视频在线观看| 亚洲清纯自拍| 亚洲性视频网址| 韩国av一区二区| 欧美成人精品激情在线观看| 夜夜爽www精品| 久久久久一区二区三区| 亚洲理伦在线| 国产裸体写真av一区二区| 久久一区二区三区四区五区| avtt综合网| 久久免费视频在线| 一区二区欧美亚洲| 久久精品国语| 久久久免费精品视频| 亚洲精选中文字幕| 国产欧美日韩中文字幕在线| 欧美大胆成人| 午夜视频一区在线观看| 亚洲人人精品| 老色鬼久久亚洲一区二区| 亚洲午夜伦理| 亚洲激情一区二区| 国产日韩欧美中文| 欧美视频一区二区三区…| 久久久五月婷婷| 亚洲视频在线观看一区| 亚洲精品视频一区| 欧美成人自拍视频| 久久精品视频导航| 亚洲一区亚洲二区| 99视频+国产日韩欧美| 激情小说另类小说亚洲欧美| 国产精品欧美一区喷水| 欧美日韩999| 欧美成人午夜激情在线| 久久久久久久综合色一本| 亚洲欧美日韩在线观看a三区| 亚洲欧美制服另类日韩| 日韩视频在线观看国产| 国产一区二区三区自拍| 国产精品一区二区三区四区五区| 亚洲欧美日韩久久精品| 日韩亚洲综合在线| 最新国产拍偷乱拍精品| 亚洲电影免费观看高清完整版在线| 亚洲第一网站免费视频| 韩国成人福利片在线播放| 国产精品一区二区a| 国产精品久久久久免费a∨| 欧美视频在线观看| 欧美高清视频在线播放| 欧美成人日韩| 免费看的黄色欧美网站| 蜜臀av国产精品久久久久| 卡通动漫国产精品| 理论片一区二区在线| 久久夜色撩人精品| 久热精品视频| 欧美国产综合视频| 欧美精品一区二区三| 欧美片第一页| 国产精品乱码一区二区三区| 国产精品嫩草影院av蜜臀| 国产欧美一区二区色老头 | 亚洲欧美日韩国产一区| 亚洲一区免费| 欧美在线视频免费| 久久久www| 欧美69视频| 国产精品家庭影院| 国产偷久久久精品专区| 亚洲国产mv| 亚洲视频网在线直播| 亚洲女同在线| 久色成人在线| 亚洲大片在线观看| av成人国产| 久久成年人视频| 欧美激情亚洲国产| 国产精品区一区二区三| 一区二区视频免费完整版观看| 国产精品日韩在线一区| 黄色亚洲免费| 亚洲最新视频在线| 久久精品女人| 最新国产精品拍自在线播放| 亚洲香蕉网站| 美女露胸一区二区三区| 国产精品国产三级国产| 狠狠久久五月精品中文字幕| 亚洲六月丁香色婷婷综合久久| 亚洲精品看片| 午夜久久久久久久久久一区二区| 亚洲高清资源| 9i看片成人免费高清| 久久精品国产一区二区三区免费看 | 日韩一级不卡| 久久狠狠亚洲综合| 最新中文字幕一区二区三区| 午夜视黄欧洲亚洲| 欧美视频二区36p| 亚洲日韩欧美一区二区在线| 欧美一区二区精品在线| 亚洲二区在线视频| 午夜久久电影网| 欧美午夜a级限制福利片| 亚洲福利av| 久久久久久网| 亚洲综合日韩中文字幕v在线| 亚洲综合导航| 欧美日韩一区在线播放|