[etoys-dev] Etoys: MorphicExtras-kfr.30.mcz

commits at source.squeak.org commits at source.squeak.org
Mon Oct 10 04:55:44 EDT 2011


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

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

Name: MorphicExtras-kfr.30
Author: kfr
Time: 10 October 2011, 10:54:50 am
UUID: 9ad7a805-01a0-6941-9be4-fad51668a4ca
Ancestors: MorphicExtras-kfr.29

Adding WebCam in MorphicExtras

=============== Diff against MorphicExtras-kfr.29 ===============

Item was changed:
  SystemOrganization addCategory: #'MorphicExtras-AdditionalMorphs'!
  SystemOrganization addCategory: #'MorphicExtras-AdditionalSupport'!
  SystemOrganization addCategory: #'MorphicExtras-AdditionalWidgets'!
  SystemOrganization addCategory: #'MorphicExtras-Books'!
  SystemOrganization addCategory: #'MorphicExtras-Demo'!
  SystemOrganization addCategory: #'MorphicExtras-EToy-Download'!
  SystemOrganization addCategory: #'MorphicExtras-Flaps'!
  SystemOrganization addCategory: #'MorphicExtras-GeeMail'!
  SystemOrganization addCategory: #'MorphicExtras-Leds'!
  SystemOrganization addCategory: #'MorphicExtras-Navigators'!
  SystemOrganization addCategory: #'MorphicExtras-Obsolete'!
  SystemOrganization addCategory: #'MorphicExtras-Palettes'!
  SystemOrganization addCategory: #'MorphicExtras-PartsBin'!
  SystemOrganization addCategory: #'MorphicExtras-Postscript Canvases'!
  SystemOrganization addCategory: #'MorphicExtras-Postscript Filters'!
  SystemOrganization addCategory: #'MorphicExtras-SoundInterface'!
  SystemOrganization addCategory: #'MorphicExtras-SqueakPage'!
  SystemOrganization addCategory: #'MorphicExtras-Support'!
  SystemOrganization addCategory: #'MorphicExtras-Text Support'!
  SystemOrganization addCategory: #'MorphicExtras-Undo'!
  SystemOrganization addCategory: #'MorphicExtras-Widgets'!
+ SystemOrganization addCategory: #'MorphicExtras-WebCam'!

Item was added:
+ Object subclass: #CameraPlugin
+ 	instanceVariableNames: ''
+ 	classVariableNames: ''
+ 	poolDictionaries: ''
+ 	category: 'MorphicExtras-WebCam'!
+ 
+ !CameraPlugin commentStamp: '<historical>' prior: 0!
+ CameraPlugin: Simple cross-platform webcam access interface from MIT Scratch. Small changes made so that different cameras can be tested when more than one is connected. The "CameraPlugin" binary should soon be included in the VM. On Linux the plugin is designed to take advantage of libv4l2 (if found) to support a wide range of cameras.
+ 
+ Copyright (c) 2009 Massachusetts Institute of Technology
+ 
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ 
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ 
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ !

Item was added:
+ ----- Method: CameraPlugin class>>camera:getParam: (in category 'camera ops') -----
+ camera: cameraNum getParam: paramNum
+ 	"Answer the given parameter for the given camera."
+ 
+ 	<primitive: 'primGetParam' module: 'CameraPlugin'>
+ 	^ nil
+ !

Item was added:
+ ----- Method: CameraPlugin class>>cameraIsAvailable (in category 'camera ops') -----
+ cameraIsAvailable
+ 	"Answer true if at least one camera is available."
+ 
+ 	| enableCamera |
+ 	enableCamera := false.
+ 	^ enableCamera and: [(self cameraName: 1) notNil]
+ !

Item was added:
+ ----- Method: CameraPlugin class>>cameraIsOpen: (in category 'camera ops') -----
+ cameraIsOpen: cameraNum
+ 	"Answer true if the camera is open."
+ 
+ 	^ (self packedFrameExtent: cameraNum) > 0
+ !

Item was added:
+ ----- Method: CameraPlugin class>>cameraName: (in category 'camera ops') -----
+ cameraName: cameraNum
+ 	"Answer the name of the given camera. Answer nil if there is no camera with the given number."
+ 
+ 	<primitive: 'primCameraName' module: 'CameraPlugin'>
+ 	^ nil
+ !

Item was added:
+ ----- Method: CameraPlugin class>>closeCamera: (in category 'camera ops') -----
+ closeCamera: cameraNum
+ 	"Close the camera. Do nothing if it was not open."
+ 
+ 	<primitive: 'primCloseCamera' module: 'CameraPlugin'>
+ !

Item was added:
+ ----- Method: CameraPlugin class>>declareCVarsIn: (in category 'translation') -----
+ declareCVarsIn: aCCodeGenerator
+ 	"self translate"
+ 
+ 	super declareCVarsIn: aCCodeGenerator.
+ 	aCCodeGenerator cExtras: '
+ #include "cameraOps.h"
+ #include <string.h>
+ '.!

Item was added:
+ ----- Method: CameraPlugin class>>frameExtent: (in category 'camera ops') -----
+ frameExtent: cameraNum
+ 	"Answer the frame extent of the currently open camera, or zero if the camera isn't open."
+ 
+ 	| packedExtent |
+ 	packedExtent := self packedFrameExtent: cameraNum.
+ 	^ (packedExtent bitShift: -16) @ (packedExtent bitAnd: 16rFFFF) !

Item was added:
+ ----- Method: CameraPlugin class>>getFrameForCamera:into: (in category 'camera ops') -----
+ getFrameForCamera: cameraNum into: aBitmap
+ 	"Copy a camera frame into the given Bitmap. The Bitmap should be a Form of depth 32 that is the same width and height as the current camera frame. Fail if the camera is not open or if the bitmap is not the right size. If successful, answer the number of frames received from the camera since the last call. If this is zero, then there has been no change."
+ 
+ 	<primitive: 'primGetFrame' module: 'CameraPlugin'>
+ 	^ 0!

Item was added:
+ ----- Method: CameraPlugin class>>openCamera:width:height: (in category 'camera ops') -----
+ openCamera: cameraNum width: frameWidth height: frameHeight
+ 	"Open the given camera requesting the given frame dimensions. The camera number is usually 1 since you typically have only one camera plugged in. If the camera does not support the exact frame dimensions, an available frame size with width >= the requested width is selected."
+ 
+ 	<primitive: 'primOpenCamera' module: 'CameraPlugin'>
+ 	^ nil
+ !

Item was added:
+ ----- Method: CameraPlugin class>>packedFrameExtent: (in category 'camera ops') -----
+ packedFrameExtent: cameraNum
+ 	"Answer the extent of the currently open camera packed in an integer. The top 16 bits are the width, the low 16 bits are the height. Answer zero if the camera isn't open."
+ 
+ 	<primitive: 'primFrameExtent' module: 'CameraPlugin'>
+ 	^ 0
+ !

Item was added:
+ ----- Method: CameraPlugin class>>videoTest: (in category 'test') -----
+ videoTest: camNum
+ 	"A quick test of video input. Displays video on the screen until the mouse is pressed."
+ 	"self videoTest: 1"
+ 	"self videoTest: 2"
+ 
+ 	| f n startTime frameCount msecs fps |
+ 	(CameraPlugin openCamera: camNum width: 320 height: 240) ifNil: [^ self inform: 'no camera'].
+ 	self waitForCameraStart: camNum.
+ 	(self frameExtent: camNum) x = 0 ifTrue: [^ self inform: 'no camera'].
+ 	f := Form extent: (CameraPlugin frameExtent: camNum) depth: 32.
+ 	frameCount := 0.
+ 	startTime := nil.
+ 	[Sensor anyButtonPressed] whileFalse: [
+ 		n := CameraPlugin getFrameForCamera: camNum into: f bits.
+ 		n > 0 ifTrue: [
+ 			startTime ifNil: [startTime := Time millisecondClockValue].
+ 			frameCount := frameCount + 1.
+ 			f display]].
+ 	Sensor waitNoButton.
+ 	msecs := Time millisecondClockValue - startTime.
+ 	CameraPlugin closeCamera: camNum.
+ 	fps := (frameCount * 1000) // msecs.
+ 	^ frameCount printString, ' frames at ', fps printString, ' frames/sec'!

Item was added:
+ ----- Method: CameraPlugin class>>waitForCameraStart: (in category 'camera ops') -----
+ waitForCameraStart: camNum
+ 	"Wait for the camera to get it's first frame (indicated by a non-zero frame extent. Timeout after a few seconds."
+ 	"self waitForCameraStart"
+ 
+ 	| startTime |
+ 	startTime := Time millisecondClockValue.
+ 	[(Time millisecondClockValue - startTime) < 2000] whileTrue: [
+ 		(self packedFrameExtent: camNum) > 0 ifTrue: [^ self].
+ 		(Delay forMilliseconds: 50) wait].!

Item was added:
+ ----- Method: CameraPlugin>>primCameraName (in category 'primitives') -----
+ primCameraName
+ 	"Get the name for the camera with the given number. Fail if the camera number is greater than the number of available cameras."
+ 
+ 	| cameraNum nameStr count resultOop dst |
+ 	self export: true.
+ 	self var: 'nameStr' declareC: 'char* nameStr'.
+ 	self var: 'dst' declareC: 'char* dst'.
+ 
+ 	cameraNum := interpreterProxy stackIntegerValue: 0.
+ 	interpreterProxy failed ifTrue: [^ 0].
+ 
+ 	nameStr := self cCode: 'CameraName(cameraNum)'.
+ 	nameStr = nil ifTrue: [
+ 		interpreterProxy success: false.
+ 		^ 0].
+ 
+ 	count := self cCode: '(int) strlen(nameStr)'.
+ 	resultOop := interpreterProxy instantiateClass: interpreterProxy classString indexableSize: count.
+ 	dst := self cCoerce: (interpreterProxy firstIndexableField: resultOop) to: 'char *'.
+ 	0 to: count - 1 do: [:i | dst at: i put: (nameStr at: i)].
+ 
+ 	interpreterProxy pop: 2 thenPush: resultOop.  "pop arg and rcvr, push result"
+ 	^ 0
+ !

Item was added:
+ ----- Method: CameraPlugin>>primCloseCamera (in category 'primitives') -----
+ primCloseCamera
+ 	"Close the camera. Do nothing if it was not open."
+ 
+ 	| cameraNum |
+ 	self export: true.
+ 	cameraNum := interpreterProxy stackIntegerValue: 0.
+ 	interpreterProxy failed ifTrue: [^ 0].
+ 
+ 	self cCode: 'CameraClose(cameraNum)'.
+ 
+ 	interpreterProxy pop: 1.  "pop arg, leave rcvr on stack"
+ 	^ 0
+ !

Item was added:
+ ----- Method: CameraPlugin>>primFrameExtent (in category 'primitives') -----
+ primFrameExtent
+ 	"Answer the frame extent of the given camera, or zero if the camera is not open. The extent is 16 bits of width and height packed into a single integer."
+ 
+ 	| cameraNum e |
+ 	self export: true.
+ 	cameraNum := interpreterProxy stackIntegerValue: 0.
+ 	interpreterProxy failed ifTrue: [^ 0].
+ 
+ 	e := self cCode: 'CameraExtent(cameraNum)'.
+ 
+ 	interpreterProxy pop: 2 thenPush: (interpreterProxy integerObjectOf: e).  "pop rcvr & arg, push int result"
+ 	^ 0
+ !

Item was added:
+ ----- Method: CameraPlugin>>primGetFrame (in category 'primitives') -----
+ primGetFrame
+ 	"Copy a camera frame into the given Bitmap. The Bitmap should be for a Form of depth 32 that is the same width and height as the current camera frame. Fail if the camera is not open or if the bitmap is not the right size. If successful, answer the number of frames received from the camera since the last call. If this is zero, then there has been no change."
+ 
+ 	| cameraNum bitmapOop bitmap pixCount result |
+ 	self export: true.
+ 	self var: 'bitmap' declareC: 'unsigned char *bitmap'.
+ 
+ 	cameraNum := interpreterProxy stackIntegerValue: 1.
+ 	bitmapOop := interpreterProxy stackValue: 0.
+ 	((interpreterProxy isIntegerObject: bitmapOop) or:
+ 	 [(interpreterProxy isWords: bitmapOop) not]) ifTrue: [
+ 		interpreterProxy success: false.
+ 		^ 0].
+ 	bitmap := self cCoerce: (interpreterProxy firstIndexableField: bitmapOop) to: 'unsigned char *'.
+ 	pixCount := interpreterProxy stSizeOf: bitmapOop.
+ 
+ 	result := self cCode: 'CameraGetFrame(cameraNum, bitmap, pixCount)'.
+ 	result < 0 ifTrue: [
+ 		interpreterProxy success: false.
+ 		^ 0].
+ 
+ 	interpreterProxy pop: 3 thenPush: (interpreterProxy integerObjectOf: result).  "pop rcvr & args, push int result"
+ 	^ 0
+ 
+ !

Item was added:
+ ----- Method: CameraPlugin>>primGetParam (in category 'primitives') -----
+ primGetParam
+ 	"Answer the given integer parameter of the given camera."
+ 
+ 	| cameraNum paramNum result |
+ 	self export: true.
+ 	cameraNum := interpreterProxy stackIntegerValue: 1.
+ 	paramNum := interpreterProxy stackIntegerValue: 0.
+ 	interpreterProxy failed ifTrue: [^ 0].
+ 
+ 	result := self cCode: 'CameraGetParam(cameraNum, paramNum)'.
+ 
+ 	interpreterProxy pop: 3 thenPush: (interpreterProxy integerObjectOf: result).  "pop rcvr & args, push int result"
+ 	^ 0
+ !

Item was added:
+ ----- Method: CameraPlugin>>primOpenCamera (in category 'primitives') -----
+ primOpenCamera
+ 	"Open a camera. Takes one argument, the index of the device to open."
+ 
+ 	| cameraNum desiredFrameWidth desiredFrameHeight ok |
+ 	self export: true.
+ 
+ 	cameraNum := interpreterProxy stackIntegerValue: 2.
+ 	desiredFrameWidth := interpreterProxy stackIntegerValue: 1.
+ 	desiredFrameHeight := interpreterProxy stackIntegerValue: 0.
+ 	interpreterProxy failed ifTrue: [^ 0].
+ 
+ 	ok := self cCode: 'CameraOpen(cameraNum, desiredFrameWidth, desiredFrameHeight)'.
+ 	ok = 0 ifTrue: [
+ 		interpreterProxy success: false.
+ 		^ 0].
+ 
+ 	interpreterProxy pop: 3.  "pop args, leave rcvr on stack"
+ 	^ 0
+ !

Item was removed:
- ----- Method: EventHandler>>adaptToWorld: (in category '*MorphicExtras-initialization') -----
- adaptToWorld: aWorld
- 	"If any of my recipients refer to a world or a hand, make them now refer
- 	to the corresponding items in the new world.  (instVarNamed: is slow, later
- 	use perform of two selectors.)"
- 
- 	| value newValue |
- 	#(mouseDownRecipient mouseStillDownRecipient mouseUpRecipient
- 	mouseEnterRecipient mouseLeaveRecipient mouseEnterDraggingRecipient
- 	mouseLeaveDraggingRecipient clickRecipient doubleClickRecipient startDragRecipient keyStrokeRecipient valueParameter) do:
- 		[:aName |
- 		(value _ self instVarNamed: aName asString) ifNotNil:[
- 			newValue _ value adaptedToWorld: aWorld.
- 			(newValue notNil and: [newValue ~~ value])
- 				ifTrue:
- 					[self instVarNamed: aName asString put: newValue]]]!

Item was removed:
- ----- Method: PasteUpMorph class>>initialize (in category '*MorphicExtras-class initialization') -----
- initialize
- 	"Initialize the class"
- 
- 	#('keyStroke') translatedNoop.
- 
- 	self registerInFlapsRegistry.	
- 	ScriptingSystem addCustomEventFor: self named: #keyStroke help: 'when a keystroke happens and nobody heard it' translatedNoop targetMorphClass: PasteUpMorph.!

Item was added:
+ 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: 'MorphicExtras-WebCam'!
+ 
+ !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
+ 
+ !

Item was added:
+ ----- 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)
+ 	))
+ 
+ )
+ !

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

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

Item was added:
+ ----- 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
+ !

Item was added:
+ ----- Method: WebCamMorph class>>initialize (in category 'class initialization') -----
+ initialize
+ 	"CameraMorph initialize"
+ 
+ "	Smalltalk addToStartUpList: self.
+ 	Smalltalk addToShutDownList: self.
+ 
+ "
+ 	CaptureDelayMs := 20.
+ !

Item was added:
+ ----- 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)
+ !

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

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

Item was added:
+ ----- 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.!

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

Item was added:
+ ----- 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.
+ !

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

Item was added:
+ ----- 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].
+ 
+ !

Item was added:
+ ----- 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!

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

Item was added:
+ ----- 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.
+ !

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

Item was added:
+ ----- Method: WebCamMorph>>getActualFPS (in category 'e-toy - settings') -----
+ getActualFPS
+ 	^ actualFPS
+ 
+ !

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

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

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

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

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

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

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

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

Item was added:
+ ----- Method: WebCamMorph>>getMaxFPS (in category 'e-toy - settings') -----
+ getMaxFPS
+ 	^ maxFPS
+ 
+ !

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

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

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

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

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

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

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

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

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

Item was added:
+ ----- 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.
+ 
+ 	!

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

Item was added:
+ ----- 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	
+ !

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

Item was added:
+ ----- Method: WebCamMorph>>scanFxHolder:frame: (in category 'private') -----
+ scanFxHolder: aMorph frame: aForm
+ 	(aMorph respondsTo: #processNewFrame:) ifTrue: [
+ 		^ aMorph processNewFrame: aForm
+ 	].
+ 
+ 	^ aForm
+ "
+ 	aMorph class = #PasteUpMorph
+ "!

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

Item was added:
+ ----- 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].
+ !

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

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

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

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

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

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

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

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

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

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

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

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

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

Item was added:
+ ----- 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].
+ 			
+ 
+ !

Item was added:
+ ----- 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.
+ !

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

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

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

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

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

Item was added:
+ SymbolListType subclass: #WebCamResolutionType
+ 	instanceVariableNames: ''
+ 	classVariableNames: ''
+ 	poolDictionaries: ''
+ 	category: 'MorphicExtras-WebCam'!

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

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

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

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



More information about the etoys-dev mailing list