Image Scanning on OS-X

Mac OSX specific forum
User avatar
Shardik
Addict
Addict
Posts: 1991
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: Image Scanning on OS-X

Post by Shardik »

In the meantime I have also solved the errors with setting measurement unit and scan area ("Object is nil."). The setting of measurement unit and scan area had to be moved to the callback didSelectFunctionalUnit(). Now the modified code example below works like a charm on both Snow Leopard and Mavericks.

The only quirk remaining is that setting the scan area doesn't seem to have any effect: the resulting image MyScan.png is always a whole page although for testing I even changed the measurement unit to the (for me) more familiar centimeters and a smaller area...

Code: Select all

#ICDeviceLocationTypeMaskLocal     = $00000100
#ICDeviceLocationTypeMaskRemote    = $0000FE00
#ICDeviceTypeMaskScanner           = $00000002
#ICDeviceTypeScanner               = $00000002

ImportC ""
  class_addMethod(Class.i, Selector.i, *Callback, Types.P-ASCII)
  class_createInstance(Class.i, ExtraBytes.i)
  class_addProtocol(Class.i, Protocol.i)
  objc_allocateClassPair(ModelClass.i, NewClassName.P-ASCII, ExtraBytes.i)
  objc_lookUpClass(ClassName.P-ASCII)
  object_setClass(ObjectToModify.i, NewClass.i)
  objc_registerClassPair(NewClass.i)
  objc_getProtocol(ProtocolName.P-ASCII)
  sel_registerName(MethodName.P-ASCII)
EndImport

ImportC "/System/Library/Frameworks/ImageCaptureCore.framework/ImageCaptureCore"
EndImport

ImportC "/System/Library/Frameworks/Quartz.framework/Quartz"
EndImport

Enumeration
  #MainWindow
  #scannerList
  #scanButton
EndEnumeration

Global Dim scanners.i(0)
Global.i delegateClass, activeScanner, selectedScanner, scanning, selectedFunctionalUnit

Procedure scan()
  Debug "initiating scan..."
  scanning = 1
  CocoaMessage(0, activeScanner, "requestScan")   
EndProcedure

ProcedureC deviceDidBecomeReady(object.i, selector.i, device.i)
  Protected FunctionalUnitArray.I
  Protected NumFunctionalUnits.I

  Debug "selected device is ready..."
 
  scanFolder = CocoaMessage(0, 0, "NSURL fileURLWithPath:$", @"/Users/Shardik/Scans/")
  scanFile = CocoaMessage(0, 0, "NSString stringWithString:$", @"MyScan")
 
  ; CocoaMessage(0, device, "requestSelectFunctionalUnit:", 0)   ;0 for flatbed type
  CocoaMessage(0, device, "requestSelectFunctionalUnit:", 3)   ;3 for document feeder type
  CocoaMessage(0, activeScanner, "setTransferMode:", 0)        ;0 for file-mode
  CocoaMessage(0, activeScanner, "setDownloadsDirectory:", scanFolder)
  CocoaMessage(0, activeScanner, "setDocumentName:$", @"MyScan")
  CocoaMessage(0, activeScanner, "setDocumentUTI:$", @"public.png")

  FunctionalUnitArray = CocoaMessage(0, device, "availableFunctionalUnitTypes")

  If FunctionalUnitArray
    NumFunctionalUnits = CocoaMessage(0, FunctionalUnitArray, "count")

    If NumFunctionalUnits
      Debug "Functional unit types:"

      For i = 0 To NumFunctionalUnits - 1
        Select CocoaMessage(0, CocoaMessage(0, FunctionalUnitArray,
          "objectAtIndex:", i), "integerValue")
          Case 0
            Debug "- Flatbed"
          Case 1
            Debug "- Positive transparency"
          Case 2
            Debug "- Negative transparency"
          Case 3
            Debug "- Document feeder"
        EndSelect    
      Next i
    EndIf
  EndIf
EndProcedure

ProcedureC didAddDevice(object.i, selector.i, deviceBrowser.i, addedDevice.i, moreComing.i)
  Debug "device found and added..."
 
  deviceType.i = CocoaMessage(0, addedDevice, "type")   
  deviceName.s = PeekS(CocoaMessage(0, CocoaMessage(0, addedDevice, "name"), "UTF8String"), -1, #PB_UTF8)
 
  If moreComing = #NO And (deviceType & #ICDeviceTypeMaskScanner) = #ICDeviceTypeScanner
    newIndex = ArraySize(scanners()) + 1
    ReDim scanners(newIndex)
    scanners(newIndex) = addedDevice
    AddGadgetItem(#scannerList, -1, deviceName)
    CocoaMessage(0, addedDevice, "setDelegate:",
                 class_createInstance(delegateClass, 0))   ;PureBasic app controls the scanner
  EndIf
EndProcedure

ProcedureC didRemoveDevice(object.i, selector.i, removedDevice.i)
  Debug "device removed"
EndProcedure

ProcedureC didRemoveDeviceInBrowser(object.i, selector.i, deviceBrowser.i, removedDevice.i, moreGoing.i)
  Debug "device removed in browser..."
  CocoaMessage(0, removedDevice, "requestCloseSession")
  CocoaMessage(0, removedDevice, "setDelegate:", 0)
  RemoveGadgetItem(#scannerList, selectedScanner - 1)
  ReDim scanners(ArraySize(scanners()) - 1)

  If activeScanner = removedDevice
    activeScanner = 0
  EndIf
EndProcedure

ProcedureC didOpenSessionWithError(object.i, selector.i, device.i, error.i)
  Debug "session opened... errors: " + Str(error)
EndProcedure

ProcedureC didCloseSessionWithError(object.i, selector.i, device.i, error.i)
  Debug "session closed... errors: " + Str(error)
EndProcedure

ProcedureC didSelectFunctionalUnit(object.i, selector.i, scannerDevice.i, functionalUnit.i, error.i)
  Protected scanArea.NSRect

  If error = 0
    If functionalUnit <> 0
      Debug "functional unit selected: " + functionalUnit
      CocoaMessage(0, functionalUnit, "setMeasurementUnit:", 0)
      Debug "MeasurementUnit set to Inches"

      With scanArea
        \origin\x = 0
        \origin\y = 0
        \size\width = 10
        \size\height = 10
      EndWith
 
      CocoaMessage(0, functionalUnit, "setScanArea:@", @scanArea)
      CocoaMessage(@scanArea, functionalUnit, "scanArea")
      Debug "scanArea set to (" + Str(scanArea\origin\x) + "," +
        Str(scanArea\origin\y) + ")-(" +
        Str(scanArea\origin\x + scanArea\size\width) + "," +
        Str(scanArea\origin\y + scanArea\size\height) + ")"
    EndIf
  Else
    Debug "functional unit selected... errors: " + Str(error)
  EndIf
EndProcedure

ProcedureC didReceiveStatusInformation(object.i, selector.i, device.i, StatusDictionary.i)
  Debug "receiving status info..."

  If StatusDictionary
    Debug PeekS(CocoaMessage(0, CocoaMessage(0, StatusDictionary,
      "valueForKey:$", @"ICLocalizedStatusNotificationKey"), "UTF8String"),
      -1, #PB_UTF8)
  EndIf
EndProcedure

ProcedureC didScanToURL(object.i, selector.i, scannerDevice.i, URL.i, *data) 
  Debug "scanned to " +
    PeekS(CocoaMessage(0, CocoaMessage(0, URL, "absoluteString"), "UTF8String"),
    -1, #PB_UTF8)
  scanning = 0
EndProcedure

ProcedureC didCompleteScanWithError(object.i, selector.i, scannerDevice.i, error.i)
  Debug "scan completed... errors: " + Str(error)
  scanning = 0
EndProcedure
 
ProcedureC didEncounterError(object.i, selector.i, device.i, error.i)
  Debug "encountered error: " + Str(error)
  scanning = 0
EndProcedure

wFlags = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
OpenWindow(#MainWindow, 100, 100, 800, 500, "Scanner Application", wFlags)
ListViewGadget(#scannerList, 0, 0, 200, 500)
ButtonGadget(#scanButton, 220, 10, 100, 30, "SCAN")
SetGadgetColor(#scannerList, #PB_Gadget_BackColor, RGB(200, 200, 255))

deviceBrowser = CocoaMessage(0, 0, "ICDeviceBrowser new")

delegateClass = objc_allocateClassPair(objc_lookUpClass("NSObject"), "PB_Delegate", 0)   ;PureBasic app class

class_addMethod(delegateClass,
                sel_registerName("didRemoveDevice:"), @didRemoveDevice(), "v@:@")
class_addMethod(delegateClass,
                sel_registerName("deviceDidBecomeReady:"), @deviceDidBecomeReady(), "v@:@")
class_addMethod(delegateClass,
                sel_registerName("device:didEncounterError:"), @didEncounterError(), "v@:@@")
class_addMethod(delegateClass,
                sel_registerName("device:didOpenSessionWithError:"), @didOpenSessionWithError(), "v@:@@")
class_addMethod(delegateClass,
                sel_registerName("device:didCloseSessionWithError:"), @didCloseSessionWithError(), "v@:@@")
class_addMethod(delegateClass,
                sel_registerName("device:didReceiveStatusInformation:"), @didReceiveStatusInformation(), "v@:@@")
class_addMethod(delegateClass,
                sel_registerName("deviceBrowser:didAddDevice:moreComing:"), @didAddDevice(), "v@:@@@")
class_addMethod(delegateClass,
                sel_registerName("deviceBrowser:didRemoveDevice:moreGoing:"), @didRemoveDeviceInBrowser(), "v@:@@@")
class_addMethod(delegateClass,
                sel_registerName("scannerDevice:didScanToURL:data:"), @didScanToURL(), "v@:@@@")
class_addMethod(delegateClass,
                sel_registerName("scannerDevice:didCompleteScanWithError:"), @didCompleteScanWithError(), "v@:@@")
class_addMethod(delegateClass,
                sel_registerName("scannerDevice:didSelectFunctionalUnit:error:"), @didSelectFunctionalUnit(), "v@:@@@")

objc_registerClassPair(delegateClass)

CocoaMessage(0, deviceBrowser, "setDelegate:", class_createInstance(delegateClass, 0))   ;PureBasic app controls the browser
CocoaMessage(0, deviceBrowser, "setBrowsedDeviceTypeMask:", #ICDeviceLocationTypeMaskLocal |
                                                            #ICDeviceLocationTypeMaskRemote |
                                                            #ICDeviceTypeMaskScanner)
CocoaMessage(0, deviceBrowser, "start")

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      appQuit = 1
    Case #PB_Event_Gadget
      Select EventGadget()
        Case #scanButton
          scan()
        Case #scannerList
          Select EventType()
            Case #PB_EventType_LeftDoubleClick
              selectedScanner = GetGadgetState(#scannerList) + 1
              activeScanner = scanners(selectedScanner)
              CocoaMessage(0, activeScanner, "requestOpenSession")
          EndSelect
      EndSelect
  EndSelect
Until appQuit = 1

If activeScanner
  CocoaMessage(0, activeScanner, "release")
EndIf

CocoaMessage(0, deviceBrowser, "stop")
; CocoaMessage(0, deviceBrowser, "release")
Update 1: I have modified the didReceiveStatusInformation() callback to also display the status which is contained in the status dictionary passed as a parameter to this callback.

Update 2: I have modified the deviceDidBecomeReady() callback to also display the available functional unit types.

Update 3: To prevent an invalid memory access (IMA) when closing the program after the scanner's power button had already been switched off, I have added

Code: Select all

  If activeScanner = removedDevice
    activeScanner = 0
  EndIf
in procedure didRemoveDeviceInBrowser() and changed

Code: Select all

CocoaMessage(0, activeScanner, "release")
to

Code: Select all

If activeScanner
  CocoaMessage(0, activeScanner, "release")
EndIf
at the end of the code example. I have tested this code successfully with PB 5.44 x86 in both ASCII and Unicode mode on MacOS 10.6.8 (Snow Leopard) and with PB 5.44 x86 and x64 in both ASCII and Unicode mode on MacOS 10.7.5 (Lion).
Last edited by Shardik on Wed Dec 28, 2016 9:30 pm, edited 5 times in total.
User avatar
TI-994A
Addict
Addict
Posts: 2512
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: Image Scanning on OS-X

Post by TI-994A »

Image
Shardik wrote:...I have also solved the errors with setting measurement unit and scan area ("Object is nil."). The setting of measurement unit and scan area had to be moved to the callback didSelectFunctionalUnit()...
You're a true genius. But more importantly, how did you figure that out? I didn't find that anywhere in the documentation or in any of the tonnes of examples that I had pored over.
Shardik wrote:The only quirk remaining is that setting the scan area doesn't seem to have any effect: the resulting image MyScan.png is always a whole page although...
No; it works! I've tested it with both inches and centimetres settings, and it scans precisely the given area. Amazing!

The reason that you're not getting it is because the Brother ADS-2100 that you're using is a document-feeder type, which ignores the scan area setting.

This is wonderful! We're really getting this thing to work.

I bow to you, GURU.
Image

But I still await updates on reading the availableFunctionalUnitTypes property array, as well as the status message returned by the didReceiveStatusInformation() method. :lol:
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
User avatar
Shardik
Addict
Addict
Posts: 1991
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: Image Scanning on OS-X

Post by Shardik »

TI-994A,

thank you very much for your kind words. But unfortunately you are massively exaggerating about my capabilities. All PB Mac programmers can't be grateful enough to Wilbert who is a true genius, a Cocoa framework magician and assembler guru. Without Wilbert's incredible work and Fred's help on the former oMsg() library which finally became the native CocoaMessage() function and the multitude of Wilbert's excellent code examples my help for your scanning program would not have been possible. And I bet that if Wilbert would have had a proper scanner at home, he would have had worked out a scanning solution for you in minutes while I needed many hours... :lol:
TI-994A wrote:The reason that you're not getting it is because the Brother ADS-2100 that you're using is a document-feeder type, which ignores the scan area setting.
That's another proof of my silliness... :oops:
Of course it's technically not possible to use a sheet feeder type scanner for scanning only a given area...
TI-994A wrote:But I still await updates on reading the availableFunctionalUnitTypes property array, as well as the status message returned by the didReceiveStatusInformation() method. :lol:
I have modified the didReceiveStatusInformation() callback in my last code posting to also display the status contained in the status dictionary and the deviceDidBecomeReady() callback to report the available functional unit types. :wink:
User avatar
TI-994A
Addict
Addict
Posts: 2512
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: Image Scanning on OS-X

Post by TI-994A »

SHARDIK! YOU DID IT MAN!

Image
Shardik wrote:...you are massively exaggerating about my capabilities. All PB Mac programmers can't be grateful enough to Wilbert who is a true genius ... and the multitude of Wilbert's excellent code examples...
Yes; I too have studied his many code examples intently, and yours too, and would not have imagined to approach this topic without them. Like you said, if only he had a scanner.

But you went out of your way to help me, and to borrow a quote from John McClane (Bruce Willis, Die Hard 4.0), "That's what makes you that guy."

Bottom-line: I can't thank you enough. Danke Vielmals!

Just FYI, in the didSelectFunctionalUnit() method, I was able to get the selected functional unit type with this CocoaMessage():

Code: Select all

CocoaMessage(0, functionalUnit, "type")
Shardik wrote:...the modified code example below works like a charm on both Snow Leopard and Mavericks.
It should also be noted that all my tests were done on Mac OSX Lion v10.7.5. So, it clearly works on Snow Leopard, Lion, and Mavericks :D

I truly believe that this is one for the PureBasic books, and warrants an addition into the Mac OSX Tricks 'n' Tips section. Please do the honours, Shardik; this is definitely your baby. :wink:

Thanks again, and hope you have a wonderful weekend. I know I will! :lol:
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Image Scanning on OS-X

Post by wilbert »

TI-994A wrote:I truly believe that this is one for the PureBasic books, and warrants an addition into the Mac OSX Tricks 'n' Tips section.
I added a link to the source in the first post of that thread :wink:
It's indeed a very nice contribution :D
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
TI-994A
Addict
Addict
Posts: 2512
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: Image Scanning on OS-X

Post by TI-994A »

wilbert wrote:I added a link to the source in the first post of that thread :wink:
It's indeed a very nice contribution :D
Hi wilbert. I just saw the new link in the Mac OSX Tricks 'n' Tips section; a very nice contribution indeed. Thank you.

And like Shardik said, it wouldn't have been possible without your contributions. I'd have to say that the techniques implemented in this example were most certainly derived from the many API examples that you, and the other OSX gurus, had posted in these forums over the years. Truly educational stuff.

So, dank u wel. :D

I just realised that it took 36 days to get this thing off the ground. But it is well worth every minute! :lol:
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Image Scanning on OS-X

Post by wilbert »

TI-994A wrote:And like Shardik said, it wouldn't have been possible without your contributions. I'd have to say that the techniques implemented in this example were most certainly derived from the many API examples that you, and the other OSX gurus, had posted in these forums over the years. Truly educational stuff.
We all contribute :)

It's just nice to see the steps that have been made since PureBasic OSX made the move from Carbon to Cocoa. 8)
Windows (x64)
Raspberry Pi OS (Arm64)
coder14
Enthusiast
Enthusiast
Posts: 327
Joined: Tue Jun 21, 2011 10:39 am

Re: Image Scanning on OS-X

Post by coder14 »

This example worked before but after I upgraded to a newer Xcode I get scan errors. The os is the same version and I tried it on older versions of PB but no luck. The only change on my Mac is Xcode 4.6. Can it be the problem? :?
User avatar
Shardik
Addict
Addict
Posts: 1991
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: Image Scanning on OS-X

Post by Shardik »

coder14 wrote:This example worked before but after I upgraded to a newer Xcode I get scan errors. The os is the same version and I tried it on older versions of PB but no luck. The only change on my Mac is Xcode 4.6. Can it be the problem? :?
To which exact version of Xcode did you upgrade? After having updated the most recent code example I can't reproduce any problems on MacOS 10.7.5 (Lion) with Xcode 4.6.3 using PB 5.44 x86 and x64 in both ASCII and Unicode mode. I also tested the code example successfully on MacOS 10.6.8 with PB 5.44 x86 in ASCII and Unicode mode.

You should also take a look into my tips in your other thread.
coder14
Enthusiast
Enthusiast
Posts: 327
Joined: Tue Jun 21, 2011 10:39 am

Re: Image Scanning on OS-X

Post by coder14 »

Shardik wrote:
coder14 wrote:This example worked before but after I upgraded to a newer Xcode I get scan errors. The os is the same version and I tried it on older versions of PB but no luck. The only change on my Mac is Xcode 4.6. Can it be the problem? :?
To which exact version of Xcode did you upgrade? After having updated the most recent code example I can't reproduce any problems on MacOS 10.7.5 (Lion) with Xcode 4.6.3 using PB 5.44 x86 and x64 in both ASCII and Unicode mode. I also tested the code example successfully on MacOS 10.6.8 with PB 5.44 x86 in ASCII and Unicode mode.

You should also take a look into my tips in your other thread.
I think that it worked for you because your scanner is ADF by default. Mine is a flatbed and the scan failed because the ADF was selected in the "deviceDidBecomeReady" proc. It works now. Thank you.
marcovilliams
New User
New User
Posts: 1
Joined: Wed Dec 05, 2018 5:47 am

Re: Image Scanning on OS-X

Post by marcovilliams »

Hello, Shardik,

Thanks for share image scanning code is very nice after that I use this code. But any situation creates a problem please solve it.

Thanks again!
Post Reply