- name: 2d.state.saverestore.transformation desc: save()/restore() affects the current transformation matrix code: | ctx.fillStyle = '#0f0'; ctx.fillRect(0, 0, 100, 50); ctx.save(); ctx.translate(200, 0); ctx.restore(); ctx.fillStyle = '#f00'; ctx.fillRect(-200, 0, 100, 50); @assert pixel 50,25 == 0,255,0,255; expected: green - name: 2d.state.saverestore.clip desc: save()/restore() affects the clipping path code: | ctx.fillStyle = '#f00'; ctx.fillRect(0, 0, 100, 50); ctx.save(); ctx.rect(0, 0, 1, 1); ctx.clip(); ctx.restore(); ctx.fillStyle = '#0f0'; ctx.fillRect(0, 0, 100, 50); @assert pixel 50,25 == 0,255,0,255; expected: green - name: 2d.state.saverestore.clip.2 desc: save()/restore() affects the clipping path code: | ctx.fillStyle = '#f00'; ctx.fillRect(0, 0, 100, 50); ctx.save(); ctx.rect(0, 0, 1, 1); ctx.clip(); ctx.clip(); ctx.restore(); ctx.fillStyle = '#0f0'; ctx.fillRect(0, 0, 100, 50); @assert pixel 50,25 == 0,255,0,255; expected: green - name: 2d.state.saverestore.path desc: save()/restore() does not affect the current path code: | ctx.fillStyle = '#f00'; ctx.fillRect(0, 0, 100, 50); ctx.save(); ctx.rect(0, 0, 100, 50); ctx.restore(); ctx.fillStyle = '#0f0'; ctx.fill(); @assert pixel 50,25 == 0,255,0,255; expected: green - name: 2d.state.saverestore.bitmap desc: save()/restore() does not affect the current bitmap code: | ctx.fillStyle = '#f00'; ctx.fillRect(0, 0, 100, 50); ctx.save(); ctx.fillStyle = '#0f0'; ctx.fillRect(0, 0, 100, 50); ctx.restore(); @assert pixel 50,25 == 0,255,0,255; expected: green - name: 2d.state.saverestore.stack desc: save()/restore() can be nested as a stack code: | ctx.lineWidth = 1; ctx.save(); ctx.lineWidth = 2; ctx.save(); ctx.lineWidth = 3; @assert ctx.lineWidth === 3; ctx.restore(); @assert ctx.lineWidth === 2; ctx.restore(); @assert ctx.lineWidth === 1; - name: 2d.state.saverestore.stackdepth desc: save()/restore() stack depth is not unreasonably limited code: | var limit = 512; for (var i = 1; i < limit; ++i) { ctx.save(); ctx.lineWidth = i; } for (var i = limit-1; i > 0; --i) { @assert ctx.lineWidth === i; ctx.restore(); } - name: 2d.state.saverestore.underflow desc: restore() with an empty stack has no effect code: | for (var i = 0; i < 16; ++i) ctx.restore(); ctx.lineWidth = 0.5; ctx.restore(); @assert ctx.lineWidth === 0.5; - name: 2d.state.saverestore variants_layout: [single_file, single_file, multi_files] variants: - restore-undoes-changes: desc: save()/restore() restores {{ variant_names[2] }}, for a canvas of size {{ size }}. code: | const old = ctx.{{ variant_names[2] }}; ctx.save(); ctx.{{ variant_names[2] }} = {{ value }}; ctx.restore(); @assert ctx.{{ variant_names[2] }} === old; save-does-not-modify-values: desc: save() does not modify {{ variant_names[2] }}, for a canvas of size {{ size }}. code: | ctx.{{ variant_names[2] }} = {{ value }}; const old = ctx.{{ variant_names[2] }}; // We're not interested in failures caused by get(set(x)) != x (e.g. // from rounding), so compare against `old` instead of {{ value }}. ctx.save(); @assert ctx.{{ variant_names[2] }} === old; ctx.restore(); - non-zero-size: size: [300, 150] zero-size: size: [0, 0] - &2d_state_test_cases strokeStyle: value: '"#ff0000"' fillStyle: value: '"#ff0000"' globalAlpha: value: 0.5 lineWidth: value: 0.5 lineCap: value: '"round"' lineJoin: value: '"round"' miterLimit: value: 0.5 shadowOffsetX: value: 5 shadowOffsetY: value: 5 shadowBlur: value: 5 shadowColor: value: '"#ff0000"' globalCompositeOperation: value: '"copy"' font: canvas_types: ['HtmlCanvas'] value: '"25px serif"' textAlign: canvas_types: ['HtmlCanvas'] value: '"center"' textBaseline: canvas_types: ['HtmlCanvas'] value: '"bottom"' - name: 2d.canvas.host.initial.reset.2dstate desc: Resetting the canvas state resets {{ variant_name }} code: | const default_val = ctx.{{ variant_name }}; ctx.{{ variant_name }} = {{ value }}; canvas.width = 100; @assert ctx.{{ variant_name }} === default_val; variants_layout: [single_file] variants: - *2d_state_test_cases