├── .gitignore ├── CMakeLists.txt ├── README.md ├── qdbmp.h ├── main.cpp └── qdbmp.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.swo 2 | *.swp 3 | build/ 4 | 5 | .vscode/ 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(jpeg_decoder) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 5 | 6 | set(SOURCE_FILES main.cpp qdbmp.cpp) 7 | add_executable(jpeg_decoder ${SOURCE_FILES}) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JPEG 解碼器 2 | 3 | 將 jpg 檔轉換為 bmp 檔 4 | 5 | 僅支援 baseline jpg 6 | 7 | ## 故事 8 | 9 | 請**就讀臺灣大學的同學**,看過這篇文章[吳家麟,我教育你](https://city-of-infinity.com/app/b/%E5%85%AB%E5%8D%A6/a/%E5%90%B3%E5%AE%B6%E9%BA%9F%EF%BC%8C%E6%88%91%E6%95%99%E8%82%B2%E4%BD%A0?id=5cbcd478271ae627b77544ff) ([備份](https://hackmd.io/ctyDPW8dTIuHCaCDkgw-SQ?both))之後,再繼續閱讀,感謝! 10 | 11 | ## 關於 JPEG 12 | 13 | 我用 rust 重新實作了一個 JPEG 解碼器,並且撰寫了一份 JPEG 教學:[跟我寫 JPEG 解碼器](https://github.com/MROS/jpeg_tutorial),我在綜合多份資料之後,加上自己的想法,並且爲其繪製多張圖片,力求做到清晰易懂,建議想要學習 JPEG 的人閱讀這份文件,若不熟悉 rust 語言,可以再參考以 C++ 撰寫的本專案 。 14 | 15 | ## 編譯 16 | 17 | ``` sh 18 | mkdir build 19 | cd build 20 | cmake .. 21 | make 22 | ``` 23 | 24 | ## 執行 25 | 26 | ``` sh 27 | ./jpeg_decoder 28 | ``` 29 | 會在同一目錄得到一個 out.bmp 30 | 31 | ## 實做 32 | idct 實做請見 [這裡](https://hackmd.io/MYZhDYE4HZgRgLQCNoBNEBYBm4CmCBDLJfDXABgFYAOayjVSkYIA) 33 | 34 | ## 優化 35 | 目前效能瓶頸為進行霍夫曼編碼時採用 C++ stl 的 map,可嘗試修改為自製之資料結構 36 | 37 | 此外 idct 也仍能進一步優化,可見 [這裡](https://dsp.stackexchange.com/questions/51311/computation-of-the-inverse-dct-idct-using-dct-or-ifft) 38 | 39 | -------------------------------------------------------------------------------- /qdbmp.h: -------------------------------------------------------------------------------- 1 | #ifndef _BMP_H_ 2 | #define _BMP_H_ 3 | 4 | 5 | /************************************************************** 6 | 7 | QDBMP - Quick n' Dirty BMP 8 | 9 | v1.0.0 - 2007-04-07 10 | http://qdbmp.sourceforge.net 11 | 12 | 13 | The library supports the following BMP variants: 14 | 1. Uncompressed 32 BPP (alpha values are ignored) 15 | 2. Uncompressed 24 BPP 16 | 3. Uncompressed 8 BPP (indexed color) 17 | 18 | QDBMP is free and open source software, distributed 19 | under the MIT licence. 20 | 21 | Copyright (c) 2007 Chai Braudo (braudo@users.sourceforge.net) 22 | 23 | Permission is hereby granted, free of charge, to any person obtaining a copy 24 | of this software and associated documentation files (the "Software"), to deal 25 | in the Software without restriction, including without limitation the rights 26 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 27 | copies of the Software, and to permit persons to whom the Software is 28 | furnished to do so, subject to the following conditions: 29 | 30 | The above copyright notice and this permission notice shall be included in 31 | all copies or substantial portions of the Software. 32 | 33 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 36 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 37 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 38 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 39 | THE SOFTWARE. 40 | 41 | **************************************************************/ 42 | 43 | #include 44 | 45 | 46 | 47 | /* Type definitions */ 48 | #ifndef UINT 49 | #define UINT unsigned long int 50 | #endif 51 | 52 | #ifndef USHORT 53 | #define USHORT unsigned short 54 | #endif 55 | 56 | #ifndef UCHAR 57 | #define UCHAR unsigned char 58 | #endif 59 | 60 | 61 | /* Version */ 62 | #define QDBMP_VERSION_MAJOR 1 63 | #define QDBMP_VERSION_MINOR 0 64 | #define QDBMP_VERSION_PATCH 1 65 | 66 | 67 | /* Error codes */ 68 | typedef enum 69 | { 70 | BMP_OK = 0, /* No error */ 71 | BMP_ERROR, /* General error */ 72 | BMP_OUT_OF_MEMORY, /* Could not allocate enough memory to complete the operation */ 73 | BMP_IO_ERROR, /* General input/output error */ 74 | BMP_FILE_NOT_FOUND, /* File not found */ 75 | BMP_FILE_NOT_SUPPORTED, /* File is not a supported BMP variant */ 76 | BMP_FILE_INVALID, /* File is not a BMP image or is an invalid BMP */ 77 | BMP_INVALID_ARGUMENT, /* An argument is invalid or out of range */ 78 | BMP_TYPE_MISMATCH, /* The requested action is not compatible with the BMP's type */ 79 | BMP_ERROR_NUM 80 | } BMP_STATUS; 81 | 82 | 83 | /* Bitmap image */ 84 | typedef struct _BMP BMP; 85 | 86 | 87 | 88 | 89 | /*********************************** Public methods **********************************/ 90 | 91 | 92 | /* Construction/destruction */ 93 | BMP* BMP_Create ( UINT width, UINT height, USHORT depth ); 94 | void BMP_Free ( BMP* bmp ); 95 | 96 | 97 | /* I/O */ 98 | BMP* BMP_ReadFile ( const char* filename ); 99 | void BMP_WriteFile ( BMP* bmp, const char* filename ); 100 | 101 | 102 | /* Meta info */ 103 | UINT BMP_GetWidth ( BMP* bmp ); 104 | UINT BMP_GetHeight ( BMP* bmp ); 105 | USHORT BMP_GetDepth ( BMP* bmp ); 106 | 107 | 108 | /* Pixel access */ 109 | void BMP_GetPixelRGB ( BMP* bmp, UINT x, UINT y, UCHAR* r, UCHAR* g, UCHAR* b ); 110 | void BMP_SetPixelRGB ( BMP* bmp, UINT x, UINT y, UCHAR r, UCHAR g, UCHAR b ); 111 | void BMP_GetPixelIndex ( BMP* bmp, UINT x, UINT y, UCHAR* val ); 112 | void BMP_SetPixelIndex ( BMP* bmp, UINT x, UINT y, UCHAR val ); 113 | 114 | 115 | /* Palette handling */ 116 | void BMP_GetPaletteColor ( BMP* bmp, UCHAR index, UCHAR* r, UCHAR* g, UCHAR* b ); 117 | void BMP_SetPaletteColor ( BMP* bmp, UCHAR index, UCHAR r, UCHAR g, UCHAR b ); 118 | 119 | 120 | /* Error handling */ 121 | BMP_STATUS BMP_GetError (); 122 | const char* BMP_GetErrorDescription (); 123 | 124 | 125 | /* Useful macro that may be used after each BMP operation to check for an error */ 126 | #define BMP_CHECK_ERROR( output_file, return_value ) \ 127 | if ( BMP_GetError() != BMP_OK ) \ 128 | { \ 129 | fprintf( ( output_file ), "BMP error: %s\n", BMP_GetErrorDescription() ); \ 130 | return( return_value ); \ 131 | } \ 132 | 133 | #endif 134 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "qdbmp.h" 7 | 8 | 9 | const int SOI_MARKER = 0xD8; 10 | const int APP0_MARKER = 0xE0; 11 | const int DQT_MARKER = 0xDB; 12 | const int SOF_MARKER = 0xC0; 13 | const int DHT_MARKER = 0xC4; 14 | const int SOS_MARKER = 0xDA; 15 | const int EOI_MARKER = 0xD9; 16 | const int COM_MARKER = 0xFE; 17 | 18 | struct { 19 | int height; 20 | int width; 21 | } image; 22 | 23 | struct { 24 | unsigned char id; 25 | unsigned char width; 26 | unsigned char height; 27 | unsigned char quant; 28 | } subVector[4]; 29 | 30 | unsigned char maxWidth, maxHeight; 31 | 32 | struct acCode { 33 | unsigned char len; 34 | unsigned char zeros; 35 | int value; 36 | }; 37 | 38 | struct RGB { 39 | unsigned char R, G, B; 40 | }; 41 | 42 | typedef double BLOCK[8][8]; 43 | 44 | int quantTable[4][128]; 45 | 46 | const int DC = 0; 47 | const int AC = 1; 48 | std::map, unsigned char> huffTable[2][2]; 49 | 50 | double cos_cache[200]; 51 | 52 | void init_cos_cache() { 53 | for (int i = 0; i < 200; i++) { 54 | cos_cache[i] = cos(i * M_PI / 16.0); 55 | } 56 | } 57 | 58 | class MCU { 59 | public: 60 | BLOCK mcu[4][2][2]; 61 | // 除錯用 62 | void show() { 63 | printf("*************** mcu show ***********************\n"); 64 | for (int id = 1; id <= 3; id++) { 65 | for (int h = 0; h < subVector[id].height; h++) { 66 | for (int w = 0; w < subVector[id].width; w++) { 67 | printf("mcu id: %d, %d %d\n", id, h, w); 68 | for (int i = 0; i < 8; i++) { 69 | for (int j = 0; j < 8; j++) { 70 | printf("%lf ", mcu[id][h][w][i][j]); 71 | } 72 | printf("\n"); 73 | } 74 | } 75 | } 76 | } 77 | }; 78 | void quantify() { 79 | for (int id = 1; id <= 3; id++) { 80 | for (int h = 0; h < subVector[id].height; h++) { 81 | for (int w = 0; w < subVector[id].width; w++) { 82 | for (int i = 0; i < 8; i++) { 83 | for (int j = 0; j < 8; j++) { 84 | mcu[id][h][w][i][j] *= quantTable[subVector[id].quant][i*8 + j]; 85 | } 86 | } 87 | } 88 | } 89 | } 90 | }; 91 | void zigzag() { 92 | for (int id = 1; id <= 3; id++) { 93 | for (int h = 0; h < subVector[id].height; h++) { 94 | for (int w = 0; w < subVector[id].width; w++) { 95 | int zz[8][8] = { 96 | { 0, 1, 5, 6, 14, 15, 27, 28}, 97 | { 2, 4, 7, 13, 16, 26, 29, 42}, 98 | { 3, 8, 12, 17, 25, 30, 41, 43}, 99 | { 9, 11, 18, 24, 31, 40, 44, 53}, 100 | {10, 19, 23, 32, 39, 45, 52, 54}, 101 | {20, 22, 33, 38, 46, 51, 55, 60}, 102 | {21, 34, 37, 47, 50, 56, 59, 61}, 103 | {35, 36, 48, 49, 57, 58, 62, 63} 104 | }; 105 | for (int i = 0; i < 8; i++) { 106 | for (int j = 0; j < 8; j++) { 107 | zz[i][j] = mcu[id][h][w][zz[i][j] / 8][zz[i][j] % 8]; 108 | } 109 | } 110 | for (int i = 0; i < 8; i++) { 111 | for (int j = 0; j < 8; j++) { 112 | mcu[id][h][w][i][j] = zz[i][j]; 113 | } 114 | } 115 | } 116 | } 117 | } 118 | }; 119 | void idct() { 120 | for (int id = 1; id <= 3; id++) { 121 | for (int h = 0; h < subVector[id].height; h++) { 122 | for (int w = 0; w < subVector[id].width; w++) { 123 | double tmp[8][8] = {0}; 124 | // 照定義展開,效能低下 125 | // for (int i = 0; i < 8; i++) { 126 | // for (int j = 0; j < 8; j++) { 127 | // for (int x = 0; x < 8; x++) { 128 | // for (int y = 0; y < 8; y++) { 129 | // tmp[i][j] += (cc(x, y) * mcu[id][h][w][x][y] * cos((2*i+1)*M_PI/16.0*x) * cos((2*j+1)*M_PI/16.0*y)); 130 | // } 131 | // } 132 | // tmp[i][j] /= 4.0; 133 | // } 134 | // } 135 | // 計算兩次一維idct去計算二維idct 136 | double s[8][8] = {}; 137 | for (int j = 0; j < 8; j++) { 138 | for (int x = 0; x < 8; x++) { 139 | for (int y = 0; y < 8; y++) { 140 | s[j][x] += c (y) * mcu[id][h][w][x][y] * cos_cache[(j + j + 1) * y]; 141 | } 142 | s[j][x] = s[j][x] / 2.0; 143 | } 144 | } 145 | for (int i = 0; i < 8; i++) { 146 | for (int j = 0; j < 8; j++) { 147 | for (int x = 0; x < 8; x++) { 148 | tmp[i][j] += c(x) * s[j][x] * cos_cache[(i + i + 1) * x]; 149 | } 150 | tmp[i][j] = tmp[i][j] / 2.0; 151 | } 152 | } 153 | for (int i = 0; i < 8; i++) { 154 | for (int j = 0; j < 8; j++) { 155 | mcu[id][h][w][i][j] = tmp[i][j]; 156 | } 157 | } 158 | } 159 | } 160 | } 161 | } 162 | void decode() { 163 | this->quantify(); 164 | this->zigzag(); 165 | this->idct(); 166 | } 167 | RGB **toRGB() { 168 | RGB **ret = (RGB **)malloc(sizeof(RGB **) * maxHeight * 8); 169 | for (int i = 0; i < maxHeight * 8; i++) { 170 | ret[i] = (RGB *)malloc(sizeof(RGB *) * maxWidth * 8); 171 | } 172 | for (int i = 0; i < maxHeight * 8; i++) { 173 | for (int j = 0; j < maxWidth * 8; j++) { 174 | double Y = trans(1, i, j); 175 | double Cb = trans(2, i, j); 176 | double Cr = trans(3, i, j); 177 | ret[i][j].R = chomp(Y + 1.402*Cr + 128); 178 | ret[i][j].G = chomp(Y - 0.34414*Cb - 0.71414*Cr + 128); 179 | ret[i][j].B = chomp(Y + 1.772*Cb + 128); 180 | } 181 | } 182 | return ret; 183 | } 184 | private: 185 | double cc(int i, int j) { 186 | if (i == 0 && j == 0) { 187 | return 1.0/2.0; 188 | } else if (i == 0 || j == 0) { 189 | return 1.0/sqrt(2.0); 190 | } else { 191 | return 1.0; 192 | } 193 | } 194 | double c(int i) { 195 | static double x = 1.0/sqrt(2.0); 196 | if (i == 0) { 197 | return x; 198 | } else { 199 | return 1.0; 200 | } 201 | } 202 | unsigned char chomp(double x) { 203 | if (x > 255.0) { 204 | return 255; 205 | } else if (x < 0) { 206 | return 0; 207 | } else { 208 | return (unsigned char) x; 209 | } 210 | } 211 | double trans(int id, int h, int w) { 212 | int vh = h * subVector[id].height / maxHeight; 213 | int vw = w * subVector[id].width / maxWidth; 214 | return mcu[id][vh / 8][vw / 8][vh % 8][vw % 8]; 215 | } 216 | }; 217 | 218 | 219 | // 讀取 Section 用之輔助函式 220 | 221 | void showSectionName(const char *s) { 222 | printf("************************ %s **************************\n", s); 223 | return; 224 | } 225 | 226 | unsigned int readSectionLength(FILE *f) { 227 | unsigned char c; 228 | unsigned int length; 229 | fread(&c, 1, 1, f); 230 | length = c; 231 | fread(&c, 1, 1, f); 232 | length = length * 256 + c; 233 | return length; 234 | } 235 | 236 | unsigned int EnterNewSection(FILE *f, const char *s) { 237 | showSectionName(s); 238 | unsigned int len = readSectionLength(f); 239 | printf("本區段長度為 %d\n", len); 240 | return len; 241 | } 242 | 243 | // 讀取各 Section 之函式 244 | 245 | void readCOM(FILE *f) { 246 | unsigned int len = EnterNewSection(f, "COM"); 247 | unsigned char c; 248 | for (int i = 0; i < len - 2; i++) { 249 | fread(&c, 1, 1, f); 250 | printf("%c", c); 251 | } 252 | printf("\n"); 253 | } 254 | 255 | void readAPP(FILE *f) { 256 | unsigned int len = EnterNewSection(f, "APP0"); 257 | char m[5]; 258 | fread(m, 1, 5, f); 259 | printf("使用 %s\n", m); 260 | unsigned char v[2]; 261 | fread(v, 1, 2, f); 262 | printf("版本 %d.%d\n", v[0], v[1]); 263 | fseek(f, 1, SEEK_CUR); 264 | fread(v, 1, 2, f); 265 | printf("x方向像素密度:%d\n", v[0] * 16 + v[1]); 266 | fread(v, 1, 2, f); 267 | printf("y方向像素密度:%d\n", v[0] * 16 + v[1]); 268 | fseek(f, len - 14, SEEK_CUR); 269 | } 270 | 271 | void readDQT(FILE *f) { 272 | unsigned int len = EnterNewSection(f, "DQT"); 273 | len -= 2; 274 | while (len > 0) { 275 | unsigned char c; 276 | fread(&c, 1, 1, f); 277 | len--; 278 | unsigned precision = c >> 4 == 0 ? 8 : 16; 279 | printf("精度:%d\n", precision); 280 | precision /= 8; 281 | unsigned char id = c & 0x0F; 282 | printf("量化表ID: %d\n", id); 283 | for (int i = 0; i < 64; i++) { 284 | unsigned char t = 0; 285 | for (int p = 0; p < precision; p++) { 286 | unsigned char s; 287 | fread(&s, 1, 1, f); 288 | t == t << 8; 289 | t += s; 290 | } 291 | quantTable[id][i] = t; 292 | } 293 | for (int i = 0; i < 64; i++) { 294 | if (i % 8 == 0) { 295 | printf("\n"); 296 | } 297 | printf("%2d ", quantTable[id][i]); 298 | } 299 | printf("\n"); 300 | len -= (precision*64); 301 | } 302 | } 303 | void readSOF(FILE *f) { 304 | unsigned int len = EnterNewSection(f, "SOF"); 305 | fseek(f, 1, SEEK_CUR); // 精度 306 | unsigned char v[3]; 307 | fread(v, 1, 2, f); 308 | image.height = v[0] * 256 + v[1]; 309 | fread(v, 1, 2, f); 310 | image.width = v[0] * 256 + v[1]; 311 | printf("高*寬: %d*%d\n", image.height, image.width); 312 | fseek(f, 1, SEEK_CUR); // 顏色分量數,固定為3 313 | for (int i = 0; i < 3; i++) { 314 | fread(v, 1, 3, f); 315 | printf("顏色分量ID:%d\n", v[0]); 316 | printf("水平採樣因子:%d\n", v[1] >> 4); 317 | printf("垂直採樣因子:%d\n", v[1] & 0x0F); 318 | printf("量化表ID:%d\n", v[2]); 319 | subVector[v[0]].id = v[0]; 320 | subVector[v[0]].width = v[1] >> 4; 321 | subVector[v[0]].height = v[1] & 0x0F; 322 | subVector[v[0]].quant = v[2]; 323 | maxHeight = (maxHeight > subVector[v[0]].height ? maxHeight : subVector[v[0]].height); 324 | maxWidth = (maxWidth > subVector[v[0]].width ? maxWidth : subVector[v[0]].width); 325 | } 326 | } 327 | 328 | std::pair* createHuffCode(unsigned char *a, unsigned int number) { 329 | int si = sizeof(std::pair); 330 | auto ret = (std::pair*)malloc(si * number); 331 | int code = 0; 332 | int count = 0; 333 | for (int i = 0; i < 16; i++) { 334 | for (int j = 0; j < a[i]; j++) { 335 | ret[count++] = std::make_pair(i + 1, code); 336 | code += 1; 337 | } 338 | code = code << 1; 339 | } 340 | return ret; 341 | } 342 | void readDHT(FILE *f) { 343 | unsigned int len = EnterNewSection(f, "DHT"); 344 | len -= 2; 345 | while (len > 0) { 346 | unsigned char v[1]; 347 | fread(v, 1, 1, f); 348 | unsigned char DCorAC = v[0] >> 4; 349 | printf(DCorAC == 0 ? "DC\n" : "AC\n"); 350 | unsigned char id = v[0] & 0x0F; 351 | printf("ID: %d\n", id); 352 | 353 | unsigned char a[16]; 354 | fread(a, 1, 16, f); 355 | unsigned int number = 0; 356 | for (int i = 0; i < 16; i++) { 357 | printf("%d ", a[i]); 358 | number += a[i]; 359 | } 360 | printf("\n"); 361 | auto huffCode = createHuffCode(a, number); 362 | for (int i = 0; i < number; i++) { 363 | unsigned char v; 364 | fread(&v, 1, 1, f); 365 | huffTable[DCorAC][id][huffCode[i]] = v; 366 | printf("%d %d: %d\n", huffCode[i].first, huffCode[i].second, v); 367 | } 368 | free(huffCode); 369 | 370 | len -= (1 + 16 + number); 371 | } 372 | } 373 | void readSOS(FILE *f) { 374 | unsigned int len = EnterNewSection(f, "SOS"); 375 | 376 | fseek(f, 1, SEEK_CUR); // 顏色分量數,固定為3 377 | for (int i = 0; i < 3; i++) { 378 | unsigned char v[1]; 379 | fread(v, 1, 1, f); 380 | printf("顏色分量id:%d\n", v[0]); 381 | fread(v, 1, 1, f); 382 | printf("DC霍夫曼id:%d\n", v[0] >> 4); 383 | printf("AC霍夫曼id:%d\n", v[0] & 0x0F); 384 | } 385 | fseek(f, 3, SEEK_CUR); 386 | } 387 | 388 | // 必須連續呼叫getBit,中間被fread斷掉就會出問題 389 | bool getBit(FILE *f) { 390 | static unsigned char buf; 391 | static unsigned char count = 0; 392 | if (count == 0) { 393 | fread(&buf, 1, 1, f); 394 | if (buf == 0xFF) { 395 | unsigned char check; 396 | fread(&check, 1, 1, f); 397 | if (check != 0x00) { 398 | fprintf(stderr, "data 段有不是 0xFF00 的數據"); 399 | } 400 | } 401 | } 402 | bool ret = buf & (1 << (7 - count)); 403 | count = (count == 7 ? 0 : count + 1); 404 | return ret; 405 | } 406 | 407 | unsigned char matchHuff(FILE *f, unsigned char number, unsigned char ACorDC) { 408 | unsigned int len = 0; 409 | unsigned char codeLen; 410 | for (int count = 1; ; count++) { 411 | len = len << 1; 412 | len += (unsigned int)getBit(f); 413 | if (huffTable[ACorDC][number].find(std::make_pair(count, len)) != huffTable[ACorDC][number].end()) { 414 | codeLen = huffTable[ACorDC][number][std::make_pair(count, len)]; 415 | return codeLen; 416 | } 417 | if (count > 16) {fprintf(stderr, "key not found\n"); count = 1; len = 0;} 418 | } 419 | } 420 | 421 | int readDC(FILE *f, unsigned char number) { 422 | unsigned char codeLen = matchHuff(f, number, DC); 423 | if (codeLen == 0) { return 0; } 424 | unsigned char first = getBit(f); 425 | int ret = 1; 426 | for (int i = 1; i < codeLen; i++) { 427 | unsigned char b = getBit(f); 428 | ret = ret << 1; 429 | ret += first ? b : !b; 430 | } 431 | ret = first ? ret : -ret; 432 | // printf("read DC: len %d, value %d\n", codeLen, ret); 433 | return ret; 434 | } 435 | 436 | // 計算ZRL 437 | acCode readAC(FILE *f, unsigned char number) { 438 | unsigned char x = matchHuff(f, number, AC); 439 | unsigned char zeros = x >> 4; 440 | unsigned char codeLen = x & 0x0F; 441 | if (x == 0) { 442 | return acCode{0,0,0}; 443 | } else if (x == 0xF0) { 444 | return acCode{0, 16, 0}; 445 | } 446 | unsigned char first = getBit(f); 447 | int code = 1; 448 | for (int i = 1; i < codeLen; i++) { 449 | unsigned char b = getBit(f); 450 | code = code << 1; 451 | code += first ? b : !b; 452 | } 453 | code = first ? code : -code; 454 | // printf("read AC: %d %d %d\n", codeLen, zeros, code); 455 | return acCode{codeLen, zeros, code}; 456 | } 457 | 458 | MCU readMCU(FILE *f) { 459 | static int dc[4] = {0, 0, 0, 0}; 460 | auto mcu = MCU(); 461 | for (int i = 1; i <= 3; i++) { 462 | for (int h = 0; h < subVector[i].height; h++) { 463 | for (int w = 0; w < subVector[i].width; w++) { 464 | dc[i] = readDC(f, i/2) + dc[i]; 465 | mcu.mcu[i][h][w][0][0] = dc[i]; 466 | unsigned int count = 1; 467 | while (count < 64) { 468 | acCode ac = readAC(f, i/2); 469 | if (ac.len == 0 && ac.zeros == 16) { 470 | for (int j = 0; j < ac.zeros; j++) { 471 | mcu.mcu[i][h][w][count/8][count%8] = 0; 472 | count++; 473 | } 474 | } else if (ac.len == 0) { 475 | break; 476 | } else { 477 | for (int j = 0; j < ac.zeros; j++) { 478 | mcu.mcu[i][h][w][count/8][count%8] = 0; 479 | count++; 480 | } 481 | mcu.mcu[i][h][w][count/8][count%8] = ac.value; 482 | count++; 483 | } 484 | } 485 | while (count < 64) { 486 | mcu.mcu[i][h][w][count/8][count%8] = 0; 487 | count++; 488 | } 489 | } 490 | } 491 | } 492 | return mcu; 493 | } 494 | 495 | void readData(FILE *f) { 496 | printf("************************* test read data **********************************\n"); 497 | int w = (image.width - 1) / (8*maxWidth) + 1; 498 | int h = (image.height - 1) / (8*maxHeight) + 1; 499 | BMP *bmp = BMP_Create(maxWidth * 8 * w, maxHeight * 8 * h, 24); 500 | for (int i = 0; i < h; i++) { 501 | for (int j = 0; j < w; j++) { 502 | MCU mcu = readMCU(f); 503 | mcu.decode(); 504 | RGB **b = mcu.toRGB(); 505 | for (int y = i*8*maxHeight; y < (i+1)*8*maxHeight; y++) { 506 | for (int x = j*8*maxWidth; x < (j+1)*8*maxWidth; x++) { 507 | int by = y - i*8*maxHeight; 508 | int bx = x - j*8*maxWidth; 509 | BMP_SetPixelRGB(bmp, x, y, b[by][bx].R, b[by][bx].G, b[by][bx].B); 510 | } 511 | } 512 | } 513 | } 514 | BMP_WriteFile(bmp, "out.bmp"); 515 | } 516 | 517 | void readStream(FILE *f) { 518 | unsigned char c; 519 | fread(&c, 1, 1, f); 520 | while (c == 0xFF) { 521 | fread(&c, 1, 1, f); 522 | switch (c) { 523 | case SOI_MARKER: 524 | printf("Start of Image\n"); 525 | break; 526 | case APP0_MARKER: 527 | case 0xE1: 528 | case 0xE2: 529 | case 0xE3: 530 | case 0xE4: 531 | case 0xE5: 532 | case 0xE6: 533 | case 0xE7: 534 | case 0xE8: 535 | case 0xE9: 536 | case 0xEA: 537 | case 0xEB: 538 | case 0xEC: 539 | case 0xED: 540 | case 0xEE: 541 | case 0xEF: 542 | readAPP(f); 543 | break; 544 | case COM_MARKER: 545 | readCOM(f); 546 | break; 547 | case DQT_MARKER: 548 | readDQT(f); 549 | break; 550 | case SOF_MARKER: 551 | readSOF(f); 552 | break; 553 | case DHT_MARKER: 554 | readDHT(f); 555 | break; 556 | case SOS_MARKER: 557 | readSOS(f); 558 | readData(f); 559 | break; 560 | case EOI_MARKER: 561 | break; 562 | } 563 | fread(&c, 1, 1, f); 564 | } 565 | if (fread(&c, 1, 1, f) != 0) { 566 | fprintf(stderr, "沒有吃完就結束\n"); 567 | } 568 | } 569 | 570 | int main(int argc, char *argv[]) { 571 | if (argc != 2) { 572 | fprintf(stderr, "用法:jpeg_decoder \n"); 573 | return 1; 574 | } 575 | FILE *f = fopen(argv[1], "r"); 576 | if (f == NULL) { 577 | fprintf(stderr, "檔案開啟失敗\n"); 578 | } 579 | init_cos_cache(); 580 | readStream(f); 581 | return 0; 582 | } 583 | -------------------------------------------------------------------------------- /qdbmp.cpp: -------------------------------------------------------------------------------- 1 | #include "qdbmp.h" 2 | #include 3 | #include 4 | 5 | 6 | /* Bitmap header */ 7 | typedef struct _BMP_Header 8 | { 9 | USHORT Magic; /* Magic identifier: "BM" */ 10 | UINT FileSize; /* Size of the BMP file in bytes */ 11 | USHORT Reserved1; /* Reserved */ 12 | USHORT Reserved2; /* Reserved */ 13 | UINT DataOffset; /* Offset of image data relative to the file's start */ 14 | UINT HeaderSize; /* Size of the header in bytes */ 15 | UINT Width; /* Bitmap's width */ 16 | UINT Height; /* Bitmap's height */ 17 | USHORT Planes; /* Number of color planes in the bitmap */ 18 | USHORT BitsPerPixel; /* Number of bits per pixel */ 19 | UINT CompressionType; /* Compression type */ 20 | UINT ImageDataSize; /* Size of uncompressed image's data */ 21 | UINT HPixelsPerMeter; /* Horizontal resolution (pixels per meter) */ 22 | UINT VPixelsPerMeter; /* Vertical resolution (pixels per meter) */ 23 | UINT ColorsUsed; /* Number of color indexes in the color table that are actually used by the bitmap */ 24 | UINT ColorsRequired; /* Number of color indexes that are required for displaying the bitmap */ 25 | } BMP_Header; 26 | 27 | 28 | /* Private data structure */ 29 | struct _BMP 30 | { 31 | BMP_Header Header; 32 | UCHAR* Palette; 33 | UCHAR* Data; 34 | }; 35 | 36 | 37 | /* Holds the last error code */ 38 | static BMP_STATUS BMP_LAST_ERROR_CODE = BMP_OK; 39 | 40 | 41 | /* Error description strings */ 42 | static const char* BMP_ERROR_STRING[] = 43 | { 44 | "", 45 | "General error", 46 | "Could not allocate enough memory to complete the operation", 47 | "File input/output error", 48 | "File not found", 49 | "File is not a supported BMP variant (must be uncompressed 8, 24 or 32 BPP)", 50 | "File is not a valid BMP image", 51 | "An argument is invalid or out of range", 52 | "The requested action is not compatible with the BMP's type" 53 | }; 54 | 55 | 56 | /* Size of the palette data for 8 BPP bitmaps */ 57 | #define BMP_PALETTE_SIZE ( 256 * 4 ) 58 | 59 | 60 | 61 | /*********************************** Forward declarations **********************************/ 62 | int ReadHeader ( BMP* bmp, FILE* f ); 63 | int WriteHeader ( BMP* bmp, FILE* f ); 64 | 65 | int ReadUINT ( UINT* x, FILE* f ); 66 | int ReadUSHORT ( USHORT *x, FILE* f ); 67 | 68 | int WriteUINT ( UINT x, FILE* f ); 69 | int WriteUSHORT ( USHORT x, FILE* f ); 70 | 71 | 72 | 73 | 74 | 75 | 76 | /*********************************** Public methods **********************************/ 77 | 78 | 79 | /************************************************************** 80 | Creates a blank BMP image with the specified dimensions 81 | and bit depth. 82 | **************************************************************/ 83 | BMP* BMP_Create( UINT width, UINT height, USHORT depth ) 84 | { 85 | BMP* bmp; 86 | int bytes_per_pixel = depth >> 3; 87 | UINT bytes_per_row; 88 | 89 | if ( height <= 0 || width <= 0 ) 90 | { 91 | BMP_LAST_ERROR_CODE = BMP_INVALID_ARGUMENT; 92 | return NULL; 93 | } 94 | 95 | if ( depth != 8 && depth != 24 && depth != 32 ) 96 | { 97 | BMP_LAST_ERROR_CODE = BMP_FILE_NOT_SUPPORTED; 98 | return NULL; 99 | } 100 | 101 | 102 | /* Allocate the bitmap data structure */ 103 | bmp = (BMP*)calloc( 1, sizeof( BMP ) ); 104 | if ( bmp == NULL ) 105 | { 106 | BMP_LAST_ERROR_CODE = BMP_OUT_OF_MEMORY; 107 | return NULL; 108 | } 109 | 110 | 111 | /* Set header' default values */ 112 | bmp->Header.Magic = 0x4D42; 113 | bmp->Header.Reserved1 = 0; 114 | bmp->Header.Reserved2 = 0; 115 | bmp->Header.HeaderSize = 40; 116 | bmp->Header.Planes = 1; 117 | bmp->Header.CompressionType = 0; 118 | bmp->Header.HPixelsPerMeter = 0; 119 | bmp->Header.VPixelsPerMeter = 0; 120 | bmp->Header.ColorsUsed = 0; 121 | bmp->Header.ColorsRequired = 0; 122 | 123 | 124 | /* Calculate the number of bytes used to store a single image row. This is always 125 | rounded up to the next multiple of 4. */ 126 | bytes_per_row = width * bytes_per_pixel; 127 | bytes_per_row += ( bytes_per_row % 4 ? 4 - bytes_per_row % 4 : 0 ); 128 | 129 | 130 | /* Set header's image specific values */ 131 | bmp->Header.Width = width; 132 | bmp->Header.Height = height; 133 | bmp->Header.BitsPerPixel = depth; 134 | bmp->Header.ImageDataSize = bytes_per_row * height; 135 | bmp->Header.FileSize = bmp->Header.ImageDataSize + 54 + ( depth == 8 ? BMP_PALETTE_SIZE : 0 ); 136 | bmp->Header.DataOffset = 54 + ( depth == 8 ? BMP_PALETTE_SIZE : 0 ); 137 | 138 | 139 | /* Allocate palette */ 140 | if ( bmp->Header.BitsPerPixel == 8 ) 141 | { 142 | bmp->Palette = (UCHAR*) calloc( BMP_PALETTE_SIZE, sizeof( UCHAR ) ); 143 | if ( bmp->Palette == NULL ) 144 | { 145 | BMP_LAST_ERROR_CODE = BMP_OUT_OF_MEMORY; 146 | free( bmp ); 147 | return NULL; 148 | } 149 | } 150 | else 151 | { 152 | bmp->Palette = NULL; 153 | } 154 | 155 | 156 | /* Allocate pixels */ 157 | bmp->Data = (UCHAR*) calloc( bmp->Header.ImageDataSize, sizeof( UCHAR ) ); 158 | if ( bmp->Data == NULL ) 159 | { 160 | BMP_LAST_ERROR_CODE = BMP_OUT_OF_MEMORY; 161 | free( bmp->Palette ); 162 | free( bmp ); 163 | return NULL; 164 | } 165 | 166 | 167 | BMP_LAST_ERROR_CODE = BMP_OK; 168 | 169 | return bmp; 170 | } 171 | 172 | 173 | /************************************************************** 174 | Frees all the memory used by the specified BMP image. 175 | **************************************************************/ 176 | void BMP_Free( BMP* bmp ) 177 | { 178 | if ( bmp == NULL ) 179 | { 180 | return; 181 | } 182 | 183 | if ( bmp->Palette != NULL ) 184 | { 185 | free( bmp->Palette ); 186 | } 187 | 188 | if ( bmp->Data != NULL ) 189 | { 190 | free( bmp->Data ); 191 | } 192 | 193 | free( bmp ); 194 | 195 | BMP_LAST_ERROR_CODE = BMP_OK; 196 | } 197 | 198 | 199 | /************************************************************** 200 | Reads the specified BMP image file. 201 | **************************************************************/ 202 | BMP* BMP_ReadFile( const char* filename ) 203 | { 204 | BMP* bmp; 205 | FILE* f; 206 | 207 | if ( filename == NULL ) 208 | { 209 | BMP_LAST_ERROR_CODE = BMP_INVALID_ARGUMENT; 210 | return NULL; 211 | } 212 | 213 | 214 | /* Allocate */ 215 | bmp = (BMP*)calloc( 1, sizeof( BMP ) ); 216 | if ( bmp == NULL ) 217 | { 218 | BMP_LAST_ERROR_CODE = BMP_OUT_OF_MEMORY; 219 | return NULL; 220 | } 221 | 222 | 223 | /* Open file */ 224 | f = fopen( filename, "rb" ); 225 | if ( f == NULL ) 226 | { 227 | BMP_LAST_ERROR_CODE = BMP_FILE_NOT_FOUND; 228 | free( bmp ); 229 | return NULL; 230 | } 231 | 232 | 233 | /* Read header */ 234 | if ( ReadHeader( bmp, f ) != BMP_OK || bmp->Header.Magic != 0x4D42 ) 235 | { 236 | BMP_LAST_ERROR_CODE = BMP_FILE_INVALID; 237 | fclose( f ); 238 | free( bmp ); 239 | return NULL; 240 | } 241 | 242 | 243 | /* Verify that the bitmap variant is supported */ 244 | if ( ( bmp->Header.BitsPerPixel != 32 && bmp->Header.BitsPerPixel != 24 && bmp->Header.BitsPerPixel != 8 ) 245 | || bmp->Header.CompressionType != 0 || bmp->Header.HeaderSize != 40 ) 246 | { 247 | BMP_LAST_ERROR_CODE = BMP_FILE_NOT_SUPPORTED; 248 | fclose( f ); 249 | free( bmp ); 250 | return NULL; 251 | } 252 | 253 | 254 | /* Allocate and read palette */ 255 | if ( bmp->Header.BitsPerPixel == 8 ) 256 | { 257 | bmp->Palette = (UCHAR*) malloc( BMP_PALETTE_SIZE * sizeof( UCHAR ) ); 258 | if ( bmp->Palette == NULL ) 259 | { 260 | BMP_LAST_ERROR_CODE = BMP_OUT_OF_MEMORY; 261 | fclose( f ); 262 | free( bmp ); 263 | return NULL; 264 | } 265 | 266 | if ( fread( bmp->Palette, sizeof( UCHAR ), BMP_PALETTE_SIZE, f ) != BMP_PALETTE_SIZE ) 267 | { 268 | BMP_LAST_ERROR_CODE = BMP_FILE_INVALID; 269 | fclose( f ); 270 | free( bmp->Palette ); 271 | free( bmp ); 272 | return NULL; 273 | } 274 | } 275 | else /* Not an indexed image */ 276 | { 277 | bmp->Palette = NULL; 278 | } 279 | 280 | 281 | /* Allocate memory for image data */ 282 | bmp->Data = (UCHAR*) malloc( bmp->Header.ImageDataSize ); 283 | if ( bmp->Data == NULL ) 284 | { 285 | BMP_LAST_ERROR_CODE = BMP_OUT_OF_MEMORY; 286 | fclose( f ); 287 | free( bmp->Palette ); 288 | free( bmp ); 289 | return NULL; 290 | } 291 | 292 | 293 | /* Read image data */ 294 | if ( fread( bmp->Data, sizeof( UCHAR ), bmp->Header.ImageDataSize, f ) != bmp->Header.ImageDataSize ) 295 | { 296 | BMP_LAST_ERROR_CODE = BMP_FILE_INVALID; 297 | fclose( f ); 298 | free( bmp->Data ); 299 | free( bmp->Palette ); 300 | free( bmp ); 301 | return NULL; 302 | } 303 | 304 | 305 | fclose( f ); 306 | 307 | BMP_LAST_ERROR_CODE = BMP_OK; 308 | 309 | return bmp; 310 | } 311 | 312 | 313 | /************************************************************** 314 | Writes the BMP image to the specified file. 315 | **************************************************************/ 316 | void BMP_WriteFile( BMP* bmp, const char* filename ) 317 | { 318 | FILE* f; 319 | 320 | if ( filename == NULL ) 321 | { 322 | BMP_LAST_ERROR_CODE = BMP_INVALID_ARGUMENT; 323 | return; 324 | } 325 | 326 | 327 | /* Open file */ 328 | f = fopen( filename, "wb" ); 329 | if ( f == NULL ) 330 | { 331 | BMP_LAST_ERROR_CODE = BMP_FILE_NOT_FOUND; 332 | return; 333 | } 334 | 335 | 336 | /* Write header */ 337 | if ( WriteHeader( bmp, f ) != BMP_OK ) 338 | { 339 | BMP_LAST_ERROR_CODE = BMP_IO_ERROR; 340 | fclose( f ); 341 | return; 342 | } 343 | 344 | 345 | /* Write palette */ 346 | if ( bmp->Palette ) 347 | { 348 | if ( fwrite( bmp->Palette, sizeof( UCHAR ), BMP_PALETTE_SIZE, f ) != BMP_PALETTE_SIZE ) 349 | { 350 | BMP_LAST_ERROR_CODE = BMP_IO_ERROR; 351 | fclose( f ); 352 | return; 353 | } 354 | } 355 | 356 | 357 | /* Write data */ 358 | if ( fwrite( bmp->Data, sizeof( UCHAR ), bmp->Header.ImageDataSize, f ) != bmp->Header.ImageDataSize ) 359 | { 360 | BMP_LAST_ERROR_CODE = BMP_IO_ERROR; 361 | fclose( f ); 362 | return; 363 | } 364 | 365 | 366 | BMP_LAST_ERROR_CODE = BMP_OK; 367 | fclose( f ); 368 | } 369 | 370 | 371 | /************************************************************** 372 | Returns the image's width. 373 | **************************************************************/ 374 | UINT BMP_GetWidth( BMP* bmp ) 375 | { 376 | if ( bmp == NULL ) 377 | { 378 | BMP_LAST_ERROR_CODE = BMP_INVALID_ARGUMENT; 379 | return -1; 380 | } 381 | 382 | BMP_LAST_ERROR_CODE = BMP_OK; 383 | 384 | return ( bmp->Header.Width ); 385 | } 386 | 387 | 388 | /************************************************************** 389 | Returns the image's height. 390 | **************************************************************/ 391 | UINT BMP_GetHeight( BMP* bmp ) 392 | { 393 | if ( bmp == NULL ) 394 | { 395 | BMP_LAST_ERROR_CODE = BMP_INVALID_ARGUMENT; 396 | return -1; 397 | } 398 | 399 | BMP_LAST_ERROR_CODE = BMP_OK; 400 | 401 | return ( bmp->Header.Height ); 402 | } 403 | 404 | 405 | /************************************************************** 406 | Returns the image's color depth (bits per pixel). 407 | **************************************************************/ 408 | USHORT BMP_GetDepth( BMP* bmp ) 409 | { 410 | if ( bmp == NULL ) 411 | { 412 | BMP_LAST_ERROR_CODE = BMP_INVALID_ARGUMENT; 413 | return -1; 414 | } 415 | 416 | BMP_LAST_ERROR_CODE = BMP_OK; 417 | 418 | return ( bmp->Header.BitsPerPixel ); 419 | } 420 | 421 | 422 | /************************************************************** 423 | Populates the arguments with the specified pixel's RGB 424 | values. 425 | **************************************************************/ 426 | void BMP_GetPixelRGB( BMP* bmp, UINT x, UINT y, UCHAR* r, UCHAR* g, UCHAR* b ) 427 | { 428 | UCHAR* pixel; 429 | UINT bytes_per_row; 430 | UCHAR bytes_per_pixel; 431 | 432 | if ( bmp == NULL || x < 0 || x >= bmp->Header.Width || y < 0 || y >= bmp->Header.Height ) 433 | { 434 | BMP_LAST_ERROR_CODE = BMP_INVALID_ARGUMENT; 435 | } 436 | else 437 | { 438 | BMP_LAST_ERROR_CODE = BMP_OK; 439 | 440 | bytes_per_pixel = bmp->Header.BitsPerPixel >> 3; 441 | 442 | /* Row's size is rounded up to the next multiple of 4 bytes */ 443 | bytes_per_row = bmp->Header.ImageDataSize / bmp->Header.Height; 444 | 445 | /* Calculate the location of the relevant pixel (rows are flipped) */ 446 | pixel = bmp->Data + ( ( bmp->Header.Height - y - 1 ) * bytes_per_row + x * bytes_per_pixel ); 447 | 448 | 449 | /* In indexed color mode the pixel's value is an index within the palette */ 450 | if ( bmp->Header.BitsPerPixel == 8 ) 451 | { 452 | pixel = bmp->Palette + *pixel * 4; 453 | } 454 | 455 | /* Note: colors are stored in BGR order */ 456 | if ( r ) *r = *( pixel + 2 ); 457 | if ( g ) *g = *( pixel + 1 ); 458 | if ( b ) *b = *( pixel + 0 ); 459 | } 460 | } 461 | 462 | 463 | /************************************************************** 464 | Sets the specified pixel's RGB values. 465 | **************************************************************/ 466 | void BMP_SetPixelRGB( BMP* bmp, UINT x, UINT y, UCHAR r, UCHAR g, UCHAR b ) 467 | { 468 | UCHAR* pixel; 469 | UINT bytes_per_row; 470 | UCHAR bytes_per_pixel; 471 | 472 | if ( bmp == NULL || x < 0 || x >= bmp->Header.Width || y < 0 || y >= bmp->Header.Height ) 473 | { 474 | BMP_LAST_ERROR_CODE = BMP_INVALID_ARGUMENT; 475 | } 476 | 477 | else if ( bmp->Header.BitsPerPixel != 24 && bmp->Header.BitsPerPixel != 32 ) 478 | { 479 | BMP_LAST_ERROR_CODE = BMP_TYPE_MISMATCH; 480 | } 481 | 482 | else 483 | { 484 | BMP_LAST_ERROR_CODE = BMP_OK; 485 | 486 | bytes_per_pixel = bmp->Header.BitsPerPixel >> 3; 487 | 488 | /* Row's size is rounded up to the next multiple of 4 bytes */ 489 | bytes_per_row = bmp->Header.ImageDataSize / bmp->Header.Height; 490 | 491 | /* Calculate the location of the relevant pixel (rows are flipped) */ 492 | pixel = bmp->Data + ( ( bmp->Header.Height - y - 1 ) * bytes_per_row + x * bytes_per_pixel ); 493 | 494 | /* Note: colors are stored in BGR order */ 495 | *( pixel + 2 ) = r; 496 | *( pixel + 1 ) = g; 497 | *( pixel + 0 ) = b; 498 | } 499 | } 500 | 501 | 502 | /************************************************************** 503 | Gets the specified pixel's color index. 504 | **************************************************************/ 505 | void BMP_GetPixelIndex( BMP* bmp, UINT x, UINT y, UCHAR* val ) 506 | { 507 | UCHAR* pixel; 508 | UINT bytes_per_row; 509 | 510 | if ( bmp == NULL || x < 0 || x >= bmp->Header.Width || y < 0 || y >= bmp->Header.Height ) 511 | { 512 | BMP_LAST_ERROR_CODE = BMP_INVALID_ARGUMENT; 513 | } 514 | 515 | else if ( bmp->Header.BitsPerPixel != 8 ) 516 | { 517 | BMP_LAST_ERROR_CODE = BMP_TYPE_MISMATCH; 518 | } 519 | 520 | else 521 | { 522 | BMP_LAST_ERROR_CODE = BMP_OK; 523 | 524 | /* Row's size is rounded up to the next multiple of 4 bytes */ 525 | bytes_per_row = bmp->Header.ImageDataSize / bmp->Header.Height; 526 | 527 | /* Calculate the location of the relevant pixel */ 528 | pixel = bmp->Data + ( ( bmp->Header.Height - y - 1 ) * bytes_per_row + x ); 529 | 530 | 531 | if ( val ) *val = *pixel; 532 | } 533 | } 534 | 535 | 536 | /************************************************************** 537 | Sets the specified pixel's color index. 538 | **************************************************************/ 539 | void BMP_SetPixelIndex( BMP* bmp, UINT x, UINT y, UCHAR val ) 540 | { 541 | UCHAR* pixel; 542 | UINT bytes_per_row; 543 | 544 | if ( bmp == NULL || x < 0 || x >= bmp->Header.Width || y < 0 || y >= bmp->Header.Height ) 545 | { 546 | BMP_LAST_ERROR_CODE = BMP_INVALID_ARGUMENT; 547 | } 548 | 549 | else if ( bmp->Header.BitsPerPixel != 8 ) 550 | { 551 | BMP_LAST_ERROR_CODE = BMP_TYPE_MISMATCH; 552 | } 553 | 554 | else 555 | { 556 | BMP_LAST_ERROR_CODE = BMP_OK; 557 | 558 | /* Row's size is rounded up to the next multiple of 4 bytes */ 559 | bytes_per_row = bmp->Header.ImageDataSize / bmp->Header.Height; 560 | 561 | /* Calculate the location of the relevant pixel */ 562 | pixel = bmp->Data + ( ( bmp->Header.Height - y - 1 ) * bytes_per_row + x ); 563 | 564 | *pixel = val; 565 | } 566 | } 567 | 568 | 569 | /************************************************************** 570 | Gets the color value for the specified palette index. 571 | **************************************************************/ 572 | void BMP_GetPaletteColor( BMP* bmp, UCHAR index, UCHAR* r, UCHAR* g, UCHAR* b ) 573 | { 574 | if ( bmp == NULL ) 575 | { 576 | BMP_LAST_ERROR_CODE = BMP_INVALID_ARGUMENT; 577 | } 578 | 579 | else if ( bmp->Header.BitsPerPixel != 8 ) 580 | { 581 | BMP_LAST_ERROR_CODE = BMP_TYPE_MISMATCH; 582 | } 583 | 584 | else 585 | { 586 | if ( r ) *r = *( bmp->Palette + index * 4 + 2 ); 587 | if ( g ) *g = *( bmp->Palette + index * 4 + 1 ); 588 | if ( b ) *b = *( bmp->Palette + index * 4 + 0 ); 589 | 590 | BMP_LAST_ERROR_CODE = BMP_OK; 591 | } 592 | } 593 | 594 | 595 | /************************************************************** 596 | Sets the color value for the specified palette index. 597 | **************************************************************/ 598 | void BMP_SetPaletteColor( BMP* bmp, UCHAR index, UCHAR r, UCHAR g, UCHAR b ) 599 | { 600 | if ( bmp == NULL ) 601 | { 602 | BMP_LAST_ERROR_CODE = BMP_INVALID_ARGUMENT; 603 | } 604 | 605 | else if ( bmp->Header.BitsPerPixel != 8 ) 606 | { 607 | BMP_LAST_ERROR_CODE = BMP_TYPE_MISMATCH; 608 | } 609 | 610 | else 611 | { 612 | *( bmp->Palette + index * 4 + 2 ) = r; 613 | *( bmp->Palette + index * 4 + 1 ) = g; 614 | *( bmp->Palette + index * 4 + 0 ) = b; 615 | 616 | BMP_LAST_ERROR_CODE = BMP_OK; 617 | } 618 | } 619 | 620 | 621 | /************************************************************** 622 | Returns the last error code. 623 | **************************************************************/ 624 | BMP_STATUS BMP_GetError() 625 | { 626 | return BMP_LAST_ERROR_CODE; 627 | } 628 | 629 | 630 | /************************************************************** 631 | Returns a description of the last error code. 632 | **************************************************************/ 633 | const char* BMP_GetErrorDescription() 634 | { 635 | if ( BMP_LAST_ERROR_CODE > 0 && BMP_LAST_ERROR_CODE < BMP_ERROR_NUM ) 636 | { 637 | return BMP_ERROR_STRING[ BMP_LAST_ERROR_CODE ]; 638 | } 639 | else 640 | { 641 | return NULL; 642 | } 643 | } 644 | 645 | 646 | 647 | 648 | 649 | /*********************************** Private methods **********************************/ 650 | 651 | 652 | /************************************************************** 653 | Reads the BMP file's header into the data structure. 654 | Returns BMP_OK on success. 655 | **************************************************************/ 656 | int ReadHeader( BMP* bmp, FILE* f ) 657 | { 658 | if ( bmp == NULL || f == NULL ) 659 | { 660 | return BMP_INVALID_ARGUMENT; 661 | } 662 | 663 | /* The header's fields are read one by one, and converted from the format's 664 | little endian to the system's native representation. */ 665 | if ( !ReadUSHORT( &( bmp->Header.Magic ), f ) ) return BMP_IO_ERROR; 666 | if ( !ReadUINT( &( bmp->Header.FileSize ), f ) ) return BMP_IO_ERROR; 667 | if ( !ReadUSHORT( &( bmp->Header.Reserved1 ), f ) ) return BMP_IO_ERROR; 668 | if ( !ReadUSHORT( &( bmp->Header.Reserved2 ), f ) ) return BMP_IO_ERROR; 669 | if ( !ReadUINT( &( bmp->Header.DataOffset ), f ) ) return BMP_IO_ERROR; 670 | if ( !ReadUINT( &( bmp->Header.HeaderSize ), f ) ) return BMP_IO_ERROR; 671 | if ( !ReadUINT( &( bmp->Header.Width ), f ) ) return BMP_IO_ERROR; 672 | if ( !ReadUINT( &( bmp->Header.Height ), f ) ) return BMP_IO_ERROR; 673 | if ( !ReadUSHORT( &( bmp->Header.Planes ), f ) ) return BMP_IO_ERROR; 674 | if ( !ReadUSHORT( &( bmp->Header.BitsPerPixel ), f ) ) return BMP_IO_ERROR; 675 | if ( !ReadUINT( &( bmp->Header.CompressionType ), f ) ) return BMP_IO_ERROR; 676 | if ( !ReadUINT( &( bmp->Header.ImageDataSize ), f ) ) return BMP_IO_ERROR; 677 | if ( !ReadUINT( &( bmp->Header.HPixelsPerMeter ), f ) ) return BMP_IO_ERROR; 678 | if ( !ReadUINT( &( bmp->Header.VPixelsPerMeter ), f ) ) return BMP_IO_ERROR; 679 | if ( !ReadUINT( &( bmp->Header.ColorsUsed ), f ) ) return BMP_IO_ERROR; 680 | if ( !ReadUINT( &( bmp->Header.ColorsRequired ), f ) ) return BMP_IO_ERROR; 681 | 682 | return BMP_OK; 683 | } 684 | 685 | 686 | /************************************************************** 687 | Writes the BMP file's header into the data structure. 688 | Returns BMP_OK on success. 689 | **************************************************************/ 690 | int WriteHeader( BMP* bmp, FILE* f ) 691 | { 692 | if ( bmp == NULL || f == NULL ) 693 | { 694 | return BMP_INVALID_ARGUMENT; 695 | } 696 | 697 | /* The header's fields are written one by one, and converted to the format's 698 | little endian representation. */ 699 | if ( !WriteUSHORT( bmp->Header.Magic, f ) ) return BMP_IO_ERROR; 700 | if ( !WriteUINT( bmp->Header.FileSize, f ) ) return BMP_IO_ERROR; 701 | if ( !WriteUSHORT( bmp->Header.Reserved1, f ) ) return BMP_IO_ERROR; 702 | if ( !WriteUSHORT( bmp->Header.Reserved2, f ) ) return BMP_IO_ERROR; 703 | if ( !WriteUINT( bmp->Header.DataOffset, f ) ) return BMP_IO_ERROR; 704 | if ( !WriteUINT( bmp->Header.HeaderSize, f ) ) return BMP_IO_ERROR; 705 | if ( !WriteUINT( bmp->Header.Width, f ) ) return BMP_IO_ERROR; 706 | if ( !WriteUINT( bmp->Header.Height, f ) ) return BMP_IO_ERROR; 707 | if ( !WriteUSHORT( bmp->Header.Planes, f ) ) return BMP_IO_ERROR; 708 | if ( !WriteUSHORT( bmp->Header.BitsPerPixel, f ) ) return BMP_IO_ERROR; 709 | if ( !WriteUINT( bmp->Header.CompressionType, f ) ) return BMP_IO_ERROR; 710 | if ( !WriteUINT( bmp->Header.ImageDataSize, f ) ) return BMP_IO_ERROR; 711 | if ( !WriteUINT( bmp->Header.HPixelsPerMeter, f ) ) return BMP_IO_ERROR; 712 | if ( !WriteUINT( bmp->Header.VPixelsPerMeter, f ) ) return BMP_IO_ERROR; 713 | if ( !WriteUINT( bmp->Header.ColorsUsed, f ) ) return BMP_IO_ERROR; 714 | if ( !WriteUINT( bmp->Header.ColorsRequired, f ) ) return BMP_IO_ERROR; 715 | 716 | return BMP_OK; 717 | } 718 | 719 | 720 | /************************************************************** 721 | Reads a little-endian unsigned int from the file. 722 | Returns non-zero on success. 723 | **************************************************************/ 724 | int ReadUINT( UINT* x, FILE* f ) 725 | { 726 | UCHAR little[ 4 ]; /* BMPs use 32 bit ints */ 727 | 728 | if ( x == NULL || f == NULL ) 729 | { 730 | return 0; 731 | } 732 | 733 | if ( fread( little, 4, 1, f ) != 1 ) 734 | { 735 | return 0; 736 | } 737 | 738 | *x = ( little[ 3 ] << 24 | little[ 2 ] << 16 | little[ 1 ] << 8 | little[ 0 ] ); 739 | 740 | return 1; 741 | } 742 | 743 | 744 | /************************************************************** 745 | Reads a little-endian unsigned short int from the file. 746 | Returns non-zero on success. 747 | **************************************************************/ 748 | int ReadUSHORT( USHORT *x, FILE* f ) 749 | { 750 | UCHAR little[ 2 ]; /* BMPs use 16 bit shorts */ 751 | 752 | if ( x == NULL || f == NULL ) 753 | { 754 | return 0; 755 | } 756 | 757 | if ( fread( little, 2, 1, f ) != 1 ) 758 | { 759 | return 0; 760 | } 761 | 762 | *x = ( little[ 1 ] << 8 | little[ 0 ] ); 763 | 764 | return 1; 765 | } 766 | 767 | 768 | /************************************************************** 769 | Writes a little-endian unsigned int to the file. 770 | Returns non-zero on success. 771 | **************************************************************/ 772 | int WriteUINT( UINT x, FILE* f ) 773 | { 774 | UCHAR little[ 4 ]; /* BMPs use 32 bit ints */ 775 | 776 | little[ 3 ] = (UCHAR)( ( x & 0xff000000 ) >> 24 ); 777 | little[ 2 ] = (UCHAR)( ( x & 0x00ff0000 ) >> 16 ); 778 | little[ 1 ] = (UCHAR)( ( x & 0x0000ff00 ) >> 8 ); 779 | little[ 0 ] = (UCHAR)( ( x & 0x000000ff ) >> 0 ); 780 | 781 | return ( f && fwrite( little, 4, 1, f ) == 1 ); 782 | } 783 | 784 | 785 | /************************************************************** 786 | Writes a little-endian unsigned short int to the file. 787 | Returns non-zero on success. 788 | **************************************************************/ 789 | int WriteUSHORT( USHORT x, FILE* f ) 790 | { 791 | UCHAR little[ 2 ]; /* BMPs use 16 bit shorts */ 792 | 793 | little[ 1 ] = (UCHAR)( ( x & 0xff00 ) >> 8 ); 794 | little[ 0 ] = (UCHAR)( ( x & 0x00ff ) >> 0 ); 795 | 796 | return ( f && fwrite( little, 2, 1, f ) == 1 ); 797 | } 798 | 799 | --------------------------------------------------------------------------------