【问题标题】:Clipping images in Webgl在 Webgl 中剪切图像
【发布时间】:2015-07-07 06:20:38
【问题描述】:

2-D 画布提供了一个名为 draw image 的 api: context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height); 其中 sx图像剪辑 开始的位置。

http://www.w3schools.com/tags/canvas_drawimage.asp,

我正在尝试使用 webgl 来使用 texImage2D 渲染 2D 图像。我想检查是否有办法使用 webgl 实现剪辑

我正在使用以下教程使用 webgl 渲染 2d 图像。 http://webglfundamentals.org/webgl/lessons/webgl-image-processing.html

原图:

使用 drawImage(2D) 进行剪辑:

使用 webgl 进行剪辑:

var gl,program,positionLocation,originalImageTexture,canvas;
var x = 10;
var y = 20;
function setupWebGL(){

	var canvas = document.getElementById("canvas");
	gl = canvas.getContext("webgl");
  	if (!gl) {
    	return;
  	}
  	// setup GLSL program
  	var program = createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
  	gl.useProgram(program);
  	// look up where the vertex data needs to go.
  	positionLocation = gl.getAttribLocation(program, "a_position");
 	texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
  	var texture = gl.createTexture();
  	gl.bindTexture(gl.TEXTURE_2D, texture);
  
  	// lookup uniforms
  	var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
  	textureSizeLocation = gl.getUniformLocation(program, "u_textureSize");
  	colorLocation = gl.getUniformLocation(program, "u_color");
   	// set the resolution
  	gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
  
  	// Set the parameters so we can render any size image.
  	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);


  	gl.enable(gl.BLEND);
  	gl.blendEquation( gl.FUNC_ADD );
  	gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
  	gl.disable(gl.DEPTH_TEST);
}

    function draw() {
    // use canvas to simulate an image
  	var image = document.createElement("canvas");
    document.body.appendChild(image); // so we can see the source image
    image.width  = 200;
    image.height = 150;
    var ctx   = image.getContext("2d");
    ctx.fillRect(0, 0, image.width, image.height);
    for (var py = 0; py < image.height; py += 25) {
      for (var px = 0; px < image.width; px += 25) {
        ctx.fillStyle = "rgb(" + (py / image.height * 255 | 0) + "," + 
                                 (px / image.width  * 255 | 0) + "," + 
                                  255 + ")";
        ctx.beginPath();
        ctx.arc(px + 12, py + 12, 10, 0, Math.PI * 2);
        ctx.fill();
      }
    }
      
  	setupWebGL();
      
    var srcX = 12;
    var srcY = 35;
    var srcWidth = 75;
    var srcHeight = 50;
      
    var dstX = 100;
    var dstY = 110;
    var dstWidth  = srcWidth;
    var dstHeight = srcHeight;
  		
    var u0 = 50 / image.width;
    var v0 = 0 / image.height;
    var u1 = (50 + image.width) / image.width;
    var v1 = (0 + image.height) / image.height;
      
  	// provide texture coordinates for the rectangle.
  	var texCoordBuffer = gl.createBuffer();
  	gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
  	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([u0, v0, u1,v0, u0,v1, u0,v1, u1,v0, u1,v1]), gl.STATIC_DRAW);
    gl.enableVertexAttribArray(texCoordLocation);
    gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);

  
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,gl.RGBA, gl.UNSIGNED_BYTE, image);
  
    // set the size of the image
    gl.uniform2f(textureSizeLocation, image.width, image.height);
    // Create a buffer for the position of the rectangle corners.
    var positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    gl.enableVertexAttribArray(positionLocation);
    gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);

    setRectangle( gl, x, y, image.width, image.height);
    
 
    gl.drawArrays(gl.TRIANGLES, 0, 6);
  
}

    function setRectangle(gl, x, y, width, height) {
	var x1 = x;
	var x2 = x + width;
	var y1 = y;
	var y2 = y + height;
	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([x1, y1,x2, y1,x1, y2,x1, y2,x2, y1,x2, y2]), gl.STATIC_DRAW);
}

draw();
canvas { border: 1px solid black; }
<canvas width="400" height="300" id="canvas"></canvas>
<script src="//webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
attribute vec2 a_texCoord;

uniform vec2 u_resolution;

varying vec2 v_texCoord;

void main() {
   // convert the rectangle from pixels to 0.0 to 1.0
   vec2 zeroToOne = a_position / u_resolution;

   // convert from 0->1 to 0->2
   vec2 zeroToTwo = zeroToOne * 2.0;

   // convert from 0->2 to -1->+1 (clipspace)
   vec2 clipSpace = zeroToTwo - 1.0;

   gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);

   // pass the texCoord to the fragment shader
   // The GPU will interpolate this value between points.
   v_texCoord = a_texCoord;
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;

// our texture
uniform sampler2D u_image;

// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;

void main() {
   gl_FragColor = texture2D(u_image, v_texCoord);
}
</script>

【问题讨论】:

    标签: html5-canvas webgl image-clipping


    【解决方案1】:

    You need to adjust the texture coordinates to select the part of the texture you're interested in.

    纹理坐标从 0 到 1,所以如果你想从 drawImage 转换,那么给定

    drawImage(image, srcX, srcY, srcWidth, srcHeight, 
              dstX, dstY, dstWidth, dstHeight);
    

    在 WebGL 中,您必须使用 src 值调整 texcoord,并使用 dst 值调整绘制位置的顶点坐标。

    u0 = srcX / image.width;
    v0 = srcY / image.height;
    
    u1 = (srcX + srcWidth) / image.width;
    v1 = (srcY + srcHeight) / image.height;
    

    现在更新您的 texcoords。在您链接到的示例中

    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
      u0,  v0,
      u1,  v0,
      u0,  v1,
      u0,  v1,
      u1,  v0,
      u1,  v1]), gl.STATIC_DRAW);
    

    您还可以在着色器中添加一些数学运算。使用the same techniques as the articles starting here,您可以使用数学来调整着色器中的纹理坐标,就像那些文章调整位置一样。

    因此,例如,您可以让 UV​​ 坐标像原来一样从 0 变为 1,但更新着色器,以便您可以传入偏移量和缩放比例,将它们相乘以获得所需的位置。或者,如果您继续阅读这些文章,您可以使用矩阵来更灵活地处理它们。

    与目标大小类似,您可以在着色器中使用各种数学运算来移动一个简单的单位正方形,然后将其缩放到您想要的任何大小,而不是更新顶点。

    至于你贴出这一行的代码

    var u1 = (50 + image.width) / image.width;
    

    基本上是说您想从纹理的右边缘 50 像素处读取像素。这就是你变黑的原因。

    第二个问题是这一行

    setRectangle( gl, x, y, image.width, image.height);
    

    除非您打算拉伸图像,否则您要求绘制一个剪切图像(例如 image.width - 50image.width 像素,因此如果您的图像是 75 像素,您将获得 25 像素的源拉伸到 75 像素目的地

    不确定这是否是您想要的。这是固定版本

    请注意,我使用画布制作图像是因为 dataURL 让编辑非常痛苦。

    var gl,program,positionLocation,originalImageTexture,canvas;
    var x = 10;
    var y = 20;
    function setupWebGL(){
    
    	var canvas = document.getElementById("canvas");
    	gl = canvas.getContext("webgl");
      	if (!gl) {
        	return;
      	}
      	// setup GLSL program
      	var program = webglUtils.createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
      	gl.useProgram(program);
      	// look up where the vertex data needs to go.
      	positionLocation = gl.getAttribLocation(program, "a_position");
     	texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
      	var texture = gl.createTexture();
      	gl.bindTexture(gl.TEXTURE_2D, texture);
      
      	// lookup uniforms
      	var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
      	textureSizeLocation = gl.getUniformLocation(program, "u_textureSize");
      	colorLocation = gl.getUniformLocation(program, "u_color");
       	// set the resolution
      	gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
      
      	// Set the parameters so we can render any size image.
      	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
      	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
      	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
      	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    
    
      	gl.enable(gl.BLEND);
      	gl.blendEquation( gl.FUNC_ADD );
      	gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
      	gl.disable(gl.DEPTH_TEST);
    }
    
        function draw() {
        // use canvas to simulate an image
      	var image = document.createElement("canvas");
        document.body.appendChild(image); // so we can see the source image
        image.width  = 200;
        image.height = 150;
        var ctx   = image.getContext("2d");
        ctx.fillRect(0, 0, image.width, image.height);
        for (var py = 0; py < image.height; py += 25) {
          for (var px = 0; px < image.width; px += 25) {
            ctx.fillStyle = "rgb(" + (py / image.height * 255 | 0) + "," + 
                                     (px / image.width  * 255 | 0) + "," + 
                                      255 + ")";
            ctx.beginPath();
            ctx.arc(px + 12, py + 12, 10, 0, Math.PI * 2);
            ctx.fill();
          }
        }
          
      	setupWebGL();
          
        var srcX = 50;
        var srcY = 0;
        var srcWidth = image.width - 50;
        var srcHeight = image.height;
          
        var dstX = x;
        var dstY = y;
        var dstWidth  = srcWidth;
        var dstHeight = srcHeight;
      		
    	var u0 = srcX / image.width;
     	var v0 = srcY / image.height;
    	var u1 = (srcX + srcWidth)  / image.width;
     	var v1 = (srcY + srcHeight) / image.height;
    	
      	// provide texture coordinates for the rectangle.
      	var texCoordBuffer = gl.createBuffer();
      	gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
      	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([u0, v0, u1,v0, u0,v1, u0,v1, u1,v0, u1,v1]), gl.STATIC_DRAW);
        gl.enableVertexAttribArray(texCoordLocation);
        gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
    
      
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,gl.RGBA, gl.UNSIGNED_BYTE, image);
      
        // set the size of the image
        gl.uniform2f(textureSizeLocation, image.width, image.height);
        // Create a buffer for the position of the rectangle corners.
        var positionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
        gl.enableVertexAttribArray(positionLocation);
        gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
    
        setRectangle( gl, dstX, dstY, dstWidth, dstHeight);
       
     
        gl.drawArrays(gl.TRIANGLES, 0, 6);
      
    }
    
        function setRectangle(gl, x, y, width, height) {
    	var x1 = x;
    	var x2 = x + width;
    	var y1 = y;
    	var y2 = y + height;
    	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([x1, y1,x2, y1,x1, y2,x1, y2,x2, y1,x2, y2]), gl.STATIC_DRAW);
    }
    
    draw();
    canvas { border: 1px solid red; }
    <canvas width="400" height="300" id="canvas"></canvas>
    <script src="//webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
    <!-- vertex shader -->
    <script id="2d-vertex-shader" type="x-shader/x-vertex">
    attribute vec2 a_position;
    attribute vec2 a_texCoord;
    
    uniform vec2 u_resolution;
    
    varying vec2 v_texCoord;
    
    void main() {
       // convert the rectangle from pixels to 0.0 to 1.0
       vec2 zeroToOne = a_position / u_resolution;
    
       // convert from 0->1 to 0->2
       vec2 zeroToTwo = zeroToOne * 2.0;
    
       // convert from 0->2 to -1->+1 (clipspace)
       vec2 clipSpace = zeroToTwo - 1.0;
    
       gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
    
       // pass the texCoord to the fragment shader
       // The GPU will interpolate this value between points.
       v_texCoord = a_texCoord;
    }
    </script>
    <!-- fragment shader -->
    <script id="2d-fragment-shader" type="x-shader/x-fragment">
    precision mediump float;
    
    // our texture
    uniform sampler2D u_image;
    
    // the texCoords passed in from the vertex shader.
    varying vec2 v_texCoord;
    
    void main() {
       gl_FragColor = texture2D(u_image, v_texCoord);
    }
    </script>

    我还想再次强调,我通常不会这样做。就像我之前针对这种特殊情况所说的那样,我将使用单位平方位置和 texcoords 单位平方。然后我会使用矩阵来翻译和抵消两者。这将让我做 2D 画布 API 可以做的所有事情。缩放图像、剪切图像、旋转图像、翻转图像。它甚至可以让我做画布 API 不能做的事情,比如旋转矩形内的纹理。

    var gl,program,positionLocation,originalImageTexture,canvas;
    function setupWebGL(){
    
    	var canvas = document.getElementById("canvas");
    	gl = canvas.getContext("webgl");
      	if (!gl) {
        	return;
      	}
      	// setup GLSL program
      	var program = webglUtils.createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
      	gl.useProgram(program);
      	// look up where the vertex data needs to go.
      	positionLocation = gl.getAttribLocation(program, "a_position");
     	texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
      	var texture = gl.createTexture();
      	gl.bindTexture(gl.TEXTURE_2D, texture);
      
      	// lookup uniforms
      	matrixLocation = gl.getUniformLocation(program, "u_matrix");
      	texMatrixLocation = gl.getUniformLocation(program, "u_texMatrix");
      
      	// Set the parameters so we can render any size image.
      	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
      	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
      	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
      	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    
      	// provide texture coordinates for the rectangle.
      	var texCoordBuffer = gl.createBuffer();
      	gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
      	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]), gl.STATIC_DRAW);
        gl.enableVertexAttribArray(texCoordLocation);
        gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
    
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,gl.RGBA, gl.UNSIGNED_BYTE, image);
      
        // Create a buffer for the position of the rectangle corners.
        var positionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0 , 1, 1]), gl.STATIC_DRAW);
        gl.enableVertexAttribArray(positionLocation);
        gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
    
      	gl.enable(gl.BLEND);
      	gl.blendEquation( gl.FUNC_ADD );
      	gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
      	gl.disable(gl.DEPTH_TEST);
    }
    
    var originX, originY, srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight, angleInRadians,texAngle;
    
    function draw(time) {
        time *= 0.001; // make time in seconds
      
      
        switch (((time / 4) | 0) % 5) {
          case 0:
            originX = 0;
            originY = 0;
            srcX = 50;
            srcY = 0;
            srcWidth = image.width - 50;
            srcHeight = image.height;
          
            dstX = 10;
            dstY = 20;
            dstWidth  = srcWidth;
            dstHeight = srcHeight;
                
            angleInRadians = 0;
            texAngle = 0;
            break;
          case 1:
             // clip it more
             srcX = 50 + Math.sin(time      ) * 20;
             srcY = 50 + Math.sin(time * 1.3) * 20;
             srcWidth  = image.width  - (50 + Math.sin(time      ) * 20) - srcX;
             srcHeight = image.height - (50 + Math.sin(time * 1.3) * 20) - srcY;
             dstWidth  = srcWidth;
             dstHeight = srcHeight;
             break;
          case 2:
            // spin image around top left
            angleInRadians = time;
            break;
          case 3:
            // spin image around center
            angleInRadians = time;
            dstX = 100;
            dstY = 100;
            originX = -srcWidth / 2;
            originY = -srcHeight / 2;
            break;
          case 4:
            // spin texture around center
            texAngle = -time;
            break;
        }
      
          
        var x = dstX;
        var y = dstY;
        // We have a 1 unit square. If will scale by destWidth and destHeight we'll stretch
        // it to the size we want
        var xScale = dstWidth;
        var yScale = dstHeight;
        
        // We also have a 1 unit square for the texcoords. We can scale that to clip
        var texXOff   = srcX      / image.width;
        var texYOff   = srcY      / image.height;
        var texXScale = srcWidth  / image.width;
        var texYScale = srcHeight / image.height;
    
        // Compute the matrices
        var projectionMatrix = make2DProjection(gl.canvas.clientWidth, gl.canvas.clientHeight);
        var translationMatrix = makeTranslation(x, y);
        var rotationMatrix = makeRotation(angleInRadians);
        var originMatrix = makeTranslation(originX, originY);
        var scaleMatrix = makeScale(xScale, yScale);
      
        // Multiply the matrices.
        var matrix = matrixMultiply(scaleMatrix, originMatrix);
        matrix = matrixMultiply(matrix, rotationMatrix);
        matrix = matrixMultiply(matrix, translationMatrix);
        matrix = matrixMultiply(matrix, projectionMatrix);
          
        // compute matrixes for texture
        var texPreRot = makeTranslation(-0.5, -0.5);
        var texRot    = makeRotation(texAngle);
        var texPostRot= makeTranslation(0.5, 0.5);
        var texOffMat = makeTranslation(texXOff, texYOff);
        var texScaleMat = makeScale(texXScale, texYScale);
      
        var texMatrix = matrixMultiply(texScaleMat, texOffMat);
        texMatrix = matrixMultiply(texMatrix, texPreRot);
        texMatrix = matrixMultiply(texMatrix, texRot);
        texMatrix = matrixMultiply(texMatrix, texPostRot);
        
    
        // Set the matrices
        gl.uniformMatrix3fv(matrixLocation, false, matrix);      
        gl.uniformMatrix3fv(texMatrixLocation, false, texMatrix);
        gl.drawArrays(gl.TRIANGLES, 0, 6);
     
        requestAnimationFrame(draw);
    }
    
        // use canvas to simulate an image
      	image = document.createElement("canvas");
        document.body.appendChild(image); // so we can see the source image
        image.width  = 200;
        image.height = 150;
        var ctx   = image.getContext("2d");
        ctx.fillRect(0, 0, image.width, image.height);
        for (var py = 0; py < image.height; py += 25) {
          for (var px = 0; px < image.width; px += 25) {
            ctx.fillStyle = "rgb(" + (py / image.height * 255 | 0) + "," + 
                                     (px / image.width  * 255 | 0) + "," + 
                                      255 + ")";
            ctx.beginPath();
            ctx.arc(px + 12, py + 12, 10, 0, Math.PI * 2);
            ctx.fill();
          }
        }
          
    setupWebGL();
    requestAnimationFrame(draw);
    canvas { border: 1px solid red; }
    <canvas width="400" height="300" id="canvas"></canvas>
    <script src="//webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
    <script src="//webglfundamentals.org/webgl/resources/webgl-2d-math.js"></script>
    <!-- vertex shader -->
    <script id="2d-vertex-shader" type="x-shader/x-vertex">
    attribute vec2 a_position;
    attribute vec2 a_texCoord;
    
    uniform mat3 u_matrix;    // manipulates position
    uniform mat3 u_texMatrix; // manipulates texcoords
    
    varying vec2 v_texCoord;
    
    void main() {
         gl_Position = vec4((u_matrix * vec3(a_position, 1)).xy, 0, 1);
    
       // pass the texCoord to the fragment shader
       // The GPU will interpolate this value between points.
       v_texCoord = (u_texMatrix * vec3(a_texCoord, 1)).xy;
    }
    </script>
    <!-- fragment shader -->
    <script id="2d-fragment-shader" type="x-shader/x-fragment">
    precision mediump float;
    
    // our texture
    uniform sampler2D u_image;
    
    // the texCoords passed in from the vertex shader.
    varying vec2 v_texCoord;
    
    void main() {
       gl_FragColor = texture2D(u_image, v_texCoord);
    }
    </script>

    【讨论】:

    • 感谢 gman,剪辑工作,但使用 webgl 时我看到黑色背景,我附上原始图像,用 2d 画布剪辑并用 webgl 剪辑问题,你能告诉我如何避免黑色背景
    • 没有看到你的代码很难猜到。您是否使用gl.clearColor(0, 0, 0, 1); gl.clear(gl.COLOR_BUFFER_BIT); 将画布清除为黑色?您是否像gl = someCanvas.getContext("webgl", { alpha: false }); 那样创建没有 alpha 的画布?你画的不是单四边形吗?您是否也调整了 dest 坐标(位置),还是仍在绘制全尺寸图像?
    • 嗨,gman,我正在附上我正在使用的代码,我正在尝试在不同的位置随机绘制一组 base64 图像,其中一些图像需要剪辑。请让我知道这是否是正确的做法。
    • 惊人的答案!
    • 确实很好的答案!虽然作为一个菜鸟,我想知道我们是否需要在每个 draw() 调用上创建新的缓冲区,以及内存泄漏怎么办?
    猜你喜欢
    • 2019-08-06
    • 2021-03-24
    • 2018-02-14
    • 2019-07-14
    • 2014-08-08
    • 2018-04-10
    • 1970-01-01
    • 1970-01-01
    • 2018-03-16
    相关资源
    最近更新 更多