Douyin APP is really a good thing, but it is also addictive. To be honest, did you uninstall and install Douyin repeatedly? Later, I also found that several of my leaders did not use Douyin, which surprised me.

I use Douyin mainly to read the news, listen to some big Vs talk about history, and study some algorithm effects of Douyin. Most importantly, Douyin provides a young person’s perspective to observe the world. In addition, if you watch too much content that you are interested in, you train Douyin to push more similar high-quality content. You can take advantage of this feature in reverse.

As for my leader always stressing that brushing douyin is bad, I don’t fully agree with him.

Special effect principle of Tiktok conveyor belt

Tiktok conveyor belt special effects have been introduced for a long time, the front has also been implemented, recently put it sorted out, if you have a careful observation of the conveyor belt special effects, you will find that its implementation principle is actually very simple.

If you look closely at Tok Tok’s conveyor belt effects, you can see that the left side of the image is constantly updated, while the right side looks like a vertical bar area that is constantly moving to the right until it reaches the right edge.

Copy images of the preview area one piece at a time to the conveyor belt, creating the effect of a continuous flow of images to the right.

The schematic diagram has been simplified, in fact, there are more vertical bars on the right side of the image, the effect will be more smooth, every frame of the preview image, first copy and update the left preview image, and then copy the image from the right side of the vertical bar image area (think about why?) .

For example, copy the pixels of region 2 to region 3, then copy the pixels of region 1 to region 2, and so on, and finally copy the pixels of the source region to region 0.

This creates a continuous transfer effect, and finally updates the copied image to the texture and renders it to the screen using OpenGL.

Tok Tok conveyor belt effect realization

In the principle analysis of the previous section, it is not efficient to copy the image region from left to right, which may cause some performance problems. Fortunately, the Android camera produces the image horizontally (rotated 90 or 270 degrees), so the image region is copied up and down more efficiently, and the image is rotated back in the final rendering.

Android camera map is YUV format, here for the convenience of copy processing, first use OpenCV YUV image conversion to RGBA format, of course, in pursuit of performance directly using YUV format image problem is not big.

cv::Mat mati420 = cv::Mat(pImage->height * 3 / 2, pImage->width, CV_8UC1, pImage->ppPlane[0]);
cv::Mat matRgba = cv::Mat(m_SrcImage.height, m_SrcImage.width, CV_8UC4, m_SrcImage.ppPlane[0]);
cv::cvtColor(mati420, matRgba, CV_YUV2RGBA_I420);
Copy the code

The shader used is a simple map:

#version 300 es
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec2 a_texCoord;
uniform mat4 u_MVPMatrix;
out vec2 v_texCoord;
void main(a)
{
gl_Position = u_MVPMatrix * a_position;
v_texCoord = a_texCoord;
}

#version 300 es
precision mediump float;
in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D u_texture;

void main(a)
{
    outColor = texture(u_texture, v_texCoord);
}
Copy the code

At the heart of the conveyor belt is the image copy operation:

memcpy(m_RenderImage.ppPlane[0], m_SrcImage.ppPlane[0], m_RenderImage.width * m_RenderImage.height * 4 / 2); Int bannerHeight = m_renderimage. height / 2 / m_bannerNum; // A banner height (small bar) int bannerPixelsBufSize = m_renderimage.width * bannerHeight * 4; // Uint8 *pBuf = m_renderimage.ppplane [0] + m_renderimage.width * m_renderimage.height * 4/2; For (int I = m_bannernum-1; i >= 1; --i) { memcpy(pBuf + i*bannerPixelsBufSize, pBuf + (i - 1)*bannerPixelsBufSize, bannerPixelsBufSize); } // Copy the pixel of the source region to the vertical bar image region 0 memcpy(pBuf, pbuf-bannerpixelsbufsize, bannerPixelsBufSize);Copy the code

Render operations:

glUseProgram (m_ProgramObj);

glBindVertexArray(m_VaoId);

glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0] [0]);

// Image copy, conveyor belt copy
memcpy(m_RenderImage.ppPlane[0], m_SrcImage.ppPlane[0], m_RenderImage.width * m_RenderImage.height * 4 / 2);
int bannerHeight = m_RenderImage.height / 2 / m_bannerNum;
int bannerPixelsBufSize = m_RenderImage.width * bannerHeight * 4;

uint8 *pBuf = m_RenderImage.ppPlane[0] + m_RenderImage.width * m_RenderImage.height * 4 / 2; // Conveyor belt boundary

for (int i = m_bannerNum - 1; i >= 1; --i) {
    memcpy(pBuf + i*bannerPixelsBufSize, pBuf + (i - 1)*bannerPixelsBufSize, bannerPixelsBufSize);
}
memcpy(pBuf, pBuf - bannerPixelsBufSize, bannerPixelsBufSize);

// Update the texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_TextureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_RenderImage.width, m_RenderImage.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_RenderImage.ppPlane[0]);
glBindTexture(GL_TEXTURE_2D, GL_NONE);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_TextureId);
GLUtils::setInt(m_ProgramObj, "u_texture".0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
glBindVertexArray(GL_NONE);
Copy the code

Detailed implementation code see the project: github.com/githubhaoha…