Promising image / texture conversions with HTML5 Canvas

I used the keystoning technique described here to successfully add perspective to the html5 canvas cover page.

It basically happens that the image / canvas element is defined as the original texture. during each rendering cycle, this is divided into segments of a certain width (1px is the best quality), and each segment has its own height, scaled depending on its position on the x-axis of the texture.

This creates a good illusion of perspective with a given source image / texture, as shown below:

enter image description here

This is great for turning pages with a hard cover, the texture along with any included text gives a great idea about the perspective. However, I need to apply the same kind of trapezoid to the soft page turns. The problem is that a simple perspective conversion will not work, since the pages themselves are defined using curves, as shown below:

enter image description here

Currently, the page texture is scaled to the maximum curve height at any given page turning point, while the edges of the square curve of the page edge are set for the image / texture clip. Since I draw shadows and lines of pages dynamically using various standard canvas functions, this seems acceptable, since diagrams (drawn by quadratic curves) provide a natural perspective for page rotation.

However, the text itself (which is obtained from another cached canvas / image element) does not look good enough, remaining completely flat at all times.

What I would like to do is somehow apply the same slicing / segmentation / scaling trapezoid method as mentioned above, but somehow calculate the height of each 1px segment based on quadratic curves (reversed as ctx.quadraticCurveTo(); ) and the vertical position inside the canvas.

In my example, the image doesn’t actually look that bad, however, when the text gets closer to the top / bottom of the page, the warp effect should certainly be big. Not only that, but we also need to calculate the horizontal scaling factor to cut out the text closest to the page layer.

I am not able to provide any sample code that I am afraid of. but, in fact, we are doing everything very similar to the way described in the link above. And to summarize, I need to be able to use the quadratic coordinates / curve values ​​to calculate the scale factor for each segment in the slice / render metric below:

 function keystoneAndDisplayImage(ctx, img, x, y, pixelWidth, scalingFactor) { var h = img.height, w = img.width, // The number of slices to draw. numSlices = Math.abs(pixelWidth), // The width of each source slice. sliceWidth = w / numSlices, // Whether to draw the slices in reverse order or not. polarity = (pixelWidth > 0) ? 1 : -1, // How much should we scale the width of the slice // before drawing? widthScale = Math.abs(pixelWidth) / w, // How much should we scale the height of the slice // before drawing? heightScale = (1 - scalingFactor) / numSlices; for(var n = 0; n < numSlices; n++) { // Source: where to take the slice from. var sx = sliceWidth * n, sy = 0, sWidth = sliceWidth, sHeight = h; // Destination: where to draw the slice to // (the transformation happens here). var dx = x + (sliceWidth * n * widthScale * polarity), dy = y + ((h * heightScale * n) / 2), dWidth = sliceWidth * widthScale, dHeight = h * (1 - (heightScale * n)); ctx.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); } } 

... but I really don't know where to start. Any help would be greatly appreciated.

EDIT:

After thinking a little more about what I hope to achieve, I realized that the perfect representation of 3D texturing through this method might be too big to ask. Therefore, I would be more than happy with any answers that can simply change the height scale and y position of each segment based on the quadratic curve defined in the html5 canvas.

Here is the best example image:

enter image description here

As you can see, large text remains completely straight with one texture without texture when I need 1px width segments that will be computed from the curve coefficient and y position so that it naturally follows the page rotation. Otherwise, it would be very useful if any other β€œhacking” or method of achieving a semi-realistic long-term effect would be very useful in this case.

+4
source share
1 answer

I finally found a solution ....

Thanks to the fantastic answer here: Center point on a square html curve

I had the equations needed to get the y value of any point on a quadratic curve.

Then it was a simple case of using this y value to calculate the adjustment of the height of the textures. I have some settings to find the best quadratic curve calculation, but the result is quite fantastic, even if there is one.

See image below:

enter image description here

It currently runs at about 60 frames per second in Firefox / Chrome and about 45 frames per second in IE9 / 10. I am sure that there is a place for optimization, but despite the fact that I am very pleased with the result. Obviously, it does not stretch / cross the texture horizontally in accordance with the curve, but this will require at least one additional pass through texure, possibly more, which will degrade performance.

Another option is to resort to the actual comparison of the texture of Affine 3d, but in my attempts I found that this method is far superior in performance and quality, although, obviously, sacrificing a little accuracy.

The loop that is inside my main render loop for the flip page is as follows:

  for (var i = 0; i < segments; i++) { var sw = i >= segments - 1 ? segmentWidth : segmentWidth + 3; var sourceLeft = texw * ((i * segmentWidth) / texw); var sourceWidth = texw * (sw / texw) var texleft = foldX - foldWidth + (i * segmentWidth); var percent = ((i * segmentWidth)/foldWidth); var curve = self.getQuadraticCurvePoint(foldX - foldWidth, 0, foldX, -verticalOutdent * 2, foldX, 0, percent) var curvedheight = self.PAGE_HEIGHT + Math.abs(curve.y*2); var y = -((curvedheight - self.PAGE_HEIGHT)/2); context.drawImage(self.flips[flip.index+1].leftcanvas, sourceLeft, 0, sourceWidth, texh, texleft, y, sw, curvedheight); } 

With corresponding quadratic point functions:

  getQuadraticCurvePoint : function(startX, startY, cpX, cpY, endX, endY, position) { return { x: this.getQBezierValue(position, startX, cpX, endX), y: this.getQBezierValue(position, startY, cpY, endY) }; }, 

and:

 getQBezierValue : function(t, p1, p2, p3) { var iT = 1 - t; return iT * iT * p1 + 2 * iT * t * p2 + t * t * p3; }, 
+4
source

All Articles