Android 上使用Opengl进行滤镜渲染效率较高,比起单纯的使用CPU给用户带来的体验会好很多。滤镜的对象是图片,图片是以Bitmap的形式表示,Opengl不能直接处理Bitmap,在Android上一般是通过GLSurfaceView来进行渲染的,也可以说成Android需要借助GLSurfaceView来完成对图片的渲染。

  GlSurfaceView 的图片来源依然是Bitmap,但是Bitmap需要以纹理(Texture)的形式载入到Opengl中。因此我首先来看一下载入纹理的步骤:

  1. GLES20.glGenTextures() : 生成纹理资源的句柄

  2. GLES20.glBindTexture(): 绑定句柄

  3. GLUtils.texImage2D() :将bitmap传递到已经绑定的纹理中

  4. GLES20.glTexParameteri() :设置纹理属性,过滤方式,拉伸方式等

  这里做滤镜使用Android4.x以后提供的 Effect 类来完成,Effect类实现也是通过Shader的方式来完成的,这些Shader程序内置在Android中,我们只需要按照一定的方式来调用就行了。在Android上使用GLSurfaceView来显示并完成图片的渲染,实现渲染需要实现GLSurfaceView.Render接口,该接口有三个方法:onDrawFrame(GL10 gl) ,该方法按照一定的刷新频率反复执行;onSurfaceChanged(GL10 gl, int width, int height),该方法在窗口重绘的时候执行;onSurfaceCreated(GL10 gl, EGLConfig config) 在创建SurfaceView的时候执行。

  使用Effect类会用到EffectFactory 和 EffectContex,在下面的例子中看看具体的使用方式。

  首先定义一个Activity:EffectivefilterActivity

package com.example.effectsfilterdemo;

import java.nio.IntBuffer;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem; public class EffectsFilterActivity extends Activity { private GLSurfaceView mEffectView; private TextureRenderer renderer; @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main); renderer = new TextureRenderer();
renderer.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.puppy));
renderer.setCurrentEffect(R.id.none); mEffectView = (GLSurfaceView) findViewById(R.id.effectsview);
//mEffectView = new GLSurfaceView(this);
mEffectView.setEGLContextClientVersion(2);
//mEffectView.setRenderer(this);
mEffectView.setRenderer(renderer);
mEffectView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); //setContentView(mEffectView);
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
Log.i("info", "menu create");
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
renderer.setCurrentEffect(item.getItemId());
mEffectView.requestRender();
return true;
}
}

  EffectivefilterActivity 中使用了两个布局文件,一个用于Activity的布局,另一个用于菜单的布局。

  R.layout.main:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" > <android.opengl.GLSurfaceView
android:id="@+id/effectsview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/> </LinearLayout>

  R.menu.main:

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
android:id="@+id/none"
android:showAsAction="never"
android:title="none"/>
<item
android:id="@+id/autofix"
android:showAsAction="never"
android:title="autofix"/>
<item
android:id="@+id/bw"
android:showAsAction="never"
android:title="bw"/>
<item
android:id="@+id/brightness"
android:showAsAction="never"
android:title="brightness"/>
<item
android:id="@+id/contrast"
android:showAsAction="never"
android:title="contrast"/>
<item
android:id="@+id/crossprocess"
android:showAsAction="never"
android:title="crossprocess"/>
<item
android:id="@+id/documentary"
android:showAsAction="never"
android:title="documentary"/>
<item
android:id="@+id/duotone"
android:showAsAction="never"
android:title="duotone"/>
<item
android:id="@+id/filllight"
android:showAsAction="never"
android:title="filllight"/>
<item
android:id="@+id/fisheye"
android:showAsAction="never"
android:title="fisheye"/>
<item
android:id="@+id/flipvert"
android:showAsAction="never"
android:title="flipvert"/>
<item
android:id="@+id/fliphor"
android:showAsAction="never"
android:title="fliphor"/>
<item
android:id="@+id/grain"
android:showAsAction="never"
android:title="grain"/>
<item
android:id="@+id/grayscale"
android:showAsAction="never"
android:title="grayscale"/>
<item
android:id="@+id/lomoish"
android:showAsAction="never"
android:title="lomoish"/>
<item
android:id="@+id/negative"
android:showAsAction="never"
android:title="negative"/>
<item
android:id="@+id/posterize"
android:showAsAction="never"
android:title="posterize"/>
<item
android:id="@+id/rotate"
android:showAsAction="never"
android:title="rotate"/>
<item
android:id="@+id/saturate"
android:showAsAction="never"
android:title="saturate"/>
<item
android:id="@+id/sepia"
android:showAsAction="never"
android:title="sepia"/>
<item
android:id="@+id/sharpen"
android:showAsAction="never"
android:title="sharpen"/>
<item
android:id="@+id/temperature"
android:showAsAction="never"
android:title="temperature"/>
<item
android:id="@+id/tint"
android:showAsAction="never"
android:title="tint"/>
<item
android:id="@+id/vignette"
android:showAsAction="never"
android:title="vignette"/> </menu>

  在R.layout.main中只定义了一个GLSurfaceView用于显示图片,R.menu.main用于显示多个菜单项,通过点击菜单来完成调用不同滤镜实现对图片的处理。

  接下来看比较关键的Renderer接口的实现。

package com.example.effectsfilterdemo;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.media.effect.Effect;
import android.media.effect.EffectContext;
import android.media.effect.EffectFactory;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.util.Log; import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.LinkedList;
import java.util.Queue; import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10; public class TextureRenderer implements GLSurfaceView.Renderer{ private int mProgram;
private int mTexSamplerHandle;
private int mTexCoordHandle;
private int mPosCoordHandle; private FloatBuffer mTexVertices;
private FloatBuffer mPosVertices; private int mViewWidth;
private int mViewHeight; private int mTexWidth;
private int mTexHeight; private Context mContext;
private final Queue<Runnable> mRunOnDraw;
private int[] mTextures = new int[2];
int mCurrentEffect;
private EffectContext mEffectContext;
private Effect mEffect;
private int mImageWidth;
private int mImageHeight;
private boolean initialized = false; private static final String VERTEX_SHADER =
"attribute vec4 a_position;\n" +
"attribute vec2 a_texcoord;\n" +
"varying vec2 v_texcoord;\n" +
"void main() {\n" +
" gl_Position = a_position;\n" +
" v_texcoord = a_texcoord;\n" +
"}\n"; private static final String FRAGMENT_SHADER =
"precision mediump float;\n" +
"uniform sampler2D tex_sampler;\n" +
"varying vec2 v_texcoord;\n" +
"void main() {\n" +
" gl_FragColor = texture2D(tex_sampler, v_texcoord);\n" +
"}\n"; private static final float[] TEX_VERTICES = {
0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f
}; private static final float[] POS_VERTICES = {
-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f
}; private static final int FLOAT_SIZE_BYTES = 4; public TextureRenderer() {
// TODO Auto-generated constructor stub
mRunOnDraw = new LinkedList<>(); } public void init() {
// Create program
mProgram = GLToolbox.createProgram(VERTEX_SHADER, FRAGMENT_SHADER); // Bind attributes and uniforms
mTexSamplerHandle = GLES20.glGetUniformLocation(mProgram,
"tex_sampler");
mTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_texcoord");
mPosCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_position"); // Setup coordinate buffers
mTexVertices = ByteBuffer.allocateDirect(
TEX_VERTICES.length * FLOAT_SIZE_BYTES)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mTexVertices.put(TEX_VERTICES).position(0);
mPosVertices = ByteBuffer.allocateDirect(
POS_VERTICES.length * FLOAT_SIZE_BYTES)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mPosVertices.put(POS_VERTICES).position(0);
} public void tearDown() {
GLES20.glDeleteProgram(mProgram);
} public void updateTextureSize(int texWidth, int texHeight) {
mTexWidth = texWidth;
mTexHeight = texHeight;
computeOutputVertices();
} public void updateViewSize(int viewWidth, int viewHeight) {
mViewWidth = viewWidth;
mViewHeight = viewHeight;
computeOutputVertices();
} public void renderTexture(int texId) {
GLES20.glUseProgram(mProgram);
GLToolbox.checkGlError("glUseProgram"); GLES20.glViewport(0, 0, mViewWidth, mViewHeight);
GLToolbox.checkGlError("glViewport"); GLES20.glDisable(GLES20.GL_BLEND); GLES20.glVertexAttribPointer(mTexCoordHandle, 2, GLES20.GL_FLOAT, false,
0, mTexVertices);
GLES20.glEnableVertexAttribArray(mTexCoordHandle);
GLES20.glVertexAttribPointer(mPosCoordHandle, 2, GLES20.GL_FLOAT, false,
0, mPosVertices);
GLES20.glEnableVertexAttribArray(mPosCoordHandle);
GLToolbox.checkGlError("vertex attribute setup"); GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLToolbox.checkGlError("glActiveTexture");
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);//把已经处理好的Texture传到GL上面
GLToolbox.checkGlError("glBindTexture");
GLES20.glUniform1i(mTexSamplerHandle, 0); GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
} private void computeOutputVertices() { //调整AspectRatio 保证landscape和portrait的时候显示比例相同,图片不会被拉伸
if (mPosVertices != null) {
float imgAspectRatio = mTexWidth / (float)mTexHeight;
float viewAspectRatio = mViewWidth / (float)mViewHeight;
float relativeAspectRatio = viewAspectRatio / imgAspectRatio;
float x0, y0, x1, y1;
if (relativeAspectRatio > 1.0f) {
x0 = -1.0f / relativeAspectRatio;
y0 = -1.0f;
x1 = 1.0f / relativeAspectRatio;
y1 = 1.0f;
} else {
x0 = -1.0f;
y0 = -relativeAspectRatio;
x1 = 1.0f;
y1 = relativeAspectRatio;
}
float[] coords = new float[] { x0, y0, x1, y0, x0, y1, x1, y1 };
mPosVertices.put(coords).position(0);
}
} private void initEffect() {
EffectFactory effectFactory = mEffectContext.getFactory();
if (mEffect != null) {
mEffect.release();
}
/**
* Initialize the correct effect based on the selected menu/action item
*/
switch (mCurrentEffect) { case R.id.none:
break; case R.id.autofix:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_AUTOFIX);
mEffect.setParameter("scale", 0.5f);
break; case R.id.bw:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_BLACKWHITE);
mEffect.setParameter("black", .1f);
mEffect.setParameter("white", .7f);
break; case R.id.brightness:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_BRIGHTNESS);
mEffect.setParameter("brightness", 2.0f);
break; case R.id.contrast:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_CONTRAST);
mEffect.setParameter("contrast", 1.4f);
break; case R.id.crossprocess:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_CROSSPROCESS);
break; case R.id.documentary:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_DOCUMENTARY);
break; case R.id.duotone:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_DUOTONE);
mEffect.setParameter("first_color", Color.YELLOW);
mEffect.setParameter("second_color", Color.DKGRAY);
break; case R.id.filllight:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_FILLLIGHT);
mEffect.setParameter("strength", .8f);
break; case R.id.fisheye:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_FISHEYE);
mEffect.setParameter("scale", .5f);
break; case R.id.flipvert:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_FLIP);
mEffect.setParameter("vertical", true);
break; case R.id.fliphor:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_FLIP);
mEffect.setParameter("horizontal", true);
break; case R.id.grain:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_GRAIN);
mEffect.setParameter("strength", 1.0f);
break; case R.id.grayscale:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_GRAYSCALE);
break; case R.id.lomoish:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_LOMOISH);
break; case R.id.negative:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_NEGATIVE);
break; case R.id.posterize:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_POSTERIZE);
break; case R.id.rotate:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_ROTATE);
mEffect.setParameter("angle", 180);
break; case R.id.saturate:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_SATURATE);
mEffect.setParameter("scale", .5f);
break; case R.id.sepia:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_SEPIA);
break; case R.id.sharpen:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_SHARPEN);
break; case R.id.temperature:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_TEMPERATURE);
mEffect.setParameter("scale", .9f);
break; case R.id.tint:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_TINT);
mEffect.setParameter("tint", Color.MAGENTA);
break; case R.id.vignette:
mEffect = effectFactory.createEffect(EffectFactory.EFFECT_VIGNETTE);
mEffect.setParameter("scale", .5f);
break; default:
break; }
} public void setCurrentEffect(int effect) {
mCurrentEffect = effect;
} public void setImageBitmap(final Bitmap bmp){
runOnDraw(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
loadTexture(bmp);
}
});
} private void loadTexture(Bitmap bmp){
GLES20.glGenTextures(2, mTextures , 0); updateTextureSize(bmp.getWidth(), bmp.getHeight()); mImageWidth = bmp.getWidth();
mImageHeight = bmp.getHeight(); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures[0]);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0); GLToolbox.initTexParams();
} private void applyEffect() {
if(mEffect == null){
Log.i("info","apply Effect null mEffect");
} mEffect.apply(mTextures[0], mImageWidth, mImageHeight, mTextures[1]);
} private void renderResult() {
if (mCurrentEffect != R.id.none) {
renderTexture(mTextures[1]);
} else {
renderTexture(mTextures[0]);
}
} @Override
public void onDrawFrame(GL10 gl) {
// TODO Auto-generated method stub
if(!initialized){
init();
mEffectContext = EffectContext.createWithCurrentGlContext();
initialized = true;
} GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
synchronized (mRunOnDraw) {
while (!mRunOnDraw.isEmpty()) {
mRunOnDraw.poll().run();
}
} if (mCurrentEffect != R.id.none) {
initEffect();
applyEffect();
}
renderResult();
} @Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// TODO Auto-generated method stub
updateViewSize(width, height);
} @Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// TODO Auto-generated method stub } protected void runOnDraw(final Runnable runnable) {
synchronized (mRunOnDraw) {
mRunOnDraw.add(runnable);
}
}
}

  这里有一个地方需要注意,任何使用Opengl接口的方法调用需要在Opengl Context中进行,否则会出现:call to OpenGL ES API with no current context (logged once per thread) 报错信息。所谓的Opengl Context 其实就是需要在onDrawFrame(GL10 gl),onSurfaceChanged(GL10 gl, int width, int height),onSurfaceCreated(GL10 gl, EGLConfig config)中调用,注意到这三个方法都有一个参数GL10。这里还有一个地方就是在载入纹理之前需要载入位图,使用了runOnDraw()方法将loadTexure的步骤放在onDrawFrame() 中来完成,巧妙的为外界提供了一个接口并使得操作在具有Opengl Context的黄金中完成。

  最后来看看辅助的工具类(GLToolbox),该类完成Shader程序的创建,应用程序提供Shader 源码给该工具类编译:

package com.example.effectsfilterdemo;
import android.opengl.GLES20; public class GLToolbox { public static int loadShader(int shaderType, String source) {
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0) {
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
int[] compiled = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
String info = GLES20.glGetShaderInfoLog(shader);
GLES20.glDeleteShader(shader);
shader = 0;
throw new RuntimeException("Could not compile shader " +
shaderType + ":" + info);
}
}
return shader;
} public static int createProgram(String vertexSource,
String fragmentSource) {
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0) {
return 0;
}
int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if (pixelShader == 0) {
return 0;
} int program = GLES20.glCreateProgram();
if (program != 0) {
GLES20.glAttachShader(program, vertexShader);
checkGlError("glAttachShader");
GLES20.glAttachShader(program, pixelShader);
checkGlError("glAttachShader");
GLES20.glLinkProgram(program);
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus,
0);
if (linkStatus[0] != GLES20.GL_TRUE) {
String info = GLES20.glGetProgramInfoLog(program);
GLES20.glDeleteProgram(program);
program = 0;
throw new RuntimeException("Could not link program: " + info);
}
}
return program;
} public static void checkGlError(String op) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
throw new RuntimeException(op + ": glError " + error);
}
} public static void initTexParams() {
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE);
} }

  这里就不提供整个工程了,结合上面的代码,自己在资源文件中提供一个图片载入就可以看到效果了。

Android OpenGL 编写简单滤镜的更多相关文章

  1. Android系统篇之—-编写简单的驱动程序并且将其编译到内核源码中【转】

    本文转载自:大神 通过之前的一篇文章,我们了解了 Android中的Binder机制和远程服务调用 在这篇文章中主要介绍了Android中的应用在调用一些系统服务的时候的原理,那么接下来就继续来介绍一 ...

  2. Android OpenGL ES(一)----必备知识

    1.手机的坐标空间 我们都知道要想在手机上随心所欲的绘制图形,就必须了解手机的坐标体系.下图就是将坐标映射到手机屏幕的坐标. 图1手机屏幕基本坐标系 2.OpenGL基本图形 在OpenGL里,只能绘 ...

  3. 在Ubuntu上为Android系统编写Linux内核驱动程序(老罗学习笔记1)

    这里,我们不会为真实的硬件设备编写内核驱动程序.为了方便描述为Android系统编写内核驱动程序的过程,我们使用一个虚拟的硬件设备,这个设备只有一个4字节的寄存器,它可读可写.想起我们第一次学习程序语 ...

  4. Android OpenGL库加载过程源码分析

    Android系统采用OpenGL绘制3D图形,使用skia来绘制二维图形:OpenGL源码位于: frameworks/native/opengl frameworks/base/opengl 本文 ...

  5. EGL接口介绍-----Android OpenGL ES底层开发

    引自:http://www.cnitblog.com/zouzheng/archive/2011/05/30/74326.html EGL 是 OpenGL ES 和底层 Native 平台视窗系统之 ...

  6. Android OpenGL ES(六)创建实例应用OpenGLDemos程序框架 .

    有了前面关于Android OpenGL ES的介绍,可以开始创建示例程序OpenGLDemos. 使用Eclipse 创建一个Android项目 Project Name: OpenGLDemos ...

  7. Android OpenGL 入门示例----绘制三角形和正方形

    Android上对OpenGl的支持是无缝的,所以才有众多3D效果如此逼真的游戏,在Camera的一些流程中也有用到GLSurfaceView的情况.本文记录OpenGL在Android上的入门级示例 ...

  8. Android OpenGL ES 开发(三): OpenGL ES 定义形状

    在上篇文章,我们能够配置好基本的Android OpenGL 使用的环境.但是如果我们不了解OpenGL ES如何定义图像的一些基本知识就使用OpenGL ES进行绘图还是有点棘手的.所以能够在Ope ...

  9. 在Android上编写模块化项目(翻译)

    来源博客:Wang Jie's Blog 本文链接:http://blog.wangjiegulu.com/2018/02/13/writing_a_modular_project_on_androi ...

随机推荐

  1. iOS开发--Block

    iOS开发--Block 1.什么是Block,block 的作用 ui开发和网络常见功能实现回调,按钮的事件处理方法是回调方法以及网络下载后的回调处理 (1)按钮 target-action   一 ...

  2. (转)使用Node.js+Socket.IO搭建WebSocket实时应用

    Web领域的实时推送技术,也被称作Realtime技术.这种技术要达到的目的是让用户不需要刷新浏览器就可以获得实时更新.它有着广泛的应用场景,比如在线聊天室.在线客服系统.评论系统.WebIM等. W ...

  3. Linux内核--网络栈实现分析(七)--数据包的传递过程(下)

    本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7545855 更多请查看专栏,地 ...

  4. GP调用arctoolbox 以Clip为例

    GP的功能非常强大,也是GIS建模的一个很重要的工具.在Arcengine中,实现Clip功能很多种方法,可以用IBasicGeoprocessor的clip方法,但是GP无疑是最简单的. publi ...

  5. 黑马程序员——File笔记读,写,复制

    #region ReadAllBytes byte[] buffer = File.ReadAllBytes(@"C:\Users\dell\Desktop\新建文件夹.txt") ...

  6. storysnail的Windows串口编程笔记

    storysnail的Windows串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据 ...

  7. 利用jmSlip写一个移动端顶部日历选择组件

    可滚动选日期,并限制哪些日期可选和不可选. 主要用来根据后台返回生成一个日期选择器. 具体实现可关注jmslip: https://github.com/jiamao/jmSlip 示例:http:/ ...

  8. Ubuntu系统下的第一个console程序

    进入自己喜欢的目录,前面步骤和windows基本一致,只简单描述下 执行 dotnet new 然后执行 dotnet restore 然后执行 dotnet run 第一次未编译,会自动编译,然后可 ...

  9. Java IO7:管道流、对象流

    前言 前面的文章主要讲了文件字符输入流FileWriter.文件字符输出流FileReader.文件字节输出流FileOutputStream.文件字节输入流FileInputStream,这些都是常 ...

  10. 海量用户-高并发SAAS产品测试上线流程

    海量用户高并发SAAS产品测试上线流程 SAAS产品测试上线流程-以Web插件产品为例子 1   概述 在互联网产品中,IT公司之间更加注重产品功能之间的协作,SAAS形态的产品扮演着越来越重要的作用 ...