JPEG解码实现
这几天接了个任务,把JPEG转为BMP。我在网上搜了很多代码,最后决定自己来写,写到后面已经变成大段借用其他人的代码了。
下面来说说这里面的几个难点,文后会附上代码。
1 IDCT, 反离散余弦变换,这个找了很多现成的代码,最后使用了stb_image的代码拷过来改了下。
2 resample,网上流行很广的文章里一笔带过,据那篇文章弄出来发现锯齿严重,和原图有差别,最后拷stb_image的代码来改的。
3 图片转为RGB和色调有点差别,后来交换了下Cr,Cb就正常了,我也不知道咋搞的。
我觉得这点代码还是挺有意思的,huffman, RLE, 字节流的处理等等.
另贴一个参考的文章 http://www.cnblogs.com/leaven/archive/2010/04/06/1705846.html
不可以用附件九直接写下面了。
#include "stdio.h" #include "string.h" #include "assert.h" #include "stdlib.h" #define jpeg_printf #define getbit(k, i) (((k)>>(i))&1) #define high(k) ((k)>>4) #define low(k) ((k)&0xf) #ifndef _DEBUG #undef assert #define assert(x) if (!(x)){ return 0; } #endif typedef unsigned char uint8; typedef struct color_t { uint8 y; uint8 cr; uint8 cb; }color; typedef struct _huffman_node { struct _huffman_node* l; struct _huffman_node* r; int w; }huffman_node; static huffman_node* huffman_node_create() { huffman_node* p; p = (huffman_node*)malloc(sizeof(huffman_node)); p->l = 0; p->r = 0; p->w = -1; return p; } static void huffman_node_visit(huffman_node* p, int c, int l) { int i; if (p == 0) { return; } if (p->l == 0 && p->r == 0) { for (i = l - 1; i >= 0; i--) { if (getbit(c, i)) { printf("1"); } else { printf("0"); } } printf(" 0x%02x ", p->w); } else { huffman_node_visit(p->l, c<<1, l + 1); huffman_node_visit(p->r, (c<<1) + 1, l + 1); } } static void huffman_node_release(huffman_node* node) { if (node != 0) { huffman_node_release(node->l); huffman_node_release(node->r); free(node); } } typedef struct _huffman { huffman_node* r; huffman_node* p; int l; int c; }huffman; #define huffman_reset(h) h->p = h->r static huffman* huffman_create() { huffman* h; h = (huffman*)malloc(sizeof(huffman)); h->r = huffman_node_create(); h->l = -1; return h; } static int huffman_match(huffman* h, int x) { if (x) { h->p = h->p->r; } else { h->p = h->p->l; } if (h->p == 0) { return -1; } return h->p->l == 0 && h->p->r == 0; } static void huffman_insert(huffman* h, int l, int v) { int i; huffman_node* p; if (h->l == -1) { h->c = 0; h->l = l; } else { h->c = (h->c + 1)<<(l - h->l); h->l = l; } p = h->r; for (i = l - 1; i >= 0; i--) { if (getbit(h->c, i)) { if (p->r == 0) { p->r = huffman_node_create(); } p = p->r; } else { if (p->l == 0) { p->l = huffman_node_create(); } p = p->l; } } p->w = v; } static void huffman_print(huffman* h) { huffman_node_visit(h->r, 0, 0); } static void huffman_release(huffman* h) { if (h != 0) { huffman_node_release(h->r); free(h); } } typedef struct _color_comp { int qi; int di; int ai; int df; }color_comp; typedef struct _jpeg { int width; int height; uint8 qt[4][64]; huffman* dh[4]; huffman* ah[4]; color_comp cc[4]; color* pixels; uint8* data; int size; int ei; int di; int rr; }jpeg; static int jpeg_decode_huffman(jpeg* jpg, uint8* p) { huffman* h; uint8* q; int i, j, l, k, f, d; int t[16]; q = jpg->data + jpg->size; assert(p + 2 <= q); l = (p[0]<<8) + p[1]; assert(p + l <= q); for (k = 2; k < l;) { d = low(p[k]); f = high(p[k]); assert(d <= 3 && f <= 1); if (f) { h = jpg->ah[d]; } else { h = jpg->dh[d]; } k++; assert(p + k + 16 <= q && k + 16 <= l); for (i = 0; i < 16; i++) { t[i] = p[k++]; } for (i = 0; i < 16; i++) { assert(p + k + t[i] <= q && k + t[i] <= l); for (j = 0; j < t[i]; j++) { huffman_insert(h, i + 1, p[k++]); } } } return 1; } static int jpeg_prepare(jpeg* jpg) { int i, j, l, p, id, qi, tp, s; uint8* data; data = jpg->data; if (data[0] != 0xff || data[1] != 0xd8) { return 0; } s = jpg->size; for (i = 2; i < s;) { if (data[i++] == 0xff) { assert(i + 3 <= s); switch (data[i++]) { case 0xdb: { l = (data[i]<<8) + data[i + 1]; assert(i + l <= s); for (p = i + 2; p < i + l;) { id = low(data[p]); assert(id <= 3 && high(data[p]) == 0); p++; assert(p + 64 <= s && p + 64 <= i + l); for (j = 0; j < 64; j++) { jpg->qt[id][j] = data[p++]; } } } break; case 0xc0: { l = (data[i]<<8) + data[i + 1]; assert(l == 17 && i + 17 <= s && data[i + 2] == 8); p = i + 3; jpg->height = (data[p]<<8) + data[p + 1]; p += 2; jpg->width = (data[p]<<8) + data[p + 1]; p += 2; assert(low(jpg->height) == 0 && low(jpg->width) == 0 && data[p] == 3); p++; id = data[p]; qi = data[p + 2]; assert(id == 1 && data[p + 1] == 0x22 && qi <= 3); jpg->cc[id].qi = qi; p += 3; id = data[p]; qi = data[p + 2]; assert(id == 2 && data[p + 1] == 0x11 && qi <= 3); jpg->cc[id].qi = qi; p += 3; id = data[p]; qi = data[p + 2]; assert(id == 3 && data[p + 1] == 0x11 && qi <= 3); jpg->cc[id].qi = qi; } break; case 0xc4: if (!jpeg_decode_huffman(jpg, data + i)) { return 0; } break; case 0xdd: { assert(0); } case 0xda: { l = (data[i]<<8) + data[i + 1]; assert(l == 12 && i + l <= s); p = i + 2; assert(data[p] == 3); p++; id = data[p]; tp = data[p + 1]; assert(id == 1 && low(tp) <= 3 &&high(tp) <= 3); jpg->cc[id].ai = low(tp); jpg->cc[id].di = high(tp); p += 2; id = data[p]; tp = data[p + 1]; assert(id == 2 && low(tp) <= 3 &&high(tp) <= 3); jpg->cc[id].ai = low(tp); jpg->cc[id].di = high(tp); p += 2; id = data[p]; tp = data[p + 1]; assert(id == 3 && low(tp) <= 3 &&high(tp) <= 3); jpg->cc[id].ai = low(tp); jpg->cc[id].di = high(tp); jpg->ei = i + l; return 1; } break; default: break; } i += (data[i]<<8) + data[i + 1]; } } return 0; } void* jpeg_load(const char* file) { FILE* fd; jpeg* jpg; int i; jpg = (jpeg*)malloc(sizeof(jpeg)); memset(jpg, 0, sizeof(jpeg)); for (i = 0; i < 4; i++) { jpg->dh[i] = huffman_create(); jpg->ah[i] = huffman_create(); } fd = fopen(file, "rb"); if (fd == 0) { jpeg_free(jpg); return 0; } //文件读写 fseek(fd, 0 ,SEEK_END); jpg->size = ftell(fd); jpg->data = (uint8*)malloc(jpg->size * sizeof(uint8)); fseek(fd, 0, SEEK_SET); fread(jpg->data, jpg->size, 1, fd); fclose(fd); if (!jpeg_prepare(jpg)) { jpeg_free(jpg); return 0; } return jpg; } void jpeg_free(void* ctx) { int i; jpeg* jpg = (jpeg*)ctx; if (jpg->pixels != 0) { free(jpg->pixels); } if (jpg->data != 0) { free(jpg->data); } for (i = 0; i < 4; i++) { huffman_release(jpg->dh[i]); huffman_release(jpg->ah[i]); } free(jpg); } int jpeg_query(void* ctx, int* width, int* height) { jpeg* jpg = (jpeg*)ctx; *width = jpg->width; *height = jpg->height; return 1; } //-1 结束 //-2 错误 static int jpeg_nextbyte(jpeg* jpg) { int x; if (jpg->ei >= jpg->size) { return -1; } x = jpg->data[jpg->ei]; if (x != 0xff) { jpg->ei++; return x; } else { jpg->ei++; if (jpg->ei >= jpg->size) { return -1; } x = jpg->data[jpg->ei]; if (x == 0) { jpg->ei++; return 0xff; } else if (x == 0xff) { return jpeg_nextbyte(jpg); } else if (x < 0xd0 || x > 0xd9) { jpg->ei++; return x; } else { return -2; } } return -2; } static int jpeg_nextbit(jpeg* jpg) { int x; if (jpg->di == 0 && (jpg->rr = jpeg_nextbyte(jpg)) < 0) { return -1; } x = getbit(jpg->rr, 7 - jpg->di); jpg->di = (jpg->di + 1) % 8; return x; } static int jpeg_decode_byte(jpeg* jpg, huffman* h) { int x, m; huffman_reset(h); while ((x = jpeg_nextbit(jpg)) >= 0) { m = huffman_match(h, x); if (m == -1) { return -1; } else if (m) { jpeg_printf("x%x ", h->p->w); return h->p->w; } } return -1; } static int jpeg_read(jpeg* jpg, int len, int* x) { int i, v, s, b; if ((s = jpeg_nextbit(jpg)) < 0) { return 0; } v = 0; for (i = 1; i < len; i++) { if ((b = jpeg_nextbit(jpg)) < 0) { return 0; } v = (v<<1) + b; } if (s == 0) { *x = -(1<<len) + 1 + v; } else { *x = (1<<(len - 1)) + v; } jpeg_printf("r%d ", *x); return 1; } //region copy from stb_image.cpp #pragma region idct uint8 clamp(int x) { if ((unsigned int) x > 255) { if (x < 0) return 0; if (x > 255) return 255; } return (uint8) x; } #define f2f(x) (int) (((x) * 4096 + 0.5)) #define fsh(x) ((x) << 12) #define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; p2 = s2; p3 = s6; p1 = (p2+p3) * f2f(0.5411961f); t2 = p1 + p3*f2f(-1.847759065f); t3 = p1 + p2*f2f( 0.765366865f); p2 = s0; p3 = s4; t0 = fsh(p2+p3); t1 = fsh(p2-p3); x0 = t0+t3; x3 = t0-t3; x1 = t1+t2; x2 = t1-t2; t0 = s7; t1 = s5; t2 = s3; t3 = s1; p3 = t0+t2; p4 = t1+t3; p1 = t0+t3; p2 = t1+t2; p5 = (p3+p4)*f2f( 1.175875602f); t0 = t0*f2f( 0.298631336f); t1 = t1*f2f( 2.053119869f); t2 = t2*f2f( 3.072711026f); t3 = t3*f2f( 1.501321110f); p1 = p5 + p1*f2f(-0.899976223f); p2 = p5 + p2*f2f(-2.562915447f); p3 = p3*f2f(-1.961570560f); p4 = p4*f2f(-0.390180644f); t3 += p1+p4; t2 += p2+p3; t1 += p2+p4; t0 += p1+p3; static void idct_block(uint8 *out, int data[64]) { int i,val[64],*v=val; uint8 *o; int *d = data; for (i=0; i < 8; ++i,++d, ++v) { if (d[8]==0 && d[16]==0 && d[24]==0 && d[32]==0 && d[40]==0 && d[48]==0 && d[56]==0) { int dcterm = d[0]<< 2; v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; } else { IDCT_1D(d[0], d[8], d[16], d[24], d[32], d[40], d[48], d[56]) x0 += 512; x1 += 512; x2 += 512; x3 += 512; v[ 0] = (x0+t3) >> 10; v[56] = (x0-t3) >> 10; v[ 8] = (x1+t2) >> 10; v[48] = (x1-t2) >> 10; v[16] = (x2+t1) >> 10; v[40] = (x2-t1) >> 10; v[24] = (x3+t0) >> 10; v[32] = (x3-t0) >> 10; } } for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=8) { IDCT_1D(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]) x0 += 65536 + (128<<17); x1 += 65536 + (128<<17); x2 += 65536 + (128<<17); x3 += 65536 + (128<<17); o[0] = clamp((x0+t3) >> 17); o[7] = clamp((x0-t3) >> 17); o[1] = clamp((x1+t2) >> 17); o[6] = clamp((x1-t2) >> 17); o[2] = clamp((x2+t1) >> 17); o[5] = clamp((x2-t1) >> 17); o[3] = clamp((x3+t0) >> 17); o[4] = clamp((x3-t0) >> 17); } } #pragma endregion static int invZ[64] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63}; static int jpeg_decode_block(jpeg* jpg, uint8* block, int ci) { int cc[64]; int aa[64]; huffman *dh, *ah; uint8* qt; int i, t, r, v; dh = jpg->dh[jpg->cc[ci].di]; ah = jpg->ah[jpg->cc[ci].ai]; qt = jpg->qt[jpg->cc[ci].qi]; memset(cc, 0, 64 * sizeof(cc[0])); //直流分量 jpeg_printf(" d:%d ", jpg->cc[ci].di); if ((t = jpeg_decode_byte(jpg, dh)) < 0) { return 0; } if (t > 0) { assert(jpeg_read(jpg, t, &r)); cc[0] = r; } cc[0] += jpg->cc[ci].df; jpg->cc[ci].df = cc[0]; cc[0] = (short)cc[0]; //交流分量 jpeg_printf(" a:%d ", jpg->cc[ci].ai); for (i = 1; i < 64;) { if ((r = jpeg_decode_byte(jpg, ah)) < 0) { return 0; } if (low(r) == 0) { if (r != 0xf0) { break; } i += 16; } else { i += high(r); assert(jpeg_read(jpg, low(r), &v)); if (i < 64) { cc[i++] = v; } } } //反量化 for (i = 0; i < 64; i++) { cc[i] *= qt[i]; } //反Zig for (i = 0; i < 64; i++) { aa[invZ[i]] = cc[i]; } //反离散余弦变换 idct_block(block, aa); return 1; } #define div16(x) ((uint8) ((x) >> 4)) #define div4(x) ((uint8) ((x) >> 2)) color* jpeg_data(void* ctx) { jpeg* jpg = (jpeg*)ctx; return jpg->pixels; } static int jpeg_process_mcu(jpeg* jpg, int l, int t, uint8* py, uint8* pcr, uint8* pcb) { int i, w; uint8 c[64]; w = jpg->width; assert(jpeg_decode_block(jpg, c, 1)); for (i = 0; i < 64; i++) { py[(t + i / 8) * w + l + i % 8] = c[i]; } assert(jpeg_decode_block(jpg, c, 1)); for (i = 0; i < 64; i++) { py[(t + i / 8) * w + l + 8 + i % 8] = c[i]; } assert(jpeg_decode_block(jpg, c, 1)); for (i = 0; i < 64; i++) { py[(t + 8 + i / 8) * w + l + i % 8] = c[i]; } assert(jpeg_decode_block(jpg, c, 1)); for (i = 0; i < 64; i++) { py[(t + 8 + i / 8) * w + l + 8 + i % 8] = c[i]; } assert(jpeg_decode_block(jpg, c, 2)); for (i = 0; i < 64; i++) { pcr[(t /2 + i / 8) * w / 2 + l / 2 + i % 8] = c[i]; } assert(jpeg_decode_block(jpg, c, 3)); for (i = 0; i < 64; i++) { pcb[(t / 2 + i / 8) * w / 2 + l / 2 + i % 8] = c[i]; } return 1; } //resample copy from stbi_image.cpp #pragma region resample typedef struct { uint8 *line0,*line1; int hs,vs; // expansion factor in each axis int w_lores; // horizontal pixels pre-expansion int ystep; // how far through vertical expansion we are int ypos; // which pre-expansion row we're on } resample; static void resampleCr(color* colors, uint8* in_near, uint8* in_far, int w) { int i, t0, t1; t1 = 3 * in_near[0] + in_far[0]; colors[0].cr = div4(t1 + 2); for (i=1; i < w; ++i) { t0 = t1; t1 = 3 * in_near[i] + in_far[i]; colors[i * 2-1].cr = div16(3*t0 + t1 + 8); colors[i * 2].cr = div16(3*t1 + t0 + 8); } colors[w * 2 - 1].cr = div4(t1+2); } static void resampleCb(color* colors, uint8* in_near, uint8* in_far, int w) { int i, t0, t1; t1 = 3 * in_near[0] + in_far[0]; colors[0].cb = div4(t1 + 2); for (i=1; i < w; ++i) { t0 = t1; t1 = 3 * in_near[i] + in_far[i]; colors[i * 2-1].cb = div16(3*t0 + t1 + 8); colors[i * 2].cb = div16(3*t1 + t0 + 8); } colors[w * 2 - 1].cb = div4(t1+2); } #pragma endregion int jpeg_decode(void* ctx) { uint8 *py, *pcr, *pcb; int w, h, i, j; jpeg* p; color* px; resample sample; p = (jpeg*)ctx; w = p->width; h = p->height; py = (uint8*)malloc(w * h * sizeof(uint8)); pcr = (uint8*)malloc(w * h * sizeof(uint8) / 4); pcb = (uint8*)malloc(w * h * sizeof(uint8) / 4); for (i = 0; i < h; i += 16) { for (j = 0; j < w; j += 16) { if (!jpeg_process_mcu(p, j, i, py, pcr, pcb)) { free(py); free(pcr); free(pcb); return 0; } } } px = (color*)malloc(w * h * sizeof(color)); for (i = w * h - 1; i >= 0; i--) { px[i].y = py[i]; } sample.hs = sample.vs = 2; sample.w_lores = w / 2; sample.ystep = 1; sample.line0 = pcr; sample.line1 = pcr; sample.ypos = 0; for (i = 0; i < h; i++) { color* out = px + i * w; int y_bot = sample.ystep >= (sample.vs >> 1); resampleCr(out, y_bot ? sample.line1 : sample.line0, y_bot ? sample.line0 : sample.line1, sample.w_lores); if (++sample.ystep >= sample.vs) { sample.ystep = 0; sample.line0 = sample.line1; if (++sample.ypos < h / 2) sample.line1 += sample.w_lores; } } sample.ystep = 1; sample.line0 = pcb; sample.line1 = pcb; sample.ypos = 0; for (i = 0; i < h; i++) { color* out = px + i * w; int y_bot = sample.ystep >= (sample.vs >> 1); resampleCb(out, y_bot ? sample.line1 : sample.line0, y_bot ? sample.line0 : sample.line1, sample.w_lores); if (++sample.ystep >= sample.vs) { sample.ystep = 0; sample.line0 = sample.line1; if (++sample.ypos < h / 2) sample.line1 += sample.w_lores; } } free(py); free(pcr); free(pcb); p->pixels = px; return 1; } void jpeg_save(const char* file,int desW, int desH, color* colors) { int desBufSize = ((desW * BITCOUNT + 31) / 32) * 4 * desH; int desLineSize = ((desW * BITCOUNT + 31) / 32) * 4; BYTE *desBuf = new BYTE[desBufSize]; for (int i = 0; i < desH; i++) { int x = i * desLineSize; for (int j = 0; j < desW; j++) { int y_fixed = (colors->y << 16) + 32768; // rounding int r,g,b; int cr = colors->cr - 128; int cb = colors->cb - 128; r = y_fixed + cb*float2fixed(1.40200f); g = y_fixed - cb*float2fixed(0.71414f) - cr*float2fixed(0.34414f); b = y_fixed + cr*float2fixed(1.77200f); r >>= 16; g >>= 16; b >>= 16; if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } desBuf[x++] = b; desBuf[x++] = g; desBuf[x++] = r; colors++; } } HFILE hfile = _lcreat(file.c_str,0); BITMAPFILEHEADER nbmfHeader = { 0 }; nbmfHeader.bfType = 0x4D42; nbmfHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + desBufSize; nbmfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); BITMAPINFOHEADER bmi = { 0 }; bmi.biSize=sizeof(BITMAPINFOHEADER); bmi.biWidth = desW; bmi.biHeight= -desH; bmi.biPlanes=1; bmi.biBitCount=BITCOUNT; _lwrite(hfile,(LPCSTR)&nbmfHeader,sizeof(BITMAPFILEHEADER)); _lwrite(hfile,(LPCSTR)&bmi,sizeof(BITMAPINFOHEADER)); _lwrite(hfile,(LPCSTR)desBuf,desBufSize); _lclose(hfile); }