Урок - Работа с изображениями в Canvas

Главная » Курсы » Курс HTML5 Canvas » Урок - Работа с изображениями в Canvas

Обучающий онлайн курс
HTML5 Canvas

Лицензия: Копирование запрещено.

Вставка изображений в Canvas

Метод drawImage() позволяет вставлять другие изображения (img и canvas) на канву. В браузере Opera также существует возможность рисования SVG-изображений внутри элемента canvas. drawImage() довольно сложный метод, который может принимать три, пять или девять аргументов.

Три аргумента: Базовое использование метода drawImage() включает один аргумент для указания изображения, которое необходимо вывести на канве, и два аргумента для задания координат.

context.drawImage(img, dx, dy);

Пять аргументов: Используются предыдущие три аргумента и еще два, задающие ширину и высоту вставляемого изображения (в случае если вы хотите изменить размеры изображения при вставке).

context.drawImage(img, dx, dy, dw, dh);

Девять аргументов: Используются предыдущие пять аргументов и еще четыре: два для координат области внутри исходного изображения и два для ширины и высоты области внутри исходного изображения для обрезки изображения перед вставкой в Canvas.

context.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);

Спецификация HTML 5 объясняет эти аргументы следующим образом:

Рассморим примеры использования всех трех вариантов.

Пример использования drawImage() с тремя аргументами:

<canvas id='imagesExample1' width=300 height=300>Обновите браузер</canvas>
<script>
//	создаем элемент изображения и указываем урл картинки
var img = new Image();
img.src = 'example1.png';
//	отрисовываем изображение, после того как браузер загрузит изображение
img.onload = function() {
	//	оператор try..catch используем для обработки ошибок, например если холст не найден
	//	в некоторых случах было замечено ошибочный вызов исключений при получении холста
	try{
		//	получаем контент холста
		var ctx = document.getElementById('imagesExample1').getContext('2d');
		// Рисуем изображение от точки с координатами 10, 40
		ctx.drawImage(img, 10, 40);
	}
	catch(err){
		//	выводит необходимую ошибку
		alert('Ошибка');
	}
}
</script>
Результат
Обновите браузер
Должно выглядеть так

Пример использования drawImage() с пятью аргументами:

<canvas id='imagesExample2' width=300 height=300>Обновите браузер</canvas>
<script>
//	создаем элемент изображения и указываем урл картинки
var img2 = new Image();
img2.src = 'example2.png'; 
//	отрисовываем изображение, после того как браузер его загрузит
img2.onload = function() {
	//	оператор try..catch используем для обработки ошибок, например если холст не найден
	//	в некоторых случах было замечено ошибочный вызов исключений при получении холста
	try{
		//	получаем контент холста
		var ctx2 = document.getElementById('imagesExample2').getContext('2d');
		// Рисуем изображения
		ctx2.drawImage(img2, -100, -50, 450, 450);
		ctx2.drawImage(img2, 0, 0, 50, 50);
		ctx2.drawImage(img2, 0, 60, 50, 100);
		ctx2.drawImage(img2, 60, 0, 100, 50);
	}
	catch(err){
		//	выводит необходимую ошибку
		alert('Ошибка');
	}
}
</script>
Результат
Обновите браузер
Должно выглядеть так

Пример использования drawImage() с пятью аргументами:

<canvas id='imagesExample3' width=300 height=300>Обновите браузер</canvas>
<script>
//	создаем элемент изображения и указываем урл картинки
var img3 = new Image();
img3.src = 'example3.png'; 
//	отрисовываем изображение, после того как браузер его загрузит
img3.onload = function() {
	//	оператор try..catch используем для обработки ошибок, например если холст не найден
	//	в некоторых случах было замечено ошибочный вызов исключений при получении холста
	try{
		//	получаем контент холста
		var ctx3 = document.getElementById('imagesExample3').getContext('2d');
		// Рисуем изображения
		ctx3.drawImage(img3, -100, -50, 450, 450);
		ctx3.drawImage(img3, 0, 0, 50, 50);
		ctx3.drawImage(img3, 0, 60, 50, 100);
		ctx3.drawImage(img3, 60, 0, 100, 50);
	}
	catch(err){
		//	выводит необходимую ошибку
		alert('Ошибка');
	}
}
</script>
Результат
Обновите браузер
Должно выглядеть так

Использование картинок

Источник: https://developer.mozilla.org/ru/Обучение_canvas%3aИспользование_картинок
Одним из наиболее привлекательных свойств canvas является возможность использовать картинки (изображения). Оно может быть применено для реализации динамических фотокомпозиций, для создания фонов графиков и т.п. Также, это свойство является единственным способом размещения текста на canvas (В спецификации нет ни одной функции для рисования текста). Внешние изображения могут быть любого формата, поддерживаемого Gecko (т.е. форматы PNG, GIF или JPEG). Другие элементы canvas на этой же странице также могут быть использованы как источники изображений.

Импортирование картинок

Обычно импортирование картинок происходит в два этапа:
Сначала нам нужна ссылка на объект JavaScript Image или на другой элемент canvas, как источник. Невозможно использовать картинки просто передавая URL/путь до них.
Потом мы рисуем картинку на canvas, используя функцию drawImage.
Давайте сначала рассмотрим первый шаг. Доступны четыре варианта:

Использование изображений с текущей страницы

Мы можем получить доступ к любому изображению на странице, используя коллекцию document.images, метод document.getElementsByTagName , либо, если нам известен атрибут ID изображения, метод document.getElementById.

Использование другого элемента canvas
Как и к нормальным изображениям, мы можем получить доступ к элементам canvas через метод document.getElementsByTagName или метод document.getElementById. Перед тем, как вы будете использовать canvas как источник, убедитесь, что вы что-либо уже нарисовали на нём.
Один из наиболее практичных вариантов использования этого метода - создание маленьких превью-картинок для больших изображений canvas.

Создание изображения с нуля
Ещё один вариант - создавать в нашем скрипте новые объекты Image. Главный недостаток такого подхода в том, что если мы не хотим, чтобы наш скрипт "зависал" посередине в ожидании загрузки изображения, нам нужно средство предварительной загрузки изображений.

Чтобы создать новый объект-картинку, мы делаем следующее:
var img = new Image(); // Создаём новый объект Image
img.src = 'myImage.png'; // Устанавливаем путь к источнику


Когда этот скрипт выполняется, изображение начинает загружаться. Если к моменту исполнения инструкции drawImage загрузка ещё не закончилась, скрипт "подвисает" до её окончания. Если вы не хотите, чтобы такое случилось, используйте обработчик события onload:
var img = new Image(); // Создать новый объект Image
img.src = 'myImage.png'; // Установить путь к источнику
img.onload = function(){
// выполнить drawImage здесь
}


Если вы используете всего одно внешнее изображение, то такой подход хорош, но при работе более чем с одной картинкой лучше придумать что-нибудь похитрее. Рассмотрение различных тактик предварительной загрузки изображений выходит за рамки этой статьи, но здесь вы можете получить уже законченное решение.

Вложение изображения с помощью data: url
Еще один возможный способ вставки изображений - это использование data: url. Data urls позволяют вам полностью определить изображение непосредственно в коде как Base64-кодированную строку. Преимущество данного метода в том, что изображение будет доступно немедленно без каких либо дополнительных запросов к серверу. (Еще одно преимущество заключается в возможности инкапсулировать все Ваши CSS, JavaScript, HTML и изображения в одном файле, что значительно упрощает перемещение всего документа.) Однако изображения, определенные таким способом, не кэшируются, и строковое представление больших изображений может быть довольно длинным.

Рисование изображений

После того как мы указали ссылку на источник мы можем использовать метод drawImage чтобы присвоить изображению положение на холсте. Как мы увидим дальше метод drawImage имеет три вохможных вариацнии. Основная форма метода выглядит таким образом.

drawImage(image, x, y)

Где image это идентификатор изображения а, x и y положение на холсте по оси ординат и абсцис соответственно.

Пример 1 использования изображения

В следующем примере мы будем использовать внешнее изображение как фон для небольшого графика. Использование фона позволяет сделать скрипт значительно быстрее. Здесь мы используем только одно изображение поэтому используем событие onload для запуска функции. С помощью метода drawImage мы позиционируем фон на координатах (0,0) тоесть в верхнем левом углу холста

  function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image();
img.onload = function(){
ctx.drawImage(img,0,0);
ctx.beginPath();
ctx.moveTo(30,96);
ctx.lineTo(70,66);
ctx.lineTo(103,76);
ctx.lineTo(170,15);
ctx.stroke();
}
img.src = 'images/backdrop.png';
}

график

Масштабирование

Второй вариант использования метода drawImage отличается от первого тем что в нем присутствуют параметры масштабного отображения на холсте.

drawImage(image, x, y, width, height)

Где width и height ширина и высота изображения на холсте.

Пример 2 использования изображения

В этом примере мы используем одно изображение как обои чтобы повторить его на холсте несколько раз. Это осуществляется с помощью цикла и размещения масштабных изображений на разных позициях. В этом примере первый цикл for отсчитывает ряды а второй цикл for отсчитывает колоны. Изображение равно одной третьей исходного изображения (50x38 пикселей).

Примечание: При масштабировании изображение теряет качество, а текст теряет очертания.

 function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image();
img.onload = function(){
for (i=0;i<4;i++){
for (j=0;j<3;j++){
ctx.drawImage(img,j*50,i*38,50,38);
}
}
}
img.src = 'images/rhino.jpg';
}

пример
носорог

Разрезание изображений

Третий и последный типа метода drawImage включает 8 новых параметров. Мы будем использовать этот метод чтобы отбражать часть изображения на холсте

drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

Первый параметр image, определяет идентификатор изображения. Следующие 2 элемента определяют положения верхнего левого угла вырезаного изображения относительного исходного положения а, следующие два параметра определяют высоту и ширину вырезаного изображения. Следующие два параметра определяют положение изображения на холсте а, следующие два параметра масштаб изображения.

Разрезание можно использовать чтобы обьеденить все элементы в один файл, что позволяет ускорить работу сайта а, также не загружать все элементы по отдельности.

Пример 3 использования изображения
В этом примере мы будем вырезать голову носорога и вставлять ее в рамку с альфа каналом.

В этом примере мы используем другой подход к загрузке изображений, чем в примере выше. Мы просто размечаем изображения непосредственно в HTML документе и используем правила CSS, чтобы скрыть их. Обоим изображениям мы присваиваем атрибут id чтобы их было легче выбрать. Скрипт сам по себе очень прост. Сначала мы вырезаем голову носорого и помещаем ее на холст а, затем помещаем изображение рамки на холст.
схема
носорог
рамка
function draw() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

// Draw slice
ctx.drawImage(document.getElementById('source'),
33,71,104,124,21,20,87,104);

// Draw frame
ctx.drawImage(document.getElementById('frame'),0,0);
}

Пример галереи

В финальном примере мы сделаем простую галерею изображений. Галерея состоит из таблицы содержащей несколько изображений. При загрузке страницы каждое изображение вставляется на холст а, затем и рамка вокруг него.

Код ниже, должен говорить сам за себя. Мы используем цикл и добавляем новые элементы на холст. Наверное, единственное, что можно отметить, для тех, кто не очень знаком с DOM, является использование insertBefore метода. insertBefore это метод родительского узла (ячейки таблицы) в который мы хотим вставить наш новый узел (холст).

function draw() {

// Loop through all images
for (i=0;i<document.images.length;i++){

// Don't add a canvas for the frame image
if (document.images[i].getAttribute('id')!='frame'){

// Create canvas element
canvas = document.createElement('CANVAS');
canvas.setAttribute('width',132);
canvas.setAttribute('height',150);

// Insert before the image
document.images[i].parentNode.insertBefore(canvas,document.images[i]);

ctx = canvas.getContext('2d');

// Draw image to canvas
ctx.drawImage(document.images[i],15,20);

// Add frame
ctx.drawImage(document.getElementById('frame'),0,0);
}
}
}


Про тег html canvas - 2

Источник: http://black-zorro.com/mediawiki/Про_тег_html_canvas_-_2

Этой статьей я продолжу серию материалов посвященных тегу canvas – специальному расширению, поддерживаемому передовыми браузерами (opera, firefox) и позволяющему вам, используя вызовы функций javascript, рисовать растровые изображения в окне браузера. В прошлый раз я рассказал об методике внедрения тега canvas внутрь веб-страницы, о том как сделать fallback – механизм позволяющий корректно обработать ситуацию когда страница открывается не поддерживающим возможности canvas браузером (это про тебя дорогой internet explorer). Так же мы рисовали простые фигуры: линии, прямоугольники, эллипсы и дуги. Сегодня мы поговорим о работе с преобразованиями, управлении режимами наложения изображений при отрисовке.

Что такое преобразования? В мире компьютерной графики под ними понимают простейшие математические операции, позволяющие вам перед началом, собственно, рисования выполнить некоторое изменение в системе координат холста. По умолчанию ваш холст имеет точку начала координат (0,0), а масштаб оперирует минимальной цифрой в 1 пиксель, оси же направлены строго под прямым углом друг к другу – одним словом, то, что мы называем прямоугольной декартовой системой координат.

И, соответственно, перед началом рисования вы можете изменить эту систему координат, использую вызовы специальных функций в составе контекста рисования. За это отвечают следующие функции. Translate – эта функция получает в качестве параметра координаты куда переместится центр координат (СК). Функция rotate позволяет выполнить вращение СК. Вращение выполняется против часовой стрелки. Последняя функция преобразования ск – scale - масштабирование. Эта функция получает два параметра x,y, которые будут играть роль коэффициентов масштабирования. Очевидно, что если вы рисуете что-то достаточно сложное, то вы будете многократно вызывать фукцнии изменения свойств СК: вы будете менять параметры, а затем возвращаться в некоторое исходное состояние. Чтобы не запутаться какие свойства у вас сейчас установлены (на самом деле вы можете изменять гораздо большее количество параметров управляющих особенностями рисования, чем я перечислил выше). Так вот, чтобы не забыть какие свойства установлены для canvas, вы можете применять две парные функции: save и restore. Эти функции не получают ни каких параметров. Функция save сохраняет текущее состояние canvas – все его свойства. А функция restore – наоборот, восстанавливает эти параметры. Работа функций сохранить/восстановить построена по принципу стека. Вы можете вызывать фукнцию save любое количество раз и все состояния будут сохраняться укладываясь в некую виртуальную стопку. Каждый же вызов restore будет брать с вершины этой стопки сохраненный набор параметров. Соответственно, вы не можете вызывать restore большее количество раз, чем было положено туда состояний с помощью save.

Теперь давайте сделаем парочку примеров на каждую из этих функций. Начнем с перемещения центра СК. Для пример нарисуем шахматную доску состоящую из множества черных квадратиков на белом фоне. Каждый квадрат будет рисоваться размером в 10px, и иметь координаты 0,0. естественно, чтобы квадратики не накладывались друг на друга я вынужден перед рисованием каждого из них передвигать центр СК.
<html>
<head>
<!-именно здесь будут размещаться функции, которые рисуют на canvas-->
<script language="javascript">
function draw (){
var canvas = document.getElementById('canva');
if (!canvas.getContext) return;

var ctx = canvas.getContext('2d');

// заливаем доску белым цветом
ctx.fillStyle = "rgb(255,255,255)";
ctx.fillRect (0, 0, 400, 400);

for (var i = 0; i < 8; i++)
for (var j = 0; j < 8; j++){
if ( (i +j ) % 2 == 0) continue;

ctx.save ();// сохраняем контекст рисования
ctx.translate (i*50 , j*50);// смещаем центр СК
ctx.fillStyle = "rgb(0,0,0)";
ctx.fillRect (0, 0, 50, 50);


ctx.restore ();// восстанавливаем свойства контекста рисования

}

}
</script>

</head>
<body>
<canvas id="canva" width="400" height="400" style="border: 1px solid black;">
<img src="lesson-04-images/no_canvas_sorry.jpg" />
</canvas>
<br />
<button onclick="draw()">draw!</button><!-эта кнопка вызывает некоторую функцию рисования -->
</body>

</html>

Результат работы показан на рис. 1.
пример 1
Для задачи демонстрации преобразования вращения и масштабирования я решил выбрать единый пример – спиральный квадрат. Это изображение множества вложенных друг в друга квадратов, каждый из которых на очередном шаге вложенности уменьшается в размере и вращается вокруг своей оси не некоторый угол. Основу формул я взял с сайта http://fractalworld.xaoc.ru/article/kineskop.html. И код у меня (благодаря преобразованиям) получился более компактным. Вкратце идея в том, что до начала цикла я смещаю центр координат в точку (200,200) – геометрический центр области canvas. Затем в цикле я рисую прямоугольник из точки (-200,-200) и размером (400,400). Эффект вращения и ухода в даль достигается за счет того что предварительно я вращаю ось на небольшой угол PI/20, а также уменьшаю коэффициент масштабирования. Результат работы показан на рис. 2.
пример 2
Внимание: в случае множественных последовательных операций вращения, масштабирования и смещения эффект накапливается.
<html>
<head>
<!-именно здесь будут размещаться функции, которые рисуют на canvas-->
<script>
function draw (){
var canvas = document.getElementById('canva');
if (!canvas.getContext) return;

var ctx = canvas.getContext('2d');

// заливаем доску белым цветом
ctx.fillStyle = "rgb(255,255,255)";
ctx.fillRect (0, 0, 400, 400);

var DELTA_ANGLE = Math.PI / 20;
ctx.translate (200,200);

for (var i = 1; i < 21; i++){
ctx.rotate (DELTA_ANGLE);
var coeff = Math.sin(Math.PI/4) / Math.sin(3*Math.PI/4 - DELTA_ANGLE);
ctx.scale (coeff,coeff);
ctx.strokeRect(-200,-200, 400,400);
}

}
</script>

</head>
<body>
<canvas id="canva" width="400" height="400" style="border: 1px solid black;">
<img src="lesson-04-images/no_canvas_sorry.jpg" />
</canvas>
<br />
<button onclick="draw()">draw!</button><!-эта кнопка вызывает некоторую функцию рисования -->
</body>

</html>


Теперь, как я и обещал, переходим к работе с внешними изображениями. Вы можете взять существующую картинку, загрузить ее и отрисовать на canvas. Существует несколько версий функции drawImage отличающихся параметрами. Так в простейшем случае достаточно указать:
drawImage(image, x, y)

В качестве ее параметров следует указать ссылку на объект изображения, затем координаты где будет располагаться левый верхний угол копируемой картинки. Возможно при отрисовке изображения выполнить ее масштабирование – в этом случае используйте функцию
drawImage(image, x, y, width, height)

Здесь добавились два новых параметра – размер рисуемого изображения. И это еще не все: есть версия функции с еще четырьмя дополнительными параметрами:
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

С их помощью можно указать какой фрагмент исходного изображения должен быть вырезан для последующей отрисовки (его координаты и размеры). Обратите внимание на то что параметры располагаются уже в несколько ином порядке: первыми идут координаты и размеры области вырезаемой из исходной картинки и только затем мы указываем координаты и размеры области куда это изображение будет помещено. Независимо от того какую версию функции вы используете, ссылка на изображение может быть взята как одна из множества картинок уже внедренных и (и это главное) загруженных на странице. Либо, как вариант, вы можете выполнить динамическую загрузку изображения – в этом случае вам необходимо будет создать некоторый код, который отслеживал бы прогресс загрузки картинки и только после того как все данные будут готовы, запускал процесс отрисовки. В первом примере я показываю как картинка с лисой рисуется в трех вариантах: оригинальном, масштабированном и вырезанном фрагменте.
пример 3
Пример кода:
<html>
<head>
<!-именно здесь будут размещаться функции, которые рисуют на canvas-->
<script>
function draw (){
var canvas = document.getElementById('canva');
if (!canvas.getContext) return;

var ctx = canvas.getContext('2d');

// заливаем доску белым цветом
ctx.fillStyle = "rgb(255,255,255)";
ctx.fillRect (0, 0, 400, 400);

var img_fox = document.getElementById ('img_fox');
//получаем ссылку на изображение
if (! img_fox){
// выполняем проверку того, что изображение действительно было найдено
alert ('изображение не найдено');
return;
}
// рисуем изображение в его естественном масштабе
ctx.drawImage (img_fox , 0, 0);

// а теперь рисуем изображение немного в стороне и в уменьшенном размере
ctx.drawImage (img_fox , 100, 250, 100, 75);

// последний прием, когда я вырезаю из изображения некоторый фрагмент
ctx.drawImage (img_fox , 100, 0, 80, 100, 250,50, 80, 100);

}
</script>

</head>
<body>
<canvas id="canva" width="400" height="400" style="border: 1px solid black;">
<img src="lesson-04-images/no_canvas_sorry.jpg" />
</canvas>
<br />
<button onclick="draw()">draw!</button><!-эта кнопка вызывает некоторую функцию рисования -->

<img src="lesson-04-images/foxes_008.jpg" id="img_fox" />
</body>

</html>


А в этом примере кода я в отличие от предыдущего варианта (тогда изображение бралось уже встроенное внутрь страницы) загружаю оригинальную картинку динамически.
<html>
<head>
<!-именно здесь будут размещаться функции, которые рисуют на canvas-->
<script>
function draw (){
var canvas = document.getElementById('canva');
if (!canvas.getContext) return;

var ctx = canvas.getContext('2d');

// заливаем доску белым цветом
ctx.fillStyle = "rgb(255,255,255)";
ctx.fillRect (0, 0, 400, 400);


var img_fox = new Image ();
// привязываем обработчик события - загрузка картинки завершена
img_fox.onload = function () {
// рисуем изображение в его естественном масштабе
ctx.drawImage (this , 0, 0);
// а теперь рисуем изображение немного в стороне и в уменьшенном размере
ctx.drawImage (this , 100, 250, 100, 75);
// последний прием, когда я вырезаю из изображения некоторый фрагмент
ctx.drawImage (this , 100, 0, 80, 100, 250,50, 80, 100);
};
img_fox.src = 'foxes_008.jpg';
}
</script>

</head>
<body>
<canvas id="canva" width="400" height="400" style="border: 1px solid black;">
<img src="lesson-04-images/no_canvas_sorry.jpg" />
</canvas>
<br />
<button onclick="draw()">draw!</button><!-эта кнопка вызывает некоторую функцию рисования -->


</body>

</html>


Интересно, что вместо изображения можно ссылаться на другой элемент canvas. Также существует нестандартное расширение (читай, поддерживаемое только mozilla firefox) которое умеет рисовать на canvas содержимое некоторого окна (например, плавающего фрейма). Работает данная функция не всегда, а только для исполняющихся в среде chrome приложений (читай, обычная веб-страница так делать не умеет – только расширения/плагины для firefox).

Комбинирование изображений.

Идея в том, что когда вы рисуете на canvas некоторое изображение, возникает вопрос как оно должно взаимодействовать с тем изображением, что уже есть. У объекта canvas есть специальное свойство: globalCompositeOperation . Ему можно присваивать набор предопределенных значений. Следующую таблицу я аккуратно позаимствовал на сайте http://developer.mozilla.org/en/docs/Canvas_tutorial. Я решил, что не стоит коверкать эту прекрасную таблицу своими заумствованиями и просто перевел текст. В общем, смотрите ниже:

Таблица-справочник возможных значений переменной globalCompositeOperation с примерами.
source-over (default)

Это режим по-умолчанию когда рисуется новая картинка поверху старой.
1
destination-over

Новое изображение рисуется под старым
2
source-in

Новое изображение рисуется только в тех местах, где старое и новое изображение пересекаются. Все остальное становится прозрачным.
3
destination-in

Существующее изображение остается только в тех местах, где старое и новое изображение пересекаются. Все остальное становится прозрачным.
4
source-out

Новое изображение рисуется только в тех местах, где оно не пересекается со старым изображением.
5
destination-out

Старое изображение сохраняется в тех местах, где оно не пересекается с новым изображением.
6
source-atop

Новое изображение рисуется только в тех местах где оно пересекается с уже существующим изображением на canvas.
7
destination-atop

Существующее изображение остается только в тех местах, где оно пересекается с новым изображением. Новая картинка рисуется позади старого содержимого.
8
lighter

В тех местах где старое и новое изображение пересекаются идет вычисление нового цвета как суммы двух цветов картинок участников.
9
darker

В тех местах где старое и новое изображение пересекаются идет вычисление нового цвета как разницы между двумя цветами картинок участников.
10
xor

Изображение становится прозрачным в том месте где происходит пересечение старой и новой картинок. В остальных же местах изображение рисуется как обычно.
11
copy

Рисуется новое изображение, а все что было до этого убирается.
12