Feat(WebWorker): Add WebWorker Image Filter Demo

This commit is contained in:
Jason Teplitz 2015-07-31 10:33:22 -07:00 committed by jteplitz
parent 2dcf714d2b
commit 84463cf0bd
29 changed files with 694 additions and 21 deletions

View File

@ -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 * 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 * Note: The WebWorker script must call bootstrapWebworker once it is set up to complete the bootstrapping process
*/ */
void bootstrap(String uri) { Future<MessageBus> bootstrap(String uri) {
spawnWorker(Uri.parse(uri)).then((bus) { return spawnWorker(Uri.parse(uri)).then((bus) {
bootstrapUICommon(bus); bootstrapUICommon(bus);
return bus;
}); });
} }

View File

@ -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 * Note: The WebWorker script must call bootstrapWebworker once it is set up to complete the
* bootstrapping process * bootstrapping process
*/ */
export function bootstrap(uri: string): void { export function bootstrap(uri: string): MessageBus {
var messageBus = spawnWorker(uri); var messageBus = spawnWorker(uri);
bootstrapUICommon(messageBus); bootstrapUICommon(messageBus);
return messageBus;
} }
export function spawnWorker(uri: string): MessageBus { export function spawnWorker(uri: string): MessageBus {

View File

@ -80,9 +80,9 @@ Map<String, dynamic> serializeGenericEvent(dynamic e) {
// TODO(jteplitz602): Allow users to specify the properties they need rather than always // TODO(jteplitz602): Allow users to specify the properties they need rather than always
// adding value #3374 // adding value #3374
Map<String, dynamic> serializeEventWithValue(dynamic e) { Map<String, dynamic> serializeEventWithTarget(dynamic e) {
var serializedEvent = serializeEvent(e, EVENT_PROPERTIES); var serializedEvent = serializeEvent(e, EVENT_PROPERTIES);
return addValue(e, serializedEvent); return addTarget(e, serializedEvent);
} }
Map<String, dynamic> serializeMouseEvent(dynamic e) { Map<String, dynamic> serializeMouseEvent(dynamic e) {
@ -91,13 +91,16 @@ Map<String, dynamic> serializeMouseEvent(dynamic e) {
Map<String, dynamic> serializeKeyboardEvent(dynamic e) { Map<String, dynamic> serializeKeyboardEvent(dynamic e) {
var serializedEvent = serializeEvent(e, KEYBOARD_EVENT_PROPERTIES); var serializedEvent = serializeEvent(e, KEYBOARD_EVENT_PROPERTIES);
return addValue(e, serializedEvent); return addTarget(e, serializedEvent);
} }
// TODO(jteplitz602): #3374. See above. // TODO(jteplitz602): #3374. See above.
Map<String, dynamic> addValue(dynamic e, Map<String, dynamic> serializedEvent) { Map<String, dynamic> addTarget(dynamic e, Map<String, dynamic> serializedEvent) {
if (NODES_WITH_VALUE.contains(e.target.tagName.toLowerCase())) { if (NODES_WITH_VALUE.contains(e.target.tagName.toLowerCase())) {
serializedEvent['target'] = {'value': e.target.value}; serializedEvent['target'] = {'value': e.target.value};
if (e.target is InputElement) {
serializedEvent['target']['files'] = e.target.files;
}
} }
return serializedEvent; return serializedEvent;
} }

View File

@ -1,4 +1,5 @@
import {StringMap, Set} from 'angular2/src/facade/collection'; import {StringMap, Set} from 'angular2/src/facade/collection';
import {isPresent} from 'angular2/src/facade/lang';
const MOUSE_EVENT_PROPERTIES = [ const MOUSE_EVENT_PROPERTIES = [
"altKey", "altKey",
@ -41,10 +42,10 @@ export function serializeGenericEvent(e: Event): StringMap<string, any> {
} }
// TODO(jteplitz602): Allow users to specify the properties they need rather than always // TODO(jteplitz602): Allow users to specify the properties they need rather than always
// adding value #3374 // adding value and files #3374
export function serializeEventWithValue(e: Event): StringMap<string, any> { export function serializeEventWithTarget(e: Event): StringMap<string, any> {
var serializedEvent = serializeEvent(e, EVENT_PROPERTIES); var serializedEvent = serializeEvent(e, EVENT_PROPERTIES);
return addValue(e, serializedEvent); return addTarget(e, serializedEvent);
} }
export function serializeMouseEvent(e: MouseEvent): StringMap<string, any> { export function serializeMouseEvent(e: MouseEvent): StringMap<string, any> {
@ -53,13 +54,17 @@ export function serializeMouseEvent(e: MouseEvent): StringMap<string, any> {
export function serializeKeyboardEvent(e: KeyboardEvent): StringMap<string, any> { export function serializeKeyboardEvent(e: KeyboardEvent): StringMap<string, any> {
var serializedEvent = serializeEvent(e, KEYBOARD_EVENT_PROPERTIES); var serializedEvent = serializeEvent(e, KEYBOARD_EVENT_PROPERTIES);
return addValue(e, serializedEvent); return addTarget(e, serializedEvent);
} }
// TODO(jteplitz602): #3374. See above. // TODO(jteplitz602): #3374. See above.
function addValue(e: Event, serializedEvent: StringMap<string, any>): StringMap<string, any> { function addTarget(e: Event, serializedEvent: StringMap<string, any>): StringMap<string, any> {
if (NODES_WITH_VALUE.has((<HTMLElement>e.target).tagName.toLowerCase())) { if (NODES_WITH_VALUE.has((<HTMLElement>e.target).tagName.toLowerCase())) {
serializedEvent['target'] = {'value': (<HTMLInputElement>e.target).value}; var target = <HTMLInputElement>e.target;
serializedEvent['target'] = {'value': target.value};
if (isPresent(target.files)) {
serializedEvent['target']['files'] = target.files;
}
} }
return serializedEvent; return serializedEvent;
} }

View File

@ -35,8 +35,9 @@ import {
serializeMouseEvent, serializeMouseEvent,
serializeKeyboardEvent, serializeKeyboardEvent,
serializeGenericEvent, serializeGenericEvent,
serializeEventWithValue serializeEventWithTarget
} from 'angular2/src/web-workers/ui/event_serializer'; } from 'angular2/src/web-workers/ui/event_serializer';
import {wtfInit} from 'angular2/src/profile/wtf_init';
/** /**
* Creates a zone, sets up the DI bindings * Creates a zone, sets up the DI bindings
@ -45,6 +46,7 @@ import {
export function bootstrapUICommon(bus: MessageBus) { export function bootstrapUICommon(bus: MessageBus) {
BrowserDomAdapter.makeCurrent(); BrowserDomAdapter.makeCurrent();
var zone = createNgZone(); var zone = createNgZone();
wtfInit();
zone.run(() => { zone.run(() => {
var injector = createInjector(zone); var injector = createInjector(zone);
var webWorkerMain = injector.get(WebWorkerMain); var webWorkerMain = injector.get(WebWorkerMain);
@ -259,7 +261,7 @@ class EventDispatcher implements RenderEventDispatcher {
case "input": case "input":
case "change": case "change":
case "blur": case "blur":
serializedEvent = serializeEventWithValue(e); serializedEvent = serializeEventWithTarget(e);
break; break;
case "abort": case "abort":
case "afterprint": case "afterprint":

View File

@ -11,6 +11,9 @@ import {bootstrapWebworkerCommon} from "angular2/src/web-workers/worker/applicat
import {ApplicationRef} from "angular2/src/core/application"; import {ApplicationRef} from "angular2/src/core/application";
import {Injectable} from "angular2/di"; import {Injectable} from "angular2/di";
// TODO(jteplitz602) remove this and compile with lib.webworker.d.ts (#3492)
var _postMessage: (message: any, transferrables?:[ArrayBuffer]) => void = <any>postMessage;
/** /**
* Bootstrapping a Webworker Application * Bootstrapping a Webworker Application
* *
@ -41,7 +44,7 @@ export class WorkerMessageBus implements MessageBus {
} }
export class WorkerMessageBusSink implements MessageBusSink { export class WorkerMessageBusSink implements MessageBusSink {
public send(message: Object) { postMessage(message, null); } public send(message: Object) { _postMessage(message); }
} }
export class WorkerMessageBusSource implements MessageBusSource { export class WorkerMessageBusSource implements MessageBusSource {

View File

@ -17,9 +17,13 @@ import {
ChangeDetection, ChangeDetection,
DynamicChangeDetection, DynamicChangeDetection,
JitChangeDetection, JitChangeDetection,
PreGeneratedChangeDetection,
Pipes, Pipes,
defaultPipes, defaultPipes,
PreGeneratedChangeDetection IterableDiffers,
defaultIterableDiffers,
KeyValueDiffers,
defaultKeyValueDiffers
} from 'angular2/src/change_detection/change_detection'; } from 'angular2/src/change_detection/change_detection';
import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver'; import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver';
import {ExceptionHandler} from 'angular2/src/core/exception_handler'; import {ExceptionHandler} from 'angular2/src/core/exception_handler';
@ -119,6 +123,8 @@ function _injectorBindings(appComponentType, bus: WorkerMessageBus,
CompilerCache, CompilerCache,
ViewResolver, ViewResolver,
defaultPipes, defaultPipes,
bind(IterableDiffers).toValue(defaultIterableDiffers),
bind(KeyValueDiffers).toValue(defaultKeyValueDiffers),
bind(ChangeDetection).toClass(bestChangeDetection), bind(ChangeDetection).toClass(bestChangeDetection),
DirectiveResolver, DirectiveResolver,
Parser, Parser,

View File

@ -0,0 +1,4 @@
declare module "B64" {
export function fromByteArray(arr: Uint8Array): string;
export function toByteArray(str: string): Uint8Array;
}

View File

@ -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<String> args, SendPort replyTo) {
reflector.reflectionCapabilities = new ReflectionCapabilities();
bootstrapWebworker(replyTo, ImageDemo).catchError((error) => throw error);
}

View File

@ -0,0 +1,6 @@
import {bootstrapWebworker} from "angular2/src/web-workers/worker/application";
import {ImageDemo} from "./index_common";
export function main() {
bootstrapWebworker(ImageDemo);
}

View File

@ -0,0 +1,8 @@
declare class Bitmap {
constructor(width: number, height: number);
subsample(n: number): void;
dataURL(): string;
pixel:[any];
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -0,0 +1,6 @@
var _FileReader = FileReader;
export {_FileReader as FileReader};
export class Uint8ArrayWrapper {
static create(buffer: ArrayBuffer) { return new Uint8Array(buffer); }
}

View File

@ -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;
}

View File

@ -0,0 +1,33 @@
<nav class="blue">
<div class="container">
<div class="nav-wrapper">
<a class="brand-logo">Angular 2 Image Demo</a>
</div>
</div>
</nav>
<section id="main" class="container">
<div class="row">
<div *ng-for="#image of images" class="col s12 m2">
<div class="card">
<div class="card-image">
<img [src]="image.src" [class.grey]="image.filtering"/>
<div class="image-loader progress" [class.hidden]="!image.filtering">
<div class="indeterminate"></div>
</div>
</div>
</div>
</div>
</div>
<div class="fixed-action-btn bottom" (click)="applyFilters()">
<a class="btn-floating btn-large blue darken-4" (click)="applyFilters()">
<i class="large material-icons" (click)="applyFilters()">invert_colors</i>
</a>
</div>
<div class="file-field">
<div class="btn blue darken-2">
<span>Select Images</span>
<input type="file" accept="image/bmp" multiple (change)="uploadFiles($event.target.files)" />
</div>
</div>
</section>

View File

@ -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");
}

View File

@ -0,0 +1,18 @@
<html>
<head>
<link rel="stylesheet" href="image_demo.css" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body>
<image-demo>
</image-demo>
$SCRIPTS$
<!-- Compiled and minified CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.0/css/materialize.min.css">
<!-- Compiled and minified JavaScript -->
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.0/js/materialize.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,2 @@
import {bootstrap} from "angular2/src/web-workers/ui/application";
bootstrap("loader.js");

View File

@ -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;
};
}
}

View File

@ -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;
}
}

View File

@ -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); });

View File

@ -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;
}
}

View File

@ -0,0 +1,188 @@
/// <reference path="../bitmap.d.ts" /> /// <reference path="../b64.d.ts" />
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 = <any>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<any>, 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;
}

View File

@ -0,0 +1,19 @@
<html>
<head>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="image_demo.css" />
<script type="text/javascript" src="b64.js"></script>
</head>
<body>
<image-demo>
</image-demo>
$SCRIPTS$
<!-- Compiled and minified CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.0/css/materialize.min.css">
<!-- Compiled and minified JavaScript -->
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.0/js/materialize.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,6 @@
import {bootstrap} from "angular2/bootstrap";
import {ImageDemo} from "./index_common";
export function main() {
bootstrap(ImageDemo);
}

View File

@ -35,6 +35,7 @@
}, },
"devDependencies": { "devDependencies": {
"angular": "1.3.5", "angular": "1.3.5",
"base64-js": "^0.0.8",
"bower": "^1.3.12", "bower": "^1.3.12",
"broccoli": "^0.15.3", "broccoli": "^0.15.3",
"broccoli-filter": "^0.1.12", "broccoli-filter": "^0.1.12",

View File

@ -217,7 +217,8 @@ class CustomLanguageServiceHost implements ts.LanguageServiceHost {
* not worth the potential issues with stale cache records. * not worth the potential issues with stale cache records.
*/ */
getScriptSnapshot(tsFilePath: string): ts.IScriptSnapshot { getScriptSnapshot(tsFilePath: string): ts.IScriptSnapshot {
let absoluteTsFilePath = (tsFilePath == this.defaultLibFilePath) ? let absoluteTsFilePath =
(tsFilePath == this.defaultLibFilePath || path.isAbsolute(tsFilePath)) ?
tsFilePath : tsFilePath :
path.join(this.treeInputPath, tsFilePath); path.join(this.treeInputPath, tsFilePath);

View File

@ -65,7 +65,8 @@ const kServedPaths = [
'examples/src/material/switcher', 'examples/src/material/switcher',
'examples/src/message_broker', 'examples/src/message_broker',
'examples/src/web_workers/kitchen_sink', '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-register.js',
'node_modules/systemjs/lib/extension-cjs.js', 'node_modules/systemjs/lib/extension-cjs.js',
'node_modules/rx/dist/rx.js', 'node_modules/rx/dist/rx.js',
'node_modules/base64-js/lib/b64.js',
'node_modules/reflect-metadata/Reflect.js', 'node_modules/reflect-metadata/Reflect.js',
'tools/build/snippets/runtime_paths.js', 'tools/build/snippets/runtime_paths.js',
path.relative(projectRootDir, TRACEUR_RUNTIME_PATH) path.relative(projectRootDir, TRACEUR_RUNTIME_PATH)