Develop and Download Open Source Software

Browse Subversion Repository

Contents of /trunk/src/ts/ImageFilter.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 170 - (show annotations) (download) (as text)
Mon May 4 03:43:41 2015 UTC (9 years ago) by tsugehara
File MIME type: text/texmacs
File size: 28020 byte(s)


1 ///<reference path="all.ts"/>
2 module jg {
3 // This classes is has modified based on the paintbrush.js.
4 // 1. Filter
5 // 2. BlurFilter
6 // 3. EmbossFilter
7 // 4. SharpenFilter
8 // 5. MosaicFilter
9 // 6. GreyscaleFilter
10 // 7. SepiaFilter
11 // 8. TintFilter
12 // 9. EdgesFilter
13 // 10. NoiseFilter
14 // 11. MatrixFilter
15 // 12. PosterizeFilter
16 // --------------------------------------------------
17 // paintbrush.js, v0.3
18 // A browser-based image processing library for HTML5 canvas
19 // Developed by Dave Shea, http://www.mezzoblue.com/
20 //
21 // This project lives on GitHub:
22 // http://github.com/mezzoblue/PaintbrushJS
23 //
24 // Except where otherwise noted, PaintbrushJS is licensed under the MIT License:
25 // http://www.opensource.org/licenses/mit-license.php
26 // --------------------------------------------------
27 /**
28 * 画像フィルタ機能を提供するモジュール
29 */
30 export module ImageFilter {
31 /**
32 * フィルタ用インターフェース
33 */
34 export interface IFilter {
35 /**
36 * フィルタを実行する
37 * @param pixels フィルタ対象のピクセルデータ
38 */
39 filter(pixels:ImageData);
40 }
41
42 /**
43 * 複数フィルタを重ねがけするためのクラス
44 */
45 export class FilterChain implements IFilter {
46 /** 管理中フィルタ */
47 filters:Filter[];
48
49 /**
50 * コンストラクタ
51 */
52 constructor() {
53 this.filters = [];
54 }
55
56 /**
57 * 対象indexのフィルタを取得
58 * @param index 取得対象index
59 */
60 get(index:number):Filter {
61 return this.filters[index];
62 }
63
64 /**
65 * フィルタを追加
66 * @param filter 追加するフィルタ
67 */
68 add(filter:Filter):FilterChain {
69 this.filters.push(filter);
70 return this;
71 }
72
73 /**
74 * 単体のフィルタを設定する。これまでset, addしたフィルタはすべて破棄される
75 * @param filter 設定するフィルタ
76 */
77 set(filter:Filter):FilterChain {
78 this.filters = [filter];
79 return this;
80 }
81
82 /**
83 * フィルタを所定の位置に挿入する
84 * @param index 挿入する場所のindex
85 * @param filter 挿入するフィルタ
86 */
87 insert(index:number, filter:Filter):FilterChain {
88 for (var i=1; i<arguments.length; i++)
89 this.filters.splice(index, 0, <Filter>arguments[i]);
90 return this;
91 }
92
93 /**
94 * フィルタを削除する
95 * @param filter 削除するフィルタ
96 */
97 remove(filter:Filter) {
98 for (var i=0; i<this.filters.length; i++) {
99 if (this.filters[i] == filter) {
100 this.filters.splice(i, 1);
101 return;
102 }
103 }
104 }
105
106 /**
107 * 全フィルタをクリアする
108 */
109 clear():FilterChain {
110 this.filters = [];
111 return this;
112 }
113
114 /**
115 * フィルタ数を返す
116 */
117 count():number {
118 return this.filters.length;
119 }
120
121 /**
122 * このフィルタチェインに一つでもフィルタが存在するかを返す
123 */
124 has():boolean {
125 return this.filters.length > 0;
126 }
127
128 /**
129 * このFilterChainによるフィルタを適用したSpriteを返す
130 * @param entity SpriteにするEntity
131 */
132 createSprite(entity:E):Sprite {
133 var buffer = new BufferedRenderer({width:entity.width, height:entity.height});
134 buffer.filter = this;
135 var x = entity.x;
136 var y = entity.y;
137 entity.x = 0;
138 entity.y = 0;
139 buffer.renderUnit(entity);
140 entity.x = x;
141 entity.y = y;
142 return buffer.createSprite();
143 }
144
145 /**
146 * このFilterChainによるフィルタを適用したImageを返す
147 * @param entity SpriteにするEntity
148 */
149 createImage(entity:Sprite):HTMLCanvasElement {
150 var buffer = new BufferedRenderer({width:entity.image.width, height:entity.image.height});
151 var image = new Sprite(entity.image);
152 image.x = 0;
153 image.y = 0;
154 buffer.filter = this;
155 buffer.renderUnit(image);
156 return buffer.createImage();
157 }
158
159 /**
160 * フィルタを適用する
161 * @param pixels 適用対象のピクセルデータ
162 */
163 filter(pixels:ImageData) {
164 var length = this.filters.length;
165 for (var i=0; i<length; i++)
166 this.filters[i].filter(pixels);
167 }
168 }
169
170 /**
171 * 一般的な機能を持つフィルタ
172 */
173 export class Filter implements IFilter {
174 /** オプション */
175 opt: any;
176 /** 横幅 */
177 width: number;
178 /** 縦幅 */
179 height: number;
180 /** 対象のゲーム */
181 game: Game;
182
183 /**
184 * 対象のゲームを指定してFilterクラスのインスタンスを生成する
185 * @param game 対象のゲーム
186 */
187 constructor(game:Game) {
188 this.opt = {}
189 this.game = game;
190 }
191
192 /**
193 * フィルタを適用する
194 * @param pixels 適用対象のピクセルデータ
195 */
196 filter(pixels:ImageData) {
197
198 }
199
200 /**
201 * オプションを取得する
202 * @param name オプション名
203 * @param defaultValue オプションが無い場合に返す値を指定する。省略可
204 */
205 getOption(name:string, defaultValue?:any):any {
206 if (this.opt[name] === undefined)
207 return defaultValue;
208 return this.opt[name];
209 }
210
211 /**
212 * 色の相違を取得
213 * @param dif
214 * @param dest
215 * @param src
216 */
217 findColorDifference(dif:number, dest:number, src:number):number {
218 return (dif * dest + (1 - dif) * src);
219 }
220
221 /**
222 * 色を作成する
223 * @param src
224 */
225 createColor(src:string):string {
226 src = src.replace(/^#/, '');
227 if (src.length == 3)
228 src = src.replace(/(.)/g, '$1$1');
229
230 return src;
231 }
232
233 /**
234 * マトリックスに基づいたフィルタを適用する
235 * @param pixels 適用対象のピクセルデータ
236 * @param matrix
237 * @param amount
238 */
239 applyMatrix(pixels:ImageData, matrix:number[], amount:number) {
240 var data = pixels.data, imgWidth = pixels.width, height = pixels.height;
241 var datalen = data.length;
242 var bufferedData = new Array(data.length);
243 for (var i=0; i<datalen; i++)
244 bufferedData[i] = data[i];
245
246 // calculate the size of the matrix
247 var matrixSize = Math.sqrt(matrix.length);
248 // also store the size of the kernel radius (half the size of the matrix)
249 var kernelRadius = Math.floor(matrixSize / 2);
250
251 // loop through every pixel
252 for (var i = 1; i < imgWidth - 1; i++) {
253 for (var j = 1; j < height - 1; j++) {
254 // temporary holders for matrix results
255 var sumR=0, sumG = 0, sumB = 0;
256
257 // loop through the matrix itself
258 for (var h = 0; h < matrixSize; h++) {
259 for (var w = 0; w < matrixSize; w++) {
260 // get a refence to a pixel position in the matrix
261 var i2 = ((i + w - kernelRadius) + (j + h - kernelRadius) * imgWidth) << 2;
262
263 // apply the value from the current matrix position
264 sumR += bufferedData[i2 ] * matrix[w + h * matrixSize];
265 sumG += bufferedData[i2+ 1] * matrix[w + h * matrixSize];
266 sumB += bufferedData[i2+ 2] * matrix[w + h * matrixSize];
267 }
268 }
269
270 // get a reference for the final pixel
271 var ref = (i + j * imgWidth) << 2;
272 var r = data[ref],
273 g = data[ref + 1],
274 b = data[ref + 2];
275
276 // finally, apply the adjusted values
277 data[ref] = this.findColorDifference(amount, sumR, r);
278 data[ref + 1] = this.findColorDifference(amount, sumG, g);
279 data[ref + 2] = this.findColorDifference(amount, sumB, b);
280 }
281 }
282
283 // code to clean the secondary buffer out of the DOM would be good here
284
285 return(pixels);
286 }
287
288 /**
289 * RGBの境界値をチェックする
290 * @param val チェックする値
291 */
292 checkRGBBoundary(val:number) {
293 if (val < 0)
294 return 0;
295 else if (val > 255)
296 return 255;
297
298 return val;
299 }
300 }
301
302 /**
303 * ユニバーサルトランジションを実現するフィルタ
304 */
305 export class UniversalTransitionFilter extends Filter {
306 /** ルール画像 */
307 ruleImage: ImageData;
308 /** マスク */
309 mask: any;
310
311 /**
312 * コンストラクタ
313 * @param game 対象のゲーム
314 * @param image ルール画像
315 * @param amount フィルタの影響度。省略時は255
316 * @param repeat サイズが合わない場合繰り返すかどうか。省略時はfalse
317 */
318 constructor(game:Game, image?:any, amount?:number, repeat?:boolean) {
319 super(game);
320 this.opt.amount = amount;
321 this.opt.image = image;
322 this.opt.repeat = repeat;
323 }
324
325 /**
326 * ImageData取得
327 * @param image 取得対象の画像
328 * @param canvas 取得対象のcanvas。省略時は内部的に生成し、drawImageでimageを描画する
329 */
330 getImageData(image:any, canvas?:HTMLCanvasElement):ImageData {
331 var context;
332 if (! canvas) {
333 canvas = window.createCanvas(image.width, image.height);
334 context = canvas.getContext("2d");
335 context.drawImage(image, 0, 0);
336 } else {
337 context = canvas.getContext("2d");
338 }
339 return context.getImageData(0, 0, image.width, image.height);
340 }
341
342 /**
343 * ルール画像を作成する
344 */
345 createRuleImage() {
346 var image = this.opt.image;
347 if (image instanceof ImageData) {
348 this.ruleImage = image;
349 } else if (image instanceof HTMLCanvasElement) {
350 this.ruleImage = this.getImageData(image, image);
351 } else if (image instanceof HTMLImageElement || image instanceof HTMLVideoElement) {
352 this.ruleImage = this.getImageData(image);
353 } else {
354 //これは多分E系じゃないかな
355 var sprite = image.createSprite();
356 this.ruleImage = this.getImageData(sprite.image);
357 }
358 }
359
360 /**
361 * マスクを作成する
362 */
363 createMask() {
364 var mask;
365 var a = (this.getImageData(this.opt.mask1)).data;
366 var b = (this.getImageData(this.opt.mask2)).data;
367 var d = this.ruleImage.data;
368 mask = {};
369 var enableCount = 0;
370 for (var i=0, len=a.length; i<len; i+=4) {
371 if (a[i] == b[i] && a[i+1] == b[i+1] && a[i+2] == b[i+2] && a[i+3] == b[i+3])
372 mask[i] = 1;
373 else
374 mask[i] = 0;
375 }
376 this.mask = mask;
377 return mask;
378 }
379
380 /**
381 * フィルタを適用する
382 * @param pixels 適用対象のピクセルデータ
383 */
384 filter(pixels:ImageData) {
385 if (! this.ruleImage)
386 this.createRuleImage();
387
388 var amount = this.getOption("amount", 255);
389 var repeat = this.getOption("repeat", false);
390
391 var w = pixels.width;
392 var h = pixels.height;
393 var w2 = this.ruleImage.width;
394 var h2 = this.ruleImage.height;
395 var data = pixels.data;
396 var data2 = this.ruleImage.data;
397 var mask;
398
399 if (this.opt.mask1 && this.opt.mask2)
400 mask = (this.mask) ? this.mask : this.createMask();
401
402 if (w == w2 && h == h2) {
403 for (var i = 0, length = data.length; i < length >> 2; i++) {
404 var index = i << 2;
405 if (mask && mask[index]) {
406 if (this.opt.maskDraw) {
407 continue;
408 } else {
409 data[index + 3] = 0;
410 continue;
411 }
412 }
413 data[index + 3] = Math.round(data[index + 3] / 255 * Math.max(0, data2[index] - amount));
414 }
415 } else if (repeat) {
416 for (var x=0; x<w; x++) {
417 for (var y=0; y<h; y++) {
418 var index = (x + y * w) * 4;
419 if (mask && mask[index]) {
420 if (this.opt.maskDraw) {
421 continue;
422 } else {
423 data[index + 3] = 0;
424 continue;
425 }
426 }
427 var x2 = x % w2;
428 var y2 = y % h2;
429 var index2 = (x2 + y2 * w2) * 4;
430 data[index + 3] = Math.round(data[index + 3] / 255 * Math.max(0, data2[index2] - amount));
431 }
432 }
433 } else {
434 //Note: 計算簡略化。0.133333...とかだと環境によっては異常に遅くなるので、大体で。
435 var xPer = Math.round(w2 / w * 100) / 100;
436 var yPer = Math.round(h2 / h * 100) / 100;
437 for (var x=0; x<w; x++) {
438 for (var y=0; y<h; y++) {
439 var index = (x + y * w) * 4;
440 if (mask && mask[index]) {
441 if (this.opt.maskDraw) {
442 continue;
443 } else {
444 data[index + 3] = 0;
445 continue;
446 }
447 }
448 var x2 = Math.round(xPer * x);
449 var y2 = Math.round(yPer * y);
450 var index2 = (x2 + y2 * w2) * 4;
451 data[index + 3] = Math.round(data[index + 3] / 255 * Math.max(0, data2[index2] - amount));
452 }
453 }
454 }
455 }
456 }
457
458 /**
459 * 逆方向からのユニバーサルトランジション
460 */
461 export class ReverseUniversalTransitionFilter extends UniversalTransitionFilter {
462 /**
463 * 画像を作成する
464 * @param width 横幅
465 * @param height 縦幅
466 */
467 createImageData(width:number, height:number) {
468 var canvas = window.createCanvas(width, height);
469 var context = <CanvasRenderingContext2D>canvas.getContext("2d");
470 return context.createImageData(width, height);
471 }
472
473 /**
474 * ルール画像を作成する
475 */
476 createRuleImage() {
477 super.createRuleImage();
478 var d = this.ruleImage.data;
479 for (var i = 0, length = d.length; i < length >> 2; i++) {
480 var index = i << 2;
481 d[index] = 255-d[index];
482 d[index+1] = 255-d[index+1];
483 d[index+2] = 255-d[index+2];
484 }
485 }
486 }
487
488 /**
489 * グレースケールにするフィルタ
490 */
491 export class GreyscaleFilter extends Filter {
492 /**
493 * コンストラクタ
494 * @param game 対象のゲーム
495 * @param opacity 透明度。省略時は1
496 */
497 constructor(game:Game, opacity?:number) {
498 super(game);
499 this.opt.opacity = opacity;
500 }
501
502 /**
503 * フィルタを適用する
504 * @param pixels 対象のピクセルデータ
505 */
506 filter(pixels:ImageData) {
507 var opacity = this.getOption("opacity", 1);
508 var data = pixels.data;
509
510 for (var i = 0, length = data.length; i < length >> 2; i++) {
511 var index = i << 2;
512 var r = data[index],
513 g = data[index + 1],
514 b = data[index + 2];
515
516 var val = r * 0.21 + g * 0.71 + b * 0.07;
517 data[index] = this.findColorDifference(opacity, val, r);
518 data[index + 1] = this.findColorDifference(opacity, val, g);
519 data[index + 2] = this.findColorDifference(opacity, val, b);
520 }
521 }
522 }
523
524 /**
525 * セピア色にするフィルタ
526 */
527 export class SepiaFilter extends Filter {
528 /**
529 * コンストラクタ
530 * @param game 対象のゲーム
531 * @param opacity 透明度。省略時は1
532 */
533 constructor(game:Game, opacity?:number) {
534 super(game);
535 this.opt.opacity = opacity;
536 }
537
538 /**
539 * フィルタを適用する
540 * @param pixels 対象のピクセルデータ
541 */
542 filter(pixels:ImageData) {
543 var opacity = this.getOption("opacity", 1);
544 var data = pixels.data;
545
546 for (var i = 0, length = data.length; i < length >> 2; i++) {
547 var index = i << 2;
548 var r = data[index],
549 g = data[index + 1],
550 b = data[index + 2];
551 data[index] = this.findColorDifference(
552 opacity,
553 r * 0.393 + g * 0.769 + b * 0.189,
554 r
555 );
556 data[index + 1] = this.findColorDifference(
557 opacity,
558 r * 0.349 + g * 0.686 + b * 0.168,
559 g
560 );
561 data[index + 2] = this.findColorDifference(
562 opacity,
563 r * 0.272 + g * 0.534 + b * 0.131,
564 b
565 );
566 }
567 }
568 }
569
570 /**
571 * 色味をつけるフィルタ
572 */
573 export class TintFilter extends Filter {
574 /**
575 * コンストラクタ
576 * @param game 対象のゲーム
577 * @param color 色。省略時は1
578 */
579 constructor(game:Game, color?:string, opacity?:number) {
580 super(game);
581 this.opt.color = color;
582 this.opt.opacity = opacity;
583 }
584
585 /**
586 * フィルタを適用する
587 * @param pixels 対象のピクセルデータ
588 */
589 filter(pixels:ImageData) {
590 var opacity = this.getOption("opacity", 1);
591 var color = this.getOption("color", "#f00");
592 var data = pixels.data;
593
594 var src = parseInt(this.createColor(color), 16);
595 var r2 = (src & 0xFF0000) >> 16,
596 g2 = (src & 0x00FF00) >> 8,
597 b2 = (src & 0x0000FF);
598
599 for (var i = 0, length = data.length; i < length >> 2; i++) {
600 var index = i << 2;
601 var r = data[index],
602 g = data[index + 1],
603 b = data[index + 2];
604 data[index] = this.findColorDifference(opacity, r2, r);
605 data[index + 1] = this.findColorDifference(opacity, g2, g);
606 data[index + 2] = this.findColorDifference(opacity, b2, b);
607 }
608 }
609 }
610
611 /**
612 * エッジを際立たせるフィルタ
613 */
614 export class EdgesFilter extends Filter {
615 /**
616 * コンストラクタ
617 * @param game 対象のゲーム
618 * @param amount 威力。省略時は1
619 */
620 constructor(game:Game, amount?:number) {
621 super(game);
622 this.opt.amount = amount;
623 }
624
625 /**
626 * フィルタを適用する
627 * @param pixels ピクセルデータ
628 */
629 filter(pixels:ImageData) {
630 var matrix = [
631 0, 1, 0,
632 1, -4, 1,
633 0, 1, 0
634 ];
635 this.applyMatrix(
636 pixels,
637 matrix,
638 this.getOption("amount", 1)
639 );
640 }
641 }
642
643 /**
644 * エンボスフィルタ
645 */
646 export class EmbossFilter extends Filter {
647 /**
648 * コンストラクタ
649 * @param game 対象のゲーム
650 * @param amount 威力。省略時は0.5
651 */
652 constructor(game:Game, amount?:number) {
653 super(game);
654 this.opt.amount = amount;
655 }
656
657 /**
658 * フィルタを適用する
659 * @param pixels 対象のピクセルデータ
660 */
661 filter(pixels:ImageData) {
662 var matrix = [
663 -2,-1, 0,
664 -1, 1, 1,
665 0, 1, 2
666 ];
667 this.applyMatrix(
668 pixels,
669 matrix,
670 this.getOption("amount", 0.5)
671 );
672 }
673 }
674
675 /**
676 * シャープにするフィルタ
677 */
678 export class SharpenFilter extends Filter {
679 /**
680 * コンストラクタ
681 * @param game 対象のゲーム
682 * @param amount 威力
683 */
684 constructor(game:Game, amount?:number) {
685 super(game);
686 this.opt.amount = amount;
687 }
688
689 /**
690 * フィルタを適用する
691 * @param pixels 対象のピクセルデータ
692 */
693 filter(pixels:ImageData) {
694 var matrix = [
695 -1,-1, -1,
696 -1, 9, -1,
697 -1,-1, -1
698 ];
699 this.applyMatrix(
700 pixels,
701 matrix,
702 this.getOption("amount", 0.5)
703 );
704 }
705 }
706
707 /**
708 * マトリックスに基づいた操作を行う汎用フィルタ
709 */
710 export class MatrixFilter extends Filter {
711 /**
712 * コンストラクタ
713 * @param game 対象のゲーム
714 * @param amount 威力。省略時は0.5
715 * @param matrix 適用するマトリックス。省略時はすべて0.111
716 */
717 constructor(game:Game, amount?:number, matrix?:number[]) {
718 super(game);
719 this.opt.amount = amount;
720 this.opt.matrix = matrix;
721 }
722
723 /**
724 * フィルタを適用する
725 * @param pixels 対象のピクセルデータ
726 */
727 filter(pixels:ImageData) {
728 var matrix = this.getOption("matrix", [
729 0.111, 0.111, 0.111,
730 0.111, 0.111, 0.111,
731 0.111, 0.111, 0.111
732 ]);
733 this.applyMatrix(
734 pixels,
735 matrix,
736 this.getOption("amount", 0.5)
737 );
738 }
739 }
740
741 /**
742 * にじませるフィルタ。
743 * アルゴリズムはガウシアンフィルタに順ずる。少し重い
744 */
745 export class BlurFilter extends Filter {
746 /**
747 * コンストラクタ
748 * @param game 対象のゲーム
749 * @param amount 威力。省略時は2
750 */
751 constructor(game:Game, amount?:number) {
752 super(game);
753 this.opt.amount = amount;
754 }
755
756 // calculate gaussian blur
757 // adapted from http://pvnick.blogspot.com/2010/01/im-currently-porting-image-segmentation.html
758 /**
759 * フィルタを適用する
760 * @param pixels 対象のピクセルデータ
761 */
762 filter(pixels:ImageData) {
763 var width = pixels.width;
764 var width4 = width << 2;
765 var height = pixels.height;
766 var amount = this.getOption("amount", 2);
767
768 if (pixels) {
769 var data = pixels.data;
770
771 // compute coefficients as a function of amount
772 var q;
773 if (amount < 0.0)
774 amount = 0.0;
775
776 if (amount >= 2.5) {
777 q = 0.98711 * amount - 0.96330;
778 } else if (amount >= 0.5) {
779 q = 3.97156 - 4.14554 * Math.sqrt(1.0 - 0.26891 * amount);
780 } else {
781 q = 2 * amount * (3.97156 - 4.14554 * Math.sqrt(1.0 - 0.26891 * 0.5));
782 }
783
784 //compute b0, b1, b2, and b3
785 var qq = q * q;
786 var qqq = qq * q;
787 var b0 = 1.57825 + (2.44413 * q) + (1.4281 * qq ) + (0.422205 * qqq);
788 var b1 = ((2.44413 * q) + (2.85619 * qq) + (1.26661 * qqq)) / b0;
789 var b2 = (-((1.4281 * qq) + (1.26661 * qqq))) / b0;
790 var b3 = (0.422205 * qqq) / b0;
791 var bigB = 1.0 - (b1 + b2 + b3);
792
793 // horizontal
794 for (var c = 0; c < 3; c++) {
795 for (var y = 0; y < height; y++) {
796 // forward
797 var index = y * width4 + c;
798 var indexLast = y * width4 + ((width - 1) << 2) + c;
799 var pixel = data[index];
800 var ppixel = pixel;
801 var pppixel = ppixel;
802 var ppppixel = pppixel;
803 for (; index <= indexLast; index += 4) {
804 pixel = bigB * data[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel;
805 data[index] = pixel;
806 ppppixel = pppixel;
807 pppixel = ppixel;
808 ppixel = pixel;
809 }
810 // backward
811 index = y * width4 + ((width - 1) << 2) + c;
812 indexLast = y * width4 + c;
813 pixel = data[index];
814 ppixel = pixel;
815 pppixel = ppixel;
816 ppppixel = pppixel;
817 for (; index >= indexLast; index -= 4) {
818 pixel = bigB * data[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel;
819 data[index] = pixel;
820 ppppixel = pppixel;
821 pppixel = ppixel;
822 ppixel = pixel;
823 }
824 }
825 }
826
827 // vertical
828 for (var c = 0; c < 3; c++) {
829 for (var x = 0; x < width; x++) {
830 // forward
831 var index = (x << 2) + c;
832 var indexLast = (height - 1) * width4 + (x << 2) + c;
833 var pixel = data[index];
834 var ppixel = pixel;
835 var pppixel = ppixel;
836 var ppppixel = pppixel;
837 for (; index <= indexLast; index += width4) {
838 pixel = bigB * data[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel;
839 data[index] = pixel;
840 ppppixel = pppixel;
841 pppixel = ppixel;
842 ppixel = pixel;
843 }
844 // backward
845 index = (height - 1) * width4 + (x << 2) + c;
846 indexLast = (x << 2) + c;
847 pixel = data[index];
848 ppixel = pixel;
849 pppixel = ppixel;
850 ppppixel = pppixel;
851 for (; index >= indexLast; index -= width4) {
852 pixel = bigB * data[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel;
853 data[index] = pixel;
854 ppppixel = pppixel;
855 pppixel = ppixel;
856 ppixel = pixel;
857 }
858 }
859 }
860 }
861 }
862 }
863
864 /**
865 * モザイクをかけるフィルタ
866 */
867 export class MosaicFilter extends Filter {
868 /**
869 * コンストラクタ
870 * @param game 対象のゲーム
871 * @param size モザイクサイズ。省略時は5
872 * @param opacity 透明度。省略時は1
873 */
874 constructor(game:Game, size?:number, opacity?:number) {
875 super(game);
876 this.opt.size = size;
877 this.opt.opacity = opacity;
878 }
879
880 /**
881 * フィルタを適用する
882 * @param pixels 対象のピクセルデータ
883 */
884 filter(pixels:ImageData) {
885 var opacity = this.getOption("opacity", 1);
886 var size = Math.round(this.getOption("size", 5));
887 var width = pixels.width;
888
889 for (var i = 0, data = pixels.data, length = data.length; i < length >> 2; i++) {
890 var index = i << 2;
891 var r = data[index],
892 g = data[index + 1],
893 b = data[index + 2];
894
895 var pos = index >> 2;
896 var stepY = Math.floor(pos / width);
897 var stepY1 = stepY % size;
898 var stepX = pos - (stepY * width);
899 var stepX1 = stepX % size;
900
901 if (stepY1) pos -= stepY1 * width;
902 if (stepX1) pos -= stepX1;
903 pos = pos << 2;
904
905 data[index ] = this.findColorDifference(opacity, data[pos] , r);
906 data[index+1] = this.findColorDifference(opacity, data[pos + 1], g);
907 data[index+2] = this.findColorDifference(opacity, data[pos + 2], b);
908 }
909 }
910 }
911
912 /**
913 * ノイズの種類。Monoでモノクロノイズ、Colorでカラーノイズ
914 */
915 export enum NoiseType {
916 Mono, //monochrome
917 Color
918 }
919
920 /**
921 * ノイズをつけるフィルタ
922 */
923 export class NoiseFilter extends Filter {
924 /**
925 * コンストラクタ
926 * @param game 対象のゲーム
927 * @param amount 威力。省略時は30
928 * @param type モノクロノイズかカラーノイズか。省略時はモノクロ
929 */
930 constructor(game:Game, amount?:number, type?:NoiseType) {
931 super(game);
932 this.opt.amount = amount;
933 this.opt.type = type;
934 }
935
936 /**
937 * フィルタを適用する
938 * @param pixels 対象のピクセルデータ
939 */
940 filter(pixels:ImageData) {
941 var amount = this.getOption("amount", 30);
942 var type = this.getOption("type", NoiseType.Mono);
943 var max = (amount >> 1) - 1;
944 var min = max - amount;
945
946 for (var i = 0, data = pixels.data, length = data.length; i < length >> 2; i++) {
947 var index = i << 2;
948 var r = data[index],
949 g = data[index + 1],
950 b = data[index + 2];
951
952 if (type == NoiseType.Mono) {
953 var val = this.game.random(min, max);
954 data[index ] = this.checkRGBBoundary(r + val);
955 data[index+1] = this.checkRGBBoundary(g + val);
956 data[index+2] = this.checkRGBBoundary(b + val);
957 } else {
958 data[index ] = this.checkRGBBoundary(r + this.game.random(min, max));
959 data[index+1] = this.checkRGBBoundary(g + this.game.random(min, max));
960 data[index+2] = this.checkRGBBoundary(b + this.game.random(min, max));
961 }
962 }
963 }
964 }
965
966 /**
967 * ポスタライズフィルタ
968 */
969 export class PosterizeFilter extends Filter {
970 /**
971 * コンストラクタ
972 * @param game 対象のゲーム
973 * @param amount 威力。省略時は2
974 * @param opacity 透明度。省略時は1
975 */
976 constructor(game:Game, amount?:number, opacity?:number) {
977 super(game);
978 this.opt.opacity = opacity;
979 this.opt.amount = amount;
980 }
981
982 /**
983 * フィルタを適用する
984 * @param pixels 対象のピクセルデータ
985 */
986 filter(pixels:ImageData) {
987 var opacity:number = this.getOption("opacity", 1);
988 var amount:number = this.getOption("amount", 2);
989 var areas:number = 256 / amount;
990 var values:number = 255 / (amount - 1);
991
992 for (var i = 0, data = pixels.data, length = data.length; i < length >> 2; i++) {
993 var index = i << 2;
994 var r = data[index],
995 g = data[index + 1],
996 b = data[index + 2];
997
998 data[index ] = this.findColorDifference(
999 opacity,
1000 Math.round(values * Math.round(r / areas)),
1001 r
1002 );
1003 data[index+1] = this.findColorDifference(
1004 opacity,
1005 Math.round(values * Math.round(g / areas)),
1006 g
1007 );
1008 data[index+2] = this.findColorDifference(
1009 opacity,
1010 Math.round(values * Math.round(b / areas)),
1011 b
1012 );
1013 }
1014 }
1015 }
1016 }
1017 }

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26