1

I can't seem to figure out how to scale pixels on an html5 canvas. Here's where I am so far.

function draw_rect(data, n, sizex, sizey, color, pitch) {
    var c = Color2RGB(color);
    for( var y = 0; y < sizey; y++) {
        var nn = n * 4 * sizex;

        for( var x = 0; x < sizex; x++) {
            data[nn++] = c[0];
            data[nn++] = c[1];
            data[nn++] = c[2];
            data[nn++] = 0xff;
        }
        n = n + pitch;;
    } 
}

function buffer_blit(buffer, width, height) {
    var c_canvas = document.getElementById("canvas1");
    var context = c_canvas.getContext("2d");
    context.scale(2, 2);
    var imageData = context.getImageData(0, 0, context.canvas.width, context.canvas.height);
    var n = width * height - 1;
    while((n--)>=0) draw_rect(imageData.data, n, pixel, pixel, buffer[n], width);

    context.putImageData(imageData, 0, 0);  
}

Edits: I updated the code here. Not sure what changed.

Maybe some images will help. First image has pixel size of one, second pixel size of 2. Note that the image doubles and only fills half the canvas.

Pixel size of 1

Pixel size of 2

Edit2: I made a webpage showing at least one of the strange behaviors I'm experiencing.

Live example

Scott
  • 5,135
  • 12
  • 57
  • 74
  • @Scott - can't figure out what are you actually scaling – fazo Apr 02 '11 at 22:03
  • Well, I'm taking what's in framebuffer, which is a pixel-by-pixel image representation. It's single level array, accessed with `n = row * width + col`. I'm trying to draw that onto the image data table, but scaled by pixel height and width. – Scott Apr 02 '11 at 22:08
  • @Scott - are you trying to scale whole image to new size or every pixel by scaling factor? – fazo Apr 02 '11 at 22:20
  • Either way would work I think. What I'm doing though is the latter -- scaling each pixel. – Scott Apr 02 '11 at 22:29
  • It would probably be faster if there's an internal way of doing this. So far what's been suggested doesn't work. – Scott Apr 02 '11 at 22:30
  • @Scott if you simply want to scale by two, then you have to skip every 2 pixels in y and n x(you are not atm). – fazo Apr 03 '11 at 01:23
  • fazo, yeah that's what was happening. It's working now. – Scott Apr 03 '11 at 03:51
  • well, it's working, but it turns out the process is too heavy for practical use. I ended up going with two blit methods -- one drawing directly to the canvas' data buffer, and the other using fillRect, which incidentally is scalable. The latter is significantly slower than direct buffer access, which in't scalable. – Scott Apr 03 '11 at 05:57
  • @Scott you might try TypedArrays https://developer.mozilla.org/en/JavaScript_typed_arrays – fazo Apr 03 '11 at 16:33

2 Answers2

3

drawImage can be used to scale any image or image-like thing ( or for instance) without using ImageData -- unless you explicitly want to handle pixel specific scaling you should probably use native support. eg.

var myContext = myCanvas.getContext("2d");
// scale image up
myContext.drawImage(myImage, 0, 0, myImage.naturalWidth * 2, myImage.naturalHeight * 2);
// scale canvas up, can even take the source canvas
myContext.drawImage(myCanvas, 0, 0, myCanvas.width * 2, myCanvas.height * 2);
// scale up a video
myContext.drawImage(myVideo, 0, 0, myVideo.width * 2, myVideo.height * 2);

alternatively you could just do:

myContext.scale(2, 2); // say
//.... draw stuff ...
myContext.scale(.5, .5);

Depending on your exact goal.

olliej
  • 35,755
  • 9
  • 58
  • 55
  • For some reason scaling doesn't work when writing data directly to the canvas' buffer. – Scott Apr 02 '11 at 23:48
  • The ImageData APIs are specifically intended to ignore any context transformations. If you're using ImageData then you'll have do do the scaling manually (or go through a intermediate canvas), the bigger question is whether you actually need to be using ImageData in the first place. – olliej Apr 03 '11 at 02:31
  • 1
    I need fast access is why I'm drawing directly to the buffer. I already tried draw methods and they were too slow for my purposes. – Scott Apr 03 '11 at 03:21
0

You could temporarly use an image with canvas.toDataURL(), then draw it resized with drawImage().

context.putImageData() should do the trick, but the dimensions parameters are not yet implemented in Firefox.

The code, with two canvas for demo purposes:

var canv1 = document.getElementById("canv1"),
    canv2 = document.getElementById("canv2"),
    ctx1  = canv1.getContext("2d"),
    ctx2  = canv2.getContext("2d"),
    tmpImg = new Image();

/* Draw the shape */
ctx1.beginPath();
ctx1.arc(75,75,50,0,Math.PI*2,true);
ctx1.moveTo(110,75);
ctx1.arc(75,75,35,0,Math.PI,false);
ctx1.moveTo(65,65);
ctx1.arc(60,65,5,0,Math.PI*2,true);
ctx1.moveTo(95,65);
ctx1.arc(90,65,5,0,Math.PI*2,true);
ctx1.stroke();

/* On image load */
tmpImg.onload = function(){
  /* Draw the image on the second canvas */
  ctx2.drawImage(tmpImg,0,0, 300, 300);
};
/* Set src attribute */
tmpImg.src = canv1.toDataURL("image/png");

EDIT: olliej is right, there is no need to use a temp image

bpierre
  • 10,957
  • 2
  • 26
  • 27