Revision 2f6f5c7a vnc-encoding-tight.c

b/vnc-encoding-tight.c
26 26
 * THE SOFTWARE.
27 27
 */
28 28

  
29
#include "qemu-common.h"
30

  
31
#ifdef CONFIG_VNC_JPEG
32
#include <stdio.h>
33
#include <jpeglib.h>
34
#endif
35

  
36
#include "bswap.h"
29 37
#include "qdict.h"
30 38
#include "qint.h"
31 39
#include "vnc.h"
......
56 64
};
57 65

  
58 66
/*
67
 * Code to guess if given rectangle is suitable for smooth image
68
 * compression (by applying "gradient" filter or JPEG coder).
69
 */
70

  
71
static uint
72
tight_detect_smooth_image24(VncState *vs, int w, int h)
73
{
74
    int off;
75
    int x, y, d, dx;
76
    uint c;
77
    uint stats[256];
78
    int pixels = 0;
79
    int pix, left[3];
80
    uint errors;
81
    unsigned char *buf = vs->tight.buffer;
82

  
83
    /*
84
     * If client is big-endian, color samples begin from the second
85
     * byte (offset 1) of a 32-bit pixel value.
86
     */
87
    off = !!(vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG);
88

  
89
    memset(stats, 0, sizeof (stats));
90

  
91
    for (y = 0, x = 0; y < h && x < w;) {
92
        for (d = 0; d < h - y && d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH;
93
             d++) {
94
            for (c = 0; c < 3; c++) {
95
                left[c] = buf[((y+d)*w+x+d)*4+off+c] & 0xFF;
96
            }
97
            for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH; dx++) {
98
                for (c = 0; c < 3; c++) {
99
                    pix = buf[((y+d)*w+x+d+dx)*4+off+c] & 0xFF;
100
                    stats[abs(pix - left[c])]++;
101
                    left[c] = pix;
102
                }
103
                pixels++;
104
            }
105
        }
106
        if (w > h) {
107
            x += h;
108
            y = 0;
109
        } else {
110
            x = 0;
111
            y += w;
112
        }
113
    }
114

  
115
    /* 95% smooth or more ... */
116
    if (stats[0] * 33 / pixels >= 95) {
117
        return 0;
118
    }
119

  
120
    errors = 0;
121
    for (c = 1; c < 8; c++) {
122
        errors += stats[c] * (c * c);
123
        if (stats[c] == 0 || stats[c] > stats[c-1] * 2) {
124
            return 0;
125
        }
126
    }
127
    for (; c < 256; c++) {
128
        errors += stats[c] * (c * c);
129
    }
130
    errors /= (pixels * 3 - stats[0]);
131

  
132
    return errors;
133
}
134

  
135
#define DEFINE_DETECT_FUNCTION(bpp)                                     \
136
                                                                        \
137
    static uint                                                         \
138
    tight_detect_smooth_image##bpp(VncState *vs, int w, int h) {        \
139
        bool endian;                                                    \
140
        uint##bpp##_t pix;                                              \
141
        int max[3], shift[3];                                           \
142
        int x, y, d, dx;                                                \
143
        uint c;                                                         \
144
        uint stats[256];                                                \
145
        int pixels = 0;                                                 \
146
        int sample, sum, left[3];                                       \
147
        uint errors;                                                    \
148
        unsigned char *buf = vs->tight.buffer;                          \
149
                                                                        \
150
        endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) !=        \
151
                  (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG));     \
152
                                                                        \
153
                                                                        \
154
        max[0] = vs->clientds.pf.rmax;                                  \
155
        max[1] = vs->clientds.pf.gmax;                                  \
156
        max[2] = vs->clientds.pf.bmax;                                  \
157
        shift[0] = vs->clientds.pf.rshift;                              \
158
        shift[1] = vs->clientds.pf.gshift;                              \
159
        shift[2] = vs->clientds.pf.bshift;                              \
160
                                                                        \
161
        memset(stats, 0, sizeof(stats));                                \
162
                                                                        \
163
        y = 0, x = 0;                                                   \
164
        while (y < h && x < w) {                                        \
165
            for (d = 0; d < h - y &&                                    \
166
                     d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH; d++) {  \
167
                pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d];              \
168
                if (endian) {                                           \
169
                    pix = bswap_##bpp(pix);                             \
170
                }                                                       \
171
                for (c = 0; c < 3; c++) {                               \
172
                    left[c] = (int)(pix >> shift[c] & max[c]);          \
173
                }                                                       \
174
                for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH;       \
175
                     dx++) {                                            \
176
                    pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d+dx];       \
177
                    if (endian) {                                       \
178
                        pix = bswap_##bpp(pix);                         \
179
                    }                                                   \
180
                    sum = 0;                                            \
181
                    for (c = 0; c < 3; c++) {                           \
182
                        sample = (int)(pix >> shift[c] & max[c]);       \
183
                        sum += abs(sample - left[c]);                   \
184
                        left[c] = sample;                               \
185
                    }                                                   \
186
                    if (sum > 255) {                                    \
187
                        sum = 255;                                      \
188
                    }                                                   \
189
                    stats[sum]++;                                       \
190
                    pixels++;                                           \
191
                }                                                       \
192
            }                                                           \
193
            if (w > h) {                                                \
194
                x += h;                                                 \
195
                y = 0;                                                  \
196
            } else {                                                    \
197
                x = 0;                                                  \
198
                y += w;                                                 \
199
            }                                                           \
200
        }                                                               \
201
                                                                        \
202
        if ((stats[0] + stats[1]) * 100 / pixels >= 90) {               \
203
            return 0;                                                   \
204
        }                                                               \
205
                                                                        \
206
        errors = 0;                                                     \
207
        for (c = 1; c < 8; c++) {                                       \
208
            errors += stats[c] * (c * c);                               \
209
            if (stats[c] == 0 || stats[c] > stats[c-1] * 2) {           \
210
                return 0;                                               \
211
            }                                                           \
212
        }                                                               \
213
        for (; c < 256; c++) {                                          \
214
            errors += stats[c] * (c * c);                               \
215
        }                                                               \
216
        errors /= (pixels - stats[0]);                                  \
217
                                                                        \
218
        return errors;                                                  \
219
    }
220

  
221
DEFINE_DETECT_FUNCTION(16)
222
DEFINE_DETECT_FUNCTION(32)
223

  
224
static int
225
tight_detect_smooth_image(VncState *vs, int w, int h)
226
{
227
    uint errors;
228
    int compression = vs->tight_compression;
229
    int quality = vs->tight_quality;
230

  
231
    if (ds_get_bytes_per_pixel(vs->ds) == 1 ||
232
        vs->clientds.pf.bytes_per_pixel == 1 ||
233
        w < VNC_TIGHT_DETECT_MIN_WIDTH || h < VNC_TIGHT_DETECT_MIN_HEIGHT) {
234
        return 0;
235
    }
236

  
237
    if (vs->tight_quality != -1) {
238
        if (w * h < VNC_TIGHT_JPEG_MIN_RECT_SIZE) {
239
            return 0;
240
        }
241
    } else {
242
        if (w * h < tight_conf[compression].gradient_min_rect_size) {
243
            return 0;
244
        }
245
    }
246

  
247
    if (vs->clientds.pf.bytes_per_pixel == 4) {
248
        if (vs->tight_pixel24) {
249
            errors = tight_detect_smooth_image24(vs, w, h);
250
            if (vs->tight_quality != -1) {
251
                return (errors < tight_conf[quality].jpeg_threshold24);
252
            }
253
            return (errors < tight_conf[compression].gradient_threshold24);
254
        } else {
255
            errors = tight_detect_smooth_image32(vs, w, h);
256
        }
257
    } else {
258
        errors = tight_detect_smooth_image16(vs, w, h);
259
    }
260
    if (quality != -1) {
261
        return (errors < tight_conf[quality].jpeg_threshold);
262
    }
263
    return (errors < tight_conf[compression].gradient_threshold);
264
}
265

  
266
/*
59 267
 * Code to determine how many different colors used in rectangle.
60 268
 */
61 269

  
......
335 543
DEFINE_MONO_ENCODE_FUNCTION(32)
336 544

  
337 545
/*
546
 * ``Gradient'' filter for 24-bit color samples.
547
 * Should be called only when redMax, greenMax and blueMax are 255.
548
 * Color components assumed to be byte-aligned.
549
 */
550

  
551
static void
552
tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h)
553
{
554
    uint32_t *buf32;
555
    uint32_t pix32;
556
    int shift[3];
557
    int *prev;
558
    int here[3], upper[3], left[3], upperleft[3];
559
    int prediction;
560
    int x, y, c;
561

  
562
    buf32 = (uint32_t *)buf;
563
    memset(vs->tight_gradient.buffer, 0, w * 3 * sizeof(int));
564

  
565
    if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
566
        (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) {
567
        shift[0] = vs->clientds.pf.rshift;
568
        shift[1] = vs->clientds.pf.gshift;
569
        shift[2] = vs->clientds.pf.bshift;
570
    } else {
571
        shift[0] = 24 - vs->clientds.pf.rshift;
572
        shift[1] = 24 - vs->clientds.pf.gshift;
573
        shift[2] = 24 - vs->clientds.pf.bshift;
574
    }
575

  
576
    for (y = 0; y < h; y++) {
577
        for (c = 0; c < 3; c++) {
578
            upper[c] = 0;
579
            here[c] = 0;
580
        }
581
        prev = (int *)vs->tight_gradient.buffer;
582
        for (x = 0; x < w; x++) {
583
            pix32 = *buf32++;
584
            for (c = 0; c < 3; c++) {
585
                upperleft[c] = upper[c];
586
                left[c] = here[c];
587
                upper[c] = *prev;
588
                here[c] = (int)(pix32 >> shift[c] & 0xFF);
589
                *prev++ = here[c];
590

  
591
                prediction = left[c] + upper[c] - upperleft[c];
592
                if (prediction < 0) {
593
                    prediction = 0;
594
                } else if (prediction > 0xFF) {
595
                    prediction = 0xFF;
596
                }
597
                *buf++ = (char)(here[c] - prediction);
598
            }
599
        }
600
    }
601
}
602

  
603

  
604
/*
605
 * ``Gradient'' filter for other color depths.
606
 */
607

  
608
#define DEFINE_GRADIENT_FILTER_FUNCTION(bpp)                            \
609
                                                                        \
610
    static void                                                         \
611
    tight_filter_gradient##bpp(VncState *vs, uint##bpp##_t *buf,        \
612
                               int w, int h) {                          \
613
        uint##bpp##_t pix, diff;                                        \
614
        bool endian;                                                    \
615
        int *prev;                                                      \
616
        int max[3], shift[3];                                           \
617
        int here[3], upper[3], left[3], upperleft[3];                   \
618
        int prediction;                                                 \
619
        int x, y, c;                                                    \
620
                                                                        \
621
        memset (vs->tight_gradient.buffer, 0, w * 3 * sizeof(int));     \
622
                                                                        \
623
        endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) !=        \
624
                  (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG));     \
625
                                                                        \
626
        max[0] = vs->clientds.pf.rmax;                                  \
627
        max[1] = vs->clientds.pf.gmax;                                  \
628
        max[2] = vs->clientds.pf.bmax;                                  \
629
        shift[0] = vs->clientds.pf.rshift;                              \
630
        shift[1] = vs->clientds.pf.gshift;                              \
631
        shift[2] = vs->clientds.pf.bshift;                              \
632
                                                                        \
633
        for (y = 0; y < h; y++) {                                       \
634
            for (c = 0; c < 3; c++) {                                   \
635
                upper[c] = 0;                                           \
636
                here[c] = 0;                                            \
637
            }                                                           \
638
            prev = (int *)vs->tight_gradient.buffer;                    \
639
            for (x = 0; x < w; x++) {                                   \
640
                pix = *buf;                                             \
641
                if (endian) {                                           \
642
                    pix = bswap_##bpp(pix);                             \
643
                }                                                       \
644
                diff = 0;                                               \
645
                for (c = 0; c < 3; c++) {                               \
646
                    upperleft[c] = upper[c];                            \
647
                    left[c] = here[c];                                  \
648
                    upper[c] = *prev;                                   \
649
                    here[c] = (int)(pix >> shift[c] & max[c]);          \
650
                    *prev++ = here[c];                                  \
651
                                                                        \
652
                    prediction = left[c] + upper[c] - upperleft[c];     \
653
                    if (prediction < 0) {                               \
654
                        prediction = 0;                                 \
655
                    } else if (prediction > max[c]) {                   \
656
                        prediction = max[c];                            \
657
                    }                                                   \
658
                    diff |= ((here[c] - prediction) & max[c])           \
659
                        << shift[c];                                    \
660
                }                                                       \
661
                if (endian) {                                           \
662
                    diff = bswap_##bpp(diff);                           \
663
                }                                                       \
664
                *buf++ = diff;                                          \
665
            }                                                           \
666
        }                                                               \
667
    }
668

  
669
DEFINE_GRADIENT_FILTER_FUNCTION(16)
670
DEFINE_GRADIENT_FILTER_FUNCTION(32)
671

  
672
/*
338 673
 * Check if a rectangle is all of the same color. If needSameColor is
339 674
 * set to non-zero, then also check that its color equals to the
340 675
 * *colorPtr value. The result is 1 if the test is successfull, and in
......
702 1037
    }
703 1038
}
704 1039

  
1040
static bool send_gradient_rect(VncState *vs, int w, int h)
1041
{
1042
    int stream = 3;
1043
    int level = tight_conf[vs->tight_compression].gradient_zlib_level;
1044
    size_t bytes;
1045

  
1046
    if (vs->clientds.pf.bytes_per_pixel == 1)
1047
        return send_full_color_rect(vs, w, h);
1048

  
1049
    vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
1050
    vnc_write_u8(vs, VNC_TIGHT_FILTER_GRADIENT);
1051

  
1052
    buffer_reserve(&vs->tight_gradient, w * 3 * sizeof (int));
1053

  
1054
    if (vs->tight_pixel24) {
1055
        tight_filter_gradient24(vs, vs->tight.buffer, w, h);
1056
        bytes = 3;
1057
    } else if (vs->clientds.pf.bytes_per_pixel == 4) {
1058
        tight_filter_gradient32(vs, (uint32_t *)vs->tight.buffer, w, h);
1059
        bytes = 4;
1060
    } else {
1061
        tight_filter_gradient16(vs, (uint16_t *)vs->tight.buffer, w, h);
1062
        bytes = 2;
1063
    }
1064

  
1065
    buffer_reset(&vs->tight_gradient);
1066

  
1067
    bytes = w * h * bytes;
1068
    vs->tight.offset = bytes;
1069

  
1070
    bytes = tight_compress_data(vs, stream, bytes,
1071
                                level, Z_FILTERED);
1072
    return (bytes >= 0);
1073
}
1074

  
705 1075
static int send_palette_rect(VncState *vs, int w, int h, struct QDict *palette)
706 1076
{
707 1077
    int stream = 2;
......
756 1126
    return (bytes >= 0);
757 1127
}
758 1128

  
1129
/*
1130
 * JPEG compression stuff.
1131
 */
1132
#ifdef CONFIG_VNC_JPEG
1133
static void jpeg_prepare_row24(VncState *vs, uint8_t *dst, int x, int y,
1134
                                     int count)
1135
{
1136
    VncDisplay *vd = vs->vd;
1137
    uint32_t *fbptr;
1138
    uint32_t pix;
1139

  
1140
    fbptr = (uint32_t *)(vd->server->data + y * ds_get_linesize(vs->ds) +
1141
                         x * ds_get_bytes_per_pixel(vs->ds));
1142

  
1143
    while (count--) {
1144
        pix = *fbptr++;
1145
        *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.rshift);
1146
        *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.gshift);
1147
        *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.bshift);
1148
    }
1149
}
1150

  
1151
#define DEFINE_JPEG_GET_ROW_FUNCTION(bpp)                               \
1152
                                                                        \
1153
    static void                                                         \
1154
    jpeg_prepare_row##bpp(VncState *vs, uint8_t *dst,                   \
1155
                                int x, int y, int count)                \
1156
    {                                                                   \
1157
        VncDisplay *vd = vs->vd;                                        \
1158
        uint##bpp##_t *fbptr;                                           \
1159
        uint##bpp##_t pix;                                              \
1160
        int r, g, b;                                                    \
1161
                                                                        \
1162
        fbptr = (uint##bpp##_t *)                                       \
1163
            (vd->server->data + y * ds_get_linesize(vs->ds) +           \
1164
             x * ds_get_bytes_per_pixel(vs->ds));                       \
1165
                                                                        \
1166
        while (count--) {                                               \
1167
            pix = *fbptr++;                                             \
1168
                                                                        \
1169
            r = (int)((pix >> vs->ds->surface->pf.rshift)               \
1170
                      & vs->ds->surface->pf.rmax);                      \
1171
            g = (int)((pix >> vs->ds->surface->pf.gshift)               \
1172
                      & vs->ds->surface->pf.gmax);                      \
1173
            b = (int)((pix >> vs->ds->surface->pf.bshift)               \
1174
                      & vs->ds->surface->pf.bmax);                      \
1175
                                                                        \
1176
            *dst++ = (uint8_t)((r * 255 + vs->ds->surface->pf.rmax / 2) \
1177
                               / vs->ds->surface->pf.rmax);             \
1178
            *dst++ = (uint8_t)((g * 255 + vs->ds->surface->pf.gmax / 2) \
1179
                               / vs->ds->surface->pf.gmax);             \
1180
            *dst++ = (uint8_t)((b * 255 + vs->ds->surface->pf.bmax / 2) \
1181
                               / vs->ds->surface->pf.bmax);             \
1182
        }                                                               \
1183
    }
1184

  
1185
DEFINE_JPEG_GET_ROW_FUNCTION(16)
1186
DEFINE_JPEG_GET_ROW_FUNCTION(32)
1187

  
1188
static void jpeg_prepare_row(VncState *vs, uint8_t *dst, int x, int y,
1189
                                       int count)
1190
{
1191
    if (vs->tight_pixel24)
1192
        jpeg_prepare_row24(vs, dst, x, y, count);
1193
    else if (ds_get_bytes_per_pixel(vs->ds) == 4)
1194
        jpeg_prepare_row32(vs, dst, x, y, count);
1195
    else
1196
        jpeg_prepare_row16(vs, dst, x, y, count);
1197
}
1198

  
1199
/*
1200
 * Destination manager implementation for JPEG library.
1201
 */
1202

  
1203
/* This is called once per encoding */
1204
static void jpeg_init_destination(j_compress_ptr cinfo)
1205
{
1206
    VncState *vs = cinfo->client_data;
1207
    Buffer *buffer = &vs->tight_jpeg;
1208

  
1209
    cinfo->dest->next_output_byte = (JOCTET *)buffer->buffer + buffer->offset;
1210
    cinfo->dest->free_in_buffer = (size_t)(buffer->capacity - buffer->offset);
1211
}
1212

  
1213
/* This is called when we ran out of buffer (shouldn't happen!) */
1214
static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo)
1215
{
1216
    VncState *vs = cinfo->client_data;
1217
    Buffer *buffer = &vs->tight_jpeg;
1218

  
1219
    buffer->offset = buffer->capacity;
1220
    buffer_reserve(buffer, 2048);
1221
    jpeg_init_destination(cinfo);
1222
    return TRUE;
1223
}
1224

  
1225
/* This is called when we are done processing data */
1226
static void jpeg_term_destination(j_compress_ptr cinfo)
1227
{
1228
    VncState *vs = cinfo->client_data;
1229
    Buffer *buffer = &vs->tight_jpeg;
1230

  
1231
    buffer->offset = buffer->capacity - cinfo->dest->free_in_buffer;
1232
}
1233

  
1234
static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)
1235
{
1236
    struct jpeg_compress_struct cinfo;
1237
    struct jpeg_error_mgr jerr;
1238
    struct jpeg_destination_mgr manager;
1239
    JSAMPROW row[1];
1240
    uint8_t *buf;
1241
    int dy;
1242

  
1243
    if (ds_get_bytes_per_pixel(vs->ds) == 1)
1244
        return send_full_color_rect(vs, w, h);
1245

  
1246
    buf = qemu_malloc(w * 3);
1247
    row[0] = buf;
1248
    buffer_reserve(&vs->tight_jpeg, 2048);
1249

  
1250
    cinfo.err = jpeg_std_error(&jerr);
1251
    jpeg_create_compress(&cinfo);
1252

  
1253
    cinfo.client_data = vs;
1254
    cinfo.image_width = w;
1255
    cinfo.image_height = h;
1256
    cinfo.input_components = 3;
1257
    cinfo.in_color_space = JCS_RGB;
1258

  
1259
    jpeg_set_defaults(&cinfo);
1260
    jpeg_set_quality(&cinfo, quality, true);
1261

  
1262
    manager.init_destination = jpeg_init_destination;
1263
    manager.empty_output_buffer = jpeg_empty_output_buffer;
1264
    manager.term_destination = jpeg_term_destination;
1265
    cinfo.dest = &manager;
1266

  
1267
    jpeg_start_compress(&cinfo, true);
1268

  
1269
    for (dy = 0; dy < h; dy++) {
1270
        jpeg_prepare_row(vs, buf, x, y + dy, w);
1271
        jpeg_write_scanlines(&cinfo, row, 1);
1272
    }
1273

  
1274
    jpeg_finish_compress(&cinfo);
1275
    jpeg_destroy_compress(&cinfo);
1276

  
1277
    vnc_write_u8(vs, VNC_TIGHT_JPEG << 4);
1278

  
1279
    tight_send_compact_size(vs, vs->tight_jpeg.offset);
1280
    vnc_write(vs, vs->tight_jpeg.buffer, vs->tight_jpeg.offset);
1281
    buffer_reset(&vs->tight_jpeg);
1282

  
1283
    return 1;
1284
}
1285
#endif /* CONFIG_VNC_JPEG */
1286

  
759 1287
static void vnc_tight_start(VncState *vs)
760 1288
{
761 1289
    buffer_reset(&vs->tight);
......
788 1316
    colors = tight_fill_palette(vs, x, y, w * h, &fg, &bg, &palette);
789 1317

  
790 1318
    if (colors == 0) {
791
        ret = send_full_color_rect(vs, w, h);
1319
        if (tight_detect_smooth_image(vs, w, h)) {
1320
            if (vs->tight_quality == -1) {
1321
                ret = send_gradient_rect(vs, w, h);
1322
            } else {
1323
#ifdef CONFIG_VNC_JPEG
1324
                int quality = tight_conf[vs->tight_quality].jpeg_quality;
1325

  
1326
                ret = send_jpeg_rect(vs, x, y, w, h, quality);
1327
#else
1328
                ret = send_full_color_rect(vs, w, h);
1329
#endif
1330
            }
1331
        } else {
1332
            ret = send_full_color_rect(vs, w, h);
1333
        }
792 1334
    } else if (colors == 1) {
793 1335
        ret = send_solid_rect(vs);
794 1336
    } else if (colors == 2) {
795 1337
        ret = send_mono_rect(vs, w, h, bg, fg);
796 1338
    } else if (colors <= 256) {
1339
#ifdef CONFIG_VNC_JPEG
1340
        if (colors > 96 && vs->tight_quality != -1 && vs->tight_quality <= 3 &&
1341
            tight_detect_smooth_image(vs, w, h)) {
1342
            int quality = tight_conf[vs->tight_quality].jpeg_quality;
1343

  
1344
            ret = send_jpeg_rect(vs, x, y, w, h, quality);
1345
        } else {
1346
            ret = send_palette_rect(vs, w, h, palette);
1347
        }
1348
#else
797 1349
        ret = send_palette_rect(vs, w, h, palette);
1350
#endif
798 1351
    }
799 1352
    QDECREF(palette);
800 1353
    return ret;
......
956 1509

  
957 1510
    buffer_free(&vs->tight);
958 1511
    buffer_free(&vs->tight_zlib);
1512
    buffer_free(&vs->tight_gradient);
1513
#ifdef CONFIG_VNC_JPEG
1514
    buffer_free(&vs->tight_jpeg);
1515
#endif
959 1516
}

Also available in: Unified diff