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

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>
            欧美一级专区免费大片| 国产精品多人| 黄色av成人| 巨胸喷奶水www久久久免费动漫| 亚洲国产欧美精品| 欧美亚洲自偷自偷| 亚洲国产成人av| 亚洲国产乱码最新视频| 欧美日韩福利视频| 午夜精品久久久| 性欧美18~19sex高清播放| 国产一区香蕉久久| 免费在线成人av| 欧美日韩日本视频| 久久成人精品一区二区三区| 久久国产成人| 亚洲理论在线| 欧美日韩免费观看一区| 亚洲精品专区| 亚洲第一区中文99精品| 欧美日韩视频在线观看一区二区三区 | 激情视频一区二区| 欧美韩国日本一区| 国产精品盗摄久久久| 欧美在线黄色| 欧美精品电影| 久久亚洲一区二区| 欧美日韩亚洲三区| 噜噜噜久久亚洲精品国产品小说| 欧美日韩国产限制| 久久一区二区三区国产精品 | 欧美成人精品激情在线观看| 亚洲欧美日韩高清| 美女主播一区| 久久视频在线视频| 欧美系列一区| 亚洲激情电影在线| 一区二区三区在线视频播放| 亚洲久久成人| 尤物yw午夜国产精品视频明星| 一本色道综合亚洲| 亚洲电影专区| 欧美在线国产精品| 亚洲一区二区在线| 欧美成人国产| 久久精品国产成人| 欧美日本国产| 亚洲国产精品久久人人爱蜜臀| 国产一区二区三区四区老人| 99在线热播精品免费| 亚洲国产精品va| 欧美一区二区三区视频在线观看 | 亚洲欧美激情视频在线观看一区二区三区 | 亚洲欧美日韩第一区| 欧美精品导航| 亚洲日本aⅴ片在线观看香蕉| 激情久久一区| 久久久久亚洲综合| 久久免费国产精品1| 国产午夜精品理论片a级探花| 99视频在线观看一区三区| 亚洲毛片在线观看| 欧美福利视频| 亚洲国产精品一区二区尤物区| 亚洲成人资源| 麻豆精品一区二区综合av| 麻豆国产精品777777在线| 狠狠色2019综合网| 久久久久久久久久久久久9999| 久久久久久精| 亚洲成人在线视频播放| 欧美91视频| 最近看过的日韩成人| 亚洲另类自拍| 国产精品v欧美精品v日韩精品| 亚洲私拍自拍| 韩国免费一区| 亚洲黄色影片| 国产精品99久久久久久www| 欧美绝品在线观看成人午夜影视| 亚洲激情电影中文字幕| 亚洲天堂免费观看| 国产嫩草影院久久久久| 欧美在线视频日韩| 欧美大片va欧美在线播放| 亚洲另类自拍| 国产欧美日韩视频一区二区三区| 欧美在线观看你懂的| 欧美福利一区二区| 亚洲影院在线| 狠狠色狠狠色综合日日小说| 欧美成人中文字幕在线| 一区二区三区日韩在线观看| 久久天天躁狠狠躁夜夜爽蜜月| 亚洲国产日韩精品| 国产精品高清免费在线观看| 久久精品国产亚洲一区二区三区 | 欧美极品色图| 亚洲综合色婷婷| 久久婷婷色综合| 日韩亚洲欧美精品| 国产欧美日本在线| 蜜臀av国产精品久久久久| 亚洲精品一区在线观看香蕉| 亚洲自拍偷拍一区| 亚洲国产精品国自产拍av秋霞| 欧美理论电影在线观看| 欧美中文字幕久久| 亚洲精品乱码视频| 亚洲欧美综合国产精品一区| 国产一区美女| 欧美日韩一区二区在线视频| 欧美呦呦网站| 日韩午夜黄色| 免费在线亚洲欧美| 午夜精品亚洲| 日韩视频精品在线| 国产在线乱码一区二区三区| 欧美日韩在线电影| 牛牛国产精品| 久久精品99国产精品| av成人免费在线观看| 欧美成人精品h版在线观看| 亚洲欧美日韩国产一区二区| 亚洲欧洲日韩在线| 136国产福利精品导航网址应用 | 欧美午夜片在线观看| 久久久人成影片一区二区三区 | 欧美不卡激情三级在线观看| 午夜精品久久久久久久男人的天堂 | 久久精品中文字幕一区二区三区| 中文国产成人精品| 亚洲日本成人在线观看| 免费成人黄色片| 久久青草欧美一区二区三区| 亚洲综合导航| 在线亚洲欧美| 一本色道婷婷久久欧美| 亚洲国内精品在线| 在线成人激情| 狠狠色狠狠色综合日日五| 国内揄拍国内精品久久| 国产美女诱惑一区二区| 另类图片国产| 久久精品国产免费看久久精品| 亚洲影视在线播放| 一区二区三区国产| 国产精品99久久久久久www| 999在线观看精品免费不卡网站| 亚洲国产另类久久久精品极度| 欧美aⅴ99久久黑人专区| 久久综合久久综合九色| 欧美风情在线| 亚洲第一黄色网| 亚洲韩国青草视频| 日韩视频三区| 亚洲午夜三级在线| 午夜国产一区| 久久伊伊香蕉| 鲁大师影院一区二区三区| 你懂的网址国产 欧美| 欧美不卡三区| 欧美日精品一区视频| 国产精品女主播一区二区三区| 国产欧美一区二区三区沐欲| 国产一区二区三区直播精品电影| 韩国欧美一区| 亚洲精品一区二区三区婷婷月| 99国产精品国产精品久久| 亚洲一区二区成人在线观看| 性欧美办公室18xxxxhd| 欧美阿v一级看视频| 亚洲欧洲日本在线| 亚洲小说欧美另类婷婷| 欧美一级欧美一级在线播放| 久久亚洲春色中文字幕| 欧美精品一区二区三| 国产精品久久一区二区三区| 国一区二区在线观看| 亚洲精品永久免费精品| 亚洲欧美一区二区精品久久久| 久久久久久亚洲精品不卡4k岛国| 欧美国产日韩一区二区三区| 99在线视频精品| 蜜桃精品一区二区三区| 欧美日韩在线亚洲一区蜜芽 | 久久久国际精品| 欧美激情一区三区| 欧美色视频一区| 狠狠色香婷婷久久亚洲精品 | 欧美日韩三级一区二区| 国产日韩一区二区三区| 亚洲激情中文1区| 久久九九精品| 一区二区三区欧美视频| 六月丁香综合| 国产手机视频精品| 亚洲视频高清| 女女同性女同一区二区三区91| 亚洲视频第一页|