spice/server/glz_encode_tmpl.c
Christophe Fergeau 78c1465ed3 add #include <config.h> to all source files
When using config.h, it must be the very first include in all source
files since it contains #define that may change the compilation process
(eg libc structure layout changes when it's used to enable large file
support on 32 bit x86 archs). This commit adds it at the beginning
of all .c and .cpp files
2011-05-03 14:44:10 +02:00

576 lines
17 KiB
C

/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define DJB2_START 5381;
#define DJB2_HASH(hash, c) (hash = ((hash << 5) + hash) ^ (c)) //|{hash = ((hash << 5) + hash) + c;}
/*
For each pixel type the following macros are defined:
PIXEL : input type
FNAME(name)
ENCODE_PIXEL(encoder, pixel) : writing a pixel to the compressed buffer (byte by byte)
SAME_PIXEL(pix1, pix2) : comparing two pixels
HASH_FUNC(value, pix_ptr) : hash func of 3 consecutive pixels
*/
#ifdef LZ_PLT
#define PIXEL one_byte_pixel_t
#define FNAME(name) glz_plt_##name
#define ENCODE_PIXEL(e, pix) encode(e, (pix).a) // gets the pixel and write only the needed bytes
// from the pixel
#define SAME_PIXEL(pix1, pix2) ((pix1).a == (pix2).a)
#define MIN_REF_ENCODE_SIZE 4
#define MAX_REF_ENCODE_SIZE 7
#define HASH_FUNC(v, p) { \
v = DJB2_START; \
DJB2_HASH(v, p[0].a); \
DJB2_HASH(v, p[1].a); \
DJB2_HASH(v, p[2].a); \
v &= HASH_MASK; \
}
#endif
#ifdef LZ_RGB_ALPHA
//#undef LZ_RGB_ALPHA
#define PIXEL rgb32_pixel_t
#define FNAME(name) glz_rgb_alpha_##name
#define ENCODE_PIXEL(e, pix) {encode(e, (pix).pad);}
#define SAME_PIXEL(pix1, pix2) ((pix1).pad == (pix2).pad)
#define MIN_REF_ENCODE_SIZE 4
#define MAX_REF_ENCODE_SIZE 7
#define HASH_FUNC(v, p) { \
v = DJB2_START; \
DJB2_HASH(v, p[0].pad); \
DJB2_HASH(v, p[1].pad); \
DJB2_HASH(v, p[2].pad); \
v &= HASH_MASK; \
}
#endif
#ifdef LZ_RGB16
#define PIXEL rgb16_pixel_t
#define FNAME(name) glz_rgb16_##name
#define GET_r(pix) (((pix) >> 10) & 0x1f)
#define GET_g(pix) (((pix) >> 5) & 0x1f)
#define GET_b(pix) ((pix) & 0x1f)
#define ENCODE_PIXEL(e, pix) {encode(e, (pix) >> 8); encode(e, (pix) & 0xff);}
#define MIN_REF_ENCODE_SIZE 2
#define MAX_REF_ENCODE_SIZE 3
#define HASH_FUNC(v, p) { \
v = DJB2_START; \
DJB2_HASH(v, p[0] & (0x00ff)); \
DJB2_HASH(v, (p[0] >> 8) & (0x007f)); \
DJB2_HASH(v, p[1] & (0x00ff)); \
DJB2_HASH(v, (p[1] >> 8) & (0x007f)); \
DJB2_HASH(v, p[2] & (0x00ff)); \
DJB2_HASH(v, (p[2] >> 8) & (0x007f)); \
v &= HASH_MASK; \
}
#endif
#ifdef LZ_RGB24
#define PIXEL rgb24_pixel_t
#define FNAME(name) glz_rgb24_##name
#define ENCODE_PIXEL(e, pix) {encode(e, (pix).b); encode(e, (pix).g); encode(e, (pix).r);}
#define MIN_REF_ENCODE_SIZE 2
#define MAX_REF_ENCODE_SIZE 2
#endif
#ifdef LZ_RGB32
#define PIXEL rgb32_pixel_t
#define FNAME(name) glz_rgb32_##name
#define ENCODE_PIXEL(e, pix) {encode(e, (pix).b); encode(e, (pix).g); encode(e, (pix).r);}
#define MIN_REF_ENCODE_SIZE 2
#define MAX_REF_ENCODE_SIZE 2
#endif
#if defined(LZ_RGB24) || defined(LZ_RGB32)
#define GET_r(pix) ((pix).r)
#define GET_g(pix) ((pix).g)
#define GET_b(pix) ((pix).b)
#define HASH_FUNC(v, p) { \
v = DJB2_START; \
DJB2_HASH(v, p[0].r); \
DJB2_HASH(v, p[0].g); \
DJB2_HASH(v, p[0].b); \
DJB2_HASH(v, p[1].r); \
DJB2_HASH(v, p[1].g); \
DJB2_HASH(v, p[1].b); \
DJB2_HASH(v, p[2].r); \
DJB2_HASH(v, p[2].g); \
DJB2_HASH(v, p[2].b); \
v &= HASH_MASK; \
}
#endif
#if defined(LZ_RGB16) || defined(LZ_RGB24) || defined(LZ_RGB32)
#define SAME_PIXEL(p1, p2) (GET_r(p1) == GET_r(p2) && GET_g(p1) == GET_g(p2) && \
GET_b(p1) == GET_b(p2))
#endif
#ifndef LZ_PLT
#define PIXEL_ID(pix_ptr, seg_ptr) \
((pix_ptr) - ((PIXEL *)(seg_ptr)->lines) + (seg_ptr)->pixels_so_far)
#define PIXEL_DIST(src_pix_ptr, src_seg_ptr, ref_pix_ptr, ref_seg_ptr) \
(PIXEL_ID(src_pix_ptr,src_seg_ptr) - PIXEL_ID(ref_pix_ptr, ref_seg_ptr))
#else
#define PIXEL_ID(pix_ptr, seg_ptr, pix_per_byte) \
(((pix_ptr) - ((PIXEL *)(seg_ptr)->lines)) * pix_per_byte + (seg_ptr)->pixels_so_far)
#define PIXEL_DIST(src_pix_ptr, src_seg_ptr, ref_pix_ptr, ref_seg_ptr, pix_per_byte) \
((PIXEL_ID(src_pix_ptr,src_seg_ptr, pix_per_byte) - \
PIXEL_ID(ref_pix_ptr, ref_seg_ptr, pix_per_byte)) / pix_per_byte)
#endif
/* returns the length of the match. 0 if no match.
if image_distance = 0, pixel_distance is the distance between the matching pixels.
Otherwise, it is the offset from the beginning of the referred image */
static INLINE size_t FNAME(do_match)(SharedDictionary *dict,
WindowImageSegment *ref_seg, const PIXEL *ref,
const PIXEL *ref_limit,
WindowImageSegment *ip_seg, const PIXEL *ip,
const PIXEL *ip_limit,
#ifdef LZ_PLT
int pix_per_byte,
#endif
size_t *o_image_dist, size_t *o_pix_distance)
{
int encode_size;
const PIXEL *tmp_ip = ip;
const PIXEL *tmp_ref = ref;
if (ref > (ref_limit - MIN_REF_ENCODE_SIZE)) {
return 0; // in case the hash entry is not relevant
}
/* min match length == MIN_REF_ENCODE_SIZE (depends on pixel type) */
if (!SAME_PIXEL(*tmp_ref, *tmp_ip)) {
return 0;
} else {
tmp_ref++;
tmp_ip++;
}
if (!SAME_PIXEL(*tmp_ref, *tmp_ip)) {
return 0;
} else {
tmp_ref++;
tmp_ip++;
}
#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
if (!SAME_PIXEL(*tmp_ref, *tmp_ip)) {
return 0;
} else {
tmp_ref++;
tmp_ip++;
}
if (!SAME_PIXEL(*tmp_ref, *tmp_ip)) {
return 0;
} else {
tmp_ref++;
tmp_ip++;
}
#endif
*o_image_dist = ip_seg->image->id - ref_seg->image->id;
if (!(*o_image_dist)) { // the ref is inside the same image - encode distance
#ifndef LZ_PLT
*o_pix_distance = PIXEL_DIST(ip, ip_seg, ref, ref_seg);
#else
// in bytes
*o_pix_distance = PIXEL_DIST(ip, ip_seg, ref, ref_seg, pix_per_byte);
#endif
} else { // the ref is at different image - encode offset from the image start
#ifndef LZ_PLT
*o_pix_distance = PIXEL_DIST(ref, ref_seg,
(PIXEL *)(dict->window.segs[ref_seg->image->first_seg].lines),
&dict->window.segs[ref_seg->image->first_seg]
);
#else
// in bytes
*o_pix_distance = PIXEL_DIST(ref, ref_seg,
(PIXEL *)(dict->window.segs[ref_seg->image->first_seg].lines),
&dict->window.segs[ref_seg->image->first_seg],
pix_per_byte);
#endif
}
if ((*o_pix_distance == 0) || (*o_pix_distance >= MAX_PIXEL_LONG_DISTANCE) ||
(*o_image_dist > MAX_IMAGE_DIST)) {
return 0;
}
/* continue the match*/
while ((tmp_ip < ip_limit) && (tmp_ref < ref_limit)) {
if (!SAME_PIXEL(*tmp_ref, *tmp_ip)) {
break;
} else {
tmp_ref++;
tmp_ip++;
}
}
if ((tmp_ip - ip) > MAX_REF_ENCODE_SIZE) {
return (tmp_ip - ip);
}
encode_size = get_encode_ref_size(*o_image_dist, *o_pix_distance);
// min number of identical pixels for a match
#if defined(LZ_RGB16)
encode_size /= 2;
#elif defined(LZ_RGB24) || defined(LZ_RGB32)
encode_size /= 3;
#endif
encode_size++; // the minimum match
// match len is smaller than the encoding - not worth encoding
if ((tmp_ip - ip) < encode_size) {
return 0;
}
return (tmp_ip - ip);
}
/* compresses one segment starting from 'from'.
In order to encode a match, we use pixels resolution when we encode RGB image,
and bytes count when we encode PLT.
*/
static void FNAME(compress_seg)(Encoder *encoder, uint32_t seg_idx, PIXEL *from, int copied)
{
WindowImageSegment *seg = &encoder->dict->window.segs[seg_idx];
const PIXEL *ip = from;
const PIXEL *ip_bound = (PIXEL *)(seg->lines_end) - BOUND_OFFSET;
const PIXEL *ip_limit = (PIXEL *)(seg->lines_end) - LIMIT_OFFSET;
int hval;
int copy = copied;
#ifdef LZ_PLT
int pix_per_byte = PLT_PIXELS_PER_BYTE[encoder->cur_image.type];
#endif
#ifdef DEBUG_ENCODE
int n_encoded = 0;
#endif
if (copy == 0) {
encode_copy_count(encoder, MAX_COPY - 1);
}
while (LZ_EXPECT_CONDITIONAL(ip < ip_limit)) {
const PIXEL *ref;
const PIXEL *ref_limit;
WindowImageSegment *ref_seg;
uint32_t ref_seg_idx;
size_t pix_dist;
size_t image_dist;
/* minimum match length */
size_t len = 0;
/* comparison starting-point */
const PIXEL *anchor = ip;
#ifdef CHAINED_HASH
int hash_id = 0;
size_t best_len = 0;
size_t best_pix_dist = 0;
size_t best_image_dist = 0;
#endif
/* check for a run */
if (LZ_EXPECT_CONDITIONAL(ip > (PIXEL *)(seg->lines))) {
if (SAME_PIXEL(ip[-1], ip[0]) && SAME_PIXEL(ip[0], ip[1]) && SAME_PIXEL(ip[1], ip[2])) {
PIXEL x;
pix_dist = 1;
image_dist = 0;
ip += 3;
ref = anchor + 2;
ref_limit = (PIXEL *)(seg->lines_end);
len = 3;
x = *ref;
while (ip < ip_bound) { // TODO: maybe separate a run from the same seg or from
// different ones in order to spare ref < ref_limit
if (!SAME_PIXEL(*ip, x)) {
ip++;
break;
} else {
ip++;
len++;
}
}
goto match;
} // END RLE MATCH
}
/* find potential match */
HASH_FUNC(hval, ip);
#ifdef CHAINED_HASH
for (hash_id = 0; hash_id < HASH_CHAIN_SIZE; hash_id++) {
ref_seg_idx = encoder->dict->htab[hval][hash_id].image_seg_idx;
#else
ref_seg_idx = encoder->dict->htab[hval].image_seg_idx;
#endif
ref_seg = encoder->dict->window.segs + ref_seg_idx;
if (REF_SEG_IS_VALID(encoder->dict, encoder->id,
ref_seg, seg)) {
#ifdef CHAINED_HASH
ref = ((PIXEL *)ref_seg->lines) + encoder->dict->htab[hval][hash_id].ref_pix_idx;
#else
ref = ((PIXEL *)ref_seg->lines) + encoder->dict->htab[hval].ref_pix_idx;
#endif
ref_limit = (PIXEL *)ref_seg->lines_end;
len = FNAME(do_match)(encoder->dict, ref_seg, ref, ref_limit, seg, ip, ip_bound,
#ifdef LZ_PLT
pix_per_byte,
#endif
&image_dist, &pix_dist);
#ifdef CHAINED_HASH
// TODO. not compare len but rather len - encode_size
if (len > best_len) {
best_len = len;
best_pix_dist = pix_dist;
best_image_dist = image_dist;
}
#endif
}
#ifdef CHAINED_HASH
} // end chain loop
len = best_len;
pix_dist = best_pix_dist;
image_dist = best_image_dist;
#endif
/* update hash table */
UPDATE_HASH(encoder->dict, hval, seg_idx, anchor - ((PIXEL *)seg->lines));
if (!len) {
goto literal;
}
match: // RLE or dictionary (both are encoded by distance from ref (-1) and length)
#ifdef DEBUG_ENCODE
printf(", match(%d, %d, %d)", image_dist, pix_dist, len);
n_encoded += len;
#endif
/* distance is biased */
if (!image_dist) {
pix_dist--;
}
/* if we have copied something, adjust the copy count */
if (copy) {
/* copy is biased, '0' means 1 byte copy */
update_copy_count(encoder, copy - 1);
} else {
/* back, to overwrite the copy count */
compress_output_prev(encoder);
}
/* reset literal counter */
copy = 0;
/* length is biased, '1' means a match of 3 pixels for PLT and alpha*/
/* for RGB 16 1 means 2 */
/* for RGB24/32 1 means 1...*/
ip = anchor + len - 2;
#if defined(LZ_RGB16)
len--;
#elif defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
len -= 2;
#endif
GLZ_ASSERT(encoder->usr, len > 0);
encode_match(encoder, image_dist, pix_dist, len);
/* update the hash at match boundary */
#if defined(LZ_RGB16) || defined(LZ_RGB24) || defined(LZ_RGB32)
if (ip > anchor) {
#endif
HASH_FUNC(hval, ip);
UPDATE_HASH(encoder->dict, hval, seg_idx, ip - ((PIXEL *)seg->lines));
ip++;
#if defined(LZ_RGB16) || defined(LZ_RGB24) || defined(LZ_RGB32)
} else {ip++;
}
#endif
#if defined(LZ_RGB24) || defined(LZ_RGB32)
if (ip > anchor) {
#endif
HASH_FUNC(hval, ip);
UPDATE_HASH(encoder->dict, hval, seg_idx, ip - ((PIXEL *)seg->lines));
ip++;
#if defined(LZ_RGB24) || defined(LZ_RGB32)
} else {
ip++;
}
#endif
/* assuming literal copy */
encode_copy_count(encoder, MAX_COPY - 1);
continue;
literal:
#ifdef DEBUG_ENCODE
printf(", copy");
n_encoded++;
#endif
ENCODE_PIXEL(encoder, *anchor);
anchor++;
ip = anchor;
copy++;
if (LZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY)) {
copy = 0;
encode_copy_count(encoder, MAX_COPY - 1);
}
} // END LOOP (ip < ip_limit)
/* left-over as literal copy */
ip_bound++;
while (ip <= ip_bound) {
#ifdef DEBUG_ENCODE
printf(", copy");
n_encoded++;
#endif
ENCODE_PIXEL(encoder, *ip);
ip++;
copy++;
if (copy == MAX_COPY) {
copy = 0;
encode_copy_count(encoder, MAX_COPY - 1);
}
}
/* if we have copied something, adjust the copy length */
if (copy) {
update_copy_count(encoder, copy - 1);
} else {
compress_output_prev(encoder);
}
#ifdef DEBUG_ENCODE
printf("\ntotal encoded=%d\n", n_encoded);
#endif
}
/* If the file is very small, copies it.
copies the first two pixels of the first segment, and sends the segments
one by one to compress_seg.
the number of bytes compressed are stored inside encoder. */
static void FNAME(compress)(Encoder *encoder)
{
uint32_t seg_id = encoder->cur_image.first_win_seg;
PIXEL *ip;
SharedDictionary *dict = encoder->dict;
int hval;
// fetch the first image segment that is not too small
while ((seg_id != NULL_IMAGE_SEG_ID) &&
(dict->window.segs[seg_id].image->id == encoder->cur_image.id) &&
((((PIXEL *)dict->window.segs[seg_id].lines_end) -
((PIXEL *)dict->window.segs[seg_id].lines)) < 4)) {
// coping the segment
if (dict->window.segs[seg_id].lines != dict->window.segs[seg_id].lines_end) {
ip = (PIXEL *)dict->window.segs[seg_id].lines;
// Note: we assume MAX_COPY > 3
encode_copy_count(encoder, (uint8_t)(
(((PIXEL *)dict->window.segs[seg_id].lines_end) -
((PIXEL *)dict->window.segs[seg_id].lines)) - 1));
while (ip < (PIXEL *)dict->window.segs[seg_id].lines_end) {
ENCODE_PIXEL(encoder, *ip);
ip++;
}
}
seg_id = dict->window.segs[seg_id].next;
}
if ((seg_id == NULL_IMAGE_SEG_ID) ||
(dict->window.segs[seg_id].image->id != encoder->cur_image.id)) {
return;
}
ip = (PIXEL *)dict->window.segs[seg_id].lines;
encode_copy_count(encoder, MAX_COPY - 1);
HASH_FUNC(hval, ip);
UPDATE_HASH(encoder->dict, hval, seg_id, 0);
ENCODE_PIXEL(encoder, *ip);
ip++;
ENCODE_PIXEL(encoder, *ip);
ip++;
#ifdef DEBUG_ENCODE
printf("copy, copy");
#endif
// compressing the first segment
FNAME(compress_seg)(encoder, seg_id, ip, 2);
// compressing the next segments
for (seg_id = dict->window.segs[seg_id].next;
seg_id != NULL_IMAGE_SEG_ID && (
dict->window.segs[seg_id].image->id == encoder->cur_image.id);
seg_id = dict->window.segs[seg_id].next) {
FNAME(compress_seg)(encoder, seg_id, (PIXEL *)dict->window.segs[seg_id].lines, 0);
}
}
#undef FNAME
#undef PIXEL_ID
#undef PIXEL_DIST
#undef PIXEL
#undef ENCODE_PIXEL
#undef SAME_PIXEL
#undef HASH_FUNC
#undef GET_r
#undef GET_g
#undef GET_b
#undef GET_CODE
#undef LZ_PLT
#undef LZ_RGB_ALPHA
#undef LZ_RGB16
#undef LZ_RGB24
#undef LZ_RGB32
#undef MIN_REF_ENCODE_SIZE
#undef MAX_REF_ENCODE_SIZE