The introduction

This series is divided into the following four parts:

  • Base type image processing techniques for scaling, cropping and rotation (portal);
  • Basic types of image processing techniques for image composition (portal);
  • Basic type image processing technology text synthesis;
  • Algorithm type Image processing technology (portal);

Last article, we introduced the picture clipping/rotation and scaling, the next article mainly introduces the picture composition, which is a more practical and complex part of the basic picture processing, can be regarded as the practice of the first article content.

Through this accumulation, I have encapsulated several features commonly used in projects:

Image synthesis     Image cropping     Like to dig in addition to

Picture composition

Image synthesis is also widely used in actual projects, you can try this demo(mobile only): 🐶🐶🐶

The puppy stickers

In fact, the principle of image composition is similar to the idea of Photoshop. It is composed and exported by stacking layers. Compared with cropping and scaling, the basic principle is the same, but it involves more calculation and more complex process.

I believe that you are familiar with Photoshop, and we can learn from its way of thinking:

  • newpsdFile, set the width and height;
  • Set the background image;
  • Add as many layers as you need from bottom to top.
  • Finally, directly export the entire file into a picture;

In order to synthesize the following figure, 🌰 :

1. First we need to create a canvas of the same size as the original;

2. Load the background image and add the background layer

3. Load the cat ears image and add the cat ears layer on the head of the beautiful woman (the order is not reversible, otherwise the ears will be covered under the beautiful woman. So image loading control is very important);

4. Export the picture of the entire canvas;

Synthesis part, mainly to package the plug-in for chestnut ha. This will be as complete as possible and avoid missing points. Before we start, we need to build a queue system to ensure that the images are drawn asynchronously.

Queue system;

Pictures of loading time is asynchronous and the unknown, and image synthesis need strictly guarantee the drawing order, the drawings will be placed after the top, so we need a set of strict mechanism to control the loading of pictures and drawing, otherwise we will not be able to avoid write callback hell, here I use the simple queue system;

The principle of the queue system is actually very simple, mainly to ensure that layers are drawn layer by layer from bottom to top. The way I designed it is as follows, the queue mode mainly ensures that the add functions are drawn in order:

// Create canvas;
let mc = new MCanvas();

// Add layer;
mc.add(image- 1).add(image2 -);

// Draw and export images;
mc.draw();
Copy the code

So we know that the queue system needs the following points:

  • Queue: Used to store layer drawing functions.

  • Next function: used to indicate that the current layer has been drawn, the next layer to draw;

  • Add function: As a unified method of adding layers, put the drawing logic into the function stack Quene and wrap the next function;

  • The draw function is used to start drawing, indicating that all layers are ready and can be drawn in order.

MCanvas.queue = [];

MCanvas.prototype.add = function(){
    this.queue.push((a)= >{
	// Draw the logic..// Execute the next layer drawing;
	this.next();
    });
}

MCanvas.prototype.next = function(){
    if(this.queue.length > 0) {// If there are still drawing tasks in the queue, push out and execute them;
        this.queue.shift()();
    }else{
    	// When the drawing is complete, call the success event and output the result graph;
        this.fn.success(); }}; MCanvas.prototype.draw =function(){
	// Export logic;.// Set the success event to export the result graph;
	this.fn.success = (a)= > {
	// Use setTimeout to slightly improve performance;
	// The queue functions are truly asynchronous, so there is no impact on the logic;
        setTimeout((a)= >{
            b64 = this.canvas.toDataURL(`image/jpeg}`.0.9); . },0);
   };
   
   // Start queue execution;
	this.next();
}
Copy the code

At this point, queue, Add, Next, and Draw form a complete queue system to ensure that the images are loaded and drawn sequentially. Once the materials and queues are ready, the actual composition of the images can begin

Create a canvas

MCanvas.prototype._init = function(){
    this.canvas = document.createElement('canvas');
    this.ctx = this.canvas.getContext('2d');
};
Copy the code

Draw the background

Set the canvas size and draw the beauty background.

By adjusting the dx,dy,dw,dh parameters of the background image, you can draw a variety of modes, similar to the background-size CSS contain/cover effect.

The scenario used above is used as an example here, namely the original image pattern.

// Keep the size of the original/renderings the same;
MCanvas.prototype.background = function(image, bgOps){
// Push to queue system;
this.queue.push((a)= > {
    let { iw, ih } = this._getSize(img);
		
    // Width ratio of picture to canvas;
    let iRatio = iw / ih;
		
    // Background rendering parameters;
    let dx,dy,dwidth,dheight;
		
    // Set the canvas size to be the same as the background image;
    this.canvas.width = iw;
    this.canvas.height = ih;
    dx = dy = 0;
    dwidth = this.canvas.width;
    dheight = this.canvas.height;
		
    // Draw the background image;
    this.ctx.drawImage(img,dx,dy,dwidth,dheight);
		
    this._next(); 
});
return this;
};
Copy the code

Draw cat ear stickers

I believe you have played with the sticker, its biggest characteristic, is the sticker and the background picture match. This means that users can change the size, position, rotation Angle of the sticker and use gestures to attach the cat’s ears to the head of the person in the photo. So the add method, you have to set the zoom, rotation, and position parameters.

Here, a set of parameters is simulated first. In the real situation, users will adjust different position parameters according to different background images.

{
	// Image path;
    image:'./images/ear.png'.options: {// Sticker width;
        width:482.pos: {// The top left of the sticker;
            x:150.y:58.// Sticker magnification coefficient;
            scale:1.// Sticker rotation coefficient;
            rotate:35,,}}}Copy the code

addfunction

Next, we will parse the position of each parameter in the add function:

Draw small canvas to handle rotation:

// Create a small canvas;
let lcvs = document.createElement('canvas'),
	lctx = lcvs.getContext('2d');

// The original size of the sticker;
let { iw, ih } = this._getSize(img);
// Draw parameters;
let ldx, ldy, ldw, ldh;

// Original size of sticker;
ldw = iw;
ldh = ih;

// Draw the starting point;
ldx = - Math.round(ldw / 2);
ldy = - Math.round(ldh / 2);

// We talked about rotary clipping in the previous article.
// Need to enlarge the container of small canvas to avoid clipping caused by rotation; The maximum amplification is 5 times;
let _ratio = iw > ih ? iw / ih : ih / iw;
let lctxScale = _ratio * 1.4 > 5 ? 5 : _ratio * 1.4;

lcvs.width =  ldw * lctxScale;
lcvs.height = ldh * lctxScale;

// Adjust the drawing base point;
lctx.translate(lcvs.width/2,lcvs.height/2);

// Rotate artboard;
lctx.rotate(ops.pos.rotate);

// Draw a sticker;
lctx.drawImage(img,ldx,ldy,ldw,ldh);
Copy the code

At this point we’ll get a small canvas with this cat ear sticker in the center:

The next step is to draw the stickers on the background image. The point to pay attention to is to enlarge the blank area of the sticker canvas. This area needs to be taken into account to calculate the final real dx and dy values:

// Draw parameters;
let cratio = iw / ih;
let cdx, cdy, cdw, cdh;

// ops.width is the final canvas width;
// Since the small canvas is enlarged, the final width also needs to be doubled;
// Multiply the configuration by the factor that needs to be scaled;
cdw = ops.width * lctxScale * ops.pos.scale;
cdh = cdw / cratio * ops.pos.scale;

// Add blank area after zooming in;
spaceX = (lctxScale - 1) * ops.width / 2;
spaceY = spaceX / cratio;

// Get the final position of the material;
// Configuration position - configuration magnification factor effect - small canvas magnification factor effect;
cdx = ops.pos.x + cdw * ( 1 - ops.pos.scale )/2 - spaceX;
cdy = ops.pos.y + cdh * ( 1 - ops.pos.scale )/2 - spaceY;

this.ctx.drawImage(lcvs,cdx,cdy,cdw,cdh);

lcvs = lctx = null;
Copy the code

This gives the resultant image, with red borders representing the small canvas and black borders representing the large canvas:

MCanvas.prototype.add = function(img, options){
    this.queue.push((a)= >{
        // Draw a small canvas of stickers;.// Draw stickers to a large canvas;. this._next(); });return this;
}
Copy the code

So we went through a series of methods and built a complete synthesis process. Through this process, we can add any image layer and compose the image.

conclusion

This paper mainly explains the method and principle of picture synthesis and some pits that need to be filled. This whole process is also after a long period of polishing and filling many pits, which is a relatively mature scheme. It has been worked in several online projects, and it is expected to be helpful to everyone! 🤗. Next article, we will continue to introduce text composition and geometric image composition, stay tuned ~~🙃🙃