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

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>
            亚洲高清网站| 一本久久青青| 99www免费人成精品| 国产亚洲精品久久久久婷婷瑜伽| 欧美亚男人的天堂| 国产精品在线看| 韩国成人精品a∨在线观看| 黄色成人片子| 91久久精品久久国产性色也91| 国产精品大片免费观看| 国产精品国产三级国产普通话99 | 亚洲美女色禁图| 在线视频你懂得一区二区三区| 日韩午夜免费视频| 亚洲欧美电影在线观看| 久久成年人视频| 亚洲国产日韩在线一区模特| 亚洲伊人网站| 蜜桃久久精品乱码一区二区| 国产精品wwwwww| 亚洲第一色中文字幕| 亚洲午夜性刺激影院| 久久久久国产精品厨房| 亚洲国产欧美日韩另类综合| 亚洲欧美精品一区| 欧美高清不卡| 激情亚洲网站| 欧美一级视频| 亚洲激情中文1区| 欧美一区精品| 国产精品国产三级国产专播品爱网| 国内不卡一区二区三区| 一本色道久久综合| 美女爽到呻吟久久久久| 亚洲一区二区三| 欧美精选午夜久久久乱码6080| 国产自产v一区二区三区c| 亚洲图中文字幕| 欧美成人精品一区二区| 亚洲欧美日本另类| 欧美日韩免费| 99热精品在线观看| 欧美大片免费久久精品三p | 亚洲精品乱码久久久久久蜜桃91| 欧美一区二区视频在线| 亚洲精品视频一区二区三区| 噜噜噜噜噜久久久久久91| 国产精品视频内| 亚洲图片欧美一区| 日韩视频二区| 欧美精品一区二| 亚洲理伦电影| 亚洲国产精品一区二区www| 久久久国际精品| 在线观看视频免费一区二区三区| 久久久久久高潮国产精品视| 性亚洲最疯狂xxxx高清| 国产欧美日韩精品一区| 午夜精品一区二区三区四区 | 亚洲国产成人久久综合一区| 久久av资源网站| 亚洲一区精品在线| 国产精品美女视频网站| 先锋影音久久| 欧美一区二区在线免费观看| 国产日韩综合一区二区性色av| 欧美在线free| 久久国产婷婷国产香蕉| 伊人婷婷欧美激情| 欧美激情成人在线| 欧美另类综合| 亚洲一区二区综合| 亚洲综合日韩中文字幕v在线| 国产欧美精品国产国产专区| 久久综合福利| 欧美激情第4页| 亚洲一区二区三区四区在线观看 | 欧美激情一区二区三区全黄| 欧美va亚洲va国产综合| 99riav久久精品riav| 99国内精品久久久久久久软件| 国产精品hd| 久久亚洲免费| 欧美日本高清视频| 亚洲欧美在线x视频| 欧美一区二区三区四区高清 | 久久伊人精品天天| 欧美激情综合色| 亚洲女同同性videoxma| 久久久久国产一区二区三区四区| 亚洲片区在线| 亚洲综合日韩| 日韩午夜视频在线观看| 亚洲欧美激情四射在线日| 极品尤物av久久免费看| 亚洲经典三级| 国产精品香蕉在线观看| 欧美激情bt| 国产日韩欧美亚洲| 亚洲激情成人| 精品91在线| 一区二区三区欧美成人| 亚洲国产精品美女| 亚洲欧美日韩国产另类专区| 99国产精品99久久久久久| 性欧美xxxx视频在线观看| 亚洲人成77777在线观看网| 亚洲摸下面视频| 亚洲一区二区三区高清不卡| 蜜桃久久av| 免费视频一区| 国产日韩一区二区三区在线播放 | 欧美不卡三区| 欧美日韩国产精品专区| 久久精品视频免费观看| 欧美精品免费视频| 久久在线免费观看| 国产精品久久久久一区二区| 亚洲欧洲三级| 亚洲国产精品一区| 久久精品2019中文字幕| 香蕉国产精品偷在线观看不卡| 欧美电影美腿模特1979在线看| 久久久久久**毛片大全| 国产日韩精品久久久| 亚洲午夜久久久久久久久电影网| 亚洲精品视频在线观看网站| 久久久久久一区二区| 欧美综合国产精品久久丁香| 欧美三级特黄| 一本色道88久久加勒比精品| 日韩视频欧美视频| 欧美精品日韩| 亚洲伦伦在线| 一区二区三区日韩| 欧美日本高清| 在线亚洲免费| 午夜性色一区二区三区免费视频| 欧美午夜大胆人体| 亚洲一区免费视频| 欧美主播一区二区三区美女 久久精品人| 欧美性生交xxxxx久久久| 一区二区欧美在线观看| 亚洲综合色婷婷| 国产精品日产欧美久久久久| 亚洲在线观看视频网站| 午夜精品影院在线观看| 国产啪精品视频| 久久久99久久精品女同性| 免费黄网站欧美| 亚洲精品一区二区三区福利| 欧美高清在线| 亚洲视频网在线直播| 久久精品国产亚洲精品| 亚洲国产成人精品视频 | 永久免费精品影视网站| 老司机一区二区| 亚洲黄页一区| 欧美一级免费视频| 在线免费高清一区二区三区| 欧美激情1区2区3区| 亚洲视频国产视频| 久久人人爽人人爽| 亚洲美女精品成人在线视频| 国产精品免费看| 浪潮色综合久久天堂| 日韩视频免费观看| 久久久夜夜夜| 在线一区二区三区做爰视频网站| 国产精品在线看| 欧美成人在线影院| 亚洲欧美日韩一区二区三区在线观看 | 日韩午夜免费| 国产欧美日韩一区二区三区在线观看 | 久久久五月天| 日韩一区二区精品| 国内欧美视频一区二区| 欧美激情一区二区三区| 午夜精品久久久久久久久久久久久| 欧美chengren| 欧美专区日韩视频| 亚洲少妇在线| 亚洲国产综合视频在线观看| 国产欧美日韩视频一区二区三区| 欧美精品一区在线发布| 久久国产精品99国产| 亚洲午夜免费福利视频| 亚洲欧洲免费视频| 老巨人导航500精品| 亚洲一区在线播放| 亚洲韩国青草视频| 国产一区二区三区高清在线观看 | 亚洲国产精品视频| 国产欧美日本| 欧美无乱码久久久免费午夜一区| 牛牛影视久久网| 久久久最新网址| 久久精品视频免费| 久久国产精品网站| 欧美伊人久久大香线蕉综合69|