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

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>
            午夜精品久久久久久久白皮肤| 欧美高清视频一区二区| 麻豆国产va免费精品高清在线| 久久激情五月激情| 久久精品亚洲精品国产欧美kt∨| 久久精品国产在热久久| 久久国产精品色婷婷| 欧美一区二区三区四区在线| 亚洲一区二区三区中文字幕在线| 国产精品99久久不卡二区| 亚洲免费在线视频| 久久青草欧美一区二区三区| 亚洲电影在线观看| 亚洲激情网址| 亚洲欧美在线看| 久久久99国产精品免费| 欧美国产日韩二区| 国产精品一区二区男女羞羞无遮挡| 国产色综合久久| 亚洲日韩视频| 性欧美大战久久久久久久久| 久久综合电影| 亚洲视频免费看| 欧美风情在线观看| 国产精品日韩精品欧美在线| 国产午夜久久久久| 亚洲精品久久久久中文字幕欢迎你 | 亚洲综合色噜噜狠狠| 另类av导航| 日韩图片一区| 欧美freesex8一10精品| 国产精品一二一区| 亚洲乱码国产乱码精品精可以看 | 欧美大片在线看| 国产视频一区三区| 中日韩高清电影网| 欧美不卡三区| 欧美亚洲网站| 国产精品成人观看视频免费| 亚洲激情欧美激情| 久久久久久婷| 午夜精品亚洲| 国产精品日韩精品欧美精品| 日韩一区二区精品| 欧美国内亚洲| 六月丁香综合| 伊人久久婷婷| 久久免费视频在线观看| 亚洲欧美日韩精品久久亚洲区| 欧美日韩免费看| 一本大道久久精品懂色aⅴ| 亚洲第一黄网| 欧美精品videossex性护士| 亚洲国产精品嫩草影院| 欧美aa在线视频| 久久伊人精品天天| 在线观看成人小视频| 久久久久久久网| 久久久久久精| 亚洲二区精品| 亚洲电影视频在线| 欧美日韩国产精品一区| 99视频在线精品国自产拍免费观看 | 久久一二三四| 亚洲大片精品永久免费| 欧美顶级大胆免费视频| 蜜臀a∨国产成人精品 | 久久综合亚州| 美女诱惑黄网站一区| 欧美猛交免费看| 欧美v国产在线一区二区三区| 黑人巨大精品欧美一区二区 | 国产日韩成人精品| 久久精品视频免费播放| 久久av一区二区三区漫画| 国产一区二区三区最好精华液| 久久人人超碰| 欧美二区视频| 亚洲午夜精品久久久久久app| 一区二区日韩免费看| 国产精品综合色区在线观看| 久久久噜噜噜久久狠狠50岁| 美女精品一区| 亚洲女ⅴideoshd黑人| 欧美一区二区高清| 亚洲日本无吗高清不卡| 亚洲一区二区在线视频| 伊人久久大香线蕉av超碰演员| 亚洲精品精选| 国产精品尤物| 欧美成人午夜视频| 欧美午夜精品一区| 欧美mv日韩mv国产网站app| 欧美精品在线视频| 久久国产黑丝| 欧美日韩精品是欧美日韩精品| 久久国产加勒比精品无码| 欧美激情精品久久久久久大尺度| 欧美亚洲一区三区| 欧美激情第三页| 久久精品国产v日韩v亚洲| 欧美激情亚洲激情| 久久麻豆一区二区| 欧美午夜女人视频在线| 你懂的成人av| 国产日韩欧美综合一区| 亚洲另类视频| 亚洲国产影院| 久久9热精品视频| 亚洲综合精品一区二区| 欧美www视频在线观看| 久久久久国产免费免费| 国产精品jvid在线观看蜜臀| 欧美国产日韩精品免费观看| 国产偷自视频区视频一区二区| 亚洲精品国精品久久99热一| 亚洲第一视频网站| 久久成人综合视频| 欧美一区二区视频免费观看| 欧美日本一区| 亚洲国产婷婷香蕉久久久久久99| 国产自产女人91一区在线观看| 一本色道88久久加勒比精品| 日韩亚洲成人av在线| 老司机免费视频一区二区| 久久亚洲一区二区三区四区| 国产欧美一区二区精品秋霞影院| 一本综合精品| 亚洲永久精品国产| 欧美亚州一区二区三区| 亚洲美女精品久久| 99精品国产在热久久婷婷| 亚洲成色精品| 欧美成年人视频网站| 开心色5月久久精品| 韩日成人av| 久久福利电影| 国产精品v片在线观看不卡| 亚洲视频一区二区在线观看| 欧美电影免费观看大全| 欧美激情一区二区三区在线| 在线日韩中文字幕| 久久深夜福利免费观看| 美女精品在线| 亚洲国产精品123| 欧美成人日韩| 亚洲精品久久久久久久久久久| 一本色道久久综合精品竹菊| 欧美日韩三级电影在线| 一区二区欧美日韩| 欧美尤物一区| 亚洲电影免费在线| 欧美精品乱人伦久久久久久| 亚洲免费观看在线视频| 亚洲欧美区自拍先锋| 国产日韩精品久久久| 麻豆精品网站| 99精品国产福利在线观看免费 | 99re6这里只有精品视频在线观看| 一本一本久久a久久精品综合麻豆| 欧美视频日韩| 久久国产精品99精品国产| 欧美成人情趣视频| 亚洲宅男天堂在线观看无病毒| 国产日韩一区二区三区| 免费一级欧美片在线播放| 亚洲精品欧美日韩| 久久精品人人做人人爽电影蜜月| 亚洲人成在线播放| 国产精品一区二区在线观看不卡| 久久青草久久| 99精品视频网| 欧美11—12娇小xxxx| 亚洲一区在线视频| 一色屋精品亚洲香蕉网站| 欧美日韩精品在线视频| 久久se精品一区二区| 亚洲美女在线一区| 老司机精品久久| 亚洲一区二区少妇| 亚洲精品国产精品久久清纯直播| 国产精品丝袜久久久久久app| 蜜桃久久精品乱码一区二区| 午夜精品一区二区在线观看| 亚洲第一福利视频| 久久久久国产精品www| 亚洲一区亚洲二区| 亚洲精品精选| 激情婷婷久久| 国产欧美日韩精品一区| 欧美精品乱人伦久久久久久| 久久免费的精品国产v∧| 亚洲伊人久久综合| 亚洲激情社区| 亚洲国产毛片完整版| 久久视频一区| 久久国产精品久久久| 亚洲午夜一区二区| 99re66热这里只有精品3直播| 在线精品国产成人综合|