4

First off, sorry about the bad title, I couldn't think of a better way to describe what I was trying to do. I have an HTML canvas, which, for argument's sake, is x pixels wide and y pixels tall. I have been trying to write a code that takes the location in the array of the canvas' image data of two pixels that are on lines z and z + 1 and fill in all the pixels in the higher row between the two pixels a certain color. I'm afraid I may not have made much sense, so here's a diagram:

A pair of pixels

Sorry about the poor graphics, but assume each rectangle is a pixel. The program should take in the first value for each of the black pixels (each is stored as r,g,b,a, the program gets the location of the r in the array representing the canvas' image data), and stores the r value for the lower pixel as bottomPixel and the higher one as topPixel. In this case, bottomPixel = 124 and topPixel = 112 It should use this to fill all pixels between the two base pixels a certain color. For example, using the previous pixel locations, the red pixels in the following picture should be colored in, but the blue one should not.

Colored canvas

Here is the code I have: (Assume that the canvas has an Id "Canvas" and is 6px wide by 10px tall)

var cnvs        = document.getElementById("Canvas");
cnvs.height     = 10; //This height and width is here as an example.
cnvs.width      = 6;
var cont        = cnvs.getContext("2d");
var environment = cont.getImageData(0,0,6,10);
var bottomPixel = 124;//Not neccesarily 124 or 112, just example values
var topPixel    = 112;
if ( bottomPixel - topPixel > 6*4 ) //If bottomPixel is to the right of topPixel
{
   for ( var i = 0 ; i < ((bottomPixel-6*4)-topPixel)/4 ; i++ )
   {
      var index = topPixel + i * 4;
      environment.data[index]      = 0;
      environment.data[index + 1 ] = 255;
      environment.data[index + 2 ] = 0;
      environment.data[index + 3 ] = 255;
   }
}
if ( bottomPixel - topPixel > 6*4 ) //If bottomPixel is to the left of topPixel
{
   for ( var i = 0 ; i < (topPixel-(bottomPixel-6*4))/4; i++ )
   {
      var index = topPixel - i * 4;
      environment.data[index]      = 0;
      environment.data[index + 1 ] = 255;
      environment.data[index + 2 ] = 0;
      environment.data[index + 3 ] = 255;
   }
}

I'd like to know why my code isn't doing what I previously described. If anything here needs clarification, please leave a comment. Thanks!

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Bill Bllson
  • 179
  • 1
  • 1
  • 14
  • Are you trying to [rasterize a line](https://en.wikipedia.org/wiki/Line_drawing_algorithm)? – Kristján Nov 17 '15 at 05:13
  • I might be, I admit I didn't understand that article, though. – Bill Bllson Nov 17 '15 at 05:16
  • 1
    It might help people fid the problem if you [edit] your question to describe what the current code is doing that's wrong. – Kristján Nov 17 '15 at 05:19
  • Yes, I think I am, albeit using a formula that the article doesn't seem to mention. – Bill Bllson Nov 17 '15 at 05:21
  • @Kristján I don't have time at the moment, but I'll try to do so some time tomorrow, thanks. – Bill Bllson Nov 17 '15 at 05:23
  • Why is `[1,5],[2,4],[3,4],[4,4]` your desired path when `[1,5],[2,5],[3,5],[4,4]` or `[1,5],[2,5],[3,4],[4,4]` are not desirable? If you want a non-aliased set of points connecting 2 endpoints then you should check out Bresenham's line algorithm: http://stackoverflow.com/questions/25277023/complete-solution-for-drawing-1-pixel-line-on-html5-canvas/25279040#25279040. – markE Nov 17 '15 at 05:43
  • The line `var imdt = cont.getImageData(0,0,6,10);` has two errors. First one is that `imdt` should be `environment` and `(0,0,6,10)` should be `(0,0,10,6)`. Also the `newColorList` array and `z9` variables do not exist. – wolfhammer Nov 18 '15 at 03:42

1 Answers1

2

This is a method that works on the point coordinates and uses a the setPixel function to modify imageData. I'm using blue for start and black for end. You'll need to adjust for your exact condition but you can use setPixel to allow for direct x and y edits on the imageData.

update

I've included an alternate line method and your line method. There is also an animation that will help you find errors.

function ptIndex(p, w) {
  return ((p.x|0) + ((p.y|0) * w)) * 4;
}

function setPixel(p, w, d, rgba) {
  var i = ptIndex(p, w);
  d[i] = rgba.r;
  d[i + 1] = rgba.g;
  d[i + 2] = rgba.b;
  d[i + 3] = rgba.a;
}


function yourLine(p1, p2, w, d, rgba) {
  var cnvs = document.getElementById("Canvas");

  var bottomPixel = ptIndex(p1, w);
  var topPixel = ptIndex(p2, w)
  if (bottomPixel - topPixel > w * 4) //If bottomPixel is to the right of topPixel
  {
    for (var i = 0; i < ((bottomPixel - w * 4) - topPixel) / 4; i++) {
      var index = topPixel + i * 4;
      d[index] = rgba.r;
      d[index + 1] = rgba.g;
      d[index + 2] = rgba.b;
      d[index + 3] = rgba.a
    }
  }
  if (bottomPixel - topPixel > w * 4) //If bottomPixel is to the left of topPixel
  {
    for (var i = 0; i < (topPixel - (bottomPixel - w * 4)) / 4; i++) {
      var index = topPixel - i * 4;
      d[index] = rgba.r;
      d[index + 1] = rgba.g;
      d[index + 2] = rgba.b;
      d[index + 3] = rgba.a
    }
  }
}

function drawRandPoints() {
  var cnvs = document.getElementById("Canvas");
  var cont = cnvs.getContext("2d");

  // ghost last draw
  cont.fillStyle = "white";
  cont.fillRect(0, 0, cnvs.width, cnvs.height);

  // get image data
  var environment = cont.getImageData(0, 0, cnvs.width, cnvs.height);
  var d = environment.data, w = cnvs.width;

  // create colors
  var black = {
    r: 0,
    g: 0,
    b: 0,
    a: 255
  };
  var red = {
    r: 255,
    g: 0,
    b: 0,
    a: 255
  };
  var blue = {
    r: 0,
    g: 0,
    b: 255,
    a: 255
  };

  var frames = 0;
  var p1 = {x: ((cnvs.width / 2|0)), y: 0, sx: 1, sy:0};
  var p2 = {x: cnvs.width, y: ((cnvs.height / 2)|0), sx: -1, sy: 0};

  function step(p) {
    if (p.x > cnvs.width) {
      p.x = cnvs.width;
      p.sx = 0;
      p.sy = 1;
    }
    if (p.y > cnvs.height) {
      p.y = cnvs.height;
      p.sy = 0;
      p.sx = -1;
    }
    if (p.x < 0) {
      p.x = 0;
      p.sx = 0;
      p.sy = -1;
    }
    if (p.y < 0) {
      p.y = 0;
      p.sy = 0;
      p.sx = 1;
    }
  }

  function ani() {
    cont.fillStyle = "white";
    cont.fillRect(0, 0, cnvs.width, cnvs.height);
    environment = cont.getImageData(0, 0, cnvs.width, cnvs.height);
    d = environment.data;

    step(p1);
    step(p2);

    var p3 = {
      x: cnvs.width - p1.x,
      y: cnvs.height - p2.y
    };
    var p4 = {
      x: cnvs.width - p2.x,
      y: cnvs.height - p1.y
    };

    yourLine(p1, p2, w, d, {r:0,g:255,b:0,a:255});
    myDrawLine(p1, p2, w, d, red);
    drawLineNoAliasing(p3, p4, w, d, blue);
    

    setPixel(p1, w, d, black);
    setPixel(p2, w, d, black);
    frames %= 12;
    p1.x += p1.sx;
    p1.y += p1.sy;
    p2.x += p2.sx;
    p2.y += p2.sy;
    // Put the pixel data on the canvas.
    cont.putImageData(environment, 0, 0);
    requestAnimationFrame(ani);
  }
  ani();


}

function myDrawLine(p1, p2, w, d, rgba) {

  // Get the max length between x or y
  var lenX = Math.abs(p1.x - p2.x);
  var lenY = Math.abs(p1.y - p2.y);
  var len = Math.sqrt(Math.pow(lenX,2) + Math.pow(lenY,2));

  // Calculate the step increment between points
  var stepX = lenX / len;
  var stepY = lenY / len;

  // If the first x or y is greater then make step negetive.
  if (p2.x < p1.x) stepX *= -1;
  if (p2.y < p1.y) stepY *= -1;

  // Start at the first point
  var x = p1.x;
  var y = p1.y;

  for (var i = 0; i < len; i++) {
    x += stepX;
    y += stepY;
    // Make a point from new x and y
    var p = {
      x: x,
      y: y
    };

    // Draw pixel on data
    setPixel(p, w, d, rgba);

    // reached goal (removes extra pixel)
    if (Math.abs(p.x - p2.x) <= 1 && Math.abs(p.y - p2.y) <= 1) {
      break;
    }

  }
  // Draw start and end pixels. (might draw over line start and end)
  setPixel(p1, w, d, rgba);
  setPixel(p2, w, d, rgba);
}

// alternate from http://stackoverflow.com/questions/4261090/html5-canvas-and-anti-aliasing answer
// some helper functions
// finds the distance between points
function DBP(x1, y1, x2, y2) {
    return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
  }
  // finds the angle of (x,y) on a plane from the origin

function getAngle(x, y) {
    return Math.atan(y / (x == 0 ? 0.01 : x)) + (x < 0 ? Math.PI : 0);
  }
  // the function

function drawLineNoAliasing(p1, p2, w, d, rgba) {
    var dist = DBP(p1.x, p1.y, p2.x, p2.y); // length of line
    var ang = getAngle(p2.x - p1.x, p2.y - p1.y); // angle of line
    var cos = Math.cos(ang);
    var sin = Math.sin(ang);
    for (var i = 0; i < dist; i++) {
      // for each point along the line
      var pt = {
        x: p1.x + cos * i,
        y: p1.y + sin * i
      };
      setPixel(pt, w, d, rgba);
    }
  }
  // end alt

drawRandPoints();
#Canvas {
  border: 1px solid red image-rendering: optimizeSpeed;
  /* Older versions of FF */
  image-rendering: -moz-crisp-edges;
  /* FF 6.0+ */
  image-rendering: -webkit-optimize-contrast;
  /* Safari */
  image-rendering: -o-crisp-edges;
  /* OS X & Windows Opera (12.02+) */
  image-rendering: pixelated;
  /* Awesome future-browsers */
  image-rendering: optimize-contrast;
  /* CSS3 Proposed */
  -ms-interpolation-mode: nearest-neighbor;
  /* IE */
}
<canvas id="Canvas" width="128" height="64" style="width:320px"></canvas>
wolfhammer
  • 2,641
  • 1
  • 12
  • 11