| /* Linux driver for Philips webcam |
| Decompression for chipset version 2 et 3 |
| (C) 2004-2006 Luc Saillard (luc@saillard.org) |
| |
| NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx |
| driver and thus may have bugs that are not present in the original version. |
| Please send bug reports and support requests to <luc@saillard.org>. |
| The decompression routines have been implemented by reverse-engineering the |
| Nemosoft binary pwcx module. Caveat emptor. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
| */ |
| |
| #include "pwc-timon.h" |
| #include "pwc-kiara.h" |
| #include "pwc-dec23.h" |
| #include <media/pwc-ioctl.h> |
| |
| #include <linux/string.h> |
| #include <linux/slab.h> |
| |
| /* |
| * USE_LOOKUP_TABLE_TO_CLAMP |
| * 0: use a C version of this tests: { a<0?0:(a>255?255:a) } |
| * 1: use a faster lookup table for cpu with a big cache (intel) |
| */ |
| #define USE_LOOKUP_TABLE_TO_CLAMP 1 |
| /* |
| * UNROLL_LOOP_FOR_COPYING_BLOCK |
| * 0: use a loop for a smaller code (but little slower) |
| * 1: when unrolling the loop, gcc produces some faster code (perhaps only |
| * valid for intel processor class). Activating this option, automaticaly |
| * activate USE_LOOKUP_TABLE_TO_CLAMP |
| */ |
| #define UNROLL_LOOP_FOR_COPY 1 |
| #if UNROLL_LOOP_FOR_COPY |
| # undef USE_LOOKUP_TABLE_TO_CLAMP |
| # define USE_LOOKUP_TABLE_TO_CLAMP 1 |
| #endif |
| |
| /* |
| * ENABLE_BAYER_DECODER |
| * 0: bayer decoder is not build (save some space) |
| * 1: bayer decoder is build and can be used |
| */ |
| #define ENABLE_BAYER_DECODER 0 |
| |
| static void build_subblock_pattern(struct pwc_dec23_private *pdec) |
| { |
| static const unsigned int initial_values[12] = { |
| -0x526500, -0x221200, 0x221200, 0x526500, |
| -0x3de200, 0x3de200, |
| -0x6db480, -0x2d5d00, 0x2d5d00, 0x6db480, |
| -0x12c200, 0x12c200 |
| |
| }; |
| static const unsigned int values_derivated[12] = { |
| 0xa4ca, 0x4424, -0x4424, -0xa4ca, |
| 0x7bc4, -0x7bc4, |
| 0xdb69, 0x5aba, -0x5aba, -0xdb69, |
| 0x2584, -0x2584 |
| }; |
| unsigned int temp_values[12]; |
| int i, j; |
| |
| memcpy(temp_values, initial_values, sizeof(initial_values)); |
| for (i = 0; i < 256; i++) { |
| for (j = 0; j < 12; j++) { |
| pdec->table_subblock[i][j] = temp_values[j]; |
| temp_values[j] += values_derivated[j]; |
| } |
| } |
| } |
| |
| static void build_bit_powermask_table(struct pwc_dec23_private *pdec) |
| { |
| unsigned char *p; |
| unsigned int bit, byte, mask, val; |
| unsigned int bitpower = 1; |
| |
| for (bit = 0; bit < 8; bit++) { |
| mask = bitpower - 1; |
| p = pdec->table_bitpowermask[bit]; |
| for (byte = 0; byte < 256; byte++) { |
| val = (byte & mask); |
| if (byte & bitpower) |
| val = -val; |
| *p++ = val; |
| } |
| bitpower<<=1; |
| } |
| } |
| |
| |
| static void build_table_color(const unsigned int romtable[16][8], |
| unsigned char p0004[16][1024], |
| unsigned char p8004[16][256]) |
| { |
| int compression_mode, j, k, bit, pw; |
| unsigned char *p0, *p8; |
| const unsigned int *r; |
| |
| /* We have 16 compressions tables */ |
| for (compression_mode = 0; compression_mode < 16; compression_mode++) { |
| p0 = p0004[compression_mode]; |
| p8 = p8004[compression_mode]; |
| r = romtable[compression_mode]; |
| |
| for (j = 0; j < 8; j++, r++, p0 += 128) { |
| |
| for (k = 0; k < 16; k++) { |
| if (k == 0) |
| bit = 1; |
| else if (k >= 1 && k < 3) |
| bit = (r[0] >> 15) & 7; |
| else if (k >= 3 && k < 6) |
| bit = (r[0] >> 12) & 7; |
| else if (k >= 6 && k < 10) |
| bit = (r[0] >> 9) & 7; |
| else if (k >= 10 && k < 13) |
| bit = (r[0] >> 6) & 7; |
| else if (k >= 13 && k < 15) |
| bit = (r[0] >> 3) & 7; |
| else |
| bit = (r[0]) & 7; |
| if (k == 0) |
| *p8++ = 8; |
| else |
| *p8++ = j - bit; |
| *p8++ = bit; |
| |
| pw = 1 << bit; |
| p0[k + 0x00] = (1 * pw) + 0x80; |
| p0[k + 0x10] = (2 * pw) + 0x80; |
| p0[k + 0x20] = (3 * pw) + 0x80; |
| p0[k + 0x30] = (4 * pw) + 0x80; |
| p0[k + 0x40] = (-1 * pw) + 0x80; |
| p0[k + 0x50] = (-2 * pw) + 0x80; |
| p0[k + 0x60] = (-3 * pw) + 0x80; |
| p0[k + 0x70] = (-4 * pw) + 0x80; |
| } /* end of for (k=0; k<16; k++, p8++) */ |
| } /* end of for (j=0; j<8; j++ , table++) */ |
| } /* end of foreach compression_mode */ |
| } |
| |
| /* |
| * |
| */ |
| static void fill_table_dc00_d800(struct pwc_dec23_private *pdec) |
| { |
| #define SCALEBITS 15 |
| #define ONE_HALF (1UL << (SCALEBITS - 1)) |
| int i; |
| unsigned int offset1 = ONE_HALF; |
| unsigned int offset2 = 0x0000; |
| |
| for (i=0; i<256; i++) { |
| pdec->table_dc00[i] = offset1 & ~(ONE_HALF); |
| pdec->table_d800[i] = offset2; |
| |
| offset1 += 0x7bc4; |
| offset2 += 0x7bc4; |
| } |
| } |
| |
| /* |
| * To decode the stream: |
| * if look_bits(2) == 0: # op == 2 in the lookup table |
| * skip_bits(2) |
| * end of the stream |
| * elif look_bits(3) == 7: # op == 1 in the lookup table |
| * skip_bits(3) |
| * yyyy = get_bits(4) |
| * xxxx = get_bits(8) |
| * else: # op == 0 in the lookup table |
| * skip_bits(x) |
| * |
| * For speedup processing, we build a lookup table and we takes the first 6 bits. |
| * |
| * struct { |
| * unsigned char op; // operation to execute |
| * unsigned char bits; // bits use to perform operation |
| * unsigned char offset1; // offset to add to access in the table_0004 % 16 |
| * unsigned char offset2; // offset to add to access in the table_0004 |
| * } |
| * |
| * How to build this table ? |
| * op == 2 when (i%4)==0 |
| * op == 1 when (i%8)==7 |
| * op == 0 otherwise |
| * |
| */ |
| static const unsigned char hash_table_ops[64*4] = { |
| 0x02, 0x00, 0x00, 0x00, |
| 0x00, 0x03, 0x01, 0x00, |
| 0x00, 0x04, 0x01, 0x10, |
| 0x00, 0x06, 0x01, 0x30, |
| 0x02, 0x00, 0x00, 0x00, |
| 0x00, 0x03, 0x01, 0x40, |
| 0x00, 0x05, 0x01, 0x20, |
| 0x01, 0x00, 0x00, 0x00, |
| 0x02, 0x00, 0x00, 0x00, |
| 0x00, 0x03, 0x01, 0x00, |
| 0x00, 0x04, 0x01, 0x50, |
| 0x00, 0x05, 0x02, 0x00, |
| 0x02, 0x00, 0x00, 0x00, |
| 0x00, 0x03, 0x01, 0x40, |
| 0x00, 0x05, 0x03, 0x00, |
| 0x01, 0x00, 0x00, 0x00, |
| 0x02, 0x00, 0x00, 0x00, |
| 0x00, 0x03, 0x01, 0x00, |
| 0x00, 0x04, 0x01, 0x10, |
| 0x00, 0x06, 0x02, 0x10, |
| 0x02, 0x00, 0x00, 0x00, |
| 0x00, 0x03, 0x01, 0x40, |
| 0x00, 0x05, 0x01, 0x60, |
| 0x01, 0x00, 0x00, 0x00, |
| 0x02, 0x00, 0x00, 0x00, |
| 0x00, 0x03, 0x01, 0x00, |
| 0x00, 0x04, 0x01, 0x50, |
| 0x00, 0x05, 0x02, 0x40, |
| 0x02, 0x00, 0x00, 0x00, |
| 0x00, 0x03, 0x01, 0x40, |
| 0x00, 0x05, 0x03, 0x40, |
| 0x01, 0x00, 0x00, 0x00, |
| 0x02, 0x00, 0x00, 0x00, |
| 0x00, 0x03, 0x01, 0x00, |
| 0x00, 0x04, 0x01, 0x10, |
| 0x00, 0x06, 0x01, 0x70, |
| 0x02, 0x00, 0x00, 0x00, |
| 0x00, 0x03, 0x01, 0x40, |
| 0x00, 0x05, 0x01, 0x20, |
| 0x01, 0x00, 0x00, 0x00, |
| 0x02, 0x00, 0x00, 0x00, |
| 0x00, 0x03, 0x01, 0x00, |
| 0x00, 0x04, 0x01, 0x50, |
| 0x00, 0x05, 0x02, 0x00, |
| 0x02, 0x00, 0x00, 0x00, |
| 0x00, 0x03, 0x01, 0x40, |
| 0x00, 0x05, 0x03, 0x00, |
| 0x01, 0x00, 0x00, 0x00, |
| 0x02, 0x00, 0x00, 0x00, |
| 0x00, 0x03, 0x01, 0x00, |
| 0x00, 0x04, 0x01, 0x10, |
| 0x00, 0x06, 0x02, 0x50, |
| 0x02, 0x00, 0x00, 0x00, |
| 0x00, 0x03, 0x01, 0x40, |
| 0x00, 0x05, 0x01, 0x60, |
| 0x01, 0x00, 0x00, 0x00, |
| 0x02, 0x00, 0x00, 0x00, |
| 0x00, 0x03, 0x01, 0x00, |
| 0x00, 0x04, 0x01, 0x50, |
| 0x00, 0x05, 0x02, 0x40, |
| 0x02, 0x00, 0x00, 0x00, |
| 0x00, 0x03, 0x01, 0x40, |
| 0x00, 0x05, 0x03, 0x40, |
| 0x01, 0x00, 0x00, 0x00 |
| }; |
| |
| /* |
| * |
| */ |
| static const unsigned int MulIdx[16][16] = { |
| {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, |
| {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3,}, |
| {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,}, |
| {4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4,}, |
| {6, 7, 8, 9, 7, 10, 11, 8, 8, 11, 10, 7, 9, 8, 7, 6,}, |
| {4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4,}, |
| {1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2,}, |
| {0, 3, 3, 0, 1, 2, 2, 1, 2, 1, 1, 2, 3, 0, 0, 3,}, |
| {0, 1, 2, 3, 3, 2, 1, 0, 3, 2, 1, 0, 0, 1, 2, 3,}, |
| {1, 1, 1, 1, 3, 3, 3, 3, 0, 0, 0, 0, 2, 2, 2, 2,}, |
| {7, 10, 11, 8, 9, 8, 7, 6, 6, 7, 8, 9, 8, 11, 10, 7,}, |
| {4, 5, 5, 4, 5, 4, 4, 5, 5, 4, 4, 5, 4, 5, 5, 4,}, |
| {7, 9, 6, 8, 10, 8, 7, 11, 11, 7, 8, 10, 8, 6, 9, 7,}, |
| {1, 3, 0, 2, 2, 0, 3, 1, 2, 0, 3, 1, 1, 3, 0, 2,}, |
| {1, 2, 2, 1, 3, 0, 0, 3, 0, 3, 3, 0, 2, 1, 1, 2,}, |
| {10, 8, 7, 11, 8, 6, 9, 7, 7, 9, 6, 8, 11, 7, 8, 10} |
| }; |
| |
| #if USE_LOOKUP_TABLE_TO_CLAMP |
| #define MAX_OUTER_CROP_VALUE (512) |
| static unsigned char pwc_crop_table[256 + 2*MAX_OUTER_CROP_VALUE]; |
| #define CLAMP(x) (pwc_crop_table[MAX_OUTER_CROP_VALUE+(x)]) |
| #else |
| #define CLAMP(x) ((x)>255?255:((x)<0?0:x)) |
| #endif |
| |
| |
| /* If the type or the command change, we rebuild the lookup table */ |
| int pwc_dec23_init(struct pwc_device *pwc, int type, unsigned char *cmd) |
| { |
| int flags, version, shift, i; |
| struct pwc_dec23_private *pdec; |
| |
| if (pwc->decompress_data == NULL) { |
| pdec = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); |
| if (pdec == NULL) |
| return -ENOMEM; |
| pwc->decompress_data = pdec; |
| } |
| pdec = pwc->decompress_data; |
| |
| if (DEVICE_USE_CODEC3(type)) { |
| flags = cmd[2] & 0x18; |
| if (flags == 8) |
| pdec->nbits = 7; /* More bits, mean more bits to encode the stream, but better quality */ |
| else if (flags == 0x10) |
| pdec->nbits = 8; |
| else |
| pdec->nbits = 6; |
| |
| version = cmd[2] >> 5; |
| build_table_color(KiaraRomTable[version][0], pdec->table_0004_pass1, pdec->table_8004_pass1); |
| build_table_color(KiaraRomTable[version][1], pdec->table_0004_pass2, pdec->table_8004_pass2); |
| |
| } else { |
| |
| flags = cmd[2] & 6; |
| if (flags == 2) |
| pdec->nbits = 7; |
| else if (flags == 4) |
| pdec->nbits = 8; |
| else |
| pdec->nbits = 6; |
| |
| version = cmd[2] >> 3; |
| build_table_color(TimonRomTable[version][0], pdec->table_0004_pass1, pdec->table_8004_pass1); |
| build_table_color(TimonRomTable[version][1], pdec->table_0004_pass2, pdec->table_8004_pass2); |
| } |
| |
| /* Informations can be coded on a variable number of bits but never less than 8 */ |
| shift = 8 - pdec->nbits; |
| pdec->scalebits = SCALEBITS - shift; |
| pdec->nbitsmask = 0xFF >> shift; |
| |
| fill_table_dc00_d800(pdec); |
| build_subblock_pattern(pdec); |
| build_bit_powermask_table(pdec); |
| |
| #if USE_LOOKUP_TABLE_TO_CLAMP |
| /* Build the static table to clamp value [0-255] */ |
| for (i=0;i<MAX_OUTER_CROP_VALUE;i++) |
| pwc_crop_table[i] = 0; |
| for (i=0; i<256; i++) |
| pwc_crop_table[MAX_OUTER_CROP_VALUE+i] = i; |
| for (i=0; i<MAX_OUTER_CROP_VALUE; i++) |
| pwc_crop_table[MAX_OUTER_CROP_VALUE+256+i] = 255; |
| #endif |
| |
| return 0; |
| } |
| |
| /* |
| * Copy the 4x4 image block to Y plane buffer |
| */ |
| static void copy_image_block_Y(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits) |
| { |
| #if UNROLL_LOOP_FOR_COPY |
| const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE; |
| const int *c = src; |
| unsigned char *d = dst; |
| |
| *d++ = cm[c[0] >> scalebits]; |
| *d++ = cm[c[1] >> scalebits]; |
| *d++ = cm[c[2] >> scalebits]; |
| *d++ = cm[c[3] >> scalebits]; |
| |
| d = dst + bytes_per_line; |
| *d++ = cm[c[4] >> scalebits]; |
| *d++ = cm[c[5] >> scalebits]; |
| *d++ = cm[c[6] >> scalebits]; |
| *d++ = cm[c[7] >> scalebits]; |
| |
| d = dst + bytes_per_line*2; |
| *d++ = cm[c[8] >> scalebits]; |
| *d++ = cm[c[9] >> scalebits]; |
| *d++ = cm[c[10] >> scalebits]; |
| *d++ = cm[c[11] >> scalebits]; |
| |
| d = dst + bytes_per_line*3; |
| *d++ = cm[c[12] >> scalebits]; |
| *d++ = cm[c[13] >> scalebits]; |
| *d++ = cm[c[14] >> scalebits]; |
| *d++ = cm[c[15] >> scalebits]; |
| #else |
| int i; |
| const int *c = src; |
| unsigned char *d = dst; |
| for (i = 0; i < 4; i++, c++) |
| *d++ = CLAMP((*c) >> scalebits); |
| |
| d = dst + bytes_per_line; |
| for (i = 0; i < 4; i++, c++) |
| *d++ = CLAMP((*c) >> scalebits); |
| |
| d = dst + bytes_per_line*2; |
| for (i = 0; i < 4; i++, c++) |
| *d++ = CLAMP((*c) >> scalebits); |
| |
| d = dst + bytes_per_line*3; |
| for (i = 0; i < 4; i++, c++) |
| *d++ = CLAMP((*c) >> scalebits); |
| #endif |
| } |
| |
| /* |
| * Copy the 4x4 image block to a CrCb plane buffer |
| * |
| */ |
| static void copy_image_block_CrCb(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits) |
| { |
| #if UNROLL_LOOP_FOR_COPY |
| /* Unroll all loops */ |
| const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE; |
| const int *c = src; |
| unsigned char *d = dst; |
| |
| *d++ = cm[c[0] >> scalebits]; |
| *d++ = cm[c[4] >> scalebits]; |
| *d++ = cm[c[1] >> scalebits]; |
| *d++ = cm[c[5] >> scalebits]; |
| *d++ = cm[c[2] >> scalebits]; |
| *d++ = cm[c[6] >> scalebits]; |
| *d++ = cm[c[3] >> scalebits]; |
| *d++ = cm[c[7] >> scalebits]; |
| |
| d = dst + bytes_per_line; |
| *d++ = cm[c[12] >> scalebits]; |
| *d++ = cm[c[8] >> scalebits]; |
| *d++ = cm[c[13] >> scalebits]; |
| *d++ = cm[c[9] >> scalebits]; |
| *d++ = cm[c[14] >> scalebits]; |
| *d++ = cm[c[10] >> scalebits]; |
| *d++ = cm[c[15] >> scalebits]; |
| *d++ = cm[c[11] >> scalebits]; |
| #else |
| int i; |
| const int *c1 = src; |
| const int *c2 = src + 4; |
| unsigned char *d = dst; |
| |
| for (i = 0; i < 4; i++, c1++, c2++) { |
| *d++ = CLAMP((*c1) >> scalebits); |
| *d++ = CLAMP((*c2) >> scalebits); |
| } |
| c1 = src + 12; |
| d = dst + bytes_per_line; |
| for (i = 0; i < 4; i++, c1++, c2++) { |
| *d++ = CLAMP((*c1) >> scalebits); |
| *d++ = CLAMP((*c2) >> scalebits); |
| } |
| #endif |
| } |
| |
| #if ENABLE_BAYER_DECODER |
| /* |
| * Format: 8x2 pixels |
| * . G . G . G . G . G . G . G |
| * . . . . . . . . . . . . . . |
| * . G . G . G . G . G . G . G |
| * . . . . . . . . . . . . . . |
| * or |
| * . . . . . . . . . . . . . . |
| * G . G . G . G . G . G . G . |
| * . . . . . . . . . . . . . . |
| * G . G . G . G . G . G . G . |
| */ |
| static void copy_image_block_Green(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits) |
| { |
| #if UNROLL_LOOP_FOR_COPY |
| /* Unroll all loops */ |
| const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE; |
| unsigned char *d = dst; |
| const int *c = src; |
| |
| d[0] = cm[c[0] >> scalebits]; |
| d[2] = cm[c[1] >> scalebits]; |
| d[4] = cm[c[2] >> scalebits]; |
| d[6] = cm[c[3] >> scalebits]; |
| d[8] = cm[c[4] >> scalebits]; |
| d[10] = cm[c[5] >> scalebits]; |
| d[12] = cm[c[6] >> scalebits]; |
| d[14] = cm[c[7] >> scalebits]; |
| |
| d = dst + bytes_per_line; |
| d[0] = cm[c[8] >> scalebits]; |
| d[2] = cm[c[9] >> scalebits]; |
| d[4] = cm[c[10] >> scalebits]; |
| d[6] = cm[c[11] >> scalebits]; |
| d[8] = cm[c[12] >> scalebits]; |
| d[10] = cm[c[13] >> scalebits]; |
| d[12] = cm[c[14] >> scalebits]; |
| d[14] = cm[c[15] >> scalebits]; |
| #else |
| int i; |
| unsigned char *d; |
| const int *c = src; |
| |
| d = dst; |
| for (i = 0; i < 8; i++, c++) |
| d[i*2] = CLAMP((*c) >> scalebits); |
| |
| d = dst + bytes_per_line; |
| for (i = 0; i < 8; i++, c++) |
| d[i*2] = CLAMP((*c) >> scalebits); |
| #endif |
| } |
| #endif |
| |
| #if ENABLE_BAYER_DECODER |
| /* |
| * Format: 4x4 pixels |
| * R . R . R . R |
| * . B . B . B . |
| * R . R . R . R |
| * . B . B . B . |
| */ |
| static void copy_image_block_RedBlue(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits) |
| { |
| #if UNROLL_LOOP_FOR_COPY |
| /* Unroll all loops */ |
| const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE; |
| unsigned char *d = dst; |
| const int *c = src; |
| |
| d[0] = cm[c[0] >> scalebits]; |
| d[2] = cm[c[1] >> scalebits]; |
| d[4] = cm[c[2] >> scalebits]; |
| d[6] = cm[c[3] >> scalebits]; |
| |
| d = dst + bytes_per_line; |
| d[1] = cm[c[4] >> scalebits]; |
| d[3] = cm[c[5] >> scalebits]; |
| d[5] = cm[c[6] >> scalebits]; |
| d[7] = cm[c[7] >> scalebits]; |
| |
| d = dst + bytes_per_line*2; |
| d[0] = cm[c[8] >> scalebits]; |
| d[2] = cm[c[9] >> scalebits]; |
| d[4] = cm[c[10] >> scalebits]; |
| d[6] = cm[c[11] >> scalebits]; |
| |
| d = dst + bytes_per_line*3; |
| d[1] = cm[c[12] >> scalebits]; |
| d[3] = cm[c[13] >> scalebits]; |
| d[5] = cm[c[14] >> scalebits]; |
| d[7] = cm[c[15] >> scalebits]; |
| #else |
| int i; |
| unsigned char *d; |
| const int *c = src; |
| |
| d = dst; |
| for (i = 0; i < 4; i++, c++) |
| d[i*2] = CLAMP((*c) >> scalebits); |
| |
| d = dst + bytes_per_line; |
| for (i = 0; i < 4; i++, c++) |
| d[i*2+1] = CLAMP((*c) >> scalebits); |
| |
| d = dst + bytes_per_line*2; |
| for (i = 0; i < 4; i++, c++) |
| d[i*2] = CLAMP((*c) >> scalebits); |
| |
| d = dst + bytes_per_line*3; |
| for (i = 0; i < 4; i++, c++) |
| d[i*2+1] = CLAMP((*c) >> scalebits); |
| #endif |
| } |
| #endif |
| |
| /* |
| * To manage the stream, we keep bits in a 32 bits register. |
| * fill_nbits(n): fill the reservoir with at least n bits |
| * skip_bits(n): discard n bits from the reservoir |
| * get_bits(n): fill the reservoir, returns the first n bits and discard the |
| * bits from the reservoir. |
| * __get_nbits(n): faster version of get_bits(n), but asumes that the reservoir |
| * contains at least n bits. bits returned is discarded. |
| */ |
| #define fill_nbits(pdec, nbits_wanted) do { \ |
| while (pdec->nbits_in_reservoir<(nbits_wanted)) \ |
| { \ |
| pdec->reservoir |= (*(pdec->stream)++) << (pdec->nbits_in_reservoir); \ |
| pdec->nbits_in_reservoir += 8; \ |
| } \ |
| } while(0); |
| |
| #define skip_nbits(pdec, nbits_to_skip) do { \ |
| pdec->reservoir >>= (nbits_to_skip); \ |
| pdec->nbits_in_reservoir -= (nbits_to_skip); \ |
| } while(0); |
| |
| #define get_nbits(pdec, nbits_wanted, result) do { \ |
| fill_nbits(pdec, nbits_wanted); \ |
| result = (pdec->reservoir) & ((1U<<(nbits_wanted))-1); \ |
| skip_nbits(pdec, nbits_wanted); \ |
| } while(0); |
| |
| #define __get_nbits(pdec, nbits_wanted, result) do { \ |
| result = (pdec->reservoir) & ((1U<<(nbits_wanted))-1); \ |
| skip_nbits(pdec, nbits_wanted); \ |
| } while(0); |
| |
| #define look_nbits(pdec, nbits_wanted) \ |
| ((pdec->reservoir) & ((1U<<(nbits_wanted))-1)) |
| |
| /* |
| * Decode a 4x4 pixel block |
| */ |
| static void decode_block(struct pwc_dec23_private *pdec, |
| const unsigned char *ptable0004, |
| const unsigned char *ptable8004) |
| { |
| unsigned int primary_color; |
| unsigned int channel_v, offset1, op; |
| int i; |
| |
| fill_nbits(pdec, 16); |
| __get_nbits(pdec, pdec->nbits, primary_color); |
| |
| if (look_nbits(pdec,2) == 0) { |
| skip_nbits(pdec, 2); |
| /* Very simple, the color is the same for all pixels of the square */ |
| for (i = 0; i < 16; i++) |
| pdec->temp_colors[i] = pdec->table_dc00[primary_color]; |
| |
| return; |
| } |
| |
| /* This block is encoded with small pattern */ |
| for (i = 0; i < 16; i++) |
| pdec->temp_colors[i] = pdec->table_d800[primary_color]; |
| |
| __get_nbits(pdec, 3, channel_v); |
| channel_v = ((channel_v & 1) << 2) | (channel_v & 2) | ((channel_v & 4) >> 2); |
| |
| ptable0004 += (channel_v * 128); |
| ptable8004 += (channel_v * 32); |
| |
| offset1 = 0; |
| do |
| { |
| unsigned int htable_idx, rows = 0; |
| const unsigned int *block; |
| |
| /* [ zzzz y x x ] |
| * xx == 00 :=> end of the block def, remove the two bits from the stream |
| * yxx == 111 |
| * yxx == any other value |
| * |
| */ |
| fill_nbits(pdec, 16); |
| htable_idx = look_nbits(pdec, 6); |
| op = hash_table_ops[htable_idx * 4]; |
| |
| if (op == 2) { |
| skip_nbits(pdec, 2); |
| |
| } else if (op == 1) { |
| /* 15bits [ xxxx xxxx yyyy 111 ] |
| * yyy => offset in the table8004 |
| * xxx => offset in the tabled004 (tree) |
| */ |
| unsigned int mask, shift; |
| unsigned int nbits, col1; |
| unsigned int yyyy; |
| |
| skip_nbits(pdec, 3); |
| /* offset1 += yyyy */ |
| __get_nbits(pdec, 4, yyyy); |
| offset1 += 1 + yyyy; |
| offset1 &= 0x0F; |
| nbits = ptable8004[offset1 * 2]; |
| |
| /* col1 = xxxx xxxx */ |
| __get_nbits(pdec, nbits+1, col1); |
| |
| /* Bit mask table */ |
| mask = pdec->table_bitpowermask[nbits][col1]; |
| shift = ptable8004[offset1 * 2 + 1]; |
| rows = ((mask << shift) + 0x80) & 0xFF; |
| |
| block = pdec->table_subblock[rows]; |
| for (i = 0; i < 16; i++) |
| pdec->temp_colors[i] += block[MulIdx[offset1][i]]; |
| |
| } else { |
| /* op == 0 |
| * offset1 is coded on 3 bits |
| */ |
| unsigned int shift; |
| |
| offset1 += hash_table_ops [htable_idx * 4 + 2]; |
| offset1 &= 0x0F; |
| |
| rows = ptable0004[offset1 + hash_table_ops [htable_idx * 4 + 3]]; |
| block = pdec->table_subblock[rows]; |
| for (i = 0; i < 16; i++) |
| pdec->temp_colors[i] += block[MulIdx[offset1][i]]; |
| |
| shift = hash_table_ops[htable_idx * 4 + 1]; |
| skip_nbits(pdec, shift); |
| } |
| |
| } while (op != 2); |
| |
| } |
| |
| static void DecompressBand23(struct pwc_dec23_private *pdec, |
| const unsigned char *rawyuv, |
| unsigned char *planar_y, |
| unsigned char *planar_u, |
| unsigned char *planar_v, |
| unsigned int compressed_image_width, |
| unsigned int real_image_width) |
| { |
| int compression_index, nblocks; |
| const unsigned char *ptable0004; |
| const unsigned char *ptable8004; |
| |
| pdec->reservoir = 0; |
| pdec->nbits_in_reservoir = 0; |
| pdec->stream = rawyuv + 1; /* The first byte of the stream is skipped */ |
| |
| get_nbits(pdec, 4, compression_index); |
| |
| /* pass 1: uncompress Y component */ |
| nblocks = compressed_image_width / 4; |
| |
| ptable0004 = pdec->table_0004_pass1[compression_index]; |
| ptable8004 = pdec->table_8004_pass1[compression_index]; |
| |
| /* Each block decode a square of 4x4 */ |
| while (nblocks) { |
| decode_block(pdec, ptable0004, ptable8004); |
| copy_image_block_Y(pdec->temp_colors, planar_y, real_image_width, pdec->scalebits); |
| planar_y += 4; |
| nblocks--; |
| } |
| |
| /* pass 2: uncompress UV component */ |
| nblocks = compressed_image_width / 8; |
| |
| ptable0004 = pdec->table_0004_pass2[compression_index]; |
| ptable8004 = pdec->table_8004_pass2[compression_index]; |
| |
| /* Each block decode a square of 4x4 */ |
| while (nblocks) { |
| decode_block(pdec, ptable0004, ptable8004); |
| copy_image_block_CrCb(pdec->temp_colors, planar_u, real_image_width/2, pdec->scalebits); |
| |
| decode_block(pdec, ptable0004, ptable8004); |
| copy_image_block_CrCb(pdec->temp_colors, planar_v, real_image_width/2, pdec->scalebits); |
| |
| planar_v += 8; |
| planar_u += 8; |
| nblocks -= 2; |
| } |
| |
| } |
| |
| #if ENABLE_BAYER_DECODER |
| /* |
| * Size need to be a multiple of 8 in width |
| * |
| * Return a block of four line encoded like this: |
| * |
| * G R G R G R G R G R G R G R G R |
| * B G B G B G B G B G B G B G B G |
| * G R G R G R G R G R G R G R G R |
| * B G B G B G B G B G B G B G B G |
| * |
| */ |
| static void DecompressBandBayer(struct pwc_dec23_private *pdec, |
| const unsigned char *rawyuv, |
| unsigned char *rgbbayer, |
| unsigned int compressed_image_width, |
| unsigned int real_image_width) |
| { |
| int compression_index, nblocks; |
| const unsigned char *ptable0004; |
| const unsigned char *ptable8004; |
| unsigned char *dest; |
| |
| pdec->reservoir = 0; |
| pdec->nbits_in_reservoir = 0; |
| pdec->stream = rawyuv + 1; /* The first byte of the stream is skipped */ |
| |
| get_nbits(pdec, 4, compression_index); |
| |
| /* pass 1: uncompress RB component */ |
| nblocks = compressed_image_width / 4; |
| |
| ptable0004 = pdec->table_0004_pass1[compression_index]; |
| ptable8004 = pdec->table_8004_pass1[compression_index]; |
| dest = rgbbayer; |
| |
| /* Each block decode a square of 4x4 */ |
| while (nblocks) { |
| decode_block(pdec, ptable0004, ptable8004); |
| copy_image_block_RedBlue(pdec->temp_colors, rgbbayer, real_image_width, pdec->scalebits); |
| dest += 8; |
| nblocks--; |
| } |
| |
| /* pass 2: uncompress G component */ |
| nblocks = compressed_image_width / 8; |
| |
| ptable0004 = pdec->table_0004_pass2[compression_index]; |
| ptable8004 = pdec->table_8004_pass2[compression_index]; |
| |
| /* Each block decode a square of 4x4 */ |
| while (nblocks) { |
| decode_block(pdec, ptable0004, ptable8004); |
| copy_image_block_Green(pdec->temp_colors, rgbbayer+1, real_image_width, pdec->scalebits); |
| |
| decode_block(pdec, ptable0004, ptable8004); |
| copy_image_block_Green(pdec->temp_colors, rgbbayer+real_image_width, real_image_width, pdec->scalebits); |
| |
| rgbbayer += 16; |
| nblocks -= 2; |
| } |
| } |
| #endif |
| |
| |
| /** |
| * |
| * Uncompress a pwc23 buffer. |
| * |
| * pwc.view: size of the image wanted |
| * pwc.image: size of the image returned by the camera |
| * pwc.offset: (x,y) to displayer image in the view |
| * |
| * src: raw data |
| * dst: image output |
| * flags: PWCX_FLAG_PLANAR or PWCX_FLAG_BAYER |
| */ |
| void pwc_dec23_decompress(const struct pwc_device *pwc, |
| const void *src, |
| void *dst, |
| int flags) |
| { |
| int bandlines_left, stride, bytes_per_block; |
| |
| bandlines_left = pwc->image.y / 4; |
| bytes_per_block = pwc->view.x * 4; |
| |
| if (flags & PWCX_FLAG_BAYER) { |
| #if ENABLE_BAYER_DECODER |
| /* RGB Bayer format */ |
| unsigned char *rgbout; |
| |
| stride = pwc->view.x * pwc->offset.y; |
| rgbout = dst + stride + pwc->offset.x; |
| |
| |
| while (bandlines_left--) { |
| |
| DecompressBandBayer(pwc->decompress_data, |
| src, |
| rgbout, |
| pwc->image.x, pwc->view.x); |
| |
| src += pwc->vbandlength; |
| rgbout += bytes_per_block; |
| |
| } |
| #else |
| memset(dst, 0, pwc->view.x * pwc->view.y); |
| #endif |
| |
| } else { |
| /* YUV420P image format */ |
| unsigned char *pout_planar_y; |
| unsigned char *pout_planar_u; |
| unsigned char *pout_planar_v; |
| unsigned int plane_size; |
| |
| plane_size = pwc->view.x * pwc->view.y; |
| |
| /* offset in Y plane */ |
| stride = pwc->view.x * pwc->offset.y; |
| pout_planar_y = dst + stride + pwc->offset.x; |
| |
| /* offsets in U/V planes */ |
| stride = (pwc->view.x * pwc->offset.y) / 4 + pwc->offset.x / 2; |
| pout_planar_u = dst + plane_size + stride; |
| pout_planar_v = dst + plane_size + plane_size / 4 + stride; |
| |
| while (bandlines_left--) { |
| |
| DecompressBand23(pwc->decompress_data, |
| src, |
| pout_planar_y, pout_planar_u, pout_planar_v, |
| pwc->image.x, pwc->view.x); |
| src += pwc->vbandlength; |
| pout_planar_y += bytes_per_block; |
| pout_planar_u += pwc->view.x; |
| pout_planar_v += pwc->view.x; |
| |
| } |
| |
| } |
| |
| } |
| |
| void pwc_dec23_exit(void) |
| { |
| /* Do nothing */ |
| |
| } |
| |
| /** |
| * Allocate a private structure used by lookup table. |
| * You must call kfree() to free the memory allocated. |
| */ |
| int pwc_dec23_alloc(struct pwc_device *pwc) |
| { |
| pwc->decompress_data = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); |
| if (pwc->decompress_data == NULL) |
| return -ENOMEM; |
| return 0; |
| } |
| |
| /* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ |