[etoys-dev] Etoys: WebCamMorph-kfr.7.mcz

commits at source.squeak.org commits at source.squeak.org
Sat Oct 8 06:42:07 EDT 2011


Karl Ramberg uploaded a new version of WebCamMorph to project Etoys:
http://source.squeak.org/etoys/WebCamMorph-kfr.7.mcz

==================== Summary ====================

Name: WebCamMorph-kfr.7
Author: kfr
Time: 8 October 2011, 12:41:53 pm
UUID: 366a7b4e-22cd-d74d-99c8-79beb89741f7
Ancestors: WebCamMorph-dmoc.6

Add WebCamMorph

==================== Snapshot ====================

SystemOrganization addCategory: #WebCamMorph!

SymbolListType subclass: #WebCamResolutionType
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'WebCamMorph'!

----- Method: WebCamResolutionType class>>initialize (in category 'as yet unclassified') -----
initialize
	Vocabulary initialize!

----- Method: WebCamResolutionType class>>resolutions (in category 'as yet unclassified') -----
resolutions
	^ #(#'low' #'medium' #'high')
!

----- Method: WebCamResolutionType>>initialize (in category 'as yet unclassified') -----
initialize
	super initialize.
	self vocabularyName: #WebCamResolutionType.
	
	self symbols: self class resolutions
!

----- Method: WebCamResolutionType>>representsAType (in category 'as yet unclassified') -----
representsAType
	^true!

----- Method: Player>>applyEffects (in category '*WebCamMorph') -----
applyEffects
	costume applyFxNow!

----- Method: Player>>captureFrame (in category '*WebCamMorph') -----
captureFrame
	costume captureFrameNow!

----- Method: Player>>getActualFPS (in category '*WebCamMorph') -----
getActualFPS
	^ costume getActualFPS!

----- Method: Player>>getAutoSnapshot (in category '*WebCamMorph') -----
getAutoSnapshot
	^ costume getAutoSnapshot
	!

----- Method: Player>>getCamNum (in category '*WebCamMorph') -----
getCamNum
	| camNum |
	camNum := costume getCamNum.
"	self setName: 'Camera',camNum asString.
"	^ camNum.
	!

----- Method: Player>>getFrameCount (in category '*WebCamMorph') -----
getFrameCount
	^ costume getFrameCount!

----- Method: Player>>getFxHolder (in category '*WebCamMorph') -----
getFxHolder
	^ costume getFxHolder.
	!

----- Method: Player>>getIsFxEnabled (in category '*WebCamMorph') -----
getIsFxEnabled
	^ costume getIsFxEnabled.
	!

----- Method: Player>>getIsLiveDisplay (in category '*WebCamMorph') -----
getIsLiveDisplay
	^ costume getIsLiveDisplay!

----- Method: Player>>getManualCapture (in category '*WebCamMorph') -----
getManualCapture
	^ costume getManualCapture!

----- Method: Player>>getMaxFPS (in category '*WebCamMorph') -----
getMaxFPS
	^ costume getMaxFPS!

----- Method: Player>>getOverlayControls (in category '*WebCamMorph') -----
getOverlayControls
	^ costume getOverlayControls!

----- Method: Player>>getOverlayControls: (in category '*WebCamMorph') -----
getOverlayControls: aBoolean
	^ costume getOverlayControls!

----- Method: Player>>getSkipFrames (in category '*WebCamMorph') -----
getSkipFrames
	^ costume getSkipFrames!

----- Method: Player>>getSnapshotHolder (in category '*WebCamMorph') -----
getSnapshotHolder
	^ costume getSnapshotHolder.
	!

----- Method: Player>>getSnapshotLimit (in category '*WebCamMorph') -----
getSnapshotLimit
	^ costume getSnapshotLimit
	!

----- Method: Player>>getUseFrameSize (in category '*WebCamMorph') -----
getUseFrameSize
	^ costume getUseFrameSize.
	!

----- Method: Player>>getWebCamIsOn (in category '*WebCamMorph') -----
getWebCamIsOn
	^ costume getWebCamIsOn!

----- Method: Player>>getWebCamResolution (in category '*WebCamMorph') -----
getWebCamResolution
	^ costume getWebCamResolution!

----- Method: Player>>getWorldH (in category '*WebCamMorph') -----
getWorldH
	^ costume getWorldH!

----- Method: Player>>getWorldW (in category '*WebCamMorph') -----
getWorldW
	^ costume getWorldW!

----- Method: Player>>setAutoSnapshot: (in category '*WebCamMorph') -----
setAutoSnapshot: aBoolean
	costume setAutoSnapshot: aBoolean
!

----- Method: Player>>setCamNum: (in category '*WebCamMorph') -----
setCamNum: num
	costume setCamNum: num!

----- Method: Player>>setFrameCount: (in category '*WebCamMorph') -----
setFrameCount: aNumber
	costume setFrameCount: aNumber!

----- Method: Player>>setFxHolder: (in category '*WebCamMorph') -----
setFxHolder: aMorph
	costume setFxHolder: aMorph
	!

----- Method: Player>>setIsFxEnabled: (in category '*WebCamMorph') -----
setIsFxEnabled: aBoolean
	costume setIsFxEnabled: aBoolean
	!

----- Method: Player>>setIsLiveDisplay: (in category '*WebCamMorph') -----
setIsLiveDisplay: aBoolean
	costume setIsLiveDisplay: aBoolean!

----- Method: Player>>setManualCapture: (in category '*WebCamMorph') -----
setManualCapture: aBoolean
	costume setManualCapture: aBoolean!

----- Method: Player>>setMaxFPS: (in category '*WebCamMorph') -----
setMaxFPS: aNumber
	costume setMaxFPS: aNumber!

----- Method: Player>>setOverlayControls: (in category '*WebCamMorph') -----
setOverlayControls: aBoolean
	costume setOverlayControls: aBoolean!

----- Method: Player>>setSkipFrames: (in category '*WebCamMorph') -----
setSkipFrames: aNumber
	costume setSkipFrames: aNumber!

----- Method: Player>>setSnapshotHolder: (in category '*WebCamMorph') -----
setSnapshotHolder: aPlayer
	costume setSnapshotHolder: aPlayer costume
	!

----- Method: Player>>setSnapshotLimit: (in category '*WebCamMorph') -----
setSnapshotLimit: aNumber
	costume setSnapshotLimit: aNumber
	!

----- Method: Player>>setUseFrameSize: (in category '*WebCamMorph') -----
setUseFrameSize: aBoolean
	costume setUseFrameSize: aBoolean!

----- Method: Player>>setWebCamIsOn: (in category '*WebCamMorph') -----
setWebCamIsOn: aBoolean
	costume setWebCamIsOn: aBoolean!

----- Method: Player>>setWebCamResolution: (in category '*WebCamMorph') -----
setWebCamResolution: aSymbol
	costume setWebCamResolution: aSymbol!

----- Method: Player>>takeSnapshot (in category '*WebCamMorph') -----
takeSnapshot
	costume snapshotNow!

----- Method: Player>>updateDisplay (in category '*WebCamMorph') -----
updateDisplay
	costume updateDisplayNow!

RectangleMorph subclass: #WebCamMorph
	instanceVariableNames: 'camNum camIsOn frameExtent captureDelayMs captureForm displayForm frameCount maxFPS skipFrames fxHolder fxEnabled liveDisplay autoCapture autoSnapshot startupDelay prevSkipFrames lastTime actualFPS resolution snapshotHolder useFrameSize overlayControls snapshotLimit lastCaptureTime minCaptureDelayMs manualCapture'
	classVariableNames: 'CaptureDelayMs'
	poolDictionaries: ''
	category: 'WebCamMorph'!

!WebCamMorph commentStamp: '<historical>' prior: 0!
INTRODUCTION
=========

WebCamMorph together with CameraPlugin (originally from MIT Scratch) provides an easy and cross platform way to use webcam input in Squeak and Etoys. The first version has been created specifically with Etoys in mind. To view a live feed simply drag a "WebCam" tile from the "WebCam" category in the objects tool. Open up a viewer on the morph and display the "camera settings" category to explore the following basic settings:

	"camera is on": turn the camera on/off.

	"camera number": usually the default of "1" is ok but if you have more than one camera connected then adjust between 1 and 9 for other instances of WebCamMorph.

	"max fps": leave as is for now. It is unusual for webcams to capture at higher than 30fps. See later for further explanation of how fps is controlled.

	"actual fps": read-only. Indicates the actual fps being achieved which can depend significantly on lighting conditions and capture resolution...

	"resolution": webcams can have a range of resolutions but for simplicity three are supported: "low" (160x120), "medium" (320x240) and "high" (640x480). Adjust in good lighting to see if "actual fps" increases. 

	"use frame size": the resolution used for capturing can differ from the resolution used for display. If this setting is true then WebCamMorph is resized to match the camera resolution. If false then you are free to resize it however you want (via the "resize" halo button, use shift to preserve aspect ratio)


Beyond viewing a live feed WebCamMorph has been designed to support different uses including simple effects, time-lapse photography, stop-motion animation, character recognition, motion detection and more complex processing of every frame for feature detection. The following information is to help you understand how and why WebCamMorph operates so you can adjust it for your particular needs.


"FRAMES PER SECOND", LIGHTING & CAMERA RESOLUTION
==================================

The maximum possible frame rate depends on many factors, some of which are outside of our control. Frame rates differ between cameras and usually depend significantly on chosen resolution and lighting conditions. To ensure a balance between capturing every available frame and keeping everything else responsive, WebCamMorph dynamically adjusts the delay between capturing one frame and the next (does not apply when in "manual capture" mode, see later). 

WebCams often include automatic compensation for lighting conditions. In low lighting it takes significantly more time for the camera to get a picture than it does in good lighting conditions. For example 30fps may be possible with good lighting compared to 7fps in low lighting. So for best capture rates ensure you have good lighting!! 

Cameras have a "native" resolution at which frame rates are usually better than for other resolutions. Note though that the native resolution might be *higher*
than the *minimum* resolution available. It pays to experiment with different resolutions to find which one results in the highest frame rate. Use good lighting conditions when experimenting with resolutions.


"MANUAL CAPTURE" MODE
===============

In simply usage WebCamMorph automatically captures a frame and displays it. To support Etoys scripting a "manual capture" mode is provided where you or your script determines when to capture, when to apply effects (or not) and when to update the display. In between these steps you can do anything you want. Note that frames rates will be lower than that in automatic capture mode and that "skip frames" (described next) will need adjusting at very low capture rates.

Tip: In manual mode the camera can be turned off. It will be turned on automatically when required and return to it's previous state after a frame has been captured. For capture periods of five seconds or more turning the camera off may save power, which can especially useful when running off batteries. For smaller periods leaving the camera on will avoid some delays and could help speed up webcam related scripts.


"SKIP FRAMES"
========

Webcams and their drivers are typically designed for streaming live video and use internal buffering to help speed things up. At low capture rates the picture can appear to lag real-time because what you see is the next available buffer not the *latest* buffer. So for example if you capture a frame every ten seconds and there are three buffers being used then what you actually see may be thirty seconds old. We have little/no control over the number of buffers used and the actual number can vary between cameras and under different circumstances for the same camera. "skip frames" is provided to compensate for buffering so increase it when doing "manual" capturing until you see what you expect to see. Typically a setting of 8 is enough but I have had to use 20 with one particular camera in low lighting.


"SNAPSHOTS"
========

Where as "capturing" is the process of getting an image from the Camera into Squeak/Etoys, a "snapshot" preserves whatever is currently displayed (which may be the captured image after effects have been applied). To store snapshots you need to designate a "holder" which at the moment can be either a "holder" morph or a "movie" morph. Create one of these before proceeding. To assign a holder open up a viewer for WebCamMorph, display the "snapshot" category and click in the box at the right of the entry called "snapshot holder". The cursor will now resemble a cross-hair and can be clicked on the target holder/movie morph. To take a single snapshot at any time click (!!) on the left of "take snapshot". In auto-capture mode WebCamMorph can also be set to take multiple consecutive snapshots . First, before turning the camera on, set a sensible limit using "snapshot limit" (to avoid using all the computers memory) then set "auto snapshot" to true. When the camera is next turned on then snapshots are taken for every frame until "snapshot limit" becomes zero. "snapshot limit" is automatically decremented but not reset to avoid problems (although you are free to reset it manually or via a script).


"EFFECTS" - WIP
=========

Similar to snapshots, a holder can be designated as the "effects holder". This holder is intended to be populated with "fx" morphs (coming soon) which will operate on captured frames prior to displaying. Stay tuned ;-)


CLEARING SNAPSHOT & EFFECTS HOLDERS
=========================

Keeping a link to snapshot or effects holders can tie up resources even after the target holders have been deleted and are no longer visible. To ensure this does not happen designate the WebCamMorph itself as the holder (for method see "snapshots" section above).


COMING SOON!!
=========

- Built-in basic effects such as brightness, contrast and hue.
- Image "fx" morphs for effects such as those found in MIT Scratch and many other types of effects/ image processing.
- More snapshot options, eg, store to file
- Demo projects

!

----- Method: WebCamMorph class>>additionsToViewerCategories (in category 'scripting') -----
additionsToViewerCategories
	"Answer a list of (<categoryName> <list of category specs>) pairs that characterize the phrases this kind of morph wishes to add to various Viewer categories."
	^ #(
	(#'camera settings' (
		(slot cameraNumber 'Camera #' Number readWrite Player getCamNum Player setCamNum:)
		(slot resolution '160x120, 320x240 or 640x480' 
			WebCamResolution readWrite Player getWebCamResolution Player setWebCamResolution:)
		(slot maxFps 'Maximum Frames Per Second' 
			Number readWrite Player getMaxFPS Player setMaxFPS:)
		(slot cameraIsOn 'Whether the camera is on/off' Boolean readWrite Player getWebCamIsOn Player setWebCamIsOn:)
		(slot liveDisplay 'Update display on new frame' Boolean readWrite Player getIsLiveDisplay Player setIsLiveDisplay:)
		(slot effectsEnabled 'Apply effects or not' Boolean readWrite Player getIsFxEnabled Player setIsFxEnabled:)
		(slot effectsHolder 'Use to select which morph applies effects' 
			Player readWrite Player getFxHolder Player setFxHolder:)
		(slot useFrameSize 'Resize the morph to match the cameras frame size' 
			Boolean readWrite Player getUseFrameSize Player setUseFrameSize:)
		(slot frameCount 'Number if frames since turned on' Number readWrite Player getFrameCount Player setFrameCount:)
		(slot actualFps 'Actual FPS' Number readOnly Player getActualFPS Player unused)
		(slot overlayControls 'If true mouse-over displays on-screen controls' 
			Boolean readWrite Player getOverlayControls Player setOverlayControls:)

"
		(slot highPerformance 'Faster display updates. Does not effect camera FPS. Uses more power!!'
			Boolean readWrite Player getHighPerformance Player setHighPerformance:)
"
	))

	(#'manual capture' (
		(slot manualCapture 'Set to true for scripting' 
			Boolean readWrite Player getManualCapture Player setManualCapture:)
		(command captureFrame 'Get a frame')
		(command applyEffects 'Apply effects')
		(command updateDisplay 'Update display')
		(slot skipFrames 'Number of frames to skip to get latest frame'
			Number readWrite Player getSkipFrames Player setSkipFrames:)
	))

	(#'snapshot' (
		(slot snapshotHolder 'Set holder for snapshot storage' Player readWrite Player getSnapshotHolder Player setSnapshotHolder:)
		(slot snapshotLimit 'Camera #' Number readWrite Player getSnapshotLimit Player setSnapshotLimit:)
		(slot autoSnapshot 'When camera is on do automatic snapshots (up to limit)' 
			Boolean readWrite Player getAutoSnapshot Player setAutoSnapshot:)
		(command takeSnapshot 'Take snapshot')
"
		Other potential options:
			- format
			- diff (from ref frame, from previous frame)
			- to file
"
	))

	"Only here for convenience until reinstated in correct context"
	(#'temp' (
		(slot worldWidth 		'world width' 		Number 	readOnly	Player getWorldW	Player unused)
		(slot worldHeight		'world height' 	Number 	readOnly	Player getWorldH		Player unused)
	))

)
!

----- Method: WebCamMorph class>>allOff (in category 'accessing') -----
allOff
	self allInstancesDo: [:each | each off].!

----- Method: WebCamMorph class>>descriptionForPartsBin (in category 'parts bin') -----
descriptionForPartsBin
	^ self
		partName: 'WebCam' translatedNoop
		categories: {'WebCam' translatedNoop}
		documentation: 'WebCam player.' translatedNoop
		sampleImageForm: self icon!

----- Method: WebCamMorph class>>icon (in category 'parts bin') -----
icon
	"Original file: imagecodericon.png"

	^ (PNGReadWriter on: (Base64MimeConverter mimeDecodeToBytes: 
'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
/i957VpDfrfOMwAAAABJRU5ErkJggg=='
	readStream) readStream) nextImage
!

----- Method: WebCamMorph class>>initialize (in category 'class initialization') -----
initialize
	"CameraMorph initialize"

"	Smalltalk addToStartUpList: self.
	Smalltalk addToShutDownList: self.

"
	CaptureDelayMs := 20.
!

----- Method: WebCamMorph class>>resolutionFor: (in category 'scripting') -----
resolutionFor: aSymbol
	(#(#low #medium #high) includes: aSymbol) ifFalse: [^ 320 at 240].

	^ {160 at 120. 320 at 240. 640 at 480} 
			at: (WebCamResolutionType resolutions indexOf: aSymbol)
!

----- Method: WebCamMorph class>>shutDown (in category 'accessing') -----
shutDown
	self allOff.
!

----- Method: WebCamMorph class>>startUp (in category 'accessing') -----
startUp
	"Try to bring up any instances that were on before shutdown"
!

----- Method: WebCamMorph>>applyFx:checkEnabled: (in category 'private') -----
applyFx: aForm checkEnabled: aBoolean
	fxHolder == nil ifTrue: [^ aForm].

	((aBoolean == true) & (fxEnabled == false)) ifTrue: [^ aForm].

	aForm bits: (
		self scanFxHolder: fxHolder frame: aForm
	) bits copy.!

----- Method: WebCamMorph>>applyFxNow (in category 'e-toy - manual capture') -----
applyFxNow
	manualCapture ifFalse: [^ false].
	self applyFx: captureForm checkEnabled: false
!

----- Method: WebCamMorph>>captureFrameAutoMode: (in category 'private') -----
captureFrameAutoMode: autoMode
	| wasOn gotFrame nFrames |
	
	captureForm bits class == ByteArray 
		ifTrue: [captureForm unhibernate].

	">>>>>>>>>>>>>>>>>>>>>>>>>>"
	"STEPPING TRIGGERED CAPTURE >>>"

	autoMode 	ifTrue: [
		manualCapture ifTrue: [^ nil].
		self getWebCamIsOn ifFalse: [^ nil].

		captureDelayMs := (captureDelayMs - 1) max: minCaptureDelayMs.
		(CameraPlugin getFrameForCamera: camNum into: captureForm bits) > 0
			ifFalse: [
				captureDelayMs := (captureDelayMs + 2) min: 500.
				^ nil.
			].

		self captureStubWithFx: true withDisplayUpdate: true.

		^ captureForm.
	].


	">>>>>>>>>>>>>>>>>>>>>>>>>>"
	"MANUAL/SCRIPT TRIGGERED CAPTURE >>>"

	(wasOn := camIsOn) ifTrue: [
		manualCapture ifFalse: [^ nil].
	].
	(self on) ifFalse: [^ nil].

	gotFrame := false.
	nFrames := skipFrames + 1.
	[nFrames > 0] whileTrue: [
		(CameraPlugin getFrameForCamera: camNum into: captureForm bits) > 0
			ifTrue: [gotFrame := gotFrame | true].
		nFrames := nFrames - 1.
	].
	gotFrame ifFalse: [^ nil].

	self captureStubWithFx: false withDisplayUpdate: false.

	wasOn ifFalse: [self off].

	^ captureForm.
!

----- Method: WebCamMorph>>captureFrameNow (in category 'e-toy - manual capture') -----
captureFrameNow
	self captureFrameAutoMode: false.
!

----- Method: WebCamMorph>>captureStubWithFx:withDisplayUpdate: (in category 'private') -----
captureStubWithFx: doFx withDisplayUpdate: doDisplayUpdate
	lastCaptureTime ifNotNil: [
		actualFPS := 1000 // (Time millisecondClockValue - lastCaptureTime)
	].
	lastCaptureTime := Time millisecondClockValue.

	frameCount := frameCount + 1.

	doFx ifTrue: [self applyFx: captureForm checkEnabled: false].
	doDisplayUpdate ifTrue: [self updateDisplay: captureForm].
	autoSnapshot ifTrue: [self snapshotNow].

!

----- Method: WebCamMorph>>defaultFloatPrecisionFor: (in category 'e-toy support') -----
defaultFloatPrecisionFor: aGetSelector 
	(#(#getActualFPS) includes: aGetSelector)
		ifTrue: [^ 0.01].
"
	(#(#getMaxFPS #getFrameCount #getSkipFrames #getSnapshotLimit)
		includes: aGetSelector) ifTrue: [^ 0].
"
	^ super defaultFloatPrecisionFor: aGetSelector!

----- Method: WebCamMorph>>delete (in category 'submorphs-add/remove') -----
delete
	self off.
	fxHolder := nil.
	snapshotHolder := nil.
	super delete.!

----- Method: WebCamMorph>>drawOn: (in category 'drawing') -----
drawOn: aCanvas 
	useFrameSize ifTrue: [self extent: frameExtent].
	aCanvas
		drawImage: (
			(self extent = displayForm extent) 
				ifTrue: [displayForm] 
				ifFalse: [displayForm scaledToSize: self extent]
		) at: bounds origin.
!

----- Method: WebCamMorph>>fxClear (in category 'e-toy - settings') -----
fxClear
	fxHolder := nil!

----- Method: WebCamMorph>>getActualFPS (in category 'e-toy - settings') -----
getActualFPS
	^ actualFPS

!

----- Method: WebCamMorph>>getAutoSnapshot (in category 'e-toy - snapshot') -----
getAutoSnapshot
	^ autoSnapshot!

----- Method: WebCamMorph>>getCamNum (in category 'e-toy - settings') -----
getCamNum
	^ camNum!

----- Method: WebCamMorph>>getFrameCount (in category 'e-toy - settings') -----
getFrameCount
	^ frameCount
!

----- Method: WebCamMorph>>getFxHolder (in category 'e-toy - settings') -----
getFxHolder
	^ fxHolder!

----- Method: WebCamMorph>>getHighPerformance (in category 'e-toy - misc/temp') -----
getHighPerformance
	^ Preferences valueOfPreference: #higherPerformance
!

----- Method: WebCamMorph>>getIsFxEnabled (in category 'e-toy - settings') -----
getIsFxEnabled
	^ fxEnabled!

----- Method: WebCamMorph>>getIsLiveDisplay (in category 'e-toy - settings') -----
getIsLiveDisplay
	^ liveDisplay
!

----- Method: WebCamMorph>>getManualCapture (in category 'e-toy - manual capture') -----
getManualCapture
	^ manualCapture
!

----- Method: WebCamMorph>>getMaxFPS (in category 'e-toy - settings') -----
getMaxFPS
	^ maxFPS

!

----- Method: WebCamMorph>>getOverlayControls (in category 'e-toy - settings') -----
getOverlayControls
	^ overlayControls
!

----- Method: WebCamMorph>>getSkipFrames (in category 'e-toy - manual capture') -----
getSkipFrames
	^ skipFrames
!

----- Method: WebCamMorph>>getSnapshotHolder (in category 'e-toy - snapshot') -----
getSnapshotHolder
	^ snapshotHolder!

----- Method: WebCamMorph>>getSnapshotLimit (in category 'e-toy - snapshot') -----
getSnapshotLimit
	^ snapshotLimit!

----- Method: WebCamMorph>>getUseFrameSize (in category 'e-toy - settings') -----
getUseFrameSize
	^ useFrameSize
!

----- Method: WebCamMorph>>getWebCamIsOn (in category 'e-toy - settings') -----
getWebCamIsOn
"	^ (camIsOn := CameraPlugin cameraIsOpen: camNum).
"
	^ camIsOn!

----- Method: WebCamMorph>>getWebCamResolution (in category 'e-toy - settings') -----
getWebCamResolution
	^ resolution
			
!

----- Method: WebCamMorph>>getWorldH (in category 'e-toy - misc/temp') -----
getWorldH
	^ World height!

----- Method: WebCamMorph>>getWorldW (in category 'e-toy - misc/temp') -----
getWorldW
	^ World width!

----- Method: WebCamMorph>>initialize (in category 'initialization') -----
initialize
	super initialize.

	self color: Color red.

	camNum := 1.
	camIsOn := false.
	startupDelay := 1.
	captureDelayMs := CaptureDelayMs.
	frameCount := 0.
	manualCapture := false.
	maxFPS := 30.
	minCaptureDelayMs := 1000 // maxFPS.
	actualFPS := 0.
	skipFrames := 8.
	liveDisplay := true.
	fxHolder := nil.
	fxEnabled := false.
	useFrameSize := false.
	overlayControls := false.

	snapshotHolder := nil.
	autoSnapshot := false.
	snapshotLimit := 10.

	resolution := #'medium'.
	frameExtent := self class resolutionFor: resolution.
	self extent: frameExtent.
	captureForm := Form extent: frameExtent depth: 32.
	self updateDisplay: captureForm.

	lastCaptureTime := Time millisecondClockValue.

	self on.

	!

----- Method: WebCamMorph>>off (in category 'accessing') -----
off
	CameraPlugin closeCamera: camNum.
	camIsOn := false.
!

----- Method: WebCamMorph>>on (in category 'accessing') -----
on
	camIsOn ifTrue: [^ true].

	"Avoid more than one WebCamMorph per Camera"
	(CameraPlugin cameraIsOpen: camNum) ifTrue: [^ false].
	
	(CameraPlugin openCamera: camNum 
		width: (frameExtent x) 
		height: (frameExtent y)
	) ifNil: [^ false].

	(Delay forSeconds: startupDelay) wait.

	frameExtent := CameraPlugin frameExtent: camNum.
	captureForm := Form extent: (CameraPlugin frameExtent: camNum) depth: 32.

	self resetFrameCount.
	lastTime := nil.
	captureDelayMs := 20.

	camIsOn := true.
	lastCaptureTime := Time millisecondClockValue.
	self startStepping.

	^ true	
!

----- Method: WebCamMorph>>resetFrameCount (in category 'accessing') -----
resetFrameCount
	frameCount := 0!

----- Method: WebCamMorph>>scanFxHolder:frame: (in category 'private') -----
scanFxHolder: aMorph frame: aForm
	(aMorph respondsTo: #processNewFrame:) ifTrue: [
		^ aMorph processNewFrame: aForm
	].

	^ aForm
"
	aMorph class = #PasteUpMorph
"!

----- Method: WebCamMorph>>setAutoSnapshot: (in category 'e-toy - snapshot') -----
setAutoSnapshot: aBoolean
	autoSnapshot := aBoolean!

----- Method: WebCamMorph>>setCamNum: (in category 'e-toy - settings') -----
setCamNum: num
	| isOn |

	((camNum == num) or: [((1 to: 9) includes: num) not]) ifTrue: [^ self].

	isOn := camIsOn.
	self off.
	camNum := num.
	isOn ifTrue: [self on].
!

----- Method: WebCamMorph>>setFrameCount: (in category 'e-toy - settings') -----
setFrameCount: aNumber
	frameCount := aNumber
!

----- Method: WebCamMorph>>setFxHolder: (in category 'e-toy - settings') -----
setFxHolder: aMorph
	fxHolder := (aMorph == self ifTrue: [nil] ifFalse: [aMorph])
!

----- Method: WebCamMorph>>setHighPerformance: (in category 'e-toy - misc/temp') -----
setHighPerformance: aBoolean
	Preferences setPreference: #higherPerformance toValue: aBoolean
!

----- Method: WebCamMorph>>setIsFxEnabled: (in category 'e-toy - settings') -----
setIsFxEnabled: aBoolean
	fxEnabled := aBoolean!

----- Method: WebCamMorph>>setIsLiveDisplay: (in category 'e-toy - settings') -----
setIsLiveDisplay: aBoolean
	liveDisplay := aBoolean. 
"	live 
		ifTrue: [self startStepping]
		ifFalse: [self stopStepping]
"!

----- Method: WebCamMorph>>setManualCapture: (in category 'e-toy - manual capture') -----
setManualCapture: aBoolean
	manualCapture := aBoolean
!

----- Method: WebCamMorph>>setMaxFPS: (in category 'e-toy - settings') -----
setMaxFPS: aNumber
	maxFPS := aNumber max: 1.
	minCaptureDelayMs := 1000 // maxFPS.


!

----- Method: WebCamMorph>>setOverlayControls: (in category 'e-toy - settings') -----
setOverlayControls: aBoolean
	overlayControls := aBoolean
!

----- Method: WebCamMorph>>setSkipFrames: (in category 'e-toy - manual capture') -----
setSkipFrames: aNumber
	skipFrames := aNumber max: 0
!

----- Method: WebCamMorph>>setSnapshotHolder: (in category 'e-toy - snapshot') -----
setSnapshotHolder: aMorph
	snapshotHolder := (aMorph == self ifTrue: [nil] ifFalse: [aMorph])
!

----- Method: WebCamMorph>>setSnapshotLimit: (in category 'e-toy - snapshot') -----
setSnapshotLimit: aNumber
	snapshotLimit < 0 ifTrue: [^ self].
	snapshotLimit := aNumber!

----- Method: WebCamMorph>>setUseFrameSize: (in category 'e-toy - settings') -----
setUseFrameSize: aBoolean
	useFrameSize := aBoolean!

----- Method: WebCamMorph>>setWebCamIsOn: (in category 'e-toy - settings') -----
setWebCamIsOn: aBoolean
	aBoolean ifTrue: [self on] ifFalse: [self off]
!

----- Method: WebCamMorph>>setWebCamResolution: (in category 'e-toy - settings') -----
setWebCamResolution: aSymbol
	| wasOn |

	((WebCamResolutionType resolutions) includes: aSymbol) ifFalse: [^ self].
	resolution := aSymbol.

	(wasOn := camIsOn) ifTrue: [self off].
	frameExtent := self class resolutionFor: aSymbol.
	captureForm := Form extent: frameExtent depth: 32.
"	self resetSize.
"	wasOn ifTrue: [self on].
			

!

----- Method: WebCamMorph>>snapshotNow (in category 'e-toy - snapshot') -----
snapshotNow
	snapshotLimit > 0 ifFalse: [^ self].
	snapshotHolder ifNil: [^ self].

	snapshotHolder class == PasteUpMorph ifTrue: [
		snapshotHolder addMorphBack: (
			displayForm asMorph 
				name: ('frame ', frameCount asString)
		).
	].

	snapshotHolder class == MovieMorph ifTrue:[
		snapshotHolder insertFrames: {
			(SketchMorph withForm: displayForm)
				position: snapshotHolder position
		}
	].

	snapshotLimit := snapshotLimit - 1.
!

----- Method: WebCamMorph>>step (in category 'stepping and presenter') -----
step
	self captureFrameAutoMode: true
!

----- Method: WebCamMorph>>stepTime (in category 'stepping and presenter') -----
stepTime
	"Answer the desired time between steps in milliseconds"
	^ captureDelayMs
!

----- Method: WebCamMorph>>updateDisplay: (in category 'private') -----
updateDisplay: aForm
	liveDisplay ifFalse: [^ self].
	displayForm := aForm deepCopy.
	self changed.
!

----- Method: WebCamMorph>>updateDisplayNoCheck: (in category 'private') -----
updateDisplayNoCheck: aForm
	displayForm := aForm deepCopy.
	self changed.
!

----- Method: WebCamMorph>>updateDisplayNow (in category 'e-toy - manual capture') -----
updateDisplayNow
	manualCapture ifFalse: [^ false].
	self updateDisplayNoCheck: captureForm
!



More information about the etoys-dev mailing list