diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-06-23 07:34:55 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2024-06-23 07:34:55 +0000 |
commit | 90c905e5c83b63f6d45ea7066502da0e6423305e (patch) | |
tree | 9e811a75ce6b97fb44a49f2c6006c9fc5e6d301e | |
parent | 2cdbec4b18e31bd81327936630eef9bdc7660c04 (diff) | |
parent | bbc34be67f4b7c4f71036b5d686c34861955f3fb (diff) | |
download | ex-busytown-mac-infra-release.tar.gz |
Merge "Snap for 11992377 from e288e7780dfdd5bb593d3de8271272b758e072c6 to busytown-mac-infra-release" into busytown-mac-infra-releasebusytown-mac-infra-release
64 files changed, 816 insertions, 3178 deletions
diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java index 1f285720..2723c41f 100644 --- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java @@ -29,7 +29,7 @@ import android.util.Log; */ public class ExtensionVersionImpl { private static final String TAG = "ExtenderVersionImpl"; - private static final String VERSION = "1.4.0"; + private static final String VERSION = "1.5.0"; /** * @hide diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java index abcbf5fd..821b11fe 100644 --- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java @@ -20,6 +20,7 @@ import android.annotation.SuppressLint; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; +import android.util.Pair; import android.util.Range; import android.util.Size; @@ -216,4 +217,20 @@ public interface AdvancedExtenderImpl { * @since 1.4 */ boolean isPostviewAvailable(); + + /** + * Returns a list of {@link CameraCharacteristics} key/value pairs for apps to use when + * querying the Extensions specific {@link CameraCharacteristics}. + * + * <p>To ensure the correct {@link CameraCharacteristics} are used when an extension is + * enabled, an application should prioritize the value returned from the list if the + * {@link CameraCharacteristics} key is present. If the key doesn't exist in the returned list, + * then the application should query the value using + * {@link CameraCharacteristics#get(CameraCharacteristics.Key)}. + * + * <p>For example, an extension may limit the zoom ratio range. In this case, an OEM can return + * a new zoom ratio range for the key {@link CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE}. + * @since 1.5 + */ + List<Pair<CameraCharacteristics.Key, Object>> getAvailableCharacteristicsKeyValues(); } diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/BaseAdvancedExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/BaseAdvancedExtenderImpl.java index 00b57058..b97fdbda 100644 --- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/BaseAdvancedExtenderImpl.java +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/BaseAdvancedExtenderImpl.java @@ -26,11 +26,13 @@ import android.content.Context; import android.graphics.ImageFormat; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.StreamConfigurationMap; +import android.hardware.DataSpace; import android.media.Image; import android.media.Image.Plane; import android.media.ImageWriter; @@ -112,7 +114,8 @@ public abstract class BaseAdvancedExtenderImpl implements AdvancedExtenderImpl { @Override public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(String cameraId) { - return filterOutputResolutions(Arrays.asList(ImageFormat.JPEG, ImageFormat.YUV_420_888)); + return filterOutputResolutions(Arrays.asList(ImageFormat.JPEG, ImageFormat.YUV_420_888, + ImageFormat.JPEG_R)); } @Override @@ -157,6 +160,8 @@ public abstract class BaseAdvancedExtenderImpl implements AdvancedExtenderImpl { protected AtomicInteger mNextCaptureSequenceId = new AtomicInteger(1); + protected boolean mProcessCapture = true; + protected void appendTag(String tag) { TAG += tag; } @@ -192,17 +197,33 @@ public abstract class BaseAdvancedExtenderImpl implements AdvancedExtenderImpl { // Image Capture if (mCaptureOutputSurfaceConfig.getSurface() != null) { - Camera2OutputConfigImplBuilder captureOutputConfigBuilder; - captureOutputConfigBuilder = - Camera2OutputConfigImplBuilder.newImageReaderConfig( - mCaptureOutputSurfaceConfig.getSize(), - ImageFormat.YUV_420_888, - BASIC_CAPTURE_PROCESS_MAX_IMAGES); + // For this sample, JPEG_R will not be processed + if (isJpegR(mCaptureOutputSurfaceConfig)) { + Camera2OutputConfigImplBuilder captureOutputConfigBuilder; + + captureOutputConfigBuilder = + Camera2OutputConfigImplBuilder.newSurfaceConfig( + mCaptureOutputSurfaceConfig.getSurface()); + + mCaptureOutputConfig = captureOutputConfigBuilder.build(); + + builder.addOutputConfig(mCaptureOutputConfig); + mProcessCapture = false; + } else { + Camera2OutputConfigImplBuilder captureOutputConfigBuilder; + + captureOutputConfigBuilder = + Camera2OutputConfigImplBuilder.newImageReaderConfig( + mCaptureOutputSurfaceConfig.getSize(), + ImageFormat.YUV_420_888, + BASIC_CAPTURE_PROCESS_MAX_IMAGES, + mCaptureOutputSurfaceConfig.getUsage()); - mCaptureOutputConfig = captureOutputConfigBuilder.build(); + mCaptureOutputConfig = captureOutputConfigBuilder.build(); - builder.addOutputConfig(mCaptureOutputConfig); + builder.addOutputConfig(mCaptureOutputConfig); + } } addSessionParameter(builder); @@ -263,6 +284,19 @@ public abstract class BaseAdvancedExtenderImpl implements AdvancedExtenderImpl { } } + protected boolean isJpegR(OutputSurfaceImpl surfaceConfig) { + // The surface configuration format for JPEG_R can be specified in either format. + // Camera2 uses HAL_PIXEL_FORMAT_BLOB and CameraX uses ImageFormat.JPEG_R. + return ((surfaceConfig.getImageFormat() == JpegEncoder.HAL_PIXEL_FORMAT_BLOB) && + (surfaceConfig.getDataspace() == DataSpace.DATASPACE_JPEG_R)) || + (surfaceConfig.getImageFormat() == ImageFormat.JPEG_R); + } + + protected boolean isJpeg(OutputSurfaceImpl surfaceConfig) { + return (JpegEncoder.imageFormatToPublic(surfaceConfig.getImageFormat()) == + ImageFormat.JPEG) || (surfaceConfig.getImageFormat() == ImageFormat.JPEG); + } + protected void addTriggerRequestKeys(RequestBuilder builder, Map<CaptureRequest.Key<?>, Object> triggers) { HashSet<CaptureRequest.Key> supportedCaptureRequestKeys = @@ -309,13 +343,13 @@ public abstract class BaseAdvancedExtenderImpl implements AdvancedExtenderImpl { @Override public void onCaptureFailed(RequestProcessorImpl.Request request, CaptureFailure captureFailure) { - captureCallback.onCaptureFailed(seqId); + captureCallback.onCaptureFailed(seqId, captureFailure.getReason()); } @Override public void onCaptureBufferLost(RequestProcessorImpl.Request request, long frameNumber, int outputStreamId) { - captureCallback.onCaptureFailed(seqId); + captureCallback.onCaptureFailed(seqId, CaptureFailure.REASON_ERROR); } @Override @@ -340,16 +374,21 @@ public abstract class BaseAdvancedExtenderImpl implements AdvancedExtenderImpl { if (mCaptureOutputSurfaceConfig.getSurface() != null) { synchronized (mLockCaptureSurfaceImageWriter) { - if (JpegEncoder.imageFormatToPublic(mCaptureOutputSurfaceConfig - .getImageFormat()) == ImageFormat.JPEG) { + if (isJpegR(mCaptureOutputSurfaceConfig)) { + return; + } + + if (isJpeg(mCaptureOutputSurfaceConfig)) { mCaptureSurfaceImageWriter = new ImageWriter .Builder(mCaptureOutputSurfaceConfig.getSurface()) .setImageFormat(ImageFormat.JPEG) .setMaxImages(MAX_NUM_IMAGES) // For JPEG format, width x height should be set to (w*h) x 1 // since the JPEG image is returned as a 1D byte array - .setWidthAndHeight(mCaptureOutputSurfaceConfig.getSize().getWidth() - * mCaptureOutputSurfaceConfig.getSize().getHeight(), 1) + .setWidthAndHeight( + mCaptureOutputSurfaceConfig.getSize().getWidth() + * mCaptureOutputSurfaceConfig.getSize().getHeight(), + 1) .build(); } else { mCaptureSurfaceImageWriter = new ImageWriter @@ -402,13 +441,13 @@ public abstract class BaseAdvancedExtenderImpl implements AdvancedExtenderImpl { @Override public void onCaptureFailed(RequestProcessorImpl.Request request, CaptureFailure captureFailure) { - captureCallback.onCaptureFailed(seqId); + captureCallback.onCaptureFailed(seqId, captureFailure.getReason()); } @Override public void onCaptureBufferLost(RequestProcessorImpl.Request request, long frameNumber, int outputStreamId) { - captureCallback.onCaptureFailed(seqId); + captureCallback.onCaptureFailed(seqId, CaptureFailure.REASON_ERROR); } @Override @@ -490,21 +529,25 @@ public abstract class BaseAdvancedExtenderImpl implements AdvancedExtenderImpl { addCaptureResultKeys(seqId, totalCaptureResult, captureCallback); - mImageCaptureCaptureResultImageMatcher.setCameraCaptureCallback( - totalCaptureResult, - requestProcessorRequest.getCaptureStageId()); + if (!mProcessCapture) { + captureCallback.onCaptureProcessStarted(seqId); + } else { + mImageCaptureCaptureResultImageMatcher.setCameraCaptureCallback( + totalCaptureResult, + requestProcessorRequest.getCaptureStageId()); + } } @Override public void onCaptureFailed(RequestProcessorImpl.Request request, CaptureFailure captureFailure) { - captureCallback.onCaptureFailed(seqId); + captureCallback.onCaptureFailed(seqId, captureFailure.getReason()); } @Override public void onCaptureBufferLost(RequestProcessorImpl.Request request, long frameNumber, int outputStreamId) { - captureCallback.onCaptureFailed(seqId); + captureCallback.onCaptureFailed(seqId, CaptureFailure.REASON_ERROR); } @Override @@ -523,7 +566,7 @@ public abstract class BaseAdvancedExtenderImpl implements AdvancedExtenderImpl { mRequestProcessor.submit(requestList, callback); - if (mCaptureOutputSurfaceConfig.getSurface() != null) { + if (mCaptureOutputSurfaceConfig.getSurface() != null && mProcessCapture) { mRequestProcessor.setImageProcessor(mCaptureOutputConfig.getId(), new ImageProcessorImpl() { @Override @@ -696,4 +739,21 @@ public abstract class BaseAdvancedExtenderImpl implements AdvancedExtenderImpl { public boolean isPostviewAvailable() { return false; } + + @Override + public List<Pair<CameraCharacteristics.Key, Object>> getAvailableCharacteristicsKeyValues() { + Range<Float> zoomRange = mCameraCharacteristics + .get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE); + float zoomRangeLower = Math.max(1f, zoomRange.getLower()); + float zoomRangeUpper = Math.min(10f, zoomRange.getUpper()); + return Arrays.asList( + Pair.create(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE, + Range.create(zoomRangeLower, zoomRangeUpper)), + Pair.create(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES, + new int[]{ + CameraMetadata.CONTROL_AF_MODE_OFF, + CameraMetadata.CONTROL_AF_MODE_AUTO + }) + ); + } } diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java index 868be834..9ae5f838 100644 --- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java @@ -49,9 +49,9 @@ public class Camera2OutputConfigImplBuilder { * with the given parameters. */ public static Camera2OutputConfigImplBuilder newImageReaderConfig( - Size size, int imageFormat, int maxImages) { + Size size, int imageFormat, int maxImages, long usage) { return new Camera2OutputConfigImplBuilder( - new ImageReaderOutputConfigImplImpl(size, imageFormat, maxImages)); + new ImageReaderOutputConfigImplImpl(size, imageFormat, maxImages, usage)); } /** @@ -181,11 +181,14 @@ public class Camera2OutputConfigImplBuilder { private Size mSize; private int mImageFormat; private int mMaxImages; + private long mUsage; - ImageReaderOutputConfigImplImpl(Size size, int imageFormat, int maxImages) { + ImageReaderOutputConfigImplImpl(Size size, int imageFormat, int maxImages, + long usage) { mSize = size; mImageFormat = imageFormat; mMaxImages = maxImages; + mUsage = usage; } @Override @@ -202,6 +205,11 @@ public class Camera2OutputConfigImplBuilder { public int getMaxImages() { return mMaxImages; } + + @Override + public long getUsage() { + return mUsage; + } } private static class MultiResolutionImageReaderOutputConfigImplImpl extends OutputConfigImplImpl diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/EyesFreeVideographyAdvancedExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/EyesFreeVideographyAdvancedExtenderImpl.java new file mode 100644 index 00000000..2f6d1cf9 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/EyesFreeVideographyAdvancedExtenderImpl.java @@ -0,0 +1,115 @@ +/* + * Copyright 2024 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.util.Pair; +import android.util.Range; +import android.util.Size; + +import java.util.List; +import java.util.Map; + +/** + * Stub advanced extender implementation for eyes free videography. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.5 + */ +@SuppressLint("UnknownNullness") +public class EyesFreeVideographyAdvancedExtenderImpl implements AdvancedExtenderImpl { + public EyesFreeVideographyAdvancedExtenderImpl() { + } + + @Override + public boolean isExtensionAvailable(String cameraId, + Map<String, CameraCharacteristics> characteristicsMap) { + return false; + } + + @Override + public void init(String cameraId, + Map<String, CameraCharacteristics> characteristicsMap) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public Range<Long> getEstimatedCaptureLatencyRange( + String cameraId, Size size, int imageFormat) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions( + String cameraId) { + throw new RuntimeException("Stub, replace with implementation."); + } + + + @Override + public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions( + String cameraId) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public Map<Integer, List<Size>> getSupportedPostviewResolutions( + Size captureSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<Size> getSupportedYuvAnalysisResolutions( + String cameraId) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public SessionProcessorImpl createSessionProcessor() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<CaptureResult.Key> getAvailableCaptureResultKeys() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isPostviewAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<Pair<CameraCharacteristics.Key, Object>> + getAvailableCharacteristicsKeyValues() { + throw new RuntimeException("Stub, replace with implementation."); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java index d8b99289..68a84b7a 100644 --- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java @@ -129,7 +129,11 @@ public class HdrAdvancedExtenderImpl extends BaseAdvancedExtenderImpl { @Override public int startCapture(@NonNull CaptureCallback captureCallback) { List<RequestProcessorImpl.Request> requestList = new ArrayList<>(); - addCaptureRequestParameters(requestList); + if (mProcessCapture) { + addCaptureRequestParameters(requestList); + } else { + super.addCaptureRequestParameters(requestList); + } final int seqId = mNextCaptureSequenceId.getAndIncrement(); RequestProcessorImpl.Callback callback = new RequestProcessorImpl.Callback() { @@ -138,7 +142,7 @@ public class HdrAdvancedExtenderImpl extends BaseAdvancedExtenderImpl { @Override public void onCaptureStarted(RequestProcessorImpl.Request request, long frameNumber, long timestamp) { - if (!mCaptureStarted) { + if (!mCaptureStarted || !mProcessCapture) { mCaptureStarted = true; captureCallback.onCaptureStarted(seqId, timestamp); } @@ -156,9 +160,14 @@ public class HdrAdvancedExtenderImpl extends BaseAdvancedExtenderImpl { RequestBuilder.RequestProcessorRequest requestProcessorRequest = (RequestBuilder.RequestProcessorRequest) request; - mImageCaptureCaptureResultImageMatcher.setCameraCaptureCallback( - totalCaptureResult, - requestProcessorRequest.getCaptureStageId()); + if (!mProcessCapture) { + captureCallback.onCaptureProcessStarted(seqId); + addCaptureResultKeys(seqId, totalCaptureResult, captureCallback); + } else { + mImageCaptureCaptureResultImageMatcher.setCameraCaptureCallback( + totalCaptureResult, + requestProcessorRequest.getCaptureStageId()); + } } @Override @@ -189,7 +198,7 @@ public class HdrAdvancedExtenderImpl extends BaseAdvancedExtenderImpl { mRequestProcessor.submit(requestList, callback); - if (mCaptureOutputSurfaceConfig.getSurface() != null) { + if (mCaptureOutputSurfaceConfig.getSurface() != null && mProcessCapture) { mRequestProcessor.setImageProcessor(mCaptureOutputConfig.getId(), new ImageProcessorImpl() { boolean mCaptureStarted = false; diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java index ca4dcafa..8ce2733c 100644 --- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java @@ -38,4 +38,9 @@ public interface ImageReaderOutputConfigImpl extends Camera2OutputConfigImpl { * Gets the capacity for TYPE_IMAGEREADER. */ int getMaxImages(); + + /** + * Get the surface usage bits. + */ + long getUsage(); } diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java index 82940635..ba4ea4ca 100644 --- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java @@ -105,7 +105,8 @@ public class NightAdvancedExtenderImpl extends BaseAdvancedExtenderImpl { Camera2OutputConfigImplBuilder.newImageReaderConfig( mPreviewOutputSurfaceConfig.getSize(), ImageFormat.YUV_420_888, - BASIC_CAPTURE_PROCESS_MAX_IMAGES); + BASIC_CAPTURE_PROCESS_MAX_IMAGES, + mPreviewOutputSurfaceConfig.getUsage()); mPreviewOutputConfig = previewOutputConfigBuilder.build(); @@ -114,17 +115,33 @@ public class NightAdvancedExtenderImpl extends BaseAdvancedExtenderImpl { // Image Capture if (mCaptureOutputSurfaceConfig.getSurface() != null) { - Camera2OutputConfigImplBuilder captureOutputConfigBuilder; - captureOutputConfigBuilder = - Camera2OutputConfigImplBuilder.newImageReaderConfig( - mCaptureOutputSurfaceConfig.getSize(), - ImageFormat.YUV_420_888, - BASIC_CAPTURE_PROCESS_MAX_IMAGES); + // For this sample, JPEG_R will not be processed + if (isJpegR(mCaptureOutputSurfaceConfig)) { + Camera2OutputConfigImplBuilder captureOutputConfigBuilder; + + captureOutputConfigBuilder = + Camera2OutputConfigImplBuilder.newSurfaceConfig( + mCaptureOutputSurfaceConfig.getSurface()); + + mCaptureOutputConfig = captureOutputConfigBuilder.build(); - mCaptureOutputConfig = captureOutputConfigBuilder.build(); + builder.addOutputConfig(mCaptureOutputConfig); + mProcessCapture = false; + } else { + Camera2OutputConfigImplBuilder captureOutputConfigBuilder; - builder.addOutputConfig(mCaptureOutputConfig); + captureOutputConfigBuilder = + Camera2OutputConfigImplBuilder.newImageReaderConfig( + mCaptureOutputSurfaceConfig.getSize(), + ImageFormat.YUV_420_888, + BASIC_CAPTURE_PROCESS_MAX_IMAGES, + mCaptureOutputSurfaceConfig.getUsage()); + + mCaptureOutputConfig = captureOutputConfigBuilder.build(); + + builder.addOutputConfig(mCaptureOutputConfig); + } } addSessionParameter(builder); diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java index f6920296..f173c1b7 100644 --- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java @@ -39,4 +39,14 @@ public interface OutputSurfaceImpl { * Gets the image format. */ int getImageFormat(); + + /** + * Gets the dataspace. + */ + int getDataspace(); + + /** + * Get the surface usage bits. + */ + long getUsage(); } diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java index d18aa926..c9eca432 100644 --- a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java @@ -19,6 +19,7 @@ package androidx.camera.extensions.impl.advanced; import android.annotation.SuppressLint; import android.content.Context; import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.util.Pair; @@ -390,5 +391,20 @@ public interface SessionProcessorImpl { * @since 1.4 */ void onCaptureProcessProgressed(int progress); + + /** + * This method is called instead of + * {@link #onCaptureProcessStarted} when the camera device failed + * to produce the required input for the device-specific extension. The + * cause could be a failed camera capture request, a failed + * capture result or dropped camera frame. + * The callback allows clients to be notified + * about failure reason. + * + * @param captureSequenceId id of the current capture sequence + * @param reason The capture failure reason @see CaptureFailure#FailureReason + * @since 1.5 + */ + void onCaptureFailed(int captureSequenceId, int reason); } } diff --git a/camera2/extensions/eyesFreeVidSample/Android.bp b/camera2/extensions/eyesFreeVidSample/Android.bp index 64709bdc..3d9567e8 100644 --- a/camera2/extensions/eyesFreeVidSample/Android.bp +++ b/camera2/extensions/eyesFreeVidSample/Android.bp @@ -22,5 +22,6 @@ android_app { static_libs: [ "androidx.annotation_annotation" ], - platform_apis: true + platform_apis: true, + system_ext_specific: true } diff --git a/camera2/extensions/eyesFreeVidSample/src/android/camera/extensions/impl/service/EyesFreeVidService.java b/camera2/extensions/eyesFreeVidSample/src/android/camera/extensions/impl/service/EyesFreeVidService.java index cf669c7a..d175c1a6 100644 --- a/camera2/extensions/eyesFreeVidSample/src/android/camera/extensions/impl/service/EyesFreeVidService.java +++ b/camera2/extensions/eyesFreeVidSample/src/android/camera/extensions/impl/service/EyesFreeVidService.java @@ -16,6 +16,7 @@ package android.camera.extensions.impl.service; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; @@ -26,15 +27,23 @@ import java.util.Set; import android.annotation.FlaggedApi; import android.graphics.ImageFormat; import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraExtensionCharacteristics; import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.ExtensionCaptureRequest; +import android.hardware.camera2.ExtensionCaptureResult; import android.hardware.camera2.extension.AdvancedExtender; import android.hardware.camera2.extension.CameraExtensionService; import android.hardware.camera2.extension.CharacteristicsMap; import android.hardware.camera2.extension.SessionProcessor; +import android.hardware.camera2.params.StreamConfiguration; +import android.hardware.camera2.params.StreamConfigurationDuration; import android.hardware.camera2.params.StreamConfigurationMap; import android.os.IBinder; +import android.util.Pair; +import android.util.Range; import android.util.Size; import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; @@ -75,7 +84,13 @@ public class EyesFreeVidService extends CameraExtensionService { @Override public AdvancedExtender onInitializeAdvancedExtension(int extensionType) { mCameraManager = getSystemService(CameraManager.class); - return new AdvancedExtenderEyesFreeImpl(mCameraManager); + + switch (extensionType) { + case CameraExtensionCharacteristics.EXTENSION_EYES_FREE_VIDEOGRAPHY: + return new AdvancedExtenderEyesFreeImpl(mCameraManager); + default: + return new AdvancedExtenderImpl(mCameraManager); + } } @FlaggedApi(Flags.FLAG_CONCERT_MODE) @@ -96,7 +111,7 @@ public class EyesFreeVidService extends CameraExtensionService { @FlaggedApi(Flags.FLAG_CONCERT_MODE) @Override - public void init(String cameraId, CharacteristicsMap map) { + public void initialize(String cameraId, CharacteristicsMap map) { mCameraCharacteristics = map.get(cameraId); } @@ -125,6 +140,10 @@ public class EyesFreeVidService extends CameraExtensionService { return formatResolutions; } + protected CameraCharacteristics getCameraCharacteristics() { + return mCameraCharacteristics; + } + @FlaggedApi(Flags.FLAG_CONCERT_MODE) @Override public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions( @@ -146,7 +165,12 @@ public class EyesFreeVidService extends CameraExtensionService { final CaptureRequest.Key [] CAPTURE_REQUEST_SET = {CaptureRequest.CONTROL_ZOOM_RATIO, CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_REGIONS, CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.JPEG_QUALITY, - CaptureRequest.JPEG_ORIENTATION}; + CaptureRequest.JPEG_ORIENTATION, ExtensionCaptureRequest.EFV_PADDING_ZOOM_FACTOR, + ExtensionCaptureRequest.EFV_AUTO_ZOOM, + ExtensionCaptureRequest.EFV_MAX_PADDING_ZOOM_FACTOR, + ExtensionCaptureRequest.EFV_STABILIZATION_MODE, + ExtensionCaptureRequest.EFV_TRANSLATE_VIEWPORT, + ExtensionCaptureRequest.EFV_ROTATE_VIEWPORT}; return Arrays.asList(CAPTURE_REQUEST_SET); } @@ -157,8 +181,192 @@ public class EyesFreeVidService extends CameraExtensionService { final CaptureResult.Key [] CAPTURE_RESULT_SET = {CaptureResult.CONTROL_ZOOM_RATIO, CaptureResult.CONTROL_AF_MODE, CaptureResult.CONTROL_AF_REGIONS, CaptureResult.CONTROL_AF_TRIGGER, CaptureResult.CONTROL_AF_STATE, - CaptureResult.JPEG_QUALITY, CaptureResult.JPEG_ORIENTATION}; + CaptureResult.JPEG_QUALITY, CaptureResult.JPEG_ORIENTATION, + ExtensionCaptureResult.EFV_PADDING_REGION, + ExtensionCaptureResult.EFV_AUTO_ZOOM, + ExtensionCaptureResult.EFV_MAX_PADDING_ZOOM_FACTOR, + ExtensionCaptureResult.EFV_AUTO_ZOOM_PADDING_REGION, + ExtensionCaptureResult.EFV_STABILIZATION_MODE, + ExtensionCaptureResult.EFV_TARGET_COORDINATES, + ExtensionCaptureResult.EFV_PADDING_ZOOM_FACTOR, + ExtensionCaptureResult.EFV_TRANSLATE_VIEWPORT, + ExtensionCaptureResult.EFV_ROTATE_VIEWPORT + }; return Arrays.asList(CAPTURE_RESULT_SET); } + + @FlaggedApi(Flags.FLAG_CAMERA_EXTENSIONS_CHARACTERISTICS_GET) + @Override + public List<Pair<CameraCharacteristics.Key, Object>> + getAvailableCharacteristicsKeyValues() { + Range<Float> zoomRange = mCameraCharacteristics + .get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE); + int[] caps = mCameraCharacteristics + .get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); + + Set<Integer> unsupportedCapabilities = new HashSet<>(Arrays.asList( + CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT, + CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_RAW, + CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING, + CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING, + CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO, + CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING, + CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING, + CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME, + CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA, + CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA, + CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR + )); + + List<Integer> filtered = new ArrayList<>(); + for (int c : caps) { + if (unsupportedCapabilities.contains(c)) { + continue; + } + filtered.add(c); + } + int[] extensionsCaps = new int[filtered.size()]; + for (int i = 0; i < filtered.size(); i++) { + extensionsCaps[i] = filtered.get(i); + } + + Set<Integer> supportedFormats = new HashSet<>( Arrays.asList( + ImageFormat.YUV_420_888, + ImageFormat.JPEG, + ImageFormat.JPEG_R, + ImageFormat.PRIVATE)); + + StreamConfiguration[] configurations = mCameraCharacteristics + .get(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS); + List<StreamConfiguration> tmpConfigs = new ArrayList<>(); + for (StreamConfiguration sc : configurations) { + if (supportedFormats.contains(sc.getFormat())) { + tmpConfigs.add(sc); + } + } + StreamConfiguration[] filteredConfigurations = + new StreamConfiguration[tmpConfigs.size()]; + filteredConfigurations = tmpConfigs.toArray(filteredConfigurations); + + StreamConfigurationDuration[] minFrameDurations = mCameraCharacteristics + .get(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS); + List<StreamConfigurationDuration> tmpMinFrameDurations = new ArrayList<>(); + for (StreamConfigurationDuration scd : minFrameDurations) { + if (supportedFormats.contains(scd.getFormat())) { + tmpMinFrameDurations.add(scd); + } + } + StreamConfigurationDuration[] filteredMinFrameDurations = + new StreamConfigurationDuration[tmpMinFrameDurations.size()]; + filteredMinFrameDurations = tmpMinFrameDurations.toArray(filteredMinFrameDurations); + + StreamConfigurationDuration[] stallDurations = mCameraCharacteristics + .get(CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS); + List<StreamConfigurationDuration> tmpStallDurations = new ArrayList<>(); + for (StreamConfigurationDuration scd : stallDurations) { + if (supportedFormats.contains(scd.getFormat())) { + tmpStallDurations.add(scd); + } + } + StreamConfigurationDuration[] filteredStallDurations = + new StreamConfigurationDuration[tmpStallDurations.size()]; + filteredStallDurations = tmpStallDurations.toArray(filteredStallDurations); + + return Arrays.asList( + Pair.create(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS, + filteredConfigurations), + Pair.create(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS, + filteredMinFrameDurations), + Pair.create(CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS, + filteredStallDurations), + Pair.create(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES, + extensionsCaps), + Pair.create(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE, zoomRange), + Pair.create(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES, + new int[]{ + CameraMetadata.CONTROL_AF_MODE_OFF, + CameraMetadata.CONTROL_AF_MODE_AUTO + }), + Pair.create( + CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES, + new int[]{ CameraCharacteristics + .CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION + }), + Pair.create(CameraExtensionCharacteristics.EFV_PADDING_ZOOM_FACTOR_RANGE, + new Range<Float>(1.0f, 2.0f)) + ); + } + } + + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public static class AdvancedExtenderImpl extends AdvancedExtender { + + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + public AdvancedExtenderImpl(@NonNull CameraManager cameraManager) { + super(cameraManager); + } + + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + @Override + public boolean isExtensionAvailable(String cameraId, + CharacteristicsMap charsMap) { + return false; + } + + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + @Override + public void initialize(String cameraId, CharacteristicsMap map) { + throw new RuntimeException("Extension not supported"); + } + + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + @Override + public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions( + String cameraId) { + throw new RuntimeException("Extension not supported"); + } + + protected Map<Integer, List<Size>> filterOutputResolutions(List<Integer> formats) { + throw new RuntimeException("Extension not supported"); + } + + protected CameraCharacteristics getCameraCharacteristics() { + throw new RuntimeException("Extension not supported"); + } + + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + @Override + public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions( + String cameraId) { + throw new RuntimeException("Extension not supported"); + } + + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + @Override + public SessionProcessor getSessionProcessor() { + throw new RuntimeException("Extension not supported"); + } + + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + @Override + public List<CaptureRequest.Key> getAvailableCaptureRequestKeys( + String cameraId) { + throw new RuntimeException("Extension not supported"); + + } + + @FlaggedApi(Flags.FLAG_CONCERT_MODE) + @Override + public List<CaptureResult.Key> getAvailableCaptureResultKeys( + String cameraId) { + throw new RuntimeException("Extension not supported"); + } + + @FlaggedApi(Flags.FLAG_CAMERA_EXTENSIONS_CHARACTERISTICS_GET) + @Override + public List<Pair<CameraCharacteristics.Key, Object>> + getAvailableCharacteristicsKeyValues() { + throw new RuntimeException("Extension not supported"); + } } } diff --git a/camera2/extensions/eyesFreeVidSample/src/android/camera/extensions/impl/service/EyesFreeVidSessionProcessor.java b/camera2/extensions/eyesFreeVidSample/src/android/camera/extensions/impl/service/EyesFreeVidSessionProcessor.java index f23f3a68..92f371fc 100644 --- a/camera2/extensions/eyesFreeVidSample/src/android/camera/extensions/impl/service/EyesFreeVidSessionProcessor.java +++ b/camera2/extensions/eyesFreeVidSample/src/android/camera/extensions/impl/service/EyesFreeVidSessionProcessor.java @@ -16,21 +16,28 @@ package android.camera.extensions.impl.service; -import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Queue; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import android.annotation.FlaggedApi; import android.annotation.NonNull; -import android.annotation.Nullable; import android.camera.extensions.impl.service.EyesFreeVidService.AdvancedExtenderEyesFreeImpl; +import android.graphics.PointF; +import android.graphics.Rect; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.ExtensionCaptureRequest; +import android.hardware.camera2.ExtensionCaptureResult; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.extension.CameraOutputSurface; import android.hardware.camera2.extension.CharacteristicsMap; @@ -39,6 +46,7 @@ import android.hardware.camera2.extension.ExtensionOutputConfiguration; import android.hardware.camera2.extension.RequestProcessor; import android.hardware.camera2.extension.SessionProcessor; import android.hardware.camera2.impl.CameraMetadataNative; +import android.hardware.camera2.utils.SurfaceUtils; import android.media.Image; import android.media.ImageReader; import android.media.ImageWriter; @@ -70,7 +78,7 @@ public class EyesFreeVidSessionProcessor extends SessionProcessor { protected final Object mParametersLock = new Object(); @GuardedBy("mParametersLock") - protected List<Pair<CaptureRequest.Key, Object>> mParameters = new ArrayList<>(); + protected HashMap<CaptureRequest.Key, Object> mParametersMap = new HashMap<>(); protected AtomicInteger mNextCaptureSequenceId = new AtomicInteger(1); @@ -83,6 +91,8 @@ public class EyesFreeVidSessionProcessor extends SessionProcessor { protected ImageReader mPreviewImageReader; protected String mCameraId; + protected AtomicBoolean mOnCaptureSessionEndStarted = new AtomicBoolean(false); + @FlaggedApi(Flags.FLAG_CONCERT_MODE) protected EyesFreeVidSessionProcessor(AdvancedExtenderEyesFreeImpl advancedExtender) { mAdvancedExtender = advancedExtender; @@ -105,7 +115,7 @@ public class EyesFreeVidSessionProcessor extends SessionProcessor { List<CameraOutputSurface> captureList = new ArrayList<>(List.of(imageCaptureSurface)); ExtensionOutputConfiguration captureConfig = new ExtensionOutputConfiguration( - captureList, CAPTURE_OUTPUT_ID, null, 0); + captureList, CAPTURE_OUTPUT_ID, null, -1); outputs.add(captureConfig); } @@ -114,14 +124,14 @@ public class EyesFreeVidSessionProcessor extends SessionProcessor { if (previewSurface.getSurface() != null) { mPreviewImageReader = ImageReader.newInstance(previewSurface.getSize().getWidth(), previewSurface.getSize().getHeight(), previewSurface.getImageFormat(), - MAX_NUM_IMAGES); + MAX_NUM_IMAGES, SurfaceUtils.getSurfaceUsage(previewSurface.getSurface())); List<CameraOutputSurface> previewList = new ArrayList<>(List.of( new CameraOutputSurface(mPreviewImageReader.getSurface(), previewSurface.getSize()))); ExtensionOutputConfiguration previewConfig = new ExtensionOutputConfiguration( - previewList, PREVIEW_OUTPUT_ID, null, 0); + previewList, PREVIEW_OUTPUT_ID, null, -1); outputs.add(previewConfig); } @@ -181,20 +191,25 @@ public class EyesFreeVidSessionProcessor extends SessionProcessor { @FlaggedApi(Flags.FLAG_CONCERT_MODE) @Override public void onCaptureSessionEnd() { + mOnCaptureSessionEndStarted.set(true); + + if (mRequestProcessor != null) { + mRequestProcessor.abortCaptures(); + } + mRequestProcessor = null; } @FlaggedApi(Flags.FLAG_CONCERT_MODE) @Override - public int startRepeating(@Nullable Executor executor, + public int startRepeating(@NonNull Executor executor, @NonNull CaptureCallback captureCallback) { List<Integer> outputConfigIds = new ArrayList<>(List.of(PREVIEW_OUTPUT_ID)); RequestProcessor.Request requestRes; - synchronized (mParametersLock) { - requestRes = new RequestProcessor.Request(outputConfigIds, mParameters, - CameraDevice.TEMPLATE_PREVIEW); - } + + requestRes = new RequestProcessor.Request(outputConfigIds, convertParameterMapToList(), + CameraDevice.TEMPLATE_PREVIEW); final int seqId = mNextCaptureSequenceId.getAndIncrement(); @@ -202,7 +217,9 @@ public class EyesFreeVidSessionProcessor extends SessionProcessor { @Override public void onCaptureStarted(RequestProcessor.Request request, long frameNumber, long timestamp) { - captureCallback.onCaptureStarted(seqId, timestamp); + if (!mOnCaptureSessionEndStarted.get()) { + captureCallback.onCaptureStarted(seqId, timestamp); + } } @Override @@ -213,34 +230,48 @@ public class EyesFreeVidSessionProcessor extends SessionProcessor { @Override public void onCaptureCompleted(RequestProcessor.Request request, TotalCaptureResult totalCaptureResult) { - addCaptureResultKeys(seqId, totalCaptureResult, captureCallback); - captureCallback.onCaptureProcessStarted(seqId); + if (!mOnCaptureSessionEndStarted.get()) { + addCaptureResultKeys(seqId, totalCaptureResult, captureCallback, request); + captureCallback.onCaptureProcessStarted(seqId); + } } @Override public void onCaptureFailed(RequestProcessor.Request request, CaptureFailure captureFailure) { - captureCallback.onCaptureFailed(seqId); + if (!mOnCaptureSessionEndStarted.get()) { + captureCallback.onCaptureFailed(seqId, captureFailure.getReason()); + } } @Override public void onCaptureBufferLost(RequestProcessor.Request request, long frameNumber, int outputStreamId) { - captureCallback.onCaptureFailed(seqId); + if (!mOnCaptureSessionEndStarted.get()) { + captureCallback.onCaptureFailed(seqId, CaptureFailure.REASON_ERROR); + } } @Override public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) { - captureCallback.onCaptureSequenceCompleted(seqId); + if (!mOnCaptureSessionEndStarted.get()) { + captureCallback.onCaptureSequenceCompleted(seqId); + } } @Override public void onCaptureSequenceAborted(int sequenceId) { - captureCallback.onCaptureSequenceAborted(seqId); + if (!mOnCaptureSessionEndStarted.get()) { + captureCallback.onCaptureSequenceAborted(seqId); + } } }; - mRequestProcessor.setRepeating(requestRes, executor, resCallback); + try { + mRequestProcessor.setRepeating(requestRes, executor, resCallback); + } catch(CameraAccessException e) { + return -1; + } return seqId; } @@ -248,26 +279,100 @@ public class EyesFreeVidSessionProcessor extends SessionProcessor { protected void addCaptureResultKeys( @NonNull int seqId, @NonNull TotalCaptureResult result, - @NonNull CaptureCallback captureCallback) { - - CameraMetadataNative cameraMetadataNative = new CameraMetadataNative(); - long vendorId = mAdvancedExtender.getMetadataVendorId(mCameraId); - cameraMetadataNative.setVendorId(vendorId); + @NonNull CaptureCallback captureCallback, + @NonNull RequestProcessor.Request request) { Long shutterTimestamp = result.get(CaptureResult.SENSOR_TIMESTAMP); if (shutterTimestamp != null) { List<CaptureResult.Key> captureResultKeys = mAdvancedExtender.getAvailableCaptureResultKeys(mCameraId); + HashMap<CaptureResult.Key, Object> captureResults = new HashMap<>(); for (CaptureResult.Key key : captureResultKeys) { if (result.get(key) != null) { - cameraMetadataNative.set(key, result.get(key)); + captureResults.put(key, result.get(key)); + } + } + + synchronized (mParametersLock) { + List<Pair<CaptureRequest.Key, Object>> requestParameters = request.getParameters(); + boolean autoZoomEnabled = false; + boolean stabilizationModeLocked = false; + for (Pair<CaptureRequest.Key, Object> parameter : requestParameters) { + if (ExtensionCaptureRequest.EFV_AUTO_ZOOM.equals(parameter.first)) { + captureResults.put(ExtensionCaptureResult.EFV_AUTO_ZOOM, + (boolean) parameter.second); + autoZoomEnabled = (boolean) parameter.second; + if (autoZoomEnabled && + ExtensionCaptureRequest.EFV_MAX_PADDING_ZOOM_FACTOR.equals( + parameter.first)) { + captureResults.put( + ExtensionCaptureResult.EFV_MAX_PADDING_ZOOM_FACTOR, + (Float) parameter.second); + } + } + if (ExtensionCaptureRequest.EFV_PADDING_ZOOM_FACTOR.equals(parameter.first)) { + captureResults.put(ExtensionCaptureResult.EFV_PADDING_ZOOM_FACTOR, + (Float) parameter.second); + } + if (ExtensionCaptureRequest.EFV_TRANSLATE_VIEWPORT.equals(parameter.first)) { + captureResults.put(ExtensionCaptureResult.EFV_TRANSLATE_VIEWPORT, + (Pair<Integer, Integer>) parameter.second); + } + if (ExtensionCaptureRequest.EFV_ROTATE_VIEWPORT.equals(parameter.first)) { + captureResults.put(ExtensionCaptureResult.EFV_ROTATE_VIEWPORT, + (Float) parameter.second); + } + if (ExtensionCaptureRequest.EFV_STABILIZATION_MODE.equals(parameter.first)) { + if (ExtensionCaptureRequest.EFV_STABILIZATION_MODE_LOCKED == + (int) parameter.second) { + stabilizationModeLocked = true; + int[] samplePaddingRegion = {5, 5, 5, 5}; + captureResults.put(ExtensionCaptureResult.EFV_PADDING_REGION, + samplePaddingRegion); + CameraCharacteristics cameraCharacteristics = + mAdvancedExtender.getCameraCharacteristics(); + Rect arraySize = cameraCharacteristics.get( + CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); + int centerX = arraySize.width() / 2; + int centerY = arraySize.height() / 2; + int squareSize = 5; + PointF[] sampleTargetCoordinates = new PointF[]{ + new PointF(centerX - squareSize, centerY - squareSize), + new PointF(centerX + squareSize, centerY - squareSize), + new PointF(centerX + squareSize, centerY + squareSize), + new PointF(centerX - squareSize, centerY + squareSize) + }; + captureResults.put(ExtensionCaptureResult.EFV_TARGET_COORDINATES, + sampleTargetCoordinates); + } + } } + + if (autoZoomEnabled && stabilizationModeLocked) { + int[] sampleAutoZoomPaddingRegion = {3, 3, 3, 3}; + captureResults.put(ExtensionCaptureResult.EFV_AUTO_ZOOM_PADDING_REGION, + sampleAutoZoomPaddingRegion); + } + } + + captureCallback.onCaptureCompleted(shutterTimestamp, seqId, captureResults); + } + } + + protected List<Pair<CaptureRequest.Key, Object>> convertParameterMapToList() { + List<Pair<CaptureRequest.Key, Object>> mParametersList = new ArrayList<>(); + + synchronized(mParametersLock) { + for (Map.Entry<CaptureRequest.Key, Object> entry : mParametersMap.entrySet()) { + CaptureRequest.Key key = entry.getKey(); + Object value = entry.getValue(); + Pair<CaptureRequest.Key, Object> pair = new Pair<>(key, value); + mParametersList.add(pair); } - CaptureResult captureResult = new CaptureResult(cameraMetadataNative, seqId); - captureCallback.onCaptureCompleted(shutterTimestamp, seqId, - captureResult); } + + return mParametersList; } @FlaggedApi(Flags.FLAG_CONCERT_MODE) @@ -278,15 +383,13 @@ public class EyesFreeVidSessionProcessor extends SessionProcessor { @FlaggedApi(Flags.FLAG_CONCERT_MODE) @Override - public int startCapture(@Nullable Executor executor, + public int startMultiFrameCapture(@NonNull Executor executor, @NonNull CaptureCallback captureCallback) { List<Integer> outputConfigIds = new ArrayList<>(List.of(CAPTURE_OUTPUT_ID)); RequestProcessor.Request requestRes; - synchronized (mParametersLock) { - requestRes = new RequestProcessor.Request(outputConfigIds, mParameters, + requestRes = new RequestProcessor.Request(outputConfigIds, convertParameterMapToList(), CameraDevice.TEMPLATE_PREVIEW); - } final int seqId = mNextCaptureSequenceId.getAndIncrement(); @@ -307,19 +410,19 @@ public class EyesFreeVidSessionProcessor extends SessionProcessor { @Override public void onCaptureCompleted(RequestProcessor.Request request, TotalCaptureResult totalCaptureResult) { - addCaptureResultKeys(seqId, totalCaptureResult, captureCallback); + addCaptureResultKeys(seqId, totalCaptureResult, captureCallback, request); } @Override public void onCaptureFailed(RequestProcessor.Request request, CaptureFailure captureFailure) { - captureCallback.onCaptureFailed(seqId); + captureCallback.onCaptureFailed(seqId, captureFailure.getReason()); } @Override public void onCaptureBufferLost(RequestProcessor.Request request, long frameNumber, int outputStreamId) { - captureCallback.onCaptureFailed(seqId); + captureCallback.onCaptureFailed(seqId, CaptureFailure.REASON_ERROR); } @Override @@ -333,7 +436,11 @@ public class EyesFreeVidSessionProcessor extends SessionProcessor { } }; - mRequestProcessor.submit(requestRes, executor, resCallback); + try { + mRequestProcessor.submit(requestRes, executor, resCallback); + } catch(CameraAccessException e) { + return -1; + } captureCallback.onCaptureProcessStarted(seqId); return seqId; @@ -342,7 +449,7 @@ public class EyesFreeVidSessionProcessor extends SessionProcessor { @FlaggedApi(Flags.FLAG_CONCERT_MODE) @Override public int startTrigger(@NonNull CaptureRequest captureRequest, - @Nullable Executor executor, @NonNull CaptureCallback captureCallback) { + @NonNull Executor executor, @NonNull CaptureCallback captureCallback) { List<Integer> outputConfigIds = new ArrayList<>(List.of(PREVIEW_OUTPUT_ID)); RequestProcessor.Request requestRes = new RequestProcessor.Request(outputConfigIds, @@ -367,19 +474,19 @@ public class EyesFreeVidSessionProcessor extends SessionProcessor { @Override public void onCaptureCompleted(RequestProcessor.Request request, TotalCaptureResult totalCaptureResult) { - addCaptureResultKeys(seqId, totalCaptureResult, captureCallback); + addCaptureResultKeys(seqId, totalCaptureResult, captureCallback, request); } @Override public void onCaptureFailed(RequestProcessor.Request request, CaptureFailure captureFailure) { - captureCallback.onCaptureFailed(seqId); + captureCallback.onCaptureFailed(seqId, captureFailure.getReason()); } @Override public void onCaptureBufferLost(RequestProcessor.Request request, long frameNumber, int outputStreamId) { - captureCallback.onCaptureFailed(seqId); + captureCallback.onCaptureFailed(seqId, CaptureFailure.REASON_ERROR); } @Override @@ -393,7 +500,11 @@ public class EyesFreeVidSessionProcessor extends SessionProcessor { } }; - mRequestProcessor.submit(requestRes, executor, resCallback); + try { + mRequestProcessor.submit(requestRes, executor, resCallback); + } catch(CameraAccessException e) { + return -1; + } captureCallback.onCaptureProcessStarted(seqId); return seqId; @@ -419,9 +530,16 @@ public class EyesFreeVidSessionProcessor extends SessionProcessor { @Override public void setParameters(@NonNull CaptureRequest captureRequest) { synchronized (mParametersLock) { - for (CaptureRequest.Key<?> key : captureRequest.getKeys()) { - Object value = captureRequest.get(key); - mParameters.add(new Pair<>(key, value)); + + List<CaptureRequest.Key> supportedCaptureRequestKeys = + mAdvancedExtender.getAvailableCaptureRequestKeys(mCameraId); + List<CaptureRequest.Key<?>> requestedCaptureRequestKeys = + captureRequest.getKeys(); + for (CaptureRequest.Key<?> key : requestedCaptureRequestKeys) { + if (supportedCaptureRequestKeys.contains(key)) { + Object value = captureRequest.get(key); + mParametersMap.put(key, value); + } } } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java index d13efc85..abf8d453 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java @@ -20,6 +20,7 @@ import android.annotation.SuppressLint; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; +import android.util.Pair; import android.util.Range; import android.util.Size; @@ -216,4 +217,23 @@ public interface AdvancedExtenderImpl { * @since 1.4 */ boolean isPostviewAvailable(); + + /** + * Returns a list of {@link CameraCharacteristics} key/value pairs for apps to use when + * querying {@link CameraExtensionCharacteristics#get} and + * {@link CameraExtensionCharacteristics#getKeys}. The key/value pairs define the limitations + * on the controls returned from {@link #getAvailableCaptureRequestKeys}. If a key is not + * present in the returned list, then the capability is either undefined or unsupported. + * + * <p>For example, an extension may limit the zoom ratio range. In this case, an OEM returns + * a new zoom ratio range for the key {@link CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE}. + * + * <p>Similarly, an extension may support preview stabilization. In this case, the OEM returns + * the array containing the elements + * {@link CameraCharacteristics#CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION} and + * {@link CameraCharacteristics#CONTROL_VIDEO_STABILIZATION_MODE_OFF} for the key + * {@link CameraCharacteristics#CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES}. + * @since 1.5 + */ + List<Pair<CameraCharacteristics.Key, Object>> getAvailableCharacteristicsKeyValues(); } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java index 8c3ac11c..45e4fccb 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java @@ -20,6 +20,7 @@ import android.annotation.SuppressLint; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; +import android.util.Pair; import android.util.Range; import android.util.Size; @@ -104,4 +105,9 @@ public class AutoAdvancedExtenderImpl implements AdvancedExtenderImpl { public boolean isPostviewAvailable() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public List<Pair<CameraCharacteristics.Key, Object>> getAvailableCharacteristicsKeyValues() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java index 135306c8..8bb17f7e 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java @@ -20,6 +20,7 @@ import android.annotation.SuppressLint; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; +import android.util.Pair; import android.util.Range; import android.util.Size; @@ -104,4 +105,9 @@ public class BeautyAdvancedExtenderImpl implements AdvancedExtenderImpl { public boolean isPostviewAvailable() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public List<Pair<CameraCharacteristics.Key, Object>> getAvailableCharacteristicsKeyValues() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java index fa4ad0dc..71b38d64 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java @@ -20,6 +20,7 @@ import android.annotation.SuppressLint; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; +import android.util.Pair; import android.util.Range; import android.util.Size; @@ -104,4 +105,9 @@ public class BokehAdvancedExtenderImpl implements AdvancedExtenderImpl { public boolean isPostviewAvailable() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public List<Pair<CameraCharacteristics.Key, Object>> getAvailableCharacteristicsKeyValues() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java index a66d3ceb..365abc3c 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java @@ -49,9 +49,9 @@ public class Camera2OutputConfigImplBuilder { * with the given parameters. */ public static Camera2OutputConfigImplBuilder newImageReaderConfig( - Size size, int imageFormat, int maxImages) { + Size size, int imageFormat, int maxImages, long usage) { return new Camera2OutputConfigImplBuilder( - new ImageReaderOutputConfigImplImpl(size, imageFormat, maxImages)); + new ImageReaderOutputConfigImplImpl(size, imageFormat, maxImages, usage)); } /** @@ -181,11 +181,14 @@ public class Camera2OutputConfigImplBuilder { private Size mSize; private int mImageFormat; private int mMaxImages; + private long mUsage; - ImageReaderOutputConfigImplImpl(Size size, int imageFormat, int maxImages) { + ImageReaderOutputConfigImplImpl(Size size, int imageFormat, int maxImages, + long usage) { mSize = size; mImageFormat = imageFormat; mMaxImages = maxImages; + mUsage = usage; } @Override @@ -202,6 +205,11 @@ public class Camera2OutputConfigImplBuilder { public int getMaxImages() { return mMaxImages; } + + @Override + public long getUsage() { + return mUsage; + } } private static class MultiResolutionImageReaderOutputConfigImplImpl extends OutputConfigImplImpl diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/EyesFreeVideographyAdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/EyesFreeVideographyAdvancedExtenderImpl.java index 01b65be0..f2eb3991 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/EyesFreeVideographyAdvancedExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/EyesFreeVideographyAdvancedExtenderImpl.java @@ -20,6 +20,7 @@ import android.annotation.SuppressLint; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; +import android.util.Pair; import android.util.Range; import android.util.Size; @@ -105,4 +106,10 @@ public class EyesFreeVideographyAdvancedExtenderImpl implements AdvancedExtender public boolean isPostviewAvailable() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public List<Pair<CameraCharacteristics.Key, Object>> + getAvailableCharacteristicsKeyValues() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java index dc5b2b60..7d87cc6e 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java @@ -20,6 +20,7 @@ import android.annotation.SuppressLint; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; +import android.util.Pair; import android.util.Range; import android.util.Size; @@ -105,4 +106,9 @@ public class HdrAdvancedExtenderImpl implements AdvancedExtenderImpl { public boolean isPostviewAvailable() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public List<Pair<CameraCharacteristics.Key, Object>> getAvailableCharacteristicsKeyValues() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java index ca4dcafa..7c3b48e0 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java @@ -38,4 +38,9 @@ public interface ImageReaderOutputConfigImpl extends Camera2OutputConfigImpl { * Gets the capacity for TYPE_IMAGEREADER. */ int getMaxImages(); + + /** + * Gets the surface usage bits. + */ + long getUsage(); } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java index 5b0ed8ee..961d6695 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java @@ -20,6 +20,7 @@ import android.annotation.SuppressLint; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; +import android.util.Pair; import android.util.Range; import android.util.Size; @@ -104,4 +105,9 @@ public class NightAdvancedExtenderImpl implements AdvancedExtenderImpl { public boolean isPostviewAvailable() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public List<Pair<CameraCharacteristics.Key, Object>> getAvailableCharacteristicsKeyValues() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java index f6920296..7bf8df2f 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java @@ -39,4 +39,14 @@ public interface OutputSurfaceImpl { * Gets the image format. */ int getImageFormat(); + + /** + * Gets the dataspace. + */ + int getDataspace(); + + /** + * Gets the surface usage bits. + */ + long getUsage(); } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java index 2e5603b5..57fffd02 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java @@ -19,6 +19,7 @@ package androidx.camera.extensions.impl.advanced; import android.annotation.SuppressLint; import android.content.Context; import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.util.Pair; @@ -390,5 +391,20 @@ public interface SessionProcessorImpl { * @since 1.4 */ void onCaptureProcessProgressed(int progress); + + /** + * This method is called instead of + * {@link #onCaptureProcessStarted} when the camera device failed + * to produce the required input for the device-specific extension. The + * cause could be a failed camera capture request, a failed + * capture result or dropped camera frame. + * The callback allows clients to be notified + * about failure reason. + * + * @param captureSequenceId id of the current capture sequence + * @param reason The capture failure reason @see CaptureFailure#FailureReason + * @since 1.5 + */ + void onCaptureFailed(int captureSequenceId, int reason); } } diff --git a/common/tests/Android.bp b/common/tests/Android.bp index 5b68d057..8738a5d4 100644 --- a/common/tests/Android.bp +++ b/common/tests/Android.bp @@ -21,9 +21,12 @@ android_test { name: "AndroidCommonTests", certificate: "platform", libs: ["android.test.runner.stubs"], - sdk_version: "8", + sdk_version: "19", srcs: ["src/**/*.java"], - static_libs: ["android-common"], + static_libs: [ + "android-common", + "androidx.test.rules", + ], optimize: { enabled: false, }, diff --git a/common/tests/src/com/android/common/OperationSchedulerTest.java b/common/tests/src/com/android/common/OperationSchedulerTest.java index a25544a0..899da495 100644 --- a/common/tests/src/com/android/common/OperationSchedulerTest.java +++ b/common/tests/src/com/android/common/OperationSchedulerTest.java @@ -18,8 +18,9 @@ package com.android.common; import android.content.SharedPreferences; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.filters.MediumTest; +import androidx.test.filters.SmallTest; public class OperationSchedulerTest extends AndroidTestCase { /** diff --git a/common/tests/src/com/android/common/Rfc822ValidatorTest.java b/common/tests/src/com/android/common/Rfc822ValidatorTest.java index 61b8f252..f9bc8080 100644 --- a/common/tests/src/com/android/common/Rfc822ValidatorTest.java +++ b/common/tests/src/com/android/common/Rfc822ValidatorTest.java @@ -16,7 +16,7 @@ package com.android.common; -import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.filters.SmallTest; import junit.framework.TestCase; diff --git a/common/tests/src/com/android/common/widget/CompositeCursorAdapterTest.java b/common/tests/src/com/android/common/widget/CompositeCursorAdapterTest.java index 53d58402..17ba182a 100644 --- a/common/tests/src/com/android/common/widget/CompositeCursorAdapterTest.java +++ b/common/tests/src/com/android/common/widget/CompositeCursorAdapterTest.java @@ -19,10 +19,11 @@ import android.content.Context; import android.database.Cursor; import android.database.MatrixCursor; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; import android.view.View; import android.view.ViewGroup; +import androidx.test.filters.SmallTest; + /** * Tests for {@link CompositeCursorAdapter}. * diff --git a/framesequence/jni/Android.bp b/framesequence/jni/Android.bp index 13ae47af..db71b51f 100644 --- a/framesequence/jni/Android.bp +++ b/framesequence/jni/Android.bp @@ -20,41 +20,10 @@ package { cc_library_shared { name: "libframesequence", - - // Main library - // NOTE: the following code when dropped during the Android.mk->Android.bp - // conversion because FRAMESEQUENCE_INCLUDE_WEBP is never set: - // ifeq ($(FRAMESEQUENCE_INCLUDE_WEBP),true) - // LOCAL_C_INCLUDES += external/webp/include - // LOCAL_SRC_FILES += FrameSequence_webp.cpp - // LOCAL_STATIC_LIBRARIES += libwebp-decode - // endif - - static_libs: ["libgif"], header_libs: ["jni_headers"], - include_dirs: ["external/giflib"], srcs: [ "BitmapDecoderJNI.cpp", - "FrameSequence.cpp", - "FrameSequenceJNI.cpp", - "FrameSequence_gif.cpp", - "JNIHelpers.cpp", - "Registry.cpp", - "Stream.cpp", - ], - cflags: [ - "-Wall", - "-Werror", - "-Wno-unused-parameter", - "-Wno-unused-variable", - "-Wno-overloaded-virtual", - "-fvisibility=hidden", ], sdk_version: "8", - shared_libs: [ - "libjnigraphics", - "liblog", - ], - product_specific: true, } diff --git a/framesequence/jni/BitmapDecoderJNI.cpp b/framesequence/jni/BitmapDecoderJNI.cpp index 5fe04b4f..6f7e246a 100644 --- a/framesequence/jni/BitmapDecoderJNI.cpp +++ b/framesequence/jni/BitmapDecoderJNI.cpp @@ -14,34 +14,8 @@ * limitations under the License. */ -#define LOG_TAG "FancyDecoding" - -#include <android/bitmap.h> -#include <stdlib.h> -#include <stdio.h> -#include "FrameSequenceJNI.h" -#include "JNIHelpers.h" -#include "Stream.h" -#include "utils/log.h" - -void throwException(JNIEnv* env, const char* error) { - jclass clazz = env->FindClass("java/lang/RuntimeException"); - env->ThrowNew(clazz, error); -} - -jint JNI_OnLoad(JavaVM* vm, void* reserved) { - JNIEnv* env; - if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { - return -1; - } - if (FrameSequence_OnLoad(env)) { - ALOGE("Failed to load FrameSequence"); - return -1; - } - if (JavaStream_OnLoad(env)) { - ALOGE("Failed to load JavaStream"); - return -1; - } +#include <jni.h> +jint JNI_OnLoad(JavaVM*, void*) { return JNI_VERSION_1_6; } diff --git a/framesequence/jni/Color.h b/framesequence/jni/Color.h deleted file mode 100644 index e49c64a3..00000000 --- a/framesequence/jni/Color.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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. - */ - -#ifndef RASTERMILL_COLOR_H -#define RASTERMILL_COLOR_H - -#include <sys/types.h> - -typedef uint32_t Color8888; - -static const Color8888 COLOR_8888_ALPHA_MASK = 0xff000000; // TODO: handle endianness -static const Color8888 TRANSPARENT = 0x0; - -// TODO: handle endianness -#define ARGB_TO_COLOR8888(a, r, g, b) \ - ((a) << 24 | (b) << 16 | (g) << 8 | (r)) - -#endif // RASTERMILL_COLOR_H diff --git a/framesequence/jni/FrameSequence.cpp b/framesequence/jni/FrameSequence.cpp deleted file mode 100644 index efcfefa0..00000000 --- a/framesequence/jni/FrameSequence.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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. - */ - -#include "FrameSequence.h" - -#include "Registry.h" - -FrameSequence* FrameSequence::create(Stream* stream) { - const RegistryEntry* entry = Registry::Find(stream); - - if (!entry) return NULL; - - FrameSequence* frameSequence = entry->createFrameSequence(stream); - if (!frameSequence->getFrameCount() || - !frameSequence->getWidth() || !frameSequence->getHeight()) { - // invalid contents, abort - delete frameSequence; - return NULL; - } - - return frameSequence; -} diff --git a/framesequence/jni/FrameSequence.h b/framesequence/jni/FrameSequence.h deleted file mode 100644 index 134c81a5..00000000 --- a/framesequence/jni/FrameSequence.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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. - */ - -#ifndef RASTERMILL_FRAME_SEQUENCE_H -#define RASTERMILL_FRAME_SEQUENCE_H - -#include "Stream.h" -#include "Color.h" - -class FrameSequenceState { -public: - /** - * Produces a frame of animation in the output buffer, drawing (at minimum) the delta since - * previousFrameNr (the current contents of the buffer), or from scratch if previousFrameNr is - * negative - * - * Returns frame's delay time in milliseconds. - */ - virtual long drawFrame(int frameNr, - Color8888* outputPtr, int outputPixelStride, int previousFrameNr) = 0; - virtual ~FrameSequenceState() {} -}; - -class FrameSequence { -public: - /** - * Creates a FrameSequence using data from the data stream - * - * Type determined by header information in the stream - */ - static FrameSequence* create(Stream* stream); - - virtual ~FrameSequence() {} - virtual int getWidth() const = 0; - virtual int getHeight() const = 0; - virtual bool isOpaque() const = 0; - virtual int getFrameCount() const = 0; - virtual int getDefaultLoopCount() const = 0; - virtual jobject getRawByteBuffer() const = 0; - - virtual FrameSequenceState* createState() const = 0; -}; - -#endif //RASTERMILL_FRAME_SEQUENCE_H diff --git a/framesequence/jni/FrameSequenceJNI.cpp b/framesequence/jni/FrameSequenceJNI.cpp deleted file mode 100644 index c701f033..00000000 --- a/framesequence/jni/FrameSequenceJNI.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - * 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. - */ - -#include <android/bitmap.h> -#include "JNIHelpers.h" -#include "utils/log.h" -#include "FrameSequence.h" - -#include "FrameSequenceJNI.h" - -#define JNI_PACKAGE "android/support/rastermill" - -static struct { - jclass clazz; - jmethodID ctor; -} gFrameSequenceClassInfo; - -//////////////////////////////////////////////////////////////////////////////// -// Frame sequence -//////////////////////////////////////////////////////////////////////////////// - -static jobject createJavaFrameSequence(JNIEnv* env, FrameSequence* frameSequence) { - if (!frameSequence) { - return NULL; - } - return env->NewObject(gFrameSequenceClassInfo.clazz, gFrameSequenceClassInfo.ctor, - reinterpret_cast<jlong>(frameSequence), - frameSequence->getWidth(), - frameSequence->getHeight(), - frameSequence->isOpaque(), - frameSequence->getFrameCount(), - frameSequence->getDefaultLoopCount()); -} - -static jobject nativeDecodeByteArray(JNIEnv* env, jobject clazz, - jbyteArray byteArray, jint offset, jint length) { - jbyte* bytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(byteArray, NULL)); - if (bytes == NULL) { - jniThrowException(env, ILLEGAL_STATE_EXEPTION, - "couldn't read array bytes"); - return NULL; - } - MemoryStream stream(bytes + offset, length, NULL); - FrameSequence* frameSequence = FrameSequence::create(&stream); - env->ReleasePrimitiveArrayCritical(byteArray, bytes, 0); - return createJavaFrameSequence(env, frameSequence); -} - -static jobject nativeDecodeByteBuffer(JNIEnv* env, jobject clazz, - jobject buf, jint offset, jint limit) { - jobject globalBuf = env->NewGlobalRef(buf); - JavaVM* vm; - env->GetJavaVM(&vm); - MemoryStream stream( - (reinterpret_cast<uint8_t*>( - env->GetDirectBufferAddress(globalBuf))) + offset, - limit, - globalBuf); - FrameSequence* frameSequence = FrameSequence::create(&stream); - jobject finalSequence = createJavaFrameSequence(env, frameSequence); - return finalSequence; -} - -static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, - jobject istream, jbyteArray byteArray) { - JavaInputStream stream(env, istream, byteArray); - FrameSequence* frameSequence = FrameSequence::create(&stream); - return createJavaFrameSequence(env, frameSequence); -} - -static void nativeDestroyFrameSequence(JNIEnv* env, jobject clazz, - jlong frameSequenceLong) { - FrameSequence* frameSequence = reinterpret_cast<FrameSequence*>(frameSequenceLong); - jobject buf = frameSequence->getRawByteBuffer(); - if (buf != NULL) { - env->DeleteGlobalRef(buf); - } - delete frameSequence; -} - -static jlong nativeCreateState(JNIEnv* env, jobject clazz, jlong frameSequenceLong) { - FrameSequence* frameSequence = reinterpret_cast<FrameSequence*>(frameSequenceLong); - FrameSequenceState* state = frameSequence->createState(); - return reinterpret_cast<jlong>(state); -} - -//////////////////////////////////////////////////////////////////////////////// -// Frame sequence state -//////////////////////////////////////////////////////////////////////////////// - -static void nativeDestroyState( - JNIEnv* env, jobject clazz, jlong frameSequenceStateLong) { - FrameSequenceState* frameSequenceState = - reinterpret_cast<FrameSequenceState*>(frameSequenceStateLong); - delete frameSequenceState; -} - -void throwIae(JNIEnv* env, const char* message, int errorCode) { - char buf[256]; - snprintf(buf, sizeof(buf), "%s, error %d", message, errorCode); - jniThrowException(env, ILLEGAL_STATE_EXEPTION, buf); -} - -static jlong JNICALL nativeGetFrame( - JNIEnv* env, jobject clazz, jlong frameSequenceStateLong, jint frameNr, - jobject bitmap, jint previousFrameNr) { - FrameSequenceState* frameSequenceState = - reinterpret_cast<FrameSequenceState*>(frameSequenceStateLong); - int ret; - AndroidBitmapInfo info; - void* pixels; - - if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) { - throwIae(env, "Couldn't get info from Bitmap", ret); - return 0; - } - - if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) { - throwIae(env, "Bitmap pixels couldn't be locked", ret); - return 0; - } - - int pixelStride = info.stride >> 2; - jlong delayMs = frameSequenceState->drawFrame(frameNr, - (Color8888*) pixels, pixelStride, previousFrameNr); - - AndroidBitmap_unlockPixels(env, bitmap); - return delayMs; -} - -static JNINativeMethod gMethods[] = { - { "nativeDecodeByteArray", - "([BII)L" JNI_PACKAGE "/FrameSequence;", - (void*) nativeDecodeByteArray - }, - { "nativeDecodeByteBuffer", - "(Ljava/nio/ByteBuffer;II)L" JNI_PACKAGE "/FrameSequence;", - (void*) nativeDecodeByteBuffer - }, - { "nativeDecodeStream", - "(Ljava/io/InputStream;[B)L" JNI_PACKAGE "/FrameSequence;", - (void*) nativeDecodeStream - }, - { "nativeDestroyFrameSequence", - "(J)V", - (void*) nativeDestroyFrameSequence - }, - { "nativeCreateState", - "(J)J", - (void*) nativeCreateState - }, - { "nativeGetFrame", - "(JILandroid/graphics/Bitmap;I)J", - (void*) nativeGetFrame - }, - { "nativeDestroyState", - "(J)V", - (void*) nativeDestroyState - }, -}; - -jint FrameSequence_OnLoad(JNIEnv* env) { - // Get jclass with env->FindClass. - // Register methods with env->RegisterNatives. - gFrameSequenceClassInfo.clazz = env->FindClass(JNI_PACKAGE "/FrameSequence"); - if (!gFrameSequenceClassInfo.clazz) { - ALOGW("Failed to find " JNI_PACKAGE "/FrameSequence"); - return -1; - } - gFrameSequenceClassInfo.clazz = (jclass)env->NewGlobalRef(gFrameSequenceClassInfo.clazz); - - gFrameSequenceClassInfo.ctor = env->GetMethodID(gFrameSequenceClassInfo.clazz, "<init>", "(JIIZII)V"); - if (!gFrameSequenceClassInfo.ctor) { - ALOGW("Failed to find constructor for FrameSequence - was it stripped?"); - return -1; - } - - return env->RegisterNatives(gFrameSequenceClassInfo.clazz, gMethods, METHOD_COUNT(gMethods)); -} diff --git a/framesequence/jni/FrameSequenceJNI.h b/framesequence/jni/FrameSequenceJNI.h deleted file mode 100644 index a52df8a7..00000000 --- a/framesequence/jni/FrameSequenceJNI.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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. - */ - -#ifndef RASTERMILL_FRAMESEQUENCE_JNI -#define RASTERMILL_FRAMESEQUENCE_JNI - -#include <jni.h> - -jint FrameSequence_OnLoad(JNIEnv* env); - -#endif // RASTERMILL_FRAMESEQUENCE_JNI diff --git a/framesequence/jni/FrameSequence_gif.cpp b/framesequence/jni/FrameSequence_gif.cpp deleted file mode 100644 index ed478342..00000000 --- a/framesequence/jni/FrameSequence_gif.cpp +++ /dev/null @@ -1,362 +0,0 @@ -/* - * 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. - */ - -#include <string.h> -#include "JNIHelpers.h" -#include "utils/log.h" -#include "utils/math.h" - -#include "FrameSequence_gif.h" - -#define GIF_DEBUG 0 - -static int streamReader(GifFileType* fileType, GifByteType* out, int size) { - Stream* stream = (Stream*) fileType->UserData; - return (int) stream->read(out, size); -} - -static Color8888 gifColorToColor8888(const GifColorType& color) { - return ARGB_TO_COLOR8888(0xff, color.Red, color.Green, color.Blue); -} - -static long getDelayMs(GraphicsControlBlock& gcb) { - return gcb.DelayTime * 10; -} - -static bool willBeCleared(const GraphicsControlBlock& gcb) { - return gcb.DisposalMode == DISPOSE_BACKGROUND || gcb.DisposalMode == DISPOSE_PREVIOUS; -} - -//////////////////////////////////////////////////////////////////////////////// -// Frame sequence -//////////////////////////////////////////////////////////////////////////////// - -FrameSequence_gif::FrameSequence_gif(Stream* stream) : - mLoopCount(1), mBgColor(TRANSPARENT), mPreservedFrames(NULL), mRestoringFrames(NULL) { - mGif = DGifOpen(stream, streamReader, NULL); - if (!mGif) { - ALOGW("Gif load failed"); - return; - } - - if (DGifSlurp(mGif) != GIF_OK) { - ALOGW("Gif slurp failed"); - DGifCloseFile(mGif, NULL); - mGif = NULL; - return; - } - - long durationMs = 0; - int lastUnclearedFrame = -1; - mPreservedFrames = new bool[mGif->ImageCount]; - mRestoringFrames = new int[mGif->ImageCount]; - - GraphicsControlBlock gcb; - for (int i = 0; i < mGif->ImageCount; i++) { - const SavedImage& image = mGif->SavedImages[i]; - - // find the loop extension pair - for (int j = 0; (j + 1) < image.ExtensionBlockCount; j++) { - ExtensionBlock* eb1 = image.ExtensionBlocks + j; - ExtensionBlock* eb2 = image.ExtensionBlocks + j + 1; - if (eb1->Function == APPLICATION_EXT_FUNC_CODE - // look for "NETSCAPE2.0" app extension - && eb1->ByteCount == 11 - && !memcmp((const char*)(eb1->Bytes), "NETSCAPE2.0", 11) - // verify extension contents and get loop count - && eb2->Function == CONTINUE_EXT_FUNC_CODE - && eb2->ByteCount == 3 - && eb2->Bytes[0] == 1) { - mLoopCount = (int)(eb2->Bytes[2] << 8) + (int)(eb2->Bytes[1]); - } - } - - DGifSavedExtensionToGCB(mGif, i, &gcb); - - // timing - durationMs += getDelayMs(gcb); - - // preserve logic - mPreservedFrames[i] = false; - mRestoringFrames[i] = -1; - if (gcb.DisposalMode == DISPOSE_PREVIOUS && lastUnclearedFrame >= 0) { - mPreservedFrames[lastUnclearedFrame] = true; - mRestoringFrames[i] = lastUnclearedFrame; - } - if (!willBeCleared(gcb)) { - lastUnclearedFrame = i; - } - } - -#if GIF_DEBUG - ALOGD("FrameSequence_gif created with size %d %d, frames %d dur %ld", - mGif->SWidth, mGif->SHeight, mGif->ImageCount, durationMs); - for (int i = 0; i < mGif->ImageCount; i++) { - DGifSavedExtensionToGCB(mGif, i, &gcb); - ALOGD(" Frame %d - must preserve %d, restore point %d, trans color %d", - i, mPreservedFrames[i], mRestoringFrames[i], gcb.TransparentColor); - } -#endif - - const ColorMapObject* cmap = mGif->SColorMap; - if (cmap) { - // calculate bg color - GraphicsControlBlock gcb; - DGifSavedExtensionToGCB(mGif, 0, &gcb); - if (gcb.TransparentColor == NO_TRANSPARENT_COLOR - && mGif->SBackGroundColor < cmap->ColorCount) { - mBgColor = gifColorToColor8888(cmap->Colors[mGif->SBackGroundColor]); - } - } -} - -FrameSequence_gif::~FrameSequence_gif() { - if (mGif) { - DGifCloseFile(mGif, NULL); - } - delete[] mPreservedFrames; - delete[] mRestoringFrames; -} - -FrameSequenceState* FrameSequence_gif::createState() const { - return new FrameSequenceState_gif(*this); -} - -//////////////////////////////////////////////////////////////////////////////// -// draw helpers -//////////////////////////////////////////////////////////////////////////////// - -// return true if area of 'target' is completely covers area of 'covered' -static bool checkIfCover(const GifImageDesc& target, const GifImageDesc& covered) { - return target.Left <= covered.Left - && covered.Left + covered.Width <= target.Left + target.Width - && target.Top <= covered.Top - && covered.Top + covered.Height <= target.Top + target.Height; -} - -static void copyLine(Color8888* dst, const unsigned char* src, const ColorMapObject* cmap, - int transparent, int width) { - for (; width > 0; width--, src++, dst++) { - if (*src != transparent && *src < cmap->ColorCount) { - *dst = gifColorToColor8888(cmap->Colors[*src]); - } - } -} - -static void setLineColor(Color8888* dst, Color8888 color, int width) { - for (; width > 0; width--, dst++) { - *dst = color; - } -} - -static void getCopySize(const GifImageDesc& imageDesc, int maxWidth, int maxHeight, - GifWord& copyWidth, GifWord& copyHeight) { - copyWidth = imageDesc.Width; - if (imageDesc.Left + copyWidth > maxWidth) { - copyWidth = maxWidth - imageDesc.Left; - } - copyHeight = imageDesc.Height; - if (imageDesc.Top + copyHeight > maxHeight) { - copyHeight = maxHeight - imageDesc.Top; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Frame sequence state -//////////////////////////////////////////////////////////////////////////////// - -FrameSequenceState_gif::FrameSequenceState_gif(const FrameSequence_gif& frameSequence) : - mFrameSequence(frameSequence), mPreserveBuffer(NULL), mPreserveBufferFrame(-1) { -} - -FrameSequenceState_gif::~FrameSequenceState_gif() { - delete[] mPreserveBuffer; -} - -void FrameSequenceState_gif::savePreserveBuffer(Color8888* outputPtr, int outputPixelStride, int frameNr) { - if (frameNr == mPreserveBufferFrame) return; - - mPreserveBufferFrame = frameNr; - const int width = mFrameSequence.getWidth(); - const int height = mFrameSequence.getHeight(); - if (!mPreserveBuffer) { - mPreserveBuffer = new Color8888[width * height]; - } - for (int y = 0; y < height; y++) { - memcpy(mPreserveBuffer + width * y, - outputPtr + outputPixelStride * y, - width * 4); - } -} - -void FrameSequenceState_gif::restorePreserveBuffer(Color8888* outputPtr, int outputPixelStride) { - const int width = mFrameSequence.getWidth(); - const int height = mFrameSequence.getHeight(); - if (!mPreserveBuffer) { - ALOGD("preserve buffer not allocated! ah!"); - return; - } - for (int y = 0; y < height; y++) { - memcpy(outputPtr + outputPixelStride * y, - mPreserveBuffer + width * y, - width * 4); - } -} - -long FrameSequenceState_gif::drawFrame(int frameNr, - Color8888* outputPtr, int outputPixelStride, int previousFrameNr) { - - GifFileType* gif = mFrameSequence.getGif(); - if (!gif) { - ALOGD("Cannot drawFrame, mGif is NULL"); - return -1; - } - -#if GIF_DEBUG - ALOGD(" drawFrame on %p nr %d on addr %p, previous frame nr %d", - this, frameNr, outputPtr, previousFrameNr); -#endif - - const int height = mFrameSequence.getHeight(); - const int width = mFrameSequence.getWidth(); - - GraphicsControlBlock gcb; - - int start = max(previousFrameNr + 1, 0); - - for (int i = max(start - 1, 0); i < frameNr; i++) { - int neededPreservedFrame = mFrameSequence.getRestoringFrame(i); - if (neededPreservedFrame >= 0 && (mPreserveBufferFrame != neededPreservedFrame)) { -#if GIF_DEBUG - ALOGD("frame %d needs frame %d preserved, but %d is currently, so drawing from scratch", - i, neededPreservedFrame, mPreserveBufferFrame); -#endif - start = 0; - } - } - - for (int i = start; i <= frameNr; i++) { - DGifSavedExtensionToGCB(gif, i, &gcb); - const SavedImage& frame = gif->SavedImages[i]; - -#if GIF_DEBUG - bool frameOpaque = gcb.TransparentColor == NO_TRANSPARENT_COLOR; - ALOGD("producing frame %d, drawing frame %d (opaque %d, disp %d, del %d)", - frameNr, i, frameOpaque, gcb.DisposalMode, gcb.DelayTime); -#endif - if (i == 0) { - //clear bitmap - Color8888 bgColor = mFrameSequence.getBackgroundColor(); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - outputPtr[y * outputPixelStride + x] = bgColor; - } - } - } else { - GraphicsControlBlock prevGcb; - DGifSavedExtensionToGCB(gif, i - 1, &prevGcb); - const SavedImage& prevFrame = gif->SavedImages[i - 1]; - bool prevFrameDisposed = willBeCleared(prevGcb); - - bool newFrameOpaque = gcb.TransparentColor == NO_TRANSPARENT_COLOR; - bool prevFrameCompletelyCovered = newFrameOpaque - && checkIfCover(frame.ImageDesc, prevFrame.ImageDesc); - - if (prevFrameDisposed && !prevFrameCompletelyCovered) { - switch (prevGcb.DisposalMode) { - case DISPOSE_BACKGROUND: { - Color8888* dst = outputPtr + prevFrame.ImageDesc.Left + - prevFrame.ImageDesc.Top * outputPixelStride; - - GifWord copyWidth, copyHeight; - getCopySize(prevFrame.ImageDesc, width, height, copyWidth, copyHeight); - for (; copyHeight > 0; copyHeight--) { - setLineColor(dst, TRANSPARENT, copyWidth); - dst += outputPixelStride; - } - } break; - case DISPOSE_PREVIOUS: { - restorePreserveBuffer(outputPtr, outputPixelStride); - } break; - } - } - - if (mFrameSequence.getPreservedFrame(i - 1)) { - // currently drawn frame will be restored by a following DISPOSE_PREVIOUS draw, so - // we preserve it - savePreserveBuffer(outputPtr, outputPixelStride, i - 1); - } - } - - bool willBeCleared = gcb.DisposalMode == DISPOSE_BACKGROUND - || gcb.DisposalMode == DISPOSE_PREVIOUS; - if (i == frameNr || !willBeCleared) { - const ColorMapObject* cmap = gif->SColorMap; - if (frame.ImageDesc.ColorMap) { - cmap = frame.ImageDesc.ColorMap; - } - - // If a cmap is missing, the frame can't be decoded, so we skip it. - if (cmap) { - const unsigned char* src = (unsigned char*)frame.RasterBits; - Color8888* dst = outputPtr + frame.ImageDesc.Left + - frame.ImageDesc.Top * outputPixelStride; - GifWord copyWidth, copyHeight; - getCopySize(frame.ImageDesc, width, height, copyWidth, copyHeight); - for (; copyHeight > 0; copyHeight--) { - copyLine(dst, src, cmap, gcb.TransparentColor, copyWidth); - src += frame.ImageDesc.Width; - dst += outputPixelStride; - } - } - } - } - - // return last frame's delay - const int maxFrame = gif->ImageCount; - const int lastFrame = (frameNr + maxFrame - 1) % maxFrame; - DGifSavedExtensionToGCB(gif, lastFrame, &gcb); - return getDelayMs(gcb); -} - -//////////////////////////////////////////////////////////////////////////////// -// Registry -//////////////////////////////////////////////////////////////////////////////// - -#include "Registry.h" - -static bool isGif(void* header, int header_size) { - return !memcmp(GIF_STAMP, header, GIF_STAMP_LEN) - || !memcmp(GIF87_STAMP, header, GIF_STAMP_LEN) - || !memcmp(GIF89_STAMP, header, GIF_STAMP_LEN); -} - -static bool acceptsBuffers() { - return false; -} - -static FrameSequence* createFramesequence(Stream* stream) { - return new FrameSequence_gif(stream); -} - -static RegistryEntry gEntry = { - GIF_STAMP_LEN, - isGif, - createFramesequence, - NULL, - acceptsBuffers, -}; -static Registry gRegister(gEntry); diff --git a/framesequence/jni/FrameSequence_gif.h b/framesequence/jni/FrameSequence_gif.h deleted file mode 100644 index 563f5b8e..00000000 --- a/framesequence/jni/FrameSequence_gif.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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. - */ - -#ifndef RASTERMILL_FRAMESQUENCE_GIF_H -#define RASTERMILL_FRAMESQUENCE_GIF_H - -#include "config.h" -#include "gif_lib.h" - -#include "Stream.h" -#include "Color.h" -#include "FrameSequence.h" - -class FrameSequence_gif : public FrameSequence { -public: - FrameSequence_gif(Stream* stream); - virtual ~FrameSequence_gif(); - - virtual int getWidth() const { - return mGif ? mGif->SWidth : 0; - } - - virtual int getHeight() const { - return mGif ? mGif->SHeight : 0; - } - - virtual bool isOpaque() const { - return (mBgColor & COLOR_8888_ALPHA_MASK) == COLOR_8888_ALPHA_MASK; - } - - virtual int getFrameCount() const { - return mGif ? mGif->ImageCount : 0; - } - - virtual int getDefaultLoopCount() const { - return mLoopCount; - } - - virtual jobject getRawByteBuffer() const { - return NULL; - } - - virtual FrameSequenceState* createState() const; - - GifFileType* getGif() const { return mGif; } - Color8888 getBackgroundColor() const { return mBgColor; } - bool getPreservedFrame(int frameIndex) const { return mPreservedFrames[frameIndex]; } - int getRestoringFrame(int frameIndex) const { return mRestoringFrames[frameIndex]; } - -private: - GifFileType* mGif; - int mLoopCount; - Color8888 mBgColor; - - // array of bool per frame - if true, frame data is used by a later DISPOSE_PREVIOUS frame - bool* mPreservedFrames; - - // array of ints per frame - if >= 0, points to the index of the preserve that frame needs - int* mRestoringFrames; -}; - -class FrameSequenceState_gif : public FrameSequenceState { -public: - FrameSequenceState_gif(const FrameSequence_gif& frameSequence); - virtual ~FrameSequenceState_gif(); - - // returns frame's delay time in ms - virtual long drawFrame(int frameNr, - Color8888* outputPtr, int outputPixelStride, int previousFrameNr); - -private: - void savePreserveBuffer(Color8888* outputPtr, int outputPixelStride, int frameNr); - void restorePreserveBuffer(Color8888* outputPtr, int outputPixelStride); - - const FrameSequence_gif& mFrameSequence; - Color8888* mPreserveBuffer; - int mPreserveBufferFrame; -}; - -#endif //RASTERMILL_FRAMESQUENCE_GIF_H diff --git a/framesequence/jni/FrameSequence_webp.cpp b/framesequence/jni/FrameSequence_webp.cpp deleted file mode 100644 index f1bae82f..00000000 --- a/framesequence/jni/FrameSequence_webp.cpp +++ /dev/null @@ -1,405 +0,0 @@ -/* - * 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. - */ - -#include <string.h> -#include "JNIHelpers.h" -#include "utils/log.h" -#include "utils/math.h" -#include "webp/format_constants.h" - -#include "FrameSequence_webp.h" - -#define WEBP_DEBUG 0 - -//////////////////////////////////////////////////////////////////////////////// -// Frame sequence -//////////////////////////////////////////////////////////////////////////////// - -static uint32_t GetLE32(const uint8_t* const data) { - return MKFOURCC(data[0], data[1], data[2], data[3]); -} - -// Returns true if the frame covers full canvas. -static bool isFullFrame(const WebPIterator& frame, int canvasWidth, int canvasHeight) { - return (frame.width == canvasWidth && frame.height == canvasHeight); -} - -// Returns true if the rectangle defined by 'frame' contains pixel (x, y). -static bool FrameContainsPixel(const WebPIterator& frame, int x, int y) { - const int left = frame.x_offset; - const int right = left + frame.width; - const int top = frame.y_offset; - const int bottom = top + frame.height; - return x >= left && x < right && y >= top && y < bottom; -} - -// Construct mIsKeyFrame array. -void FrameSequence_webp::constructDependencyChain() { - const size_t frameCount = getFrameCount(); - mIsKeyFrame = new bool[frameCount]; - const int canvasWidth = getWidth(); - const int canvasHeight = getHeight(); - - WebPIterator prev; - WebPIterator curr; - - // Note: WebPDemuxGetFrame() uses base-1 counting. - int ok = WebPDemuxGetFrame(mDemux, 1, &curr); - ALOG_ASSERT(ok, "Could not retrieve frame# 0"); - mIsKeyFrame[0] = true; // 0th frame is always a key frame. - for (size_t i = 1; i < frameCount; i++) { - prev = curr; - ok = WebPDemuxGetFrame(mDemux, i + 1, &curr); // Get ith frame. - ALOG_ASSERT(ok, "Could not retrieve frame# %d", i); - - if ((!curr.has_alpha || curr.blend_method == WEBP_MUX_NO_BLEND) && - isFullFrame(curr, canvasWidth, canvasHeight)) { - mIsKeyFrame[i] = true; - } else { - mIsKeyFrame[i] = (prev.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) && - (isFullFrame(prev, canvasWidth, canvasHeight) || mIsKeyFrame[i - 1]); - } - } - WebPDemuxReleaseIterator(&prev); - WebPDemuxReleaseIterator(&curr); - -#if WEBP_DEBUG - ALOGD("Dependency chain:"); - for (size_t i = 0; i < frameCount; i++) { - ALOGD("Frame# %zu: %s", i, mIsKeyFrame[i] ? "Key frame" : "NOT a key frame"); - } -#endif -} - -FrameSequence_webp::FrameSequence_webp(Stream* stream) - : mDemux(NULL) - , mIsKeyFrame(NULL) - , mRawByteBuffer(NULL) { - if (stream->getRawBuffer() != NULL) { - mData.size = stream->getRawBufferSize(); - mData.bytes = stream->getRawBufferAddr(); - mRawByteBuffer = stream->getRawBuffer(); - } else { - // Read RIFF header to get file size. - uint8_t riff_header[RIFF_HEADER_SIZE]; - if (stream->read(riff_header, RIFF_HEADER_SIZE) != RIFF_HEADER_SIZE) { - ALOGE("WebP header load failed"); - return; - } - uint32_t readSize = GetLE32(riff_header + TAG_SIZE); - if (readSize > MAX_CHUNK_PAYLOAD) { - ALOGE("WebP got header size too large"); - return; - } - mData.size = CHUNK_HEADER_SIZE + readSize; - if(mData.size < RIFF_HEADER_SIZE) { - ALOGE("WebP file malformed"); - return; - } - mData.bytes = new uint8_t[mData.size]; - memcpy((void*)mData.bytes, riff_header, RIFF_HEADER_SIZE); - - // Read rest of the bytes. - void* remaining_bytes = (void*)(mData.bytes + RIFF_HEADER_SIZE); - size_t remaining_size = mData.size - RIFF_HEADER_SIZE; - if (stream->read(remaining_bytes, remaining_size) != remaining_size) { - ALOGE("WebP full load failed"); - return; - } - } - - // Construct demux. - mDemux = WebPDemux(&mData); - if (!mDemux) { - ALOGE("Parsing of WebP container file failed"); - return; - } - mLoopCount = WebPDemuxGetI(mDemux, WEBP_FF_LOOP_COUNT); - mFormatFlags = WebPDemuxGetI(mDemux, WEBP_FF_FORMAT_FLAGS); -#if WEBP_DEBUG - ALOGD("FrameSequence_webp created with size = %d x %d, number of frames = %d, flags = 0x%X", - getWidth(), getHeight(), getFrameCount(), mFormatFlags); -#endif - constructDependencyChain(); -} - -FrameSequence_webp::~FrameSequence_webp() { - WebPDemuxDelete(mDemux); - delete[] mIsKeyFrame; - if (mRawByteBuffer == NULL) { - delete[] mData.bytes; - } -} - -FrameSequenceState* FrameSequence_webp::createState() const { - return new FrameSequenceState_webp(*this); -} - -//////////////////////////////////////////////////////////////////////////////// -// draw helpers -//////////////////////////////////////////////////////////////////////////////// - -static bool willBeCleared(const WebPIterator& iter) { - return iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND; -} - -// return true if area of 'target' completely covers area of 'covered' -static bool checkIfCover(const WebPIterator& target, const WebPIterator& covered) { - const int covered_x_max = covered.x_offset + covered.width; - const int target_x_max = target.x_offset + target.width; - const int covered_y_max = covered.y_offset + covered.height; - const int target_y_max = target.y_offset + target.height; - return target.x_offset <= covered.x_offset - && covered_x_max <= target_x_max - && target.y_offset <= covered.y_offset - && covered_y_max <= target_y_max; -} - -// Clear all pixels in a line to transparent. -static void clearLine(Color8888* dst, int width) { - memset(dst, 0, width * sizeof(*dst)); // Note: Assumes TRANSPARENT == 0x0. -} - -// Copy all pixels from 'src' to 'dst'. -static void copyFrame(const Color8888* src, int srcStride, Color8888* dst, int dstStride, - int width, int height) { - for (int y = 0; y < height; y++) { - memcpy(dst, src, width * sizeof(*dst)); - src += srcStride; - dst += dstStride; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Frame sequence state -//////////////////////////////////////////////////////////////////////////////// - -FrameSequenceState_webp::FrameSequenceState_webp(const FrameSequence_webp& frameSequence) : - mFrameSequence(frameSequence) { - WebPInitDecoderConfig(&mDecoderConfig); - mDecoderConfig.output.is_external_memory = 1; - mDecoderConfig.output.colorspace = MODE_rgbA; // Pre-multiplied alpha mode. - const int canvasWidth = mFrameSequence.getWidth(); - const int canvasHeight = mFrameSequence.getHeight(); - mPreservedBuffer = new Color8888[canvasWidth * canvasHeight]; -} - -FrameSequenceState_webp::~FrameSequenceState_webp() { - delete[] mPreservedBuffer; -} - -void FrameSequenceState_webp::initializeFrame(const WebPIterator& currIter, Color8888* currBuffer, - int currStride, const WebPIterator& prevIter, const Color8888* prevBuffer, int prevStride) { - const int canvasWidth = mFrameSequence.getWidth(); - const int canvasHeight = mFrameSequence.getHeight(); - const bool currFrameIsKeyFrame = mFrameSequence.isKeyFrame(currIter.frame_num - 1); - - if (currFrameIsKeyFrame) { // Clear canvas. - for (int y = 0; y < canvasHeight; y++) { - Color8888* dst = currBuffer + y * currStride; - clearLine(dst, canvasWidth); - } - } else { - // Preserve previous frame as starting state of current frame. - copyFrame(prevBuffer, prevStride, currBuffer, currStride, canvasWidth, canvasHeight); - - // Dispose previous frame rectangle to Background if needed. - bool prevFrameCompletelyCovered = - (!currIter.has_alpha || currIter.blend_method == WEBP_MUX_NO_BLEND) && - checkIfCover(currIter, prevIter); - if ((prevIter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) && - !prevFrameCompletelyCovered) { - Color8888* dst = currBuffer + prevIter.x_offset + prevIter.y_offset * currStride; - for (int j = 0; j < prevIter.height; j++) { - clearLine(dst, prevIter.width); - dst += currStride; - } - } - } -} - -bool FrameSequenceState_webp::decodeFrame(const WebPIterator& currIter, Color8888* currBuffer, - int currStride, const WebPIterator& prevIter, const Color8888* prevBuffer, int prevStride) { - Color8888* dst = currBuffer + currIter.x_offset + currIter.y_offset * currStride; - mDecoderConfig.output.u.RGBA.rgba = (uint8_t*)dst; - mDecoderConfig.output.u.RGBA.stride = currStride * 4; - mDecoderConfig.output.u.RGBA.size = mDecoderConfig.output.u.RGBA.stride * currIter.height; - - const WebPData& currFrame = currIter.fragment; - if (WebPDecode(currFrame.bytes, currFrame.size, &mDecoderConfig) != VP8_STATUS_OK) { - return false; - } - - const int canvasWidth = mFrameSequence.getWidth(); - const int canvasHeight = mFrameSequence.getHeight(); - const bool currFrameIsKeyFrame = mFrameSequence.isKeyFrame(currIter.frame_num - 1); - // During the decoding of current frame, we may have set some pixels to be transparent - // (i.e. alpha < 255). However, the value of each of these pixels should have been determined - // by blending it against the value of that pixel in the previous frame if WEBP_MUX_BLEND was - // specified. So, we correct these pixels based on disposal method of the previous frame and - // the previous frame buffer. - if (currIter.blend_method == WEBP_MUX_BLEND && !currFrameIsKeyFrame) { - if (prevIter.dispose_method == WEBP_MUX_DISPOSE_NONE) { - for (int y = 0; y < currIter.height; y++) { - const int canvasY = currIter.y_offset + y; - for (int x = 0; x < currIter.width; x++) { - const int canvasX = currIter.x_offset + x; - Color8888& currPixel = currBuffer[canvasY * currStride + canvasX]; - // FIXME: Use alpha-blending when alpha is between 0 and 255. - if (!(currPixel & COLOR_8888_ALPHA_MASK)) { - const Color8888 prevPixel = prevBuffer[canvasY * prevStride + canvasX]; - currPixel = prevPixel; - } - } - } - } else { // prevIter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND - // Need to restore transparent pixels to as they were just after frame initialization. - // That is: - // * Transparent if it belongs to previous frame rectangle <-- This is a no-op. - // * Pixel in the previous canvas otherwise <-- Need to restore. - for (int y = 0; y < currIter.height; y++) { - const int canvasY = currIter.y_offset + y; - for (int x = 0; x < currIter.width; x++) { - const int canvasX = currIter.x_offset + x; - Color8888& currPixel = currBuffer[canvasY * currStride + canvasX]; - // FIXME: Use alpha-blending when alpha is between 0 and 255. - if (!(currPixel & COLOR_8888_ALPHA_MASK) - && !FrameContainsPixel(prevIter, canvasX, canvasY)) { - const Color8888 prevPixel = prevBuffer[canvasY * prevStride + canvasX]; - currPixel = prevPixel; - } - } - } - } - } - return true; -} - -long FrameSequenceState_webp::drawFrame(int frameNr, - Color8888* outputPtr, int outputPixelStride, int previousFrameNr) { - WebPDemuxer* demux = mFrameSequence.getDemuxer(); - ALOG_ASSERT(demux, "Cannot drawFrame, mDemux is NULL"); - -#if WEBP_DEBUG - ALOGD(" drawFrame called for frame# %d, previous frame# %d", frameNr, previousFrameNr); -#endif - - const int canvasWidth = mFrameSequence.getWidth(); - const int canvasHeight = mFrameSequence.getHeight(); - - // Find the first frame to be decoded. - int start = max(previousFrameNr + 1, 0); - int earliestRequired = frameNr; - while (earliestRequired > start) { - if (mFrameSequence.isKeyFrame(earliestRequired)) { - start = earliestRequired; - break; - } - earliestRequired--; - } - - WebPIterator currIter; - WebPIterator prevIter; - int ok = WebPDemuxGetFrame(demux, start, &currIter); // Get frame number 'start - 1'. - ALOG_ASSERT(ok, "Could not retrieve frame# %d", start - 1); - - // Use preserve buffer only if needed. - Color8888* prevBuffer = (frameNr == 0) ? outputPtr : mPreservedBuffer; - int prevStride = (frameNr == 0) ? outputPixelStride : canvasWidth; - Color8888* currBuffer = outputPtr; - int currStride = outputPixelStride; - - for (int i = start; i <= frameNr; i++) { - prevIter = currIter; - ok = WebPDemuxGetFrame(demux, i + 1, &currIter); // Get ith frame. - ALOG_ASSERT(ok, "Could not retrieve frame# %d", i); -#if WEBP_DEBUG - ALOGD(" producing frame %d (has_alpha = %d, dispose = %s, blend = %s, duration = %d)", - i, currIter.has_alpha, - (currIter.dispose_method == WEBP_MUX_DISPOSE_NONE) ? "none" : "background", - (currIter.blend_method == WEBP_MUX_BLEND) ? "yes" : "no", currIter.duration); -#endif - // We swap the prev/curr buffers as we go. - Color8888* tmpBuffer = prevBuffer; - prevBuffer = currBuffer; - currBuffer = tmpBuffer; - - int tmpStride = prevStride; - prevStride = currStride; - currStride = tmpStride; - -#if WEBP_DEBUG - ALOGD(" prev = %p, curr = %p, out = %p, tmp = %p", - prevBuffer, currBuffer, outputPtr, mPreservedBuffer); -#endif - // Process this frame. - initializeFrame(currIter, currBuffer, currStride, prevIter, prevBuffer, prevStride); - - if (i == frameNr || !willBeCleared(currIter)) { - if (!decodeFrame(currIter, currBuffer, currStride, prevIter, prevBuffer, prevStride)) { - ALOGE("Error decoding frame# %d", i); - return -1; - } - } - } - - if (outputPtr != currBuffer) { - copyFrame(currBuffer, currStride, outputPtr, outputPixelStride, canvasWidth, canvasHeight); - } - - // Return last frame's delay. - const int frameCount = mFrameSequence.getFrameCount(); - const int lastFrame = (frameNr + frameCount - 1) % frameCount; - ok = WebPDemuxGetFrame(demux, lastFrame + 1, &currIter); - ALOG_ASSERT(ok, "Could not retrieve frame# %d", lastFrame); - const int lastFrameDelay = currIter.duration; - - WebPDemuxReleaseIterator(&currIter); - WebPDemuxReleaseIterator(&prevIter); - - return lastFrameDelay; -} - -//////////////////////////////////////////////////////////////////////////////// -// Registry -//////////////////////////////////////////////////////////////////////////////// - -#include "Registry.h" - -static bool isWebP(void* header, int header_size) { - const uint8_t* const header_str = (const uint8_t*)header; - return (header_size >= RIFF_HEADER_SIZE) && - !memcmp("RIFF", header_str, 4) && - !memcmp("WEBP", header_str + 8, 4); -} - -static bool acceptsWebPBuffer() { - return true; -} - -static FrameSequence* createFramesequence(Stream* stream) { - return new FrameSequence_webp(stream); -} - -static RegistryEntry gEntry = { - RIFF_HEADER_SIZE, - isWebP, - createFramesequence, - NULL, - acceptsWebPBuffer, -}; -static Registry gRegister(gEntry); - diff --git a/framesequence/jni/FrameSequence_webp.h b/framesequence/jni/FrameSequence_webp.h deleted file mode 100644 index 148146f0..00000000 --- a/framesequence/jni/FrameSequence_webp.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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. - */ - -#ifndef RASTERMILL_FRAMESQUENCE_WEBP_H -#define RASTERMILL_FRAMESQUENCE_WEBP_H - -#include "config.h" -#include "webp/decode.h" -#include "webp/demux.h" - -#include "Stream.h" -#include "Color.h" -#include "FrameSequence.h" - -// Parser for a possibly-animated WebP bitstream. -class FrameSequence_webp : public FrameSequence { -public: - FrameSequence_webp(Stream* stream); - virtual ~FrameSequence_webp(); - - virtual int getWidth() const { - if (!mDemux) { - return 0; - } - return WebPDemuxGetI(mDemux, WEBP_FF_CANVAS_WIDTH); - } - - virtual int getHeight() const { - if (!mDemux) { - return 0; - } - return WebPDemuxGetI(mDemux, WEBP_FF_CANVAS_HEIGHT); - } - - virtual bool isOpaque() const { - return !(mFormatFlags & ALPHA_FLAG); - } - - virtual int getFrameCount() const { - if (!mDemux) { - return 0; - } - return WebPDemuxGetI(mDemux, WEBP_FF_FRAME_COUNT); - } - - virtual int getDefaultLoopCount() const { - return mLoopCount; - } - - virtual jobject getRawByteBuffer() const { - return mRawByteBuffer; - } - - virtual FrameSequenceState* createState() const; - - WebPDemuxer* getDemuxer() const { return mDemux; } - - bool isKeyFrame(size_t frameNr) const { return mIsKeyFrame[frameNr]; } - -private: - void constructDependencyChain(); - - WebPData mData; - WebPDemuxer* mDemux; - int mLoopCount; - uint32_t mFormatFlags; - // mIsKeyFrame[i] is true if ith canvas can be constructed without decoding any prior frames. - bool* mIsKeyFrame; - jobject mRawByteBuffer = nullptr; -}; - -// Produces frames of a possibly-animated WebP file for display. -class FrameSequenceState_webp : public FrameSequenceState { -public: - FrameSequenceState_webp(const FrameSequence_webp& frameSequence); - virtual ~FrameSequenceState_webp(); - - // Returns frame's delay time in milliseconds. - virtual long drawFrame(int frameNr, - Color8888* outputPtr, int outputPixelStride, int previousFrameNr); - -private: - void initializeFrame(const WebPIterator& currIter, Color8888* currBuffer, int currStride, - const WebPIterator& prevIter, const Color8888* prevBuffer, int prevStride); - bool decodeFrame(const WebPIterator& iter, Color8888* currBuffer, int currStride, - const WebPIterator& prevIter, const Color8888* prevBuffer, int prevStride); - - const FrameSequence_webp& mFrameSequence; - WebPDecoderConfig mDecoderConfig; - Color8888* mPreservedBuffer; -}; - -#endif //RASTERMILL_FRAMESQUENCE_WEBP_H diff --git a/framesequence/jni/JNIHelpers.cpp b/framesequence/jni/JNIHelpers.cpp deleted file mode 100644 index dd0c8185..00000000 --- a/framesequence/jni/JNIHelpers.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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. - */ - -#include "JNIHelpers.h" -#include "utils/log.h" - -void jniThrowException(JNIEnv* env, const char* className, const char* msg) { - jclass clazz = env->FindClass(className); - if (!clazz) { - ALOGE("Unable to find exception class %s", className); - /* ClassNotFoundException now pending */ - return; - } - - if (env->ThrowNew(clazz, msg) != JNI_OK) { - ALOGE("Failed throwing '%s' '%s'", className, msg); - /* an exception, most likely OOM, will now be pending */ - } - env->DeleteLocalRef(clazz); -} diff --git a/framesequence/jni/JNIHelpers.h b/framesequence/jni/JNIHelpers.h deleted file mode 100644 index 3718b388..00000000 --- a/framesequence/jni/JNIHelpers.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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. - */ - -#ifndef RASTERMILL_JNIHELPERS_H -#define RASTERMILL_JNIHELPERS_H - -#include <jni.h> - -#define METHOD_COUNT(methodArray) (sizeof(methodArray) / sizeof((methodArray)[0])) - -#define ILLEGAL_STATE_EXEPTION "java/lang/IllegalStateException" - -void jniThrowException(JNIEnv* env, const char* className, const char* msg); - - -#endif //RASTERMILL_JNIHELPERS_H diff --git a/framesequence/jni/Registry.cpp b/framesequence/jni/Registry.cpp deleted file mode 100644 index e632bb27..00000000 --- a/framesequence/jni/Registry.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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. - */ - -#include "Registry.h" - -#include "Stream.h" - -static Registry* gHead = 0; -static int gHeaderBytesRequired = 0; - -Registry::Registry(const RegistryEntry& entry) { - mImpl = entry; - - mNext = gHead; - gHead = this; - - if (gHeaderBytesRequired < entry.requiredHeaderBytes) { - gHeaderBytesRequired = entry.requiredHeaderBytes; - } -} - -const RegistryEntry* Registry::Find(Stream* stream) { - Registry* registry = gHead; - - if (stream->getRawBuffer() != NULL) { - while (registry) { - if (registry->mImpl.acceptsBuffer()) { - return &(registry->mImpl); - } - registry = registry->mNext; - } - } else { - int headerSize = gHeaderBytesRequired; - char header[headerSize]; - headerSize = stream->peek(header, headerSize); - while (registry) { - if (headerSize >= registry->mImpl.requiredHeaderBytes - && registry->mImpl.checkHeader(header, headerSize)) { - return &(registry->mImpl); - } - registry = registry->mNext; - } - } - return 0; -} - diff --git a/framesequence/jni/Registry.h b/framesequence/jni/Registry.h deleted file mode 100644 index 8db43cf8..00000000 --- a/framesequence/jni/Registry.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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. - */ - -#ifndef RASTERMILL_REGISTRY_H -#define RASTERMILL_REGISTRY_H - -#include "jni.h" -#include <stdint.h> - -class FrameSequence; -class Decoder; -class Stream; - -struct RegistryEntry { - int requiredHeaderBytes; - bool (*checkHeader)(void* header, int header_size); - FrameSequence* (*createFrameSequence)(Stream* stream); - Decoder* (*createDecoder)(Stream* stream); - bool (*acceptsBuffer)(); -}; - -/** - * Template class for registering subclasses that can produce instances of themselves given a - * DataStream pointer. - * - * The super class / root constructable type only needs to define a single static construction - * meathod that creates an instance by iterating through all factory methods. - */ -class Registry { -public: - Registry(const RegistryEntry& entry); - - static const RegistryEntry* Find(Stream* stream); - -private: - RegistryEntry mImpl; - Registry* mNext; -}; - -#endif // RASTERMILL_REGISTRY_H diff --git a/framesequence/jni/Stream.cpp b/framesequence/jni/Stream.cpp deleted file mode 100644 index 10dc805b..00000000 --- a/framesequence/jni/Stream.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "Stream" - -#include "Stream.h" - -#include <string.h> - -#include "JNIHelpers.h" -#include "utils/log.h" -#include "utils/math.h" - -static struct { - jmethodID read; - jmethodID reset; -} gInputStreamClassInfo; - -Stream::Stream() - : mPeekBuffer(0) - , mPeekSize(0) - , mPeekOffset(0) { -} - -Stream::~Stream() { - delete[] mPeekBuffer; -} - -size_t Stream::peek(void* buffer, size_t size) { - size_t peek_remaining = mPeekSize - mPeekOffset; - if (size > peek_remaining) { - char* old_peek = mPeekBuffer; - mPeekBuffer = new char[size]; - if (old_peek) { - memcpy(mPeekBuffer, old_peek + mPeekOffset, peek_remaining); - delete[] old_peek; - } - size_t read = doRead(mPeekBuffer + mPeekOffset, size - peek_remaining); - mPeekOffset = 0; - mPeekSize = peek_remaining + read; - } - size = min(size, mPeekSize - mPeekOffset); - memcpy(buffer, mPeekBuffer + mPeekOffset, size); - return size; -} - -size_t Stream::read(void* buffer, size_t size) { - size_t bytes_read = 0; - size_t peek_remaining = mPeekSize - mPeekOffset; - if (peek_remaining) { - bytes_read = min(size, peek_remaining); - memcpy(buffer, mPeekBuffer + mPeekOffset, bytes_read); - mPeekOffset += bytes_read; - if (mPeekOffset == mPeekSize) { - delete[] mPeekBuffer; - mPeekBuffer = 0; - mPeekOffset = 0; - mPeekSize = 0; - } - size -= bytes_read; - buffer = ((char*) buffer) + bytes_read; - } - if (size) { - bytes_read += doRead(buffer, size); - } - return bytes_read; -} - -uint8_t* Stream::getRawBufferAddr() { - return NULL; -} - -jobject Stream::getRawBuffer() { - return NULL; -} - -int Stream::getRawBufferSize() { - return 0; -} - -uint8_t* MemoryStream::getRawBufferAddr() { - return mBuffer; -} - -jobject MemoryStream::getRawBuffer() { - return mRawBuffer; -} - -int MemoryStream::getRawBufferSize() { - if (mRawBuffer != NULL) { - return mRemaining; - } else { - return 0; - } -} - -size_t MemoryStream::doRead(void* buffer, size_t size) { - size = min(size, mRemaining); - memcpy(buffer, mBuffer, size); - mBuffer += size; - mRemaining -= size; - return size; -} - -size_t FileStream::doRead(void* buffer, size_t size) { - return fread(buffer, 1, size, mFd); -} - -size_t JavaInputStream::doRead(void* dstBuffer, size_t size) { - size_t totalBytesRead = 0; - - do { - size_t requested = min(size, mByteArrayLength); - - jint bytesRead = mEnv->CallIntMethod(mInputStream, - gInputStreamClassInfo.read, mByteArray, 0, requested); - if (mEnv->ExceptionCheck() || bytesRead < 0) { - return 0; - } - - mEnv->GetByteArrayRegion(mByteArray, 0, bytesRead, (jbyte*)dstBuffer); - dstBuffer = (char*)dstBuffer + bytesRead; - totalBytesRead += bytesRead; - size -= bytesRead; - } while (size > 0); - - return totalBytesRead; -} - -jint JavaStream_OnLoad(JNIEnv* env) { - // Skip the verbose logging on error for these, as they won't be subject - // to obfuscators or similar and are thus unlikely to ever fail - jclass inputStreamClazz = env->FindClass("java/io/InputStream"); - if (!inputStreamClazz) { - return -1; - } - gInputStreamClassInfo.read = env->GetMethodID(inputStreamClazz, "read", "([BII)I"); - gInputStreamClassInfo.reset = env->GetMethodID(inputStreamClazz, "reset", "()V"); - if (!gInputStreamClassInfo.read || !gInputStreamClassInfo.reset) { - return -1; - } - return 0; -} diff --git a/framesequence/jni/Stream.h b/framesequence/jni/Stream.h deleted file mode 100644 index f0f38954..00000000 --- a/framesequence/jni/Stream.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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. - */ - -#ifndef RASTERMILL_STREAM_H -#define RASTERMILL_STREAM_H - -#include <jni.h> -#include <stdio.h> -#include <sys/types.h> - -class Stream { -public: - Stream(); - virtual ~Stream(); - - size_t peek(void* buffer, size_t size); - size_t read(void* buffer, size_t size); - virtual uint8_t* getRawBufferAddr(); - virtual jobject getRawBuffer(); - virtual int getRawBufferSize(); - -protected: - virtual size_t doRead(void* buffer, size_t size) = 0; - -private: - char* mPeekBuffer; - size_t mPeekSize; - size_t mPeekOffset; -}; - -class MemoryStream : public Stream { -public: - MemoryStream(void* buffer, size_t size, jobject buf) : - mBuffer((uint8_t*)buffer), - mRemaining(size), - mRawBuffer(buf) {} - virtual uint8_t* getRawBufferAddr(); - virtual jobject getRawBuffer(); - virtual int getRawBufferSize(); - -protected: - virtual size_t doRead(void* buffer, size_t size); - -private: - uint8_t* mBuffer; - size_t mRemaining; - jobject mRawBuffer; -}; - -class FileStream : public Stream { -public: - FileStream(FILE* fd) : mFd(fd) {} - -protected: - virtual size_t doRead(void* buffer, size_t size); - -private: - FILE* mFd; -}; - -class JavaInputStream : public Stream { -public: - JavaInputStream(JNIEnv* env, jobject inputStream, jbyteArray byteArray) : - mEnv(env), - mInputStream(inputStream), - mByteArray(byteArray), - mByteArrayLength(env->GetArrayLength(byteArray)) {} - -protected: - virtual size_t doRead(void* buffer, size_t size); - -private: - JNIEnv* mEnv; - const jobject mInputStream; - const jbyteArray mByteArray; - const size_t mByteArrayLength; -}; - -jint JavaStream_OnLoad(JNIEnv* env); - -#endif //RASTERMILL_STREAM_H diff --git a/framesequence/jni/utils/log.h b/framesequence/jni/utils/log.h deleted file mode 100644 index d8441dc1..00000000 --- a/framesequence/jni/utils/log.h +++ /dev/null @@ -1,288 +0,0 @@ -/* - * 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. - */ - -#ifndef LOG_H_ -#define LOG_H_ - -#include <android/log.h> - -#ifdef __cplusplus -extern "C" { -#endif - -// --------------------------------------------------------------------- - -/* - * Normally we strip ALOGV (VERBOSE messages) from release builds. - * You can modify this (for example with "#define LOG_NDEBUG 0" - * at the top of your source file) to change that behavior. - */ -#ifndef LOG_NDEBUG -#ifdef NDEBUG -#define LOG_NDEBUG 1 -#else -#define LOG_NDEBUG 0 -#endif -#endif - -/* - * This is the local tag used for the following simplified - * logging macros. You can change this preprocessor definition - * before using the other macros to change the tag. - */ -#ifndef LOG_TAG -#define LOG_TAG "RasterMill" -#endif - -// --------------------------------------------------------------------- - -/* - * Simplified macro to send a verbose log message using the current LOG_TAG. - */ -#ifndef ALOGV -#if LOG_NDEBUG -#define ALOGV(...) ((void)0) -#else -#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) -#endif -#endif - -#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) - -#ifndef ALOGV_IF -#if LOG_NDEBUG -#define ALOGV_IF(cond, ...) ((void)0) -#else -#define ALOGV_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif -#endif - -/* - * Simplified macro to send a debug log message using the current LOG_TAG. - */ -#ifndef ALOGD -#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGD_IF -#define ALOGD_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -/* - * Simplified macro to send an info log message using the current LOG_TAG. - */ -#ifndef ALOGI -#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGI_IF -#define ALOGI_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -/* - * Simplified macro to send a warning log message using the current LOG_TAG. - */ -#ifndef ALOGW -#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGW_IF -#define ALOGW_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -/* - * Simplified macro to send an error log message using the current LOG_TAG. - */ -#ifndef ALOGE -#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGE_IF -#define ALOGE_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -// --------------------------------------------------------------------- - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * verbose priority. - */ -#ifndef IF_ALOGV -#if LOG_NDEBUG -#define IF_ALOGV() if (false) -#else -#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG) -#endif -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * debug priority. - */ -#ifndef IF_ALOGD -#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG) -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * info priority. - */ -#ifndef IF_ALOGI -#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG) -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * warn priority. - */ -#ifndef IF_ALOGW -#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG) -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * error priority. - */ -#ifndef IF_ALOGE -#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG) -#endif - -// --------------------------------------------------------------------- - -/* - * Log a fatal error. If the given condition fails, this stops program - * execution like a normal assertion, but also generating the given message. - * It is NOT stripped from release builds. Note that the condition test - * is -inverted- from the normal assert() semantics. - */ -#ifndef LOG_ALWAYS_FATAL_IF -#define LOG_ALWAYS_FATAL_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)android_printAssert(#cond, LOG_TAG, ## __VA_ARGS__)) \ - : (void)0 ) -#endif - -#ifndef LOG_ALWAYS_FATAL -#define LOG_ALWAYS_FATAL(...) \ - ( ((void)android_printAssert(NULL, LOG_TAG, ## __VA_ARGS__)) ) -#endif - -/* - * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that - * are stripped out of release builds. - */ -#if LOG_NDEBUG - -#ifndef LOG_FATAL_IF -#define LOG_FATAL_IF(cond, ...) ((void)0) -#endif -#ifndef LOG_FATAL -#define LOG_FATAL(...) ((void)0) -#endif - -#else - -#ifndef LOG_FATAL_IF -#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ## __VA_ARGS__) -#endif -#ifndef LOG_FATAL -#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__) -#endif - -#endif - -/* - * Assertion that generates a log message when the assertion fails. - * Stripped out of release builds. Uses the current LOG_TAG. - */ -#ifndef ALOG_ASSERT -#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__) -//#define ALOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond) -#endif - -// --------------------------------------------------------------------- - -/* - * Basic log message macro. - * - * Example: - * ALOG(LOG_WARN, NULL, "Failed with error %d", errno); - * - * The second argument may be NULL or "" to indicate the "global" tag. - */ -#ifndef ALOG -#define ALOG(priority, tag, ...) \ - LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) -#endif - -/* - * Log macro that allows you to specify a number for the priority. - */ -#ifndef LOG_PRI -#define LOG_PRI(priority, tag, ...) \ - __android_log_print(priority, tag, __VA_ARGS__) -#endif - -/* - * Log macro that allows you to pass in a varargs ("args" is a va_list). - */ -#ifndef LOG_PRI_VA -#define LOG_PRI_VA(priority, tag, fmt, args) \ - __android_log_vprint(priority, NULL, tag, fmt, args) -#endif - -/* - * Conditional given a desired logging priority and tag. - */ -#ifndef IF_ALOG -#define IF_ALOG(priority, tag) \ - if (__android_log_assert(ANDROID_##priority, tag)) -#endif - -/* Returns 2nd arg. Used to substitute default value if caller's vararg list - * is empty. - */ -#define __android_second(dummy, second, ...) second - -/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise - * returns nothing. - */ -#define __android_rest(first, ...) , ## __VA_ARGS__ - -#define android_printAssert(cond, tag, fmt...) \ - __android_log_assert(cond, tag, \ - __android_second(0, ## fmt, NULL) __android_rest(fmt)) - -#ifdef __cplusplus -} -#endif - -#endif /* LOG_H_ */ diff --git a/framesequence/jni/utils/math.h b/framesequence/jni/utils/math.h deleted file mode 100644 index 87f100b9..00000000 --- a/framesequence/jni/utils/math.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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. - */ - -#ifndef MATH_H_ -#define MATH_H_ - -#define max(a,b) \ - ({ __typeof__ (a) _a = (a); \ - __typeof__ (b) _b = (b); \ - _a > _b ? _a : _b; }) - -#define min(a,b) \ - ({ __typeof__ (a) _a = (a); \ - __typeof__ (b) _b = (b); \ - _a < _b ? _a : _b; }) - -#endif /* MATH_H_ */ diff --git a/framesequence/samples/FrameSequenceSamples/Android.bp b/framesequence/samples/FrameSequenceSamples/Android.bp deleted file mode 100644 index f6bd93f0..00000000 --- a/framesequence/samples/FrameSequenceSamples/Android.bp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2014 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 { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -android_app { - - name: "FrameSequenceSample", - // java dependency - static_libs: ["android-common-framesequence"], - jni_libs: ["libframesequence"], - // proguard for framesequence library code - optimize: { - proguard_flags_files: ["proguard.flags"], - }, - sdk_version: "19", - srcs: ["src/**/*.java"], - resource_dirs: ["res"], - aaptflags: [ - "--auto-add-overlay", - "--extra-packages", - "com.android.framesequence.samples", - ], - - product_specific: true, -} diff --git a/framesequence/samples/FrameSequenceSamples/AndroidManifest.xml b/framesequence/samples/FrameSequenceSamples/AndroidManifest.xml deleted file mode 100644 index d6146318..00000000 --- a/framesequence/samples/FrameSequenceSamples/AndroidManifest.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.framesequence.samples" - android:versionCode="1" - android:versionName="1.0" > - - <uses-sdk - android:minSdkVersion="15" - android:targetSdkVersion="18" /> - - <application - android:allowBackup="true" - android:icon="@drawable/ic_launcher" - android:label="@string/app_name" > - <activity - android:name=".SamplesList" - android:label="@string/app_name" > - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - <activity android:name=".FrameSequenceTest" /> - </application> - -</manifest> diff --git a/framesequence/samples/FrameSequenceSamples/build.xml b/framesequence/samples/FrameSequenceSamples/build.xml deleted file mode 100644 index 5e55b4e5..00000000 --- a/framesequence/samples/FrameSequenceSamples/build.xml +++ /dev/null @@ -1,99 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project name="RastermillSamples" default="help"> - - <!-- The local.properties file is created and updated by the 'android' tool. - It contains the path to the SDK. It should *NOT* be checked into - Version Control Systems. --> - <property file="local.properties" /> - - <!-- The ant.properties file can be created by you. It is only edited by the - 'android' tool to add properties to it. - This is the place to change some Ant specific build properties. - Here are some properties you may want to change/update: - - source.dir - The name of the source directory. Default is 'src'. - out.dir - The name of the output directory. Default is 'bin'. - - For other overridable properties, look at the beginning of the rules - files in the SDK, at tools/ant/build.xml - - Properties related to the SDK location or the project target should - be updated using the 'android' tool with the 'update' action. - - This file is an integral part of the build system for your - application and should be checked into Version Control Systems. - - --> - <property file="ant.properties" /> - - <!-- if sdk.dir was not set from one of the property file, then - get it from the ANDROID_HOME env var. - This must be done before we load project.properties since - the proguard config can use sdk.dir --> - <property environment="env" /> - <condition property="sdk.dir" value="${env.ANDROID_HOME}"> - <isset property="env.ANDROID_HOME" /> - </condition> - - <!-- The project.properties file is created and updated by the 'android' - tool, as well as ADT. - - This contains project specific properties such as project target, and library - dependencies. Lower level build properties are stored in ant.properties - (or in .classpath for Eclipse projects). - - This file is an integral part of the build system for your - application and should be checked into Version Control Systems. --> - <loadproperties srcFile="project.properties" /> - - <!-- quick check on sdk.dir --> - <fail - message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable." - unless="sdk.dir" - /> - - <target name="-pre-build"> - <ant dir="../../" target="release" inheritAll="false" /> - <copy todir="libs"> - <fileset dir="../../exported_libs" /> - </copy> - </target> - - <!-- - Import per project custom build rules if present at the root of the project. - This is the place to put custom intermediary targets such as: - -pre-build - -pre-compile - -post-compile (This is typically used for code obfuscation. - Compiled code location: ${out.classes.absolute.dir} - If this is not done in place, override ${out.dex.input.absolute.dir}) - -post-package - -post-build - -pre-clean - --> - <import file="custom_rules.xml" optional="true" /> - - <!-- Import the actual build file. - - To customize existing targets, there are two options: - - Customize only one target: - - copy/paste the target into this file, *before* the - <import> task. - - customize it to your needs. - - Customize the whole content of build.xml - - copy/paste the content of the rules files (minus the top node) - into this file, replacing the <import> task. - - customize to your needs. - - *********************** - ****** IMPORTANT ****** - *********************** - In all cases you must update the value of version-tag below to read 'custom' instead of an integer, - in order to avoid having your file be overridden by tools such as "android update project" - --> - <!-- version-tag: 1 --> - <import file="${sdk.dir}/tools/ant/build.xml" /> - -</project> diff --git a/framesequence/samples/FrameSequenceSamples/proguard.flags b/framesequence/samples/FrameSequenceSamples/proguard.flags deleted file mode 100644 index 4acde2d6..00000000 --- a/framesequence/samples/FrameSequenceSamples/proguard.flags +++ /dev/null @@ -1,3 +0,0 @@ --keep class android.support.rastermill.** { - *; -} diff --git a/framesequence/samples/FrameSequenceSamples/project.properties b/framesequence/samples/FrameSequenceSamples/project.properties deleted file mode 100644 index ce39f2d0..00000000 --- a/framesequence/samples/FrameSequenceSamples/project.properties +++ /dev/null @@ -1,14 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-18 diff --git a/framesequence/samples/FrameSequenceSamples/res/drawable-hdpi/ic_launcher.png b/framesequence/samples/FrameSequenceSamples/res/drawable-hdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 96a442e5..00000000 --- a/framesequence/samples/FrameSequenceSamples/res/drawable-hdpi/ic_launcher.png +++ /dev/null diff --git a/framesequence/samples/FrameSequenceSamples/res/drawable-mdpi/ic_launcher.png b/framesequence/samples/FrameSequenceSamples/res/drawable-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 359047df..00000000 --- a/framesequence/samples/FrameSequenceSamples/res/drawable-mdpi/ic_launcher.png +++ /dev/null diff --git a/framesequence/samples/FrameSequenceSamples/res/drawable-xhdpi/ic_launcher.png b/framesequence/samples/FrameSequenceSamples/res/drawable-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 71c6d760..00000000 --- a/framesequence/samples/FrameSequenceSamples/res/drawable-xhdpi/ic_launcher.png +++ /dev/null diff --git a/framesequence/samples/FrameSequenceSamples/res/layout/basic_test_activity.xml b/framesequence/samples/FrameSequenceSamples/res/layout/basic_test_activity.xml deleted file mode 100644 index ebe72290..00000000 --- a/framesequence/samples/FrameSequenceSamples/res/layout/basic_test_activity.xml +++ /dev/null @@ -1,42 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <View - android:id="@+id/drawableview" - android:layout_width="match_parent" - android:layout_height="300dp" /> - <LinearLayout - android:orientation="horizontal" - android:layout_width="wrap_content" - android:layout_height="wrap_content"> - <Button - android:id="@+id/start" - android:text="@string/start" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> - <Button - android:id="@+id/stop" - android:text="@string/stop" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> - <Button - android:id="@+id/vis" - android:text="@string/vis" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> - <Button - android:id="@+id/invis" - android:text="@string/invis" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> - </LinearLayout> - <Button - android:id="@+id/circle_mask" - android:text="@string/circle_mask" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> -</LinearLayout> diff --git a/framesequence/samples/FrameSequenceSamples/res/raw/animated_gif.gif b/framesequence/samples/FrameSequenceSamples/res/raw/animated_gif.gif Binary files differdeleted file mode 100644 index 51baf158..00000000 --- a/framesequence/samples/FrameSequenceSamples/res/raw/animated_gif.gif +++ /dev/null diff --git a/framesequence/samples/FrameSequenceSamples/res/raw/animated_webp.webp b/framesequence/samples/FrameSequenceSamples/res/raw/animated_webp.webp Binary files differdeleted file mode 100644 index 25c6a4dd..00000000 --- a/framesequence/samples/FrameSequenceSamples/res/raw/animated_webp.webp +++ /dev/null diff --git a/framesequence/samples/FrameSequenceSamples/res/values/strings.xml b/framesequence/samples/FrameSequenceSamples/res/values/strings.xml deleted file mode 100644 index b9ace00d..00000000 --- a/framesequence/samples/FrameSequenceSamples/res/values/strings.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <!-- NOTE: all strings should be marked as translatable=false, - since this sample app is for testing, and won't be shipped --> - - <string name="app_name" translatable="false">FrameSequence Samples</string> - <string name="action_settings" translatable="false">Settings</string> - - <string name="start" translatable="false">start</string> - <string name="stop" translatable="false">stop</string> - <string name="vis" translatable="false">vis</string> - <string name="invis" translatable="false">invis</string> - - <string name="circle_mask" translatable="false">Toggle Circle Mask</string> - -</resources> diff --git a/framesequence/samples/FrameSequenceSamples/res/values/styles.xml b/framesequence/samples/FrameSequenceSamples/res/values/styles.xml deleted file mode 100644 index 737bdc38..00000000 --- a/framesequence/samples/FrameSequenceSamples/res/values/styles.xml +++ /dev/null @@ -1,7 +0,0 @@ -<resources> - - <!-- Application theme. --> - <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar"> - </style> - -</resources> diff --git a/framesequence/samples/FrameSequenceSamples/src/com/android/framesequence/samples/FrameSequenceTest.java b/framesequence/samples/FrameSequenceSamples/src/com/android/framesequence/samples/FrameSequenceTest.java deleted file mode 100644 index 7f66ce14..00000000 --- a/framesequence/samples/FrameSequenceSamples/src/com/android/framesequence/samples/FrameSequenceTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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.framesequence.samples; - -import android.app.Activity; -import android.graphics.Bitmap; -import android.os.Bundle; -import android.support.rastermill.FrameSequence; -import android.support.rastermill.FrameSequenceDrawable; -import android.view.View; -import android.widget.Toast; - -import java.io.InputStream; -import java.util.HashSet; - -public class FrameSequenceTest extends Activity { - FrameSequenceDrawable mDrawable; - int mResourceId; - - // This provider is entirely unnecessary, just here to validate the acquire/release process - private class CheckingProvider implements FrameSequenceDrawable.BitmapProvider { - HashSet<Bitmap> mBitmaps = new HashSet<Bitmap>(); - @Override - public Bitmap acquireBitmap(int minWidth, int minHeight) { - Bitmap bitmap = - Bitmap.createBitmap(minWidth + 1, minHeight + 4, Bitmap.Config.ARGB_8888); - mBitmaps.add(bitmap); - return bitmap; - } - - @Override - public void releaseBitmap(Bitmap bitmap) { - if (!mBitmaps.contains(bitmap)) throw new IllegalStateException(); - mBitmaps.remove(bitmap); - bitmap.recycle(); - } - - public boolean isEmpty() { - return mBitmaps.isEmpty(); - } - } - - final CheckingProvider mProvider = new CheckingProvider(); - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mResourceId = getIntent().getIntExtra("resourceId", R.raw.animated_gif); - - setContentView(R.layout.basic_test_activity); - findViewById(R.id.start).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - mDrawable.start(); - } - }); - findViewById(R.id.stop).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - mDrawable.stop(); - } - }); - findViewById(R.id.vis).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - mDrawable.setVisible(true, true); - } - }); - findViewById(R.id.invis).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - mDrawable.setVisible(false, true); - } - }); - findViewById(R.id.circle_mask).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - mDrawable.setCircleMaskEnabled(!mDrawable.getCircleMaskEnabled()); - } - }); - } - - @Override - protected void onResume() { - super.onResume(); - - View drawableView = findViewById(R.id.drawableview); - InputStream is = getResources().openRawResource(mResourceId); - - FrameSequence fs = FrameSequence.decodeStream(is); - mDrawable = new FrameSequenceDrawable(fs, mProvider); - mDrawable.setOnFinishedListener(new FrameSequenceDrawable.OnFinishedListener() { - @Override - public void onFinished(FrameSequenceDrawable drawable) { - Toast.makeText(getApplicationContext(), - "The animation has finished", Toast.LENGTH_SHORT).show(); - } - }); - drawableView.setBackgroundDrawable(mDrawable); - } - - @Override - protected void onPause() { - super.onPause(); - View drawableView = findViewById(R.id.drawableview); - - mDrawable.destroy(); - if (!mProvider.isEmpty()) throw new IllegalStateException("All bitmaps not recycled"); - - mDrawable = null; - drawableView.setBackgroundDrawable(null); - } -} diff --git a/framesequence/samples/FrameSequenceSamples/src/com/android/framesequence/samples/SamplesList.java b/framesequence/samples/FrameSequenceSamples/src/com/android/framesequence/samples/SamplesList.java deleted file mode 100644 index 36cc784c..00000000 --- a/framesequence/samples/FrameSequenceSamples/src/com/android/framesequence/samples/SamplesList.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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.framesequence.samples; - -import android.app.ListActivity; -import android.content.Intent; -import android.os.Bundle; -import android.view.View; -import android.widget.ListView; -import android.widget.SimpleAdapter; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -public class SamplesList extends ListActivity { - - static final String KEY_NAME = "name"; - static final String KEY_CLASS = "clazz"; - static final String KEY_RESOURCE = "res"; - - static Map<String,?> makeSample(String name, Class<?> activity, int resourceId) { - Map<String,Object> ret = new HashMap<String,Object>(); - ret.put(KEY_NAME, name); - ret.put(KEY_CLASS, activity); - ret.put(KEY_RESOURCE, resourceId); - return ret; - } - - static final ArrayList<Map<String,?>> SAMPLES = new ArrayList<>(); - static { - SAMPLES.add(makeSample("GIF animation", FrameSequenceTest.class, R.raw.animated_gif)); - SAMPLES.add(makeSample("WEBP animation", FrameSequenceTest.class, R.raw.animated_webp)); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setListAdapter(new SimpleAdapter(this, SAMPLES, - android.R.layout.simple_list_item_1, new String[] { KEY_NAME }, - new int[] { android.R.id.text1 })); - } - - @Override - protected void onListItemClick(ListView l, View v, int position, long id) { - Class<?> clazz = (Class<?>) SAMPLES.get(position).get(KEY_CLASS); - int resourceId = ((Integer) SAMPLES.get(position).get(KEY_RESOURCE)).intValue(); - - Intent intent = new Intent(this, clazz); - intent.putExtra("resourceId", resourceId); - startActivity(intent); - } - -} diff --git a/framesequence/src/android/support/rastermill/FrameSequence.java b/framesequence/src/android/support/rastermill/FrameSequence.java index 8ff241fa..1af64d2b 100644 --- a/framesequence/src/android/support/rastermill/FrameSequence.java +++ b/framesequence/src/android/support/rastermill/FrameSequence.java @@ -16,136 +16,42 @@ package android.support.rastermill; -import android.graphics.Bitmap; import java.nio.ByteBuffer; import java.io.InputStream; +/** + * Entire class is a no-op, does nothing + */ +@Deprecated public class FrameSequence { static { System.loadLibrary("framesequence"); } - private final long mNativeFrameSequence; - private final int mWidth; - private final int mHeight; - private final boolean mOpaque; - private final int mFrameCount; - private final int mDefaultLoopCount; - - public int getWidth() { return mWidth; } - public int getHeight() { return mHeight; } - public boolean isOpaque() { return mOpaque; } - public int getFrameCount() { return mFrameCount; } - public int getDefaultLoopCount() { return mDefaultLoopCount; } + public int getWidth() { return 0; } + public int getHeight() { return 0; } + public boolean isOpaque() { return true; } + public int getFrameCount() { return 0; } + public int getDefaultLoopCount() { return 0; } - private static native FrameSequence nativeDecodeByteArray(byte[] data, int offset, int length); - private static native FrameSequence nativeDecodeStream(InputStream is, byte[] tempStorage); - private static native FrameSequence nativeDecodeByteBuffer(ByteBuffer buffer, int offset, int capacity); - private static native void nativeDestroyFrameSequence(long nativeFrameSequence); - private static native long nativeCreateState(long nativeFrameSequence); - private static native void nativeDestroyState(long nativeState); - private static native long nativeGetFrame(long nativeState, int frameNr, - Bitmap output, int previousFrameNr); - - @SuppressWarnings("unused") // called by native - private FrameSequence(long nativeFrameSequence, int width, int height, - boolean opaque, int frameCount, int defaultLoopCount) { - mNativeFrameSequence = nativeFrameSequence; - mWidth = width; - mHeight = height; - mOpaque = opaque; - mFrameCount = frameCount; - mDefaultLoopCount = defaultLoopCount; + @SuppressWarnings("unused") + private FrameSequence() { } public static FrameSequence decodeByteArray(byte[] data) { - return decodeByteArray(data, 0, data.length); + return null; } public static FrameSequence decodeByteArray(byte[] data, int offset, int length) { - if (data == null) throw new IllegalArgumentException(); - if (offset < 0 || length < 0 || (offset + length > data.length)) { - throw new IllegalArgumentException("invalid offset/length parameters"); - } - return nativeDecodeByteArray(data, offset, length); + return null; } public static FrameSequence decodeByteBuffer(ByteBuffer buffer) { - if (buffer == null) throw new IllegalArgumentException(); - if (!buffer.isDirect()) { - if (buffer.hasArray()) { - byte[] byteArray = buffer.array(); - return decodeByteArray(byteArray, buffer.position(), buffer.remaining()); - } else { - throw new IllegalArgumentException("Cannot have non-direct ByteBuffer with no byte array"); - } - } - return nativeDecodeByteBuffer(buffer, buffer.position(), buffer.remaining()); + return null; } public static FrameSequence decodeStream(InputStream stream) { - if (stream == null) throw new IllegalArgumentException(); - byte[] tempStorage = new byte[16 * 1024]; // TODO: use buffer pool - return nativeDecodeStream(stream, tempStorage); - } - - State createState() { - if (mNativeFrameSequence == 0) { - throw new IllegalStateException("attempted to use incorrectly built FrameSequence"); - } - - long nativeState = nativeCreateState(mNativeFrameSequence); - if (nativeState == 0) { - return null; - } - return new State(nativeState); - } - - @Override - protected void finalize() throws Throwable { - try { - if (mNativeFrameSequence != 0) nativeDestroyFrameSequence(mNativeFrameSequence); - } finally { - super.finalize(); - } - } - - /** - * Playback state used when moving frames forward in a frame sequence. - * - * Note that this doesn't require contiguous frames to be rendered, it just stores - * information (in the case of gif, a recall buffer) that will be used to construct - * frames based upon data recorded before previousFrameNr. - * - * Note: {@link #destroy()} *must* be called before the object is GC'd to free native resources - * - * Note: State holds a native ref to its FrameSequence instance, so its FrameSequence should - * remain ref'd while it is in use - */ - static class State { - private long mNativeState; - - public State(long nativeState) { - mNativeState = nativeState; - } - - public void destroy() { - if (mNativeState != 0) { - nativeDestroyState(mNativeState); - mNativeState = 0; - } - } - - // TODO: consider adding alternate API for drawing into a SurfaceTexture - public long getFrame(int frameNr, Bitmap output, int previousFrameNr) { - if (output == null || output.getConfig() != Bitmap.Config.ARGB_8888) { - throw new IllegalArgumentException("Bitmap passed must be non-null and ARGB_8888"); - } - if (mNativeState == 0) { - throw new IllegalStateException("attempted to draw destroyed FrameSequenceState"); - } - return nativeGetFrame(mNativeState, frameNr, output, previousFrameNr); - } + return null; } } diff --git a/framesequence/src/android/support/rastermill/FrameSequenceDrawable.java b/framesequence/src/android/support/rastermill/FrameSequenceDrawable.java index d86df62a..30e3a0c4 100644 --- a/framesequence/src/android/support/rastermill/FrameSequenceDrawable.java +++ b/framesequence/src/android/support/rastermill/FrameSequenceDrawable.java @@ -17,524 +17,116 @@ package android.support.rastermill; import android.graphics.Bitmap; -import android.graphics.BitmapShader; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.ColorFilter; -import android.graphics.Paint; import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.Shader; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Process; -import android.os.SystemClock; -import android.util.Log; +/** + * Entire class is a no-op, does nothing + */ +@Deprecated public class FrameSequenceDrawable extends Drawable implements Animatable, Runnable { - private static final String TAG = "FrameSequence"; - /** - * These constants are chosen to imitate common browser behavior for WebP/GIF. - * If other decoders are added, this behavior should be moved into the WebP/GIF decoders. - * - * Note that 0 delay is undefined behavior in the GIF standard. - */ - private static final long MIN_DELAY_MS = 20; - private static final long DEFAULT_DELAY_MS = 100; - - private static final Object sLock = new Object(); - private static HandlerThread sDecodingThread; - private static Handler sDecodingThreadHandler; - private static void initializeDecodingThread() { - synchronized (sLock) { - if (sDecodingThread != null) return; - - sDecodingThread = new HandlerThread("FrameSequence decoding thread", - Process.THREAD_PRIORITY_BACKGROUND); - sDecodingThread.start(); - sDecodingThreadHandler = new Handler(sDecodingThread.getLooper()); - } - } public static interface OnFinishedListener { - /** - * Called when a FrameSequenceDrawable has finished looping. - * - * Note that this is will not be called if the drawable is explicitly - * stopped, or marked invisible. - */ public abstract void onFinished(FrameSequenceDrawable drawable); } public static interface BitmapProvider { - /** - * Called by FrameSequenceDrawable to aquire an 8888 Bitmap with minimum dimensions. - */ public abstract Bitmap acquireBitmap(int minWidth, int minHeight); - /** - * Called by FrameSequenceDrawable to release a Bitmap it no longer needs. The Bitmap - * will no longer be used at all by the drawable, so it is safe to reuse elsewhere. - * - * This method may be called by FrameSequenceDrawable on any thread. - */ public abstract void releaseBitmap(Bitmap bitmap); } - private static BitmapProvider sAllocatingBitmapProvider = new BitmapProvider() { - @Override - public Bitmap acquireBitmap(int minWidth, int minHeight) { - return Bitmap.createBitmap(minWidth, minHeight, Bitmap.Config.ARGB_8888); - } - - @Override - public void releaseBitmap(Bitmap bitmap) {} - }; - - /** - * Register a callback to be invoked when a FrameSequenceDrawable finishes looping. - * - * @see #setLoopBehavior(int) - */ public void setOnFinishedListener(OnFinishedListener onFinishedListener) { - mOnFinishedListener = onFinishedListener; } - /** - * Loop a finite number of times, which can be set using setLoopCount. Default to loop once. - */ public static final int LOOP_FINITE = 1; - /** - * Loop continuously. The OnFinishedListener will never be called. - */ public static final int LOOP_INF = 2; - /** - * Use loop count stored in source data, or LOOP_ONCE if not present. - */ public static final int LOOP_DEFAULT = 3; - /** - * Loop only once. - * - * @deprecated Use LOOP_FINITE instead. - */ @Deprecated public static final int LOOP_ONCE = LOOP_FINITE; - /** - * Define looping behavior of frame sequence. - * - * Must be one of LOOP_ONCE, LOOP_INF, LOOP_DEFAULT, or LOOP_FINITE. - */ public void setLoopBehavior(int loopBehavior) { - mLoopBehavior = loopBehavior; + } - /** - * Set the number of loops in LOOP_FINITE mode. The number must be a postive integer. - */ public void setLoopCount(int loopCount) { - mLoopCount = loopCount; - } - private final FrameSequence mFrameSequence; - private final FrameSequence.State mFrameSequenceState; - - private final Paint mPaint; - private BitmapShader mFrontBitmapShader; - private BitmapShader mBackBitmapShader; - private final Rect mSrcRect; - private boolean mCircleMaskEnabled; - - //Protects the fields below - private final Object mLock = new Object(); - - private final BitmapProvider mBitmapProvider; - private boolean mDestroyed = false; - private Bitmap mFrontBitmap; - private Bitmap mBackBitmap; - - private static final int STATE_SCHEDULED = 1; - private static final int STATE_DECODING = 2; - private static final int STATE_WAITING_TO_SWAP = 3; - private static final int STATE_READY_TO_SWAP = 4; - - private int mState; - private int mCurrentLoop; - private int mLoopBehavior = LOOP_DEFAULT; - private int mLoopCount = 1; - - private long mLastSwap; - private long mNextSwap; - private int mNextFrameToDecode; - private OnFinishedListener mOnFinishedListener; - - private RectF mTempRectF = new RectF(); - - /** - * Runs on decoding thread, only modifies mBackBitmap's pixels - */ - private Runnable mDecodeRunnable = new Runnable() { - @Override - public void run() { - int nextFrame; - Bitmap bitmap; - synchronized (mLock) { - if (mDestroyed) return; - - nextFrame = mNextFrameToDecode; - if (nextFrame < 0) { - return; - } - bitmap = mBackBitmap; - mState = STATE_DECODING; - } - int lastFrame = nextFrame - 2; - boolean exceptionDuringDecode = false; - long invalidateTimeMs = 0; - try { - invalidateTimeMs = mFrameSequenceState.getFrame(nextFrame, bitmap, lastFrame); - } catch(Exception e) { - // Exception during decode: continue, but delay next frame indefinitely. - Log.e(TAG, "exception during decode: " + e); - exceptionDuringDecode = true; - } - - if (invalidateTimeMs < MIN_DELAY_MS) { - invalidateTimeMs = DEFAULT_DELAY_MS; - } - - boolean schedule = false; - Bitmap bitmapToRelease = null; - synchronized (mLock) { - if (mDestroyed) { - bitmapToRelease = mBackBitmap; - mBackBitmap = null; - } else if (mNextFrameToDecode >= 0 && mState == STATE_DECODING) { - schedule = true; - mNextSwap = exceptionDuringDecode ? Long.MAX_VALUE : invalidateTimeMs + mLastSwap; - mState = STATE_WAITING_TO_SWAP; - } - } - if (schedule) { - scheduleSelf(FrameSequenceDrawable.this, mNextSwap); - } - if (bitmapToRelease != null) { - // destroy the bitmap here, since there's no safe way to get back to - // drawable thread - drawable is likely detached, so schedule is noop. - mBitmapProvider.releaseBitmap(bitmapToRelease); - } - } - }; - - private Runnable mFinishedCallbackRunnable = new Runnable() { - @Override - public void run() { - synchronized (mLock) { - mNextFrameToDecode = -1; - mState = 0; - } - if (mOnFinishedListener != null) { - mOnFinishedListener.onFinished(FrameSequenceDrawable.this); - } - } - }; - - private static Bitmap acquireAndValidateBitmap(BitmapProvider bitmapProvider, - int minWidth, int minHeight) { - Bitmap bitmap = bitmapProvider.acquireBitmap(minWidth, minHeight); - - if (bitmap.getWidth() < minWidth - || bitmap.getHeight() < minHeight - || bitmap.getConfig() != Bitmap.Config.ARGB_8888) { - throw new IllegalArgumentException("Invalid bitmap provided"); - } - - return bitmap; } public FrameSequenceDrawable(FrameSequence frameSequence) { - this(frameSequence, sAllocatingBitmapProvider); } public FrameSequenceDrawable(FrameSequence frameSequence, BitmapProvider bitmapProvider) { - if (frameSequence == null || bitmapProvider == null) throw new IllegalArgumentException(); - - mFrameSequence = frameSequence; - mFrameSequenceState = frameSequence.createState(); - final int width = frameSequence.getWidth(); - final int height = frameSequence.getHeight(); - - mBitmapProvider = bitmapProvider; - mFrontBitmap = acquireAndValidateBitmap(bitmapProvider, width, height); - mBackBitmap = acquireAndValidateBitmap(bitmapProvider, width, height); - mSrcRect = new Rect(0, 0, width, height); - mPaint = new Paint(); - mPaint.setFilterBitmap(true); - - mFrontBitmapShader - = new BitmapShader(mFrontBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); - mBackBitmapShader - = new BitmapShader(mBackBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); - - mLastSwap = 0; - - mNextFrameToDecode = -1; - mFrameSequenceState.getFrame(0, mFrontBitmap, -1); - initializeDecodingThread(); } - /** - * Pass true to mask the shape of the animated drawing content to a circle. - * - * <p> The masking circle will be the largest circle contained in the Drawable's bounds. - * Masking is done with BitmapShader, incurring minimal additional draw cost. - */ public final void setCircleMaskEnabled(boolean circleMaskEnabled) { - if (mCircleMaskEnabled != circleMaskEnabled) { - mCircleMaskEnabled = circleMaskEnabled; - // Anti alias only necessary when using circular mask - mPaint.setAntiAlias(circleMaskEnabled); - invalidateSelf(); - } } public final boolean getCircleMaskEnabled() { - return mCircleMaskEnabled; - } - - private void checkDestroyedLocked() { - if (mDestroyed) { - throw new IllegalStateException("Cannot perform operation on recycled drawable"); - } + return false; } public boolean isDestroyed() { - synchronized (mLock) { - return mDestroyed; - } + return true; } - /** - * Marks the drawable as permanently recycled (and thus unusable), and releases any owned - * Bitmaps drawable to its BitmapProvider, if attached. - * - * If no BitmapProvider is attached to the drawable, recycle() is called on the Bitmaps. - */ public void destroy() { - if (mBitmapProvider == null) { - throw new IllegalStateException("BitmapProvider must be non-null"); - } - - Bitmap bitmapToReleaseA; - Bitmap bitmapToReleaseB = null; - synchronized (mLock) { - checkDestroyedLocked(); - - bitmapToReleaseA = mFrontBitmap; - mFrontBitmap = null; - - if (mState != STATE_DECODING) { - bitmapToReleaseB = mBackBitmap; - mBackBitmap = null; - } - - mDestroyed = true; - } - - // For simplicity and safety, we don't destroy the state object here - mBitmapProvider.releaseBitmap(bitmapToReleaseA); - if (bitmapToReleaseB != null) { - mBitmapProvider.releaseBitmap(bitmapToReleaseB); - } - } - - @Override - protected void finalize() throws Throwable { - try { - mFrameSequenceState.destroy(); - } finally { - super.finalize(); - } } @Override public void draw(Canvas canvas) { - synchronized (mLock) { - checkDestroyedLocked(); - if (mState == STATE_WAITING_TO_SWAP) { - // may have failed to schedule mark ready runnable, - // so go ahead and swap if swapping is due - if (mNextSwap - SystemClock.uptimeMillis() <= 0) { - mState = STATE_READY_TO_SWAP; - } - } - - if (isRunning() && mState == STATE_READY_TO_SWAP) { - // Because draw has occurred, the view system is guaranteed to no longer hold a - // reference to the old mFrontBitmap, so we now use it to produce the next frame - Bitmap tmp = mBackBitmap; - mBackBitmap = mFrontBitmap; - mFrontBitmap = tmp; - - BitmapShader tmpShader = mBackBitmapShader; - mBackBitmapShader = mFrontBitmapShader; - mFrontBitmapShader = tmpShader; - - mLastSwap = SystemClock.uptimeMillis(); - - boolean continueLooping = true; - if (mNextFrameToDecode == mFrameSequence.getFrameCount() - 1) { - mCurrentLoop++; - if ((mLoopBehavior == LOOP_FINITE && mCurrentLoop == mLoopCount) || - (mLoopBehavior == LOOP_DEFAULT && mCurrentLoop == mFrameSequence.getDefaultLoopCount())) { - continueLooping = false; - } - } - - if (continueLooping) { - scheduleDecodeLocked(); - } else { - scheduleSelf(mFinishedCallbackRunnable, 0); - } - } - } - - if (mCircleMaskEnabled) { - final Rect bounds = getBounds(); - final int bitmapWidth = getIntrinsicWidth(); - final int bitmapHeight = getIntrinsicHeight(); - final float scaleX = 1.0f * bounds.width() / bitmapWidth; - final float scaleY = 1.0f * bounds.height() / bitmapHeight; - - canvas.save(); - // scale and translate to account for bounds, so we can operate in intrinsic - // width/height (so it's valid to use an unscaled bitmap shader) - canvas.translate(bounds.left, bounds.top); - canvas.scale(scaleX, scaleY); - - final float unscaledCircleDiameter = Math.min(bounds.width(), bounds.height()); - final float scaledDiameterX = unscaledCircleDiameter / scaleX; - final float scaledDiameterY = unscaledCircleDiameter / scaleY; - - // Want to draw a circle, but we have to compensate for canvas scale - mTempRectF.set( - (bitmapWidth - scaledDiameterX) / 2.0f, - (bitmapHeight - scaledDiameterY) / 2.0f, - (bitmapWidth + scaledDiameterX) / 2.0f, - (bitmapHeight + scaledDiameterY) / 2.0f); - mPaint.setShader(mFrontBitmapShader); - canvas.drawOval(mTempRectF, mPaint); - canvas.restore(); - } else { - mPaint.setShader(null); - canvas.drawBitmap(mFrontBitmap, mSrcRect, getBounds(), mPaint); - } - } - - private void scheduleDecodeLocked() { - mState = STATE_SCHEDULED; - mNextFrameToDecode = (mNextFrameToDecode + 1) % mFrameSequence.getFrameCount(); - sDecodingThreadHandler.post(mDecodeRunnable); + canvas.drawColor(Color.MAGENTA); } @Override public void run() { - // set ready to swap as necessary - boolean invalidate = false; - synchronized (mLock) { - if (mNextFrameToDecode >= 0 && mState == STATE_WAITING_TO_SWAP) { - mState = STATE_READY_TO_SWAP; - invalidate = true; - } - } - if (invalidate) { - invalidateSelf(); - } } @Override public void start() { - if (!isRunning()) { - synchronized (mLock) { - checkDestroyedLocked(); - if (mState == STATE_SCHEDULED) return; // already scheduled - mCurrentLoop = 0; - scheduleDecodeLocked(); - } - } } @Override public void stop() { - if (isRunning()) { - unscheduleSelf(this); - } } @Override public boolean isRunning() { - synchronized (mLock) { - return mNextFrameToDecode > -1 && !mDestroyed; - } + return false; } @Override - public void unscheduleSelf(Runnable what) { - synchronized (mLock) { - mNextFrameToDecode = -1; - mState = 0; - } - super.unscheduleSelf(what); - } - - @Override - public boolean setVisible(boolean visible, boolean restart) { - boolean changed = super.setVisible(visible, restart); - - if (!visible) { - stop(); - } else if (restart || changed) { - stop(); - start(); - } - - return changed; - } - - // drawing properties - - @Override public void setFilterBitmap(boolean filter) { - mPaint.setFilterBitmap(filter); } @Override public void setAlpha(int alpha) { - mPaint.setAlpha(alpha); } @Override public void setColorFilter(ColorFilter colorFilter) { - mPaint.setColorFilter(colorFilter); + } @Override public int getIntrinsicWidth() { - return mFrameSequence.getWidth(); + return 0; } @Override public int getIntrinsicHeight() { - return mFrameSequence.getHeight(); + return 0; } @Override public int getOpacity() { - return mFrameSequence.isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSPARENT; + return PixelFormat.OPAQUE; } } |