gdkrgb.c 148 KB
Newer Older
1 2 3 4
/* GDK - The GIMP Drawing Kit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6 7 8 9 10 11
 * License as published by the Free Software Foundation; either
 * version 2 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
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15 16 17 18 19 20
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/* For more information on GdkRgb, see http://www.levien.com/gdkrgb/
21

22
   Raph Levien <raph@acm.org>
23
   */
24

25
/*
26
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
27 28 29 30 31
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

32
#include <math.h>
33 34 35 36 37 38 39 40 41 42 43 44 45

#if HAVE_CONFIG_H
#  include <config.h>
#  if STDC_HEADERS
#    include <stdio.h>
#    include <stdlib.h>
#    include <string.h>
#  endif
#else
#  include <stdio.h>
#  include <stdlib.h>
#endif

46 47 48

#define ENABLE_GRAYSCALE

49
#include "config.h"
50
#include "gdkprivate.h"
51
#include "gdkinternals.h"	/* _gdk_windowing_get_bits_for_depth() */
52

Owen Taylor's avatar
Started  
Owen Taylor committed
53
#include "gdkrgb.h"
54
#include "gdkscreen.h"
55

56 57
typedef struct _GdkRgbInfo     GdkRgbInfo;
typedef struct _GdkRgbCmapInfo GdkRgbCmapInfo;
58

59
typedef void (*GdkRgbConvFunc) (GdkRgbInfo *image_info, GdkImage *image,
60 61 62 63 64 65
				gint x0, gint y0,
				gint width, gint height,
				guchar *buf, int rowstride,
				gint x_align, gint y_align,
				GdkRgbCmap *cmap);

66 67 68 69 70 71 72 73 74 75
static const gchar* visual_names[] =
{
  "static gray",
  "grayscale",
  "static color",
  "pseudo color",
  "true color",
  "direct color",
};

76
#define STAGE_ROWSTRIDE (GDK_SCRATCH_IMAGE_WIDTH * 3)
77

78
/* Some of these fields should go, as they're not being used at all. (?)
79
 */
80 81 82 83
struct _GdkRgbInfo
{
  GdkVisual *visual;
  GdkColormap *cmap;
84

85 86 87 88 89
  guint nred_shades;
  guint ngreen_shades;
  guint nblue_shades;
  guint ngray_shades;
  guint nreserved;
90

91 92 93
  guint bpp;
  gint cmap_alloced;
  gdouble gamma;
94

95 96 97
  /* Generally, the stage buffer is used to convert 32bit RGB, gray,
     and indexed images into 24 bit packed RGB. */
  guchar *stage_buf;
98

99
  GdkRgbCmap *gray_cmap;
100

101
  gboolean dith_default;
102

103 104 105
  gboolean bitmap; /* set true if in 1 bit per pixel mode */
  GdkGC *own_gc;

106 107 108
  /* Convert functions */
  GdkRgbConvFunc conv;
  GdkRgbConvFunc conv_d;
109

110 111
  GdkRgbConvFunc conv_32;
  GdkRgbConvFunc conv_32_d;
112

113 114
  GdkRgbConvFunc conv_gray;
  GdkRgbConvFunc conv_gray_d;
115

116 117
  GdkRgbConvFunc conv_indexed;
  GdkRgbConvFunc conv_indexed_d;
118 119 120

  guchar *colorcube;
  guchar *colorcube_d;
121 122 123 124 125 126 127 128 129 130 131 132 133 134

  /* We need to track LUT's for pairs of GdkRgbInfo / GdkRgbCmap, so we
   * keep a list of pointers to GdkRgbCmapInfo on both structures so we
   * can remove as necessary when freeing a GdkRgbInfo or GdkRgbCmap
   */
  GSList *cmap_info_list;
};

struct _GdkRgbCmapInfo
{
  GdkRgbInfo *image_info;
  GdkRgbCmap *cmap;

  guchar lut[256];		/* For 8-bit modes */
135 136
};

137 138 139 140 141
static GdkRgbCmapInfo *gdk_rgb_cmap_get_info (GdkRgbCmap *cmap, GdkRgbInfo *image_info);

static const char *gdk_rgb_key = "gdk-rgb-info";
static GQuark gdk_rgb_quark = 0;

142 143 144 145 146 147 148 149 150 151
static gboolean gdk_rgb_install_cmap = FALSE;
static gint gdk_rgb_min_colors = 5 * 5 * 5;
static gboolean gdk_rgb_verbose = FALSE;

static gint
gdk_rgb_cmap_fail (const char *msg, GdkColormap *cmap, gulong *pixels)
{
  gulong free_pixels[256];
  gint n_free;
  gint i;
152

153
#ifdef VERBOSE
154
  g_print ("%s", msg);
155 156 157 158 159 160 161 162 163 164 165
#endif
  n_free = 0;
  for (i = 0; i < 256; i++)
    if (pixels[i] < 256)
      free_pixels[n_free++] = pixels[i];
  if (n_free)
    gdk_colors_free (cmap, free_pixels, n_free, 0);
  return 0;
}

static void
166 167
gdk_rgb_make_colorcube (GdkRgbInfo *image_info, gulong *pixels,
			gint nr, gint ng, gint nb)
168 169 170
{
  guchar rt[16], gt[16], bt[16];
  gint i;
171

172
  image_info->colorcube = g_new (guchar, 4096);
173 174 175 176 177 178
  for (i = 0; i < 16; i++)
    {
      rt[i] = ng * nb * ((i * 17 * (nr - 1) + 128) >> 8);
      gt[i] = nb * ((i * 17 * (ng - 1) + 128) >> 8);
      bt[i] = ((i * 17 * (nb - 1) + 128) >> 8);
    }
179

180 181
  for (i = 0; i < 4096; i++)
    {
182
      image_info->colorcube[i] = pixels[rt[i >> 8] + gt[(i >> 4) & 0x0f] + bt[i & 0x0f]];
183
#ifdef VERBOSE
Sven Neumann's avatar
Sven Neumann committed
184
      g_print ("%03x %02x %x %x %x\n", i, image_info->colorcube[i], rt[i >> 8], gt[(i >> 4) & 0x0f], bt[i & 0x0f]);
185 186 187 188 189 190
#endif
    }
}

/* this is the colorcube suitable for dithering */
static void
191 192
gdk_rgb_make_colorcube_d (GdkRgbInfo *image_info, gulong *pixels,
			  gint nr, gint ng, gint nb)
193 194 195
{
  gint r, g, b;
  gint i;
196

197
  image_info->colorcube_d = g_new (guchar, 512);
198 199 200 201 202
  for (i = 0; i < 512; i++)
    {
      r = MIN (nr - 1, i >> 6);
      g = MIN (ng - 1, (i >> 3) & 7);
      b = MIN (nb - 1, i & 7);
203
      image_info->colorcube_d[i] = pixels[(r * ng + g) * nb + b];
204 205 206 207 208 209
    }
}

/* Try installing a color cube of the specified size.
   Make the colorcube and return TRUE on success */
static gint
210 211
gdk_rgb_try_colormap (GdkRgbInfo *image_info, gboolean force,
		      gint nr, gint ng, gint nb)
212 213 214 215 216 217 218 219 220 221 222 223 224
{
  gint r, g, b;
  gint ri, gi, bi;
  gint r0, g0, b0;
  GdkColormap *cmap;
  GdkColor color;
  gulong pixels[256];
  gulong junk[256];
  gint i;
  gint d2;
  gint colors_needed;
  gint idx;
  gint best[256];
225
  GdkScreen *screen;
226

227
  if (!force && nr * ng * nb < gdk_rgb_min_colors)
228
    return FALSE;
229

230 231
  screen = gdk_visual_get_screen (image_info->visual);

232
  if (image_info->cmap)
233 234
    cmap = image_info->cmap;
  else
235
    cmap = gdk_screen_get_system_colormap (screen);
236

237 238 239 240 241 242
  colors_needed = nr * ng * nb;
  for (i = 0; i < 256; i++)
    {
      best[i] = 192;
      pixels[i] = 256;
    }
243

244
#ifndef GAMMA
245
  if (cmap == gdk_screen_get_system_colormap (screen))
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
    /* find color cube colors that are already present */
    for (i = 0; i < MIN (256, cmap->size); i++)
      {
	r = cmap->colors[i].red >> 8;
	g = cmap->colors[i].green >> 8;
	b = cmap->colors[i].blue >> 8;
	ri = (r * (nr - 1) + 128) >> 8;
	gi = (g * (ng - 1) + 128) >> 8;
	bi = (b * (nb - 1) + 128) >> 8;
	r0 = ri * 255 / (nr - 1);
	g0 = gi * 255 / (ng - 1);
	b0 = bi * 255 / (nb - 1);
	idx = ((ri * nr) + gi) * nb + bi;
	d2 = (r - r0) * (r - r0) + (g - g0) * (g - g0) + (b - b0) * (b - b0);
	if (d2 < best[idx]) {
	  if (pixels[idx] < 256)
	    gdk_colors_free (cmap, pixels + idx, 1, 0);
	  else
	    colors_needed--;
	  color = cmap->colors[i];
	  if (!gdk_colormap_alloc_color (cmap, &color, FALSE, FALSE))
	    return gdk_rgb_cmap_fail ("error allocating system color\n",
				      cmap, pixels);
	  pixels[idx] = color.pixel; /* which is almost certainly i */
	  best[idx] = d2;
	}
272 273
      }
#endif
274

275 276
  if (colors_needed && 
      image_info->visual->type != GDK_VISUAL_STATIC_COLOR)
277 278 279 280 281 282
    {
      if (!gdk_colors_alloc (cmap, 0, NULL, 0, junk, colors_needed))
	{
	  char tmp_str[80];
	  
	  sprintf (tmp_str,
283
		   "%d %d %d colormap failed (in gdk_colors_alloc)\n",
284 285 286
		   nr, ng, nb);
	  return gdk_rgb_cmap_fail (tmp_str, cmap, pixels);
	}
287

288 289
      gdk_colors_free (cmap, junk, colors_needed, 0);
    }
290

291 292 293 294 295 296 297 298 299
  for (r = 0, i = 0; r < nr; r++)
    for (g = 0; g < ng; g++)
      for (b = 0; b < nb; b++, i++)
	{
	  if (pixels[i] == 256)
	    {
	      color.red = r * 65535 / (nr - 1);
	      color.green = g * 65535 / (ng - 1);
	      color.blue = b * 65535 / (nb - 1);
300

301 302 303 304 305
#ifdef GAMMA
	      color.red = 65535 * pow (color.red / 65535.0, 0.5);
	      color.green = 65535 * pow (color.green / 65535.0, 0.5);
	      color.blue = 65535 * pow (color.blue / 65535.0, 0.5);
#endif
306

307
	      if (!gdk_colormap_alloc_color (cmap, &color, FALSE, force))
308 309
		{
		  char tmp_str[80];
310 311

		  sprintf (tmp_str, "%d %d %d colormap failed\n",
312 313 314 315 316 317 318
			   nr, ng, nb);
		  return gdk_rgb_cmap_fail (tmp_str,
					    cmap, pixels);
		}
	      pixels[i] = color.pixel;
	    }
#ifdef VERBOSE
319
	  g_print ("%d: %lx\n", i, pixels[i]);
320 321
#endif
	}
322

323 324 325
  image_info->nred_shades = nr;
  image_info->ngreen_shades = ng;
  image_info->nblue_shades = nb;
326 327
  gdk_rgb_make_colorcube (image_info, pixels, nr, ng, nb);
  gdk_rgb_make_colorcube_d (image_info, pixels, nr, ng, nb);
328 329 330 331 332
  return TRUE;
}

/* Return TRUE on success. */
static gboolean
333
gdk_rgb_do_colormaps (GdkRgbInfo *image_info, gboolean force)
334
{
335
  static const gint sizes[][3] = {
336
    /*    { 6, 7, 6 }, */
337 338 339 340 341 342 343 344 345 346
    { 6, 6, 6 }, 
    { 6, 6, 5 }, 
    { 6, 6, 4 }, 
    { 5, 5, 5 }, 
    { 5, 5, 4 }, 
    { 4, 4, 4 }, 
    { 4, 4, 3 }, 
    { 3, 3, 3 }, 
    { 2, 2, 2 }
  };
347
  static const gint n_sizes = G_N_ELEMENTS (sizes);
348
  gint i;
349

350 351 352 353 354
  /* Try the possible sizes. If the force parameter is set to TRUE
   * and all larger sizes fail, force the larger size to succeed -
   * this will involve allowing closest matches when allocating the
   * colors
   */
355
  for (i = 0; i < n_sizes; i++)
356 357 358
    if (gdk_rgb_try_colormap (image_info,
			      (i == n_sizes - 1 ) && force,
			      sizes[i][0], sizes[i][1], sizes[i][2]))
359 360 361 362
      return TRUE;
  return FALSE;
}

363 364
/* Make a 2 x 2 x 2 colorcube */
static void
365
gdk_rgb_colorcube_222 (GdkRgbInfo *image_info)
366 367 368 369
{
  int i;
  GdkColor color;

370
  image_info->colorcube_d = g_new (guchar, 512);
371 372 373 374 375 376

  for (i = 0; i < 8; i++)
    {
      color.red = ((i & 4) >> 2) * 65535;
      color.green = ((i & 2) >> 1) * 65535;
      color.blue = (i & 1) * 65535;
377
      gdk_colormap_alloc_color (image_info->cmap, &color, FALSE, TRUE);
378
      image_info->colorcube_d[((i & 4) << 4) | ((i & 2) << 2) | (i & 1)] = color.pixel;
379 380 381
    }
}

382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
void
gdk_rgb_set_verbose (gboolean verbose)
{
  gdk_rgb_verbose = verbose;
}

void
gdk_rgb_set_install (gboolean install)
{
  gdk_rgb_install_cmap = install;
}

void
gdk_rgb_set_min_colors (gint min_colors)
{
  gdk_rgb_min_colors = min_colors;
}

/* Return a "score" based on the following criteria (in hex):
401

402
   x000 is the quality - 1 is 1bpp, 2 is 4bpp,
403 404 405
                         4 is 8bpp,
			 7 is 15bpp truecolor, 8 is 16bpp truecolor,
			 9 is 24bpp truecolor.
406
   0x00 is the speed - 1 is the normal case,
407
                       2 means faster than normal
408 409
   00x0 gets a point for being the system visual
   000x gets a point for being pseudocolor
410

411 412 413 414 415 416 417 418
   A caveat: in the 8bpp modes, being the system visual seems to be
   quite important. Thus, all of the 8bpp modes should be ranked at
   the same speed.
*/
static guint32
gdk_rgb_score_visual (GdkVisual *visual)
{
  guint32 quality, speed, sys, pseudo;
419

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
  quality = 0;
  speed = 1;
  sys = 0;
  if (visual->type == GDK_VISUAL_TRUE_COLOR ||
      visual->type == GDK_VISUAL_DIRECT_COLOR)
    {
      if (visual->depth == 24)
	{
	  quality = 9;
	  /* Should test for MSB visual here, and set speed if so. */
	}
      else if (visual->depth == 16)
	quality = 8;
      else if (visual->depth == 15)
	quality = 7;
      else if (visual->depth == 8)
	quality = 4;
    }
438 439
  else if (visual->type == GDK_VISUAL_PSEUDO_COLOR ||
	   visual->type == GDK_VISUAL_STATIC_COLOR)
440 441 442
    {
      if (visual->depth == 8)
	quality = 4;
443 444 445 446
      else if (visual->depth == 4)
	quality = 2;
      else if (visual->depth == 1)
	quality = 1;
447 448 449 450 451 452 453 454 455 456 457 458 459 460
    }
  else if (visual->type == GDK_VISUAL_STATIC_GRAY
#ifdef ENABLE_GRAYSCALE
	   || visual->type == GDK_VISUAL_GRAYSCALE
#endif
	   )
    {
      if (visual->depth == 8)
	quality = 4;
      else if (visual->depth == 4)
	quality = 2;
      else if (visual->depth == 1)
	quality = 1;
    }
461

462 463
  if (quality == 0)
    return 0;
464

465
  sys = (visual == gdk_screen_get_system_visual (gdk_visual_get_screen (visual)));
466

Manish Singh's avatar
Manish Singh committed
467
  pseudo = (visual->type == GDK_VISUAL_PSEUDO_COLOR || visual->type == GDK_VISUAL_TRUE_COLOR);
468

469
  if (gdk_rgb_verbose)
470
    g_print ("Visual type = %s, depth = %d, %x:%x:%x%s; score=%x\n",
471 472 473 474 475 476 477 478
	     visual_names[visual->type],
	     visual->depth,
	     visual->red_mask,
	     visual->green_mask,
	     visual->blue_mask,
	     sys ? " (system)" : "",
	     (quality << 12) | (speed << 8) | (sys << 4) | pseudo);

479 480 481
  return (quality << 12) | (speed << 8) | (sys << 4) | pseudo;
}

482
static GdkVisual *
483
gdk_rgb_choose_visual (GdkScreen *screen)
484
{
485
  GList *visuals, *tmp_list;
486 487
  guint32 score, best_score;
  GdkVisual *visual, *best_visual;
488

489
  visuals = gdk_screen_list_visuals (screen);
490
  tmp_list = visuals;
491

492
  best_visual = tmp_list->data;
493
  best_score = gdk_rgb_score_visual (best_visual);
494 495
  tmp_list = tmp_list->next;
  while (tmp_list)
496
    {
497
      visual = tmp_list->data;
498 499 500 501 502 503
      score = gdk_rgb_score_visual (visual);
      if (score > best_score)
	{
	  best_score = score;
	  best_visual = visual;
	}
504
      tmp_list = tmp_list->next;
505
    }
506

507 508
  g_list_free (visuals);

509
  return best_visual;
510 511
}

512
static void gdk_rgb_select_conv (GdkRgbInfo *image_info);
513 514

static void
515 516
gdk_rgb_set_gray_cmap (GdkRgbInfo  *image_info,
		       GdkColormap *cmap)
517 518 519 520 521 522
{
  gint i;
  GdkColor color;
  gint status;
  gulong pixels[256];
  gint r, g, b, gray;
523

524 525 526 527 528 529
  for (i = 0; i < 256; i++)
    {
      color.pixel = i;
      color.red = i * 257;
      color.green = i * 257;
      color.blue = i * 257;
530
      status = gdk_colormap_alloc_color (cmap, &color, FALSE, TRUE);
531 532
      pixels[i] = color.pixel;
#ifdef VERBOSE
533 534
      g_print ("allocating pixel %d, %x %x %x, result %d\n",
	       color.pixel, color.red, color.green, color.blue, status);
535 536
#endif
    }
537

538 539
  /* Now, we make fake colorcubes - we ultimately just use the pseudocolor
     methods. */
540

541
  image_info->colorcube = g_new (guchar, 4096);
542

543 544 545 546 547 548 549 550 551
  for (i = 0; i < 4096; i++)
    {
      r = (i >> 4) & 0xf0;
      r = r | r >> 4;
      g = i & 0xf0;
      g = g | g >> 4;
      b = (i << 4 & 0xf0);
      b = b | b >> 4;
      gray = (g + ((r + b) >> 1)) >> 1;
552 553 554 555
      image_info->colorcube[i] = pixels[gray];
    }
}

556 557
static void
gdk_rgb_free_info (GdkRgbInfo *image_info)
558
{
559 560 561 562 563 564 565 566 567
  GSList *tmp_list;
  
  if (image_info->stage_buf)
    g_free (image_info->stage_buf);
  
  if (image_info->gray_cmap)
    gdk_rgb_cmap_free (image_info->gray_cmap);

  if (image_info->own_gc)
568
    g_object_unref (image_info->own_gc);
569 570 571 572 573 574 575 576 577

  if (image_info->colorcube)
    g_free (image_info->colorcube);
  
  if (image_info->colorcube_d)
    g_free (image_info->colorcube_d);

  tmp_list = image_info->cmap_info_list;
  while (tmp_list)
578
    {
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
      GdkRgbCmapInfo *cmap_info = tmp_list->data;
      cmap_info->cmap->info_list = g_slist_remove (cmap_info->cmap->info_list, cmap_info);
      g_free (cmap_info);
    }
  g_slist_free (image_info->cmap_info_list);
  
  g_free (image_info);
}

/* Create a GdkRgbInfo for the given visual/colormap pair. If colormap
 * is NULL, it will be determined and stored in image_info->cmap. 
 * In this case, image_info->cmap will have an extra refcount which
 * is owned by the caller. 
 */
static GdkRgbInfo *
gdk_rgb_create_info (GdkVisual *visual, GdkColormap *colormap)
{
  GdkRgbInfo *image_info;
597
  GdkScreen *screen = gdk_visual_get_screen (visual);
598

599
  image_info = g_new0 (GdkRgbInfo, 1);
600

601 602
  image_info->visual = visual;
  image_info->cmap = NULL;
603

604 605 606 607 608
  image_info->nred_shades = 6;
  image_info->ngreen_shades = 6;
  image_info->nblue_shades = 4;
  image_info->ngray_shades = 24;
  image_info->nreserved = 0;
609

610 611 612
  image_info->bpp = 0;
  image_info->cmap_alloced = FALSE;
  image_info->gamma = 1.0;
613

614
  image_info->stage_buf = NULL;
615

616
  image_info->own_gc = NULL;
617

618 619
  image_info->cmap = colormap;

620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
  /* We used to use the 2x2x2 color cube for pseudo-color with depths
   * 5, 6, 7 as well but now only use it for depths (3 and) 4 in
   * pseudo-color. The reason for this is that on Win32 we let the
   * user restrict the color allocation for PSEUDO_COLOR visuals
   * (i.e., 256-color mode) and we probably want to do the full
   * gdk_rgb_do_colormaps() if we are doing that. (Though the color
   * sharing code won't really be right.)
   *
   * (The actual usefulness of this user-requested restriction remains
   * to be seen, but the code is there in gdkvisual-win32.c. The
   * thought is that it might occasionally be useful to restrict the
   * palette size in a GTK application in order to reduce color
   * flashing.)
   */
  if ((image_info->visual->type == GDK_VISUAL_PSEUDO_COLOR &&
       image_info->visual->depth <= 4 &&
       image_info->visual->depth >= 3) ||
      (image_info->visual->type == GDK_VISUAL_STATIC_COLOR &&
       image_info->visual->depth < 8 &&
       image_info->visual->depth >= 3))
640 641
    {
      if (!image_info->cmap)
642
	image_info->cmap = g_object_ref (gdk_screen_get_system_colormap (screen));
643 644 645
      
      gdk_rgb_colorcube_222 (image_info);
    }
646 647
  else if (image_info->visual->type == GDK_VISUAL_PSEUDO_COLOR
    || image_info->visual->type == GDK_VISUAL_STATIC_COLOR)
648 649
    {
      if (!image_info->cmap &&
650
	  (gdk_rgb_install_cmap || image_info->visual != gdk_screen_get_system_visual (screen)))
651
	{
652 653
	  image_info->cmap = gdk_colormap_new (image_info->visual, FALSE);
	  image_info->cmap_alloced = TRUE;
654
	}
655
      if (!gdk_rgb_do_colormaps (image_info, image_info->cmap != NULL))
656
	{
657 658 659
	  image_info->cmap = gdk_colormap_new (image_info->visual, FALSE);
	  image_info->cmap_alloced = TRUE;
	  gdk_rgb_do_colormaps (image_info, TRUE);
660
	}
661 662 663 664 665 666 667
      if (gdk_rgb_verbose)
	g_print ("color cube: %d x %d x %d\n",
		 image_info->nred_shades,
		 image_info->ngreen_shades,
		 image_info->nblue_shades);

      if (!image_info->cmap)
668
	image_info->cmap = g_object_ref (gdk_screen_get_system_colormap (screen));
669
    }
670
#ifdef ENABLE_GRAYSCALE
671 672 673
  else if (image_info->visual->type == GDK_VISUAL_GRAYSCALE)
    {
      if (!image_info->cmap)
674 675 676
	{
	  image_info->cmap = gdk_colormap_new (image_info->visual, FALSE);
	  image_info->cmap_alloced = TRUE;
677 678 679 680
	}
      
      gdk_rgb_set_gray_cmap (image_info, image_info->cmap);
    }
681
#endif
682 683 684
  else
    {
      if (!image_info->cmap)
685 686 687
	{
	  /* Always install colormap in direct color. */
	  if (image_info->visual->type != GDK_VISUAL_DIRECT_COLOR &&
688
	      image_info->visual == gdk_screen_get_system_visual (screen))
689
	    image_info->cmap = g_object_ref (gdk_screen_get_system_colormap (screen));
690 691 692 693 694 695
	  else
	    {
	      image_info->cmap = gdk_colormap_new (image_info->visual, FALSE);
	      image_info->cmap_alloced = TRUE;
	    }
	}
696
    }
697

698
  image_info->bitmap = (image_info->visual->depth == 1);
699

700
  image_info->bpp = (_gdk_windowing_get_bits_for_depth (gdk_screen_get_display (screen), image_info->visual->depth) + 7) / 8;
701
  gdk_rgb_select_conv (image_info);
702 703 704 705 706 707 708

  if (!gdk_rgb_quark)
    gdk_rgb_quark = g_quark_from_static_string (gdk_rgb_key);

  g_object_set_qdata_full (G_OBJECT (image_info->cmap), gdk_rgb_quark,
			   image_info, (GDestroyNotify)gdk_rgb_free_info);
  return image_info;
709 710
}

711 712 713 714 715
void
gdk_rgb_init (void)
{
  static const gint byte_order[1] = { 1 };

716 717 718
  if (_gdk_debug_flags & GDK_DEBUG_GDKRGB)
    gdk_rgb_verbose = TRUE;

719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
  /* check endian sanity */
#if G_BYTE_ORDER == G_BIG_ENDIAN
  if (((char *)byte_order)[0] == 1)
    g_error ("gdk_rgb_init: compiled for big endian, but this is a little endian machine.\n\n");
#else
  if (((char *)byte_order)[0] != 1)
    g_error ("gdk_rgb_init: compiled for little endian, but this is a big endian machine.\n\n");
#endif
}

static GdkRgbInfo *
gdk_rgb_get_info_from_colormap (GdkColormap *cmap)
{
  GdkRgbInfo *image_info;

  if (!gdk_rgb_quark)
    gdk_rgb_quark = g_quark_from_static_string (gdk_rgb_key);

  image_info = g_object_get_qdata (G_OBJECT (cmap), gdk_rgb_quark);
  if (!image_info)
    image_info = gdk_rgb_create_info (gdk_colormap_get_visual (cmap), cmap);

  return image_info;
}

static gulong
gdk_rgb_xpixel_from_rgb_internal (GdkColormap *colormap,
				  guint16 r, guint16 g, guint16 b)
747
{
748
  gulong pixel = 0;
749

750 751
  GdkRgbInfo *image_info = gdk_rgb_get_info_from_colormap (colormap);

752 753
  if (image_info->bitmap)
    {
754
      return (r + (g << 1) + b) > 131070;
755 756
    }
  else if (image_info->visual->type == GDK_VISUAL_PSEUDO_COLOR)
757 758 759
    pixel = image_info->colorcube[((r & 0xf000) >> 4) |
				  ((g & 0xf000) >> 8) |
				  ((b & 0xf000) >> 12)];
760
  else if (image_info->visual->depth < 8 &&
761 762
	   image_info->visual->type == GDK_VISUAL_STATIC_COLOR)
    {
763 764 765
      pixel = image_info->colorcube_d[((r & 0x8000) >> 9) |
				      ((g & 0x8000) >> 12) |
				      ((b & 0x8000) >> 15)];
766
    }
767 768
  else if (image_info->visual->type == GDK_VISUAL_TRUE_COLOR ||
	   image_info->visual->type == GDK_VISUAL_DIRECT_COLOR)
769
    {
770
#ifdef VERBOSE
771 772 773 774 775 776 777
      g_print ("shift, prec: r %d %d g %d %d b %d %d\n",
	       image_info->visual->red_shift,
	       image_info->visual->red_prec,
	       image_info->visual->green_shift,
	       image_info->visual->green_prec,
	       image_info->visual->blue_shift,
	       image_info->visual->blue_prec);
778
#endif
779

780 781 782
      pixel = (((r >> (16 - image_info->visual->red_prec)) << image_info->visual->red_shift) +
	       ((g >> (16 - image_info->visual->green_prec)) << image_info->visual->green_shift) +
	       ((b >> (16 - image_info->visual->blue_prec)) << image_info->visual->blue_shift));
783
    }
784 785 786
  else if (image_info->visual->type == GDK_VISUAL_STATIC_GRAY ||
	   image_info->visual->type == GDK_VISUAL_GRAYSCALE)
    {
787 788
      int gray = r + g * 2 + b;
      return gray >> (18 - image_info->visual->depth);
789
    }
790

791 792 793
  return pixel;
}

794 795 796 797 798 799 800 801
/* convert an rgb value into an X pixel code */
gulong
gdk_rgb_xpixel_from_rgb (guint32 rgb)
{
  guint32 r = rgb & 0xff0000;
  guint32 g = rgb & 0xff00;
  guint32 b = rgb & 0xff;

Owen Taylor's avatar
Owen Taylor committed
802
  return gdk_rgb_xpixel_from_rgb_internal (gdk_screen_get_rgb_colormap (gdk_screen_get_default ()),
803 804 805
					   (r >> 8) + (r >> 16), g + (g >> 8), b + (b << 8));
}

806 807 808 809
void
gdk_rgb_gc_set_foreground (GdkGC *gc, guint32 rgb)
{
  GdkColor color;
810

811 812 813 814 815 816 817 818
  color.pixel = gdk_rgb_xpixel_from_rgb (rgb);
  gdk_gc_set_foreground (gc, &color);
}

void
gdk_rgb_gc_set_background (GdkGC *gc, guint32 rgb)
{
  GdkColor color;
819

820 821 822 823
  color.pixel = gdk_rgb_xpixel_from_rgb (rgb);
  gdk_gc_set_background (gc, &color);
}

Havoc Pennington's avatar
Havoc Pennington committed
824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
/**
 * gdk_rgb_find_color:
 * @colormap: a #GdkColormap
 * @color: a #GdkColor
 *
 * @colormap should be the colormap for the graphics context and
 * drawable you're using to draw. If you're drawing to a #GtkWidget,
 * call gtk_widget_get_colormap().
 *
 * @color should have its %red, %green, and %blue fields initialized;
 * gdk_rgb_find_color() will fill in the %pixel field with the best
 * matching pixel from a color cube. The color is then ready to be
 * used for drawing, e.g. you can call gdk_gc_set_foreground() which
 * expects %pixel to be initialized.
 *
 * In many cases, you can avoid this whole issue by calling
 * gdk_gc_set_rgb_fg_color() or gdk_gc_set_rgb_bg_color(), which
 * do not expect %pixel to be initialized in advance. If you use those
 * functions, there's no need for gdk_rgb_find_color().
 * 
 **/
845 846 847 848 849 850 851
void
gdk_rgb_find_color (GdkColormap *colormap, GdkColor *color)
{
  color->pixel = gdk_rgb_xpixel_from_rgb_internal (colormap,
						   color->red, color->green, color->blue);
}

852
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
853 854 855 856 857
#define HAIRY_CONVERT_8
#endif

#ifdef HAIRY_CONVERT_8
static void
858
gdk_rgb_convert_8 (GdkRgbInfo *image_info, GdkImage *image,
859 860 861 862 863 864 865 866 867
		   gint x0, gint y0, gint width, gint height,
		   guchar *buf, int rowstride,
		   gint x_align, gint y_align, GdkRgbCmap *cmap)
{
  int x, y;
  gint bpl;
  guchar *obuf, *obptr;
  guchar *bptr, *bp2;
  gint r, g, b;
868
  guchar *colorcube = image_info->colorcube;
869

870 871 872 873 874 875 876
  bptr = buf;
  bpl = image->bpl;
  obuf = ((guchar *)image->mem) + y0 * bpl + x0;
  for (y = 0; y < height; y++)
    {
      bp2 = bptr;
      obptr = obuf;
877
      if (((unsigned long)obuf | (unsigned long) bp2) & 3)
878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
	{
	  for (x = 0; x < width; x++)
	    {
	      r = *bp2++;
	      g = *bp2++;
	      b = *bp2++;
	      obptr[0] = colorcube[((r & 0xf0) << 4) |
				  (g & 0xf0) |
				  (b >> 4)];
	      obptr++;
	    }
	}
      else
	{
	  for (x = 0; x < width - 3; x += 4)
	    {
	      guint32 r1b0g0r0;
	      guint32 g2r2b1g1;
	      guint32 b3g3r3b2;
897

898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
	      r1b0g0r0 = ((guint32 *)bp2)[0];
	      g2r2b1g1 = ((guint32 *)bp2)[1];
	      b3g3r3b2 = ((guint32 *)bp2)[2];
	      ((guint32 *)obptr)[0] =
		colorcube[((r1b0g0r0 & 0xf0) << 4) | 
			 ((r1b0g0r0 & 0xf000) >> 8) |
			 ((r1b0g0r0 & 0xf00000) >> 20)] |
		(colorcube[((r1b0g0r0 & 0xf0000000) >> 20) |
			  (g2r2b1g1 & 0xf0) |
			  ((g2r2b1g1 & 0xf000) >> 12)] << 8) |
		(colorcube[((g2r2b1g1 & 0xf00000) >> 12) |
			  ((g2r2b1g1 & 0xf0000000) >> 24) |
			  ((b3g3r3b2 & 0xf0) >> 4)] << 16) |
		(colorcube[((b3g3r3b2 & 0xf000) >> 4) |
			  ((b3g3r3b2 & 0xf00000) >> 16) |
			  (b3g3r3b2 >> 28)] << 24);
	      bp2 += 12;
	      obptr += 4;
	    }
	  for (; x < width; x++)
	    {
	      r = *bp2++;
	      g = *bp2++;
	      b = *bp2++;
	      obptr[0] = colorcube[((r & 0xf0) << 4) |
				  (g & 0xf0) |
				  (b >> 4)];
	      obptr++;
	    }
	}
      bptr += rowstride;
      obuf += bpl;
    }
}
#else
static void
934
gdk_rgb_convert_8 (GdkRgbInfo *image_info, GdkImage *image,
935 936 937 938 939 940 941 942 943
		   gint x0, gint y0, gint width, gint height,
		   guchar *buf, int rowstride,
		   gint x_align, gint y_align, GdkRgbCmap *cmap)
{
  int x, y;
  gint bpl;
  guchar *obuf, *obptr;
  guchar *bptr, *bp2;
  gint r, g, b;
944
  guchar *colorcube = image_info->colorcube;
945

946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975
  bptr = buf;
  bpl = image->bpl;
  obuf = ((guchar *)image->mem) + y0 * bpl + x0;
  for (y = 0; y < height; y++)
    {
      bp2 = bptr;
      obptr = obuf;
      for (x = 0; x < width; x++)
	{
	  r = *bp2++;
	  g = *bp2++;
	  b = *bp2++;
	  obptr[0] = colorcube[((r & 0xf0) << 4) |
			      (g & 0xf0) |
			      (b >> 4)];
	  obptr++;
	}
      bptr += rowstride;
      obuf += bpl;
    }
}
#endif

#if 1

/* This dither table was generated by Raph Levien using patented
   technology (US Patent 5,276,535). The dither table itself is in the
   public domain. */

#define DM_WIDTH 128
976
#define DM_WIDTH_SHIFT 7
977
#define DM_HEIGHT 128
Owen Taylor's avatar
Owen Taylor committed
978
static const guchar DM[128][128] =
979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
{
  { 0, 41, 23, 5, 17, 39, 7, 15, 62, 23, 40, 51, 31, 47, 9, 32, 52, 27, 57, 25, 6, 61, 27, 52, 37, 7, 40, 63, 18, 36, 10, 42, 25, 62, 45, 34, 20, 42, 37, 14, 35, 29, 50, 10, 61, 2, 40, 8, 37, 12, 58, 22, 5, 41, 10, 39, 0, 60, 11, 46, 2, 55, 38, 17, 36, 59, 13, 54, 37, 56, 8, 29, 16, 13, 63, 22, 41, 55, 7, 20, 49, 14, 23, 55, 37, 23, 19, 36, 15, 49, 23, 63, 30, 14, 38, 27, 53, 13, 22, 41, 19, 31, 7, 19, 50, 30, 49, 16, 3, 32, 56, 40, 29, 34, 8, 48, 19, 45, 4, 51, 12, 46, 35, 49, 16, 42, 12, 62 },
  { 30, 57, 36, 54, 47, 34, 52, 27, 43, 4, 28, 7, 17, 36, 62, 13, 44, 7, 18, 48, 33, 21, 44, 14, 30, 47, 12, 33, 5, 55, 31, 58, 13, 30, 4, 17, 52, 10, 60, 26, 46, 0, 39, 27, 42, 22, 47, 25, 60, 32, 9, 38, 48, 17, 59, 30, 49, 18, 34, 25, 51, 19, 5, 48, 21, 8, 28, 46, 1, 32, 41, 19, 54, 47, 37, 18, 28, 11, 44, 30, 39, 56, 2, 33, 8, 42, 61, 28, 58, 8, 46, 9, 41