51 |
47 |
52 imageDepth |
48 imageDepth |
53 ^ 1 |
49 ^ 1 |
54 ! ! |
50 ! ! |
55 |
51 |
56 !Depth1Image methodsFor:'queries'! |
|
57 |
|
58 bitsPerPixel |
|
59 "return the number of bits per pixel" |
|
60 |
|
61 ^ 1 |
|
62 ! |
|
63 |
|
64 bitsPerRow |
|
65 "return the number of bits in one scanline of the image" |
|
66 |
|
67 ^ width |
|
68 ! |
|
69 |
|
70 bitsPerSample |
|
71 "return the number of bits per sample. |
|
72 The return value is an array of bits-per-plane." |
|
73 |
|
74 ^ #(1) |
|
75 ! |
|
76 |
|
77 bytesPerRow |
|
78 "return the number of bytes in one scanline of the image" |
|
79 |
|
80 |nbytes| |
|
81 |
|
82 nbytes := width // 8. |
|
83 ((width \\ 8) ~~ 0) ifTrue:[ |
|
84 ^ nbytes + 1 |
|
85 ]. |
|
86 ^ nbytes |
|
87 ! |
|
88 |
|
89 samplesPerPixel |
|
90 "return the number of samples per pixel in the image." |
|
91 |
|
92 ^ 1 |
|
93 ! |
|
94 |
|
95 usedColors |
|
96 "return a collection of colors used in the receiver. |
|
97 For depth1 images, this is very easy" |
|
98 |
|
99 photometric ~~ #palette ifTrue:[ |
|
100 ^ Array with:Color white with:Color black. |
|
101 ]. |
|
102 ^ colorMap |
|
103 |
|
104 " |
|
105 (Image fromFile:'bitmaps/garfield.gif') usedColors |
|
106 (Image fromFile:'bitmaps/SBrowser.xbm') usedColors |
|
107 " |
|
108 ! |
|
109 |
|
110 usedValues |
|
111 "return a collection of color values used in the receiver. |
|
112 For depth1 images, this is very easy" |
|
113 |
|
114 ^ #(0 1) |
|
115 ! ! |
|
116 |
|
117 !Depth1Image methodsFor:'accessing'! |
52 !Depth1Image methodsFor:'accessing'! |
118 |
|
119 valueAtX:x y:y |
|
120 "retrieve a pixelValue at x/y; return a number. |
|
121 The interpretation of the returned value depends on the photometric |
|
122 and the colormap. See also Image>>atX:y:) |
|
123 Pixels start at 0@0 for upper left pixel, end at |
|
124 (width-1)@(height-1) for lower right pixel" |
|
125 |
|
126 |bytesPerRow "{Class: SmallInteger}" |
|
127 index "{Class: SmallInteger}" |
|
128 byte "{Class: SmallInteger}" |
|
129 mask "{Class: SmallInteger}"| |
|
130 |
|
131 %{ /* NOCONTEXT */ |
|
132 |
|
133 OBJ b = _INST(bytes); |
|
134 OBJ w = _INST(width); |
|
135 |
|
136 if (__isByteArray(b) && __bothSmallInteger(x, y) && __isSmallInteger(w) ) { |
|
137 int _w = _intVal(w); |
|
138 int _y = _intVal(y); |
|
139 int _x = _intVal(x); |
|
140 unsigned _byte; |
|
141 |
|
142 _byte = _ByteArrayInstPtr(b)->ba_element[(_w + 7) / 8 * _y + (_x / 8)]; |
|
143 RETURN( (_byte & (0x80 >> (_x % 8))) ? _MKSMALLINT(1) : _MKSMALLINT(0) ); |
|
144 } |
|
145 %}. |
|
146 |
|
147 "/ the above is equivalent to: |
|
148 "/ (notice that the code below is evaluated if the bytes-collection is |
|
149 "/ not a byteArray, or the arguments are not integers) |
|
150 |
|
151 "/ bytesPerRow := width // 8. |
|
152 "/ ((width \\ 8) ~~ 0) ifTrue:[ |
|
153 "/ bytesPerRow := bytesPerRow + 1 |
|
154 "/ ]. |
|
155 "/ index := (bytesPerRow * y) + 1 + (x // 8). |
|
156 "/ |
|
157 "/ "left pixel is in high bit" |
|
158 "/ byte := bytes at:index. |
|
159 "/ mask := #(16r80 16r40 16r20 16r10 16r08 16r04 16r02 16r01) at:((x \\ 8) + 1). |
|
160 "/ (byte bitAnd:mask) == 0 ifTrue:[^ 0]. |
|
161 "/ ^ 1 |
|
162 |
|
163 "/ since that cannot happen, we faile here |
|
164 self primitiveFailed. |
|
165 ^ 0 |
|
166 ! |
|
167 |
53 |
168 atX:x y:y |
54 atX:x y:y |
169 "retrieve a pixel at x/y; return a color. |
55 "retrieve a pixel at x/y; return a color. |
170 Pixels start at x=0 , y=0 for upper left pixel, end at |
56 Pixels start at x=0 , y=0 for upper left pixel, end at |
171 x = width-1, y=height-1 for lower right pixel" |
57 x = width-1, y=height-1 for lower right pixel" |
196 self error:'format not supported'. |
82 self error:'format not supported'. |
197 ^ nil |
83 ^ nil |
198 ]. |
84 ]. |
199 value := value + 1. |
85 value := value + 1. |
200 ^ colorMap at:value |
86 ^ colorMap at:value |
201 ! |
|
202 |
|
203 atX:x y:y putValue:aPixelValue |
|
204 "set a pixels value at x/y to aPixelValue. |
|
205 Pixels start at x=0 , y=0 for upper left pixel, end at |
|
206 x = width-1, y=height-1 for lower right pixel" |
|
207 |
|
208 |bytesPerRow "{Class: SmallInteger}" |
|
209 index "{Class: SmallInteger}" |
|
210 byte "{Class: SmallInteger}" |
|
211 mask "{Class: SmallInteger}"| |
|
212 |
|
213 %{ /* NOCONTEXT */ |
|
214 |
|
215 OBJ b = _INST(bytes); |
|
216 OBJ w = _INST(width); |
|
217 |
|
218 if (__isByteArray(b) && __bothSmallInteger(x, y) && __isSmallInteger(w) ) { |
|
219 int _w = _intVal(w); |
|
220 int _y = _intVal(y); |
|
221 int _x = _intVal(x); |
|
222 int _idx; |
|
223 |
|
224 _idx = (_w + 7) / 8 * _y + (_x / 8); |
|
225 if (aPixelValue == _MKSMALLINT(0)) { |
|
226 _ByteArrayInstPtr(b)->ba_element[_idx] &= ~(0x80 >> (_x % 8)); |
|
227 } else { |
|
228 _ByteArrayInstPtr(b)->ba_element[_idx] |= (0x80 >> (_x % 8)); |
|
229 } |
|
230 RETURN( self ); |
|
231 } |
|
232 %}. |
|
233 "fall back code for nonByteArray or nonInteger arguments" |
|
234 |
|
235 bytesPerRow := width // 8. |
|
236 ((width \\ 8) ~~ 0) ifTrue:[ |
|
237 bytesPerRow := bytesPerRow + 1 |
|
238 ]. |
|
239 index := (bytesPerRow * y) + 1 + (x // 8). |
|
240 |
|
241 "left pixel is in high bit" |
|
242 byte := bytes at:index. |
|
243 mask := #(16r80 16r40 16r20 16r10 16r08 16r04 16r02 16r01) at:((x \\ 8) + 1). |
|
244 aPixelValue == 0 ifTrue:[ |
|
245 byte := byte bitAnd:(mask bitInvert) |
|
246 ] ifFalse:[ |
|
247 byte := byte bitOr:mask |
|
248 ]. |
|
249 bytes at:index put:byte |
|
250 ! |
87 ! |
251 |
88 |
252 atX:x y:y put:aColor |
89 atX:x y:y put:aColor |
253 "set the pixel at x/y to aColor. |
90 "set the pixel at x/y to aColor. |
254 Pixels start at x=0 , y=0 for upper left pixel, end at |
91 Pixels start at x=0 , y=0 for upper left pixel, end at |
285 ]. |
122 ]. |
286 " |
123 " |
287 the color to be stored is not in the images colormap |
124 the color to be stored is not in the images colormap |
288 " |
125 " |
289 self error:'invalid color' |
126 self error:'invalid color' |
|
127 ! |
|
128 |
|
129 atX:x y:y putValue:aPixelValue |
|
130 "set a pixels value at x/y to aPixelValue. |
|
131 Pixels start at x=0 , y=0 for upper left pixel, end at |
|
132 x = width-1, y=height-1 for lower right pixel" |
|
133 |
|
134 |bytesPerRow "{Class: SmallInteger}" |
|
135 index "{Class: SmallInteger}" |
|
136 byte "{Class: SmallInteger}" |
|
137 mask "{Class: SmallInteger}"| |
|
138 |
|
139 %{ /* NOCONTEXT */ |
|
140 |
|
141 OBJ b = _INST(bytes); |
|
142 OBJ w = _INST(width); |
|
143 |
|
144 if (__isByteArray(b) && __bothSmallInteger(x, y) && __isSmallInteger(w) ) { |
|
145 int _w = _intVal(w); |
|
146 int _y = _intVal(y); |
|
147 int _x = _intVal(x); |
|
148 int _idx; |
|
149 |
|
150 _idx = (_w + 7) / 8 * _y + (_x / 8); |
|
151 if (aPixelValue == _MKSMALLINT(0)) { |
|
152 _ByteArrayInstPtr(b)->ba_element[_idx] &= ~(0x80 >> (_x % 8)); |
|
153 } else { |
|
154 _ByteArrayInstPtr(b)->ba_element[_idx] |= (0x80 >> (_x % 8)); |
|
155 } |
|
156 RETURN( self ); |
|
157 } |
|
158 %}. |
|
159 "fall back code for nonByteArray or nonInteger arguments" |
|
160 |
|
161 bytesPerRow := width // 8. |
|
162 ((width \\ 8) ~~ 0) ifTrue:[ |
|
163 bytesPerRow := bytesPerRow + 1 |
|
164 ]. |
|
165 index := (bytesPerRow * y) + 1 + (x // 8). |
|
166 |
|
167 "left pixel is in high bit" |
|
168 byte := bytes at:index. |
|
169 mask := #(16r80 16r40 16r20 16r10 16r08 16r04 16r02 16r01) at:((x \\ 8) + 1). |
|
170 aPixelValue == 0 ifTrue:[ |
|
171 byte := byte bitAnd:(mask bitInvert) |
|
172 ] ifFalse:[ |
|
173 byte := byte bitOr:mask |
|
174 ]. |
|
175 bytes at:index put:byte |
|
176 ! |
|
177 |
|
178 valueAtX:x y:y |
|
179 "retrieve a pixelValue at x/y; return a number. |
|
180 The interpretation of the returned value depends on the photometric |
|
181 and the colormap. See also Image>>atX:y:) |
|
182 Pixels start at 0@0 for upper left pixel, end at |
|
183 (width-1)@(height-1) for lower right pixel" |
|
184 |
|
185 |bytesPerRow "{Class: SmallInteger}" |
|
186 index "{Class: SmallInteger}" |
|
187 byte "{Class: SmallInteger}" |
|
188 mask "{Class: SmallInteger}"| |
|
189 |
|
190 %{ /* NOCONTEXT */ |
|
191 |
|
192 OBJ b = _INST(bytes); |
|
193 OBJ w = _INST(width); |
|
194 |
|
195 if (__isByteArray(b) && __bothSmallInteger(x, y) && __isSmallInteger(w) ) { |
|
196 int _w = _intVal(w); |
|
197 int _y = _intVal(y); |
|
198 int _x = _intVal(x); |
|
199 unsigned _byte; |
|
200 |
|
201 _byte = _ByteArrayInstPtr(b)->ba_element[(_w + 7) / 8 * _y + (_x / 8)]; |
|
202 RETURN( (_byte & (0x80 >> (_x % 8))) ? _MKSMALLINT(1) : _MKSMALLINT(0) ); |
|
203 } |
|
204 %}. |
|
205 |
|
206 "/ the above is equivalent to: |
|
207 "/ (notice that the code below is evaluated if the bytes-collection is |
|
208 "/ not a byteArray, or the arguments are not integers) |
|
209 |
|
210 "/ bytesPerRow := width // 8. |
|
211 "/ ((width \\ 8) ~~ 0) ifTrue:[ |
|
212 "/ bytesPerRow := bytesPerRow + 1 |
|
213 "/ ]. |
|
214 "/ index := (bytesPerRow * y) + 1 + (x // 8). |
|
215 "/ |
|
216 "/ "left pixel is in high bit" |
|
217 "/ byte := bytes at:index. |
|
218 "/ mask := #(16r80 16r40 16r20 16r10 16r08 16r04 16r02 16r01) at:((x \\ 8) + 1). |
|
219 "/ (byte bitAnd:mask) == 0 ifTrue:[^ 0]. |
|
220 "/ ^ 1 |
|
221 |
|
222 "/ since that cannot happen, we faile here |
|
223 self primitiveFailed. |
|
224 ^ 0 |
|
225 ! ! |
|
226 |
|
227 !Depth1Image methodsFor:'converting greyscale images'! |
|
228 |
|
229 greyImageAsFormOn:aDevice |
|
230 "convert a greyscale image to a device form" |
|
231 |
|
232 |f| |
|
233 |
|
234 f := Form width:width height:height fromArray:bytes on:aDevice. |
|
235 photometric == #blackIs0 ifTrue:[ |
|
236 f function:#xor. |
|
237 f paint:(Color colorId:1). |
|
238 f fillRectangleX:0 y:0 width:width height:height |
|
239 ]. |
|
240 ^ f |
|
241 ! |
|
242 |
|
243 greyImageAsMonoFormOn:aDevice |
|
244 "convert to a monochrome form - thats easy" |
|
245 |
|
246 ^ self greyImageAsFormOn:aDevice |
|
247 ! ! |
|
248 |
|
249 !Depth1Image methodsFor:'converting palette images'! |
|
250 |
|
251 paletteImageAsPseudoFormOn:aDevice |
|
252 "return a pseudo-deviceForm from the palette image." |
|
253 |
|
254 |f| |
|
255 |
|
256 " |
|
257 this is easy, since Form already supports colorMaps |
|
258 " |
|
259 f := Form width:width height:height fromArray:bytes. |
|
260 f colorMap:colorMap. |
|
261 ^ f |
|
262 ! |
|
263 |
|
264 paletteImageAsTrueColorFormOn:aDevice |
|
265 "since all devices must support monochrome images, and |
|
266 a 2-entry colormap is implemented by ST/X's drawForm methods, |
|
267 we can do this on all color devices as a palette image." |
|
268 |
|
269 ^ self paletteImageAsPseudoFormOn:aDevice |
290 ! ! |
270 ! ! |
291 |
271 |
292 !Depth1Image methodsFor:'enumerating'! |
272 !Depth1Image methodsFor:'enumerating'! |
293 |
|
294 valueAtY:y from:xLow to:xHigh do:aBlock |
|
295 "perform aBlock for each pixelValue from x1 to x2 in row y. |
|
296 The block is passed the color at each pixel. |
|
297 This method allows slighly faster processing of an |
|
298 image than using atX:y:, since some processing can be |
|
299 avoided when going from pixel to pixel. However, for |
|
300 real image processing, specialized methods should be written." |
|
301 |
|
302 |srcIndex "{ Class: SmallInteger }" |
|
303 byte "{ Class: SmallInteger }" |
|
304 mask "{ Class: SmallInteger }" |
|
305 x1 "{ Class: SmallInteger }" |
|
306 x2 "{ Class: SmallInteger }" |
|
307 pixelValue| |
|
308 |
|
309 "this method needs more tuning, if used heavily |
|
310 (fetch 8 bits at once, unroll the loop over these 8 pixels)" |
|
311 |
|
312 x1 := xLow. |
|
313 x2 := xHigh. |
|
314 srcIndex := (self bytesPerRow * y) + 1. |
|
315 |
|
316 "left pixel is in high bit" |
|
317 |
|
318 srcIndex := srcIndex + (x1 // 8). |
|
319 mask := #[2r10000000 |
|
320 2r01000000 |
|
321 2r00100000 |
|
322 2r00010000 |
|
323 2r00001000 |
|
324 2r00000100 |
|
325 2r00000010 |
|
326 2r00000001] at:((x1 \\ 8) + 1). |
|
327 |
|
328 x1 to:x2 do:[:x | |
|
329 byte := bytes at:srcIndex. |
|
330 (byte bitAnd:mask) == 0 ifTrue:[ |
|
331 pixelValue := 0 |
|
332 ] ifFalse:[ |
|
333 pixelValue := 1 |
|
334 ]. |
|
335 aBlock value:x value:pixelValue. |
|
336 |
|
337 mask := mask bitShift:-1. |
|
338 mask == 0 ifTrue:[ |
|
339 mask := 2r10000000. |
|
340 srcIndex := srcIndex + 1 |
|
341 ] |
|
342 ] |
|
343 ! |
|
344 |
273 |
345 atY:y from:xLow to:xHigh do:aBlock |
274 atY:y from:xLow to:xHigh do:aBlock |
346 "perform aBlock for each pixel from x1 to x2 in row y. |
275 "perform aBlock for each pixel from x1 to x2 in row y. |
347 The block is passed the color at each pixel. |
276 The block is passed the color at each pixel. |
348 This method allows slighly faster processing of an |
277 This method allows slighly faster processing of an |
404 mask == 0 ifTrue:[ |
333 mask == 0 ifTrue:[ |
405 mask := 2r10000000. |
334 mask := 2r10000000. |
406 srcIndex := srcIndex + 1 |
335 srcIndex := srcIndex + 1 |
407 ] |
336 ] |
408 ] |
337 ] |
409 ! ! |
338 ! |
410 |
339 |
411 !Depth1Image methodsFor:'converting greyscale images'! |
340 valueAtY:y from:xLow to:xHigh do:aBlock |
412 |
341 "perform aBlock for each pixelValue from x1 to x2 in row y. |
413 greyImageAsFormOn:aDevice |
342 The block is passed the color at each pixel. |
414 "convert a greyscale image to a device form" |
343 This method allows slighly faster processing of an |
415 |
344 image than using atX:y:, since some processing can be |
416 |f| |
345 avoided when going from pixel to pixel. However, for |
417 |
346 real image processing, specialized methods should be written." |
418 f := Form width:width height:height fromArray:bytes on:aDevice. |
347 |
419 photometric == #blackIs0 ifTrue:[ |
348 |srcIndex "{ Class: SmallInteger }" |
420 f function:#xor. |
349 byte "{ Class: SmallInteger }" |
421 f paint:(Color colorId:1). |
350 mask "{ Class: SmallInteger }" |
422 f fillRectangleX:0 y:0 width:width height:height |
351 x1 "{ Class: SmallInteger }" |
423 ]. |
352 x2 "{ Class: SmallInteger }" |
424 ^ f |
353 pixelValue| |
425 ! |
354 |
426 |
355 "this method needs more tuning, if used heavily |
427 greyImageAsMonoFormOn:aDevice |
356 (fetch 8 bits at once, unroll the loop over these 8 pixels)" |
428 "convert to a monochrome form - thats easy" |
357 |
429 |
358 x1 := xLow. |
430 ^ self greyImageAsFormOn:aDevice |
359 x2 := xHigh. |
431 ! ! |
360 srcIndex := (self bytesPerRow * y) + 1. |
432 |
361 |
433 !Depth1Image methodsFor:'converting palette images'! |
362 "left pixel is in high bit" |
434 |
363 |
435 paletteImageAsPseudoFormOn:aDevice |
364 srcIndex := srcIndex + (x1 // 8). |
436 "return a pseudo-deviceForm from the palette image." |
365 mask := #[2r10000000 |
437 |
366 2r01000000 |
438 |f| |
367 2r00100000 |
439 |
368 2r00010000 |
440 " |
369 2r00001000 |
441 this is easy, since Form already supports colorMaps |
370 2r00000100 |
442 " |
371 2r00000010 |
443 f := Form width:width height:height fromArray:bytes. |
372 2r00000001] at:((x1 \\ 8) + 1). |
444 f colorMap:colorMap. |
373 |
445 ^ f |
374 x1 to:x2 do:[:x | |
446 ! |
375 byte := bytes at:srcIndex. |
447 |
376 (byte bitAnd:mask) == 0 ifTrue:[ |
448 paletteImageAsTrueColorFormOn:aDevice |
377 pixelValue := 0 |
449 "since all devices must support monochrome images, and |
378 ] ifFalse:[ |
450 a 2-entry colormap is implemented by ST/X's drawForm methods, |
379 pixelValue := 1 |
451 we can do this on all color devices as a palette image." |
380 ]. |
452 |
381 aBlock value:x value:pixelValue. |
453 ^ self paletteImageAsPseudoFormOn:aDevice |
382 |
|
383 mask := mask bitShift:-1. |
|
384 mask == 0 ifTrue:[ |
|
385 mask := 2r10000000. |
|
386 srcIndex := srcIndex + 1 |
|
387 ] |
|
388 ] |
454 ! ! |
389 ! ! |
455 |
390 |
456 !Depth1Image methodsFor:'magnification'! |
391 !Depth1Image methodsFor:'magnification'! |
457 |
392 |
458 magnifyRowFrom:srcBytes offset:srcStart |
393 magnifyRowFrom:srcBytes offset:srcStart |