title: Zooming And undo-redo on canvas not working properly
body: <p>I am trying to implement a paint bucket tool with undo and redo functionality. The issue is that undo and redo are working properly the first time, but when I do undo redo multiple times, the code fails. Can anyone help me figure the issue out? This is my complete code. You can just copy paste and it will work at your end.</p>
  
  <pre><code>&lt;!DOCTYPE html&gt;
  &lt;html&gt;
      &lt;head&gt;
          &lt;title&gt;Painitng&lt;/title&gt;
          &lt;style&gt;
              body {
                  width: 100%;
                  height: auto;
                  text-align: center;
              }
              .colorpick {
                  widh: 100%;
                  height: atuo;
              }
              .pick {
                  display: inline-block;
                  width: 30px;
                  height: 30px;
                  margin: 5px;
                  cursor: pointer;
              }
              canvas {
                  border: 2px solid silver;
              }
          &lt;/style&gt;
      &lt;/head&gt;
      &lt;body&gt;
          &lt;button id="zoomin"&gt;Zoom In&lt;/button&gt;
          &lt;button id="zoomout"&gt;Zoom Out&lt;/button&gt;
          &lt;button onclick="undo()"&gt;Undo&lt;/button&gt;
          &lt;button onclick="redo()"&gt;Redo&lt;/button&gt;
          &lt;div id="canvasDiv"&gt;&lt;/div&gt;
          &lt;script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js"&gt;&lt;/script&gt; 
          &lt;script type="text/javascript"&gt;
              var colorYellow = {
                  r: 255,
                  g: 207,
                  b: 51
              };
              var context;
              var canvasWidth = 500;
              var canvasHeight = 500;
              var myColor = colorYellow;
              var curColor = myColor;
              var outlineImage = new Image();
              var backgroundImage = new Image();
              var drawingAreaX = 0;
              var drawingAreaY = 0;
              var drawingAreaWidth = 500;
              var drawingAreaHeight = 500;
              var colorLayerData;
              var outlineLayerData;
              var totalLoadResources = 2;
              var curLoadResNum = 0;
              var undoarr = new Array();
              var redoarr = new Array();
              var uc = 0;
              var rc = 0;
  
              // Clears the canvas.
              function clearCanvas() {
                  context.clearRect(0, 0, context.canvas.width, context.canvas.height);
              }
  
              function undo() {
                  if (undoarr.length &lt;= 0)
                      return;
  
                  if (uc==0) {
                      redoarr.push(undoarr.pop());
                      uc = 1;
                  }
                  var a = undoarr.pop();
                  colorLayerData = a;
                  redoarr.push(a);
                  clearCanvas();
                  context.putImageData(a, 0, 0);
                  context.drawImage(backgroundImage, 0, 0, canvasWidth, canvasHeight);
                  context.drawImage(outlineImage, 0, 0, drawingAreaWidth, drawingAreaHeight);
                  console.log(undoarr);
              }
  
              function redo() {
                  if (redoarr.length &lt;= 0)
                      return;
                  if (rc==0) {
                      undoarr.push(redoarr.pop());
                      rc = 1;
                  }
                  var a = redoarr.pop();
                  colorLayerData = a;
                  undoarr.push(a);
                  clearCanvas();
                  context.putImageData(a, 0, 0);
                  context.drawImage(backgroundImage, 0, 0, canvasWidth, canvasHeight);
                  context.drawImage(outlineImage, 0, 0, drawingAreaWidth, drawingAreaHeight);
                  console.log(redoarr);
              }
              // Draw the elements on the canvas
              function redraw() {
                  uc = 0;
                  rc = 0;
                  var locX,
                          locY;
  
                  // Make sure required resources are loaded before redrawing
                  if (curLoadResNum &lt; totalLoadResources) {
                      return; // To check if images are loaded successfully or not.
                  }
  
                  clearCanvas();
                  // Draw the current state of the color layer to the canvas
                  context.putImageData(colorLayerData, 0, 0);
  
                  undoarr.push(context.getImageData(0, 0, canvasWidth, canvasHeight));
                  console.log(undoarr);
                  redoarr = new Array();
                  // Draw the background
                  context.drawImage(backgroundImage, 0, 0, canvasWidth, canvasHeight);
  
                  // Draw the outline image on top of everything. We could move this to a separate 
                  //   canvas so we did not have to redraw this everyime.
                  context.drawImage(outlineImage, 0, 0, drawingAreaWidth, drawingAreaHeight);
  
  
              }
              ;
  
              function matchOutlineColor(r, g, b, a) {
  
                  return (r + g + b &lt; 100 &amp;&amp; a === 255);
              }
              ;
  
              function matchStartColor(pixelPos, startR, startG, startB) {
  
                  var r = outlineLayerData.data[pixelPos],
                          g = outlineLayerData.data[pixelPos + 1],
                          b = outlineLayerData.data[pixelPos + 2],
                          a = outlineLayerData.data[pixelPos + 3];
  
                  // If current pixel of the outline image is black
                  if (matchOutlineColor(r, g, b, a)) {
                      return false;
                  }
  
                  r = colorLayerData.data[pixelPos];
                  g = colorLayerData.data[pixelPos + 1];
                  b = colorLayerData.data[pixelPos + 2];
  
                  // If the current pixel matches the clicked color
                  if (r === startR &amp;&amp; g === startG &amp;&amp; b === startB) {
                      return true;
                  }
  
                  // If current pixel matches the new color
                  if (r === curColor.r &amp;&amp; g === curColor.g &amp;&amp; b === curColor.b) {
                      return false;
                  }
  
                  return true;
              }
              ;
  
              function colorPixel(pixelPos, r, g, b, a) {
                  colorLayerData.data[pixelPos] = r;
                  colorLayerData.data[pixelPos + 1] = g;
                  colorLayerData.data[pixelPos + 2] = b;
                  colorLayerData.data[pixelPos + 3] = a !== undefined ? a : 255;
              }
              ;
  
              function floodFill(startX, startY, startR, startG, startB) {
                  var newPos,
                          x,
                          y,
                          pixelPos,
                          reachLeft,
                          reachRight,
                          drawingBoundLeft = drawingAreaX,
                          drawingBoundTop = drawingAreaY,
                          drawingBoundRight = drawingAreaX + drawingAreaWidth - 1,
                          drawingBoundBottom = drawingAreaY + drawingAreaHeight - 1,
                          pixelStack = [[startX, startY]];
  
                  while (pixelStack.length) {
  
                      newPos = pixelStack.pop();
                      x = newPos[0];
                      y = newPos[1];
  
                      // Get current pixel position
                      pixelPos = (y * canvasWidth + x) * 4;
  
                      // Go up as long as the color matches and are inside the canvas
                      while (y &gt;= drawingBoundTop &amp;&amp; matchStartColor(pixelPos, startR, startG, startB)) {
                          y -= 1;
                          pixelPos -= canvasWidth * 4;
                      }
  
                      pixelPos += canvasWidth * 4;
                      y += 1;
                      reachLeft = false;
                      reachRight = false;
  
                      // Go down as long as the color matches and in inside the canvas
                      while (y &lt;= drawingBoundBottom &amp;&amp; matchStartColor(pixelPos, startR, startG, startB)) {
                          y += 1;
  
                          colorPixel(pixelPos, curColor.r, curColor.g, curColor.b);
  
                          if (x &gt; drawingBoundLeft) {
                              if (matchStartColor(pixelPos - 4, startR, startG, startB)) {
                                  if (!reachLeft) {
                                      // Add pixel to stack
                                      pixelStack.push([x - 1, y]);
                                      reachLeft = true;
                                  }
  
                              } else if (reachLeft) {
                                  reachLeft = false;
                              }
                          }
  
                          if (x &lt; drawingBoundRight) {
                              if (matchStartColor(pixelPos + 4, startR, startG, startB)) {
                                  if (!reachRight) {
                                      // Add pixel to stack
                                      pixelStack.push([x + 1, y]);
                                      reachRight = true;
                                  }
                              } else if (reachRight) {
                                  reachRight = false;
                              }
                          }
  
                          pixelPos += canvasWidth * 4;
                      }
                  }
              }
              ;
  
              // Start painting with paint bucket tool starting from pixel specified by startX and startY
              function paintAt(startX, startY) {
  
                  var pixelPos = (startY * canvasWidth + startX) * 4,
                          r = colorLayerData.data[pixelPos],
                          g = colorLayerData.data[pixelPos + 1],
                          b = colorLayerData.data[pixelPos + 2],
                          a = colorLayerData.data[pixelPos + 3];
  
                  if (r === curColor.r &amp;&amp; g === curColor.g &amp;&amp; b === curColor.b) {
                      // Return because trying to fill with the same color
                      return;
                  }
  
                  if (matchOutlineColor(r, g, b, a)) {
                      // Return because clicked outline
                      return;
                  }
  
                  floodFill(startX, startY, r, g, b);
  
                  redraw();
              }
              ;
  
              // Add mouse event listeners to the canvas
              function createMouseEvents() {
  
                  $('#canvas').mousedown(function (e) {
                      // Mouse down location
                      var mouseX = e.pageX - this.offsetLeft,
                              mouseY = e.pageY - this.offsetTop;
  
                      if ((mouseY &gt; drawingAreaY &amp;&amp; mouseY &lt; drawingAreaY + drawingAreaHeight) &amp;&amp; (mouseX &lt;= drawingAreaX + drawingAreaWidth)) {
                          paintAt(mouseX, mouseY);
                      }
                  });
              }
              ;
  
              resourceLoaded = function () {
  
                  curLoadResNum += 1;
                  //if (curLoadResNum === totalLoadResources) {
                  createMouseEvents();
                  redraw();
                  //}
              };
  
              function start() {
  
                  var canvas = document.createElement('canvas');
                  canvas.setAttribute('width', canvasWidth);
                  canvas.setAttribute('height', canvasHeight);
                  canvas.setAttribute('id', 'canvas');
                  document.getElementById('canvasDiv').appendChild(canvas);
  
                  if (typeof G_vmlCanvasManager !== "undefined") {
                      canvas = G_vmlCanvasManager.initElement(canvas);
                  }
                  context = canvas.getContext("2d");
                  backgroundImage.onload = resourceLoaded();
                  backgroundImage.src = "images/t1.png";
  
                  outlineImage.onload = function () {
                      context.drawImage(outlineImage, drawingAreaX, drawingAreaY, drawingAreaWidth, drawingAreaHeight);
  
                      try {
                          outlineLayerData = context.getImageData(0, 0, canvasWidth, canvasHeight);
                      } catch (ex) {
                          window.alert("Application cannot be run locally. Please run on a server.");
                          return;
                      }
                      clearCanvas();
                      colorLayerData = context.getImageData(0, 0, canvasWidth, canvasHeight);
                      resourceLoaded();
                  };
                  outlineImage.src = "images/d.png";
              }
              ;
  
              getColor = function () {
  
              };
  
          &lt;/script&gt; 
          &lt;script type="text/javascript"&gt; $(document).ready(function () {
                  start();
              });&lt;/script&gt; 
          &lt;script language="javascript"&gt;
              $('#zoomin').click(function () {
                  if ($("#canvas").width()==500){
                  $("#canvas").width(750);
                  $("#canvas").height(750);
                  var ctx = canvas.getContext("2d");
                  ctx.drawImage(backgroundImage, 0, 0, 749, 749);
                  ctx.drawImage(outlineImage, 0, 0, 749, 749);
                  redraw();
                   } else if ($("#canvas").width()==750){
  
                  $("#canvas").width(1000);
                  $("#canvas").height(1000);
                  var ctx = canvas.getContext("2d");
                  ctx.drawImage(backgroundImage, 0, 0, 999, 999);
                  ctx.drawImage(outlineImage, 0, 0, 999, 999);
                  redraw();
                   }
              });
              $('#zoomout').click(function () {
                  if ($("#canvas").width() == 1000) {
  
                  $("#canvas").width(750);
                  $("#canvas").height(750);
                  var ctx = canvas.getContext("2d");
                  ctx.drawImage(backgroundImage, 0, 0, 749, 749);
                  ctx.drawImage(outlineImage, 0, 0, 749, 749);
                  redraw();
                  } else if ($("#canvas").width() == 750) {
  
                  $("#canvas").width(500);
                  $("#canvas").height(500);
                  var ctx = canvas.getContext("2d");
                  ctx.drawImage(backgroundImage, 0, 0, 499, 499);
                  ctx.drawImage(outlineImage, 0, 0, 499, 499);
                  redraw();
                  }
              });
          &lt;/script&gt;
          &lt;div class="colorpick"&gt;
              &lt;div class="pick" style="background-color:rgb(150, 0, 0);" onclick="hello(this.style.backgroundColor);"&gt;&lt;/div&gt;
              &lt;div class="pick" style="background-color:rgb(0, 0, 152);" onclick="hello(this.style.backgroundColor);"&gt;&lt;/div&gt;
              &lt;div class="pick" style="background-color:rgb(0, 151, 0);" onclick="hello(this.style.backgroundColor);"&gt;&lt;/div&gt;
              &lt;div class="pick" style="background-color:rgb(255, 0, 5);" onclick="hello(this.style.backgroundColor);"&gt;&lt;/div&gt;
              &lt;div class="pick" style="background-color:rgb(255, 255, 0);" onclick="hello(this.style.backgroundColor);"&gt;&lt;/div&gt;
              &lt;div class="pick" style="background-color:rgb(0, 255, 255);" onclick="hello(this.style.backgroundColor);"&gt;&lt;/div&gt;
              &lt;div class="pick" style="background-color:rgb(255, 0, 255);" onclick="hello(this.style.backgroundColor);"&gt;&lt;/div&gt;
              &lt;div class="pick" style="background-color:rgb(255, 150, 0);" onclick="hello(this.style.backgroundColor);"&gt;&lt;/div&gt;
              &lt;div class="pick" style="background-color:rgb(255, 0, 150);" onclick="hello(this.style.backgroundColor);"&gt;&lt;/div&gt;
              &lt;div class="pick" style="background-color:rgb(0, 255, 150);" onclick="hello(this.style.backgroundColor);"&gt;&lt;/div&gt;
              &lt;div class="pick" style="background-color:rgb(150, 0, 255);" onclick="hello(this.style.backgroundColor);"&gt;&lt;/div&gt;
              &lt;div class="pick" style="background-color:rgb(0, 150, 255);" onclick="hello(this.style.backgroundColor);"&gt;&lt;/div&gt;
          &lt;/div&gt;
          &lt;script&gt;
              function hello(e) {
                  var rgb = e.replace(/^(rgb|rgba)\(/, '').replace(/\)$/, '').replace(/\s/g, '').split(',');
                  myColor.r = parseInt(rgb[0]);
                  myColor.g = parseInt(rgb[1]);
                  myColor.b = parseInt(rgb[2]);
                  curColor = myColor;
                  console.log(curColor);
              }
          &lt;/script&gt;
      &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>