Chromium Code Reviews| Index: runtime/embedders/openglui/common/gl.dart |
| =================================================================== |
| --- runtime/embedders/openglui/common/gl.dart (revision 20570) |
| +++ runtime/embedders/openglui/common/gl.dart (working copy) |
| @@ -19,39 +19,57 @@ |
| class Window { |
| static int _nextId = 0; |
| - Map<int, RequestAnimationFrameCallback> _callbacks; |
| + List _callbacks; |
| + List _arguments; |
| - Window._internal() : _callbacks = new Map(); |
| + Window._internal() : _callbacks = [], _arguments = []; |
| + int scheduleCallback(callback, [argument]) { |
|
vsm
2013/03/27 22:27:06
Make this private? It's not a public Window api.
|
| + _callbacks.add(callback); |
| + _arguments.add(argument); |
| + return _callbacks.length - 1; |
| + } |
| + |
| int requestAnimationFrame(RequestAnimationFrameCallback callback) { |
| - _callbacks[_nextId++] = callback; |
| + return scheduleCallback(callback, |
| + (new DateTime.now()).millisecondsSinceEpoch); |
| } |
| + |
| void cancelAnimationFrame(id) { |
| - if (_callbacks.containsKey(id)) { |
| - _callbacks.remove(id); |
| - } |
| + _callbacks[id] = null; |
| + _arguments[id] = null; |
| } |
| + |
| get animationFrame { |
| // TODO(gram) |
| return null; |
| } |
| void _dispatch() { |
| - var when = (new DateTime.now()).millisecondsSinceEpoch; |
| // We clear out the callbacks map before calling any callbacks, |
| // as they may schedule new callbacks. |
| var oldcallbacks = _callbacks; |
| - _callbacks = new Map(); |
| - for (var c in oldcallbacks.values) { |
| - c(when); |
| + var oldarguments = _arguments; |
| + _callbacks = []; |
| + _arguments = []; |
| + for (var i = 0; i < oldcallbacks.length; i++) { |
| + if (oldcallbacks[i] != null) { |
| + oldcallbacks[i](oldarguments[i]); |
| + } |
| } |
| + // We could loop around here to handle any callbacks |
| + // scheduled in processing the prior ones, but then we |
| + // need some other mechanism for trying to get requestAnimationFrame |
| + // callbacks at 60fps. |
| } |
| + |
| + Map localStorage = {}; |
|
vsm
2013/03/27 22:27:06
Should this be persistent somehow? (Not in this C
|
| } |
| Window window = new Window._internal(); |
| // The OpenGLUI "equivalent" of HtmlDocument. |
| -class Document { |
| +class Document extends Node { |
| BodyElement _body; |
| get body => _body; |
| Document._internal() : _body = new BodyElement(); |
| @@ -61,6 +79,7 @@ |
| // TODO(gram): make private and call from within library context. |
| update_() { |
| + log("in update"); |
| window._dispatch(); |
| } |
| @@ -76,6 +95,7 @@ |
| static get listeners => _listeners; |
| bool dispatchEvent(Event event) { |
| + var rtn = false; |
| if (!_listeners.containsKey(this)) return false; |
| var listeners = _listeners[this]; |
| if (!listeners.containsKey(event.type)) return false; |
| @@ -83,9 +103,10 @@ |
| for (var eventListener in eventListeners) { |
| if (eventListener != null) { |
| eventListener(event); |
| + rtn = true; |
| } |
| } |
| - return true; |
| + return rtn; |
| } |
| void addListener(String eventType, EventListener handler) { |
| @@ -126,15 +147,17 @@ |
| final String type; |
| EventTarget target; |
| Event(String type) : this.type = type; |
| + preventDefault() {} |
| + stopPropagation() {} |
| } |
| -class KeyEvent extends Event { |
| +class KeyboardEvent extends Event { |
| final bool altKey; |
| final bool ctrlKey; |
| final bool shiftKey; |
| final int keyCode; |
| - KeyEvent(String type, int keycode, bool alt, bool ctrl, bool shift) |
| + KeyboardEvent(String type, int keycode, bool alt, bool ctrl, bool shift) |
| : super(type), |
| keyCode = keycode, |
| altKey = alt, |
| @@ -156,7 +179,6 @@ |
| } |
| } |
| - |
| class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> { |
| int _pauseCount = 0; |
| EventTarget _target; |
| @@ -256,8 +278,8 @@ |
| } |
| class Node extends EventTarget { |
| - Stream<KeyEvent> get onKeyDown => new _EventStream(this, 'keydown'); |
| - Stream<KeyEvent> get onKeyUp => new _EventStream(this, 'keyup'); |
| + Stream<KeyboardEvent> get onKeyDown => new _EventStream(this, 'keydown'); |
| + Stream<KeyboardEvent> get onKeyUp => new _EventStream(this, 'keyup'); |
| Stream<MouseEvent> get onMouseDown => new _EventStream(this, 'mousedown'); |
| Stream<MouseEvent> get onMouseMove => new _EventStream(this, 'mousemove'); |
| Stream<MouseEvent> get onMouseUp => new _EventStream(this, 'mouseup'); |
| @@ -272,13 +294,14 @@ |
| for (var target in document.body.nodes) { |
| event.target = target; |
| if (target.dispatchEvent(event)) { |
| - break; |
| + return; |
| } |
| } |
| + document.dispatchEvent(event); |
| } |
| _dispatchKeyEvent(String type, int keyCode, bool alt, bool ctrl, bool shift) { |
| - _dispatchEvent(new KeyEvent(type, keyCode, alt, ctrl, shift)); |
| + _dispatchEvent(new KeyboardEvent(type, keyCode, alt, ctrl, shift)); |
| } |
| _dispatchMouseEvent(String type, double x, double y) { |
| @@ -302,12 +325,9 @@ |
| _dispatchMouseEvent('mouseup', x, y); |
| class CanvasElement extends Node { |
| - int _height; |
| - int _width; |
| + int height; |
| + int width; |
| - get height => _height; |
| - get width => _width; |
| - |
| CanvasRenderingContext2D _context2d; |
| WebGLRenderingContext _context3d; |
| @@ -318,14 +338,15 @@ |
| CanvasElement({int width, int height}) |
| : super() { |
| - _width = (width == null) ? getDeviceScreenWidth() : width; |
| - _height = (height == null) ? getDeviceScreenHeight() : height; |
| + this.width = (width == null) ? getDeviceScreenWidth() : width; |
| + this.height = (height == null) ? getDeviceScreenHeight() : height; |
| + getContext('2d'); |
| } |
| CanvasRenderingContext getContext(String contextId) { |
| if (contextId == "2d") { |
| if (_context2d == null) { |
| - _context2d = new CanvasRenderingContext2D(this, _width, _height); |
| + _context2d = new CanvasRenderingContext2D(this, width, height); |
| } |
| return _context2d; |
| } else if (contextId == "webgl" || |
| @@ -336,6 +357,18 @@ |
| return _context3d; |
| } |
| } |
| + |
| + String toDataUrl(String type) { |
| + // This needs to take the contents of the underlying |
| + // canvas painted by the 2d context, give that a unique |
| + // URL, and return that. The canvas element should be |
| + // reuable afterwards without destroying the previously |
| + // rendered data associated with this URL. |
| + assert(_context2d != null); |
| + var rtn = src; |
| + _context2d = null; |
| + return rtn; |
| + } |
| } |
| class CanvasRenderingContext { |
| @@ -344,6 +377,21 @@ |
| CanvasRenderingContext(this.canvas); |
| } |
| +class AudioElement { |
| + double volume; |
| + String _src; |
| + get src => _src; |
| + set src(String v) { |
| + _src = v; |
| + loadSample(v); |
| + } |
| + |
| + AudioElement([this._src]); |
| + void play() { |
| + playSample(_src); |
| + } |
| +} |
| + |
| // The simplest way to call native code: top-level functions. |
| int systemRand() native "SystemRand"; |
| void systemSrand(int seed) native "SystemSrand"; |
| @@ -646,6 +694,57 @@ |
| void C2DCreateNativeContext(int handle, int width, int height) |
| native "C2DCreateNativeContext"; |
| +void C2DSetFillGradient(int handle, bool isRadial, |
|
vsm
2013/03/27 22:27:06
Should these C2D functions be private?
|
| + double x0, double y0, double r0, |
| + double x1, double y1, double r1, |
| + List<double> positions, List<String> colors) |
| + native "C2DSetFillGradient"; |
| + |
| +void C2DSetStrokeGradient(int handle, bool isRadial, |
| + double x0, double y0, double r0, |
| + double x1, double y1, double r1, |
| + List<double> positions, List<String> colors) |
| + native "C2DSetStrokeGradient"; |
| + |
| +int C2DGetImageWidth(String url) |
| + native "C2DGetImageWidth"; |
| + |
| +int C2DGetImageHeight(String url) |
| + native "C2DGetImageHeight"; |
| + |
| +class CanvasGradient { |
| + num _x0, _y0, _r0 = 0, _x1, _y1, _r1 = 0; |
| + bool _isRadial; |
| + List<double> _colorStopPositions = []; |
| + List<String> _colorStopColors = []; |
| + |
| + void addColorStop(num offset, String color) { |
| + _colorStopPositions.add(offset.toDouble()); |
| + _colorStopColors.add(color); |
| + } |
| + |
| + CanvasGradient.linear(this._x0, this._y0, this._x1, this._y1) |
| + : _isRadial = false; |
| + |
| + CanvasGradient.radial(this._x0, this._y0, this._r0, |
| + this._x1, this._y1, this._r1) |
| + : _isRadial = true; |
| + |
| + void setAsFillStyle(_handle) { |
| + C2DSetFillGradient(_handle, _isRadial, |
| + _x0.toDouble(), _y0.toDouble(), _r0.toDouble(), |
| + _x1.toDouble(), _y1.toDouble(), _r1.toDouble(), |
| + _colorStopPositions, _colorStopColors); |
| + } |
| + |
| + void setAsStrokeStyle(_handle) { |
| + C2DSetStrokeGradient(_handle, _isRadial, |
| + _x0.toDouble(), _y0.toDouble(), _r0.toDouble(), |
| + _x1.toDouble(), _y1.toDouble(), _r1.toDouble(), |
| + _colorStopPositions, _colorStopColors); |
| + } |
| +} |
| + |
| class ImageElement extends Node { |
| Stream<Event> get onLoad => new _EventStream(this, 'load'); |
| @@ -654,23 +753,36 @@ |
| int _height; |
| get src => _src; |
| + |
| set src(String v) { |
| + log("Set ImageElement src to $v"); |
| _src = v; |
| - var e = new Event('load'); |
| - e.target = this; |
| - dispatchEvent(e); |
| } |
| - get width => _width; |
| + // The onLoad handler may be set after the src, so |
| + // we hook into that here... |
| + void addListener(String eventType, EventListener handler) { |
| + super.addListener(eventType, handler); |
| + if (eventType == 'load') { |
| + var e = new Event('load'); |
| + e.target = this; |
| + window.scheduleCallback(handler, e); |
| + } |
| + } |
| + |
| + get width => _width == null ? _width = C2DGetImageWidth(_src) : _width; |
| + get height => _height == null ? _height = C2DGetImageHeight(_src) : _height; |
| set width(int widthp) => _width = widthp; |
| - |
| - get height => _height; |
| set height(int heightp) => _height = heightp; |
| ImageElement({String srcp, int widthp, int heightp}) |
| : _src = srcp, |
| _width = widthp, |
| _height = heightp { |
| + if (_src != null) { |
| + if (_width == null) _width = C2DGetImageWidth(_src); |
| + if (_height == null) _height = C2DGetImageHeight(_src); |
| + } |
| } |
| } |
| @@ -690,6 +802,11 @@ |
| CanvasRenderingContext2D.next_handle = 0; |
| } |
| +class Rect { |
| + final num top, left, width, height; |
| + const Rect(this.left, this.top, this.width, this.height); |
| +} |
| + |
| class CanvasRenderingContext2D extends CanvasRenderingContext { |
| // TODO(gram): We need to support multiple contexts, for cached content |
| // prerendered to an offscreen buffer. For this we will use handles, with |
| @@ -720,7 +837,13 @@ |
| // fillStyle = strokeStyle = "red" |
| var _fillStyle = "#000"; |
| set fillStyle(fs) { |
| - C2DSetFillStyle(_handle, _fillStyle = fs); |
| + _fillStyle = fs; |
| + // TODO(gram): Support for CanvasPattern. |
| + if (fs is CanvasGradient) { |
| + fs.setAsFillStyle(_handle); |
| + } else { |
| + C2DSetFillStyle(_handle, fs); |
| + } |
| } |
| get fillStyle => _fillStyle; |
| @@ -791,7 +914,13 @@ |
| var _strokeStyle = "#000"; |
| get strokeStyle => _strokeStyle; |
| set strokeStyle(ss) { |
| - C2DSetStrokeStyle(_handle, _strokeStyle = ss); |
| + _strokeStyle = ss; |
| + // TODO(gram): Support for CanvasPattern. |
| + if (ss is CanvasGradient) { |
| + ss.setAsStrokeStyle(_handle); |
| + } else { |
| + C2DSetStrokeStyle(_handle, ss); |
| + } |
| } |
| String _textAlign = "start"; |
| @@ -864,19 +993,25 @@ |
| } |
| CanvasGradient createLinearGradient(num x0, num y0, num x1, num y1) { |
| - throw new Exception('Unimplemented createLinearGradient'); |
| + return new CanvasGradient.linear(x0, y0, x1, y1); |
| } |
| CanvasPattern createPattern(canvas_OR_image, String repetitionType) { |
| throw new Exception('Unimplemented createPattern'); |
| } |
| - CanvasGradient createRadialGradient(num x0, num y0, num x1, num y1, num r1) { |
| - throw new Exception('Unimplemented createRadialGradient'); |
| + CanvasGradient createRadialGradient(num x0, num y0, num r0, |
| + num x1, num y1, num r1) { |
| + return new CanvasGradient.radial(x0, y0, r0, x1, y1, r1); |
| } |
| void drawImage(element, num x1, num y1, |
| [num w1, num h1, num x2, num y2, num w2, num h2]) { |
| + if (element == null || element.src == null || element.src.length == 0) { |
| + throw "drawImage called with no valid src"; |
| + } else { |
| + log("drawImage ${element.src}"); |
| + } |
| var w = (element.width == null) ? 0 : element.width; |
| var h = (element.height == null) ? 0 : element.height; |
| if (!?w1) { // drawImage(element, dx, dy) |
| @@ -892,6 +1027,16 @@ |
| } |
| } |
| + void drawImageAtScale(element, Rect dest, {Rect sourceRect}) { |
| + if (sourceRect == null) { |
| + drawImage(element, dest.left, dest.top, dest.width, dest.height); |
| + } else { |
| + drawImage(element, |
| + sourceRect.left, sourceRect.top, sourceRect.width, sourceRect.height, |
| + dest.left, dest.top, dest.width, dest.height); |
| + } |
| + } |
| + |
| void fill() => C2DFill(_handle); |
| void fillRect(num x, num y, num w, num h) => |
| @@ -1034,3 +1179,5 @@ |
| } |
| } |
| +int loadSample(String s) native "LoadSample"; |
|
vsm
2013/03/27 22:27:06
Make these private?
|
| +int playSample(String s) native "PlaySample"; |