您的位置:首页 > 其它

相机的连拍和水印的功能

2017-05-03 17:24 274 查看

相机的连拍和水印的功能

首先介绍相机的连拍功能

说到连拍的功能,大家很容易想到就是拍照获取照片,其实这样的连拍是最常见的,不过会出现卡一下的现象,需要做类似录像截屏的操作。

下面我就简单的说明一下这种连拍的实现,都要实现无卡顿的连拍我们就不能再通过

camera.takePicture(null, null, pictureCallback);


而要换成另外的方法,

/**
* <p>Installs a callback to be invoked for the next preview frame in
* addition to displaying it on the screen.  After one invocation, the
* callback is cleared. This method can be called any time, even when
* preview is live.  Any other preview callbacks are overridden.</p>
*
* <p>If you are using the preview data to create video or still images,
* strongly consider using {@link android.media.MediaActionSound} to
* properly indicate image capture or recording start/stop to the user.</p>
*
* @param cb a callback object that receives a copy of the next preview frame,
*     or null to stop receiving callbacks.
* @see android.media.MediaActionSound
*/
camera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
/**
* Called as preview frames are displayed.  This callback is invoked
* on the event thread {@link #open(int)} was called from.
*
* <p>If using the {@link android.graphics.ImageFormat#YV12} format,
* refer to the equations in {@link Camera.Parameters#setPreviewFormat}
* for the arrangement of the pixel data in the preview callback
* buffers.
*
* @param data the contents of the preview frame in the format defined
*  by {@link android.graphics.ImageFormat}, which can be queried
*  with {@link android.hardware.Camera.Parameters#getPreviewFormat()}.
*  If {@link android.hardware.Camera.Parameters#setPreviewFormat(int)}
*             is never called, the default will be the YCbCr_420_SP
*             (NV21) format.
* @param camera the Camera service object.
*/
@Override
public void onPreviewFrame(byte[] data, Camera camera) {

});


下面的方法主要不直接获取jpeg的图片流,是获取视频流格式的数据,这里只要将格式做转换即可得到数据图片,下面是转换的代码:

Size size = camera.getParameters().getPreviewSize();
Bitmap bitmap = null;
try {
YuvImage image = new YuvImage(data, ImageFormat.NV21, size.width, size.height, null);
if (image != null) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
image.compressToJpeg(new Rect(0, 0, size.width, size.height), 80, stream);
bitmap = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size());
stream.close();
}
} catch (Exception ex) {
Log.e("Sys", "Error:" + ex.getMessage());
}


这样即实现了简单的连拍功能。

相机的水印功能添加

水印添加主要是两部分,一部分是预览界面的水印,一部分是实际打到照片上的水印。

下面分开介绍:

水印旋转的秘密



上图是完成后的图片,主要的要求是水印信息要根据屏幕旋转而旋转。

这里主要是在预览的suferview上方覆盖一层水印层,然后在通过设置水印层view的参数实现,因为我这个水印会根据横屏或竖屏来变化水印的显示效果,所以会有对坐标的转换。

下面是水印文字旋转的核心代码:

private void rotateText(int orientation) {
switch (orientation) {
case ORIGIN:
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) linearLayout.getLayoutParams();
lp.setMargins(0, 0, 0, 0);
linearLayout.setLayoutParams(lp);
linearLayout.setRotation(0);
break;
case LEFT:
int marginRight = ll_preview_watermark.getWidth() - ll_preview_watermark.getHeight();
lp = (LinearLayout.LayoutParams) linearLayout.getLayoutParams();
lp.setMargins(0, 0, marginRight, 0);
linearLayout.setLayoutParams(lp);
linearLayout.measure(
View.MeasureSpec.makeMeasureSpec(linearLayout.getWidth() - marginRight, View.MeasureSpec
.EXACTLY), 0);
int hRoot = ll_preview_watermark.getHeight();
int hBottom = linearLayout.getMeasuredHeight();
int x1 = hBottom;
int y1 = 0;
int x2 = 0;
int y2 = hRoot - hBottom;
float[] pivot = calcPivot(x1, y1, x2, y2, false, hRoot / 2);
linearLayout.setPivotX(pivot[0]);
linearLayout.setPivotY(pivot[1] - y2);
linearLayout.setRotation(90);
break;
case RIGHT:
marginRight = ll_preview_watermark.getWidth() - ll_preview_watermark.getHeight();
lp = (LinearLayout.LayoutParams) linearLayout.getLayoutParams();
lp.setMargins(0, 0, marginRight, 0);
linearLayout.setLayoutParams(lp);
linearLayout.measure(
View.MeasureSpec.makeMeasureSpec(linearLayout.getWidth() - marginRight, View.MeasureSpec
.EXACTLY), 0);
int wRoot = ll_preview_watermark.getWidth();
hRoot = ll_preview_watermark.getHeight();
hBottom = linearLayout.getMeasuredHeight();
x1 = 0;
y1 = hRoot - hBottom;
x2 = wRoot - hBottom;
y2 = hRoot;
pivot = calcPivot(x1, y1, x2, y2, true, wRoot / 2);
linearLayout.setPivotX(pivot[0]);
linearLayout.setPivotY(pivot[1] - y1);
linearLayout.setRotation(-90);
break;
case TOP:
lp = (LinearLayout.LayoutParams) linearLayout.getLayoutParams();
lp.setMargins(0, 0, 0, 0);
linearLayout.setLayoutParams(lp);
wRoot = ll_preview_watermark.getWidth();
hRoot = ll_preview_watermark.getHeight();
hBottom = linearLayout.getHeight();
linearLayout.setPivotX(wRoot / 2);
linearLayout.setPivotY(hBottom - hRoot / 2);
linearLayout.setRotation(180);
break;
}
}

private float[] calcPivot(int x1, int y1, int x2, int y2, boolean isConstantX, int constant) {
double k1 = ((double) y2 - y1) / (x2 - x1);
double k2 = -1d / k1;
int x3 = (x1 + x2) / 2;
int y3 = (y1 + y2) / 2;
double b = y3 - k2 * x3;
if (isConstantX) {
return new float[] {constant, (float) (k2 * constant + b)};
} else {
return new float[] {(float) ((constant - b) / k2), constant};
}
}


通过传入屏幕的四个方向的标记来做相应的旋转。

水印是如何打到照片上

方式有很多,我是用两种方式来实现的:

1.因为我这的水印信息分为两部分,一个是icon一个是文字,我之前是通过在拍照获取到的照片上,通过canvas来进行绘制的,不过如果说你的水印信息很简单,这个方法是很好的选择,以为写上去的水印清晰度很高。

2.另一种就是通过照片合成来做,不过这里遇到了比较多的问题,主要的原因是因为对图片的压缩到导致的,我简单介绍一下我这的图片合成操作,我的水印信息是一个view上承载的内容,图片是通过相机获取的,我吧水印view上的内容转换为图片在做合成即可。

下面介绍一下view获取图片的代码,

public static Bitmap convertViewToBitmap(View view) {
//        view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
// View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
//        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
view.destroyDrawingCache();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bitmap = view.getDrawingCache();
//        Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
//                Bitmap.Config.ARGB_8888);
//        Canvas canvas = new Canvas(bitmap);
//        view.draw(canvas);
return bitmap;
}


获取到view转换过来的图片后就可以和照片进行合成了,照片和成主要是看水印在照片上的位置,

下面就是简单的工具类:

public static Bitmap newBitmapToWatermark(Bitmap src, Bitmap watermark) {
if (src == null) {
return null;
}

int screenWidth = FSScreen.getScreenWidth();
int screenHeight = FSScreen.getScreenHeight();

int w = src.getWidth();
int h = src.getHeight();
Log.i("图片尺寸,宽", w + "--高" + h);
//横着 宽: 1920--高1080
//竖着 宽: 1080--高1920
//      create the new blank bitmap
Bitmap newb = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);// 创建一个新的和SRC长度宽度一样的位图,Bitmap.Config.ARGB_8888
Canvas cv = new Canvas(newb);
//      draw src into
cv.drawBitmap(src, 0, 0, null);// 在 0,0坐标开始画入src
if (null != watermark) {
int ww = watermark.getWidth();
int wh = watermark.getHeight();
watermark = watermark.copy(Bitmap.Config.ARGB_8888, true);
Canvas c = new Canvas(watermark);
Paint p1 = new Paint();
int removeColor = 0;//要去除的背景色; // store this color's int for later use
p1.setAlpha(0);
p1.setXfermode(new AvoidXfermode(removeColor, 0, AvoidXfermode.Mode.TARGET));
c.drawPaint(p1);
/**下面是绘制黑色背景,判断照片横竖**/
Paint p = new Paint();
//CLAMP夹紧   REPEAT重复  MIRROR镜像
LinearGradient lg = new
LinearGradient(0, h - wh, 0, h, Color.parseColor("#00000000"), Color.parseColor("#99000000"), Shader.TileMode.CLAMP);
p.setShader(lg);
if (ww > w) {
float i = w / (float) ww;
int result = (int) (wh * i);
watermark = cropPhotoImage(src, watermark, result);
cv.drawRect(0, h, screenWidth, wh, p);
cv.drawBitmap(watermark, 5, h - result + 5, null);// 在src的左下角画入水印
} else {

cv.drawRect(0, h, screenWidth, wh, p);
cv.drawBitmap(watermark, 5, h - wh + 5, null);// 在src的左下角画入水印

}
}
// save all clip
cv.save(Canvas.ALL_SAVE_FLAG);// 保存
// store
cv.restore();// 存储
return newb;
}


里面涉及到一个LinearGradient线性渐变的类,这个类可以去看官方的文档,里面介绍的比较清除,主要是通过几种模式来绘制不同样式,我这里主要是在水印的背景上绘制一个渐变的黑色背景条,防止水印打在白色区域无法看到的问题。

上面基本上就这主要和两个知识点了,如果有什么问题,欢迎反馈。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  连拍 水印 线性渐变