ImageEditView.st
changeset 627 e0bf28f61361
parent 624 d98fe4700fc2
child 628 7f36d8a7735f
equal deleted inserted replaced
626:ccc858f815e4 627:e0bf28f61361
     9  other person.  No title to or ownership of the software is
     9  other person.  No title to or ownership of the software is
    10  hereby transferred.
    10  hereby transferred.
    11 "
    11 "
    12 
    12 
    13 ImageView subclass:#ImageEditView
    13 ImageView subclass:#ImageEditView
    14 	instanceVariableNames:'magnification gridMagnification antiAlias pixelSelectColor
    14 	instanceVariableNames:'magnification gridMagnification selectColors imageReaderClass
    15 		modified imageReaderClass makeRedraw resourceClass
    15 		resourceClass resourceSelector editMode mouseKeyColorMode
    16 		resourceSelector'
    16 		undoImage modified coordInfoBlock'
    17 	classVariableNames:''
    17 	classVariableNames:'Clipboard'
    18 	poolDictionaries:''
    18 	poolDictionaries:''
    19 	category:'Views-Misc'
    19 	category:'Views-Misc'
    20 !
    20 !
    21 
    21 
    22 !ImageEditView class methodsFor:'documentation'!
    22 !ImageEditView class methodsFor:'documentation'!
    50         ImageEditView openOn:'bitmaps/gifImages/garfield.gif'
    50         ImageEditView openOn:'bitmaps/gifImages/garfield.gif'
    51         ImageEditView openOnImage:(Image fromFile:'bitmaps/gifImages/garfield.gif')
    51         ImageEditView openOnImage:(Image fromFile:'bitmaps/gifImages/garfield.gif')
    52 "
    52 "
    53 ! !
    53 ! !
    54 
    54 
    55 !ImageEditView class methodsFor:'interface specs'!
       
    56 
       
    57 menuEffects
       
    58     "this window spec was automatically generated by the ST/X MenuEditor"
       
    59 
       
    60     "do not manually edit this - the builder may not be able to
       
    61      handle the specification if its corrupted."
       
    62 
       
    63     "
       
    64      MenuEditor new openOnClass:ImageEditView andSelector:#menuEffects
       
    65      (Menu new fromLiteralArrayEncoding:(ImageEditView menuEffects)) startUp
       
    66     "
       
    67 
       
    68     <resource: #menu>
       
    69 
       
    70     ^
       
    71      
       
    72        #(#Menu
       
    73           
       
    74            #(
       
    75              #(#MenuItem
       
    76                 #'label:' 'flip - vertical'
       
    77                 #'value:' #flipVertical
       
    78             )
       
    79              #(#MenuItem
       
    80                 #'label:' 'flip - horizontal'
       
    81                 #'value:' #flipHorizontal
       
    82             )
       
    83              #(#MenuItem
       
    84                 #'label:' '-'
       
    85             )
       
    86              #(#MenuItem
       
    87                 #'label:' 'rotate - clockwise'
       
    88                 #'value:' #rotateCW
       
    89             )
       
    90              #(#MenuItem
       
    91                 #'label:' 'rotate - counter clockwise'
       
    92                 #'value:' #rotateCCW
       
    93             )
       
    94              #(#MenuItem
       
    95                 #'label:' '-'
       
    96             )
       
    97              #(#MenuItem
       
    98                 #'label:' 'negative'
       
    99                 #'value:' #negative
       
   100             )
       
   101              #(#MenuItem
       
   102                 #'label:' '-'
       
   103             )
       
   104              #(#MenuItem
       
   105                 #'label:' 'resize'
       
   106                 #'value:' #resizeImage
       
   107             )
       
   108           ) nil
       
   109           nil
       
   110       )
       
   111 ! !
       
   112 
       
   113 !ImageEditView class methodsFor:'menu specs'!
       
   114 
       
   115 menu
       
   116     "this window spec was automatically generated by the ST/X MenuEditor"
       
   117 
       
   118     "do not manually edit this - the builder may not be able to
       
   119      handle the specification if its corrupted."
       
   120 
       
   121     "
       
   122      MenuEditor new openOnClass:ImageEditor andSelector:#menu
       
   123      (Menu new fromLiteralArrayEncoding:(ImageEditor menu)) startUp
       
   124     "
       
   125 
       
   126     <resource: #menu>
       
   127 
       
   128     ^
       
   129 
       
   130        #(#Menu
       
   131 
       
   132            #(
       
   133              #(#MenuItem
       
   134                 #'label:' 'about'
       
   135                 #'labelImage:' #(#ResourceRetriever #Launcher #smallAboutIcon)
       
   136                 #'submenuChannel:' #menuAbout
       
   137             )
       
   138              #(#MenuItem
       
   139                 #'label:' 'file'
       
   140                 #'submenu:' 
       
   141                  #(#Menu
       
   142 
       
   143                      #(
       
   144                        #(#MenuItem
       
   145                           #'label:' 'new...'
       
   146                           #'value:' #newImage
       
   147                       )
       
   148                        #(#MenuItem
       
   149                           #'label:' '-'
       
   150                       )
       
   151                        #(#MenuItem
       
   152                           #'label:' 'load from file...'
       
   153                           #'value:' #loadFromFile
       
   154                       )
       
   155                        #(#MenuItem
       
   156                           #'label:' 'load from class..'
       
   157                           #'value:' #loadFromClass
       
   158                       )
       
   159                        #(#MenuItem
       
   160                           #'label:' '-'
       
   161                       )
       
   162                        #(#MenuItem
       
   163                           #'label:' 'save'
       
   164                           #'value:' #saveFile
       
   165                           #'enabled:' #canBeSaved
       
   166                       )
       
   167                        #(#MenuItem
       
   168                           #'label:' 'save as...'
       
   169                           #'value:' #saveFileAs
       
   170                           #'enabled:' #isImageLoaded
       
   171                       )
       
   172                        #(#MenuItem
       
   173                           #'label:' '-'
       
   174                       )
       
   175                        #(#MenuItem
       
   176                           #'label:' 'generate image '
       
   177                           #'value:' #generateImage
       
   178                       )
       
   179                        #(#MenuItem
       
   180                           #'label:' 'generate file access'
       
   181                           #'value:' #generateFileAccess
       
   182                       )
       
   183                        #(#MenuItem
       
   184                           #'label:' '-'
       
   185                       )
       
   186                        #(#MenuItem
       
   187                           #'label:' 'exit'
       
   188                           #'value:' #close
       
   189                       )
       
   190                     ) nil
       
   191                     nil
       
   192                 )
       
   193             )
       
   194              #(#MenuItem
       
   195                 #'label:' 'effects'
       
   196                 #'enabled:' #isImageLoaded
       
   197                 #'submenu:' 
       
   198                  #(#Menu
       
   199 
       
   200                      #(
       
   201                        #(#MenuItem
       
   202                           #'label:' 'flip - vertical'
       
   203                       )
       
   204                        #(#MenuItem
       
   205                           #'label:' 'flip - horizontal'
       
   206                       )
       
   207                        #(#MenuItem
       
   208                           #'label:' '-'
       
   209                       )
       
   210                        #(#MenuItem
       
   211                           #'label:' 'rotate - clockwise'
       
   212                       )
       
   213                        #(#MenuItem
       
   214                           #'label:' 'rotate - counter clockwise'
       
   215                       )
       
   216                        #(#MenuItem
       
   217                           #'label:' '-'
       
   218                       )
       
   219                        #(#MenuItem
       
   220                           #'label:' 'negative'
       
   221                       )
       
   222                        #(#MenuItem
       
   223                           #'label:' '-'
       
   224                       )
       
   225                        #(#MenuItem
       
   226                           #'label:' 'resize'
       
   227                       )
       
   228                     ) nil
       
   229                     nil
       
   230                 )
       
   231             )
       
   232              #(#MenuItem
       
   233                 #'label:' 'convert'
       
   234                 #'enabled:' #isImageLoaded
       
   235                 #'submenu:' 
       
   236                  #(#Menu
       
   237 
       
   238                      #(
       
   239                        #(#MenuItem
       
   240                           #'label:' '8-plane'
       
   241                           #'argument:' 'color 8-plane'
       
   242                           #'indication:' #'mode:value:'
       
   243                       )
       
   244                        #(#MenuItem
       
   245                           #'label:' '4-plane'
       
   246                           #'argument:' 'color 4-plane'
       
   247                           #'indication:' #'mode:value:'
       
   248                       )
       
   249                        #(#MenuItem
       
   250                           #'label:' '2-plane'
       
   251                           #'argument:' 'color 2-plane'
       
   252                           #'indication:' #'mode:value:'
       
   253                       )
       
   254                        #(#MenuItem
       
   255                           #'label:' '-'
       
   256                       )
       
   257                        #(#MenuItem
       
   258                           #'label:' 'mono'
       
   259                           #'argument:' 'mono'
       
   260                           #'indication:' #'mode:value:'
       
   261                       )
       
   262                     ) nil
       
   263                     nil
       
   264                 )
       
   265             )
       
   266              #(#MenuItem
       
   267                 #'label:' 'history'
       
   268                 #'enabled:' #hasHistory
       
   269                 #'submenuChannel:' #menuHistory
       
   270             )
       
   271              #(#MenuItem
       
   272                 #'label:' 'help'
       
   273                 #'submenuChannel:' #menuHelp
       
   274             )
       
   275           ) nil
       
   276           nil
       
   277       )
       
   278 
       
   279 ! !
       
   280 
       
   281 !ImageEditView class methodsFor:'startup'!
    55 !ImageEditView class methodsFor:'startup'!
   282 
    56 
   283 openOn:aFileName
    57 openOn:aFileName
   284     "startup an image editor on an image read from a file"
    58     "startup an image editor on an image read from a file"
   285 
    59 
   301     "Modified: / 31.10.1997 / 16:19:23 / cg"
    75     "Modified: / 31.10.1997 / 16:19:23 / cg"
   302 ! !
    76 ! !
   303 
    77 
   304 !ImageEditView methodsFor:'accessing'!
    78 !ImageEditView methodsFor:'accessing'!
   305 
    79 
       
    80 coordInfoBlock: aBlock
       
    81 
       
    82     coordInfoBlock := aBlock
       
    83 !
       
    84 
       
    85 gridMagnification: aPoint
       
    86 
       
    87     gridMagnification := aPoint
       
    88 !
       
    89 
       
    90 imageReaderClass
       
    91 
       
    92     ^imageReaderClass
       
    93 !
       
    94 
       
    95 magnification
       
    96 
       
    97     ^magnification
       
    98 
       
    99 !
       
   100 
       
   101 magnification:aPoint
       
   102 
       
   103     magnification ~= aPoint
       
   104     ifTrue:
       
   105     [
       
   106         magnification := aPoint asPoint.
       
   107         self scrollToTopLeft.
       
   108         self contentsChanged.
       
   109         self invalidate.
       
   110     ].
       
   111 !
       
   112 
       
   113 resourceClass
       
   114 
       
   115     ^resourceClass
       
   116 !
       
   117 
       
   118 resourceClass: aClassOrSymbol
       
   119 
       
   120     resourceClass := aClassOrSymbol isClass ifTrue: [aClassOrSymbol name] ifFalse: [aClassOrSymbol asSymbol].
       
   121 
       
   122 !
       
   123 
       
   124 resourceMessage
       
   125 
       
   126     ^resourceClass, ' ', resourceSelector
       
   127 !
       
   128 
       
   129 resourceMessage: aMessage
       
   130 
       
   131     (aMessage notNil and: [aMessage trimBlanks size > 0])
       
   132     ifTrue:
       
   133     [
       
   134         resourceClass := aMessage readStream nextWord asSymbol.
       
   135         resourceSelector := aMessage reversed readStream nextWord reverse asSymbol.
       
   136     ]
       
   137     ifFalse:
       
   138     [
       
   139         ^nil
       
   140     ].
       
   141 
       
   142     ^self resourceClass: resourceClass selector: resourceSelector
       
   143 
       
   144    
       
   145 !
       
   146 
       
   147 resourceSelector
       
   148 
       
   149     ^resourceSelector
       
   150 !
       
   151 
       
   152 resourceSelector: aStringOrSymbol
       
   153 
       
   154     resourceSelector := aStringOrSymbol asSymbol
       
   155 !
       
   156 
       
   157 selectedColor
       
   158 
       
   159    ^selectColors at: mouseKeyColorMode
       
   160 !
       
   161 
       
   162 selectedColor: aColor
       
   163 
       
   164     selectColors at: mouseKeyColorMode put: aColor
       
   165 ! !
       
   166 
       
   167 !ImageEditView methodsFor:'accessing menu'!
       
   168 
       
   169 menuEdit
       
   170     "this window spec was automatically generated by the UI Builder"
       
   171 
       
   172     ^ self class menuEdit
       
   173 
       
   174 
       
   175 ! !
       
   176 
       
   177 !ImageEditView methodsFor:'drawing'!
       
   178 
       
   179 redrawImageX:x y:y width:w height:h
       
   180     |ih iw dotW dotH minX maxX minY maxY color last lastY runW x0 xI yI maskColor|
       
   181 
       
   182     ih := image height.
       
   183     iw := image width.
       
   184     dotW := magnification x.
       
   185     dotH := magnification y.
       
   186 
       
   187     minX := (x // dotW).
       
   188     minX >= iw ifTrue:[minX := iw - 1].
       
   189     minY := (y // dotH).
       
   190     minY >= ih ifTrue:[minY := ih - 1].
       
   191     maxX := (x + w) // dotW + 1.
       
   192     maxX > iw ifTrue:[maxX := iw].
       
   193     maxY := (y + h) // dotH + 1.
       
   194     maxY > ih ifTrue:[maxY := ih].
       
   195 
       
   196     lastY := -1.
       
   197 
       
   198     x0 := minX.
       
   199     runW := 0.
       
   200     maskColor := false.
       
   201     image colorsFromX:minX y:minY toX:maxX-1 y:maxY-1 do:
       
   202     [:xx :yy :color|
       
   203 
       
   204         yy ~~ lastY ifTrue:
       
   205         [
       
   206             runW ~~ 0 ifTrue:
       
   207             [
       
   208                 |origin|
       
   209                 origin := (x0 * dotW + margin)@(lastY * dotH + margin).
       
   210                 self fillRectangle: (origin extent: (runW@dotH)).                    
       
   211                 0 to: runW by: dotW do: [:xxx| self drawFrameAt: ((origin x + xxx) @origin y)].
       
   212                 maskColor ifTrue:
       
   213                 [
       
   214                     self drawMaskPointAt: origin
       
   215                 ].
       
   216                 runW := 0.
       
   217             ]. 
       
   218             x0 := xx.
       
   219             lastY := yy.
       
   220         ]. 
       
   221 
       
   222         color ~~ last ifTrue:
       
   223         [
       
   224             runW ~~ 0 ifTrue:
       
   225             [
       
   226                 |origin|
       
   227                 origin := (x0 * dotW + margin)@(yy * dotH + margin).
       
   228                 self fillRectangle: (origin extent: (runW@dotH)).
       
   229                 0 to: runW by: dotW do: [:xxx| self drawFrameAt: ((origin x + xxx) @origin y)].
       
   230                 maskColor ifTrue:
       
   231                 [
       
   232                     self drawMaskPointAt: origin
       
   233                 ].
       
   234                 runW := 0.
       
   235             ].
       
   236 
       
   237             self paint: (last := color).
       
   238             image mask notNil ifTrue:
       
   239             [  
       
   240                 maskColor := false.
       
   241                 (image mask colorAt: xx@yy) = Color black ifTrue:
       
   242                 [
       
   243                     self paint: (last := self viewBackground).
       
   244                     maskColor := true.
       
   245                 ].
       
   246                 last := nil.
       
   247             ].
       
   248             runW := 0.
       
   249             x0 := xx.
       
   250         ].  
       
   251         runW := runW + dotW
       
   252     ].
       
   253     runW ~~ 0 ifTrue:
       
   254     [
       
   255         |origin|
       
   256         origin := (x0 * dotW + margin)@(lastY * dotH + margin).
       
   257         self fillRectangle: (origin extent: runW@dotH).
       
   258         0 to: runW by: dotW do: [:xxx| self drawFrameAt: ((origin x + xxx) @origin y)].
       
   259         maskColor ifTrue:
       
   260         [
       
   261             self drawMaskPointAt: origin.
       
   262         ].
       
   263         runW := 0.
       
   264     ].
       
   265 !
       
   266 
       
   267 redrawX:x y:y width:w height:h
       
   268     |ih iw xI yI|
       
   269 
       
   270     image isNil ifTrue:[^self].
       
   271 
       
   272     magnification = (1@1) ifTrue:
       
   273     [
       
   274         super redrawX:x y:y width:w height:h.
       
   275         self drawFrame.
       
   276         ^ self
       
   277     ].
       
   278     self clippingRectangle: (x@y extent: w@h). 
       
   279 
       
   280     self redrawImageX:x y:y width:w height:h.
       
   281 
       
   282     "/ right of image ?
       
   283     adjust == #center ifTrue:
       
   284     [
       
   285         xI := (width - (margin * 2) - ih) // 2.
       
   286         yI := (height - (margin * 2) - iw) // 2.
       
   287     ]
       
   288     ifFalse:
       
   289     [
       
   290         xI := yI := margin
       
   291     ].
       
   292     (x + w - 1) > (xI + (magnification x * image width)) ifTrue:
       
   293     [
       
   294         self clearRectangleX:(xI + (magnification x * image width))
       
   295                            y:y
       
   296                        width:(x + w - (magnification x * image width) - xI)
       
   297                       height:h
       
   298     ].
       
   299     (y + h - 1) > (yI + (magnification y * image height)) ifTrue:
       
   300     [
       
   301         self clearRectangleX:margin
       
   302                            y:(yI + (magnification y * image height))
       
   303                        width:w
       
   304                       height:(y + h - (magnification y * image height) - yI)  
       
   305     ].
       
   306     self drawFrame.
       
   307     self clippingRectangle: nil.
       
   308 ! !
       
   309 
       
   310 !ImageEditView methodsFor:'edit modes'!
       
   311 
       
   312 editMode
       
   313 
       
   314     editMode isNil ifTrue: [editMode := 'point'].
       
   315     ^editMode
       
   316 !
       
   317 
       
   318 editMode:aMode
       
   319 
       
   320     editMode := aMode
       
   321 !
       
   322 
       
   323 mouseKeyColorMode
       
   324 
       
   325     ^mouseKeyColorMode printString
       
   326 !
       
   327 
       
   328 mouseKeyColorMode:aMode
       
   329 
       
   330     mouseKeyColorMode := aMode asInteger
       
   331 ! !
       
   332 
       
   333 !ImageEditView methodsFor:'event handling'!
       
   334 
       
   335 buttonMotion:state x:x y:y
       
   336 
       
   337     self selectedColor notNil & image notNil & (self imageContainsPoint: x@y) & (editMode = 'point')
       
   338         ifTrue: [^self pointAt: x@y].
       
   339 
       
   340     ^super buttonMotion:state x:x y:y
       
   341 
       
   342 !
       
   343 
       
   344 buttonPress:button x:x y:y
       
   345 
       
   346     self selectedColor notNil & image notNil & (self imageContainsPoint: x@y)
       
   347     ifTrue:
       
   348     [   
       
   349         undoImage := image copy.
       
   350         mouseKeyColorMode := button.
       
   351         (editMode = 'point')   ifTrue: [self pointAt: x@y].
       
   352         (editMode = 'replace') ifTrue: [self replaceAt: x@y].
       
   353         (editMode = 'paste')   ifTrue: [self pasteAt: x@y].
       
   354         (editMode = 'box') | (editMode = 'copy') ifTrue: [self boxAt: x@y].
       
   355         ^self
       
   356     ].
       
   357     
       
   358     ^super buttonPress:button x:x y:y
       
   359 ! !
       
   360 
       
   361 !ImageEditView methodsFor:'image drawing'!
       
   362 
       
   363 boxAt: aPoint
       
   364 
       
   365     |firstPoint currentPoint lastCurrentPoint currentExtent imageFirstPoint imageExtent|
       
   366 
       
   367     firstPoint := lastCurrentPoint := aPoint//magnification*magnification.
       
   368     [Display anyButtonPressed]
       
   369     whileTrue:
       
   370     [   
       
   371         currentPoint := (0@0) max: (image extent * magnification min: (self translation negated + (device translatePoint: self sensor mousePoint from:device rootView id to:self id))).
       
   372         currentPoint := currentPoint//magnification*magnification.
       
   373         currentExtent := (firstPoint - currentPoint) abs.
       
   374         currentPoint ~= lastCurrentPoint ifTrue:
       
   375         [
       
   376             self redraw: ((firstPoint min: lastCurrentPoint) - 1 extent: (firstPoint - lastCurrentPoint) abs + 2).
       
   377             editMode = 'copy'
       
   378             ifTrue:
       
   379             [
       
   380                 self xoring: [self fillRectangle: ((firstPoint min: currentPoint) + 1 extent: currentExtent - 1)]
       
   381             ].
       
   382             editMode = 'box'
       
   383             ifTrue:
       
   384             [
       
   385                 self selectedColor ~= Color noColor
       
   386                     ifTrue: [self paint: self selectedColor]
       
   387                     ifFalse: [self paint: self viewBackground].
       
   388                 self fillRectangle: ((firstPoint min: currentPoint) + 1 extent: currentExtent - 1)
       
   389             ]. 
       
   390         ]. 
       
   391         self drawLabel: currentPoint//magnification.
       
   392         lastCurrentPoint := currentPoint.
       
   393     ].
       
   394 
       
   395     imageFirstPoint := (firstPoint min: currentPoint)//magnification.
       
   396     imageExtent := currentExtent//magnification.
       
   397     editMode = 'box'
       
   398     ifTrue:
       
   399     [
       
   400         self selectedColor ~= Color noColor
       
   401         ifTrue:
       
   402         [   
       
   403             image mask notNil ifTrue: [image mask fillRectangleX: imageFirstPoint x y: imageFirstPoint y width: imageExtent x height: imageExtent y with:Color white].
       
   404             image fillRectangleX: imageFirstPoint x y: imageFirstPoint y width: imageExtent x height: imageExtent y with: self selectedColor.
       
   405             self paint: self selectedColor.
       
   406         ] 
       
   407         ifFalse:
       
   408         [
       
   409             image fillRectangleX: imageFirstPoint x y: imageFirstPoint y width: imageExtent x height: imageExtent y with: ((image colorMap includes: Color black) ifTrue: [Color black] ifFalse: [image colorMap first]).
       
   410             image mask notNil ifTrue: [image mask fillRectangleX: imageFirstPoint x y: imageFirstPoint y width: imageExtent x height: imageExtent y with: Color black].
       
   411             self paint: self viewBackground.
       
   412         ].
       
   413         image restored.
       
   414         modified := true.
       
   415     ].
       
   416     editMode = 'copy'
       
   417     ifTrue:
       
   418     [      
       
   419         Clipboard := image subImageIn: (imageFirstPoint extent: imageExtent - 1)
       
   420     ].
       
   421     self redraw: ((firstPoint min: currentPoint) - 1 extent: (firstPoint - currentPoint) abs + 2).    
       
   422 !
       
   423 
       
   424 pasteAt: aPoint
       
   425 
       
   426     Object errorSignal handle:
       
   427     [:ex|
       
   428         WarningBox warn: 'Pasting into this image failed!!'.
       
   429     ] 
       
   430     do:
       
   431     [   
       
   432         |imagePoint|
       
   433         imagePoint := aPoint//magnification.
       
   434         image copyFrom: Clipboard x:0 y:0 toX: imagePoint x y: imagePoint y width: Clipboard width height: Clipboard height.
       
   435         self redraw: (imagePoint * magnification extent: (Clipboard extent * magnification)).
       
   436         self drawLabel: imagePoint.
       
   437         image restored.
       
   438         modified := true.
       
   439     ]
       
   440 !
       
   441 
       
   442 pointAt: aPoint
       
   443 
       
   444     |imagePoint|
       
   445     imagePoint := aPoint//magnification.
       
   446     self selectedColor ~= Color noColor
       
   447     ifTrue:
       
   448     [   
       
   449         image mask notNil ifTrue: [image mask colorAt: imagePoint put: Color white].
       
   450         image colorAt: imagePoint put: self selectedColor.
       
   451         self paint: self selectedColor.
       
   452     ] 
       
   453     ifFalse:
       
   454     [
       
   455         image colorAt: imagePoint put: ((image colorMap includes: Color black) ifTrue: [Color black] ifFalse: [image colorMap first]).
       
   456         image mask notNil ifTrue: [image mask colorAt: imagePoint put: Color black].
       
   457         self paint:self viewBackground.
       
   458     ].
       
   459 
       
   460     self fillRectangle: (imagePoint * magnification + 1 extent: magnification).
       
   461     self selectedColor = Color noColor
       
   462     ifTrue:
       
   463     [       
       
   464         self drawMaskPointAt: imagePoint * magnification + 1.
       
   465     ].
       
   466     self drawFrameAt: aPoint.
       
   467     self drawLabel: imagePoint.
       
   468     image restored.
       
   469     modified := true.
       
   470 !
       
   471 
       
   472 replaceAt: aPoint
       
   473 
       
   474     |imagePoint|
       
   475     imagePoint := aPoint//magnification.
       
   476     self selectedColor ~= Color noColor
       
   477     ifTrue:
       
   478     [   
       
   479         image mask notNil ifTrue: [image mask fillAround: imagePoint withColor: Color white].
       
   480         image fillAround: imagePoint withColor: self selectedColor.
       
   481         self paint: self selectedColor.
       
   482     ] 
       
   483     ifFalse:
       
   484     [
       
   485         image mask notNil ifTrue: [image mask fillAround: imagePoint withColor: Color black].
       
   486         self paint:self viewBackground.
       
   487     ].
       
   488     self drawLabel: imagePoint.
       
   489     self invalidate.
       
   490     image restored.
       
   491     modified := true.
       
   492 
       
   493 !
       
   494 
       
   495 undo
       
   496 
       
   497     undoImage notNil
       
   498     ifTrue:
       
   499     [
       
   500         modified := false.
       
   501         self image: undoImage.
       
   502         self invalidate
       
   503     ]
       
   504 ! !
       
   505 
       
   506 !ImageEditView methodsFor:'image editing'!
       
   507 
       
   508 flipHorizontal
       
   509 
       
   510     self image: image flipHorizontal.
       
   511 
       
   512 !
       
   513 
       
   514 flipVertical
       
   515 
       
   516     self image: image flipVertical.
       
   517 
       
   518 !
       
   519 
       
   520 negativeImage
       
   521 
       
   522     self image: image negative.
       
   523 
       
   524 !
       
   525 
       
   526 resizeImage
       
   527 
       
   528     |b newSize|
       
   529 
       
   530     b := EnterBox new.
       
   531     b title:'resize image'.
       
   532     b okText:'apply'.
       
   533     b abortText:'abort'.
       
   534     b initialText:image extent printString.
       
   535     b showAtPointer.
       
   536     (newSize := Object readFromString: b contents onError:nil) notNil
       
   537     ifTrue:
       
   538     [
       
   539         self image: (image magnifiedBy: newSize/image extent)
       
   540     ].
       
   541 !
       
   542 
       
   543 rotateImage
       
   544 
       
   545     |b rotation|
       
   546 
       
   547     b := EnterBox new.
       
   548     b title:'rotate image'.
       
   549     b okText:'apply'.
       
   550     b abortText:'abort'.
       
   551     b initialText: '0'.
       
   552     b showAtPointer.
       
   553     (rotation := Object readFromString: b contents onError:nil) notNil
       
   554     ifTrue:
       
   555     [   Object errorSignal handle:
       
   556         [:ex|
       
   557             WarningBox warn: 'Image rotation failed.\' withCRs, 'An increase of image depth could help.'.
       
   558         ] 
       
   559         do:
       
   560         [   
       
   561             self image: (image hardRotated: rotation)
       
   562         ]
       
   563     ].
       
   564 ! !
       
   565 
       
   566 !ImageEditView methodsFor:'image emphasis'!
       
   567 
       
   568 drawFrame
       
   569 
       
   570     self paint:Color black.
       
   571     "self lineWidth: (magnification x//3 min: 3). "
       
   572     self displayRectangle: ((0@0) extent:(image extent * magnification) + 2).
       
   573     self lineWidth:1.
       
   574 !
       
   575 
       
   576 drawFrameAt: aPoint
       
   577 
       
   578     magnification > gridMagnification
       
   579     ifTrue:
       
   580     [   
       
   581         |lineStartingPoint lineEndingPoint oldColor|
       
   582         lineStartingPoint := aPoint//magnification*magnification.
       
   583         lineEndingPoint   := aPoint//magnification*magnification + magnification.
       
   584         oldColor := self paint.
       
   585         self xoring:
       
   586         [
       
   587             self displayLineFrom: lineEndingPoint 
       
   588                               to: (lineEndingPoint x)@(lineStartingPoint y).
       
   589             self displayLineFrom: lineEndingPoint 
       
   590                               to: (lineStartingPoint x)@(lineEndingPoint y).
       
   591         ].
       
   592         self paint: oldColor.
       
   593     ]
       
   594 !
       
   595 
       
   596 drawLabel: aLabel
       
   597     coordInfoBlock notNil
       
   598     ifTrue:
       
   599     [         
       
   600         coordInfoBlock value: aLabel printString
       
   601     ]
       
   602 !
       
   603 
       
   604 drawMaskPointAt: aPoint
       
   605 
       
   606     |sizeOfMaskPoint|
       
   607     sizeOfMaskPoint := magnification//3.
       
   608     self xoring: [self fillRectangle: (aPoint + sizeOfMaskPoint extent: sizeOfMaskPoint)].
       
   609    
       
   610 ! !
       
   611 
       
   612 !ImageEditView methodsFor:'image setting'!
       
   613 
       
   614 image:anImage
       
   615 
       
   616     (anImage isImage and: [image isNil or: [self checkModified]])
       
   617     ifTrue:
       
   618     [
       
   619         super image: anImage.
       
   620         image photometric = #palette
       
   621         ifTrue:
       
   622         [
       
   623             (image usedColors includes: selectColors first) ifFalse: [selectColors at: 1 put: nil].
       
   624             (image usedColors includes: selectColors last) ifFalse: [selectColors at: 2 put: nil].
       
   625         ].
       
   626         ^self
       
   627     ].
       
   628     ^nil
       
   629 !
       
   630 
       
   631 loadFromFile: aFileName
       
   632 
       
   633     |fileName newImage|
       
   634     fileName := aFileName asFilename.
       
   635 
       
   636     Object errorSignal handle:
       
   637     [:exeption|
       
   638         WarningBox warn: exeption errorString.
       
   639         ^nil
       
   640     ] 
       
   641     do:
       
   642     [
       
   643         newImage := Image fromFile: fileName name.
       
   644     ].
       
   645 
       
   646     (self image: newImage) notNil
       
   647     ifTrue:
       
   648     [
       
   649         imageReaderClass := ImageReader allSubclasses detect: [:cls| cls isValidImageFile:fileName name] ifNone:
       
   650             [WarningBox warn: 'Unknown image file format'. ^nil].
       
   651     ]
       
   652 !
       
   653 
       
   654 resourceClass: aClassOrSymbol selector: aStringOrSymbol
       
   655 
       
   656     |aClass|
       
   657     imageReaderClass := nil.
       
   658     self resourceClass: aClassOrSymbol.
       
   659     self resourceSelector: aStringOrSymbol.
       
   660     aClass := Smalltalk at: resourceClass. 
       
   661     (aClass isClass and: [aClass class implements: resourceSelector])
       
   662     ifTrue:
       
   663     [ 
       
   664         ^self image: (aClass perform: resourceSelector) copy
       
   665     ].
       
   666     ^nil
       
   667 ! !
       
   668 
       
   669 !ImageEditView methodsFor:'initialization'!
       
   670 
       
   671 initialize
       
   672 
       
   673     super initialize.
       
   674 
       
   675     magnification := 1@1.
       
   676     gridMagnification := 8@8.
       
   677     modified := false.
       
   678     mouseKeyColorMode := 1.
       
   679     resourceClass := resourceSelector := ''.
       
   680     selectColors := Array with: nil with: nil.
       
   681     self menuHolder:self; menuPerformer:self; menuMessage:#imageMenu. 
       
   682 ! !
       
   683 
       
   684 !ImageEditView methodsFor:'menu actions'!
       
   685 
       
   686 changeMagnification
       
   687     |b newMag|
       
   688 
       
   689     b := EnterBox new.
       
   690     b title:'magnification (magX @ magY)'.
       
   691     b okText:'apply'.
       
   692     b abortText:'abort'.
       
   693     b action:[:string | newMag := (Object readFromString:string onError:nil)].
       
   694     b initialText:(magnification printString).
       
   695     b showAtPointer.
       
   696 
       
   697     newMag notNil ifTrue:[
       
   698         self magnification:newMag.
       
   699     ].
       
   700 
       
   701     "Modified: 31.7.1997 / 11:43:12 / cg"
       
   702 !
       
   703 
       
   704 loadFromClass
       
   705 
       
   706     self resourceMessage: (ResourceBrowserView
       
   707         openOnSuperclass: ApplicationModel
       
   708         class: self resourceClass
       
   709         selector: self resourceSelector
       
   710         resourceTypes: #(#image #fileImage))
       
   711         
       
   712 
       
   713 !
       
   714 
       
   715 loadFromUser
       
   716 
       
   717     self image: 
       
   718         ((Image fromUser)
       
   719             asDitheredTrueColor8FormOn: Display)
       
   720 
       
   721 !
       
   722 
       
   723 print
       
   724     |stream psgc|
       
   725 
       
   726     image isNil ifTrue: [^nil].
       
   727 
       
   728     Printer supportsPostscript ifFalse:[
       
   729         self warn:'need a postscript printer'.
       
   730         ^ self
       
   731     ].
       
   732 
       
   733     stream := Printer newNative.
       
   734     stream isNil ifTrue:[
       
   735         self warn:'cannot open printer stream'.
       
   736         ^ nil
       
   737     ].
       
   738 
       
   739     self withWaitCursorDo:[
       
   740         psgc := PSGraphicsContext on:stream. "/  extent:(1.0 @ 1.0).
       
   741         psgc displayForm: (image magnifiedBy: magnification) x:0 y:0.
       
   742         psgc close.
       
   743     ]
       
   744 !
       
   745 
       
   746 save
       
   747 
       
   748     Object errorSignal handle:
       
   749     [:ex|
       
   750         WarningBox warn: ex errorString.
       
   751         ^nil                                 
       
   752     ] 
       
   753     do:
       
   754     [   
       
   755         |fileName|
       
   756         image isNil ifTrue: [^self error: 'No image to save!!'].
       
   757         image fileName isNil ifTrue: [^self error: 'No file name for image detected!!'].
       
   758         fileName := image fileName asFilename.
       
   759         (fileName suffix = 'tiff') | (fileName suffix = 'tif') ifTrue: [imageReaderClass := TIFFReader].
       
   760         fileName suffix = 'xpm' ifTrue: [imageReaderClass := XPMReader].
       
   761         fileName suffix = 'xbm' ifTrue: [imageReaderClass := XBMReader].
       
   762         fileName suffix = 'gif' ifTrue: [imageReaderClass := GIFReader].
       
   763         (fileName suffix = 'jpg') | (fileName suffix = 'jpeg') ifTrue: [imageReaderClass := JPEGReader].
       
   764         imageReaderClass isNil ifTrue: [imageReaderClass := XPMReader. image fileName: image fileName, '.xpm'].
       
   765         
       
   766         image saveOn: image fileName using: imageReaderClass.
       
   767         modified := false.
       
   768     ]
       
   769        
       
   770 !
       
   771 
       
   772 saveAs
       
   773     "save contents into a file 
       
   774      - ask user for filename using a fileSelectionBox."
       
   775 
       
   776     self saveImageFileAs
       
   777 !
       
   778 
       
   779 saveAsMethod
       
   780 
       
   781     Object errorSignal handle:
       
   782     [:ex|
       
   783         WarningBox warn: ex errorString.
       
   784         ^nil                                 
       
   785     ] 
       
   786     do:
       
   787     [   
       
   788         |compileString stream aClass|  
       
   789         stream := WriteStream on: ''.
       
   790         self resourceSelector trimBlanks size = 0 ifTrue: [^self error: 'No image selector detected'].
       
   791         (aClass := Smalltalk at: self resourceClass) isClass ifFalse: [^self error: 'No class for image selector detected'].
       
   792         self image storeOn: stream.
       
   793         compileString :=
       
   794             self resourceSelector,
       
   795             '\\' withCRs,
       
   796             '    <resource: #image>\' withCRs,
       
   797             '    ^',
       
   798             stream contents.   
       
   799         ByteCodeCompiler compile: compileString forClass: aClass class inCategory: 'resources'.
       
   800         modified := false.
       
   801     ]
       
   802 !
       
   803 
       
   804 saveImageFileAs
       
   805 
       
   806     |aFileName|
       
   807 
       
   808     (aFileName := (FileBrowserView requestFileName: self image fileName fileFilters: #('*.xpm' '*.gif'))) notNil
       
   809     ifTrue:
       
   810     [
       
   811         self saveImageFileAs: aFileName
       
   812     ].
       
   813 !
       
   814 
       
   815 saveImageFileAs: aFileName
       
   816 
       
   817     image notNil
       
   818     ifTrue:
       
   819     [
       
   820         image fileName: aFileName.
       
   821         self save
       
   822     ]
       
   823     ifFalse:
       
   824     [
       
   825         WarningBox warn: 'No image detected'
       
   826     ]
       
   827 ! !
       
   828 
       
   829 !ImageEditView methodsFor:'queries'!
       
   830 
   306 checkModified
   831 checkModified
   307 
   832 
   308     modified
   833     modified ifTrue:
   309     ifTrue:
       
   310     [
   834     [
   311         |aBox|
   835         |aBox|
   312         aBox := YesNoBox title:'Image was modified'.        
   836         aBox := YesNoBox title:'Image was modified'.        
   313         aBox noText:'abort'.
   837         aBox noText:'abort'.
   314         aBox yesText:'ignore'.
   838         aBox yesText:'ignore'.
   317         modified := false
   841         modified := false
   318     ].
   842     ].
   319     ^true
   843     ^true
   320 !
   844 !
   321 
   845 
   322 gridMagnification
       
   323 
       
   324     ^gridMagnification
       
   325 
       
   326 !
       
   327 
       
   328 gridMagnification:aPathName
       
   329 
       
   330     gridMagnification := aPathName
       
   331 !
       
   332 
       
   333 image:anImage
       
   334 
       
   335     |oldMag|
       
   336     (anImage isImage and: [image isNil or: [self checkModified]])
       
   337     ifTrue:
       
   338     [
       
   339         oldMag := magnification.
       
   340         magnification := 1@1.
       
   341         super image: anImage.
       
   342         self magnification:oldMag.
       
   343         ^image
       
   344     ].
       
   345     ^nil
       
   346 !
       
   347 
       
   348 imageReaderClass
       
   349 
       
   350     ^imageReaderClass
       
   351 !
       
   352 
       
   353 magnification
       
   354 
       
   355     ^magnification
       
   356 
       
   357 !
       
   358 
       
   359 magnification:aMagnificationPoint
       
   360 
       
   361     magnification ~= aMagnificationPoint
       
   362     ifTrue:
       
   363     [
       
   364         magnification := aMagnificationPoint asPoint.
       
   365         self scrollToTopLeft.
       
   366         self contentsChanged.
       
   367         self invalidate.
       
   368     ].
       
   369 !
       
   370 
       
   371 makeRedraw:aBooelean
       
   372 
       
   373     makeRedraw := aBooelean
       
   374 !
       
   375 
       
   376 openFile: aFileName
       
   377 
       
   378     |fileName newImage|
       
   379     fileName := aFileName asFilename.
       
   380 
       
   381     Object errorSignal handle:
       
   382     [:exeption|
       
   383         WarningBox warn: exeption errorString.
       
   384         ^nil
       
   385     ] 
       
   386     do:
       
   387     [
       
   388         newImage := Image fromFile: fileName name.
       
   389     ].
       
   390 
       
   391     (self image: newImage) notNil
       
   392     ifTrue:
       
   393     [
       
   394         imageReaderClass := ImageReader allSubclasses detect: [:cls| cls isValidImageFile:fileName name] ifNone:
       
   395             [WarningBox warn: 'Unknown image file format'. ^nil].
       
   396     ]
       
   397 !
       
   398 
       
   399 pixelSelectColor: aColor
       
   400 
       
   401     pixelSelectColor := aColor
       
   402 !
       
   403 
       
   404 resourceClass
       
   405 
       
   406     ^resourceClass
       
   407 !
       
   408 
       
   409 resourceClass: aClass
       
   410 
       
   411     resourceClass := aClass
       
   412 !
       
   413 
       
   414 resourceClass: aClass selector: aSelector
       
   415 
       
   416     resourceClass := aClass.
       
   417     resourceSelector := aSelector.
       
   418     (aClass isClass and: [aClass class implements: aSelector])
       
   419     ifTrue:
       
   420     [   
       
   421         ^self image: (aClass perform: aSelector)
       
   422     ].
       
   423     ^nil
       
   424 !
       
   425 
       
   426 resourceMessage
       
   427 
       
   428     (resourceClass value notNil and: [resourceSelector value notNil])
       
   429     ifTrue:
       
   430     [ 
       
   431         ^resourceClass name, ' ', resourceSelector
       
   432     ].
       
   433     ^nil
       
   434 !
       
   435 
       
   436 resourceMessage: aMessage
       
   437 
       
   438     aMessage isNil ifTrue: [^nil].
       
   439     resourceClass := Smalltalk at: aMessage copy readStream nextWord asSymbol.
       
   440     resourceSelector := aMessage copy reverse readStream nextWord reverse asSymbol.
       
   441 
       
   442     ^self resourceClass: resourceClass selector: resourceSelector
       
   443 
       
   444    
       
   445 !
       
   446 
       
   447 resourceSelector
       
   448 
       
   449     ^resourceSelector
       
   450 !
       
   451 
       
   452 resourceSelector: aSelector
       
   453 
       
   454     resourceSelector := aSelector
       
   455 ! !
       
   456 
       
   457 !ImageEditView methodsFor:'accessing menu'!
       
   458 
       
   459 menuEffects
       
   460     "this window spec was automatically generated by the UI Builder"
       
   461 
       
   462     ^ self class menuEffects
       
   463 
       
   464 
       
   465 ! !
       
   466 
       
   467 !ImageEditView methodsFor:'drawing'!
       
   468 
       
   469 colorAt: aPoint put: aColor
       
   470 
       
   471     |tempPaint|
       
   472     tempPaint := self paint.
       
   473     aColor redByte = 'mask'
       
   474     ifTrue:
       
   475     [       
       
   476         image restored; colorAt: aPoint//magnification put: Color black.
       
   477         image mask notNil ifTrue: [image mask restored; colorAt: aPoint//magnification put: Color black].
       
   478         self paint:Color lightGray.
       
   479     ]
       
   480     ifFalse:
       
   481     [
       
   482         image mask notNil ifTrue: [image mask restored; colorAt: aPoint//magnification put: Color white].
       
   483         image restored; colorAt: aPoint//magnification put: aColor.
       
   484         self paint:aColor.
       
   485     ].
       
   486 
       
   487     self fillRectangleX:(aPoint x // magnification x) * magnification x + 1
       
   488         y:(aPoint y // magnification y) * magnification y + 1
       
   489         width:magnification x  height:magnification y.
       
   490 
       
   491     self paint:tempPaint.
       
   492     magnification > gridMagnification
       
   493     ifTrue:
       
   494     [
       
   495         self drawGridMagnification
       
   496     ].
       
   497 
       
   498 !
       
   499 
       
   500 drawGridMagnification
       
   501 
       
   502     |tempPaint|
       
   503     tempPaint := self paint.
       
   504     self paint:Color black.
       
   505     0 to: (image width * magnification x) by: magnification x do:
       
   506     [:x|
       
   507         self displayLineFromX:x y:0 toX:x y:(image height * magnification y)
       
   508     ].
       
   509     0 to: (image height * magnification y) by: magnification y do:
       
   510     [:y|
       
   511         self displayLineFromX:0 y:y toX:(image width * magnification x) y:y
       
   512     ].
       
   513     self paint:tempPaint.
       
   514 !
       
   515 
       
   516 redrawImageX:x y:y width:w height:h
       
   517     |ih iw dotW dotH minX maxX minY maxY color last lastY runW x0 xI yI|
       
   518 
       
   519     ih := image height.
       
   520     iw := image width.
       
   521     dotW := magnification x.
       
   522     dotH := magnification y.
       
   523 
       
   524     minX := (x // dotW).
       
   525     minX > iw ifTrue:[
       
   526         minX := iw - 1
       
   527     ].
       
   528 
       
   529     minY := (y // dotH) .
       
   530     minY > ih ifTrue:[
       
   531         minY := ih - 1
       
   532     ].
       
   533     maxX := (x + w) // dotW + 1.
       
   534     maxX > iw ifTrue:[
       
   535         maxX := iw
       
   536     ].
       
   537     maxY := (y + h) // dotH + 1.
       
   538     maxY > ih ifTrue:[ 
       
   539         maxY := ih
       
   540     ].
       
   541 
       
   542     lastY := -1.
       
   543 
       
   544     x0 := minX.
       
   545     runW := 0.
       
   546 
       
   547     image colorsFromX:minX y:minY toX:maxX-1 y:maxY-1 do:[:xx :yy :color |
       
   548 
       
   549         yy ~~ lastY ifTrue:[
       
   550             runW ~~ 0 ifTrue:[
       
   551                 self fillRectangleX:(x0 * dotW + margin)
       
   552                                   y:(lastY * dotH + margin)
       
   553                               width:runW height:dotH.
       
   554                 runW := 0.
       
   555             ]. 
       
   556             x0 := xx.
       
   557             lastY := yy.
       
   558         ]. 
       
   559 
       
   560         color ~~ last ifTrue:[
       
   561             runW ~~ 0 ifTrue:[
       
   562                 self fillRectangleX:(x0 * dotW + margin)
       
   563                                   y:(yy * dotH + margin)
       
   564                               width:runW height:dotH.
       
   565                 runW := 0.
       
   566             ].
       
   567 
       
   568             "self paint:color."
       
   569             (image mask notNil and: [(image mask colorAt: xx@yy) = Color black])
       
   570                 ifTrue: [self paint: Color lightGray] ifFalse: [self paint: color].
       
   571             last := color.
       
   572             runW := 0.
       
   573             x0 := xx.
       
   574         ].  
       
   575         runW := runW + dotW
       
   576     ].
       
   577     runW ~~ 0 ifTrue:[
       
   578         self fillRectangleX:(x0 * dotW + margin)
       
   579                           y:(lastY * dotH + margin)
       
   580                       width:runW height:dotH.
       
   581         runW := 0.
       
   582     ].
       
   583 !
       
   584 
       
   585 redrawX:x y:y width:w height:h
       
   586     |ih iw dotW dotH minX maxX minY maxY color last lastY runW x0 xI yI|
       
   587 
       
   588     image isNil ifTrue:[^self].
       
   589 
       
   590     magnification = (1@1) ifTrue:[
       
   591         super redrawX:x y:y width:w height:h.
       
   592         ^ self
       
   593     ].
       
   594     self clippingRectangle: (x@y extent: w@h). 
       
   595 
       
   596     self redrawImageX:x y:y width:w height:h.
       
   597 
       
   598     "/ right of image ?
       
   599     adjust == #center ifTrue:[
       
   600             xI := (width - (margin * 2) - ih) // 2.
       
   601             yI := (height - (margin * 2) - iw) // 2.
       
   602         ] ifFalse:[
       
   603             xI := yI := margin
       
   604         ].
       
   605     (x + w - 1) > (xI + (magnification x * image width)) ifTrue:[
       
   606         self clearRectangleX:(xI + (magnification x * image width))
       
   607                            y:y
       
   608                        width:(x + w - (magnification x * image width) - xI)
       
   609                       height:h
       
   610     ].
       
   611     (y + h - 1) > (yI + (magnification y * image height)) ifTrue:[
       
   612         self clearRectangleX:margin
       
   613                            y:(yI + (magnification y * image height))
       
   614                        width:w
       
   615                       height:(y + h - (magnification y * image height) - yI)  
       
   616     ].
       
   617     magnification > gridMagnification
       
   618     ifTrue:
       
   619     [
       
   620         self drawGridMagnification
       
   621     ].
       
   622     self clippingRectangle: nil
       
   623 ! !
       
   624 
       
   625 !ImageEditView methodsFor:'event handling'!
       
   626 
       
   627 buttonMotion:state x:x y:y
       
   628 
       
   629     self showColorAtX:x y:y.
       
   630 !
       
   631 
       
   632 buttonMultiPress:button x:x y:y
       
   633     button == 1 ifTrue:[
       
   634         |clr|
       
   635         clr := self getColorAtX:x y:y.
       
   636     ].
       
   637     super buttonMultiPress:button x:x y:y
       
   638 
       
   639     "Created: 8.5.1996 / 00:18:06 / stefan"
       
   640 !
       
   641 
       
   642 buttonPress:button x:x y:y
       
   643     button == 1 ifTrue:[
       
   644         self showColorAtX:x y:y.
       
   645         ^ self
       
   646     ].
       
   647     super buttonPress:button x:x y:y
       
   648 !
       
   649 
       
   650 getColorAtX:x y:y
       
   651     |pi|
       
   652 
       
   653     pi := (((x @ y) - margin + 1) / magnification)  floor.
       
   654     ((0@0 corner:(image extent) - 1) containsPoint:pi)
       
   655     ifTrue:
       
   656     [
       
   657         ^ image at:pi
       
   658     ].
       
   659     ^ nil
       
   660 
       
   661     "Created: 8.5.1996 / 00:15:55 / stefan"
       
   662 !
       
   663 
       
   664 showColorAtX:x y:y
       
   665     |clr|
       
   666 
       
   667     clr := self getColorAtX:x y:y.
       
   668     clr notNil
       
   669     ifTrue:
       
   670     [
       
   671         pixelSelectColor isColor" & (image colorMap includes: clr)"
       
   672         ifTrue:
       
   673         [   
       
   674             makeRedraw ifTrue: [self invalidate. makeRedraw := false].
       
   675             modified := true.
       
   676             self colorAt: x@y put: pixelSelectColor.
       
   677         ]
       
   678     ]
       
   679 ! !
       
   680 
       
   681 !ImageEditView methodsFor:'image conversion'!
       
   682 
       
   683 convertToColor24
       
   684     (Depth24Image fromImage:image) inspect
       
   685 
       
   686     "Modified: 3.6.1997 / 18:34:34 / cg"
       
   687 !
       
   688 
       
   689 convertToColor4
       
   690     (Depth4Image fromImage:image) inspect
       
   691 
       
   692     "Modified: 3.6.1997 / 18:34:45 / cg"
       
   693 !
       
   694 
       
   695 convertToColor8
       
   696     (Depth8Image fromImage:image) inspect
       
   697 
       
   698     "Created: 3.6.1997 / 18:34:08 / cg"
       
   699     "Modified: 3.6.1997 / 18:34:51 / cg"
       
   700 !
       
   701 
       
   702 convertToGray2
       
   703     (image asFloydSteinbergDitheredGrayImageDepth:2) inspect
       
   704 
       
   705     "Created: 3.6.1997 / 18:34:02 / cg"
       
   706     "Modified: 3.6.1997 / 18:39:23 / cg"
       
   707 !
       
   708 
       
   709 convertToGray4
       
   710     (image asFloydSteinbergDitheredGrayImageDepth:4) inspect
       
   711 
       
   712     "Created: 3.6.1997 / 18:34:04 / cg"
       
   713     "Modified: 3.6.1997 / 18:39:20 / cg"
       
   714 !
       
   715 
       
   716 convertToGray8
       
   717     (image asFloydSteinbergDitheredGrayImageDepth:8) inspect
       
   718 
       
   719     "Created: 3.6.1997 / 18:34:05 / cg"
       
   720     "Modified: 3.6.1997 / 18:39:16 / cg"
       
   721 !
       
   722 
       
   723 convertToMono
       
   724     (image asFloydSteinbergDitheredGrayImageDepth:1) inspect
       
   725 
       
   726     "Created: 3.6.1997 / 18:33:42 / cg"
       
   727     "Modified: 3.6.1997 / 18:39:26 / cg"
       
   728 ! !
       
   729 
       
   730 !ImageEditView methodsFor:'image processing'!
       
   731 
       
   732 flipHorizontal
       
   733     self performImageOperation:#flipHorizontal withArguments:nil
       
   734 !
       
   735 
       
   736 flipVertical
       
   737     self performImageOperation:#flipVertical withArguments:nil
       
   738 !
       
   739 
       
   740 negative
       
   741     self performImageOperation:#negative withArguments:nil 
       
   742 !
       
   743 
       
   744 performImageOperation:operation withArguments:args
       
   745     |oldMag newImage|
       
   746 
       
   747     windowGroup withCursor:Cursor wait do:[
       
   748         oldMag := magnification.
       
   749         magnification := 1@1.
       
   750         newImage := image perform:operation withArguments:args.
       
   751         newImage isNil ifTrue:[
       
   752             self information:'conversion failed - revert to original'.
       
   753             ^ self
       
   754         ].
       
   755         image := newImage.
       
   756         self clear.
       
   757         (oldMag isNil or:[oldMag = magnification]) ifTrue:[
       
   758             self invalidate
       
   759         ] ifFalse:[
       
   760             self magnification:oldMag.
       
   761         ]
       
   762     ]
       
   763 
       
   764     "Modified: 23.6.1997 / 09:49:26 / cg"
       
   765 !
       
   766 
       
   767 resizeImage
       
   768     |b newSize newImage wNew hNew bits|
       
   769 
       
   770     b := EnterBox new.
       
   771     b title:'new size (x @ y) contents will be located at top-left'.
       
   772     b okText:'apply'.
       
   773     b abortText:'abort'.
       
   774     b action:[:string | newSize := Object readFromString:string onError:nil].
       
   775     b initialText:(image extent printString).
       
   776     b showAtPointer.
       
   777 
       
   778     newSize notNil ifTrue:[
       
   779         wNew := image width min:newSize x.
       
   780         hNew := image height min:newSize y.
       
   781 
       
   782         newImage := Image width:(newSize x) height:(newSize y) depth:(image depth).
       
   783         newImage photometric:image photometric.
       
   784         newImage colorMap:image colorMap copy.
       
   785         newImage bits:(ByteArray new:(newImage bytesPerRow * newSize y)).
       
   786 
       
   787         newImage copyFrom:image x:0 y:0 toX:0 y:0 width:wNew height:hNew.
       
   788 
       
   789         self image: newImage
       
   790     ].
       
   791 
       
   792     "Created: 31.7.1997 / 11:41:14 / cg"
       
   793     "Modified: 31.7.1997 / 13:45:47 / cg"
       
   794 !
       
   795 
       
   796 rotateCCW
       
   797     self performImageOperation:#rotated: withArguments:#(270)
       
   798 !
       
   799 
       
   800 rotateCW
       
   801     self performImageOperation:#rotated: withArguments:#(90)
       
   802 ! !
       
   803 
       
   804 !ImageEditView methodsFor:'initialization'!
       
   805 
       
   806 initialize
       
   807 
       
   808     super initialize.
       
   809     magnification := 1@1.
       
   810     gridMagnification := 5@5.
       
   811     makeRedraw := modified := antiAlias := false.
       
   812 
       
   813     self menuHolder:self; menuPerformer:self; menuMessage:#imageMenu.
       
   814 
       
   815     "Modified: 2.6.1997 / 15:49:00 / cg"
       
   816 ! !
       
   817 
       
   818 !ImageEditView methodsFor:'menu'!
       
   819 
       
   820 imageMenu
       
   821     |m convertMenu labels selectors|
       
   822 
       
   823     image mask notNil ifTrue:[
       
   824         labels := #(
       
   825                         'save as ...'
       
   826                         'save mask as ...'
       
   827                        ).
       
   828         selectors := #(
       
   829                         saveAs
       
   830                         saveMaskAs
       
   831                       ).
       
   832         magnification ~~ (1@1) notNil ifTrue:[
       
   833             labels := labels , #(
       
   834                         '-'
       
   835                         'save magnified as ...'
       
   836                         'save magnified mask as ...'
       
   837                        ).
       
   838 
       
   839             selectors := selectors , #(
       
   840                         nil
       
   841                         saveMagnifiedAs
       
   842                         saveMagnifiedMaskAs
       
   843                       ).
       
   844         ]
       
   845     ] ifFalse:[
       
   846         labels := #(
       
   847                         'save as ...'
       
   848                        ).
       
   849         selectors := #(
       
   850                         saveAs
       
   851                       ).
       
   852         magnification ~~ (1@1) ifTrue:[
       
   853             labels := labels , #(
       
   854                         '-'
       
   855                         'save magnified as ...'
       
   856                        ).
       
   857 
       
   858             selectors := selectors , #(
       
   859                         nil
       
   860                         saveMagnifiedAs
       
   861                       ).
       
   862         ]
       
   863     ].
       
   864 
       
   865     labels := labels , #(
       
   866                             '-'
       
   867                             'print'
       
   868                             'print magnified'
       
   869                             '-'
       
   870                             'magnification'
       
   871                             'magnify & antiAlias'
       
   872 "/                            'colors'
       
   873                             'effects'
       
   874                             'convert to'
       
   875                         ).
       
   876     selectors := selectors , #(
       
   877                         nil
       
   878                         doPrint
       
   879                         doPrintMagnified
       
   880                         nil
       
   881                         changeMagnification
       
   882                         changeMagnificationAndAntiAlias
       
   883 "/                        showColors
       
   884                         effects
       
   885                         convert
       
   886                         ).
       
   887 
       
   888     m := PopUpMenu
       
   889                labels:(resources array:labels)
       
   890             selectors:selectors
       
   891              receiver:self
       
   892                   for:self.
       
   893 
       
   894     magnification = 1 ifTrue:[
       
   895         m disable:#doPrintMagnified
       
   896     ].
       
   897 
       
   898     m subMenuAt:#effects put:(
       
   899         PopUpMenu labels:(resources array:#(
       
   900                             'flip - vertical'
       
   901                             'flip - horizontal'
       
   902                             '-'
       
   903                             'rotate - clockwise'
       
   904                             'rotate - counter clockwise'
       
   905                             '-'
       
   906                             'negative'
       
   907                             '-'
       
   908                             'resize'
       
   909 "
       
   910                             'blurr'
       
   911 "
       
   912                            ))
       
   913                selectors:#(
       
   914                             flipVertical
       
   915                             flipHorizontal
       
   916                             nil
       
   917                             rotateCW
       
   918                             rotateCCW
       
   919                             nil
       
   920                             negative
       
   921                             nil
       
   922                             resizeImage
       
   923 "
       
   924                             blurr
       
   925 "
       
   926                            )
       
   927                 receiver:self
       
   928                      for:self
       
   929 
       
   930     ).
       
   931 
       
   932     m subMenuAt:#convert put:(
       
   933         convertMenu :=
       
   934         PopUpMenu labels:(resources array:#(
       
   935                             'monochrome (dither)'
       
   936                             '-'
       
   937                             'gray 2-plane (dither)'
       
   938                             'gray 4-plane (dither) '
       
   939                             'gray 8-plane '
       
   940                             '-'
       
   941 "/                            'color 4-plane (dither)'
       
   942 "/                            'color 8-plane (dither)'
       
   943                             'color 24-plane'
       
   944                            ))
       
   945                selectors:#(
       
   946                             convertToMono
       
   947                             nil
       
   948                             convertToGray2
       
   949                             convertToGray4
       
   950                             convertToGray8
       
   951                             nil
       
   952 "/                            convertToColor4
       
   953 "/                            convertToColor8
       
   954                             convertToColor24
       
   955                            )
       
   956                 receiver:self
       
   957                      for:self
       
   958     ).
       
   959 
       
   960     image depth == 1 ifTrue:[
       
   961         convertMenu disable:#convertToMono
       
   962     ].
       
   963     image depth == 2 ifTrue:[
       
   964         (image photometric ~~ #palette
       
   965         and:[image photometric ~~ #rgb]) ifTrue:[
       
   966             convertMenu disable:#convertToGray2
       
   967         ]
       
   968     ].
       
   969     image depth == 4 ifTrue:[
       
   970         (image photometric ~~ #palette
       
   971         and:[image photometric ~~ #rgb]) ifTrue:[
       
   972             convertMenu disable:#convertToGray4
       
   973         ] ifFalse:[
       
   974             convertMenu disable:#convertToColor4
       
   975         ]
       
   976     ].
       
   977     image depth == 8 ifTrue:[
       
   978         (image photometric ~~ #palette
       
   979         and:[image photometric ~~ #rgb]) ifTrue:[
       
   980             convertMenu disable:#convertToGray8
       
   981         ] ifFalse:[
       
   982             convertMenu disable:#convertToColor8
       
   983         ]
       
   984     ].
       
   985     image depth == 24 ifTrue:[
       
   986         image photometric == #rgb ifTrue:[
       
   987             convertMenu disable:#convertToColor24
       
   988         ]
       
   989     ].
       
   990 
       
   991     ^ m
       
   992 
       
   993     "
       
   994      ImageEditView openOn:'bitmaps/SBrowser.xbm'
       
   995      ImageEditView openOn:'bitmaps/garfield.gif'
       
   996     "
       
   997 
       
   998     "Created: 20.2.1997 / 18:47:17 / cg"
       
   999     "Modified: 31.7.1997 / 11:41:26 / cg"
       
  1000 ! !
       
  1001 
       
  1002 !ImageEditView methodsFor:'menu actions'!
       
  1003 
       
  1004 changeMagnification
       
  1005     |b newMag|
       
  1006 
       
  1007     b := EnterBox new.
       
  1008     b title:'magnification (magX @ magY)'.
       
  1009     b okText:'apply'.
       
  1010     b abortText:'abort'.
       
  1011     b action:[:string | newMag := (Object readFromString:string onError:nil)].
       
  1012     b initialText:(magnification printString).
       
  1013     b showAtPointer.
       
  1014 
       
  1015     newMag notNil ifTrue:[
       
  1016         antiAlias ifTrue:[
       
  1017             magnification := nil.
       
  1018         ].
       
  1019         antiAlias := false.
       
  1020         self magnification:newMag.
       
  1021     ].
       
  1022 
       
  1023     "Modified: 31.7.1997 / 11:43:12 / cg"
       
  1024 !
       
  1025 
       
  1026 changeMagnificationAndAntiAlias
       
  1027     |b newMag|
       
  1028 
       
  1029     b := EnterBox new.
       
  1030     b title:'magnification (magX @ magY)'.
       
  1031     b okText:'apply'.
       
  1032     b abortText:'abort'.
       
  1033     b action:[:string | newMag := (Object readFromString:string onError:nil)].
       
  1034     b initialText:(magnification printString).
       
  1035     b showAtPointer.
       
  1036 
       
  1037     newMag notNil ifTrue:[
       
  1038         antiAlias ifFalse:[
       
  1039             magnification := nil.
       
  1040         ].
       
  1041         antiAlias := true.
       
  1042         self magnification:newMag
       
  1043     ].
       
  1044 
       
  1045     "Created: 2.6.1997 / 15:51:01 / cg"
       
  1046     "Modified: 31.7.1997 / 11:43:47 / cg"
       
  1047 !
       
  1048 
       
  1049 createFileName
       
  1050 
       
  1051     image fileName notNil
       
  1052     ifFalse:
       
  1053     [   
       
  1054         |fileName|
       
  1055         (fileName := FileBrowserView requestFileName) notNil
       
  1056         ifTrue:
       
  1057         [    self halt.
       
  1058             image fileName: fileName.
       
  1059             ^true
       
  1060         ].
       
  1061     ].
       
  1062     ^true.
       
  1063 !
       
  1064 
       
  1065 createResourceMessage
       
  1066 
       
  1067     (self resourceClass class implements: self resourceSelector)
       
  1068     ifFalse:
       
  1069     [   
       
  1070         ((ResourceBrowserView
       
  1071             openOnSuperclass: #ApplicationModel
       
  1072             class: resourceClass
       
  1073             selector: resourceSelector
       
  1074             resourceTypes: #(#image #fileImage))) notNil
       
  1075         ifTrue:
       
  1076         [
       
  1077             ^true
       
  1078         ].
       
  1079     ].
       
  1080     ^false.
       
  1081 !
       
  1082 
       
  1083 doPrint
       
  1084     self doPrint:image
       
  1085 
       
  1086     "Modified: 2.6.1997 / 18:32:11 / cg"
       
  1087 !
       
  1088 
       
  1089 doPrint:anImage
       
  1090     |stream psgc|
       
  1091 
       
  1092     Printer supportsPostscript ifFalse:[
       
  1093         self warn:'need a postscript printer'.
       
  1094         ^ self
       
  1095     ].
       
  1096 
       
  1097     stream := Printer newNative.
       
  1098     stream isNil ifTrue:[
       
  1099         self warn:'cannot open printer stream'.
       
  1100         ^ nil
       
  1101     ].
       
  1102 
       
  1103     self withWaitCursorDo:[
       
  1104         psgc := PSGraphicsContext on:stream. "/  extent:(1.0 @ 1.0).
       
  1105         psgc displayForm:anImage x:0 y:0.
       
  1106         psgc close.
       
  1107     ]
       
  1108 
       
  1109     "Modified: 28.5.1997 / 10:54:11 / cg"
       
  1110     "Created: 2.6.1997 / 18:32:03 / cg"
       
  1111 !
       
  1112 
       
  1113 doPrintMagnified
       
  1114     "self doPrint:(magnifiedImage ? image)"
       
  1115 
       
  1116     "Modified: 2.6.1997 / 18:31:54 / cg"
       
  1117 !
       
  1118 
       
  1119 doSaveImageAs:anImage title:aTitle
       
  1120     "save contents into a file 
       
  1121      - ask user for filename using a fileSelectionBox."
       
  1122 
       
  1123     |fileName imgFileName defaultName rdr i txt suffix|
       
  1124 
       
  1125     defaultName := pathName ? ''.
       
  1126 
       
  1127     pathName isNil ifTrue:[
       
  1128         suffix := 'tiff'.
       
  1129         (imgFileName := image fileName) notNil ifTrue:[
       
  1130             suffix := imgFileName asFilename suffix.
       
  1131             defaultName := imgFileName asFilename baseName
       
  1132         ]
       
  1133     ] ifFalse:[
       
  1134         suffix := pathName asFilename suffix.
       
  1135         "/ a supported suffix ?
       
  1136         ((rdr := Image imageReaderClassForSuffix:suffix) isNil 
       
  1137         or:[(rdr canRepresent:anImage) not]) ifTrue:[
       
  1138             suffix := 'tiff'.
       
  1139             defaultName := (pathName asFilename withSuffix:suffix) pathName
       
  1140         ].
       
  1141     ].
       
  1142 
       
  1143     Image cannotRepresentImageSignal handle:[:ex |
       
  1144         self warn:('cannot represent this image in that format.\\(%1)' bindWith:ex errorString) withCRs.
       
  1145         fileName := nil.
       
  1146         ex restart
       
  1147     ] do:[
       
  1148         fileName := Dialog
       
  1149                         requestFileName:(resources string:aTitle)
       
  1150                         default:defaultName
       
  1151                         ok:(resources string:'save')
       
  1152                         abort:(resources string:'abort')
       
  1153                         pattern:('*.' , suffix).
       
  1154 
       
  1155         fileName notNil ifTrue:[
       
  1156             anImage saveOn:fileName.
       
  1157         ].
       
  1158     ].
       
  1159     pathName := fileName.
       
  1160 
       
  1161     "Created: / 20.2.1997 / 18:52:08 / cg"
       
  1162     "Modified: / 3.11.1997 / 15:02:27 / cg"
       
  1163 !
       
  1164 
       
  1165 generateFileAccess
       
  1166 
       
  1167     self createFileName
       
  1168     ifTrue:
       
  1169     [
       
  1170         |compileString stream|
       
  1171         stream := WriteStream on: ''.
       
  1172         self image storeOn: stream.
       
  1173         compileString :=
       
  1174             self resourceSelector,
       
  1175             '\\' withCRs,
       
  1176             '    <resource: #fileImage>\' withCRs,
       
  1177             '    ^Image fromFile:''',
       
  1178             image fileName, ''''.
       
  1179         ByteCodeCompiler compile: compileString forClass: self resourceClass class inCategory: 'image specs'.
       
  1180     ]
       
  1181 !
       
  1182 
       
  1183 generateImage
       
  1184 
       
  1185     "self createResourceMessage
       
  1186     ifTrue:
       
  1187     ["
       
  1188         |compileString stream|  
       
  1189         stream := WriteStream on: ''.
       
  1190         self image storeOn: stream.
       
  1191         compileString :=
       
  1192             self resourceSelector,
       
  1193             '\\' withCRs,
       
  1194             '    <resource: #image>\' withCRs,
       
  1195             '    ^',
       
  1196             stream contents.   
       
  1197         ByteCodeCompiler compile: compileString forClass: self resourceClass class inCategory: 'image specs'.
       
  1198     "]"   
       
  1199 !
       
  1200 
       
  1201 loadFromClass
       
  1202 
       
  1203     self resourceMessage: (ResourceBrowserView
       
  1204         openOnSuperclass: ApplicationModel
       
  1205         class: self resourceClass
       
  1206         selector: self resourceSelector
       
  1207         resourceTypes: #(#image #fileImage))
       
  1208         
       
  1209 
       
  1210 !
       
  1211 
       
  1212 save
       
  1213 
       
  1214     |fileName|
       
  1215     fileName := image fileName.
       
  1216     Object errorSignal handle:
       
  1217     [:exeption|
       
  1218         WarningBox warn: exeption errorString.
       
  1219         ^nil
       
  1220     ] 
       
  1221     do:
       
  1222     [
       
  1223         (fileName suffix = 'tiff') | (fileName suffix = 'tif') ifTrue: [imageReaderClass := TIFFReader].
       
  1224         fileName suffix = 'xpm' ifTrue: [imageReaderClass := XPMReader].
       
  1225         fileName suffix = 'xbm' ifTrue: [imageReaderClass := XBMReader].
       
  1226         fileName suffix = 'gif' ifTrue: [imageReaderClass := GIFReader].
       
  1227         (fileName suffix = 'jpg') | (fileName suffix = 'jpeg') ifTrue: [imageReaderClass := JPEGReader].
       
  1228         imageReaderClass isNil ifTrue: [^self error: 'no appropriate image reader found'].
       
  1229         image saveOn: fileName using: imageReaderClass.
       
  1230         modified := false.
       
  1231     ]
       
  1232        
       
  1233 !
       
  1234 
       
  1235 saveAs
       
  1236     "save contents into a file 
       
  1237      - ask user for filename using a fileSelectionBox."
       
  1238 
       
  1239     self doSaveImageAs:image title:'save image in:'
       
  1240 
       
  1241     "Modified: 20.2.1997 / 18:52:37 / cg"
       
  1242 !
       
  1243 
       
  1244 saveMagnifiedAs
       
  1245     "save contents into a file 
       
  1246      - ask user for filename using a fileSelectionBox."
       
  1247 
       
  1248     "self doSaveImageAs:magnifiedImage title:'save magnified image in:' "
       
  1249 
       
  1250     "Created: 20.2.1997 / 18:52:53 / cg"
       
  1251 !
       
  1252 
       
  1253 saveMagnifiedMaskAs
       
  1254     "save contents into a file 
       
  1255      - ask user for filename using a fileSelectionBox."
       
  1256 
       
  1257     "self doSaveImageAs:(magnifiedImage mask) title:'save magnified mask in:'"
       
  1258 
       
  1259     "Created: 20.2.1997 / 18:53:31 / cg"
       
  1260     "Modified: 20.2.1997 / 18:56:48 / cg"
       
  1261 !
       
  1262 
       
  1263 saveMaskAs
       
  1264     "save contents into a file 
       
  1265      - ask user for filename using a fileSelectionBox."
       
  1266 
       
  1267     self doSaveImageAs:(image mask) title:'save mask in:'
       
  1268 
       
  1269     "Created: 20.2.1997 / 18:51:06 / cg"
       
  1270     "Modified: 20.2.1997 / 18:56:54 / cg"
       
  1271 ! !
       
  1272 
       
  1273 !ImageEditView methodsFor:'queries'!
       
  1274 
       
  1275 heightOfContents
   846 heightOfContents
  1276     "return the images height"
   847     "return the images height"
  1277 
   848 
  1278     image isNil ifTrue:[^ 0].
   849     image isNil ifTrue:[^ 0].
  1279     ^ (image height * magnification y) rounded
   850     ^ (image height * magnification y) rounded
  1280 !
   851 !
  1281 
   852 
       
   853 imageContainsPoint: aPoint
       
   854     |pi|
       
   855     image isNil ifTrue: [^false].
       
   856     pi := ((aPoint - margin + 1) / magnification) floor.
       
   857     ^((0@0 corner:(image extent) - 1) containsPoint:pi)
       
   858 !
       
   859 
  1282 widthOfContents
   860 widthOfContents
  1283     "return the images width"
   861     "return the images width"
  1284 
   862 
  1285     image isNil ifTrue:[^ 0].
   863     image isNil ifTrue:[^ 0].
  1286     ^ (image width * magnification x) rounded
   864     ^ (image width * magnification x) rounded
  1287 ! !
   865 ! !
  1288 
   866 
       
   867 !ImageEditView methodsFor:'release'!
       
   868 
       
   869 destroy
       
   870 
       
   871     undoImage := nil.
       
   872     Clipboard := nil.
       
   873     super destroy.
       
   874 
       
   875 ! !
       
   876 
  1289 !ImageEditView class methodsFor:'documentation'!
   877 !ImageEditView class methodsFor:'documentation'!
  1290 
   878 
  1291 version
   879 version
  1292     ^ '$Header: /cvs/stx/stx/libwidg2/ImageEditView.st,v 1.48 1997-11-26 18:53:45 tz Exp $'
   880     ^ '$Header: /cvs/stx/stx/libwidg2/ImageEditView.st,v 1.49 1997-12-03 13:34:40 tz Exp $'
  1293 ! !
   881 ! !