canvas 互換機能の bugfix
uupaa.js コードリード用のエントリです。興味が無い方は読み飛ばしてください。
VML backend で ctx.drawImage(image) + 不透明度(globalAlpha) を有効にしました(条件あり)
<vml:image> は opacity 属性が機能しません。そのため、<vml:image> ではなく、<vml:shape> <v:fill opacity="..." src="..." /> </v:shape> を使うことで、不透明度を設定できるようにしました。
# opacity属性はVMLの草案にありましたが、MSDNには記載されていません。
ただし、以下の条件があります。
-
-
- ctx.drawImage(image, dx, dy) で、Matrixが変更されていなければ不透明度が有効になります。
-
-
-
- ctx.drawImage(image, dx, dy, dw, dh) や ctx.drawImage(image, sx, sy, ,,,,,) では機能しません。(左から、Silverlight, VML, Flashです)
-
var _IMAGE_FILL = '<v:shape style="position:absolute;width:10px;' + 'height:10px;z-index:?;left:?px;top:?px"' + ' filled="t" stroked="f" coordsize="100,100"' + ' path="?"><v:fill type="tile" opacity="?" src="?" /></v:shape>', _IMAGE_SHADOW = '<v:shape style="position:absolute;width:10px;' + 'height:10px;z-index:?;left:?px;top:?px"' + ' filled="t" stroked="f" coordsize="100,100"' + ' path="?"><v:fill color="?" opacity="?" /></v:shape>'; // CanvasRenderingContext2D.prototype.drawImage // drawImage(image, dx, dy) // drawImage(image, dx, dy, dw, dh) // drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) function drawImage(image, a1, a2, a3, a4, a5, a6, a7, a8) { if (this.globalAlpha <= 0) { return; } if (this.shadowColor !== this._shadowColor) { this.__shadowColor = uu.color(this._shadowColor = this.shadowColor); } if (this.globalCompositeOperation !== this._mix) { this.__mix = _COMPOS[this._mix = this.globalCompositeOperation]; } var dim = uu.img.size(image), // img actual size args = arguments.length, full = (args === 9), sx = full ? a1 : 0, sy = full ? a2 : 0, sw = full ? a3 : dim.w, sh = full ? a4 : dim.h, dx = full ? a5 : a1, dy = full ? a6 : a2, dw = full ? a7 : a3 || dim.w, dh = full ? a8 : a4 || dim.h, rv = [], fg, m, history, // HTMLCanvasElement context history frag = [], tfrag, // code fragment i = 0, iz, c0, zindex = (this.__mix === 4) ? --this._zindex : (this.__mix === 10) ? (this.clear(), 0) : 0, renderShadow = this.__shadowColor.a && this.shadowBlur, sizeTrans; // 0: none size transform, 1: size transform if (image.src) { // HTMLImageElement + if (!this._matrixEffected) { + + // shadow + if (this.__shadowColor.a && this.shadowBlur) { + rv.push(uu.fmt(_IMAGE_SHADOW, + [zindex, dx + (this.shadowOffsetX + 1), + dy + (this.shadowOffsetY + 1), + _rect(this, 0, 0, dw, dh), + this.__shadowColor.hex, + (this.globalAlpha / Math.sqrt(this.shadowBlur) * 0.5)])); + } + + // no resize + no opacity + if (args === 3 && this.globalAlpha !== 1) { + rv.push(uu.fmt(_IMAGE_FILL, + [zindex, dx, dy, _rect(this, 0, 0, dw, dh), + this.globalAlpha, image.src])); + } else { rv.push( '<v:image style="position:absolute;z-index:', zindex, ';width:', dw, 'px;height:', dh, 'px;left:', dx, 'px;top:', dy, 'px" coordsize="100,100" src="', image.src, '" opacity="', this.globalAlpha, // <vml:image opacity> doesn't work. '" cropleft="', sx / dim.w, '" croptop="', sy / dim.h, '" cropright="', (dim.w - sx - sw) / dim.w, '" cropbottom="', (dim.h - sy - sh) / dim.h, '" />'); + } + } else {
Silverlight backend で、擬似的な strokeText をサポートしました
uuCanvas.js version 2.02 では Silverlight の strokeText は fillText と同じ結果になっていました(塗りつぶしていた)。
Silverlightには、テキストパス(テキストのアウトライン)を描画する機能がなく(WPFにはあるらしい)、中抜きの文字を描画することができないのが理由です。
この制限を以下の順番で描画することにより回避し、擬似的に strokeText をサポートしました。
- ctx.fillText() を DropShadow Effect 付きで描画する。DropShadow の影の色は ctx.strokeStyle の色を使用する
- ctx.fillText() を ctx.xKnockoutColor で描画する。これにより、ctx.xKnockoutColor の色で塗りつぶされた輪郭を持つ文字が描画される
ctx.xKnockoutColor はstrokeText用に今回追加した独自プロパティです。デフォルトは"white"なので、白で塗りつぶされた文字が描画されます。
(上から Silverlight, VML, Flash です)
(左から Silverlight, VML, Flash です)
Flash は背景色で抜いた文字を、Silverlight は白で塗りつぶした文字を描画しています。
// CanvasRenderingContext2D.prototype.strokeText function strokeText(text, x, y, maxWidth, fill) { if (fill) { _strokeText(this, text, x, y, maxWidth, fill); } else { var fillStyle = this.fillStyle; // save _strokeText(this, text, x, y, maxWidth, 0); this.fillStyle = this.xKnockoutColor; _strokeText(this, text, x, y, maxWidth, 1); this.fillStyle = fillStyle; // restore } } function _strokeText(ctx, text, x, y, maxWidth, fill) { // (snip) rv.push('" FontFamily="', font.rawfamily, '" FontSize="', font.size.toFixed(2), '" FontStyle="', _FONT_STYLES[font.style] || "Normal", '" FontWeight="', _FONT_WEIGHTS[font.weight] || "Normal", '">', uu.esc(text), _matrix('TextBlock', uu.m2d.translate(x - offX, y, ctx._matrix))); + if (fill) { rv.push((ctx.__shadowColor.a && ctx.shadowBlur) ? _dropShadow(ctx, "TextBlock", ctx.__shadowColor) : ""); + } else { + rv.push(['<TextBlock.Effect><DropShadowEffect Opacity="1" Color="', ctx.__strokeStyle.hex, + '" BlurRadius="', 4, + '" Direction="', 0, + '" ShadowDepth="', 1, + '" /></TextBlock.Effect>'].join("")); + } // (snip) }