blob: c37c80b3cd47877cf6b072456e7c0e3dcdba82b2 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library android_extension;
import 'dart:async';
// A VERY simplified DOM.
class BodyElement {
List _nodes;
get nodes => _nodes;
BodyElement() : _nodes = new List();
}
// The OpenGLUI "equivalent" of Window.
typedef void RequestAnimationFrameCallback(num highResTime);
class Window {
static int _nextId = 0;
List _callbacks;
List _arguments;
Window._internal() : _callbacks = [], _arguments = [];
int _scheduleCallback(callback, [argument]) {
_callbacks.add(callback);
_arguments.add(argument);
return _callbacks.length - 1;
}
int requestAnimationFrame(RequestAnimationFrameCallback callback) {
return _scheduleCallback(callback,
(new DateTime.now()).millisecondsSinceEpoch);
}
void cancelAnimationFrame(id) {
_callbacks[id] = null;
_arguments[id] = null;
}
get animationFrame {
// TODO(gram)
return null;
}
void _dispatch() {
// We clear out the callbacks map before calling any callbacks,
// as they may schedule new callbacks.
var oldcallbacks = _callbacks;
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 = {}; // TODO(gram) - Make this persistent.
}
Window window = new Window._internal();
// The OpenGLUI "equivalent" of HtmlDocument.
class Document extends Node {
BodyElement _body;
get body => _body;
Document._internal() : _body = new BodyElement();
}
Document document = new Document._internal();
// TODO(gram): make private and call from within library context.
update_() {
log("in update");
window._dispatch();
}
// Event handling. This is very kludgy for now, especially the
// bare-bones Stream stuff!
typedef void EventListener(Event event);
class EventTarget {
static Map<EventTarget, Map<String, List<EventListener>>>
_listeners = new Map();
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;
var eventListeners = listeners[event.type];
for (var eventListener in eventListeners) {
if (eventListener != null) {
eventListener(event);
rtn = true;
}
}
return rtn;
}
void addListener(String eventType, EventListener handler) {
if (!_listeners.containsKey(this)) {
_listeners[this] = new Map();
}
var listeners = _listeners[this];
if (!listeners.containsKey(eventType)) {
listeners[eventType] = new List();
}
var event_listeners = listeners[eventType];
for (var i = 0; i < event_listeners.length; i++) {
if (event_listeners[i] == null) {
event_listeners[i] = handler;
return;
}
}
event_listeners.add(handler);
}
void removeListener(String eventType, EventListener handler) {
if (_listeners.containsKey(this)) {
var listeners = _listeners[this];
if (listeners.containsKey(eventType)) {
var event_listeners = listeners[eventType];
for (var i = 0; i < event_listeners.length; i++) {
if (event_listeners[i] == handler) {
event_listeners[i] = null;
break;
}
}
}
}
}
}
class Event {
final String type;
EventTarget target;
Event(String type) : this.type = type;
preventDefault() {}
stopPropagation() {}
}
class KeyboardEvent extends Event {
final bool altKey;
final bool ctrlKey;
final bool shiftKey;
final int keyCode;
KeyboardEvent(String type, int keycode, bool alt, bool ctrl, bool shift)
: super(type),
keyCode = keycode,
altKey = alt,
ctrlKey = ctrl,
shiftKey = shift {
}
}
class MouseEvent extends Event {
final int screenX, screenY;
final int clientX, clientY;
MouseEvent(String type, int x, int y)
: super(type),
screenX = x,
screenY = y,
clientX = x,
clientY = y {
}
}
class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
int _pauseCount = 0;
EventTarget _target;
final String _eventType;
var _onData;
_EventStreamSubscription(this._target, this._eventType, this._onData) {
_tryResume();
}
void cancel() {
if (_canceled) {
throw new StateError("Subscription has been canceled.");
}
_unlisten();
// Clear out the target to indicate this is complete.
_target = null;
_onData = null;
}
bool get _canceled => _target == null;
void onData(void handleData(T event)) {
if (_canceled) {
throw new StateError("Subscription has been canceled.");
}
// Remove current event listener.
_unlisten();
_onData = handleData
_tryResume();
}
/// Has no effect.
void onError(void handleError(Object error)) {}
/// Has no effect.
void onDone(void handleDone()) {}
void pause([Future resumeSignal]) {
if (_canceled) {
throw new StateError("Subscription has been canceled.");
}
++_pauseCount;
_unlisten();
if (resumeSignal != null) {
resumeSignal.whenComplete(resume);
}
}
bool get _paused => _pauseCount > 0;
void resume() {
if (_canceled) {
throw new StateError("Subscription has been canceled.");
}
if (!_paused) {
throw new StateError("Subscription is not paused.");
}
--_pauseCount;
_tryResume();
}
void _tryResume() {
if (_onData != null && !_paused) {
_target.addListener(_eventType, _onData);
}
}
void _unlisten() {
if (_onData != null) {
_target.removeListener(_eventType, _onData);
}
}
Future asFuture([var futureValue]) {
// We just need a future that will never succeed or fail.
Completer completer = new Completer();
return completer.future;
}
}
class _EventStream<T extends Event> extends Stream<T> {
final Object _target;
final String _eventType;
_EventStream(this._target, this._eventType);
// DOM events are inherently multi-subscribers.
Stream<T> asBroadcastStream() => this;
bool get isBroadcast => true;
StreamSubscription<T> listen(void onData(T event),
{ void onError(Object error),
void onDone(),
bool cancelOnError}) {
return new _EventStreamSubscription<T>(
this._target, this._eventType, onData);
}
}
class Node extends EventTarget {
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');
}
// TODO(gram): If we support more than one on-screen canvas, we will
// need to filter dispatched mouse and key events by the target Node
// with more granularity; right now we just iterate through DOM nodes
// until we find one that handles the event.
_dispatchEvent(Event event) {
assert(document.body.nodes.length <= 1);
for (var target in document.body.nodes) {
event.target = target;
if (target.dispatchEvent(event)) {
return;
}
}
document.dispatchEvent(event);
}
_dispatchKeyEvent(String type, int keyCode, bool alt, bool ctrl, bool shift) {
_dispatchEvent(new KeyboardEvent(type, keyCode, alt, ctrl, shift));
}
_dispatchMouseEvent(String type, double x, double y) {
_dispatchEvent(new MouseEvent(type, x.toInt(), y.toInt()));
}
// These next few are called by vmglue.cc.
onKeyDown_(int when, int keyCode, bool alt, bool ctrl, bool shift, int repeat)
=> _dispatchKeyEvent('keydown', keyCode, alt, ctrl, shift);
onKeyUp_(int when, int keyCode, bool alt, bool ctrl, bool shift, int repeat) =>
_dispatchKeyEvent('keyup', keyCode, alt, ctrl, shift);
onMouseDown_(int when, double x, double y) =>
_dispatchMouseEvent('mousedown', x, y);
onMouseMove_(int when, double x, double y) =>
_dispatchMouseEvent('mousemove', x, y);
onMouseUp_(int when, double x, double y) =>
_dispatchMouseEvent('mouseup', x, y);
class CanvasElement extends Node {
int height;
int width;
CanvasRenderingContext2D _context2d;
WebGLRenderingContext _context3d;
// For use with drawImage, we want to support a src property
// like ImageElement, which maps to the context handle in native
// code.
get src => "context2d://${_context2d.handle}";
CanvasElement({int width, int height})
: super() {
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);
}
return _context2d;
} else if (contextId == "webgl" ||
contextId == "experimental-webgl") {
if (_context3d == null) {
_context3d = new WebGLRenderingContext(this);
}
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 {
final CanvasElement canvas;
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";
void log(String what) native "Log";
int getDeviceScreenWidth() native "GetDeviceScreenWidth";
int getDeviceScreenHeight() native "GetDeviceScreenHeight";
// EGL functions.
void glSwapBuffers() native "SwapBuffers";
// GL functions.
void glAttachShader(int program, int shader) native "GLAttachShader";
void glBindBuffer(int target, int buffer) native "GLBindBuffer";
void glBufferData(int target, List data, int usage) native "GLBufferData";
void glClearColor(num r, num g, num b, num alpha) native "GLClearColor";
void glClearDepth(num depth) native "GLClearDepth";
void glClear(int mask) native "GLClear";
void glCompileShader(int shader) native "GLCompileShader";
int glCreateBuffer() native "GLCreateBuffer";
int glCreateProgram() native "GLCreateProgram";
int glCreateShader(int shaderType) native "GLCreateShader";
void glDrawArrays(int mode, int first, int count) native "GLDrawArrays";
void glEnableVertexAttribArray(int index) native "GLEnableVertexAttribArray";
int glGetAttribLocation(int program, String name) native "GLGetAttribLocation";
int glGetError() native "GLGetError";
int glGetProgramParameter(int program, int param)
native "GLGetProgramParameter";
int glGetShaderParameter(int shader, int param) native "GLGetShaderParameter";
int glGetUniformLocation(int program, String name)
native "GLGetUniformLocation";
void glLinkProgram(int program) native "GLLinkProgram";
void glShaderSource(int shader, String source) native "GLShaderSource";
void glUniform1f(int location, double v0) native "GLUniform1f";
void glUniform2f(int location, double v0, double v1) native "GLUniform2f";
void glUniform3f(int location, double v0, double v1, double v2)
native "GLUniform3f";
void glUniform4f(int location, double v0, double v1, double v2, double v3)
native "GLUniform4f";
void glUniform1i(int location, int v0) native "GLUniform1i";
void glUniform2i(int location, int v0, int v1) native "GLUniform2i";
void glUniform3i(int location, int v0, int v1, int v2) native "GLUniform3i";
void glUniform4i(int location, int v0, int v1, int v2, int v3)
native "GLUniform4i";
void glUniform1fv(int location, List values) native "GLUniform1fv";
void glUniform2fv(int location, List values) native "GLUniform2fv";
void glUniform3fv(int location, List values) native "GLUniform3fv";
void glUniform4fv(int location, List values) native "GLUniform4fv";
void glUniform1iv(int location, List values) native "GLUniform1iv";
void glUniform2iv(int location, List values) native "GLUniform2iv";
void glUniform3iv(int location, List values) native "GLUniform3iv";
void glUniform4iv(int location, List values) native "GLUniform4iv";
void glUseProgram(int program) native "GLUseProgram";
void glVertexAttribPointer(int index, int size, int type, bool normalized,
int stride, int pointer) native "GLVertexAttribPointer";
void glViewport(int x, int y, int width, int height) native "GLViewport";
int glArrayBuffer() native "GLArrayBuffer";
int glColorBufferBit() native "GLColorBufferBit";
int glCompileStatus() native "GLCompileStatus";
int glDeleteStatus() native "GLDeleteStatus";
int glDepthBufferBit() native "GLDepthBufferBit";
int glFloat() native "GLFloat";
int glFragmentShader() native "GLFragmentShader";
int glLinkStatus() native "GLLinkStatus";
int glStaticDraw() native "GLStaticDraw";
int glTriangleStrip() native "GLTriangleStrip";
int glTriangles() native "GLTriangles";
int glTrue() native "GLTrue";
int glValidateStatus() native "GLValidateStatus";
int glVertexShader() native "GLVertexShader";
String glGetShaderInfoLog(int shader) native "GLGetShaderInfoLog";
String glGetProgramInfoLog(int program) native "GLGetProgramInfoLog";
class WebGLRenderingContext extends CanvasRenderingContext {
WebGLRenderingContext(canvas) : super(canvas);
static get ARRAY_BUFFER => glArrayBuffer();
static get COLOR_BUFFER_BIT => glColorBufferBit();
static get COMPILE_STATUS => glCompileStatus();
static get DELETE_STATUS => glDeleteStatus();
static get DEPTH_BUFFER_BIT => glDepthBufferBit();
static get FLOAT => glFloat();
static get FRAGMENT_SHADER => glFragmentShader();
static get LINK_STATUS => glLinkStatus();
static get STATIC_DRAW => glStaticDraw();
static get TRUE => glTrue();
static get TRIANGLE_STRIP => glTriangleStrip();
static get TRIANGLES => glTriangles();
static get VALIDATE_STATUS => glValidateStatus();
static get VERTEX_SHADER => glVertexShader();
attachShader(program, shader) => glAttachShader(program, shader);
bindBuffer(target, buffer) => glBindBuffer(target, buffer);
bufferData(target, data, usage) => glBufferData(target, data, usage);
clearColor(r, g, b, alpha) => glClearColor(r, g, b, alpha);
clearDepth(depth) => glClearDepth(depth);
clear(mask) => glClear(mask);
compileShader(shader) => glCompileShader(shader);
createBuffer() => glCreateBuffer();
createProgram() => glCreateProgram();
createShader(shaderType) => glCreateShader(shaderType);
drawArrays(mode, first, count) => glDrawArrays(mode, first, count);
enableVertexAttribArray(index) => glEnableVertexAttribArray(index);
getAttribLocation(program, name) => glGetAttribLocation(program, name);
getError() => glGetError();
getProgramParameter(program, name) {
var rtn = glGetProgramParameter(program, name);
if (name == DELETE_STATUS ||
name == LINK_STATUS ||
name == VALIDATE_STATUS) {
return (rtn == 0) ? false : true;
}
return rtn;
}
getShaderParameter(shader, name) {
var rtn = glGetShaderParameter(shader, name);
if (name == DELETE_STATUS || name == COMPILE_STATUS) {
return (rtn == 0) ? false : true;
}
return rtn;
}
getUniformLocation(program, name) => glGetUniformLocation(program, name);
linkProgram(program) => glLinkProgram(program);
shaderSource(shader, source) => glShaderSource(shader, source);
uniform1f(location, v0) => glUniform1f(location, v0);
uniform2f(location, v0, v1) => glUniform2f(location, v0, v1);
uniform3f(location, v0, v1, v2) => glUniform3f(location, v0, v1, v2);
uniform4f(location, v0, v1, v2, v3) => glUniform4f(location, v0, v1, v2, v3);
uniform1i(location, v0) => glUniform1i(location, v0);
uniform2i(location, v0, v1) => glUniform2i(location, v0, v1);
uniform3i(location, v0, v1, v2) => glUniform3i(location, v0, v1, v2);
uniform4i(location, v0, v1, v2, v3) => glUniform4i(location, v0, v1, v2, v3);
uniform1fv(location, values) => glUniform1fv(location, values);
uniform2fv(location, values) => glUniform2fv(location, values);
uniform3fv(location, values) => glUniform3fv(location, values);
uniform4fv(location, values) => glUniform4fv(location, values);
uniform1iv(location, values) => glUniform1iv(location, values);
uniform2iv(location, values) => glUniform2iv(location, values);
uniform3iv(location, values) => glUniform3iv(location, values);
uniform4iv(location, values) => glUniform4iv(location, values);
useProgram(program) => glUseProgram(program);
vertexAttribPointer(index, size, type, normalized, stride, pointer) =>
glVertexAttribPointer(index, size, type, normalized, stride, pointer);
viewport(x, y, width, height) => glViewport(x, y, width, height);
getShaderInfoLog(shader) => glGetShaderInfoLog(shader);
getProgramInfoLog(program) => glGetProgramInfoLog(program);
// TODO(vsm): Kill.
noSuchMethod(invocation) {
throw new Exception('Unimplemented ${invocation.memberName}');
}
}
//------------------------------------------------------------------
// Simple audio support.
void playBackground(String path) native "PlayBackground";
void stopBackground() native "StopBackground";
//-------------------------------------------------------------------
// Set up print().
get _printClosure => (s) {
try {
log(s);
} catch (_) {
throw(s);
}
};
//------------------------------------------------------------------
// Temp hack for compat with WebGL.
class Float32Array extends List<double> {
Float32Array.fromList(List a) {
addAll(a);
}
}
//------------------------------------------------------------------
// 2D canvas support
int _SetWidth(int handle, int width)
native "C2DSetWidth";
int _SetHeight(int handle, int height)
native "C2DSetHeight";
double _SetGlobalAlpha(int handle, double globalAlpha)
native "C2DSetGlobalAlpha";
void _SetFillStyle(int handle, fs)
native "C2DSetFillStyle";
String _SetFont(int handle, String font)
native "C2DSetFont";
void _SetGlobalCompositeOperation(int handle, String op)
native "C2DSetGlobalCompositeOperation";
_SetLineCap(int handle, String lc)
native "C2DSetLineCap";
_SetLineJoin(int handle, String lj)
native "C2DSetLineJoin";
_SetLineWidth(int handle, double w)
native "C2DSetLineWidth";
_SetMiterLimit(int handle, double limit)
native "C2DSetMiterLimit";
_SetShadowBlur(int handle, double blur)
native "C2DSetShadowBlur";
_SetShadowColor(int handle, String color)
native "C2DSetShadowColor";
_SetShadowOffsetX(int handle, double offset)
native "C2DSetShadowOffsetX";
_SetShadowOffsetY(int handle, double offset)
native "C2DSetShadowOffsetY";
void _SetStrokeStyle(int handle, ss)
native "C2DSetStrokeStyle";
String _SetTextAlign(int handle, String align)
native "C2DSetTextAlign";
String _SetTextBaseline(int handle, String baseline)
native "C2DSetTextBaseline";
_GetBackingStorePixelRatio(int handle)
native "C2DGetBackingStorePixelRatio";
void _SetImageSmoothingEnabled(int handle, bool ise)
native "C2DSetImageSmoothingEnabled";
void _SetLineDash(int handle, List v)
native "C2DSetLineDash";
_SetLineDashOffset(int handle, int v)
native "C2DSetLineDashOffset";
void _Arc(int handle, double x, double y, double radius,
double startAngle, double endAngle, [bool anticlockwise = false])
native "C2DArc";
void _ArcTo(int handle, double x1, double y1,
double x2, double y2, double radius)
native "C2DArcTo";
void _ArcTo2(int handle, double x1, double y1,
double x2, double y2, double radiusX,
double radiusY, double rotation)
native "C2DArcTo2";
void _BeginPath(int handle)
native "C2DBeginPath";
void _BezierCurveTo(int handle, double cp1x, double cp1y,
double cp2x, double cp2y, double x, double y)
native "C2DBezierCurveTo";
void _ClearRect(int handle, double x, double y, double w, double h)
native "C2DClearRect";
void _Clip(int handle)
native "C2DClip";
void _ClosePath(int handle)
native "C2DClosePath";
ImageData _CreateImageDataFromDimensions(int handle, num w, num h)
native "C2DCreateImageDataFromDimensions";
void _DrawImage(int handle, String src_url,
int sx, int sy,
bool has_src_dimensions, int sw, int sh,
int dx, int dy,
bool has_dst_dimensions, int dw, int dh)
native "C2DDrawImage";
void _Fill(int handle)
native "C2DFill";
void _FillRect(int handle, double x, double y, double w, double h)
native "C2DFillRect";
void _FillText(int handle, String text, double x, double y, double maxWidth)
native "C2DFillText";
ImageData _GetImageData(num sx, num sy, num sw, num sh)
native "C2DGetImageData";
void _LineTo(int handle, double x, double y)
native "C2DLineTo";
double _MeasureText(int handle, String text)
native "C2DMeasureText";
void _MoveTo(int handle, double x, double y)
native "C2DMoveTo";
void _PutImageData(int handle, ImageData imagedata, double dx, double dy)
native "C2DPutImageData";
void _QuadraticCurveTo(int handle, double cpx, double cpy,
double x, double y)
native "C2DQuadraticCurveTo";
void _Rect(int handle, double x, double y, double w, double h)
native "C2DRect";
void _Restore(int handle)
native "C2DRestore";
void _Rotate(int handle, double a)
native "C2DRotate";
void _Save(int handle)
native "C2DSave";
void _Scale(int handle, double sx, double sy)
native "C2DScale";
void _SetTransform(int handle, double m11, double m12,
double m21, double m22, double dx, double dy)
native "C2DSetTransform";
void _Stroke(int handle)
native "C2DStroke";
void _StrokeRect(int handle, double x, double y, double w, double h)
native "C2DStrokeRect";
void _StrokeText(int handle, String text, double x, double y,
double maxWidth)
native "C2DStrokeText";
void _Transform(int handle, double m11, double m12,
double m21, double m22, double dx, double dy)
native "C2DTransform";
void _Translate(int handle, double x, double y)
native "C2DTranslate";
void _CreateNativeContext(int handle, int width, int height)
native "C2DCreateNativeContext";
void _SetFillGradient(int handle, bool isRadial,
double x0, double y0, double r0,
double x1, double y1, double r1,
List<double> positions, List<String> colors)
native "C2DSetFillGradient";
void _SetStrokeGradient(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 _GetImageWidth(String url)
native "C2DGetImageWidth";
int _GetImageHeight(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) {
_SetFillGradient(_handle, _isRadial,
_x0.toDouble(), _y0.toDouble(), _r0.toDouble(),
_x1.toDouble(), _y1.toDouble(), _r1.toDouble(),
_colorStopPositions, _colorStopColors);
}
void setAsStrokeStyle(_handle) {
_SetStrokeGradient(_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');
String _src;
int _width;
int _height;
get src => _src;
set src(String v) {
log("Set ImageElement src to $v");
_src = v;
}
// 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 = _GetImageWidth(_src) : _width;
get height => _height == null ? _height = _GetImageHeight(_src) : _height;
set width(int widthp) => _width = widthp;
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 = _GetImageWidth(_src);
if (_height == null) _height = _GetImageHeight(_src);
}
}
}
class ImageData {
final Uint8ClampedArray data;
final int height;
final int width;
ImageData(this.height, this.width, this.data);
}
class TextMetrics {
final num width;
TextMetrics(this.width);
}
void shutdown() {
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
// handle 0 being the physical display.
static int next_handle = 0;
int _handle = 0;
get handle => _handle;
int _width, _height;
set width(int w) { _width = SetWidth(_handle, w); }
get width => _width;
set height(int h) { _height = SetHeight(_handle, h); }
get height => _height;
CanvasRenderingContext2D(canvas, width, height) : super(canvas) {
_width = width;
_height = height;
_CreateNativeContext(_handle = next_handle++, width, height);
}
double _alpha = 1.0;
set globalAlpha(num a) {
_alpha = _SetGlobalAlpha(_handle, a.toDouble());
}
get globalAlpha => _alpha;
// TODO(gram): make sure we support compound assignments like:
// fillStyle = strokeStyle = "red"
var _fillStyle = "#000";
set fillStyle(fs) {
_fillStyle = fs;
// TODO(gram): Support for CanvasPattern.
if (fs is CanvasGradient) {
fs.setAsFillStyle(_handle);
} else {
_SetFillStyle(_handle, fs);
}
}
get fillStyle => _fillStyle;
String _font = "10px sans-serif";
set font(String f) { _font = _SetFont(_handle, f); }
get font => _font;
String _globalCompositeOperation = "source-over";
set globalCompositeOperation(String o) =>
_SetGlobalCompositeOperation(_handle, _globalCompositeOperation = o);
get globalCompositeOperation => _globalCompositeOperation;
String _lineCap = "butt"; // "butt", "round", "square"
get lineCap => _lineCap;
set lineCap(String lc) => _SetLineCap(_handle, _lineCap = lc);
int _lineDashOffset = 0;
get lineDashOffset => _lineDashOffset;
set lineDashOffset(num v) {
_lineDashOffset = v.toInt();
_SetLineDashOffset(_handle, _lineDashOffset);
}
String _lineJoin = "miter"; // "round", "bevel", "miter"
get lineJoin => _lineJoin;
set lineJoin(String lj) => _SetLineJoin(_handle, _lineJoin = lj);
num _lineWidth = 1.0;
get lineWidth => _lineWidth;
set lineWidth(num w) {
_SetLineWidth(_handle, w.toDouble());
_lineWidth = w;
}
num _miterLimit = 10.0; // (default 10)
get miterLimit => _miterLimit;
set miterLimit(num limit) {
_SetMiterLimit(_handle, limit.toDouble());
_miterLimit = limit;
}
num _shadowBlur;
get shadowBlur => _shadowBlur;
set shadowBlur(num blur) {
_shadowBlur = blur;
_SetShadowBlur(_handle, blur.toDouble());
}
String _shadowColor;
get shadowColor => _shadowColor;
set shadowColor(String color) =>
_SetShadowColor(_handle, _shadowColor = color);
num _shadowOffsetX;
get shadowOffsetX => _shadowOffsetX;
set shadowOffsetX(num offset) {
_shadowOffsetX = offset;
_SetShadowOffsetX(_handle, offset.toDouble());
}
num _shadowOffsetY;
get shadowOffsetY => _shadowOffsetY;
set shadowOffsetY(num offset) {
_shadowOffsetY = offset;
_SetShadowOffsetY(_handle, offset.toDouble());
}
var _strokeStyle = "#000";
get strokeStyle => _strokeStyle;
set strokeStyle(ss) {
_strokeStyle = ss;
// TODO(gram): Support for CanvasPattern.
if (ss is CanvasGradient) {
ss.setAsStrokeStyle(_handle);
} else {
_SetStrokeStyle(_handle, ss);
}
}
String _textAlign = "start";
get textAlign => _textAlign;
set textAlign(String a) { _textAlign = _SetTextAlign(_handle, a); }
String _textBaseline = "alphabetic";
get textBaseline => _textBaseline;
set textBaseline(String b) { _textBaseline = _SetTextBaseline(_handle, b); }
get webkitBackingStorePixelRatio => _GetBackingStorePixelRatio(_handle);
bool _webkitImageSmoothingEnabled;
get webkitImageSmoothingEnabled => _webkitImageSmoothingEnabled;
set webkitImageSmoothingEnabled(bool v) =>
_SetImageSmoothingEnabled(_webkitImageSmoothingEnabled = v);
get webkitLineDash => lineDash;
set webkitLineDash(List v) => lineDash = v;
get webkitLineDashOffset => lineDashOffset;
set webkitLineDashOffset(num v) => lineDashOffset = v;
// Methods
void arc(num x, num y, num radius, num a1, num a2, bool anticlockwise) {
if (radius < 0) {
// throw IndexSizeError
} else {
_Arc(_handle, x.toDouble(), y.toDouble(), radius.toDouble(),
a1.toDouble(), a2.toDouble(), anticlockwise);
}
}
// Note - looking at the Dart docs it seems Dart doesn't support
// the second form in the browser.
void arcTo(num x1, num y1, num x2, num y2,
num radiusX, [num radiusY, num rotation]) {
if (radiusY == null) {
_ArcTo(_handle, x1.toDouble(), y1.toDouble(),
x2.toDouble(), y2.toDouble(), radiusX.toDouble());
} else {
_ArcTo2(_handle, x1.toDouble(), y1.toDouble(),
x2.toDouble(), y2.toDouble(),
radiusX.toDouble(), radiusY.toDouble(),
rotation.toDouble());
}
}
void beginPath() => _BeginPath(_handle);
void bezierCurveTo(num cp1x, num cp1y, num cp2x, num cp2y,
num x, num y) =>
_BezierCurveTo(_handle, cp1x.toDouble(), cp1y.toDouble(),
cp2x.toDouble(), cp2y.toDouble(),
x.toDouble(), y.toDouble());
void clearRect(num x, num y, num w, num h) =>
_ClearRect(_handle, x.toDouble(), y.toDouble(),
w.toDouble(), h.toDouble());
void clip() => _Clip(_handle);
void closePath() => _ClosePath(_handle);
ImageData createImageData(var imagedata_OR_sw, [num sh = null]) {
if (sh == null) {
throw new Exception('Unimplemented createImageData(imagedata)');
} else {
return _CreateImageDataFromDimensions(_handle, imagedata_OR_sw, sh);
}
}
CanvasGradient createLinearGradient(num x0, num y0, num x1, num y1) {
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 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)
_DrawImage(_handle, element.src, 0, 0, false, w, h,
x1.toInt(), y1.toInt(), false, 0, 0);
} else if (!?x2) { // drawImage(element, dx, dy, dw, dh)
_DrawImage(_handle, element.src, 0, 0, false, w, h,
x1.toInt(), y1.toInt(), true, w1.toInt(), h1.toInt());
} else { // drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
_DrawImage(_handle, element.src,
x1.toInt(), y1.toInt(), true, w1.toInt(), h1.toInt(),
x2.toInt(), y2.toInt(), true, w2.toInt(), h2.toInt());
}
}
void drawImage(source, num destX, num destY) {
_drawImage(source, destX, destY);
}
void drawImageScaled(source,
num destX, num destY, num destWidth, num destHeight) {
_drawImage(source, destX, destY, destWidth, destHeight);
}
void drawImageScaledFromSource(source,
num sourceX, num sourceY, num sourceWidth, num sourceHeight,
num destX, num destY, num destWidth, num destHeight) {
_drawImage(source, sourceX, sourceY, sourceWidth, sourceHeight,
destX, destY, destWidth, destHeight);
}
void drawImageToRect(source, Rect dest, {Rect sourceRect}) {
if (sourceRect == null) {
_drawImage(source, dest.left, dest.top, dest.width, dest.height);
} else {
_drawImage(source,
sourceRect.left, sourceRect.top, sourceRect.width, sourceRect.height,
dest.left, dest.top, dest.width, dest.height);
}
}
void fill() => _Fill(_handle);
void fillRect(num x, num y, num w, num h) =>
_FillRect(_handle, x.toDouble(), y.toDouble(),
w.toDouble(), h.toDouble());
void fillText(String text, num x, num y, [num maxWidth = -1]) =>
_FillText(_handle, text, x.toDouble(), y.toDouble(),
maxWidth.toDouble());
ImageData getImageData(num sx, num sy, num sw, num sh) =>
_GetImageData(sx, sy, sw, sh);
List<double> _lineDash = null;
List<num> getLineDash() {
if (_lineDash == null) return [];
return _lineDash; // TODO(gram): should we return a copy?
}
bool isPointInPath(num x, num y) {
throw new Exception('Unimplemented isPointInPath');
}
void lineTo(num x, num y) {
_LineTo(_handle, x.toDouble(), y.toDouble());
}
TextMetrics measureText(String text) {
double w = _MeasureText(_handle, text);
return new TextMetrics(w);
}
void moveTo(num x, num y) =>
_MoveTo(_handle, x.toDouble(), y.toDouble());
void putImageData(ImageData imagedata, num dx, num dy,
[num dirtyX, num dirtyY, num dirtyWidth, num dirtyHeight]) {
if (dirtyX != null || dirtyY != null) {
throw new Exception('Unimplemented putImageData');
} else {
_PutImageData(_handle, imagedata, dx, dy);
}
}
void quadraticCurveTo(num cpx, num cpy, num x, num y) =>
_QuadraticCurveTo(_handle, cpx.toDouble(), cpy.toDouble(),
x.toDouble(), y.toDouble());
void rect(num x, num y, num w, num h) =>
_Rect(_handle, x.toDouble(), y.toDouble(), w.toDouble(), h.toDouble());
void restore() => _Restore(_handle);
void rotate(num angle) => _Rotate(_handle, angle.toDouble());
void save() => _Save(_handle);
void scale(num x, num y) => _Scale(_handle, x.toDouble(), y.toDouble());
void setFillColorHsl(int h, num s, num l, [num a = 1]) {
throw new Exception('Unimplemented setFillColorHsl');
}
void setFillColorRgb(int r, int g, int b, [num a = 1]) {
throw new Exception('Unimplemented setFillColorRgb');
}
void setLineDash(List<num> dash) {
var valid = true;
var new_dash;
if (dash.length % 2 == 1) {
new_dash = new List<double>(2 * dash.length);
for (int i = 0; i < dash.length; i++) {
double v = dash[i].toDouble();
if (v < 0) {
valid = false;
break;
}
new_dash[i] = new_dash[i + dash.length] = v;
}
} else {
new_dash = new List<double>(dash.length);
for (int i = 0; i < dash.length; i++) {
double v = dash[i].toDouble();
if (v < 0) {
valid = false;
break;
}
new_dash[i] = v;
}
}
if (valid) {
_SetLineDash(_handle, _lineDash = new_dash);
}
}
void setStrokeColorHsl(int h, num s, num l, [num a = 1]) {
throw new Exception('Unimplemented setStrokeColorHsl');
}
void setStrokeColorRgb(int r, int g, int b, [num a = 1]) {
throw new Exception('Unimplemented setStrokeColorRgb');
}
void setTransform(num m11, num m12, num m21, num m22, num dx, num dy) =>
_SetTransform(_handle, m11.toDouble(), m12.toDouble(),
m21.toDouble(), m22.toDouble(),
dx.toDouble(), dy.toDouble());
void stroke() => _Stroke(_handle);
void strokeRect(num x, num y, num w, num h, [num lineWidth]) =>
_StrokeRect(_handle, x.toDouble(), y.toDouble(),
w.toDouble(), h.toDouble());
void strokeText(String text, num x, num y, [num maxWidth = -1]) =>
_StrokeText(_handle, text, x.toDouble(), y.toDouble(),
maxWidth.toDouble());
void transform(num m11, num m12, num m21, num m22, num dx, num dy) =>
_Transform(_handle, m11.toDouble(), m12.toDouble(),
m21.toDouble(), m22.toDouble(),
dx.toDouble(), dy.toDouble());
void translate(num x, num y) =>
_Translate(_handle, x.toDouble(), y.toDouble());
ImageData webkitGetImageDataHD(num sx, num sy, num sw, num sh) {
throw new Exception('Unimplemented webkitGetImageDataHD');
}
void webkitPutImageDataHD(ImageData imagedata, num dx, num dy,
[num dirtyX, num dirtyY,
num dirtyWidth, num dirtyHeight]) {
throw new Exception('Unimplemented webkitGetImageDataHD');
}
// TODO(vsm): Kill.
noSuchMethod(invocation) {
throw new Exception('Unimplemented/unknown ${invocation.memberName}');
}
}
var sfx_extension = 'raw';
int _loadSample(String s) native "LoadSample";
int _playSample(String s) native "PlaySample";