001 package org.hd.d.pg2k.svrCore.MIME; // Put in package for PG2K by DHD20030831.
002
003 /*
004 * @(#)Quantize.java 0.90 9/19/00 Adam Doppelt
005 */
006
007 /**
008 * An efficient color quantization algorithm, adapted from the C++
009 * implementation quantize.c in <a
010 * href="http://www.imagemagick.org/">ImageMagick</a>. The pixels for
011 * an image are placed into an oct tree. The oct tree is reduced in
012 * size, and the pixels from the original image are reassigned to the
013 * nodes in the reduced tree.<p>
014 *
015 * Here is the copyright notice from ImageMagick:
016 *
017 * <pre>
018 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
019 % Permission is hereby granted, free of charge, to any person obtaining a %
020 % copy of this software and associated documentation files ("ImageMagick"), %
021 % to deal in ImageMagick without restriction, including without limitation %
022 % the rights to use, copy, modify, merge, publish, distribute, sublicense, %
023 % and/or sell copies of ImageMagick, and to permit persons to whom the %
024 % ImageMagick is furnished to do so, subject to the following conditions: %
025 % %
026 % The above copyright notice and this permission notice shall be included in %
027 % all copies or substantial portions of ImageMagick. %
028 % %
029 % The software is provided "as is", without warranty of any kind, express or %
030 % implied, including but not limited to the warranties of merchantability, %
031 % fitness for a particular purpose and noninfringement. In no event shall %
032 % E. I. du Pont de Nemours and Company be liable for any claim, damages or %
033 % other liability, whether in an action of contract, tort or otherwise, %
034 % arising from, out of or in connection with ImageMagick or the use or other %
035 % dealings in ImageMagick. %
036 % %
037 % Except as contained in this notice, the name of the E. I. du Pont de %
038 % Nemours and Company shall not be used in advertising or otherwise to %
039 % promote the sale, use or other dealings in ImageMagick without prior %
040 % written authorization from the E. I. du Pont de Nemours and Company. %
041 % %
042 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
043 </pre>
044 *
045 *
046 * @version 0.90 19 Sep 2000
047 * @author <a href="http://www.gurge.com/amd/">Adam Doppelt</a>
048 */
049 public class Quantize {
050
051 /*
052 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
053 % %
054 % %
055 % %
056 % QQQ U U AAA N N TTTTT IIIII ZZZZZ EEEEE %
057 % Q Q U U A A NN N T I ZZ E %
058 % Q Q U U AAAAA N N N T I ZZZ EEEEE %
059 % Q QQ U U A A N NN T I ZZ E %
060 % QQQQ UUU A A N N T IIIII ZZZZZ EEEEE %
061 % %
062 % %
063 % Reduce the Number of Unique Colors in an Image %
064 % %
065 % %
066 % Software Design %
067 % John Cristy %
068 % July 1992 %
069 % %
070 % %
071 % Copyright 1998 E. I. du Pont de Nemours and Company %
072 % %
073 % Permission is hereby granted, free of charge, to any person obtaining a %
074 % copy of this software and associated documentation files ("ImageMagick"), %
075 % to deal in ImageMagick without restriction, including without limitation %
076 % the rights to use, copy, modify, merge, publish, distribute, sublicense, %
077 % and/or sell copies of ImageMagick, and to permit persons to whom the %
078 % ImageMagick is furnished to do so, subject to the following conditions: %
079 % %
080 % The above copyright notice and this permission notice shall be included in %
081 % all copies or substantial portions of ImageMagick. %
082 % %
083 % The software is provided "as is", without warranty of any kind, express or %
084 % implied, including but not limited to the warranties of merchantability, %
085 % fitness for a particular purpose and noninfringement. In no event shall %
086 % E. I. du Pont de Nemours and Company be liable for any claim, damages or %
087 % other liability, whether in an action of contract, tort or otherwise, %
088 % arising from, out of or in connection with ImageMagick or the use or other %
089 % dealings in ImageMagick. %
090 % %
091 % Except as contained in this notice, the name of the E. I. du Pont de %
092 % Nemours and Company shall not be used in advertising or otherwise to %
093 % promote the sale, use or other dealings in ImageMagick without prior %
094 % written authorization from the E. I. du Pont de Nemours and Company. %
095 % %
096 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
097 %
098 % Realism in computer graphics typically requires using 24 bits/pixel to
099 % generate an image. Yet many graphic display devices do not contain
100 % the amount of memory necessary to match the spatial and color
101 % resolution of the human eye. The QUANTIZE program takes a 24 bit
102 % image and reduces the number of colors so it can be displayed on
103 % raster device with less bits per pixel. In most instances, the
104 % quantized image closely resembles the original reference image.
105 %
106 % A reduction of colors in an image is also desirable for image
107 % transmission and real-time animation.
108 %
109 % Function Quantize takes a standard RGB or monochrome images and quantizes
110 % them down to some fixed number of colors.
111 %
112 % For purposes of color allocation, an image is a set of n pixels, where
113 % each pixel is a point in RGB space. RGB space is a 3-dimensional
114 % vector space, and each pixel, pi, is defined by an ordered triple of
115 % red, green, and blue coordinates, (ri, gi, bi).
116 %
117 % Each primary color component (red, green, or blue) represents an
118 % intensity which varies linearly from 0 to a maximum value, cmax, which
119 % corresponds to full saturation of that color. Color allocation is
120 % defined over a domain consisting of the cube in RGB space with
121 % opposite vertices at (0,0,0) and (cmax,cmax,cmax). QUANTIZE requires
122 % cmax = 255.
123 %
124 % The algorithm maps this domain onto a tree in which each node
125 % represents a cube within that domain. In the following discussion
126 % these cubes are defined by the coordinate of two opposite vertices:
127 % The vertex nearest the origin in RGB space and the vertex farthest
128 % from the origin.
129 %
130 % The tree's root node represents the the entire domain, (0,0,0) through
131 % (cmax,cmax,cmax). Each lower level in the tree is generated by
132 % subdividing one node's cube into eight smaller cubes of equal size.
133 % This corresponds to bisecting the parent cube with planes passing
134 % through the midpoints of each edge.
135 %
136 % The basic algorithm operates in three phases: Classification,
137 % Reduction, and Assignment. Classification builds a color
138 % description tree for the image. Reduction collapses the tree until
139 % the number it represents, at most, the number of colors desired in the
140 % output image. Assignment defines the output image's color map and
141 % sets each pixel's color by reclassification in the reduced tree.
142 % Our goal is to minimize the numerical discrepancies between the original
143 % colors and quantized colors (quantization error).
144 %
145 % Classification begins by initializing a color description tree of
146 % sufficient depth to represent each possible input color in a leaf.
147 % However, it is impractical to generate a fully-formed color
148 % description tree in the classification phase for realistic values of
149 % cmax. If colors components in the input image are quantized to k-bit
150 % precision, so that cmax= 2k-1, the tree would need k levels below the
151 % root node to allow representing each possible input color in a leaf.
152 % This becomes prohibitive because the tree's total number of nodes is
153 % 1 + sum(i=1,k,8k).
154 %
155 % A complete tree would require 19,173,961 nodes for k = 8, cmax = 255.
156 % Therefore, to avoid building a fully populated tree, QUANTIZE: (1)
157 % Initializes data structures for nodes only as they are needed; (2)
158 % Chooses a maximum depth for the tree as a function of the desired
159 % number of colors in the output image (currently log2(colormap size)).
160 %
161 % For each pixel in the input image, classification scans downward from
162 % the root of the color description tree. At each level of the tree it
163 % identifies the single node which represents a cube in RGB space
164 % containing the pixel's color. It updates the following data for each
165 % such node:
166 %
167 % n1: Number of pixels whose color is contained in the RGB cube
168 % which this node represents;
169 %
170 % n2: Number of pixels whose color is not represented in a node at
171 % lower depth in the tree; initially, n2 = 0 for all nodes except
172 % leaves of the tree.
173 %
174 % Sr, Sg, Sb: Sums of the red, green, and blue component values for
175 % all pixels not classified at a lower depth. The combination of
176 % these sums and n2 will ultimately characterize the mean color of a
177 % set of pixels represented by this node.
178 %
179 % E: The distance squared in RGB space between each pixel contained
180 % within a node and the nodes' center. This represents the quantization
181 % error for a node.
182 %
183 % Reduction repeatedly prunes the tree until the number of nodes with
184 % n2 > 0 is less than or equal to the maximum number of colors allowed
185 % in the output image. On any given iteration over the tree, it selects
186 % those nodes whose E count is minimal for pruning and merges their
187 % color statistics upward. It uses a pruning threshold, Ep, to govern
188 % node selection as follows:
189 %
190 % Ep = 0
191 % while number of nodes with (n2 > 0) > required maximum number of colors
192 % prune all nodes such that E <= Ep
193 % Set Ep to minimum E in remaining nodes
194 %
195 % This has the effect of minimizing any quantization error when merging
196 % two nodes together.
197 %
198 % When a node to be pruned has offspring, the pruning procedure invokes
199 % itself recursively in order to prune the tree from the leaves upward.
200 % n2, Sr, Sg, and Sb in a node being pruned are always added to the
201 % corresponding data in that node's parent. This retains the pruned
202 % node's color characteristics for later averaging.
203 %
204 % For each node, n2 pixels exist for which that node represents the
205 % smallest volume in RGB space containing those pixel's colors. When n2
206 % > 0 the node will uniquely define a color in the output image. At the
207 % beginning of reduction, n2 = 0 for all nodes except a the leaves of
208 % the tree which represent colors present in the input image.
209 %
210 % The other pixel count, n1, indicates the total number of colors
211 % within the cubic volume which the node represents. This includes n1 -
212 % n2 pixels whose colors should be defined by nodes at a lower level in
213 % the tree.
214 %
215 % Assignment generates the output image from the pruned tree. The
216 % output image consists of two parts: (1) A color map, which is an
217 % array of color descriptions (RGB triples) for each color present in
218 % the output image; (2) A pixel array, which represents each pixel as
219 % an index into the color map array.
220 %
221 % First, the assignment phase makes one pass over the pruned color
222 % description tree to establish the image's color map. For each node
223 % with n2 > 0, it divides Sr, Sg, and Sb by n2 . This produces the
224 % mean color of all pixels that classify no lower than this node. Each
225 % of these colors becomes an entry in the color map.
226 %
227 % Finally, the assignment phase reclassifies each pixel in the pruned
228 % tree to identify the deepest node containing the pixel's color. The
229 % pixel's value in the pixel array becomes the index of this node's mean
230 % color in the color map.
231 %
232 % With the permission of USC Information Sciences Institute, 4676 Admiralty
233 % Way, Marina del Rey, California 90292, this code was adapted from module
234 % ALCOLS written by Paul Raveling.
235 %
236 % The names of ISI and USC are not used in advertising or publicity
237 % pertaining to distribution of the software without prior specific
238 % written permission from ISI.
239 %
240 */
241
242 final static boolean QUICK = true;
243
244 final static int MAX_RGB = 255;
245 final static int MAX_NODES = 266817;
246 final static int MAX_TREE_DEPTH = 8;
247
248 // these are precomputed in advance
249 static int SQUARES[];
250 static int SHIFT[];
251
252 static {
253 SQUARES = new int[MAX_RGB + MAX_RGB + 1];
254 for (int i= -MAX_RGB; i <= MAX_RGB; i++) {
255 SQUARES[i + MAX_RGB] = i * i;
256 }
257
258 SHIFT = new int[MAX_TREE_DEPTH + 1];
259 for (int i = 0; i < MAX_TREE_DEPTH + 1; ++i) {
260 SHIFT[i] = 1 << (15 - i);
261 }
262 }
263
264 /**
265 * Reduce the image to the given number of colors. The pixels are
266 * reduced in place.
267 * @return The new color palette.
268 */
269 public static int[] quantizeImage(int pixels[][], int max_colors) {
270 Cube cube = new Cube(pixels, max_colors);
271 cube.classification();
272 cube.reduction();
273 cube.assignment();
274 return cube.colormap;
275 }
276
277 static class Cube {
278 int pixels[][];
279 int max_colors;
280 int colormap[];
281
282 Node root;
283 int depth;
284
285 // counter for the number of colors in the cube. this gets
286 // recalculated often.
287 int colors;
288
289 // counter for the number of nodes in the tree
290 int nodes;
291
292 Cube(int pixels[][], int max_colors) {
293 this.pixels = pixels;
294 this.max_colors = max_colors;
295
296 int i = max_colors;
297 // tree_depth = log max_colors
298 // 4
299 for (depth = 1; i != 0; depth++) {
300 i /= 4;
301 }
302 if (depth > 1) {
303 --depth;
304 }
305 if (depth > MAX_TREE_DEPTH) {
306 depth = MAX_TREE_DEPTH;
307 } else if (depth < 2) {
308 depth = 2;
309 }
310
311 root = new Node(this);
312 }
313
314 /*
315 * Procedure Classification begins by initializing a color
316 * description tree of sufficient depth to represent each
317 * possible input color in a leaf. However, it is impractical
318 * to generate a fully-formed color description tree in the
319 * classification phase for realistic values of cmax. If
320 * colors components in the input image are quantized to k-bit
321 * precision, so that cmax= 2k-1, the tree would need k levels
322 * below the root node to allow representing each possible
323 * input color in a leaf. This becomes prohibitive because the
324 * tree's total number of nodes is 1 + sum(i=1,k,8k).
325 *
326 * A complete tree would require 19,173,961 nodes for k = 8,
327 * cmax = 255. Therefore, to avoid building a fully populated
328 * tree, QUANTIZE: (1) Initializes data structures for nodes
329 * only as they are needed; (2) Chooses a maximum depth for
330 * the tree as a function of the desired number of colors in
331 * the output image (currently log2(colormap size)).
332 *
333 * For each pixel in the input image, classification scans
334 * downward from the root of the color description tree. At
335 * each level of the tree it identifies the single node which
336 * represents a cube in RGB space containing It updates the
337 * following data for each such node:
338 *
339 * number_pixels : Number of pixels whose color is contained
340 * in the RGB cube which this node represents;
341 *
342 * unique : Number of pixels whose color is not represented
343 * in a node at lower depth in the tree; initially, n2 = 0
344 * for all nodes except leaves of the tree.
345 *
346 * total_red/green/blue : Sums of the red, green, and blue
347 * component values for all pixels not classified at a lower
348 * depth. The combination of these sums and n2 will
349 * ultimately characterize the mean color of a set of pixels
350 * represented by this node.
351 */
352 void classification() {
353 int pixels[][] = this.pixels;
354
355 int width = pixels.length;
356 int height = pixels[0].length;
357
358 // convert to indexed color
359 for (int x = width; x-- > 0; ) {
360 for (int y = height; y-- > 0; ) {
361 int pixel = pixels[x][y];
362 int red = (pixel >> 16) & 0xFF;
363 int green = (pixel >> 8) & 0xFF;
364 int blue = (pixel >> 0) & 0xFF;
365
366 // a hard limit on the number of nodes in the tree
367 if (nodes > MAX_NODES) {
368 System.out.println("pruning");
369 root.pruneLevel();
370 --depth;
371 }
372
373 // walk the tree to depth, increasing the
374 // number_pixels count for each node
375 Node node = root;
376 for (int level = 1; level <= depth; ++level) {
377 int id = (((red > node.mid_red ? 1 : 0) << 0) |
378 ((green > node.mid_green ? 1 : 0) << 1) |
379 ((blue > node.mid_blue ? 1 : 0) << 2));
380 if (node.child[id] == null) {
381 new Node(node, id, level);
382 }
383 node = node.child[id];
384 node.number_pixels += SHIFT[level];
385 }
386
387 ++node.unique;
388 node.total_red += red;
389 node.total_green += green;
390 node.total_blue += blue;
391 }
392 }
393 }
394
395 /*
396 * reduction repeatedly prunes the tree until the number of
397 * nodes with unique > 0 is less than or equal to the maximum
398 * number of colors allowed in the output image.
399 *
400 * When a node to be pruned has offspring, the pruning
401 * procedure invokes itself recursively in order to prune the
402 * tree from the leaves upward. The statistics of the node
403 * being pruned are always added to the corresponding data in
404 * that node's parent. This retains the pruned node's color
405 * characteristics for later averaging.
406 */
407 void reduction() {
408 int threshold = 1;
409 while (colors > max_colors) {
410 colors = 0;
411 threshold = root.reduce(threshold, Integer.MAX_VALUE);
412 }
413 }
414
415 /**
416 * The result of a closest color search.
417 */
418 static class Search {
419 int distance;
420 int color_number;
421 }
422
423 /*
424 * Procedure assignment generates the output image from the
425 * pruned tree. The output image consists of two parts: (1) A
426 * color map, which is an array of color descriptions (RGB
427 * triples) for each color present in the output image; (2) A
428 * pixel array, which represents each pixel as an index into
429 * the color map array.
430 *
431 * First, the assignment phase makes one pass over the pruned
432 * color description tree to establish the image's color map.
433 * For each node with n2 > 0, it divides Sr, Sg, and Sb by n2.
434 * This produces the mean color of all pixels that classify no
435 * lower than this node. Each of these colors becomes an entry
436 * in the color map.
437 *
438 * Finally, the assignment phase reclassifies each pixel in
439 * the pruned tree to identify the deepest node containing the
440 * pixel's color. The pixel's value in the pixel array becomes
441 * the index of this node's mean color in the color map.
442 */
443 void assignment() {
444 colormap = new int[colors];
445
446 colors = 0;
447 root.colormap();
448
449 int pixels[][] = this.pixels;
450
451 int width = pixels.length;
452 int height = pixels[0].length;
453
454 Search search = new Search();
455
456 // convert to indexed color
457 for (int x = width; x-- > 0; ) {
458 for (int y = height; y-- > 0; ) {
459 int pixel = pixels[x][y];
460 int red = (pixel >> 16) & 0xFF;
461 int green = (pixel >> 8) & 0xFF;
462 int blue = (pixel >> 0) & 0xFF;
463
464 // walk the tree to find the cube containing that color
465 Node node = root;
466 for ( ; ; ) {
467 int id = (((red > node.mid_red ? 1 : 0) << 0) |
468 ((green > node.mid_green ? 1 : 0) << 1) |
469 ((blue > node.mid_blue ? 1 : 0) << 2) );
470 if (node.child[id] == null) {
471 break;
472 }
473 node = node.child[id];
474 }
475
476 if (QUICK) {
477 // if QUICK is set, just use that
478 // node. Strictly speaking, this isn't
479 // necessarily best match.
480 pixels[x][y] = node.color_number;
481 } else {
482 // Find the closest color.
483 search.distance = Integer.MAX_VALUE;
484 node.parent.closestColor(red, green, blue, search);
485 pixels[x][y] = search.color_number;
486 }
487 }
488 }
489 }
490
491 /**
492 * A single Node in the tree.
493 */
494 static class Node {
495 Cube cube;
496
497 // parent node
498 Node parent;
499
500 // child nodes
501 Node child[];
502 int nchild;
503
504 // our index within our parent
505 int id;
506 // our level within the tree
507 int level;
508 // our color midpoint
509 int mid_red;
510 int mid_green;
511 int mid_blue;
512
513 // the pixel count for this node and all children
514 int number_pixels;
515
516 // the pixel count for this node
517 int unique;
518 // the sum of all pixels contained in this node
519 int total_red;
520 int total_green;
521 int total_blue;
522
523 // used to build the colormap
524 int color_number;
525
526 Node(Cube cube) {
527 this.cube = cube;
528 this.parent = this;
529 this.child = new Node[8];
530 this.id = 0;
531 this.level = 0;
532
533 this.number_pixels = Integer.MAX_VALUE;
534
535 this.mid_red = (MAX_RGB + 1) >> 1;
536 this.mid_green = (MAX_RGB + 1) >> 1;
537 this.mid_blue = (MAX_RGB + 1) >> 1;
538 }
539
540 Node(Node parent, int id, int level) {
541 this.cube = parent.cube;
542 this.parent = parent;
543 this.child = new Node[8];
544 this.id = id;
545 this.level = level;
546
547 // add to the cube
548 ++cube.nodes;
549 if (level == cube.depth) {
550 ++cube.colors;
551 }
552
553 // add to the parent
554 ++parent.nchild;
555 parent.child[id] = this;
556
557 // figure out our midpoint
558 int bi = (1 << (MAX_TREE_DEPTH - level)) >> 1;
559 mid_red = parent.mid_red + ((id & 1) > 0 ? bi : -bi);
560 mid_green = parent.mid_green + ((id & 2) > 0 ? bi : -bi);
561 mid_blue = parent.mid_blue + ((id & 4) > 0 ? bi : -bi);
562 }
563
564 /**
565 * Remove this child node, and make sure our parent
566 * absorbs our pixel statistics.
567 */
568 void pruneChild() {
569 --parent.nchild;
570 parent.unique += unique;
571 parent.total_red += total_red;
572 parent.total_green += total_green;
573 parent.total_blue += total_blue;
574 parent.child[id] = null;
575 --cube.nodes;
576 cube = null;
577 parent = null;
578 }
579
580 /**
581 * Prune the lowest layer of the tree.
582 */
583 void pruneLevel() {
584 if (nchild != 0) {
585 for (int id = 0; id < 8; id++) {
586 if (child[id] != null) {
587 child[id].pruneLevel();
588 }
589 }
590 }
591 if (level == cube.depth) {
592 pruneChild();
593 }
594 }
595
596 /**
597 * Remove any nodes that have fewer than threshold
598 * pixels. Also, as long as we're walking the tree:
599 *
600 * - figure out the color with the fewest pixels
601 * - recalculate the total number of colors in the tree
602 */
603 int reduce(int threshold, int next_threshold) {
604 if (nchild != 0) {
605 for (int id = 0; id < 8; id++) {
606 if (child[id] != null) {
607 next_threshold = child[id].reduce(threshold, next_threshold);
608 }
609 }
610 }
611 if (number_pixels <= threshold) {
612 pruneChild();
613 } else {
614 if (unique != 0) {
615 cube.colors++;
616 }
617 if (number_pixels < next_threshold) {
618 next_threshold = number_pixels;
619 }
620 }
621 return next_threshold;
622 }
623
624 /*
625 * colormap traverses the color cube tree and notes each
626 * colormap entry. A colormap entry is any node in the
627 * color cube tree where the number of unique colors is
628 * not zero.
629 */
630 void colormap() {
631 if (nchild != 0) {
632 for (int id = 0; id < 8; id++) {
633 if (child[id] != null) {
634 child[id].colormap();
635 }
636 }
637 }
638 if (unique != 0) {
639 int r = ((total_red + (unique >> 1)) / unique);
640 int g = ((total_green + (unique >> 1)) / unique);
641 int b = ((total_blue + (unique >> 1)) / unique);
642 cube.colormap[cube.colors] = ((( 0xFF) << 24) |
643 ((r & 0xFF) << 16) |
644 ((g & 0xFF) << 8) |
645 ((b & 0xFF) << 0));
646 color_number = cube.colors++;
647 }
648 }
649
650 /* ClosestColor traverses the color cube tree at a
651 * particular node and determines which colormap entry
652 * best represents the input color.
653 */
654 void closestColor(int red, int green, int blue, Search search) {
655 if (nchild != 0) {
656 for (int id = 0; id < 8; id++) {
657 if (child[id] != null) {
658 child[id].closestColor(red, green, blue, search);
659 }
660 }
661 }
662
663 if (unique != 0) {
664 int color = cube.colormap[color_number];
665 int distance = distance(color, red, green, blue);
666 if (distance < search.distance) {
667 search.distance = distance;
668 search.color_number = color_number;
669 }
670 }
671 }
672
673 /**
674 * Figure out the distance between this node and som color.
675 */
676 final static int distance(int color, int r, int g, int b) {
677 return (SQUARES[((color >> 16) & 0xFF) - r + MAX_RGB] +
678 SQUARES[((color >> 8) & 0xFF) - g + MAX_RGB] +
679 SQUARES[((color >> 0) & 0xFF) - b + MAX_RGB]);
680 }
681
682 @Override
683 public String toString() {
684 StringBuilder buf = new StringBuilder();
685 if (parent == this) {
686 buf.append("root");
687 } else {
688 buf.append("node");
689 }
690 buf.append(' ');
691 buf.append(level);
692 buf.append(" [");
693 buf.append(mid_red);
694 buf.append(',');
695 buf.append(mid_green);
696 buf.append(',');
697 buf.append(mid_blue);
698 buf.append(']');
699 return new String(buf);
700 }
701 }
702 }
703 }