Depth8Image.st
changeset 89 ea2bf46eb669
parent 86 032006651226
child 97 dd6116883ac0
equal deleted inserted replaced
88:8f9c629a4245 89:ea2bf46eb669
    19 
    19 
    20 Depth8Image comment:'
    20 Depth8Image comment:'
    21 COPYRIGHT (c) 1993 by Claus Gittinger
    21 COPYRIGHT (c) 1993 by Claus Gittinger
    22 	      All Rights Reserved
    22 	      All Rights Reserved
    23 
    23 
    24 $Header: /cvs/stx/stx/libview/Depth8Image.st,v 1.15 1994-11-28 21:00:47 claus Exp $
    24 $Header: /cvs/stx/stx/libview/Depth8Image.st,v 1.16 1995-02-06 00:35:59 claus Exp $
    25 '!
    25 '!
    26 
    26 
    27 !Depth8Image class methodsFor:'documentation'!
    27 !Depth8Image class methodsFor:'documentation'!
    28 
    28 
    29 copyright
    29 copyright
    40 "
    40 "
    41 !
    41 !
    42 
    42 
    43 version
    43 version
    44 "
    44 "
    45 $Header: /cvs/stx/stx/libview/Depth8Image.st,v 1.15 1994-11-28 21:00:47 claus Exp $
    45 $Header: /cvs/stx/stx/libview/Depth8Image.st,v 1.16 1995-02-06 00:35:59 claus Exp $
    46 "
    46 "
    47 !
    47 !
    48 
    48 
    49 documentation
    49 documentation
    50 "
    50 "
    52     It mainly consists of methods already implemented in Image,
    52     It mainly consists of methods already implemented in Image,
    53     reimplemented here for more performance.
    53     reimplemented here for more performance.
    54 "
    54 "
    55 ! !
    55 ! !
    56 
    56 
    57 !Depth8Image methodsFor:'accessing'!
    57 !Depth8Image class methodsFor:'queries'!
       
    58 
       
    59 imageDepth
       
    60     ^ 8
       
    61 ! !
       
    62 
       
    63 !Depth8Image methodsFor:'queries'!
    58 
    64 
    59 bitsPerPixel
    65 bitsPerPixel
    60     "return the number of bits per pixel"
    66     "return the number of bits per pixel"
    61 
    67 
    62     ^ 8
    68     ^ 8
    83 
    89 
    84 samplesPerPixel
    90 samplesPerPixel
    85     "return the number of samples per pixel in the image."
    91     "return the number of samples per pixel in the image."
    86 
    92 
    87     ^ 1
    93     ^ 1
       
    94 !
       
    95 
       
    96 usedValues
       
    97     "return a collection of color values used in the receiver."
       
    98 
       
    99     ^ bytes usedValues
    88 ! !
   100 ! !
    89 
   101 
    90 !Depth8Image methodsFor:'accessing'!
   102 !Depth8Image methodsFor:'accessing'!
    91 
   103 
    92 atX:x y:y
   104 atX:x y:y
   109     photometric ~~ #palette ifTrue:[
   121     photometric ~~ #palette ifTrue:[
   110 	self error:'format not supported'.
   122 	self error:'format not supported'.
   111 	^ nil
   123 	^ nil
   112     ].
   124     ].
   113     index := value + 1.
   125     index := value + 1.
   114     ^ Color red:(((colorMap at:1) at:index) * 100 / 255)
   126     ^ colorMap at:index
   115 	  green:(((colorMap at:2) at:index) * 100 / 255)
       
   116 	   blue:(((colorMap at:3) at:index) * 100 / 255)
       
   117 !
   127 !
   118 
   128 
   119 valueAtX:x y:y
   129 valueAtX:x y:y
   120     "retrieve a pixel at x/y; return a pixelValue.
   130     "retrieve a pixel at x/y; return a pixelValue.
   121      Pixels start at x=0 , y=0 for upper left pixel, end at
   131      Pixels start at x=0 , y=0 for upper left pixel, end at
   136 
   146 
   137     index := (width * y) + 1 + x.
   147     index := (width * y) + 1 + x.
   138     bytes at:index put:aPixelValue.
   148     bytes at:index put:aPixelValue.
   139 ! !
   149 ! !
   140 
   150 
   141 !Depth8Image methodsFor:'enumeration'!
   151 !Depth8Image methodsFor:'enumerating'!
   142 
   152 
   143 valueAtY:y from:xLow to:xHigh do:aBlock
   153 valueAtY:y from:xLow to:xHigh do:aBlock
   144     "perform aBlock for each pixelValue from x1 to x2 in row y.
   154     "perform aBlock for each pixelValue from x1 to x2 in row y.
   145      The block is passed the pixelValue at each pixel.
   155      The block is passed the pixelValue at each pixel.
   146      This method allows slighly faster processing of an
   156      This method allows slighly faster processing of an
   147      image than using valueAtX:y:, since some processing can be
   157      image than using valueAtX:y:, since some processing can be
   148      avoided when going from pixel to pixel. However, for
   158      avoided when going from pixel to pixel. However, for
   149      real image processing, specialized methods should be written."
   159      real image processing, specialized methods should be written."
   150 
   160 
   151     |srcIndex   "{ Class: SmallInteger }"
   161     |srcIndex   "{ Class: SmallInteger }"
   152      index      "{ Class: SmallInteger }"
       
   153      pixelValue "{ Class: SmallInteger }"
   162      pixelValue "{ Class: SmallInteger }"
   154      x1         "{ Class: SmallInteger }"
   163      x1         "{ Class: SmallInteger }"
   155      x2         "{ Class: SmallInteger }"|
   164      x2         "{ Class: SmallInteger }"|
   156 
   165 
   157     x1 := xLow.
   166     x1 := xLow.
   178      value    "{ Class: SmallInteger }"
   187      value    "{ Class: SmallInteger }"
   179      x1       "{ Class: SmallInteger }"
   188      x1       "{ Class: SmallInteger }"
   180      x2       "{ Class: SmallInteger }"
   189      x2       "{ Class: SmallInteger }"
   181      color colors last|
   190      color colors last|
   182 
   191 
   183     colors := Array new:256.
   192     photometric == #palette ifTrue:[
       
   193 	colors := colorMap.
       
   194     ] ifFalse:[
       
   195 	colors := Array new:256.
       
   196     ].
   184 
   197 
   185     x1 := xLow.
   198     x1 := xLow.
   186     x2 := xHigh.
   199     x2 := xHigh.
   187     srcIndex := (width * y) + 1 + x1.
   200     srcIndex := (width * y) + 1 + x1.
   188 
   201 
   193 	    last := value.
   206 	    last := value.
   194 	    index := value + 1.
   207 	    index := value + 1.
   195 	    color := colors at:index.
   208 	    color := colors at:index.
   196 	    color isNil ifTrue:[
   209 	    color isNil ifTrue:[
   197 		photometric == #whiteIs0 ifTrue:[
   210 		photometric == #whiteIs0 ifTrue:[
   198 		    color := (Color grey:100 - (100 / 255 * value))
   211 		    color := (Color grey:100 - (100 * value / 255))
   199 		] ifFalse:[
   212 		] ifFalse:[
   200 		    photometric == #blackIs0 ifTrue:[
   213 		    photometric == #blackIs0 ifTrue:[
   201 			color := (Color grey:(100 / 255 * value))
   214 			color := (Color grey:(100 * value / 255))
   202 		    ] ifFalse:[
   215 		    ] ifFalse:[
   203 			photometric == #palette ifTrue:[
   216 			photometric ~~ #palette ifTrue:[
   204 			    color := (Color red:(((colorMap at:1) at:index) * 100 / 255)
       
   205 					  green:(((colorMap at:2) at:index) * 100 / 255)
       
   206 					   blue:(((colorMap at:3) at:index) * 100 / 255))
       
   207 			] ifFalse:[
       
   208 			    self error:'format not supported'.
   217 			    self error:'format not supported'.
   209 			    ^ nil
   218 			    ^ nil
   210 			]
   219 			]
   211 		    ]
   220 		    ]
   212 		].
   221 		].
   366     formBytes := ByteArray uninitializedNew:(w * 2 + 7 // 8) * h.
   375     formBytes := ByteArray uninitializedNew:(w * 2 + 7 // 8) * h.
   367     patterns := Array new:nColors.
   376     patterns := Array new:nColors.
   368     pixel0bytes := ByteArray uninitializedNew:nColors.
   377     pixel0bytes := ByteArray uninitializedNew:nColors.
   369     pixel1bytes := ByteArray uninitializedNew:nColors.
   378     pixel1bytes := ByteArray uninitializedNew:nColors.
   370 
   379 
   371     "extract dither patterns and values to use for 1/o bits
   380     "extract dither patterns and values to use for 1/0 bits
   372      in those from the dithercolors"
   381      in those from the dithercolors"
   373 
   382 
   374     1 to:nColors do:[:i |
   383     1 to:nColors do:[:i |
   375 	clr := (map at:i) on:aDevice.
   384 	clr := (map at:i) on:aDevice.
   376 	ditherPattern := clr ditherForm.
   385 	ditherPattern := clr ditherForm.
   464      the result is a thresholded form, with white for
   473      the result is a thresholded form, with white for
   465      brightness values above 50%, black below"
   474      brightness values above 50%, black below"
   466 
   475 
   467     |monoBits f
   476     |monoBits f
   468      map rMap gMap bMap
   477      map rMap gMap bMap
   469      fast
   478      failed
   470      r g b 
   479      r g b 
   471      v        "{ Class: SmallInteger }"
   480      v        "{ Class: SmallInteger }"
   472      bitCount "{ Class: SmallInteger }"
   481      bitCount "{ Class: SmallInteger }"
   473      bits     "{ Class: SmallInteger }"
   482      bits     "{ Class: SmallInteger }"
   474      w        "{ Class: SmallInteger }"
   483      w        "{ Class: SmallInteger }"
   475      h        "{ Class: SmallInteger }"
   484      h        "{ Class: SmallInteger }"
   476      mapSize  "{ Class: SmallInteger }"
   485      mapSize  "{ Class: SmallInteger }"
   477      srcIndex "{ Class: SmallInteger }"
   486      srcIndex "{ Class: SmallInteger }"
   478      dstIndex "{ Class: SmallInteger }" |
   487      dstIndex "{ Class: SmallInteger }"|
   479 
   488 
   480     w := width.
   489     w := width.
   481     h := height.
   490     h := height.
   482     monoBits := ByteArray uninitializedNew:(((w + 7) // 8) * h).
   491     monoBits := ByteArray uninitializedNew:(((w + 7) // 8) * h).
   483 
   492 
   484     rMap := colorMap at:1.
       
   485     gMap := colorMap at:2.
       
   486     bMap := colorMap at:3.
       
   487     map := ByteArray uninitializedNew:256.
   493     map := ByteArray uninitializedNew:256.
   488 
   494     mapSize := colorMap size.
   489     mapSize := rMap size.
   495 
       
   496     "
       
   497      map entries: 0 for dark entries, 1 for bright entries
       
   498     "
   490     1 to:mapSize do:[:i |
   499     1 to:mapSize do:[:i |
   491 	r := rMap at:i.
   500 	map at:i put:(colorMap at:i) brightness rounded
   492 	r notNil ifTrue:[
   501     ].
   493 	    g := gMap at:i.
   502 
   494 	    b := bMap at:i.
   503     failed := true.
   495 	    v := ((3 * r) + (6 * g) + (1 * b)) // 10.
       
   496 	    v := v bitShift:-7. "only keep hi-bit"
       
   497 	    (v == 1) ifTrue:[
       
   498 		map at:i put:0   "was: 1"
       
   499 	    ] ifFalse:[
       
   500 		map at:i put:1   "was: 0"
       
   501 	    ]
       
   502 	]
       
   503     ].
       
   504 
       
   505     fast := false.
       
   506 %{
   504 %{
   507     register unsigned char *srcPtr, *dstPtr, *mapPtr;
   505     register unsigned char *srcPtr, *dstPtr, *mapPtr;
   508     register __v, __bits, __bitCount;
   506     register __v, __bits, __bitCount;
   509     register j;
   507     register j;
   510     register i;
   508     register i;
   511     extern OBJ ByteArray;
   509     extern OBJ ByteArray;
   512 
   510 
   513     if (__isByteArray(_INST(bytes)) && __isByteArray(map) && __isByteArray(monoBits)) {
   511     if (__isByteArray(_INST(bytes))
   514 	fast = true;
   512      && __isByteArray(map)
       
   513      && __isByteArray(monoBits)) {
       
   514 	failed = false;
   515 	srcPtr = _ByteArrayInstPtr(_INST(bytes))->ba_element;
   515 	srcPtr = _ByteArrayInstPtr(_INST(bytes))->ba_element;
   516 	dstPtr = _ByteArrayInstPtr(monoBits)->ba_element;
   516 	dstPtr = _ByteArrayInstPtr(monoBits)->ba_element;
   517 	mapPtr = _ByteArrayInstPtr(map)->ba_element;
   517 	mapPtr = _ByteArrayInstPtr(map)->ba_element;
   518 	for (i=_intVal(h); i>0; i--) {
   518 	for (i=_intVal(h); i>0; i--) {
   519 	    __bitCount = 0;
   519 	    __bitCount = 0;
   533 	    }
   533 	    }
   534 	}
   534 	}
   535     }
   535     }
   536 %}
   536 %}
   537 .
   537 .
   538     fast ifFalse:[
   538     failed ifTrue:[
   539 	srcIndex := 1.
   539 "/
   540 	dstIndex := 1.
   540 "/ the above code is equivalent to:
   541 	1 to:h do:[:row |
   541 "/
   542 
   542 "/        srcIndex := 1.
   543 	    bitCount := 0.
   543 "/        dstIndex := 1.
   544 	    bits := 0.
   544 "/        1 to:h do:[:row |
   545 	    1 to:w do:[:col |
   545 "/
   546 		v := bytes at:srcIndex.
   546 "/            bitCount := 0.
   547 		srcIndex := srcIndex + 1.
   547 "/            bits := 0.
   548 		v := map at:(v + 1).
   548 "/            1 to:w do:[:col |
   549 		bits := (bits bitShift:1) bitOr:v.
   549 "/                v := bytes at:srcIndex.
   550 		bitCount := bitCount + 1.
   550 "/                srcIndex := srcIndex + 1.
   551 		(bitCount == 8) ifTrue:[
   551 "/                v := map at:(v + 1).
   552 		    monoBits at:dstIndex put:bits.
   552 "/                bits := (bits bitShift:1) bitOr:v.
   553 		    dstIndex := dstIndex + 1.
   553 "/                bitCount := bitCount + 1.
   554 		    bits := 0.
   554 "/                (bitCount == 8) ifTrue:[
   555 		    bitCount := 0
   555 "/                    monoBits at:dstIndex put:bits.
   556 		]
   556 "/                    dstIndex := dstIndex + 1.
   557 	    ].
   557 "/                    bits := 0.
   558 	    (bitCount ~~ 0) ifTrue:[
   558 "/                    bitCount := 0
   559 		monoBits at:dstIndex put:bits.
   559 "/                ]
   560 		dstIndex := dstIndex + 1
   560 "/            ].
   561 	    ]
   561 "/            (bitCount ~~ 0) ifTrue:[
   562 	]
   562 "/                monoBits at:dstIndex put:bits.
       
   563 "/                dstIndex := dstIndex + 1
       
   564 "/            ]
       
   565 "/        ]
       
   566 "/
       
   567 "/ we dont need the fall-back code; so trigger an error
       
   568 	self primitiveFailed.
       
   569 	^ nil
   563     ].
   570     ].
   564 
   571 
   565     f := Form width:w height:h depth:1 on:aDevice.
   572     f := Form width:w height:h depth:1 on:aDevice.
   566     f isNil ifTrue:[^ nil].
   573     f isNil ifTrue:[^ nil].
   567     f initGC.
   574     f initGC.
   579     "return a 2-bit greyForm from the palette picture -
   586     "return a 2-bit greyForm from the palette picture -
   580      the result is a thresholded form, with white/lightGrey/darkGrey
   587      the result is a thresholded form, with white/lightGrey/darkGrey
   581      and black for brightness values 100..75, 75..50, 50..25 and 25..0 %"
   588      and black for brightness values 100..75, 75..50, 50..25 and 25..0 %"
   582 
   589 
   583     |twoPlaneBits f
   590     |twoPlaneBits f
   584      map rMap gMap bMap fast
   591      map rMap gMap bMap failed
   585      v        "{ Class: SmallInteger }"
   592      v        "{ Class: SmallInteger }"
   586      bitCount "{ Class: SmallInteger }"
   593      bitCount "{ Class: SmallInteger }"
   587      bits     "{ Class: SmallInteger }"
   594      bits     "{ Class: SmallInteger }"
   588      w        "{ Class: SmallInteger }"
   595      w        "{ Class: SmallInteger }"
   589      h        "{ Class: SmallInteger }"
   596      h        "{ Class: SmallInteger }"
   593 
   600 
   594     w := width.
   601     w := width.
   595     h := height.
   602     h := height.
   596     twoPlaneBits := ByteArray uninitializedNew:(((w * 2 + 7) // 8) * h).
   603     twoPlaneBits := ByteArray uninitializedNew:(((w * 2 + 7) // 8) * h).
   597 
   604 
   598     rMap := colorMap at:1.
       
   599     gMap := colorMap at:2.
       
   600     bMap := colorMap at:3.
       
   601     map := ByteArray uninitializedNew:256.
   605     map := ByteArray uninitializedNew:256.
   602     1 to:(rMap size) do:[:i |
   606     mapSize := colorMap size.
   603 	|r g b v|
   607 
   604 
   608     "
   605 	r := rMap at:i.
   609      map entries: 0 .. 3 give brightness in 4 thresholded steps
   606 	r notNil ifTrue:[
   610     "
   607 	    g := gMap at:i.
   611     1 to:mapSize do:[:i |
   608 	    b := bMap at:i.
   612 	map at:i put:(colorMap at:i) brightness * 3 rounded
   609 	    v := ((3 * r) + (6 * g) + (1 * b)) // 10.
   613     ].
   610 	    v := v bitShift:-6. "only keep hi-2-bits"
   614 
   611 	    map at:i put:v
   615     failed := true.
   612 	]
       
   613     ].
       
   614     fast := false.
       
   615 %{
   616 %{
   616     register unsigned char *srcPtr, *dstPtr, *mapPtr;
   617     register unsigned char *srcPtr, *dstPtr, *mapPtr;
   617     register __v, __bits, __bitCount;
   618     register __v, __bits, __bitCount;
   618     register j;
   619     register j;
   619     register i;
   620     register i;
   620     extern OBJ ByteArray;
   621     extern OBJ ByteArray;
   621 
   622 
   622     if ((__isByteArray(_INST(bytes))) && (__isByteArray(map)) && (__isByteArray(twoPlaneBits))) {
   623     if ((__isByteArray(_INST(bytes)))
   623 	fast = true;
   624      && (__isByteArray(map))
       
   625      && (__isByteArray(twoPlaneBits))) {
       
   626 	failed = false;
   624 	srcPtr = _ByteArrayInstPtr(_INST(bytes))->ba_element;
   627 	srcPtr = _ByteArrayInstPtr(_INST(bytes))->ba_element;
   625 	dstPtr = _ByteArrayInstPtr(twoPlaneBits)->ba_element;
   628 	dstPtr = _ByteArrayInstPtr(twoPlaneBits)->ba_element;
   626 	mapPtr = _ByteArrayInstPtr(map)->ba_element;
   629 	mapPtr = _ByteArrayInstPtr(map)->ba_element;
   627 	for (i=_intVal(h); i>0; i--) {
   630 	for (i=_intVal(h); i>0; i--) {
   628 	    __bitCount = 0;
   631 	    __bitCount = 0;
   640 	    if (__bitCount != 0) {
   643 	    if (__bitCount != 0) {
   641 		*dstPtr++ = __bits;
   644 		*dstPtr++ = __bits;
   642 	    }
   645 	    }
   643 	}
   646 	}
   644     }
   647     }
   645 %}
   648 %}.
   646 .
   649     failed ifTrue:[
   647     fast ifFalse:[
   650 "/
   648 	srcIndex := 1.
   651 "/ the above code is equivalent to:
   649 	dstIndex := 1.
   652 "/
   650 	1 to:h do:[:row |
   653 "/        srcIndex := 1.
   651 	    bitCount := 0.
   654 "/        dstIndex := 1.
   652 	    bits := 0.
   655 "/        1 to:h do:[:row |
   653 	    1 to:w do:[:col |
   656 "/            bitCount := 0.
   654 		v := bytes at:srcIndex.
   657 "/            bits := 0.
   655 		srcIndex := srcIndex + 1.
   658 "/            1 to:w do:[:col |
   656 		v := map at:(v + 1).
   659 "/                v := bytes at:srcIndex.
   657 		bits := (bits bitShift:2) bitOr:v.
   660 "/                srcIndex := srcIndex + 1.
   658 		bitCount := bitCount + 1.
   661 "/                v := map at:(v + 1).
   659 		(bitCount == 4) ifTrue:[
   662 "/                bits := (bits bitShift:2) bitOr:v.
   660 		    twoPlaneBits at:dstIndex put:bits.
   663 "/                bitCount := bitCount + 1.
   661 		    dstIndex := dstIndex + 1.
   664 "/                (bitCount == 4) ifTrue:[
   662 		    bits := 0.
   665 "/                    twoPlaneBits at:dstIndex put:bits.
   663 		    bitCount := 0
   666 "/                    dstIndex := dstIndex + 1.
   664 		]
   667 "/                    bits := 0.
   665 	    ].
   668 "/                    bitCount := 0
   666 	    (bitCount ~~ 0) ifTrue:[
   669 "/                ]
   667 		twoPlaneBits at:dstIndex put:bits.
   670 "/            ].
   668 		dstIndex := dstIndex + 1
   671 "/            (bitCount ~~ 0) ifTrue:[
   669 	    ]
   672 "/                twoPlaneBits at:dstIndex put:bits.
   670 	]
   673 "/                dstIndex := dstIndex + 1
       
   674 "/            ]
       
   675 "/        ]
       
   676 "/
       
   677 	self primitiveFailed.
       
   678 	^ nil
   671     ].
   679     ].
   672 
   680 
   673     f := Form width:w height:h depth:2 on:aDevice.
   681     f := Form width:w height:h depth:2 on:aDevice.
   674     f isNil ifTrue:[^ nil].
   682     f isNil ifTrue:[^ nil].
   675     f initGC.
   683     f initGC.
   681 		   x:0 y:0
   689 		   x:0 y:0
   682 		into:(f id) x:0 y:0 width:w height:h with:(f gcId).
   690 		into:(f id) x:0 y:0 width:w height:h with:(f gcId).
   683     ^ f
   691     ^ f
   684 !
   692 !
   685 
   693 
   686 paletteImageAsPseudoFormOn:aDevice
   694 paletteImageAsDitheredPseudoFormOn:aDevice
   687     "return a pseudoForm from the palette picture. The main work is
   695     "return a dithered pseudoForm from the palette picture. Depend
   688      in color reduction, when not all colors can be aquired."
   696      on dither colors being preallocated (see Color>>getColors*)"
   689 
   697 
   690     |pseudoBits f gcRound has8BitImage deviceDepth
   698     ^ self paletteImageAsDitheredPseudoFormOn:aDevice 
   691      imgMap newImage pxl
   699 	   colors:Color fixColors 
   692      usedColors usageCounts nUsed map mapIndex rMap gMap bMap
   700 	   nRed:Color numFixRed
   693      fit scale lastOK error
   701 	   nGreen:Color numFixGreen
   694      div
   702 	   nBlue:Color numFixBlue
   695      shift "{Class: SmallInteger }"
   703 !
   696      m     "{Class: SmallInteger }" |
   704 
   697 
   705 paletteImageAsDitheredPseudoFormOn:aDevice colors:fixColors nRed:nRed nGreen:nGreen nBlue:nBlue 
   698     'D8Image: allocating colors ...' errorPrintNewline.
   706     "return a dithered pseudoForm from the palette picture. 
   699 
   707      Use the colors in the fixColors array, which must be fixR x fixG x fixB
   700     "find used colors"
   708      colors assigned to aDevice, such as the preallocated colors of the
   701 
   709      Color class. 
   702     usedColors := bytes usedValues.         "gets us an array filled with used values"
   710      By passing the ditherColors as extra array, this method can
   703 					   "(could use bytes asBag)"
   711      also be used to dither an 8bit image into a smaller number of colors,
   704     nUsed := usedColors max + 1.
   712      for example to create Depth4Images."
   705 
   713 
   706     "sort by usage"
   714     "the code below is a q&d hack - it needs a rewrite to use a
   707     usageCounts := bytes usageCounts.
   715      floyd-steinberg dither (currently, the error is only forwarded
   708     usageCounts := usedColors asArray collect:[:clr | usageCounts at:(clr + 1)].
   716      to the next pixel on the right, which seems ok for photograph-like
   709     usageCounts sort:[:a :b | a > b] with:usedColors.
   717      images, but leads to more artifacts with equal colored areas).
   710 
   718     "
   711     "allocate the colors (in order of usage count)"
   719 
   712 
   720     |pseudoBits f has8BitImage deviceDepth
   713     rMap := colorMap at:1.
   721      redBytes greenBytes blueBytes
   714     gMap := colorMap at:2.
   722 "/     eR    "{Class: SmallInteger }"
   715     bMap := colorMap at:3.
   723 "/     eG    "{Class: SmallInteger }"
   716 
   724 "/     eB    "{Class: SmallInteger }"
   717     imgMap := Array new:nUsed.
   725 "/     wantR "{Class: SmallInteger }"
   718 
   726 "/     wantG "{Class: SmallInteger }"
   719     "first,  we try to get the exact colors"
   727 "/     wantB "{Class: SmallInteger }"
   720 
   728      w     "{Class: SmallInteger }"
   721     shift := (8 - aDevice bitsPerRGB) negated.
   729      h     "{Class: SmallInteger }"
   722     m := (1 bitShift:(aDevice bitsPerRGB)) - 1.
   730      index "{Class: SmallInteger }"
   723     div := m asFloat.
   731      fixR  "{Class: SmallInteger }"
   724 
   732      fixG  "{Class: SmallInteger }"
   725     fit := true.
   733      fixB  "{Class: SmallInteger }"
   726     scale := 100.0 / div.       "to scale 0..255 into 0.0 .. 100.0"
   734      fixIds failed|
   727     lastOK := 0.
   735 
   728     usedColors do:[:aColorIndex |
   736     aDevice ~~ Display ifTrue:[^ nil].
   729 	|devColor color
   737 
   730 	 r     "{Class: SmallInteger }"
   738     fixR := nRed.
   731 	 g     "{Class: SmallInteger }"
   739     fixR == 0 ifTrue:[ ^ nil].
   732 	 b     "{Class: SmallInteger }"
   740     fixG := nGreen.
   733 	 rMask "{Class: SmallInteger }"
   741     fixG == 0 ifTrue:[ ^ nil].
   734 	 gMask "{Class: SmallInteger }"
   742     fixB := nBlue.
   735 	 bMask "{Class: SmallInteger }"|
   743     fixB == 0 ifTrue:[ ^ nil].
   736 
   744     "/ simple check
   737 	fit ifTrue:[
   745     (fixR * fixG * fixB) ~~ fixColors size ifTrue:[
   738 	    gMask := bMask := rMask := m.
   746 	self error:'invalid color array passed'.
   739 
   747 	^ nil
   740 	    mapIndex := aColorIndex + 1.
   748     ].
   741 	    r := rMap at:mapIndex.
   749     fixIds := (fixColors asArray collect:[:clr | clr colorId]) asByteArray.
   742 	    g := gMap at:mapIndex.
       
   743 	    b := bMap at:mapIndex.
       
   744 	    color := Color red:((r bitShift:shift) bitAnd:rMask) * scale
       
   745 			 green:((g bitShift:shift) bitAnd:gMask) * scale
       
   746 			  blue:((b bitShift:shift) bitAnd:bMask) * scale.
       
   747 	    devColor := color exactOn:aDevice.
       
   748 	    devColor isNil ifTrue:[
       
   749 		"no such color - on the first round, do a GC to flush unused
       
   750 		 colors - this may help"
       
   751 		gcRound == 0 ifTrue:[
       
   752 		    ObjectMemory scavenge.
       
   753 		    devColor := color exactOn:aDevice.
       
   754 		    gcRound := 1
       
   755 		].
       
   756 		devColor isNil ifTrue:[
       
   757 		    gcRound == 1 ifTrue:[
       
   758 			'D8Image: force GC for possible color reclamation.' errorPrintNL.
       
   759 			ObjectMemory markAndSweep.
       
   760 			devColor := color exactOn:aDevice.
       
   761 			gcRound := 2
       
   762 		    ]
       
   763 		]
       
   764 	    ].
       
   765 	    (devColor notNil and:[devColor colorId notNil]) ifTrue:[
       
   766 		imgMap at:mapIndex put:devColor.
       
   767 		lastOK := lastOK + 1.
       
   768 	    ] ifFalse:[
       
   769 		fit := false
       
   770 	    ]
       
   771 	]
       
   772     ].
       
   773 
       
   774     "again, this time allow wrong colors (loop while increasing allowed error)"
       
   775 
       
   776     fit ifFalse:[
       
   777 	gcRound := 0.
       
   778 	error := 10.
       
   779 	[fit] whileFalse:[
       
   780 	    fit := true.
       
   781 	    usedColors from:(lastOK+1) to:(usedColors size) do:[:aColorIndex |
       
   782 		|devColor color
       
   783 		 r     "{Class: SmallInteger }"
       
   784 		 g     "{Class: SmallInteger }"
       
   785 		 b     "{Class: SmallInteger }"
       
   786 		 rMask "{Class: SmallInteger }"
       
   787 		 gMask "{Class: SmallInteger }"
       
   788 		 bMask "{Class: SmallInteger }"|
       
   789 
       
   790 		fit ifTrue:[
       
   791 		    gMask := bMask := rMask := m.
       
   792 
       
   793 		    mapIndex := aColorIndex + 1.
       
   794 		    r := rMap at:mapIndex.
       
   795 		    g := gMap at:mapIndex.
       
   796 		    b := bMap at:mapIndex.
       
   797 		    color := Color red:((r bitShift:shift) bitAnd:rMask) * scale
       
   798 				 green:((g bitShift:shift) bitAnd:gMask) * scale
       
   799 				  blue:((b bitShift:shift) bitAnd:bMask) * scale.
       
   800 		    devColor := color nearestOn:aDevice error:error.
       
   801 		    devColor isNil ifTrue:[
       
   802 			"no such color - on the first round, do a GC to flush unused
       
   803 			 colors - this may help"
       
   804 			gcRound == 0 ifTrue:[
       
   805 			    ObjectMemory scavenge.
       
   806 			    devColor := color nearestOn:aDevice error:error.
       
   807 			    gcRound := 1
       
   808 			].
       
   809 			devColor isNil ifTrue:[
       
   810 			    gcRound == 1 ifTrue:[
       
   811 				'D8Image: force GC for possible color reclamation.' errorPrintNL.
       
   812 				ObjectMemory markAndSweep.
       
   813 				devColor := color nearestOn:aDevice error:error.
       
   814 				gcRound := 2
       
   815 			    ]
       
   816 			]
       
   817 		    ].
       
   818 		    (devColor notNil and:[devColor colorId notNil]) ifTrue:[
       
   819 			imgMap at:mapIndex put:devColor.
       
   820 			lastOK := lastOK + 1.
       
   821 		    ] ifFalse:[
       
   822 			fit := false
       
   823 		    ]
       
   824 		].
       
   825 	    ].
       
   826 	    error := error * 2
       
   827 	].
       
   828 
       
   829 	error > 100 ifTrue:[
       
   830 	    'D8Image: not enough colors for a reasonable image' errorPrintNewline
       
   831 	] ifFalse:[
       
   832 	    'D8Image: not enough colors for exact picture' errorPrintNewline.
       
   833 	]
       
   834     ].
       
   835 
       
   836     "create translation map"
       
   837     map := ByteArray new:256.
       
   838     1 to:imgMap size do:[:i |
       
   839 	(imgMap at:i) notNil ifTrue:[
       
   840 	    map at:i put:(imgMap at:i) colorId
       
   841 	]
       
   842     ].
       
   843 
   750 
   844     deviceDepth := aDevice depth.
   751     deviceDepth := aDevice depth.
   845     deviceDepth == 8 ifTrue:[
   752     deviceDepth == 8 ifTrue:[
   846 	has8BitImage := true.
   753 	has8BitImage := true.
   847     ] ifFalse:[
   754     ] ifFalse:[
   850 	    (fmt at:2) == 8 ifTrue:[
   757 	    (fmt at:2) == 8 ifTrue:[
   851 		has8BitImage := true.
   758 		has8BitImage := true.
   852 	    ]
   759 	    ]
   853 	]
   760 	]
   854     ].
   761     ].
   855 
   762     has8BitImage ifFalse:[^ nil].
       
   763 
       
   764     'D8IMAGE: dithering ...' errorPrintNL.
       
   765 
       
   766     "
       
   767      collect color components as integer values
       
   768      (code below uses components percent * 2.55 asInteger everywhere, to avoid
       
   769       float arithmetic, rounding etc. Thus, the range is 0..255 here)
       
   770     "
       
   771     redBytes := ByteArray uninitializedNew:(colorMap size).
       
   772     greenBytes := ByteArray uninitializedNew:(colorMap size).
       
   773     blueBytes := ByteArray uninitializedNew:(colorMap size).
       
   774     1 to:(colorMap size) do:[:i |
       
   775 	|clr|
       
   776 	clr := colorMap at:i.
       
   777 	redBytes at:i put:(clr red * 2.55) asInteger.
       
   778 	greenBytes at:i put:(clr green * 2.55) asInteger.
       
   779 	blueBytes at:i put:(clr blue * 2.55) asInteger.
       
   780     ].
       
   781 
       
   782     pseudoBits := ByteArray uninitializedNew:(width * height).
       
   783 
       
   784     w := width.
       
   785     h := height.
       
   786 
       
   787     failed := true.
       
   788 
       
   789 %{
       
   790     int __x, __y;
       
   791     int __eR, __eG, __eB;
       
   792     int __wantR, __wantG, __wantB;
       
   793     unsigned char *srcP, *dstP;
       
   794     unsigned char *redP, *greenP, *blueP;
       
   795     int pix;
       
   796     unsigned char *idP;
       
   797     int __fR, __fG, __fB;
       
   798     int iR, iG, iB;
       
   799     int idx;
       
   800 
       
   801     if (__isByteArray(_INST(bytes))
       
   802      && __isByteArray(pseudoBits)
       
   803      && __isByteArray(redBytes)
       
   804      && __isByteArray(greenBytes)
       
   805      && __isByteArray(blueBytes)
       
   806      && __isByteArray(fixIds)
       
   807      && _isSmallInteger(fixR)
       
   808      && _isSmallInteger(fixG)
       
   809      && _isSmallInteger(fixB)) {
       
   810 	failed = false;
       
   811 
       
   812 	srcP = _ByteArrayInstPtr(_INST(bytes))->ba_element;
       
   813 	dstP = _ByteArrayInstPtr(pseudoBits)->ba_element;
       
   814 	redP = _ByteArrayInstPtr(redBytes)->ba_element;
       
   815 	greenP = _ByteArrayInstPtr(greenBytes)->ba_element;
       
   816 	blueP = _ByteArrayInstPtr(blueBytes)->ba_element;
       
   817 	idP = _ByteArrayInstPtr(fixIds)->ba_element;
       
   818 	__fR = _intVal(fixR)-1;
       
   819 	__fG = _intVal(fixG)-1;
       
   820 	__fB = _intVal(fixB)-1;
       
   821 
       
   822 	for (__y=_intVal(h); __y>0; __y--) {
       
   823 	    __eR = __eG = __eB = 0;
       
   824 	    for (__x=_intVal(w); __x>0; __x--) {
       
   825 		int t;
       
   826 		int __want;
       
   827 
       
   828 		pix = *srcP++;
       
   829 
       
   830 		/*
       
   831 		 * wR, wG and wB is the wanted r/g/b value;
       
   832 		 * compute the index into the dId table ..
       
   833 		 * values: 0..255; scale to 0..fR-1, 0..fG-1, 0..fB-1
       
   834 		 *
       
   835 		 * bad kludge: knows how to index into FixColor table
       
   836 		 */
       
   837 		__wantR = redP[pix] + __eR;
       
   838 		__wantG = greenP[pix] + __eG;
       
   839 		__wantB = blueP[pix] + __eB;
       
   840 
       
   841 		if (__wantR > 255) __want = 255;
       
   842 		else if (__wantR < 0) __want = 0;
       
   843 		else __want = __wantR;
       
   844 
       
   845 		iR = __want * __fR / 128;
       
   846 		iR = (iR / 2) + (iR & 1);
       
   847 		idx = iR * (__fG+1);
       
   848 
       
   849 		if (__wantG > 255) __want = 255;
       
   850 		else if (__wantG < 0) __want = 0;
       
   851 		else __want = __wantG;
       
   852 
       
   853 		iG = __want * __fG / 128;
       
   854 		iG = (iG / 2) + (iG & 1);
       
   855 		idx = (idx + iG) * (__fB+1);
       
   856 
       
   857 		if (__wantB > 255) __want = 255;
       
   858 		else if (__wantB < 0) __want = 0;
       
   859 		else __want = __wantB;
       
   860 
       
   861 		iB = __want * __fB / 128;
       
   862 		iB = (iB / 2) + (iB & 1);
       
   863 		idx = idx + iB;
       
   864 
       
   865 		/*
       
   866 		 * store the corresponding dither colorId
       
   867 		 */
       
   868 		*dstP++ = idP[idx];
       
   869 
       
   870 		/*
       
   871 		 * the new error:
       
   872 		 */
       
   873 		__eR = __wantR - (iR * 256 / __fR); 
       
   874 		__eG = __wantG - (iG * 256 / __fG); 
       
   875 		__eB = __wantB - (iB * 256 / __fB); 
       
   876 	    }
       
   877 	}
       
   878     }
       
   879 %}.
       
   880     failed ifTrue:[
       
   881 	self primitiveFailed.
       
   882 	^ nil
       
   883 
       
   884 "/ for non-C programmers:
       
   885 "/   the above code is (roughly) equivalent to:
       
   886 "/
       
   887 "/    index := 1.
       
   888 "/    1 to:h do:[:y |
       
   889 "/        eR := eG := eB := 0.
       
   890 "/        1 to:w do:[:x |
       
   891 "/            |pixel "{ Class: SmallInteger }"
       
   892 "/             clr 
       
   893 "/             wR    "{ Class: SmallInteger }"
       
   894 "/             wG    "{ Class: SmallInteger }"
       
   895 "/             wB    "{ Class: SmallInteger }" |
       
   896 "/
       
   897 "/            pixel := (bytes at:index) + 1.
       
   898 "/
       
   899 "/            wantR := ((redBytes at:pixel) + eR).
       
   900 "/            wantG := ((greenBytes at:pixel) + eG).
       
   901 "/            wantB := ((blueBytes at:pixel) + eB).
       
   902 "/            wR := wantR.
       
   903 "/            wR > 200 ifTrue:[wR := 200] ifFalse:[wR < 0 ifTrue:[wR := 0]].
       
   904 "/            wG := wantG.
       
   905 "/            wG > 200 ifTrue:[wG := 200] ifFalse:[wG < 0 ifTrue:[wG := 0]].
       
   906 "/            wB := wantB.
       
   907 "/            wB > 200 ifTrue:[wB := 200] ifFalse:[wB < 0 ifTrue:[wB := 0]].
       
   908 "/
       
   909 "/            iR := wR * (fixR-1) / 128.
       
   910 "/            iR := (iR / 2) + (iR bitAnd:1).
       
   911 "/            iG := wG * (fixG-1) / 128.
       
   912 "/            iG := (iG / 2) + (iG bitAnd:1).
       
   913 "/            iB := wB * (fixB-1) / 128.
       
   914 "/            iB := (iB / 2) + (iB bitAnd:1).
       
   915 "/            idx := (iR * fixR + iG) * fixB + iB.
       
   916 "/            clr := fixColors at:idx. 
       
   917 "/
       
   918 "/            eR := wantR - (clr red * 2) asInteger.
       
   919 "/            eG := wantG - (clr green * 2) asInteger.
       
   920 "/            eB := wantB - (clr blue * 2) asInteger.
       
   921 "/
       
   922 "/            pixel := clr colorId.
       
   923 "/            pseudoBits at:index put:pixel.
       
   924 "/
       
   925 "/            index := index + 1
       
   926 "/        ].
       
   927 "/    ].
       
   928     ].
       
   929 
       
   930     f := Form width:width height:height depth:deviceDepth on:aDevice.
       
   931     f isNil ifTrue:[^ nil].
       
   932     f colorMap:fixColors. 
       
   933     f initGC.
       
   934     aDevice drawBits:pseudoBits bitsPerPixel:8 depth:deviceDepth  
       
   935 	       width:width height:height
       
   936 		   x:0 y:0
       
   937 		into:(f id) x:0 y:0 
       
   938 	       width:width height:height with:(f gcId).
       
   939     ^ f
       
   940 
       
   941     "
       
   942      example: color reduction from Depth8 to Depth4 can be done by:
       
   943 
       
   944      |img8 reducedImg8 img4 map|
       
   945 
       
   946      map := #( 
       
   947 		  (0     0   0)
       
   948 		  (0     0 100)
       
   949 		  (0    50   0)
       
   950 		  (0    50 100)
       
   951 		  (0   100   0)
       
   952 		  (0   100 100)
       
   953 		  (100   0   0)
       
   954 		  (100   0 100)
       
   955 		  (100  50   0)
       
   956 		  (100  50 100)
       
   957 		  (100 100   0)
       
   958 		  (100 100 100)) collect:[:rgb | (Color red:(rgb at:1)
       
   959 						      green:(rgb at:2)
       
   960 						       blue:(rgb at:3)) on:Display].
       
   961 
       
   962      img8 := Image fromFile:'bitmaps/bf.im8'.
       
   963      form := img8 paletteImageAsDitheredPseudoFormOn:Display 
       
   964 		      colors:map 
       
   965 			nRed:2
       
   966 		      nGreen:3
       
   967 		       nBlue:2.
       
   968      img8 := Depth8Image fromForm:form.
       
   969      img4 := Depth4Image fromImage:img8.
       
   970     "
       
   971 !
       
   972 
       
   973 paletteImageAsPseudoFormOn:aDevice
       
   974     "return a pseudoForm from the palette picture. The main work is
       
   975      in color reduction, when not all colors can be aquired."
       
   976 
       
   977     |pseudoBits f gcRound has8BitImage deviceDepth
       
   978      imgMap newImage pxl
       
   979      usedColors usageCounts nUsed map
       
   980      fit scale lastOK error 
       
   981      div
       
   982      shift "{Class: SmallInteger }"
       
   983      m     "{Class: SmallInteger }" |
       
   984 
       
   985     Color fixColors notNil ifTrue:[
       
   986 	f := self paletteImageAsDitheredPseudoFormOn:aDevice.
       
   987 	f notNil ifTrue:[^ f].
       
   988     ].
       
   989 
       
   990     'D8IMAGE: allocating colors ...' errorPrintNL.
       
   991 
       
   992     "find used colors"
       
   993 
       
   994     usedColors := bytes usedValues.         "gets us an array filled with used values"
       
   995 					   "(could use bytes asBag)"
       
   996     nUsed := usedColors max + 1.
       
   997 
       
   998     "sort by usage"
       
   999     usageCounts := bytes usageCounts.
       
  1000     usageCounts := usedColors asArray collect:[:clr | usageCounts at:(clr + 1)].
       
  1001     usageCounts sort:[:a :b | a > b] with:usedColors.
       
  1002 
       
  1003     "allocate the colors (in order of usage count)"
       
  1004 
       
  1005     imgMap := Array new:nUsed.
       
  1006 
       
  1007     "
       
  1008      first, try to get the exact colors ...
       
  1009     "
       
  1010     shift := (8 - aDevice bitsPerRGB) negated.
       
  1011     m := (1 bitShift:(aDevice bitsPerRGB)) - 1.
       
  1012     div := m asFloat.
       
  1013 
       
  1014     fit := true.
       
  1015     scale := 100.0 / div.       "to scale 0..255 into 0.0 .. 100.0"
       
  1016     lastOK := 0.
       
  1017     gcRound := 0.
       
  1018 
       
  1019     usedColors do:[:aColorIndex |
       
  1020 	|devColor color
       
  1021 	 r        "{Class: SmallInteger }"
       
  1022 	 g        "{Class: SmallInteger }"
       
  1023 	 b        "{Class: SmallInteger }"
       
  1024 	 mapIndex "{Class: SmallInteger }"
       
  1025 	 rMask    "{Class: SmallInteger }"
       
  1026 	 gMask    "{Class: SmallInteger }"
       
  1027 	 bMask    "{Class: SmallInteger }"|
       
  1028 
       
  1029 	fit ifTrue:[
       
  1030 	    gMask := bMask := rMask := m.
       
  1031 
       
  1032 	    mapIndex := aColorIndex + 1.
       
  1033 	    color := colorMap at:mapIndex.
       
  1034 	    color colorId notNil ifTrue:[
       
  1035 		"wow - an immediate hit"
       
  1036 		devColor := color
       
  1037 	    ] ifFalse:[
       
  1038 		devColor := color exactOn:aDevice.
       
  1039 		devColor isNil ifTrue:[
       
  1040 		    "
       
  1041 		     could not allocate color - on the first round, do a GC to flush 
       
  1042 		     unused colors - this may help if some colors where locked by 
       
  1043 		     already free images.
       
  1044 		    "
       
  1045 		    gcRound == 0 ifTrue:[
       
  1046 			ObjectMemory scavenge.
       
  1047 			devColor := color exactOn:aDevice.
       
  1048 			gcRound := 1
       
  1049 		    ].
       
  1050 		    devColor isNil ifTrue:[
       
  1051 			gcRound == 1 ifTrue:[
       
  1052 			    'D8IMAGE: force GC for possible color reclamation.' errorPrintNL.
       
  1053 			    ObjectMemory markAndSweep.
       
  1054 			    devColor := color exactOn:aDevice.
       
  1055 			    gcRound := 2
       
  1056 			]
       
  1057 		    ]
       
  1058 		].
       
  1059 	    ].
       
  1060 	    (devColor notNil and:[devColor colorId notNil]) ifTrue:[
       
  1061 		imgMap at:mapIndex put:devColor.
       
  1062 		lastOK := lastOK + 1.
       
  1063 	    ] ifFalse:[
       
  1064 		fit := false
       
  1065 	    ]
       
  1066 	]
       
  1067     ].
       
  1068 
       
  1069     fit ifFalse:[
       
  1070 	"
       
  1071 	 again, this time allow wrong colors (loop while increasing allowed error)
       
  1072 	"
       
  1073 	error := 10.
       
  1074 	[fit] whileFalse:[
       
  1075 	    fit := true.
       
  1076 	    usedColors from:(lastOK+1) to:(usedColors size) do:[:aColorIndex |
       
  1077 		|devColor color
       
  1078 		 r        "{Class: SmallInteger }"
       
  1079 		 g        "{Class: SmallInteger }"
       
  1080 		 b        "{Class: SmallInteger }"
       
  1081 		 mapIndex "{Class: SmallInteger }"
       
  1082 		 rMask    "{Class: SmallInteger }"
       
  1083 		 gMask    "{Class: SmallInteger }"
       
  1084 		 bMask    "{Class: SmallInteger }"|
       
  1085 
       
  1086 		fit ifTrue:[
       
  1087 		    gMask := bMask := rMask := m.
       
  1088 
       
  1089 		    mapIndex := aColorIndex + 1.
       
  1090 		    color := colorMap at:mapIndex.
       
  1091 		    r := (color red * 255 / 100) rounded.
       
  1092 		    g := (color green * 255 / 100) rounded.
       
  1093 		    b := (color blue * 255 / 100) rounded.
       
  1094 
       
  1095 		    color := Color red:((r bitShift:shift) bitAnd:rMask) * scale
       
  1096 				 green:((g bitShift:shift) bitAnd:gMask) * scale
       
  1097 				  blue:((b bitShift:shift) bitAnd:bMask) * scale.
       
  1098 
       
  1099 		    color colorId notNil ifTrue:[
       
  1100 			"wow - an immediate hit"
       
  1101 			devColor := color
       
  1102 		    ] ifFalse:[
       
  1103 			devColor := color nearestOn:aDevice error:error.
       
  1104 			devColor isNil ifTrue:[
       
  1105 			    "
       
  1106 			     no free color - on the first round, do a GC to flush unused
       
  1107 			     colors - this may help if some colors where locked by already
       
  1108 			     free images.
       
  1109 			    "
       
  1110 			    gcRound == 0 ifTrue:[
       
  1111 				ObjectMemory scavenge.
       
  1112 				devColor := color nearestOn:aDevice error:error.
       
  1113 				gcRound := 1
       
  1114 			    ].
       
  1115 			    devColor isNil ifTrue:[
       
  1116 				gcRound == 1 ifTrue:[
       
  1117 				    'D8IMAGE: force GC for possible color reclamation.' errorPrintNL.
       
  1118 				    ObjectMemory markAndSweep.
       
  1119 				    devColor := color nearestOn:aDevice error:error.
       
  1120 				    gcRound := 2
       
  1121 				]
       
  1122 			    ]
       
  1123 			].
       
  1124 		    ].
       
  1125 		    (devColor notNil and:[devColor colorId notNil]) ifTrue:[
       
  1126 			imgMap at:mapIndex put:devColor.
       
  1127 			lastOK := lastOK + 1.
       
  1128 		    ] ifFalse:[
       
  1129 			fit := false
       
  1130 		    ]
       
  1131 		].
       
  1132 	    ].
       
  1133 	    error := error * 2.
       
  1134 	    error > 1000 ifTrue:[
       
  1135 		"
       
  1136 		 break out, if the error becomes too big.
       
  1137 		"
       
  1138 		'D8IMAGE: hard color allocation problem - revert to b&w' errorPrintNL.
       
  1139 		"
       
  1140 		 map to b&w as a last fallback.
       
  1141 		 (should really do a dither here)
       
  1142 		"
       
  1143 		usedColors from:(lastOK+1) to:(usedColors size) do:[:aColorIndex |
       
  1144 		    |color
       
  1145 		     mapIndex "{ Class: SmallInteger }"|
       
  1146 
       
  1147 		    mapIndex := aColorIndex + 1.
       
  1148 		    color := colorMap at:mapIndex.
       
  1149 		    color brightness > 0.5 ifTrue:[
       
  1150 			imgMap at:mapIndex put:(Color white on:aDevice).
       
  1151 		    ] ifFalse:[
       
  1152 			imgMap at:mapIndex put:(Color black on:aDevice).
       
  1153 		    ]
       
  1154 		].
       
  1155 		fit := true.
       
  1156 	    ]
       
  1157 	].
       
  1158 
       
  1159 	error > 100 ifTrue:[
       
  1160 	    'D8IMAGE: not enough colors for a reasonable image' errorPrintNL
       
  1161 	] ifFalse:[
       
  1162 	    'D8IMAGE: not enough colors for exact picture' errorPrintNL.
       
  1163 	]
       
  1164     ].
       
  1165 
       
  1166     "
       
  1167      create translation map (from image colors to allocated colorIds)
       
  1168     "
       
  1169     map := ByteArray new:256.
       
  1170     1 to:imgMap size do:[:i |
       
  1171 	(imgMap at:i) notNil ifTrue:[
       
  1172 	    map at:i put:(imgMap at:i) colorId
       
  1173 	]
       
  1174     ].
       
  1175 
       
  1176     deviceDepth := aDevice depth.
       
  1177     deviceDepth == 8 ifTrue:[
       
  1178 	has8BitImage := true.
       
  1179     ] ifFalse:[
       
  1180 	has8BitImage := false.
       
  1181 	aDevice supportedImageFormats do:[:fmt |
       
  1182 	    (fmt at:2) == 8 ifTrue:[
       
  1183 		has8BitImage := true.
       
  1184 	    ]
       
  1185 	]
       
  1186     ].
       
  1187 
       
  1188     "
       
  1189      finally, create a form on the device and copy (& translate)
       
  1190      the pixel values
       
  1191     "
   856     has8BitImage ifTrue:[
  1192     has8BitImage ifTrue:[
   857 	pseudoBits := ByteArray uninitializedNew:(width * height).
  1193 	pseudoBits := ByteArray uninitializedNew:(width * height).
   858 
  1194 
   859 	bytes expandPixels:8         "xlate only"
  1195 	bytes expandPixels:8         "xlate only"
   860 		    width:width 
  1196 		    width:width 
   907 
  1243 
   908 paletteImageAsGreyFormOn:aDevice
  1244 paletteImageAsGreyFormOn:aDevice
   909     "return an 8-bit greyForm from the 8-bit palette picture;
  1245     "return an 8-bit greyForm from the 8-bit palette picture;
   910      only a translation has to be done"
  1246      only a translation has to be done"
   911 
  1247 
   912     |greyBits f v
  1248     |greyBits f map
   913      nColors "{ Class: SmallInteger }"
  1249      mapSize "{ Class: SmallInteger }"|
   914      r       "{ Class: SmallInteger }"
       
   915      g       "{ Class: SmallInteger }"
       
   916      b       "{ Class: SmallInteger }"
       
   917      map rMap gMap bMap|
       
   918 
  1250 
   919     greyBits := ByteArray uninitializedNew:(width * height).
  1251     greyBits := ByteArray uninitializedNew:(width * height).
   920 
  1252 
   921     rMap := colorMap at:1.
       
   922     gMap := colorMap at:2.
       
   923     bMap := colorMap at:3.
       
   924     nColors := rMap size.
       
   925     map := ByteArray uninitializedNew:256.
  1253     map := ByteArray uninitializedNew:256.
   926 
  1254     mapSize := colorMap size.
   927     1 to:nColors do:[:i |
  1255 
   928 	r := rMap at:i.
  1256     1 to:mapSize do:[:i |
   929 	r notNil ifTrue:[
  1257 	map at:i put:((colorMap at:i) brightness * 255) rounded
   930 	    g := gMap at:i.
  1258     ].
   931 	    b := bMap at:i.
  1259 
   932 	    v := ((3 * r) + (6 * g) + (1 * b)) // 10.
       
   933 	    map at:i put:v
       
   934 	]
       
   935     ].
       
   936     bytes expandPixels:8         "xlate only"
  1260     bytes expandPixels:8         "xlate only"
   937 		width:width 
  1261 		width:width 
   938 	       height:height
  1262 	       height:height
   939 		 into:greyBits
  1263 		 into:greyBits
   940 	      mapping:map.
  1264 	      mapping:map.
   952 paletteImageAsPatternDitheredGreyFormOn:aDevice
  1276 paletteImageAsPatternDitheredGreyFormOn:aDevice
   953     "return a dithered greyForm from the 8-bit palette picture.
  1277     "return a dithered greyForm from the 8-bit palette picture.
   954      works for any destination depth - but is very slow for some."
  1278      works for any destination depth - but is very slow for some."
   955 
  1279 
   956     |f 
  1280     |f 
   957      r g b
  1281      map run last ditherColors first delta
   958      map rMap gMap bMap 
  1282      clr depth grey
   959      run last ditherColors first delta
       
   960      clr depth
       
   961      nDither       "{Class: SmallInteger }"
  1283      nDither       "{Class: SmallInteger }"
   962      nColors       "{Class: SmallInteger }"
  1284      nColors       "{Class: SmallInteger }"
   963      w             "{Class: SmallInteger }"
  1285      w             "{Class: SmallInteger }"
   964      h             "{Class: SmallInteger }"
  1286      h             "{Class: SmallInteger }"
   965      v             "{Class: SmallInteger }"
  1287      v             "{Class: SmallInteger }"
   975     delta := 100 / nDither.
  1297     delta := 100 / nDither.
   976     0 to:nDither-1 do:[:i |
  1298     0 to:nDither-1 do:[:i |
   977 	ditherColors at:i+1 put:(Color grey:(i * delta + first)).
  1299 	ditherColors at:i+1 put:(Color grey:(i * delta + first)).
   978     ].
  1300     ].
   979 
  1301 
   980     rMap := colorMap at:1.
  1302     nColors := colorMap size.
   981     gMap := colorMap at:2.
       
   982     bMap := colorMap at:3.
       
   983     nColors := rMap size.
       
   984     map := Array new:nColors.
  1303     map := Array new:nColors.
   985 
       
   986     1 to:nColors do:[:i |
  1304     1 to:nColors do:[:i |
   987 	r := rMap at:i.
  1305 	clr := colorMap at:i.
   988 	r notNil ifTrue:[
  1306 	grey := clr brightness.
   989 	    g := gMap at:i.
  1307 	map at:i put:(ditherColors at:(v * (nDither - 1)) rounded)
   990 	    b := bMap at:i.
       
   991 	    v := ((3 * r) + (6 * g) + (1 * b)) asInteger.
       
   992 	    " v is now in the range 0 .. 2550 "
       
   993 	    v := (v * (nDither - 1) // 2550) rounded.
       
   994 	    " v is now 0 .. nDither-1 "
       
   995 	    map at:i put:(ditherColors at:(v + 1))
       
   996 	]
       
   997     ].
  1308     ].
   998 
  1309 
   999     "tuning - code below is so slooow"
  1310     "tuning - code below is so slooow"
  1000     "get the patterns, fill form bytes here"
  1311     "get the patterns, fill form bytes here"
  1001 
  1312 
  1393 
  1704 
  1394 magnifyRowFrom:srcBytes offset:srcStart  
  1705 magnifyRowFrom:srcBytes offset:srcStart  
  1395 	  into:dstBytes offset:dstStart factor:mX
  1706 	  into:dstBytes offset:dstStart factor:mX
  1396 
  1707 
  1397     "magnify a single pixel row - can only magnify by integer factors.
  1708     "magnify a single pixel row - can only magnify by integer factors.
  1398      Especially tuned for factors 2,3 and 4."
  1709      Specially tuned for factors 2,3 and 4."
  1399 
  1710 
  1400 %{
  1711 %{
  1401     REGISTER unsigned char *srcP, *dstP;
  1712     REGISTER unsigned char *srcP, *dstP;
  1402     REGISTER unsigned char _byte;
  1713     REGISTER unsigned char _byte;
  1403     int _mag;
  1714     int _mag;
  1466      This is the general magnification method, handling non-integral values"
  1777      This is the general magnification method, handling non-integral values"
  1467 
  1778 
  1468     |mX mY
  1779     |mX mY
  1469      newWidth  "{ Class: SmallInteger }"
  1780      newWidth  "{ Class: SmallInteger }"
  1470      newHeight "{ Class: SmallInteger }"
  1781      newHeight "{ Class: SmallInteger }"
  1471      w          "{ Class: SmallInteger }"
  1782      w         "{ Class: SmallInteger }"
  1472      h         "{ Class: SmallInteger }"
  1783      h         "{ Class: SmallInteger }"
  1473      newImage newBytes
  1784      newImage newBytes
  1474      value     "{ Class: SmallInteger }"
  1785      value     "{ Class: SmallInteger }"
  1475      srcRowIdx "{ Class: SmallInteger }"
  1786      srcRowIdx "{ Class: SmallInteger }"
  1476      srcIndex  "{ Class: SmallInteger }"
  1787      srcIndex  "{ Class: SmallInteger }"
  1516 	    *_dstP++ = _srcRowP[(int)((double)_col / _mX)];
  1827 	    *_dstP++ = _srcRowP[(int)((double)_col / _mX)];
  1517 	}
  1828 	}
  1518     }
  1829     }
  1519 %}
  1830 %}
  1520 .
  1831 .
  1521 "   the above C-code is equivalent to:
  1832 "/   the above C-code is equivalent to:
  1522 
  1833 "/
  1523     dstIndex := 1.
  1834 "/    dstIndex := 1.
  1524     w := newWidth - 1.
  1835 "/    w := newWidth - 1.
  1525     h := newHeight - 1.
  1836 "/    h := newHeight - 1.
  1526     0 to:h do:[:row |
  1837 "/    0 to:h do:[:row |
  1527 	srcRowIdx := (width * (row // mY)) + 1.
  1838 "/        srcRowIdx := (width * (row // mY)) + 1.
  1528 	0 to:w do:[:col |
  1839 "/        0 to:w do:[:col |
  1529 	    srcIndex := srcRowIdx + (col // mX).
  1840 "/            srcIndex := srcRowIdx + (col // mX).
  1530 	    value := bytes at:srcIndex.
  1841 "/            value := bytes at:srcIndex.
  1531 	    newBytes at:dstIndex put:value.
  1842 "/            newBytes at:dstIndex put:value.
  1532 	    dstIndex := dstIndex + 1
  1843 "/            dstIndex := dstIndex + 1
  1533 	]
  1844 "/        ]
  1534     ].
  1845 "/    ].
  1535 "
  1846 "/
  1536 
  1847 
  1537     ^ newImage
  1848     ^ newImage
  1538 ! !
  1849 ! !