00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057 #ifdef HAVE_CONFIG_H
00058 # include "config.h"
00059 #endif
00060 #if NEED_GNUG_PRAGMAS
00061 # pragma implementation
00062 #endif
00063
00064 #include "GException.h"
00065 #include "ByteStream.h"
00066 #include "BSByteStream.h"
00067 #include "DjVuPalette.h"
00068 #include <stdlib.h>
00069 #include <math.h>
00070
00071
00072 #ifdef HAVE_NAMESPACES
00073 namespace DJVU {
00074 # ifdef NOT_DEFINED // Just to fool emacs c++ mode
00075 }
00076 #endif
00077 #endif
00078
00079
00080 #define CUBEBITS 4
00081 #define CUBESIDE (1<<CUBEBITS)
00082 #define CUBESIZE (CUBESIDE*CUBESIDE*CUBESIDE)
00083
00084 #define RMUL 5
00085 #define GMUL 9
00086 #define BMUL 2
00087 #define SMUL (RMUL+GMUL+BMUL)
00088
00089 #define MAXPALETTESIZE 65535 // Limit for a 16 bit unsigned read.
00090
00091
00092 inline unsigned char
00093 umax(unsigned char a, unsigned char b)
00094 { return (a>b) ? a : b; }
00095
00096 inline unsigned char
00097 umin(unsigned char a, unsigned char b)
00098 { return (a>b) ? b : a; }
00099
00100 inline float
00101 fmin(float a, float b)
00102 { return (a>b) ? b : a; }
00103
00104
00105
00106
00107
00108
00109 DjVuPalette::DjVuPalette()
00110 : mask(0), hist(0), pmap(0)
00111 {
00112 }
00113
00114 DjVuPalette::~DjVuPalette()
00115 {
00116 delete hist;
00117 delete pmap;
00118 }
00119
00120 DjVuPalette&
00121 DjVuPalette::operator=(const DjVuPalette &ref)
00122 {
00123 if (this != &ref)
00124 {
00125 delete hist;
00126 delete pmap;
00127 mask = 0;
00128 palette = ref.palette;
00129 colordata = ref.colordata;
00130 }
00131 return *this;
00132 }
00133
00134 DjVuPalette::DjVuPalette(const DjVuPalette &ref)
00135 : mask(0), hist(0), pmap(0)
00136 {
00137 this->operator=(ref);
00138 }
00139
00140
00141
00142
00143
00144 void
00145 DjVuPalette::allocate_hist()
00146 {
00147 if (! hist)
00148 {
00149 hist = new GMap<int,int>;
00150 mask = 0;
00151 }
00152 else
00153 {
00154 GMap<int,int> *old = hist;
00155 hist = new GMap<int,int>;
00156 mask = (mask<<1)|(0x010101);
00157 for (GPosition p = *old; p; ++p)
00158 {
00159 int k = old->key(p);
00160 int w = (*old)[p];
00161 (*hist)[k | mask] += w;
00162 }
00163 delete old;
00164 }
00165 }
00166
00167
00168
00169
00170
00171 #ifndef NEED_DECODER_ONLY
00172
00173 struct PData
00174 {
00175 unsigned char p[3];
00176 int w;
00177 };
00178
00179 struct PBox
00180 {
00181 PData *data;
00182 int colors;
00183 int boxsize;
00184 int sum;
00185 };
00186 int
00187 DjVuPalette::bcomp (const void *a, const void *b)
00188 {
00189 return ((PData*)a)->p[0] - ((PData*)b)->p[0];
00190 }
00191
00192 int
00193
00194 DjVuPalette::gcomp (const void *a, const void *b)
00195 {
00196 return ((PData*)a)->p[1] - ((PData*)b)->p[1];
00197 }
00198
00199 int
00200 DjVuPalette::rcomp (const void *a, const void *b)
00201 {
00202 return ((PData*)a)->p[2] - ((PData*)b)->p[2];
00203 }
00204
00205 int
00206 DjVuPalette::lcomp (const void *a, const void *b)
00207 {
00208 unsigned char *aa = ((PColor*)a)->p;
00209 unsigned char *bb = ((PColor*)b)->p;
00210 if (aa[3] != bb[3])
00211 return aa[3]-bb[3];
00212 else if (aa[2] != bb[2])
00213 return aa[2]-bb[2];
00214 else if (aa[1] != bb[1])
00215 return aa[1]=bb[1];
00216 else
00217 return aa[0]-bb[0];
00218 }
00219
00220 int
00221 DjVuPalette::compute_palette(int maxcolors, int minboxsize)
00222 {
00223 if (!hist)
00224 G_THROW( ERR_MSG("DjVuPalette.no_color") );
00225 if (maxcolors<1 || maxcolors>MAXPALETTESIZE)
00226 G_THROW( ERR_MSG("DjVuPalette.many_colors") );
00227
00228
00229
00230
00231
00232 int sum = 0;
00233 int ncolors = 0;
00234 GTArray<PData> pdata;
00235 for (GPosition p = *hist; p; ++p)
00236 {
00237 pdata.touch(ncolors);
00238 PData &data = pdata[ncolors++];
00239 int k = hist->key(p);
00240 data.p[0] = (k>>16) & 0xff;
00241 data.p[1] = (k>>8) & 0xff;
00242 data.p[2] = (k) & 0xff;
00243 data.w = (*hist)[p];
00244 sum += data.w;
00245 }
00246
00247 GList<PBox> boxes;
00248 PBox newbox;
00249 newbox.data = pdata;
00250 newbox.colors = ncolors;
00251 newbox.boxsize = 256;
00252 newbox.sum = sum;
00253 boxes.append(newbox);
00254
00255 while (boxes.size() < maxcolors)
00256 {
00257
00258 GPosition p;
00259 for (p=boxes; p; ++p)
00260 if (boxes[p].colors>=2 && boxes[p].boxsize>minboxsize)
00261 break;
00262 if (! p)
00263 break;
00264
00265 PBox &splitbox = boxes[p];
00266 unsigned char pmax[3];
00267 unsigned char pmin[3];
00268 pmax[0] = pmin[0] = splitbox.data->p[0];
00269 pmax[1] = pmin[1] = splitbox.data->p[1];
00270 pmax[2] = pmin[2] = splitbox.data->p[2];
00271 for (int j=1; j<splitbox.colors; j++)
00272 {
00273 pmax[0] = umax(pmax[0], splitbox.data[j].p[0]);
00274 pmax[1] = umax(pmax[1], splitbox.data[j].p[1]);
00275 pmax[2] = umax(pmax[2], splitbox.data[j].p[2]);
00276 pmin[0] = umin(pmin[0], splitbox.data[j].p[0]);
00277 pmin[1] = umin(pmin[1], splitbox.data[j].p[1]);
00278 pmin[2] = umin(pmin[2], splitbox.data[j].p[2]);
00279 }
00280
00281 int bl = pmax[0]-pmin[0];
00282 int gl = pmax[1]-pmin[1];
00283 int rl = pmax[2]-pmin[2];
00284 splitbox.boxsize = (bl>gl ? (rl>bl ? rl : bl) : (rl>gl ? rl : gl));
00285 if (splitbox.boxsize <= minboxsize)
00286 continue;
00287 if (gl == splitbox.boxsize)
00288 qsort(splitbox.data, splitbox.colors, sizeof(PData), gcomp);
00289 else if (rl == splitbox.boxsize)
00290 qsort(splitbox.data, splitbox.colors, sizeof(PData), rcomp);
00291 else
00292 qsort(splitbox.data, splitbox.colors, sizeof(PData), bcomp);
00293
00294 int lowercolors = 0;
00295 int lowersum = 0;
00296 while (lowercolors<splitbox.colors-1 && lowersum+lowersum<splitbox.sum)
00297 lowersum += splitbox.data[lowercolors++].w;
00298
00299 newbox.data = splitbox.data + lowercolors;
00300 newbox.colors = splitbox.colors - lowercolors;
00301 newbox.sum = splitbox.sum - lowersum;
00302 splitbox.colors = lowercolors;
00303 splitbox.sum = lowersum;
00304
00305 GPosition q;
00306 for (q=p; q; ++q)
00307 if (boxes[q].sum < newbox.sum)
00308 break;
00309 boxes.insert_before(q, newbox);
00310 for (q=p; q; ++q)
00311 if (boxes[q].sum < splitbox.sum)
00312 break;
00313 boxes.insert_before(q, boxes, p);
00314 }
00315
00316 ncolors = 0;
00317 palette.empty();
00318 palette.resize(0,boxes.size()-1);
00319 for (GPosition p=boxes; p; ++p)
00320 {
00321 PBox &box = boxes[p];
00322
00323 float bsum = 0;
00324 float gsum = 0;
00325 float rsum = 0;
00326 for (int j=0; j<box.colors; j++)
00327 {
00328 float w = (float)box.data[j].w;
00329 bsum += box.data[j].p[0] * w;
00330 gsum += box.data[j].p[1] * w;
00331 rsum += box.data[j].p[2] * w;
00332 }
00333 PColor &color = palette[ncolors++];
00334 color.p[0] = (unsigned char) fmin(255, bsum/box.sum);
00335 color.p[1] = (unsigned char) fmin(255, gsum/box.sum);
00336 color.p[2] = (unsigned char) fmin(255, rsum/box.sum);
00337 color.p[3] = ( color.p[0]*BMUL + color.p[1]*GMUL + color.p[2]*RMUL) / SMUL;
00338 }
00339
00340 PColor dcolor = palette[0];
00341
00342 qsort((PColor*)palette, ncolors, sizeof(PColor), lcomp);
00343
00344 colordata.empty();
00345 delete pmap;
00346 pmap = 0;
00347
00348 return color_to_index_slow(dcolor.p);
00349 }
00350
00351
00352
00353 int
00354 DjVuPalette::compute_pixmap_palette(const GPixmap &pm, int ncolors, int minboxsize)
00355 {
00356
00357 histogram_clear();
00358 for (int j=0; j<(int)pm.rows(); j++)
00359 {
00360 const GPixel *p = pm[j];
00361 for (int i=0; i<(int)pm.columns(); i++)
00362 histogram_add(p[i], 1);
00363 }
00364
00365 return compute_palette(ncolors, minboxsize);
00366 }
00367
00368
00369 #endif
00370
00371
00372
00373
00374
00375
00376
00377 void
00378 DjVuPalette::allocate_pmap()
00379 {
00380 if (! pmap)
00381 pmap = new GMap<int,int>;
00382 }
00383
00384 int
00385 DjVuPalette::color_to_index_slow(const unsigned char *bgr)
00386 {
00387 PColor *pal = palette;
00388 const int ncolors = palette.size();
00389 if (! ncolors)
00390 G_THROW( ERR_MSG("DjVuPalette.not_init") );
00391
00392 int found = 0;
00393 int founddist = 3*256*256;
00394 for (int i=0; i<ncolors; i++)
00395 {
00396 int bd = bgr[0] - pal[i].p[0];
00397 int gd = bgr[1] - pal[i].p[1];
00398 int rd = bgr[2] - pal[i].p[2];
00399 int dist = (bd*bd)+(gd*gd)+(rd*rd);
00400 if (dist < founddist)
00401 {
00402 found = i;
00403 founddist = dist;
00404 }
00405 }
00406
00407 if (pmap && pmap->size()<0x8000)
00408 {
00409 int key = (bgr[0]<<16)|(bgr[1]<<8)|(bgr[2]);
00410 (*pmap)[key] = found;
00411 }
00412
00413 return found;
00414 }
00415
00416
00417 #ifndef NEED_DECODER_ONLY
00418
00419 void
00420 DjVuPalette::quantize(GPixmap &pm)
00421 {
00422 for (int j=0; j<(int)pm.rows(); j++)
00423 {
00424 GPixel *p = pm[j];
00425 for (int i=0; i<(int)pm.columns(); i++)
00426 index_to_color(color_to_index(p[i]), p[i]);
00427 }
00428 }
00429
00430 int
00431 DjVuPalette::compute_palette_and_quantize(GPixmap &pm, int maxcolors, int minboxsize)
00432 {
00433 int result = compute_pixmap_palette(pm, maxcolors, minboxsize);
00434 quantize(pm);
00435 return result;
00436 }
00437
00438 void
00439 DjVuPalette::color_correct(double corr)
00440 {
00441 const int palettesize = palette.size();
00442 if (palettesize > 0)
00443 {
00444
00445 int i;
00446 GTArray<GPixel> pix(0,palettesize-1);
00447 GPixel *r = pix;
00448 PColor *q = palette;
00449 for (i=0; i<palettesize; i++)
00450 {
00451 r[i].b = q[i].p[0];
00452 r[i].g = q[i].p[1];
00453 r[i].r = q[i].p[2];
00454 }
00455
00456 GPixmap::color_correct(corr, r, palettesize);
00457
00458 for (i=0; i<palettesize; i++)
00459 {
00460 q[i].p[0] = r[i].b;
00461 q[i].p[1] = r[i].g;
00462 q[i].p[2] = r[i].r;
00463 }
00464 }
00465 }
00466
00467 #endif
00468
00469
00470
00471
00472 #define DJVUPALETTEVERSION 0
00473
00474 void
00475 DjVuPalette::encode_rgb_entries(ByteStream &bs) const
00476 {
00477 const int palettesize = palette.size();
00478 for (int c=0; c<palettesize; c++)
00479 {
00480 unsigned char p[3];
00481 p[2] = palette[c].p[0];
00482 p[1] = palette[c].p[1];
00483 p[0] = palette[c].p[2];
00484 bs.writall((const void*)p, 3);
00485 }
00486 }
00487
00488 void
00489 DjVuPalette::encode(GP<ByteStream> gbs) const
00490 {
00491 ByteStream &bs=*gbs;
00492 const int palettesize = palette.size();
00493 const int datasize = colordata.size();
00494
00495 int version = DJVUPALETTEVERSION;
00496 if (datasize>0) version |= 0x80;
00497 bs.write8(version);
00498
00499 bs.write16(palettesize);
00500 for (int c=0; c<palettesize; c++)
00501 {
00502 unsigned char p[3];
00503 p[0] = palette[c].p[0];
00504 p[1] = palette[c].p[1];
00505 p[2] = palette[c].p[2];
00506 bs.writall((const void*)p, 3);
00507 }
00508
00509 if (datasize > 0)
00510 {
00511 bs.write24(datasize);
00512 GP<ByteStream> gbsb=BSByteStream::create(gbs, 50);
00513 ByteStream &bsb=*gbsb;
00514 for (int d=0; d<datasize; d++)
00515 bsb.write16(colordata[d]);
00516 }
00517 }
00518
00519 void
00520 DjVuPalette::decode_rgb_entries(ByteStream &bs, const int palettesize)
00521 {
00522 palette.resize(0,palettesize-1);
00523 for (int c=0; c<palettesize; c++)
00524 {
00525 unsigned char p[3];
00526 bs.readall((void*)p, 3);
00527 palette[c].p[0] = p[2];
00528 palette[c].p[1] = p[1];
00529 palette[c].p[2] = p[0];
00530 palette[c].p[3] = (p[0]*BMUL+p[1]*GMUL+p[2]*RMUL)/SMUL;
00531 }
00532 }
00533
00534 void
00535 DjVuPalette::decode(GP<ByteStream> gbs)
00536 {
00537 ByteStream &bs=*gbs;
00538
00539 delete hist;
00540 delete pmap;
00541 hist = 0;
00542 pmap = 0;
00543 mask = 0;
00544
00545 int version = bs.read8();
00546 if ( (version & 0x7f) != DJVUPALETTEVERSION)
00547 G_THROW( ERR_MSG("DjVuPalette.bad_version") );
00548
00549 const int palettesize = bs.read16();
00550 if (palettesize<0 || palettesize>MAXPALETTESIZE)
00551 G_THROW( ERR_MSG("DjVuPalette.bad_palette") );
00552 palette.resize(0,palettesize-1);
00553 for (int c=0; c<palettesize; c++)
00554 {
00555 unsigned char p[3];
00556 bs.readall((void*)p, 3);
00557 palette[c].p[0] = p[0];
00558 palette[c].p[1] = p[1];
00559 palette[c].p[2] = p[2];
00560 palette[c].p[3] = (p[0]*BMUL+p[1]*GMUL+p[2]*RMUL)/SMUL;
00561 }
00562
00563 if (version & 0x80)
00564 {
00565 int datasize = bs.read24();
00566 if (datasize<0)
00567 G_THROW( ERR_MSG("DjVuPalette.bad_palette") );
00568 colordata.resize(0,datasize-1);
00569 GP<ByteStream> gbsb=BSByteStream::create(gbs);
00570 ByteStream &bsb=*gbsb;
00571 for (int d=0; d<datasize; d++)
00572 {
00573 short s = bsb.read16();
00574 if (s<0 || s>=palettesize)
00575 G_THROW( ERR_MSG("DjVuPalette.bad_palette") );
00576 colordata[d] = s;
00577 }
00578 }
00579 }
00580
00581
00582
00583
00584 #ifdef HAVE_NAMESPACES
00585 }
00586 # ifndef NOT_USING_DJVU_NAMESPACE
00587 using namespace DJVU;
00588 # endif
00589 #endif
00590