blob: 71f076e606d07acb0812a4358f49765ecbad1195 [file] [log] [blame]
Peter Åstrand9b0809c2005-02-10 15:13:38 +00001/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19//
20// tightEncode.h - Tight encoding function.
21//
22// This file is #included after having set the following macros:
23// BPP - 8, 16 or 32
24// EXTRA_ARGS - optional extra arguments
25// GET_IMAGE_INTO_BUF - gets a rectangle of pixel data into a buffer
26//
27
28#include <rdr/OutStream.h>
29#include <rdr/ZlibOutStream.h>
30#include <assert.h>
31
32namespace rfb {
33
34// CONCAT2E concatenates its arguments, expanding them if they are macros
35
36#ifndef CONCAT2E
37#define CONCAT2(a,b) a##b
38#define CONCAT2E(a,b) CONCAT2(a,b)
39#endif
40
41#define PIXEL_T rdr::CONCAT2E(U,BPP)
42#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP)
43#define TIGHT_ENCODE CONCAT2E(tightEncode,BPP)
44#define SWAP_PIXEL CONCAT2E(SWAP,BPP)
45#define HASH_FUNCTION CONCAT2E(HASH_FUNC,BPP)
46#define PACK_PIXELS CONCAT2E(packPixels,BPP)
47#define DETECT_SMOOTH_IMAGE CONCAT2E(detectSmoothImage,BPP)
48#define ENCODE_SOLID_RECT CONCAT2E(encodeSolidRect,BPP)
49#define ENCODE_FULLCOLOR_RECT CONCAT2E(encodeFullColorRect,BPP)
50#define ENCODE_MONO_RECT CONCAT2E(encodeMonoRect,BPP)
51#define ENCODE_INDEXED_RECT CONCAT2E(encodeIndexedRect,BPP)
52#define PREPARE_JPEG_ROW CONCAT2E(prepareJpegRow,BPP)
53#define ENCODE_JPEG_RECT CONCAT2E(encodeJpegRect,BPP)
54#define FILL_PALETTE CONCAT2E(fillPalette,BPP)
55
56#ifndef TIGHT_ONCE
57#define TIGHT_ONCE
58
59//
60// C-style structures to store palette entries and compression paramentes.
61// Such code probably should be converted into C++ classes.
62//
63
64struct TIGHT_COLOR_LIST {
65 TIGHT_COLOR_LIST *next;
66 int idx;
67 rdr::U32 rgb;
68};
69
70struct TIGHT_PALETTE_ENTRY {
71 TIGHT_COLOR_LIST *listNode;
72 int numPixels;
73};
74
75struct TIGHT_PALETTE {
76 TIGHT_PALETTE_ENTRY entry[256];
77 TIGHT_COLOR_LIST *hash[256];
78 TIGHT_COLOR_LIST list[256];
79};
80
81// FIXME: Is it really a good idea to use static variables for this?
82static int s_endianMismatch; // local/remote formats differ in byte order
83static bool s_pack24; // use 24-bit packing for 32-bit pixels
84static int s_rs, s_gs, s_bs; // shifts for 24-bit pixel conversion
85
86// FIXME: Make a separate class for palette operations.
87static int s_palMaxColors, s_palNumColors;
88static rdr::U32 s_monoBackground, s_monoForeground;
89static TIGHT_PALETTE s_palette;
90
91//
92// Swapping bytes in pixels.
93// FIXME: Use a sort of ImageGetter that does not convert pixel format?
94//
95
96#ifndef SWAP16
97#define SWAP16(n) ((((n) & 0xff) << 8) | (((n) >> 8) & 0xff))
98#endif
99#ifndef SWAP32
100#define SWAP32(n) (((n) >> 24) | (((n) & 0x00ff0000) >> 8) | \
101 (((n) & 0x0000ff00) << 8) | ((n) << 24))
102#endif
103
104//
105// Functions to operate on palette structures.
106//
107
108#define HASH_FUNC16(rgb) ((int)(((rgb >> 8) + rgb) & 0xFF))
109#define HASH_FUNC32(rgb) ((int)(((rgb >> 16) + (rgb >> 8)) & 0xFF))
110
111static void paletteReset(void)
112{
113 s_palNumColors = 0;
114 memset(s_palette.hash, 0, 256 * sizeof(TIGHT_COLOR_LIST *));
115}
116
117static int paletteInsert(rdr::U32 rgb, int numPixels, int bpp)
118{
119 TIGHT_COLOR_LIST *pnode;
120 TIGHT_COLOR_LIST *prev_pnode = NULL;
121 int hash_key, idx, new_idx, count;
122
123 hash_key = (bpp == 16) ? HASH_FUNC16(rgb) : HASH_FUNC32(rgb);
124
125 pnode = s_palette.hash[hash_key];
126
127 while (pnode != NULL) {
128 if (pnode->rgb == rgb) {
129 // Such palette entry already exists.
130 new_idx = idx = pnode->idx;
131 count = s_palette.entry[idx].numPixels + numPixels;
132 if (new_idx && s_palette.entry[new_idx-1].numPixels < count) {
133 do {
134 s_palette.entry[new_idx] = s_palette.entry[new_idx-1];
135 s_palette.entry[new_idx].listNode->idx = new_idx;
136 new_idx--;
137 }
138 while (new_idx &&
139 s_palette.entry[new_idx-1].numPixels < count);
140 s_palette.entry[new_idx].listNode = pnode;
141 pnode->idx = new_idx;
142 }
143 s_palette.entry[new_idx].numPixels = count;
144 return s_palNumColors;
145 }
146 prev_pnode = pnode;
147 pnode = pnode->next;
148 }
149
150 // Check if palette is full.
151 if ( s_palNumColors == 256 || s_palNumColors == s_palMaxColors ) {
152 s_palNumColors = 0;
153 return 0;
154 }
155
156 // Move palette entries with lesser pixel counts.
157 for ( idx = s_palNumColors;
158 idx > 0 && s_palette.entry[idx-1].numPixels < numPixels;
159 idx-- ) {
160 s_palette.entry[idx] = s_palette.entry[idx-1];
161 s_palette.entry[idx].listNode->idx = idx;
162 }
163
164 // Add new palette entry into the freed slot.
165 pnode = &s_palette.list[s_palNumColors];
166 if (prev_pnode != NULL) {
167 prev_pnode->next = pnode;
168 } else {
169 s_palette.hash[hash_key] = pnode;
170 }
171 pnode->next = NULL;
172 pnode->idx = idx;
173 pnode->rgb = rgb;
174 s_palette.entry[idx].listNode = pnode;
175 s_palette.entry[idx].numPixels = numPixels;
176
177 return (++s_palNumColors);
178}
179
180//
181// Compress the data (but do not perform actual compression if the data
182// size is less than TIGHT_MIN_TO_COMPRESS bytes.
183//
184
185static void compressData(rdr::OutStream *os, rdr::ZlibOutStream *zos,
186 const void *buf, unsigned int length, int zlibLevel)
187{
188 if (length < TIGHT_MIN_TO_COMPRESS) {
189 os->writeBytes(buf, length);
190 } else {
191 // FIXME: Using a temporary MemOutStream may be not efficient.
192 // Maybe use the same static object used in the JPEG coder?
193 rdr::MemOutStream mem_os;
194 zos->setUnderlying(&mem_os);
Constantin Kaplinskya22779b2005-09-28 13:11:38 +0000195 zos->setCompressionLevel(zlibLevel);
Peter Åstrand9b0809c2005-02-10 15:13:38 +0000196 zos->writeBytes(buf, length);
197 zos->flush();
198 os->writeCompactLength(mem_os.length());
199 os->writeBytes(mem_os.data(), mem_os.length());
200 }
201}
202
203//
204// Destination manager implementation for the JPEG library.
205// FIXME: Implement JPEG compression in new rdr::JpegOutStream class.
206//
207
208// FIXME: Keeping a MemOutStream instance may consume too much space.
209rdr::MemOutStream s_jpeg_os;
210
211static struct jpeg_destination_mgr s_jpegDstManager;
212static JOCTET *s_jpegDstBuffer;
213static size_t s_jpegDstBufferLen;
214
215static void
216JpegInitDestination(j_compress_ptr cinfo)
217{
218 s_jpeg_os.clear();
219 s_jpegDstManager.next_output_byte = s_jpegDstBuffer;
220 s_jpegDstManager.free_in_buffer = s_jpegDstBufferLen;
221}
222
223static boolean
224JpegEmptyOutputBuffer(j_compress_ptr cinfo)
225{
226 s_jpeg_os.writeBytes(s_jpegDstBuffer, s_jpegDstBufferLen);
227 s_jpegDstManager.next_output_byte = s_jpegDstBuffer;
228 s_jpegDstManager.free_in_buffer = s_jpegDstBufferLen;
229
230 return TRUE;
231}
232
233static void
234JpegTermDestination(j_compress_ptr cinfo)
235{
236 int dataLen = s_jpegDstBufferLen - s_jpegDstManager.free_in_buffer;
237 s_jpeg_os.writeBytes(s_jpegDstBuffer, dataLen);
238}
239
240static void
241JpegSetDstManager(j_compress_ptr cinfo, JOCTET *buf, size_t buflen)
242{
243 s_jpegDstBuffer = buf;
244 s_jpegDstBufferLen = buflen;
245 s_jpegDstManager.init_destination = JpegInitDestination;
246 s_jpegDstManager.empty_output_buffer = JpegEmptyOutputBuffer;
247 s_jpegDstManager.term_destination = JpegTermDestination;
248 cinfo->dest = &s_jpegDstManager;
249}
250
251#endif // #ifndef TIGHT_ONCE
252
253static void ENCODE_SOLID_RECT (rdr::OutStream *os,
254 PIXEL_T *buf);
255static void ENCODE_FULLCOLOR_RECT (rdr::OutStream *os, rdr::ZlibOutStream zos[4],
256 PIXEL_T *buf, const Rect& r);
257static void ENCODE_MONO_RECT (rdr::OutStream *os, rdr::ZlibOutStream zos[4],
258 PIXEL_T *buf, const Rect& r);
259#if (BPP != 8)
260static void ENCODE_INDEXED_RECT (rdr::OutStream *os, rdr::ZlibOutStream zos[4],
261 PIXEL_T *buf, const Rect& r);
262static void ENCODE_JPEG_RECT (rdr::OutStream *os,
263 PIXEL_T *buf, const PixelFormat& pf, const Rect& r);
264#endif
265
266static void FILL_PALETTE (PIXEL_T *data, int count);
267
268//
269// Convert 32-bit color samples into 24-bit colors, in place.
270// Performs packing only when redMax, greenMax and blueMax are all 255.
271// Color components are assumed to be byte-aligned.
272//
273
274static inline unsigned int PACK_PIXELS (PIXEL_T *buf, unsigned int count)
275{
276#if (BPP != 32)
277 return count * sizeof(PIXEL_T);
278#else
279 if (!s_pack24)
280 return count * sizeof(PIXEL_T);
281
282 rdr::U32 pix;
283 rdr::U8 *dst = (rdr::U8 *)buf;
284 for (unsigned int i = 0; i < count; i++) {
285 pix = *buf++;
286 *dst++ = (rdr::U8)(pix >> s_rs);
287 *dst++ = (rdr::U8)(pix >> s_gs);
288 *dst++ = (rdr::U8)(pix >> s_bs);
289 }
290 return count * 3;
291#endif
292}
293
294//
295// Function to guess if a given rectangle is suitable for JPEG compression.
296// Returns true is it looks like a good data for JPEG, false otherwise.
297//
298// FIXME: Scan the image and determine is it really good for JPEG.
299//
300
301#if (BPP != 8)
302static bool DETECT_SMOOTH_IMAGE (PIXEL_T *buf, const Rect& r)
303{
304 if (r.width() < TIGHT_DETECT_MIN_WIDTH ||
305 r.height() < TIGHT_DETECT_MIN_HEIGHT ||
306 r.area() < TIGHT_JPEG_MIN_RECT_SIZE ||
307 s_pjconf == NULL)
308 return 0;
309
Peter Åstrand9b0809c2005-02-10 15:13:38 +0000310 return 1;
311}
312#endif
313
314// FIXME: Split rectangles into smaller ones!
315// FIXME: Compare encoder code with 1.3 before the final version.
316
317//
318// Main function of the Tight encoder
319//
320
321void TIGHT_ENCODE (const Rect& r, rdr::OutStream *os,
322 rdr::ZlibOutStream zos[4], void* buf, ConnParams* cp
323#ifdef EXTRA_ARGS
324 , EXTRA_ARGS
325#endif
326 )
327{
328 const PixelFormat& pf = cp->pf();
329 GET_IMAGE_INTO_BUF(r, buf);
330 PIXEL_T* pixels = (PIXEL_T*)buf;
331
332#if (BPP != 8)
333 union {
334 rdr::U32 value32;
335 rdr::U8 test;
336 } littleEndian;
337 littleEndian.value32 = 1;
338 s_endianMismatch = (littleEndian.test != !pf.bigEndian);
339#endif
340
341#if (BPP == 32)
342 // Check if it's necessary to pack 24-bit pixels, and
343 // compute appropriate shift values if necessary.
344 s_pack24 = (pf.depth == 24 && pf.redMax == 0xFF &&
345 pf.greenMax == 0xFF && pf.blueMax == 0xFF);
346 if (s_pack24) {
347 if (!s_endianMismatch) {
348 s_rs = pf.redShift;
349 s_gs = pf.greenShift;
350 s_bs = pf.blueShift;
351 } else {
352 s_rs = 24 - pf.redShift;
353 s_gs = 24 - pf.greenShift;
354 s_bs = 24 - pf.blueShift;
355 }
356 }
357#endif
358
359 s_palMaxColors = r.area() / s_pconf->idxMaxColorsDivisor;
360 if (s_palMaxColors < 2 && r.area() >= s_pconf->monoMinRectSize) {
361 s_palMaxColors = 2;
362 }
Constantin Kaplinsky95ff53a2005-09-28 15:55:49 +0000363 // FIXME: Temporary limitation for switching to JPEG earlier.
364 if (s_palMaxColors > 96 && s_pjconf != NULL) {
365 s_palMaxColors = 96;
366 }
367
Peter Åstrand9b0809c2005-02-10 15:13:38 +0000368 FILL_PALETTE(pixels, r.area());
369
370 switch (s_palNumColors) {
371 case 0:
372 // Truecolor image
373#if (BPP != 8)
374 if (s_pjconf != NULL && DETECT_SMOOTH_IMAGE(pixels, r)) {
375 ENCODE_JPEG_RECT(os, pixels, pf, r);
376 break;
377 }
378#endif
379 ENCODE_FULLCOLOR_RECT(os, zos, pixels, r);
380 break;
381 case 1:
382 // Solid rectangle
383 ENCODE_SOLID_RECT(os, pixels);
384 break;
385 case 2:
386 // Two-color rectangle
387 ENCODE_MONO_RECT(os, zos, pixels, r);
388 break;
389#if (BPP != 8)
390 default:
391 // Up to 256 different colors
Constantin Kaplinsky95ff53a2005-09-28 15:55:49 +0000392 ENCODE_INDEXED_RECT(os, zos, pixels, r);
Peter Åstrand9b0809c2005-02-10 15:13:38 +0000393#endif
394 }
395}
396
397//
398// Subencoding implementations.
399//
400
401static void ENCODE_SOLID_RECT (rdr::OutStream *os, PIXEL_T *buf)
402{
403 os->writeU8(0x08 << 4);
404
405 int length = PACK_PIXELS(buf, 1);
406 os->writeBytes(buf, length);
407}
408
409static void ENCODE_FULLCOLOR_RECT (rdr::OutStream *os, rdr::ZlibOutStream zos[4],
410 PIXEL_T *buf, const Rect& r)
411{
412 const int streamId = 0;
413 os->writeU8(streamId << 4);
414
415 int length = PACK_PIXELS(buf, r.area());
416 compressData(os, &zos[streamId], buf, length, s_pconf->rawZlibLevel);
417}
418
419static void ENCODE_MONO_RECT (rdr::OutStream *os, rdr::ZlibOutStream zos[4],
420 PIXEL_T *buf, const Rect& r)
421{
422 const int streamId = 1;
423 os->writeU8((streamId | 0x04) << 4);
424 os->writeU8(0x01);
425
426 // Write the palette
427 PIXEL_T pal[2] = { (PIXEL_T)s_monoBackground, (PIXEL_T)s_monoForeground };
428 os->writeU8(1);
429 os->writeBytes(pal, PACK_PIXELS(pal, 2));
430
431 // Encode the data in-place
432 PIXEL_T *src = buf;
433 rdr::U8 *dst = (rdr::U8 *)buf;
434 int w = r.width();
435 int h = r.height();
436 PIXEL_T bg;
437 unsigned int value, mask;
438 int aligned_width;
439 int x, y, bg_bits;
440
441 bg = (PIXEL_T) s_monoBackground;
442 aligned_width = w - w % 8;
443
444 for (y = 0; y < h; y++) {
445 for (x = 0; x < aligned_width; x += 8) {
446 for (bg_bits = 0; bg_bits < 8; bg_bits++) {
447 if (*src++ != bg)
448 break;
449 }
450 if (bg_bits == 8) {
451 *dst++ = 0;
452 continue;
453 }
454 mask = 0x80 >> bg_bits;
455 value = mask;
456 for (bg_bits++; bg_bits < 8; bg_bits++) {
457 mask >>= 1;
458 if (*src++ != bg) {
459 value |= mask;
460 }
461 }
462 *dst++ = (rdr::U8)value;
463 }
464
465 mask = 0x80;
466 value = 0;
467 if (x >= w)
468 continue;
469
470 for (; x < w; x++) {
471 if (*src++ != bg) {
472 value |= mask;
473 }
474 mask >>= 1;
475 }
476 *dst++ = (rdr::U8)value;
477 }
478
479 // Write the data
480 int length = (w + 7) / 8;
481 length *= h;
482 compressData(os, &zos[streamId], buf, length, s_pconf->monoZlibLevel);
483}
484
485#if (BPP != 8)
486static void ENCODE_INDEXED_RECT (rdr::OutStream *os, rdr::ZlibOutStream zos[4],
487 PIXEL_T *buf, const Rect& r)
488{
489 const int streamId = 2;
490 os->writeU8((streamId | 0x04) << 4);
491 os->writeU8(0x01);
492
493 // Write the palette
494 {
495 PIXEL_T pal[256];
496 for (int i = 0; i < s_palNumColors; i++)
497 pal[i] = (PIXEL_T)s_palette.entry[i].listNode->rgb;
498 os->writeU8((rdr::U8)(s_palNumColors - 1));
499 os->writeBytes(pal, PACK_PIXELS(pal, s_palNumColors));
500 }
501
502 // Encode data in-place
503 PIXEL_T *src = buf;
504 rdr::U8 *dst = (rdr::U8 *)buf;
505 int count = r.area();
506 PIXEL_T rgb;
507 TIGHT_COLOR_LIST *pnode;
508 int rep = 0;
509
510 while (count--) {
511 rgb = *src++;
512 while (count && *src == rgb) {
513 rep++, src++, count--;
514 }
515 pnode = s_palette.hash[HASH_FUNCTION(rgb)];
516 while (pnode != NULL) {
517 if ((PIXEL_T)pnode->rgb == rgb) {
518 *dst++ = (rdr::U8)pnode->idx;
519 while (rep) {
520 *dst++ = (rdr::U8)pnode->idx;
521 rep--;
522 }
523 break;
524 }
525 pnode = pnode->next;
526 }
527 }
528
529 // Write the data
530 compressData(os, &zos[streamId], buf, r.area(), s_pconf->idxZlibLevel);
531}
532#endif // #if (BPP != 8)
533
534//
535// JPEG compression.
536//
537
538#if (BPP != 8)
539static void PREPARE_JPEG_ROW (PIXEL_T *src, const PixelFormat& pf,
540 rdr::U8 *dst, int count)
541{
542 // FIXME: Add a version of this function optimized for 24-bit colors?
543 PIXEL_T pix;
544 while (count--) {
545 pix = *src++;
546 if (s_endianMismatch)
547 pix = SWAP_PIXEL(pix);
548 *dst++ = (rdr::U8)((pix >> pf.redShift & pf.redMax) * 255 / pf.redMax);
549 *dst++ = (rdr::U8)((pix >> pf.greenShift & pf.greenMax) * 255 / pf.greenMax);
550 *dst++ = (rdr::U8)((pix >> pf.blueShift & pf.blueMax) * 255 / pf.blueMax);
551 }
552}
553#endif // #if (BPP != 8)
554
555#if (BPP != 8)
556static void ENCODE_JPEG_RECT (rdr::OutStream *os, PIXEL_T *buf,
557 const PixelFormat& pf, const Rect& r)
558{
559 int w = r.width();
560 int h = r.height();
561
562 struct jpeg_compress_struct cinfo;
563 struct jpeg_error_mgr jerr;
564
565 // FIXME: Make srcBuf[] and/or dstBuf[] static?
566 rdr::U8 *srcBuf = new rdr::U8[w * 3];
567 JSAMPROW rowPointer[1];
568 rowPointer[0] = (JSAMPROW)srcBuf;
569
570 cinfo.err = jpeg_std_error(&jerr);
571 jpeg_create_compress(&cinfo);
572
573 cinfo.image_width = w;
574 cinfo.image_height = h;
575 cinfo.input_components = 3;
576 cinfo.in_color_space = JCS_RGB;
577
578 jpeg_set_defaults(&cinfo);
579 jpeg_set_quality(&cinfo, s_pjconf->jpegQuality, TRUE);
580
581 rdr::U8 *dstBuf = new rdr::U8[2048];
582 JpegSetDstManager(&cinfo, dstBuf, 2048);
583
584 jpeg_start_compress(&cinfo, TRUE);
585 for (int dy = 0; dy < h; dy++) {
586 PREPARE_JPEG_ROW(&buf[dy * w], pf, srcBuf, w);
587 jpeg_write_scanlines(&cinfo, rowPointer, 1);
588 }
589 jpeg_finish_compress(&cinfo);
590 jpeg_destroy_compress(&cinfo);
591
592 delete[] srcBuf;
593 delete[] dstBuf;
594
595 os->writeU8(0x09 << 4);
596 os->writeCompactLength(s_jpeg_os.length());
597 os->writeBytes(s_jpeg_os.data(), s_jpeg_os.length());
598}
599#endif // #if (BPP != 8)
600
601//
602// Determine the number of colors in the rectangle, and fill in the palette.
603//
604
605#if (BPP == 8)
606static void FILL_PALETTE (PIXEL_T *data, int count)
607{
608 PIXEL_T c0, c1;
609 int i, n0, n1;
610
611 s_palNumColors = 0;
612
613 c0 = data[0];
614 for (i = 1; i < count && data[i] == c0; i++);
615 if (i == count) {
616 s_palNumColors = 1;
617 return; // Solid rectangle
618 }
619
620 if (s_palMaxColors < 2)
621 return;
622
623 n0 = i;
624 c1 = data[i];
625 n1 = 0;
626 for (i++; i < count; i++) {
627 if (data[i] == c0) {
628 n0++;
629 } else if (data[i] == c1) {
630 n1++;
631 } else
632 break;
633 }
634 if (i == count) {
635 if (n0 > n1) {
636 s_monoBackground = (rdr::U32)c0;
637 s_monoForeground = (rdr::U32)c1;
638 } else {
639 s_monoBackground = (rdr::U32)c1;
640 s_monoForeground = (rdr::U32)c0;
641 }
642 s_palNumColors = 2; // Two colors
643 }
644}
645#else // (BPP != 8)
646static void FILL_PALETTE (PIXEL_T *data, int count)
647{
648 PIXEL_T c0, c1, ci = 0;
649 int i, n0, n1, ni;
650
651 c0 = data[0];
652 for (i = 1; i < count && data[i] == c0; i++);
653 if (i >= count) {
654 s_palNumColors = 1; // Solid rectangle
655 return;
656 }
657
658 if (s_palMaxColors < 2) {
659 s_palNumColors = 0; // Full-color format preferred
660 return;
661 }
662
663 n0 = i;
664 c1 = data[i];
665 n1 = 0;
666 for (i++; i < count; i++) {
667 ci = data[i];
668 if (ci == c0) {
669 n0++;
670 } else if (ci == c1) {
671 n1++;
672 } else
673 break;
674 }
675 if (i >= count) {
676 if (n0 > n1) {
677 s_monoBackground = (rdr::U32)c0;
678 s_monoForeground = (rdr::U32)c1;
679 } else {
680 s_monoBackground = (rdr::U32)c1;
681 s_monoForeground = (rdr::U32)c0;
682 }
683 s_palNumColors = 2; // Two colors
684 return;
685 }
686
687 paletteReset();
688 paletteInsert (c0, (rdr::U32)n0, BPP);
689 paletteInsert (c1, (rdr::U32)n1, BPP);
690
691 ni = 1;
692 for (i++; i < count; i++) {
693 if (data[i] == ci) {
694 ni++;
695 } else {
696 if (!paletteInsert (ci, (rdr::U32)ni, BPP))
697 return;
698 ci = data[i];
699 ni = 1;
700 }
701 }
702 paletteInsert (ci, (rdr::U32)ni, BPP);
703}
704#endif // #if (BPP == 8)
705
706#undef PIXEL_T
707#undef WRITE_PIXEL
708#undef TIGHT_ENCODE
709#undef SWAP_PIXEL
710#undef HASH_FUNCTION
711#undef PACK_PIXELS
712#undef DETECT_SMOOTH_IMAGE
713#undef ENCODE_SOLID_RECT
714#undef ENCODE_FULLCOLOR_RECT
715#undef ENCODE_MONO_RECT
716#undef ENCODE_INDEXED_RECT
717#undef PREPARE_JPEG_ROW
718#undef ENCODE_JPEG_RECT
719#undef FILL_PALETTE
720}