From 84463cf0bd53dc88db24faff0b65cba9753d3707 Mon Sep 17 00:00:00 2001 From: Jason Teplitz Date: Fri, 31 Jul 2015 10:33:22 -0700 Subject: [PATCH] Feat(WebWorker): Add WebWorker Image Filter Demo --- .../src/web-workers/ui/application.dart | 5 +- .../src/web-workers/ui/application.ts | 3 +- .../src/web-workers/ui/event_serializer.dart | 11 +- .../src/web-workers/ui/event_serializer.ts | 17 +- modules/angular2/src/web-workers/ui/impl.ts | 6 +- .../src/web-workers/worker/application.ts | 5 +- .../web-workers/worker/application_common.ts | 8 +- .../examples/src/web_workers/images/b64.d.ts | 4 + .../web_workers/images/background_index.dart | 13 ++ .../web_workers/images/background_index.ts | 6 + .../src/web_workers/images/bitmap.d.ts | 8 + .../examples/src/web_workers/images/bitmap.js | 127 ++++++++++++ .../src/web_workers/images/file_api.dart | 9 + .../src/web_workers/images/file_api.ts | 6 + .../src/web_workers/images/image_demo.css | 30 +++ .../src/web_workers/images/image_demo.html | 33 +++ .../src/web_workers/images/index.dart | 10 + .../src/web_workers/images/index.html | 18 ++ .../examples/src/web_workers/images/index.ts | 2 + .../src/web_workers/images/index_common.ts | 50 +++++ .../src/web_workers/images/loader.css | 71 +++++++ .../examples/src/web_workers/images/loader.js | 25 +++ .../web_workers/images/services/bitmap.dart | 23 +++ .../src/web_workers/images/services/bitmap.ts | 188 ++++++++++++++++++ .../src/web_workers/images/single_thread.html | 19 ++ .../src/web_workers/images/single_thread.ts | 6 + package.json | 1 + tools/broccoli/broccoli-typescript.ts | 7 +- tools/broccoli/trees/browser_tree.ts | 4 +- 29 files changed, 694 insertions(+), 21 deletions(-) create mode 100644 modules/examples/src/web_workers/images/b64.d.ts create mode 100644 modules/examples/src/web_workers/images/background_index.dart create mode 100644 modules/examples/src/web_workers/images/background_index.ts create mode 100644 modules/examples/src/web_workers/images/bitmap.d.ts create mode 100644 modules/examples/src/web_workers/images/bitmap.js create mode 100644 modules/examples/src/web_workers/images/file_api.dart create mode 100644 modules/examples/src/web_workers/images/file_api.ts create mode 100644 modules/examples/src/web_workers/images/image_demo.css create mode 100644 modules/examples/src/web_workers/images/image_demo.html create mode 100644 modules/examples/src/web_workers/images/index.dart create mode 100644 modules/examples/src/web_workers/images/index.html create mode 100644 modules/examples/src/web_workers/images/index.ts create mode 100644 modules/examples/src/web_workers/images/index_common.ts create mode 100644 modules/examples/src/web_workers/images/loader.css create mode 100644 modules/examples/src/web_workers/images/loader.js create mode 100644 modules/examples/src/web_workers/images/services/bitmap.dart create mode 100644 modules/examples/src/web_workers/images/services/bitmap.ts create mode 100644 modules/examples/src/web_workers/images/single_thread.html create mode 100644 modules/examples/src/web_workers/images/single_thread.ts diff --git a/modules/angular2/src/web-workers/ui/application.dart b/modules/angular2/src/web-workers/ui/application.dart index f40da98726..dacb314f5a 100644 --- a/modules/angular2/src/web-workers/ui/application.dart +++ b/modules/angular2/src/web-workers/ui/application.dart @@ -13,9 +13,10 @@ import 'package:angular2/src/web-workers/ui/impl.dart' show bootstrapUICommon; * You instantiate a WebWorker application by calling bootstrap with the URI of your worker's index script * Note: The WebWorker script must call bootstrapWebworker once it is set up to complete the bootstrapping process */ -void bootstrap(String uri) { - spawnWorker(Uri.parse(uri)).then((bus) { +Future bootstrap(String uri) { + return spawnWorker(Uri.parse(uri)).then((bus) { bootstrapUICommon(bus); + return bus; }); } diff --git a/modules/angular2/src/web-workers/ui/application.ts b/modules/angular2/src/web-workers/ui/application.ts index 6d7cced8ae..8f230d038c 100644 --- a/modules/angular2/src/web-workers/ui/application.ts +++ b/modules/angular2/src/web-workers/ui/application.ts @@ -15,9 +15,10 @@ import {bootstrapUICommon} from "angular2/src/web-workers/ui/impl"; * Note: The WebWorker script must call bootstrapWebworker once it is set up to complete the * bootstrapping process */ -export function bootstrap(uri: string): void { +export function bootstrap(uri: string): MessageBus { var messageBus = spawnWorker(uri); bootstrapUICommon(messageBus); + return messageBus; } export function spawnWorker(uri: string): MessageBus { diff --git a/modules/angular2/src/web-workers/ui/event_serializer.dart b/modules/angular2/src/web-workers/ui/event_serializer.dart index ebe2a13122..78d9c5783f 100644 --- a/modules/angular2/src/web-workers/ui/event_serializer.dart +++ b/modules/angular2/src/web-workers/ui/event_serializer.dart @@ -80,9 +80,9 @@ Map serializeGenericEvent(dynamic e) { // TODO(jteplitz602): Allow users to specify the properties they need rather than always // adding value #3374 -Map serializeEventWithValue(dynamic e) { +Map serializeEventWithTarget(dynamic e) { var serializedEvent = serializeEvent(e, EVENT_PROPERTIES); - return addValue(e, serializedEvent); + return addTarget(e, serializedEvent); } Map serializeMouseEvent(dynamic e) { @@ -91,13 +91,16 @@ Map serializeMouseEvent(dynamic e) { Map serializeKeyboardEvent(dynamic e) { var serializedEvent = serializeEvent(e, KEYBOARD_EVENT_PROPERTIES); - return addValue(e, serializedEvent); + return addTarget(e, serializedEvent); } // TODO(jteplitz602): #3374. See above. -Map addValue(dynamic e, Map serializedEvent) { +Map addTarget(dynamic e, Map serializedEvent) { if (NODES_WITH_VALUE.contains(e.target.tagName.toLowerCase())) { serializedEvent['target'] = {'value': e.target.value}; + if (e.target is InputElement) { + serializedEvent['target']['files'] = e.target.files; + } } return serializedEvent; } diff --git a/modules/angular2/src/web-workers/ui/event_serializer.ts b/modules/angular2/src/web-workers/ui/event_serializer.ts index 5ee913e901..8be40065c3 100644 --- a/modules/angular2/src/web-workers/ui/event_serializer.ts +++ b/modules/angular2/src/web-workers/ui/event_serializer.ts @@ -1,4 +1,5 @@ import {StringMap, Set} from 'angular2/src/facade/collection'; +import {isPresent} from 'angular2/src/facade/lang'; const MOUSE_EVENT_PROPERTIES = [ "altKey", @@ -41,10 +42,10 @@ export function serializeGenericEvent(e: Event): StringMap { } // TODO(jteplitz602): Allow users to specify the properties they need rather than always -// adding value #3374 -export function serializeEventWithValue(e: Event): StringMap { +// adding value and files #3374 +export function serializeEventWithTarget(e: Event): StringMap { var serializedEvent = serializeEvent(e, EVENT_PROPERTIES); - return addValue(e, serializedEvent); + return addTarget(e, serializedEvent); } export function serializeMouseEvent(e: MouseEvent): StringMap { @@ -53,13 +54,17 @@ export function serializeMouseEvent(e: MouseEvent): StringMap { export function serializeKeyboardEvent(e: KeyboardEvent): StringMap { var serializedEvent = serializeEvent(e, KEYBOARD_EVENT_PROPERTIES); - return addValue(e, serializedEvent); + return addTarget(e, serializedEvent); } // TODO(jteplitz602): #3374. See above. -function addValue(e: Event, serializedEvent: StringMap): StringMap { +function addTarget(e: Event, serializedEvent: StringMap): StringMap { if (NODES_WITH_VALUE.has((e.target).tagName.toLowerCase())) { - serializedEvent['target'] = {'value': (e.target).value}; + var target = e.target; + serializedEvent['target'] = {'value': target.value}; + if (isPresent(target.files)) { + serializedEvent['target']['files'] = target.files; + } } return serializedEvent; } diff --git a/modules/angular2/src/web-workers/ui/impl.ts b/modules/angular2/src/web-workers/ui/impl.ts index 835e17a329..3e0b10f006 100644 --- a/modules/angular2/src/web-workers/ui/impl.ts +++ b/modules/angular2/src/web-workers/ui/impl.ts @@ -35,8 +35,9 @@ import { serializeMouseEvent, serializeKeyboardEvent, serializeGenericEvent, - serializeEventWithValue + serializeEventWithTarget } from 'angular2/src/web-workers/ui/event_serializer'; +import {wtfInit} from 'angular2/src/profile/wtf_init'; /** * Creates a zone, sets up the DI bindings @@ -45,6 +46,7 @@ import { export function bootstrapUICommon(bus: MessageBus) { BrowserDomAdapter.makeCurrent(); var zone = createNgZone(); + wtfInit(); zone.run(() => { var injector = createInjector(zone); var webWorkerMain = injector.get(WebWorkerMain); @@ -259,7 +261,7 @@ class EventDispatcher implements RenderEventDispatcher { case "input": case "change": case "blur": - serializedEvent = serializeEventWithValue(e); + serializedEvent = serializeEventWithTarget(e); break; case "abort": case "afterprint": diff --git a/modules/angular2/src/web-workers/worker/application.ts b/modules/angular2/src/web-workers/worker/application.ts index bd8c20e0de..c017191192 100644 --- a/modules/angular2/src/web-workers/worker/application.ts +++ b/modules/angular2/src/web-workers/worker/application.ts @@ -11,6 +11,9 @@ import {bootstrapWebworkerCommon} from "angular2/src/web-workers/worker/applicat import {ApplicationRef} from "angular2/src/core/application"; import {Injectable} from "angular2/di"; +// TODO(jteplitz602) remove this and compile with lib.webworker.d.ts (#3492) +var _postMessage: (message: any, transferrables?:[ArrayBuffer]) => void = postMessage; + /** * Bootstrapping a Webworker Application * @@ -41,7 +44,7 @@ export class WorkerMessageBus implements MessageBus { } export class WorkerMessageBusSink implements MessageBusSink { - public send(message: Object) { postMessage(message, null); } + public send(message: Object) { _postMessage(message); } } export class WorkerMessageBusSource implements MessageBusSource { diff --git a/modules/angular2/src/web-workers/worker/application_common.ts b/modules/angular2/src/web-workers/worker/application_common.ts index 59a1c5fbbe..6233f8e4fb 100644 --- a/modules/angular2/src/web-workers/worker/application_common.ts +++ b/modules/angular2/src/web-workers/worker/application_common.ts @@ -17,9 +17,13 @@ import { ChangeDetection, DynamicChangeDetection, JitChangeDetection, + PreGeneratedChangeDetection, Pipes, defaultPipes, - PreGeneratedChangeDetection + IterableDiffers, + defaultIterableDiffers, + KeyValueDiffers, + defaultKeyValueDiffers } from 'angular2/src/change_detection/change_detection'; import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver'; import {ExceptionHandler} from 'angular2/src/core/exception_handler'; @@ -119,6 +123,8 @@ function _injectorBindings(appComponentType, bus: WorkerMessageBus, CompilerCache, ViewResolver, defaultPipes, + bind(IterableDiffers).toValue(defaultIterableDiffers), + bind(KeyValueDiffers).toValue(defaultKeyValueDiffers), bind(ChangeDetection).toClass(bestChangeDetection), DirectiveResolver, Parser, diff --git a/modules/examples/src/web_workers/images/b64.d.ts b/modules/examples/src/web_workers/images/b64.d.ts new file mode 100644 index 0000000000..d34048ed49 --- /dev/null +++ b/modules/examples/src/web_workers/images/b64.d.ts @@ -0,0 +1,4 @@ +declare module "B64" { + export function fromByteArray(arr: Uint8Array): string; + export function toByteArray(str: string): Uint8Array; +} \ No newline at end of file diff --git a/modules/examples/src/web_workers/images/background_index.dart b/modules/examples/src/web_workers/images/background_index.dart new file mode 100644 index 0000000000..0060bd5e96 --- /dev/null +++ b/modules/examples/src/web_workers/images/background_index.dart @@ -0,0 +1,13 @@ +library examples.src.web_workers.images.background_index; + +import "index_common.dart" show ImageDemo; +import "dart:isolate"; +import "package:angular2/src/web-workers/worker/application.dart" + show bootstrapWebworker; +import "package:angular2/src/reflection/reflection_capabilities.dart"; +import "package:angular2/src/reflection/reflection.dart"; + +main(List args, SendPort replyTo) { + reflector.reflectionCapabilities = new ReflectionCapabilities(); + bootstrapWebworker(replyTo, ImageDemo).catchError((error) => throw error); +} diff --git a/modules/examples/src/web_workers/images/background_index.ts b/modules/examples/src/web_workers/images/background_index.ts new file mode 100644 index 0000000000..e7dff8031c --- /dev/null +++ b/modules/examples/src/web_workers/images/background_index.ts @@ -0,0 +1,6 @@ +import {bootstrapWebworker} from "angular2/src/web-workers/worker/application"; +import {ImageDemo} from "./index_common"; + +export function main() { + bootstrapWebworker(ImageDemo); +} diff --git a/modules/examples/src/web_workers/images/bitmap.d.ts b/modules/examples/src/web_workers/images/bitmap.d.ts new file mode 100644 index 0000000000..b8820d20a5 --- /dev/null +++ b/modules/examples/src/web_workers/images/bitmap.d.ts @@ -0,0 +1,8 @@ +declare class Bitmap { + constructor(width: number, height: number); + + subsample(n: number): void; + dataURL(): string; + + pixel:[any]; +} diff --git a/modules/examples/src/web_workers/images/bitmap.js b/modules/examples/src/web_workers/images/bitmap.js new file mode 100644 index 0000000000..759f979571 --- /dev/null +++ b/modules/examples/src/web_workers/images/bitmap.js @@ -0,0 +1,127 @@ +/* + Copyright 2011 Andrey Zholos + + 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. +*/ + +function Bitmap(width, height) { + this.width = width; + this.height = height; + this.pixel = new Array(width); + for (var x = 0; x < width; x++) { + this.pixel[x] = new Array(height); + for (var y = 0; y < height; y++) this.pixel[x][y] = [0, 0, 0, 0]; + } +} + +Bitmap.prototype.subsample = + function(n) { + var width = ~~(this.width / n); + var height = ~~(this.height / n); + var pixel = new Array(width); + for (var x = 0; x < width; x++) { + pixel[x] = new Array(height); + for (var y = 0; y < height; y++) { + var q = [0, 0, 0, 0]; + for (var i = 0; i < n; i++) + for (var j = 0; j < n; j++) { + var r = this.pixel[x * n + i][y * n + j]; + q[0] += r[3] * r[0]; + q[1] += r[3] * r[1]; + q[2] += r[3] * r[2]; + q[3] += r[3]; + } + if (q[3]) { + q[0] /= q[3]; + q[1] /= q[3]; + q[2] /= q[3]; + q[3] /= n * n; + } + pixel[x][y] = q; + } + } + this.width = width; + this.height = height; + this.pixel = pixel; +} + + Bitmap.prototype.dataURL = function() { + function sample(v) { return ~~(Math.max(0, Math.min(1, v)) * 255); } + + function gamma(v) { return sample(Math.pow(v, .45455)); } + + function row(pixel, width, y) { + var data = "\0"; + for (var x = 0; x < width; x++) { + var r = pixel[x][y]; + data += String.fromCharCode(gamma(r[0]), gamma(r[1]), gamma(r[2]), sample(r[3])); + } + return data; + } + + function rows(pixel, width, height) { + var data = ""; + for (var y = 0; y < height; y++) data += row(pixel, width, y); + return data; + } + + function adler(data) { + var s1 = 1, s2 = 0; + for (var i = 0; i < data.length; i++) { + s1 = (s1 + data.charCodeAt(i)) % 65521; + s2 = (s2 + s1) % 65521; + } + return s2 << 16 | s1; + } + + function hton(i) { return String.fromCharCode(i >>> 24, i >>> 16 & 255, i >>> 8 & 255, i & 255); } + + function deflate(data) { + var len = data.length; + return "\170\1\1" + String.fromCharCode(len & 255, len >>> 8, ~len & 255, (~len >>> 8) & 255) + + data + hton(adler(data)); + } + + function crc32(data) { + var c = ~0; + for (var i = 0; i < data.length; i++) + for (var b = data.charCodeAt(i) | 0x100; b != 1; b >>>= 1) + c = (c >>> 1) ^ ((c ^ b) & 1 ? 0xedb88320 : 0); + return ~c; + } + + function chunk(type, data) { return hton(data.length) + type + data + hton(crc32(type + data)); } + + function base64(data) { + enc = ""; + for (var i = 5, n = data.length * 8 + 5; i < n; i += 6) + enc += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + [(data.charCodeAt(~~(i / 8) - 1) << 8 | data.charCodeAt(~~(i / 8))) >> 7 - i % 8 & 63]; + for (; enc.length % 4; enc += "=") + ; + return enc; + } + + var png = "\211PNG\r\n\32\n" + + chunk("IHDR", hton(this.width) + hton(this.height) + "\10\6\0\0\0") + + chunk("IDAT", deflate(rows(this.pixel, this.width, this.height))) + chunk("IEND", ""); + + return "data:image/png;base64," + base64(png); +} diff --git a/modules/examples/src/web_workers/images/file_api.dart b/modules/examples/src/web_workers/images/file_api.dart new file mode 100644 index 0000000000..ad0b5da1bc --- /dev/null +++ b/modules/examples/src/web_workers/images/file_api.dart @@ -0,0 +1,9 @@ +export 'dart:html' show FileReader; + +import 'dart:typed_data'; + +class Uint8ArrayWrapper { + static Uint8ClampedList create(ByteBuffer buffer) { + return new Uint8ClampedList.view(buffer); + } +} diff --git a/modules/examples/src/web_workers/images/file_api.ts b/modules/examples/src/web_workers/images/file_api.ts new file mode 100644 index 0000000000..2df2bf51f5 --- /dev/null +++ b/modules/examples/src/web_workers/images/file_api.ts @@ -0,0 +1,6 @@ +var _FileReader = FileReader; +export {_FileReader as FileReader}; + +export class Uint8ArrayWrapper { + static create(buffer: ArrayBuffer) { return new Uint8Array(buffer); } +} diff --git a/modules/examples/src/web_workers/images/image_demo.css b/modules/examples/src/web_workers/images/image_demo.css new file mode 100644 index 0000000000..bf19e97801 --- /dev/null +++ b/modules/examples/src/web_workers/images/image_demo.css @@ -0,0 +1,30 @@ +.hidden{ + display: none !important; +} +#images { + width: 600px; + margin: 0 auto; +} +ul li { + list-style-type: none; + float: left; + margin-left: 20px; + width: 200px; +} +#main ul { + width: 700px; +} +.card-image .image-loader{ + position: absolute; + bottom: 0; + margin: auto; +} + +.card-image img.grey { + opacity: 0.4; +} + +.fixed-action-btn.bottom { + bottom: 45px; + right: 24px; +} diff --git a/modules/examples/src/web_workers/images/image_demo.html b/modules/examples/src/web_workers/images/image_demo.html new file mode 100644 index 0000000000..a6ce9621fd --- /dev/null +++ b/modules/examples/src/web_workers/images/image_demo.html @@ -0,0 +1,33 @@ + +
+
+
+
+
+ +
+
+
+
+
+
+
+ + +
+
+ Select Images + +
+
+
diff --git a/modules/examples/src/web_workers/images/index.dart b/modules/examples/src/web_workers/images/index.dart new file mode 100644 index 0000000000..930edea0b8 --- /dev/null +++ b/modules/examples/src/web_workers/images/index.dart @@ -0,0 +1,10 @@ +library angular2.examples.web_workers.images.index; + +import "package:angular2/src/web-workers/ui/application.dart" show bootstrap; +import "package:angular2/src/reflection/reflection_capabilities.dart"; +import "package:angular2/src/reflection/reflection.dart"; + +main() { + reflector.reflectionCapabilities = new ReflectionCapabilities(); + bootstrap("background_index.dart"); +} diff --git a/modules/examples/src/web_workers/images/index.html b/modules/examples/src/web_workers/images/index.html new file mode 100644 index 0000000000..29f3fdb535 --- /dev/null +++ b/modules/examples/src/web_workers/images/index.html @@ -0,0 +1,18 @@ + + + + + + + + + + $SCRIPTS$ + + + + + + + + diff --git a/modules/examples/src/web_workers/images/index.ts b/modules/examples/src/web_workers/images/index.ts new file mode 100644 index 0000000000..170f78bba0 --- /dev/null +++ b/modules/examples/src/web_workers/images/index.ts @@ -0,0 +1,2 @@ +import {bootstrap} from "angular2/src/web-workers/ui/application"; +bootstrap("loader.js"); diff --git a/modules/examples/src/web_workers/images/index_common.ts b/modules/examples/src/web_workers/images/index_common.ts new file mode 100644 index 0000000000..8f47ae0e14 --- /dev/null +++ b/modules/examples/src/web_workers/images/index_common.ts @@ -0,0 +1,50 @@ +import {NgZone, NgFor, Component, View, NgIf, formDirectives} from 'angular2/angular2'; +import {BitmapService} from './services/bitmap'; +import {EventListener} from 'angular2/src/facade/browser'; +import {FileReader, Uint8ArrayWrapper} from './file_api'; +import {TimerWrapper} from 'angular2/src/facade/async'; + +@Component({selector: 'image-demo', viewBindings: [BitmapService]}) +@View({templateUrl: 'image_demo.html', directives: [NgFor, NgIf, formDirectives]}) +export class ImageDemo { + images = []; + fileInput: String; + + constructor(private _bitmapService: BitmapService) {} + + uploadFiles(files) { + for (var i = 0; i < files.length; i++) { + var reader = new FileReader(); + reader.addEventListener("load", this.handleReaderLoad(reader)); + reader.readAsArrayBuffer(files[i]); + } + } + + handleReaderLoad(reader: FileReader): EventListener { + return (e) => { + var buffer = reader.result; + this.images.push({ + src: this._bitmapService.arrayBufferToDataUri(Uint8ArrayWrapper.create(reader.result)), + buffer: buffer, + filtering: false + }); + }; + } + + applyFilters() { + for (var i = 0; i < this.images.length; i++) { + this.images[i].filtering = true; + + TimerWrapper.setTimeout(this._filter(i), 0); + } + } + + private _filter(i: number): Function { + return () => { + var imageData = this._bitmapService.convertToImageData(this.images[i].buffer); + imageData = this._bitmapService.applySepia(imageData); + this.images[i].src = this._bitmapService.toDataUri(imageData); + this.images[i].filtering = false; + }; + } +} diff --git a/modules/examples/src/web_workers/images/loader.css b/modules/examples/src/web_workers/images/loader.css new file mode 100644 index 0000000000..30e38fef8b --- /dev/null +++ b/modules/examples/src/web_workers/images/loader.css @@ -0,0 +1,71 @@ +body { + background: #eaecfa; +} + +.loader { + width: 250px; + height: 50px; + line-height: 50px; + text-align: center; + position: absolute; + top: 50%; + left: 50%; + -webkit-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + font-family: helvetica, arial, sans-serif; + text-transform: uppercase; + font-weight: 900; + color: #ce4233; + letter-spacing: 0.2em; +} +.loader::before, .loader::after { + content: ""; + display: block; + width: 15px; + height: 15px; + background: #ce4233; + position: absolute; + -webkit-animation: load .7s infinite alternate ease-in-out; + animation: load .7s infinite alternate ease-in-out; +} +.loader::before { + top: 0; +} +.loader::after { + bottom: 0; +} + +@-webkit-keyframes load { + 0% { + left: 0; + height: 30px; + width: 15px; + } + 50% { + height: 8px; + width: 40px; + } + 100% { + left: 235px; + height: 30px; + width: 15px; + } +} + +@keyframes load { + 0% { + left: 0; + height: 30px; + width: 15px; + } + 50% { + height: 8px; + width: 40px; + } + 100% { + left: 235px; + height: 30px; + width: 15px; + } +} diff --git a/modules/examples/src/web_workers/images/loader.js b/modules/examples/src/web_workers/images/loader.js new file mode 100644 index 0000000000..444cec555a --- /dev/null +++ b/modules/examples/src/web_workers/images/loader.js @@ -0,0 +1,25 @@ +$SCRIPTS$ window = { + setTimeout: setTimeout, + Map: Map, + Set: Set, + Array: Array, + Reflect: Reflect, + RegExp: RegExp, + Promise: Promise, + Date: Date, + zone: zone +}; +assert = function() {}; +importScripts("b64.js"); + +System.import("examples/src/web_workers/images/background_index") + .then( + function(m) { + console.log("running main"); + try { + m.main(); + } catch (e) { + console.error(e); + } + }, + function(error) { console.error("error loading background", error); }); diff --git a/modules/examples/src/web_workers/images/services/bitmap.dart b/modules/examples/src/web_workers/images/services/bitmap.dart new file mode 100644 index 0000000000..e569bb362c --- /dev/null +++ b/modules/examples/src/web_workers/images/services/bitmap.dart @@ -0,0 +1,23 @@ +library angular2.examples.web_workers.images.bitmap_service; + +import 'dart:html'; +import 'dart:typed_data'; + +// TODO(jteplitz602) Implement this class #3493 +class BitmapService { + ImageData applySepia (ImageData imageData) { + return null; + } + + String arrayBufferToDataUri (Uint8ClampedList data) { + return null; + } + + ImageData convertToImageData (ByteBuffer buffer) { + return null; + } + + String toDataUri (ImageData imageData) { + return null; + } +} diff --git a/modules/examples/src/web_workers/images/services/bitmap.ts b/modules/examples/src/web_workers/images/services/bitmap.ts new file mode 100644 index 0000000000..f0bbecf492 --- /dev/null +++ b/modules/examples/src/web_workers/images/services/bitmap.ts @@ -0,0 +1,188 @@ +/// /// +import {Injectable} from 'angular2/angular2'; +declare var base64js; + +// Temporary fix for Typescript issue #4220 (https://github.com/Microsoft/TypeScript/issues/4220) +// var _ImageData: (width: number, height: number) => void = postMessage; +var _ImageData: { + prototype: ImageData, new (width: number, height: number): ImageData; +} += ImageData; + +// This class is based on the Bitmap examples at: +// http://www.i-programmer.info/projects/36-web/6234-reading-a-bmp-file-in-javascript.html +// and +// http://www.worldwidewhat.net/2012/07/how-to-draw-bitmaps-using-javascript/ +@Injectable() +export class BitmapService { + convertToImageData(buffer: ArrayBuffer): ImageData { + var bmp = this._getBMP(buffer); + return this._BMPToImageData(bmp); + } + + applySepia(imageData: ImageData): ImageData { + var buffer = imageData.data; + for (var i = 0; i < buffer.length; i += 4) { + var r = buffer[i]; + var g = buffer[i + 1]; + var b = buffer[i + 2]; + buffer[i] = (r * .393) + (g * .769) + (b * .189); + buffer[i + 1] = (r * .349) + (g * .686) + (b * .168); + buffer[i + 2] = (r * .272) + (g * .534) + (b * .131); + } + return imageData; + } + + toDataUri(imageData: ImageData): string { + var header = this._createBMPHeader(imageData); + imageData = this._imageDataToBMP(imageData); + return 'data:image/bmp;base64,' + btoa(header) + base64js.fromByteArray(imageData.data); + } + + // converts a .bmp file ArrayBuffer to a dataURI + arrayBufferToDataUri(data: Uint8Array): string { + return 'data:image/bmp;base64,' + base64js.fromByteArray(data); + } + + // returns a UInt8Array in BMP order (starting from the bottom) + private _imageDataToBMP(imageData: ImageData): ImageData { + var width = imageData.width; + var height = imageData.height; + + var data = imageData.data; + for (var y = 0; y < height / 2; ++y) { + var topIndex = y * width * 4; + var bottomIndex = (height - y) * width * 4; + for (var i = 0; i < width * 4; i++) { + this._swap(data, topIndex, bottomIndex); + topIndex++; + bottomIndex++; + } + } + + return imageData; + } + + private _swap(data: List, index1: number, index2: number) { + var temp = data[index1]; + data[index1] = data[index2]; + data[index2] = temp; + } + + // Based on example from + // http://www.worldwidewhat.net/2012/07/how-to-draw-bitmaps-using-javascript/ + private _createBMPHeader(imageData: ImageData): string { + var numFileBytes = this._getLittleEndianHex(imageData.width * imageData.height); + var w = this._getLittleEndianHex(imageData.width); + var h = this._getLittleEndianHex(imageData.height); + return 'BM' + // Signature + numFileBytes + // size of the file (bytes)* + '\x00\x00' + // reserved + '\x00\x00' + // reserved + '\x36\x00\x00\x00' + // offset of where BMP data lives (54 bytes) + '\x28\x00\x00\x00' + // number of remaining bytes in header from here (40 bytes) + w + // the width of the bitmap in pixels* + h + // the height of the bitmap in pixels* + '\x01\x00' + // the number of color planes (1) + '\x20\x00' + // 32 bits / pixel + '\x00\x00\x00\x00' + // No compression (0) + '\x00\x00\x00\x00' + // size of the BMP data (bytes)* + '\x13\x0B\x00\x00' + // 2835 pixels/meter - horizontal resolution + '\x13\x0B\x00\x00' + // 2835 pixels/meter - the vertical resolution + '\x00\x00\x00\x00' + // Number of colors in the palette (keep 0 for 32-bit) + '\x00\x00\x00\x00'; // 0 important colors (means all colors are important) + } + + private _BMPToImageData(bmp: BitmapFile): ImageData { + var width = bmp.infoHeader.biWidth; + var height = bmp.infoHeader.biHeight; + var imageData = new _ImageData(width, height); + + var data = imageData.data; + var bmpData = bmp.pixels; + var stride = bmp.stride; + + for (var y = 0; y < height; ++y) { + for (var x = 0; x < width; ++x) { + var index1 = (x + width * (height - y)) * 4; + var index2 = x * 3 + stride * y; + data[index1] = bmpData[index2 + 2]; + data[index1 + 1] = bmpData[index2 + 1]; + data[index1 + 2] = bmpData[index2]; + data[index1 + 3] = 255; + } + } + return imageData; + } + + private _getBMP(buffer: ArrayBuffer): BitmapFile { + var datav = new DataView(buffer); + var bitmap: BitmapFile = { + fileHeader: { + bfType: datav.getUint16(0, true), + bfSize: datav.getUint32(2, true), + bfReserved1: datav.getUint16(6, true), + bfReserved2: datav.getUint16(8, true), + bfOffBits: datav.getUint32(10, true), + }, + infoHeader: { + biSize: datav.getUint32(14, true), + biWidth: datav.getUint32(18, true), + biHeight: datav.getUint32(22, true), + biPlanes: datav.getUint16(26, true), + biBitCount: datav.getUint16(28, true), + biCompression: datav.getUint32(30, true), + biSizeImage: datav.getUint32(34, true), + biXPelsPerMeter: datav.getUint32(38, true), + biYPelsPerMeter: datav.getUint32(42, true), + biClrUsed: datav.getUint32(46, true), + biClrImportant: datav.getUint32(50, true) + }, + stride: null, + pixels: null + }; + var start = bitmap.fileHeader.bfOffBits; + bitmap.stride = + Math.floor((bitmap.infoHeader.biBitCount * bitmap.infoHeader.biWidth + 31) / 32) * 4; + bitmap.pixels = new Uint8Array(datav.buffer, start); + return bitmap; + } + + // Based on example from + // http://www.worldwidewhat.net/2012/07/how-to-draw-bitmaps-using-javascript/ + private _getLittleEndianHex(value: number): string { + var result = []; + + for (var bytes = 4; bytes > 0; bytes--) { + result.push(String.fromCharCode(value & 255)); + value >>= 8; + } + + return result.join(''); + } +} + +interface BitmapFile { + fileHeader: { + bfType: number; + bfSize: number; + bfReserved1: number; + bfReserved2: number; + bfOffBits: number; + }; + infoHeader: { + biSize: number; + biWidth: number; + biHeight: number; + biPlanes: number; + biBitCount: number; + biCompression: number; + biSizeImage: number; + biXPelsPerMeter: number; + biYPelsPerMeter: number; + biClrUsed: number; + biClrImportant: number + }; + stride: number; + pixels: Uint8Array; +} diff --git a/modules/examples/src/web_workers/images/single_thread.html b/modules/examples/src/web_workers/images/single_thread.html new file mode 100644 index 0000000000..dc9f33dcb2 --- /dev/null +++ b/modules/examples/src/web_workers/images/single_thread.html @@ -0,0 +1,19 @@ + + + + + + + + + + + $SCRIPTS$ + + + + + + + + diff --git a/modules/examples/src/web_workers/images/single_thread.ts b/modules/examples/src/web_workers/images/single_thread.ts new file mode 100644 index 0000000000..9f2c66b14c --- /dev/null +++ b/modules/examples/src/web_workers/images/single_thread.ts @@ -0,0 +1,6 @@ +import {bootstrap} from "angular2/bootstrap"; +import {ImageDemo} from "./index_common"; + +export function main() { + bootstrap(ImageDemo); +} diff --git a/package.json b/package.json index 16a0aea6bf..7c35f60b5e 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ }, "devDependencies": { "angular": "1.3.5", + "base64-js": "^0.0.8", "bower": "^1.3.12", "broccoli": "^0.15.3", "broccoli-filter": "^0.1.12", diff --git a/tools/broccoli/broccoli-typescript.ts b/tools/broccoli/broccoli-typescript.ts index 9bceee563d..b4581b3029 100644 --- a/tools/broccoli/broccoli-typescript.ts +++ b/tools/broccoli/broccoli-typescript.ts @@ -217,9 +217,10 @@ class CustomLanguageServiceHost implements ts.LanguageServiceHost { * not worth the potential issues with stale cache records. */ getScriptSnapshot(tsFilePath: string): ts.IScriptSnapshot { - let absoluteTsFilePath = (tsFilePath == this.defaultLibFilePath) ? - tsFilePath : - path.join(this.treeInputPath, tsFilePath); + let absoluteTsFilePath = + (tsFilePath == this.defaultLibFilePath || path.isAbsolute(tsFilePath)) ? + tsFilePath : + path.join(this.treeInputPath, tsFilePath); if (!fs.existsSync(absoluteTsFilePath)) { // TypeScript seems to request lots of bogus paths during import path lookup and resolution, diff --git a/tools/broccoli/trees/browser_tree.ts b/tools/broccoli/trees/browser_tree.ts index 938f79d036..aee455c5ee 100644 --- a/tools/broccoli/trees/browser_tree.ts +++ b/tools/broccoli/trees/browser_tree.ts @@ -65,7 +65,8 @@ const kServedPaths = [ 'examples/src/material/switcher', 'examples/src/message_broker', 'examples/src/web_workers/kitchen_sink', - 'examples/src/web_workers/todo' + 'examples/src/web_workers/todo', + 'examples/src/web_workers/images' ]; @@ -129,6 +130,7 @@ module.exports = function makeBrowserTree(options, destinationPath) { 'node_modules/systemjs/lib/extension-register.js', 'node_modules/systemjs/lib/extension-cjs.js', 'node_modules/rx/dist/rx.js', + 'node_modules/base64-js/lib/b64.js', 'node_modules/reflect-metadata/Reflect.js', 'tools/build/snippets/runtime_paths.js', path.relative(projectRootDir, TRACEUR_RUNTIME_PATH)