1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.rs.refocus;
18 
19 import android.content.ContentResolver;
20 import android.content.Context;
21 import android.graphics.Bitmap;
22 import android.graphics.BitmapFactory;
23 import android.graphics.Color;
24 import android.net.Uri;
25 import android.util.Log;
26 import com.android.rs.refocus.image.RangeInverseDepthTransform;
27 import java.io.FileNotFoundException;
28 import java.io.IOException;
29 
30 /**
31  * An RGBZ image, where Z stands for depth, i.e. a color+depth image.
32  * The RGBZ always has a preview image, which represents the latest rendering of the RGBZ.
33  * The preview is encoded as the normal jpeg content for client compatibility,
34  * while the color channel and depth channels are encoded as XMP data.
35  * The class supports lazy initialization where the XMP meta data is loaded only when first
36  * accessed.
37  */
38 public class RGBZ {
39   public static final String TAG = "RGBZ";
40 
41   private Bitmap bitmap;
42   private Bitmap preview;
43   private Bitmap depthBitmap;
44   private DepthTransform depthTransform;
45   private DepthImage depthImage;
46 
47   /**
48    * Creates an RGBZ from a content uri.
49    *
50    * @param uri The uri name of the RGBZ
51    * @throws FileNotFoundException if the RGBZ could not be read
52    */
RGBZ(Uri uri, ContentResolver contentResolver, Context context)53   public RGBZ(Uri uri, ContentResolver contentResolver, Context context) throws IOException {
54     preview = BitmapFactory.decodeStream(contentResolver.openInputStream(uri));
55     if (preview == null) {
56       throw new FileNotFoundException(uri.toString());
57     }
58     depthImage = DepthImage.createFromXMPMetadata(context, uri);
59     depthBitmap = depthImage.getDepthBitmap();
60     //MediaStoreSaver.savePNG(depthBitmap, "depthmap", "depthmap", context);
61     bitmap = setAlphaChannel(preview, depthBitmap);
62     depthTransform = depthImage.getDepthTransform();
63   }
64 
65   /**
66    * Creates an RGBZ from uris to an image and a depthmap.
67    *
68    * @param uriImage The uri name of the image
69    * @param uriDepthmap The uri name of the depthmap
70    * @throws FileNotFoundException if the RGBZ could not be read
71    */
RGBZ(Uri uriImage, Uri uriDepthmap, ContentResolver contentResolver, Context context)72   public RGBZ(Uri uriImage, Uri uriDepthmap, ContentResolver contentResolver,
73               Context context) throws IOException {
74     preview = BitmapFactory.decodeStream(contentResolver.openInputStream(uriImage));
75     if (preview == null) {
76       throw new FileNotFoundException(uriImage.toString());
77     }
78     depthImage = DepthImage.createFromDepthmap(context, uriDepthmap);
79     depthBitmap = depthImage.getDepthBitmap();
80     bitmap = setAlphaChannel(preview, depthBitmap);
81     depthTransform = depthImage.getDepthTransform();
82   }
83 
84 
RGBZ(Bitmap image, DepthImage depthImage)85     public RGBZ(Bitmap image, DepthImage depthImage) {
86         preview = image;
87         this.depthImage = depthImage;
88         depthBitmap = depthImage.getDepthBitmap();
89         bitmap = setAlphaChannel(preview, depthBitmap);
90         depthTransform = depthImage.getDepthTransform();
91     }
92 
createFromBitmapDepthmap(Uri uriImage, Uri uriDepthmap, ContentResolver contentResolver, Context context)93     public static RGBZ createFromBitmapDepthmap(Uri uriImage, Uri uriDepthmap,
94                                                 ContentResolver contentResolver, Context context)
95             throws IOException {
96         Bitmap image = BitmapFactory.decodeStream(contentResolver.openInputStream(uriImage));
97         if (image == null) {
98             throw new FileNotFoundException(uriImage.toString());
99         }
100         DepthImage depthImage = DepthImage.createFromDepthmap(context, uriDepthmap);
101         return new RGBZ(image, depthImage);
102     }
103 
createFromPFMDepthmap(Uri uriImage, Uri uriDepthmap, ContentResolver contentResolver, Context context)104     public static RGBZ createFromPFMDepthmap(Uri uriImage, Uri uriDepthmap,
105                                              ContentResolver contentResolver, Context context)
106             throws IOException {
107         Bitmap image = BitmapFactory.decodeStream(contentResolver.openInputStream(uriImage));
108         if (image == null) {
109             throw new FileNotFoundException(uriImage.toString());
110         }
111         DepthImage depthImage = DepthImage.createFromPFM(context, uriDepthmap);
112         MediaStoreSaver.savePNG(depthImage.getDepthBitmap(), "depthmap",
113                                 "depthmap", context);
114         return new RGBZ(image, depthImage);
115     }
116 
117   /**
118    * @return Whether the RGBZ has a depth channel
119    */
hasDepthmap()120   public boolean hasDepthmap() {
121     return depthTransform != null;
122   }
123 
124   /**
125    * @return The color+depth {@code Bitmap}
126    */
getBitmap()127   public Bitmap getBitmap() {
128     return bitmap;
129   }
130 
131   /**
132    * @return The depthmap component of this RGBZ
133    */
getDepthTransform()134   public DepthTransform getDepthTransform() {
135     return depthTransform;
136   }
137 
getFocusDepth()138   public double getFocusDepth() {
139     return this.depthImage.getFocalDistance();
140   }
141 
getDepthOfField()142   public double getDepthOfField() {
143     return this.depthImage.getDepthOfField();
144   }
145 
getBlurInfinity()146   public double getBlurInfinity() {
147     return this.depthImage.getBlurAtInfinity();
148   }
149 
150   /**
151    * @return the width of this {@code RGBZ}
152    */
getWidth()153   public int getWidth() {
154     return bitmap.getWidth();
155   }
156 
157   /**
158    * @return the height of this {@code RGBZ}
159    */
getHeight()160   public int getHeight() {
161     return bitmap.getHeight();
162   }
163 
164   /**
165    * @return the depth value of the given pixel
166    */
167 
getDepth(int x, int y)168   public float getDepth(int x, int y) {
169     if (!hasDepthmap()) {
170       return 0.0f;
171     }
172     if (x < 0 || x > depthBitmap.getWidth() ||
173             y < 0 || y > depthBitmap.getHeight()) {
174       Log.e("RGBZ getDepth", "index out of bound");
175       return 0;
176     }
177     return getDepthTransform().reconstruct(Color.blue(depthBitmap.getPixel(x, y)));
178   }
179 
180   /**
181    * Sets the depthmap as the alpha channel of the {@code Bitmap}.
182    */
setAlphaChannel(Bitmap bitmap, Bitmap depthBitmap)183   public Bitmap setAlphaChannel(Bitmap bitmap, Bitmap depthBitmap) {
184     if (bitmap == null) {
185       return bitmap;
186     }
187     Bitmap result = bitmap.copy(Bitmap.Config.ARGB_8888, true);
188     // set the alpha channel of depthBitmap to alpha of bitmap
189     result = setAlphaChannelFromBitmap(depthBitmap, bitmap, result);
190     return result;
191   }
192 
setAlphaChannelFromBitmap(Bitmap depth, Bitmap orig, Bitmap dest)193   private Bitmap setAlphaChannelFromBitmap(Bitmap depth, Bitmap orig, Bitmap dest) {
194     int w = orig.getWidth();
195     int h = orig.getHeight();
196     int[] orig_data = new int[w*h];
197     int[] depth_data = new int[w*h];
198 
199     orig.getPixels(orig_data, 0, w, 0, 0, w, h);
200     depth.getPixels(depth_data, 0, w, 0, 0, w, h);
201     for (int i = 0; i < orig_data.length; i++) {
202       int v = orig_data[i] & 0x00FFFFFF;
203       int temp = (depth_data[i] & 0x000000FF) << 24;
204       v = v | temp;
205       orig_data[i] = v;
206     }
207     dest.setPixels(orig_data, 0, w, 0, 0, w, h);
208     return dest;
209   }
210 }
211 
212