相机的连拍和水印的功能

相机的连拍和水印的功能

首先介绍相机的连拍功能

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

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

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());
                    }

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

相机的水印功能添加

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

下面分开介绍:

水印旋转的秘密

device-2017-05-03-160103

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

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

上面基本上就这主要和两个知识点了,如果有什么问题,欢迎反馈。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值