diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/com/android/camera/Mosaic.java | 206 | ||||
-rw-r--r-- | src/com/android/camera/MosaicFrameProcessor.java | 237 | ||||
-rw-r--r-- | src/com/android/camera/MosaicPreviewRenderer.java | 179 | ||||
-rw-r--r-- | src/com/android/camera/MosaicRenderer.java | 89 | ||||
-rw-r--r-- | src/com/android/camera/PanoProgressBar.java | 188 | ||||
-rw-r--r-- | src/com/android/camera/PanoUtil.java | 86 | ||||
-rw-r--r-- | src/com/android/gallery3d/filtershow/tools/MatrixFit.java | 200 | ||||
-rw-r--r-- | src/com/android/gallery3d/ingest/IngestActivity.java | 21 | ||||
-rw-r--r-- | src/com/android/gallery3d/ingest/ui/MtpImageView.java | 44 |
9 files changed, 265 insertions, 985 deletions
diff --git a/src/com/android/camera/Mosaic.java b/src/com/android/camera/Mosaic.java deleted file mode 100644 index 78876c384..000000000 --- a/src/com/android/camera/Mosaic.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera; - -/** - * The Java interface to JNI calls regarding mosaic stitching. - * - * A high-level usage is: - * - * Mosaic mosaic = new Mosaic(); - * mosaic.setSourceImageDimensions(width, height); - * mosaic.reset(blendType); - * - * while ((pixels = hasNextImage()) != null) { - * mosaic.setSourceImage(pixels); - * } - * - * mosaic.createMosaic(highRes); - * byte[] result = mosaic.getFinalMosaic(); - * - */ -public class Mosaic { - /** - * In this mode, the images are stitched together in the same spatial arrangement as acquired - * i.e. if the user follows a curvy trajectory, the image boundary of the resulting mosaic will - * be curved in the same manner. This mode is useful if the user wants to capture a mosaic as - * if "painting" the scene using the smart-phone device and does not want any corrective warps - * to distort the captured images. - */ - public static final int BLENDTYPE_FULL = 0; - - /** - * This mode is the same as BLENDTYPE_FULL except that the resulting mosaic is rotated - * to balance the first and last images to be approximately at the same vertical offset in the - * output mosaic. This is useful when acquiring a mosaic by a typical panning-like motion to - * remove a one-sided curve in the mosaic (typically due to the camera not staying horizontal - * during the video capture) and convert it to a more symmetrical "smiley-face" like output. - */ - public static final int BLENDTYPE_PAN = 1; - - /** - * This mode compensates for typical "smiley-face" like output in longer mosaics and creates - * a rectangular mosaic with minimal black borders (by unwrapping the mosaic onto an imaginary - * cylinder). If the user follows a curved trajectory (instead of a perfect panning trajectory), - * the resulting mosaic here may suffer from some image distortions in trying to map the - * trajectory to a cylinder. - */ - public static final int BLENDTYPE_CYLINDERPAN = 2; - - /** - * This mode is basically BLENDTYPE_CYLINDERPAN plus doing a rectangle cropping before returning - * the mosaic. The mode is useful for making the resulting mosaic have a rectangle shape. - */ - public static final int BLENDTYPE_HORIZONTAL =3; - - /** - * This strip type will use the default thin strips where the strips are - * spaced according to the image capture rate. - */ - public static final int STRIPTYPE_THIN = 0; - - /** - * This strip type will use wider strips for blending. The strip separation - * is controlled by a threshold on the native side. Since the strips are - * wider, there is an additional cross-fade blending step to make the seam - * boundaries smoother. Since this mode uses lesser image frames, it is - * computationally more efficient than the thin strip mode. - */ - public static final int STRIPTYPE_WIDE = 1; - - /** - * Return flags returned by createMosaic() are one of the following. - */ - public static final int MOSAIC_RET_OK = 1; - public static final int MOSAIC_RET_ERROR = -1; - public static final int MOSAIC_RET_CANCELLED = -2; - public static final int MOSAIC_RET_LOW_TEXTURE = -3; - public static final int MOSAIC_RET_FEW_INLIERS = 2; - - - static { - System.loadLibrary("jni_mosaic"); - } - - /** - * Allocate memory for the image frames at the given resolution. - * - * @param width width of the input frames in pixels - * @param height height of the input frames in pixels - */ - public native void allocateMosaicMemory(int width, int height); - - /** - * Free memory allocated by allocateMosaicMemory. - * - */ - public native void freeMosaicMemory(); - - /** - * Pass the input image frame to the native layer. Each time the a new - * source image t is set, the transformation matrix from the first source - * image to t is computed and returned. - * - * @param pixels source image of NV21 format. - * @return Float array of length 11; first 9 entries correspond to the 3x3 - * transformation matrix between the first frame and the passed frame; - * the 10th entry is the number of the passed frame, where the counting - * starts from 1; and the 11th entry is the returning code, whose value - * is one of those MOSAIC_RET_* returning flags defined above. - */ - public native float[] setSourceImage(byte[] pixels); - - /** - * This is an alternative to the setSourceImage function above. This should - * be called when the image data is already on the native side in a fixed - * byte array. In implementation, this array is filled by the GL thread - * using glReadPixels directly from GPU memory (where it is accessed by - * an associated SurfaceTexture). - * - * @return Float array of length 11; first 9 entries correspond to the 3x3 - * transformation matrix between the first frame and the passed frame; - * the 10th entry is the number of the passed frame, where the counting - * starts from 1; and the 11th entry is the returning code, whose value - * is one of those MOSAIC_RET_* returning flags defined above. - */ - public native float[] setSourceImageFromGPU(); - - /** - * Set the type of blending. - * - * @param type the blending type defined in the class. {BLENDTYPE_FULL, - * BLENDTYPE_PAN, BLENDTYPE_CYLINDERPAN, BLENDTYPE_HORIZONTAL} - */ - public native void setBlendingType(int type); - - /** - * Set the type of strips to use for blending. - * @param type the blending strip type to use {STRIPTYPE_THIN, - * STRIPTYPE_WIDE}. - */ - public native void setStripType(int type); - - /** - * Tell the native layer to create the final mosaic after all the input frame - * data have been collected. - * The case of generating high-resolution mosaic may take dozens of seconds to finish. - * - * @param value True means generating a high-resolution mosaic - - * which is based on the original images set in setSourceImage(). - * False means generating a low-resolution version - - * which is based on 1/4 downscaled images from the original images. - * @return Returns a status code suggesting if the mosaic building was - * successful, in error, or was cancelled by the user. - */ - public native int createMosaic(boolean value); - - /** - * Get the data for the created mosaic. - * - * @return Returns an integer array which contains the final mosaic in the ARGB_8888 format. - * The first MosaicWidth*MosaicHeight values contain the image data, followed by 2 - * integers corresponding to the values MosaicWidth and MosaicHeight respectively. - */ - public native int[] getFinalMosaic(); - - /** - * Get the data for the created mosaic. - * - * @return Returns a byte array which contains the final mosaic in the NV21 format. - * The first MosaicWidth*MosaicHeight*1.5 values contain the image data, followed by - * 8 bytes which pack the MosaicWidth and MosaicHeight integers into 4 bytes each - * respectively. - */ - public native byte[] getFinalMosaicNV21(); - - /** - * Reset the state of the frame arrays which maintain the captured frame data. - * Also re-initializes the native mosaic object to make it ready for capturing a new mosaic. - */ - public native void reset(); - - /** - * Get the progress status of the mosaic computation process. - * @param hires Boolean flag to select whether to report progress of the - * low-res or high-res mosaicer. - * @param cancelComputation Boolean flag to allow cancelling the - * mosaic computation when needed from the GUI end. - * @return Returns a number from 0-100 where 50 denotes that the mosaic - * computation is 50% done. - */ - public native int reportProgress(boolean hires, boolean cancelComputation); -} diff --git a/src/com/android/camera/MosaicFrameProcessor.java b/src/com/android/camera/MosaicFrameProcessor.java deleted file mode 100644 index efd4ad2ae..000000000 --- a/src/com/android/camera/MosaicFrameProcessor.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera; - -import android.util.Log; - -/** - * Class to handle the processing of each frame by Mosaicer. - */ -public class MosaicFrameProcessor { - private static final String TAG = "MosaicFrameProcessor"; - private static final int NUM_FRAMES_IN_BUFFER = 2; - private static final int MAX_NUMBER_OF_FRAMES = 100; - private static final int MOSAIC_RET_CODE_INDEX = 10; - private static final int FRAME_COUNT_INDEX = 9; - private static final int X_COORD_INDEX = 2; - private static final int Y_COORD_INDEX = 5; - private static final int HR_TO_LR_DOWNSAMPLE_FACTOR = 4; - private static final int WINDOW_SIZE = 3; - - private Mosaic mMosaicer; - private boolean mIsMosaicMemoryAllocated = false; - private float mTranslationLastX; - private float mTranslationLastY; - - private int mFillIn = 0; - private int mTotalFrameCount = 0; - private int mLastProcessFrameIdx = -1; - private int mCurrProcessFrameIdx = -1; - private boolean mFirstRun; - - // Panning rate is in unit of percentage of image content translation per - // frame. Use moving average to calculate the panning rate. - private float mPanningRateX; - private float mPanningRateY; - - private float[] mDeltaX = new float[WINDOW_SIZE]; - private float[] mDeltaY = new float[WINDOW_SIZE]; - private int mOldestIdx = 0; - private float mTotalTranslationX = 0f; - private float mTotalTranslationY = 0f; - - private ProgressListener mProgressListener; - - private int mPreviewWidth; - private int mPreviewHeight; - private int mPreviewBufferSize; - - private static MosaicFrameProcessor sMosaicFrameProcessor; // singleton - - public interface ProgressListener { - public void onProgress(boolean isFinished, float panningRateX, float panningRateY, - float progressX, float progressY); - } - - public static MosaicFrameProcessor getInstance() { - if (sMosaicFrameProcessor == null) { - sMosaicFrameProcessor = new MosaicFrameProcessor(); - } - return sMosaicFrameProcessor; - } - - private MosaicFrameProcessor() { - mMosaicer = new Mosaic(); - } - - public void setProgressListener(ProgressListener listener) { - mProgressListener = listener; - } - - public int reportProgress(boolean hires, boolean cancel) { - return mMosaicer.reportProgress(hires, cancel); - } - - public void initialize(int previewWidth, int previewHeight, int bufSize) { - mPreviewWidth = previewWidth; - mPreviewHeight = previewHeight; - mPreviewBufferSize = bufSize; - setupMosaicer(mPreviewWidth, mPreviewHeight, mPreviewBufferSize); - setStripType(Mosaic.STRIPTYPE_WIDE); - // no need to call reset() here. reset() should be called by the client - // after this initialization before calling other methods of this object. - } - - public void clear() { - if (mIsMosaicMemoryAllocated) { - mMosaicer.freeMosaicMemory(); - mIsMosaicMemoryAllocated = false; - } - synchronized (this) { - notify(); - } - } - - public boolean isMosaicMemoryAllocated() { - return mIsMosaicMemoryAllocated; - } - - public void setStripType(int type) { - mMosaicer.setStripType(type); - } - - private void setupMosaicer(int previewWidth, int previewHeight, int bufSize) { - Log.v(TAG, "setupMosaicer w, h=" + previewWidth + ',' + previewHeight + ',' + bufSize); - - if (mIsMosaicMemoryAllocated) throw new RuntimeException("MosaicFrameProcessor in use!"); - mIsMosaicMemoryAllocated = true; - mMosaicer.allocateMosaicMemory(previewWidth, previewHeight); - } - - public void reset() { - // reset() can be called even if MosaicFrameProcessor is not initialized. - // Only counters will be changed. - mFirstRun = true; - mTotalFrameCount = 0; - mFillIn = 0; - mTotalTranslationX = 0; - mTranslationLastX = 0; - mTotalTranslationY = 0; - mTranslationLastY = 0; - mPanningRateX = 0; - mPanningRateY = 0; - mLastProcessFrameIdx = -1; - mCurrProcessFrameIdx = -1; - for (int i = 0; i < WINDOW_SIZE; ++i) { - mDeltaX[i] = 0f; - mDeltaY[i] = 0f; - } - mMosaicer.reset(); - } - - public int createMosaic(boolean highRes) { - return mMosaicer.createMosaic(highRes); - } - - public byte[] getFinalMosaicNV21() { - return mMosaicer.getFinalMosaicNV21(); - } - - // Processes the last filled image frame through the mosaicer and - // updates the UI to show progress. - // When done, processes and displays the final mosaic. - public void processFrame() { - if (!mIsMosaicMemoryAllocated) { - // clear() is called and buffers are cleared, stop computation. - // This can happen when the onPause() is called in the activity, but still some frames - // are not processed yet and thus the callback may be invoked. - return; - } - - mCurrProcessFrameIdx = mFillIn; - mFillIn = ((mFillIn + 1) % NUM_FRAMES_IN_BUFFER); - - // Check that we are trying to process a frame different from the - // last one processed (useful if this class was running asynchronously) - if (mCurrProcessFrameIdx != mLastProcessFrameIdx) { - mLastProcessFrameIdx = mCurrProcessFrameIdx; - - // TODO: make the termination condition regarding reaching - // MAX_NUMBER_OF_FRAMES solely determined in the library. - if (mTotalFrameCount < MAX_NUMBER_OF_FRAMES) { - // If we are still collecting new frames for the current mosaic, - // process the new frame. - calculateTranslationRate(); - - // Publish progress of the ongoing processing - if (mProgressListener != null) { - mProgressListener.onProgress(false, mPanningRateX, mPanningRateY, - mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth, - mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight); - } - } else { - if (mProgressListener != null) { - mProgressListener.onProgress(true, mPanningRateX, mPanningRateY, - mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth, - mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight); - } - } - } - } - - public void calculateTranslationRate() { - float[] frameData = mMosaicer.setSourceImageFromGPU(); - int ret_code = (int) frameData[MOSAIC_RET_CODE_INDEX]; - mTotalFrameCount = (int) frameData[FRAME_COUNT_INDEX]; - float translationCurrX = frameData[X_COORD_INDEX]; - float translationCurrY = frameData[Y_COORD_INDEX]; - - if (mFirstRun) { - // First time: no need to update delta values. - mTranslationLastX = translationCurrX; - mTranslationLastY = translationCurrY; - mFirstRun = false; - return; - } - - // Moving average: remove the oldest translation/deltaTime and - // add the newest translation/deltaTime in - int idx = mOldestIdx; - mTotalTranslationX -= mDeltaX[idx]; - mTotalTranslationY -= mDeltaY[idx]; - mDeltaX[idx] = Math.abs(translationCurrX - mTranslationLastX); - mDeltaY[idx] = Math.abs(translationCurrY - mTranslationLastY); - mTotalTranslationX += mDeltaX[idx]; - mTotalTranslationY += mDeltaY[idx]; - - // The panning rate is measured as the rate of the translation percentage in - // image width/height. Take the horizontal panning rate for example, the image width - // used in finding the translation is (PreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR). - // To get the horizontal translation percentage, the horizontal translation, - // (translationCurrX - mTranslationLastX), is divided by the - // image width. We then get the rate by dividing the translation percentage with the - // number of frames. - mPanningRateX = mTotalTranslationX / - (mPreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR) / WINDOW_SIZE; - mPanningRateY = mTotalTranslationY / - (mPreviewHeight / HR_TO_LR_DOWNSAMPLE_FACTOR) / WINDOW_SIZE; - - mTranslationLastX = translationCurrX; - mTranslationLastY = translationCurrY; - mOldestIdx = (mOldestIdx + 1) % WINDOW_SIZE; - } -} diff --git a/src/com/android/camera/MosaicPreviewRenderer.java b/src/com/android/camera/MosaicPreviewRenderer.java deleted file mode 100644 index 3dd44ee86..000000000 --- a/src/com/android/camera/MosaicPreviewRenderer.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera; - -import android.annotation.TargetApi; -import android.graphics.SurfaceTexture; -import android.os.ConditionVariable; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; - -import com.android.gallery3d.common.ApiHelper; - -import javax.microedition.khronos.opengles.GL10; - -@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) // uses SurfaceTexture -public class MosaicPreviewRenderer { - private static final String TAG = "MosaicPreviewRenderer"; - - private int mWidth; // width of the view in UI - private int mHeight; // height of the view in UI - - private boolean mIsLandscape = true; - private final float[] mTransformMatrix = new float[16]; - - private ConditionVariable mEglThreadBlockVar = new ConditionVariable(); - private HandlerThread mEglThread; - private MyHandler mHandler; - private SurfaceTextureRenderer mSTRenderer; - - private SurfaceTexture mInputSurfaceTexture; - - private class MyHandler extends Handler { - public static final int MSG_INIT_SYNC = 0; - public static final int MSG_SHOW_PREVIEW_FRAME_SYNC = 1; - public static final int MSG_SHOW_PREVIEW_FRAME = 2; - public static final int MSG_ALIGN_FRAME_SYNC = 3; - public static final int MSG_RELEASE = 4; - - public MyHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_INIT_SYNC: - doInit(); - mEglThreadBlockVar.open(); - break; - case MSG_SHOW_PREVIEW_FRAME_SYNC: - doShowPreviewFrame(); - mEglThreadBlockVar.open(); - break; - case MSG_SHOW_PREVIEW_FRAME: - doShowPreviewFrame(); - break; - case MSG_ALIGN_FRAME_SYNC: - doAlignFrame(); - mEglThreadBlockVar.open(); - break; - case MSG_RELEASE: - doRelease(); - mEglThreadBlockVar.open(); - break; - } - } - - private void doAlignFrame() { - mInputSurfaceTexture.updateTexImage(); - mInputSurfaceTexture.getTransformMatrix(mTransformMatrix); - - MosaicRenderer.setWarping(true); - // Call preprocess to render it to low-res and high-res RGB textures. - MosaicRenderer.preprocess(mTransformMatrix); - // Now, transfer the textures from GPU to CPU memory for processing - MosaicRenderer.transferGPUtoCPU(); - MosaicRenderer.updateMatrix(); - MosaicRenderer.step(); - } - - private void doShowPreviewFrame() { - mInputSurfaceTexture.updateTexImage(); - mInputSurfaceTexture.getTransformMatrix(mTransformMatrix); - - MosaicRenderer.setWarping(false); - // Call preprocess to render it to low-res and high-res RGB textures. - MosaicRenderer.preprocess(mTransformMatrix); - MosaicRenderer.updateMatrix(); - MosaicRenderer.step(); - } - - private void doInit() { - mInputSurfaceTexture = new SurfaceTexture(MosaicRenderer.init()); - MosaicRenderer.reset(mWidth, mHeight, mIsLandscape); - } - - private void doRelease() { - releaseSurfaceTexture(mInputSurfaceTexture); - mEglThread.quit(); - } - - @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) - private void releaseSurfaceTexture(SurfaceTexture st) { - if (ApiHelper.HAS_RELEASE_SURFACE_TEXTURE) { - st.release(); - } - } - - // Should be called from other thread. - public void sendMessageSync(int msg) { - mEglThreadBlockVar.close(); - sendEmptyMessage(msg); - mEglThreadBlockVar.block(); - } - } - - public MosaicPreviewRenderer(SurfaceTexture tex, int w, int h, boolean isLandscape) { - mIsLandscape = isLandscape; - - mEglThread = new HandlerThread("PanoramaRealtimeRenderer"); - mEglThread.start(); - mHandler = new MyHandler(mEglThread.getLooper()); - mWidth = w; - mHeight = h; - - SurfaceTextureRenderer.FrameDrawer dummy = new SurfaceTextureRenderer.FrameDrawer() { - @Override - public void onDrawFrame(GL10 gl) { - // nothing, we have our draw functions. - } - }; - mSTRenderer = new SurfaceTextureRenderer(tex, mHandler, dummy); - - // We need to sync this because the generation of surface texture for input is - // done here and the client will continue with the assumption that the - // generation is completed. - mHandler.sendMessageSync(MyHandler.MSG_INIT_SYNC); - } - - public void release() { - mSTRenderer.release(); - mHandler.sendMessageSync(MyHandler.MSG_RELEASE); - } - - public void showPreviewFrameSync() { - mHandler.sendMessageSync(MyHandler.MSG_SHOW_PREVIEW_FRAME_SYNC); - mSTRenderer.draw(true); - } - - public void showPreviewFrame() { - mHandler.sendEmptyMessage(MyHandler.MSG_SHOW_PREVIEW_FRAME); - mSTRenderer.draw(false); - } - - public void alignFrameSync() { - mHandler.sendMessageSync(MyHandler.MSG_ALIGN_FRAME_SYNC); - mSTRenderer.draw(true); - } - - public SurfaceTexture getInputSurfaceTexture() { - return mInputSurfaceTexture; - } -} diff --git a/src/com/android/camera/MosaicRenderer.java b/src/com/android/camera/MosaicRenderer.java deleted file mode 100644 index c50ca0d52..000000000 --- a/src/com/android/camera/MosaicRenderer.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera; - -/** - * The Java interface to JNI calls regarding mosaic preview rendering. - * - */ -public class MosaicRenderer -{ - static - { - System.loadLibrary("jni_mosaic"); - } - - /** - * Function to be called in onSurfaceCreated() to initialize - * the GL context, load and link the shaders and create the - * program. Returns a texture ID to be used for SurfaceTexture. - * - * @return textureID the texture ID of the newly generated texture to - * be assigned to the SurfaceTexture object. - */ - public static native int init(); - - /** - * Pass the drawing surface's width and height to initialize the - * renderer viewports and FBO dimensions. - * - * @param width width of the drawing surface in pixels. - * @param height height of the drawing surface in pixels. - * @param isLandscapeOrientation is the orientation of the activity layout in landscape. - */ - public static native void reset(int width, int height, boolean isLandscapeOrientation); - - /** - * Calling this function will render the SurfaceTexture to a new 2D texture - * using the provided STMatrix. - * - * @param stMatrix texture coordinate transform matrix obtained from the - * Surface texture - */ - public static native void preprocess(float[] stMatrix); - - /** - * This function calls glReadPixels to transfer both the low-res and high-res - * data from the GPU memory to the CPU memory for further processing by the - * mosaicing library. - */ - public static native void transferGPUtoCPU(); - - /** - * Function to be called in onDrawFrame() to update the screen with - * the new frame data. - */ - public static native void step(); - - /** - * Call this function when a new low-res frame has been processed by - * the mosaicing library. This will tell the renderer library to - * update its texture and warping transformation. Any calls to step() - * after this call will use the new image frame and transformation data. - */ - public static native void updateMatrix(); - - /** - * This function allows toggling between showing the input image data - * (without applying any warp) and the warped image data. For running - * the renderer as a viewfinder, we set the flag to false. To see the - * preview mosaic, we set the flag to true. - * - * @param flag boolean flag to set the warping to true or false. - */ - public static native void setWarping(boolean flag); -} diff --git a/src/com/android/camera/PanoProgressBar.java b/src/com/android/camera/PanoProgressBar.java deleted file mode 100644 index 8dfb3660b..000000000 --- a/src/com/android/camera/PanoProgressBar.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.widget.ImageView; - -class PanoProgressBar extends ImageView { - @SuppressWarnings("unused") - private static final String TAG = "PanoProgressBar"; - public static final int DIRECTION_NONE = 0; - public static final int DIRECTION_LEFT = 1; - public static final int DIRECTION_RIGHT = 2; - private float mProgress = 0; - private float mMaxProgress = 0; - private float mLeftMostProgress = 0; - private float mRightMostProgress = 0; - private float mProgressOffset = 0; - private float mIndicatorWidth = 0; - private int mDirection = 0; - private final Paint mBackgroundPaint = new Paint(); - private final Paint mDoneAreaPaint = new Paint(); - private final Paint mIndicatorPaint = new Paint(); - private float mWidth; - private float mHeight; - private RectF mDrawBounds; - private OnDirectionChangeListener mListener = null; - - public interface OnDirectionChangeListener { - public void onDirectionChange(int direction); - } - - public PanoProgressBar(Context context, AttributeSet attrs) { - super(context, attrs); - mDoneAreaPaint.setStyle(Paint.Style.FILL); - mDoneAreaPaint.setAlpha(0xff); - - mBackgroundPaint.setStyle(Paint.Style.FILL); - mBackgroundPaint.setAlpha(0xff); - - mIndicatorPaint.setStyle(Paint.Style.FILL); - mIndicatorPaint.setAlpha(0xff); - - mDrawBounds = new RectF(); - } - - public void setOnDirectionChangeListener(OnDirectionChangeListener l) { - mListener = l; - } - - private void setDirection(int direction) { - if (mDirection != direction) { - mDirection = direction; - if (mListener != null) { - mListener.onDirectionChange(mDirection); - } - invalidate(); - } - } - - public int getDirection() { - return mDirection; - } - - @Override - public void setBackgroundColor(int color) { - mBackgroundPaint.setColor(color); - invalidate(); - } - - public void setDoneColor(int color) { - mDoneAreaPaint.setColor(color); - invalidate(); - } - - public void setIndicatorColor(int color) { - mIndicatorPaint.setColor(color); - invalidate(); - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - mWidth = w; - mHeight = h; - mDrawBounds.set(0, 0, mWidth, mHeight); - } - - public void setMaxProgress(int progress) { - mMaxProgress = progress; - } - - public void setIndicatorWidth(float w) { - mIndicatorWidth = w; - invalidate(); - } - - public void setRightIncreasing(boolean rightIncreasing) { - if (rightIncreasing) { - mLeftMostProgress = 0; - mRightMostProgress = 0; - mProgressOffset = 0; - setDirection(DIRECTION_RIGHT); - } else { - mLeftMostProgress = mWidth; - mRightMostProgress = mWidth; - mProgressOffset = mWidth; - setDirection(DIRECTION_LEFT); - } - invalidate(); - } - - public void setProgress(int progress) { - // The panning direction will be decided after user pan more than 10 degrees in one - // direction. - if (mDirection == DIRECTION_NONE) { - if (progress > 10) { - setRightIncreasing(true); - } else if (progress < -10) { - setRightIncreasing(false); - } - } - // mDirection might be modified by setRightIncreasing() above. Need to check again. - if (mDirection != DIRECTION_NONE) { - mProgress = progress * mWidth / mMaxProgress + mProgressOffset; - // value bounds. - mProgress = Math.min(mWidth, Math.max(0, mProgress)); - if (mDirection == DIRECTION_RIGHT) { - // The right most progress is adjusted. - mRightMostProgress = Math.max(mRightMostProgress, mProgress); - } - if (mDirection == DIRECTION_LEFT) { - // The left most progress is adjusted. - mLeftMostProgress = Math.min(mLeftMostProgress, mProgress); - } - invalidate(); - } - } - - public void reset() { - mProgress = 0; - mProgressOffset = 0; - setDirection(DIRECTION_NONE); - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - // the background - canvas.drawRect(mDrawBounds, mBackgroundPaint); - if (mDirection != DIRECTION_NONE) { - // the progress area - canvas.drawRect(mLeftMostProgress, mDrawBounds.top, mRightMostProgress, - mDrawBounds.bottom, mDoneAreaPaint); - // the indication bar - float l; - float r; - if (mDirection == DIRECTION_RIGHT) { - l = Math.max(mProgress - mIndicatorWidth, 0f); - r = mProgress; - } else { - l = mProgress; - r = Math.min(mProgress + mIndicatorWidth, mWidth); - } - canvas.drawRect(l, mDrawBounds.top, r, mDrawBounds.bottom, mIndicatorPaint); - } - - // draw the mask image on the top for shaping. - super.onDraw(canvas); - } -} diff --git a/src/com/android/camera/PanoUtil.java b/src/com/android/camera/PanoUtil.java deleted file mode 100644 index e50eaccc8..000000000 --- a/src/com/android/camera/PanoUtil.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera; - -import java.text.SimpleDateFormat; -import java.util.Date; - -public class PanoUtil { - public static String createName(String format, long dateTaken) { - Date date = new Date(dateTaken); - SimpleDateFormat dateFormat = new SimpleDateFormat(format); - return dateFormat.format(date); - } - - // TODO: Add comments about the range of these two arguments. - public static double calculateDifferenceBetweenAngles(double firstAngle, - double secondAngle) { - double difference1 = (secondAngle - firstAngle) % 360; - if (difference1 < 0) { - difference1 += 360; - } - - double difference2 = (firstAngle - secondAngle) % 360; - if (difference2 < 0) { - difference2 += 360; - } - - return Math.min(difference1, difference2); - } - - public static void decodeYUV420SPQuarterRes(int[] rgb, byte[] yuv420sp, int width, int height) { - final int frameSize = width * height; - - for (int j = 0, ypd = 0; j < height; j += 4) { - int uvp = frameSize + (j >> 1) * width, u = 0, v = 0; - for (int i = 0; i < width; i += 4, ypd++) { - int y = (0xff & (yuv420sp[j * width + i])) - 16; - if (y < 0) { - y = 0; - } - if ((i & 1) == 0) { - v = (0xff & yuv420sp[uvp++]) - 128; - u = (0xff & yuv420sp[uvp++]) - 128; - uvp += 2; // Skip the UV values for the 4 pixels skipped in between - } - int y1192 = 1192 * y; - int r = (y1192 + 1634 * v); - int g = (y1192 - 833 * v - 400 * u); - int b = (y1192 + 2066 * u); - - if (r < 0) { - r = 0; - } else if (r > 262143) { - r = 262143; - } - if (g < 0) { - g = 0; - } else if (g > 262143) { - g = 262143; - } - if (b < 0) { - b = 0; - } else if (b > 262143) { - b = 262143; - } - - rgb[ypd] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | - ((b >> 10) & 0xff); - } - } - } -} diff --git a/src/com/android/gallery3d/filtershow/tools/MatrixFit.java b/src/com/android/gallery3d/filtershow/tools/MatrixFit.java new file mode 100644 index 000000000..3b815673c --- /dev/null +++ b/src/com/android/gallery3d/filtershow/tools/MatrixFit.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.filtershow.tools; + +import android.util.Log; + +public class MatrixFit { + // Simple implementation of a matrix fit in N dimensions. + + private static final String LOGTAG = "MatrixFit"; + + private double[][] mMatrix; + private int mDimension; + private boolean mValid = false; + private static double sEPS = 1.0f/10000000000.0f; + + public MatrixFit(double[][] from, double[][] to) { + mValid = fit(from, to); + } + + public int getDimension() { + return mDimension; + } + + public boolean isValid() { + return mValid; + } + + public double[][] getMatrix() { + return mMatrix; + } + + public boolean fit(double[][] from, double[][] to) { + if ((from.length != to.length) || (from.length < 1)) { + Log.e(LOGTAG, "from and to must be of same size"); + return false; + } + + mDimension = from[0].length; + mMatrix = new double[mDimension +1][mDimension + mDimension +1]; + + if (from.length < mDimension) { + Log.e(LOGTAG, "Too few points => under-determined system"); + return false; + } + + double[][] q = new double[from.length][mDimension]; + for (int i = 0; i < from.length; i++) { + for (int j = 0; j < mDimension; j++) { + q[i][j] = from[i][j]; + } + } + + double[][] p = new double[to.length][mDimension]; + for (int i = 0; i < to.length; i++) { + for (int j = 0; j < mDimension; j++) { + p[i][j] = to[i][j]; + } + } + + // Make an empty (dim) x (dim + 1) matrix and fill it + double[][] c = new double[mDimension+1][mDimension]; + for (int j = 0; j < mDimension; j++) { + for (int k = 0; k < mDimension + 1; k++) { + for (int i = 0; i < q.length; i++) { + double qt = 1; + if (k < mDimension) { + qt = q[i][k]; + } + c[k][j] += qt * p[i][j]; + } + } + } + + // Make an empty (dim+1) x (dim+1) matrix and fill it + double[][] Q = new double[mDimension+1][mDimension+1]; + for (int qi = 0; qi < q.length; qi++) { + double[] qt = new double[mDimension + 1]; + for (int i = 0; i < mDimension; i++) { + qt[i] = q[qi][i]; + } + qt[mDimension] = 1; + for (int i = 0; i < mDimension + 1; i++) { + for (int j = 0; j < mDimension + 1; j++) { + Q[i][j] += qt[i] * qt[j]; + } + } + } + + // Use a gaussian elimination to solve the linear system + for (int i = 0; i < mDimension + 1; i++) { + for (int j = 0; j < mDimension + 1; j++) { + mMatrix[i][j] = Q[i][j]; + } + for (int j = 0; j < mDimension; j++) { + mMatrix[i][mDimension + 1 + j] = c[i][j]; + } + } + if (!gaussianElimination(mMatrix)) { + return false; + } + return true; + } + + public double[] apply(double[] point) { + if (mDimension != point.length) { + return null; + } + double[] res = new double[mDimension]; + for (int j = 0; j < mDimension; j++) { + for (int i = 0; i < mDimension; i++) { + res[j] += point[i] * mMatrix[i][j+ mDimension +1]; + } + res[j] += mMatrix[mDimension][j+ mDimension +1]; + } + return res; + } + + public void printEquation() { + for (int j = 0; j < mDimension; j++) { + String str = "x" + j + "' = "; + for (int i = 0; i < mDimension; i++) { + str += "x" + i + " * " + mMatrix[i][j+mDimension+1] + " + "; + } + str += mMatrix[mDimension][j+mDimension+1]; + Log.v(LOGTAG, str); + } + } + + private void printMatrix(String name, double[][] matrix) { + Log.v(LOGTAG, "name: " + name); + for (int i = 0; i < matrix.length; i++) { + String str = ""; + for (int j = 0; j < matrix[0].length; j++) { + str += "" + matrix[i][j] + " "; + } + Log.v(LOGTAG, str); + } + } + + /* + * Transforms the given matrix into a row echelon matrix + */ + private boolean gaussianElimination(double[][] m) { + int h = m.length; + int w = m[0].length; + + for (int y = 0; y < h; y++) { + int maxrow = y; + for (int y2 = y + 1; y2 < h; y2++) { // Find max pivot + if (Math.abs(m[y2][y]) > Math.abs(m[maxrow][y])) { + maxrow = y2; + } + } + // swap + for (int i = 0; i < mDimension; i++) { + double t = m[y][i]; + m[y][i] = m[maxrow][i]; + m[maxrow][i] = t; + } + + if (Math.abs(m[y][y]) <= sEPS) { // Singular Matrix + return false; + } + for (int y2 = y + 1; y2 < h; y2++) { // Eliminate column y + double c = m[y2][y] / m[y][y]; + for (int x = y; x < w; x++) { + m[y2][x] -= m[y][x] * c; + } + } + } + for (int y = h -1; y > -1; y--) { // Back substitution + double c = m[y][y]; + for (int y2 = 0; y2 < y; y2++) { + for (int x = w - 1; x > y - 1; x--) { + m[y2][x] -= m[y][x] * m[y2][y] / c; + } + } + m[y][y] /= c; + for (int x = h; x < w; x++) { // Normalize row y + m[y][x] /= c; + } + } + return true; + } +} diff --git a/src/com/android/gallery3d/ingest/IngestActivity.java b/src/com/android/gallery3d/ingest/IngestActivity.java index ffc4b50cd..687e9fd44 100644 --- a/src/com/android/gallery3d/ingest/IngestActivity.java +++ b/src/com/android/gallery3d/ingest/IngestActivity.java @@ -75,6 +75,14 @@ public class IngestActivity extends Activity implements private MenuItem mMenuSwitcherItem; private MenuItem mActionMenuSwitcherItem; + // The MTP framework components don't give us fine-grained file copy + // progress updates, so for large photos and videos, we will be stuck + // with a dialog not updating for a long time. To give the user feedback, + // we switch to the animated indeterminate progress bar after the timeout + // specified by INDETERMINATE_SWITCH_TIMEOUT_MS. On the next update from + // the framework, we switch back to the normal progress bar. + private static final int INDETERMINATE_SWITCH_TIMEOUT_MS = 3000; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -437,6 +445,9 @@ public class IngestActivity extends Activity implements mProgressState.current = visitedCount; mProgressState.title = getResources().getString(R.string.ingest_importing); mHandler.sendEmptyMessage(ItemListHandler.MSG_PROGRESS_UPDATE); + mHandler.removeMessages(ItemListHandler.MSG_PROGRESS_INDETERMINATE); + mHandler.sendEmptyMessageDelayed(ItemListHandler.MSG_PROGRESS_INDETERMINATE, + INDETERMINATE_SWITCH_TIMEOUT_MS); } @Override @@ -444,6 +455,7 @@ public class IngestActivity extends Activity implements int numVisited) { // Not guaranteed to be called on the UI thread mHandler.sendEmptyMessage(ItemListHandler.MSG_PROGRESS_HIDE); + mHandler.removeMessages(ItemListHandler.MSG_PROGRESS_INDETERMINATE); // TODO: maybe show an extra dialog listing the ones that failed // importing, if any? } @@ -477,6 +489,11 @@ public class IngestActivity extends Activity implements } } + private void makeProgressDialogIndeterminate() { + ProgressDialog dialog = getProgressDialog(); + dialog.setIndeterminate(true); + } + private void cleanupProgressDialog() { if (mProgressDialog != null) { mProgressDialog.hide(); @@ -490,6 +507,7 @@ public class IngestActivity extends Activity implements public static final int MSG_PROGRESS_HIDE = 1; public static final int MSG_NOTIFY_CHANGED = 2; public static final int MSG_BULK_CHECKED_CHANGE = 3; + public static final int MSG_PROGRESS_INDETERMINATE = 4; WeakReference<IngestActivity> mParentReference; @@ -515,6 +533,9 @@ public class IngestActivity extends Activity implements case MSG_BULK_CHECKED_CHANGE: parent.mPositionMappingCheckBroker.onBulkCheckedChange(); break; + case MSG_PROGRESS_INDETERMINATE: + parent.makeProgressDialogIndeterminate(); + break; default: break; } diff --git a/src/com/android/gallery3d/ingest/ui/MtpImageView.java b/src/com/android/gallery3d/ingest/ui/MtpImageView.java index a773f4485..80c105126 100644 --- a/src/com/android/gallery3d/ingest/ui/MtpImageView.java +++ b/src/com/android/gallery3d/ingest/ui/MtpImageView.java @@ -17,7 +17,9 @@ package com.android.gallery3d.ingest.ui; import android.content.Context; +import android.graphics.Canvas; import android.graphics.Matrix; +import android.graphics.drawable.Drawable; import android.mtp.MtpDevice; import android.mtp.MtpObjectInfo; import android.os.Handler; @@ -27,6 +29,7 @@ import android.os.Message; import android.util.AttributeSet; import android.widget.ImageView; +import com.android.gallery3d.R; import com.android.gallery3d.ingest.MtpDeviceIndex; import com.android.gallery3d.ingest.data.BitmapWithMetadata; import com.android.gallery3d.ingest.data.MtpBitmapFetch; @@ -46,6 +49,8 @@ public class MtpImageView extends ImageView { private MtpObjectInfo mFetchObjectInfo; private MtpDevice mFetchDevice; private Object mFetchResult; + private Drawable mOverlayIcon; + private boolean mShowOverlayIcon; private static final FetchImageHandler sFetchHandler = FetchImageHandler.createOnNewThread(); private static final ShowImageHandler sFetchCompleteHandler = new ShowImageHandler(); @@ -82,6 +87,11 @@ public class MtpImageView extends ImageView { showPlaceholder(); mGeneration = gen; mObjectHandle = handle; + mShowOverlayIcon = MtpDeviceIndex.SUPPORTED_VIDEO_FORMATS.contains(object.getFormat()); + if (mShowOverlayIcon && mOverlayIcon == null) { + mOverlayIcon = getResources().getDrawable(R.drawable.ic_control_play); + updateOverlayIconBounds(); + } synchronized (mFetchLock) { mFetchObjectInfo = object; mFetchDevice = device; @@ -143,12 +153,46 @@ public class MtpImageView extends ImageView { setImageMatrix(mDrawMatrix); } + private static final int OVERLAY_ICON_SIZE_DENOMINATOR = 4; + + private void updateOverlayIconBounds() { + int iheight = mOverlayIcon.getIntrinsicHeight(); + int iwidth = mOverlayIcon.getIntrinsicWidth(); + int vheight = getHeight(); + int vwidth = getWidth(); + float scale_height = ((float) vheight) / (iheight * OVERLAY_ICON_SIZE_DENOMINATOR); + float scale_width = ((float) vwidth) / (iwidth * OVERLAY_ICON_SIZE_DENOMINATOR); + if (scale_height >= 1f && scale_width >= 1f) { + mOverlayIcon.setBounds((vwidth - iwidth) / 2, + (vheight - iheight) / 2, + (vwidth + iwidth) / 2, + (vheight + iheight) / 2); + } else { + float scale = Math.min(scale_height, scale_width); + mOverlayIcon.setBounds((int) (vwidth - scale * iwidth) / 2, + (int) (vheight - scale * iheight) / 2, + (int) (vwidth + scale * iwidth) / 2, + (int) (vheight + scale * iheight) / 2); + } + } + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (changed && getScaleType() == ScaleType.MATRIX) { updateDrawMatrix(); } + if (mShowOverlayIcon && changed && mOverlayIcon != null) { + updateOverlayIconBounds(); + } + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (mShowOverlayIcon && mOverlayIcon != null) { + mOverlayIcon.draw(canvas); + } } protected void onMtpImageDataFetchedFromDevice(Object result) { |