diff options
author | Jay Wang <jaywang@codeaurora.org> | 2016-02-22 16:38:13 -0800 |
---|---|---|
committer | Steve Kondik <steve@cyngn.com> | 2016-08-03 15:45:46 -0700 |
commit | 16ac0c5d1326a232624804a9d5697274186fcce4 (patch) | |
tree | a34dc6f2241382370ef469d8bfa9c086b4ae6ebe /src/com/android/camera | |
parent | e5d300a3dc13848fe8a8920bafb5474c29b44213 (diff) | |
download | android_packages_apps_Snap-16ac0c5d1326a232624804a9d5697274186fcce4.tar.gz android_packages_apps_Snap-16ac0c5d1326a232624804a9d5697274186fcce4.tar.bz2 android_packages_apps_Snap-16ac0c5d1326a232624804a9d5697274186fcce4.zip |
SnapdragonCamera: Add support for outputting MPO format files
Add support for generating MPO formatted files from the application
layer.
CRs-Fixed: 993611
Change-Id: I9a78d33e1d80b7da748f9bc75446f49172342078
Diffstat (limited to 'src/com/android/camera')
-rw-r--r-- | src/com/android/camera/exif/ExifTag.java | 20 | ||||
-rw-r--r-- | src/com/android/camera/exif/JpegHeader.java | 3 | ||||
-rw-r--r-- | src/com/android/camera/exif/OrderedDataOutputStream.java | 2 | ||||
-rw-r--r-- | src/com/android/camera/mpo/MpoData.java | 184 | ||||
-rw-r--r-- | src/com/android/camera/mpo/MpoIfdData.java | 126 | ||||
-rw-r--r-- | src/com/android/camera/mpo/MpoImageData.java | 248 | ||||
-rw-r--r-- | src/com/android/camera/mpo/MpoInterface.java | 143 | ||||
-rw-r--r-- | src/com/android/camera/mpo/MpoOutputStream.java | 333 | ||||
-rw-r--r-- | src/com/android/camera/mpo/MpoTag.java | 163 |
9 files changed, 1210 insertions, 12 deletions
diff --git a/src/com/android/camera/exif/ExifTag.java b/src/com/android/camera/exif/ExifTag.java index 1d50316dd..fcf0f5c29 100644 --- a/src/com/android/camera/exif/ExifTag.java +++ b/src/com/android/camera/exif/ExifTag.java @@ -85,7 +85,7 @@ public class ExifTag { TYPE_TO_SIZE_MAP[TYPE_RATIONAL] = 8; } - static final int SIZE_UNDEFINED = 0; + public static final int SIZE_UNDEFINED = 0; // Exif TagId private final short mTagId; @@ -124,7 +124,7 @@ public class ExifTag { } // Use builtTag in ExifInterface instead of constructor. - ExifTag(short tagId, short type, int componentCount, int ifd, + public ExifTag(short tagId, short type, int componentCount, int ifd, boolean hasDefinedComponentCount) { mTagId = tagId; mDataType = type; @@ -163,7 +163,7 @@ public class ExifTag { return mIfd; } - protected void setIfd(int ifdId) { + public void setIfd(int ifdId) { mIfd = ifdId; } @@ -785,7 +785,7 @@ public class ExifTag { * @exception IllegalArgumentException if the data type is * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}. */ - protected long getValueAt(int index) { + public long getValueAt(int index) { if (mValue instanceof long[]) { return ((long[]) mValue)[index]; } else if (mValue instanceof byte[]) { @@ -812,7 +812,7 @@ public class ExifTag { /* * Get the converted ascii byte. Used by ExifOutputStream. */ - protected byte[] getStringByte() { + public byte[] getStringByte() { return (byte[]) mValue; } @@ -822,7 +822,7 @@ public class ExifTag { * @exception IllegalArgumentException If the type is NOT * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}. */ - protected Rational getRational(int index) { + public Rational getRational(int index) { if ((mDataType != TYPE_RATIONAL) && (mDataType != TYPE_UNSIGNED_RATIONAL)) { throw new IllegalArgumentException("Cannot get RATIONAL value from " + convertTypeToString(mDataType)); @@ -833,7 +833,7 @@ public class ExifTag { /** * Equivalent to getBytes(buffer, 0, buffer.length). */ - protected void getBytes(byte[] buf) { + public void getBytes(byte[] buf) { getBytes(buf, 0, buf.length); } @@ -847,7 +847,7 @@ public class ExifTag { * @exception IllegalArgumentException If the type is NOT * {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE}. */ - protected void getBytes(byte[] buf, int offset, int length) { + public void getBytes(byte[] buf, int offset, int length) { if ((mDataType != TYPE_UNDEFINED) && (mDataType != TYPE_UNSIGNED_BYTE)) { throw new IllegalArgumentException("Cannot get BYTE value from " + convertTypeToString(mDataType)); @@ -860,14 +860,14 @@ public class ExifTag { * Gets the offset of this tag. This is only valid if this data size > 4 and * contains an offset to the location of the actual value. */ - protected int getOffset() { + public int getOffset() { return mOffset; } /** * Sets the offset of this tag. */ - protected void setOffset(int offset) { + public void setOffset(int offset) { mOffset = offset; } diff --git a/src/com/android/camera/exif/JpegHeader.java b/src/com/android/camera/exif/JpegHeader.java index 383617af4..6fc46ca18 100644 --- a/src/com/android/camera/exif/JpegHeader.java +++ b/src/com/android/camera/exif/JpegHeader.java @@ -16,8 +16,9 @@ package com.android.camera.exif; -class JpegHeader { +public class JpegHeader { public static final short SOI = (short) 0xFFD8; + public static final short APP2 = (short) 0xFFE2; public static final short APP1 = (short) 0xFFE1; public static final short APP0 = (short) 0xFFE0; public static final short EOI = (short) 0xFFD9; diff --git a/src/com/android/camera/exif/OrderedDataOutputStream.java b/src/com/android/camera/exif/OrderedDataOutputStream.java index abc0a6eb1..3206d72b0 100644 --- a/src/com/android/camera/exif/OrderedDataOutputStream.java +++ b/src/com/android/camera/exif/OrderedDataOutputStream.java @@ -22,7 +22,7 @@ import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; -class OrderedDataOutputStream extends FilterOutputStream { +public class OrderedDataOutputStream extends FilterOutputStream { private final ByteBuffer mByteBuffer = ByteBuffer.allocate(4); private int mSize = 0; diff --git a/src/com/android/camera/mpo/MpoData.java b/src/com/android/camera/mpo/MpoData.java new file mode 100644 index 000000000..7cd431ebe --- /dev/null +++ b/src/com/android/camera/mpo/MpoData.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.camera.mpo; + +import java.util.ArrayList; +import java.util.List; + +import com.android.camera.mpo.MpoTag.MpEntry; + +public class MpoData { + + private MpoImageData mPrimaryMpoImage; + private ArrayList<MpoImageData> mAuxiliaryImages = new ArrayList<MpoImageData>(); + + public MpoData() { + } + + public void setPrimaryMpoImage(MpoImageData image) { + mPrimaryMpoImage = image; + addDefaultAttribIfdTags(mPrimaryMpoImage, 1); + addDefaultIndexIfdTags(); + } + + public void addAuxiliaryMpoImage(MpoImageData image) { + mAuxiliaryImages.add(image); + int imageNum = getAuxiliaryImageCount() + ((mPrimaryMpoImage == null) ? 0 : 1); + addDefaultAttribIfdTags(image, imageNum); + } + + public boolean removeAuxiliaryMpoImage(MpoImageData image) { + boolean ret = mAuxiliaryImages.remove(image); + return ret; + } + + public MpoImageData getPrimaryMpoImage() { + return mPrimaryMpoImage; + } + + public List<MpoImageData> getAuxiliaryMpoImages() { + return mAuxiliaryImages; + } + + public int getAuxiliaryImageCount() { + return mAuxiliaryImages.size(); + } + + public void addDefaultAttribIfdTags(MpoImageData image, int imageNum) { + MpoTag mpFormatVersionTag = new MpoTag((short) MpoInterface.TAG_MP_FORMAT_VERSION, + MpoTag.TYPE_UNDEFINED, 4, MpoIfdData.TYPE_MP_ATTRIB_IFD, true); + mpFormatVersionTag.setValue(MpoIfdData.MP_FORMAT_VER_VALUE); + image.addTag(mpFormatVersionTag); + + MpoTag imageNumTag = new MpoTag((short) MpoInterface.TAG_IMAGE_NUMBER, + MpoTag.TYPE_UNSIGNED_LONG, 1, MpoIfdData.TYPE_MP_ATTRIB_IFD, false); + imageNumTag.setValue(imageNum); + image.addTag(imageNumTag); + } + + public void addDefaultIndexIfdTags() { + if (mPrimaryMpoImage == null) + throw new IllegalArgumentException("Primary Mpo Image has not been set"); + if (getAuxiliaryImageCount() == 0) + throw new IllegalArgumentException("No auxiliary images have been added"); + + MpoTag mpFormatVersionTag = mPrimaryMpoImage.getTag( + (short) MpoInterface.TAG_MP_FORMAT_VERSION, MpoIfdData.TYPE_MP_INDEX_IFD); + if (mpFormatVersionTag == null) { + mpFormatVersionTag = new MpoTag((short) MpoInterface.TAG_MP_FORMAT_VERSION, + MpoTag.TYPE_UNDEFINED, 4, MpoIfdData.TYPE_MP_INDEX_IFD, true); + mpFormatVersionTag.setValue(MpoIfdData.MP_FORMAT_VER_VALUE); + mPrimaryMpoImage.addTag(mpFormatVersionTag); + } + + MpoTag numImagesTag = mPrimaryMpoImage.getTag((short) MpoInterface.TAG_NUM_IMAGES, + MpoIfdData.TYPE_MP_INDEX_IFD); + if (numImagesTag == null) { + numImagesTag = new MpoTag((short) MpoInterface.TAG_NUM_IMAGES, + MpoTag.TYPE_UNSIGNED_LONG, 1, MpoIfdData.TYPE_MP_INDEX_IFD, false); + } + numImagesTag.setValue(getAuxiliaryImageCount() + 1); + mPrimaryMpoImage.addTag(numImagesTag); + + // check, create and add required tags + MpoTag mpEntryTag = new MpoTag((short) MpoInterface.TAG_MP_ENTRY, MpoTag.TYPE_UNDEFINED, + MpoTag.SIZE_UNDEFINED, MpoIfdData.TYPE_MP_INDEX_IFD, false); + ArrayList<MpEntry> mpEntries = new ArrayList<MpEntry>(getAuxiliaryImageCount() + 1); + mpEntries.add(new MpEntry()); // primary image + for (int i = 0; i < getAuxiliaryImageCount(); i++) { + mpEntries.add(new MpEntry()); // aux images + } + mpEntryTag.setValue(mpEntries); + mPrimaryMpoImage.addTag(mpEntryTag); + } + + public void updateAllTags() { + updateAttribIfdTags(); + updateIndexIfdTags(); + } + + private void updateIndexIfdTags() { + if (mPrimaryMpoImage == null) + throw new IllegalArgumentException("Primary Mpo Image has not been set"); + if (getAuxiliaryImageCount() == 0) + throw new IllegalArgumentException("No auxiliary images have been added"); + + MpoTag numImagesTag = mPrimaryMpoImage.getTag((short) MpoInterface.TAG_NUM_IMAGES, + MpoIfdData.TYPE_MP_INDEX_IFD); + if (numImagesTag == null) { + numImagesTag = new MpoTag((short) MpoInterface.TAG_NUM_IMAGES, + MpoTag.TYPE_UNSIGNED_LONG, 1, MpoIfdData.TYPE_MP_INDEX_IFD, false); + } + numImagesTag.setValue(getAuxiliaryImageCount() + 1); + mPrimaryMpoImage.addTag(numImagesTag); + + // check, create and add required tags + MpoTag mpEntryTag = new MpoTag((short) MpoInterface.TAG_MP_ENTRY, MpoTag.TYPE_UNDEFINED, + MpoTag.SIZE_UNDEFINED, MpoIfdData.TYPE_MP_INDEX_IFD, false); + ArrayList<MpEntry> mpEntries = new ArrayList<MpEntry>(getAuxiliaryImageCount() + 1); + + int imgOffset = 0; + // primary image + MpEntry entry = new MpEntry(1 << 29, mPrimaryMpoImage.calculateImageSize(), imgOffset); + mpEntries.add(entry); + imgOffset += mPrimaryMpoImage.calculateImageSize(); + + for (MpoImageData image : getAuxiliaryMpoImages()) { + int imageSize = image.calculateImageSize(); + entry = new MpEntry(0x020002, imageSize, imgOffset); + mpEntries.add(entry); // aux images + imgOffset += imageSize; + } + mpEntryTag.setValue(mpEntries); + mPrimaryMpoImage.addTag(mpEntryTag); + } + + private void updateAttribIfdTags() { + if (mPrimaryMpoImage == null) + throw new IllegalArgumentException("Primary Mpo Image has not been set"); + if (getAuxiliaryImageCount() == 0) + throw new IllegalArgumentException("No auxiliary images have been added"); + + int imageNum = 1; + MpoTag imageNumTag = null; + + imageNumTag = new MpoTag((short) MpoInterface.TAG_IMAGE_NUMBER, MpoTag.TYPE_UNSIGNED_LONG, + 1, MpoIfdData.TYPE_MP_ATTRIB_IFD, false); + imageNumTag.setValue(0xFFFFFFFFL); + mPrimaryMpoImage.addTag(imageNumTag); + + for (MpoImageData image : getAuxiliaryMpoImages()) { + imageNumTag = new MpoTag((short) MpoInterface.TAG_IMAGE_NUMBER, + MpoTag.TYPE_UNSIGNED_LONG, 1, MpoIfdData.TYPE_MP_ATTRIB_IFD, false); + imageNumTag.setValue(imageNum++); + image.addTag(imageNumTag); + } + } +}
\ No newline at end of file diff --git a/src/com/android/camera/mpo/MpoIfdData.java b/src/com/android/camera/mpo/MpoIfdData.java new file mode 100644 index 000000000..8e422944a --- /dev/null +++ b/src/com/android/camera/mpo/MpoIfdData.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Not a contribution. + * + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.mpo; + +import java.util.HashMap; +import java.util.Map; + +/** + * This class stores all the tags in an MP Index IFD. + */ +public class MpoIfdData { + public static final int TYPE_MP_INDEX_IFD = 1; + public static final int TYPE_MP_ATTRIB_IFD = 2; + public static final byte[] MP_FORMAT_VER_VALUE = { 0x30, 0x31, 0x30, 0x30 }; + + private final int mIfdId; + private final Map<Short, MpoTag> mTags = new HashMap<Short, MpoTag>(); + private int mOffsetToNextIfd = 0; + + /** + * Creates an empty MpIndexIfdData + */ + public MpoIfdData(int ifdId) { + mIfdId = ifdId; + } + + /** + * Get a array the contains all {@link MpoTag} in this IFD. + */ + protected MpoTag[] getAllTags() { + return mTags.values().toArray(new MpoTag[mTags.size()]); + } + + /** + * Gets the {@link MpoTag} with given tag id. Return null if there is no + * such tag. + */ + protected MpoTag getTag(short tagId) { + return mTags.get(tagId); + } + + /** + * Adds or replaces a {@link MpoTag}. + */ + protected MpoTag setTag(MpoTag tag) { + tag.setIfd(mIfdId); + return mTags.put(tag.getTagId(), tag); + } + + protected boolean checkCollision(short tagId) { + return mTags.get(tagId) != null; + } + + /** + * Removes the tag of the given ID + */ + protected void removeTag(short tagId) { + mTags.remove(tagId); + } + + /** + * Gets the tags count in the IFD. + */ + protected int getTagCount() { + return mTags.size(); + } + + /** + * Sets the offset of next IFD. + */ + protected void setOffsetToNextIfd(int offset) { + mOffsetToNextIfd = offset; + } + + /** + * Gets the offset of next IFD. + */ + protected int getOffsetToNextIfd() { + return mOffsetToNextIfd; + } + + /** + * Returns true if all tags in this two IFDs are equal. Note that tags of + * IFD offset will be ignored. + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (obj instanceof MpoIfdData) { + MpoIfdData data = (MpoIfdData) obj; + if (data.getTagCount() == getTagCount()) { + MpoTag[] tags = data.getAllTags(); + for (MpoTag tag : tags) { + MpoTag tag2 = mTags.get(tag.getTagId()); + if (!tag.equals(tag2)) { + return false; + } + } + return true; + } + } + return false; + } +} diff --git a/src/com/android/camera/mpo/MpoImageData.java b/src/com/android/camera/mpo/MpoImageData.java new file mode 100644 index 000000000..84d2d0f6e --- /dev/null +++ b/src/com/android/camera/mpo/MpoImageData.java @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Not a contribution. + * + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.mpo; + +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.List; + +/** + * This class stores the MPO header in IFDs according to the MPO specification. + */ + +public class MpoImageData { + private static final String TAG = "MpoImageData"; + static final int OFFSET_TO_FIRST_IFD = 8; + static final int MP_FORMAT_IDENTIFIER = 0x4D504600; // 'M' 'P' 'F' 'NULL' + static final int MP_HEADER_SIZE = 8; + static final int APP_HEADER_SIZE = 6; + + private final MpoIfdData mMpIndexIfdData = new MpoIfdData(MpoIfdData.TYPE_MP_INDEX_IFD); + private final MpoIfdData mMpAttribIfdData = new MpoIfdData(MpoIfdData.TYPE_MP_ATTRIB_IFD); + private final byte[] mJpegData; + private final ByteOrder mByteOrder; + + public MpoImageData(byte[] jpegData, ByteOrder byteOrder) { + mJpegData = jpegData; + mByteOrder = byteOrder; + } + + /** + * Gets the jpeg data. + */ + protected byte[] getJpegData() { + return mJpegData; + } + + /** + * Gets the byte order. + */ + protected ByteOrder getByteOrder() { + return mByteOrder; + } + + /** + * Returns the {@link mMpAttribIfdData} object if it exists or null. + */ + protected MpoIfdData getAttribIfdData() { + return mMpAttribIfdData; + } + + /** + * Returns the {@link mMpIndexIfdData} object if it exists or null. + */ + protected MpoIfdData getIndexIfdData() { + return mMpIndexIfdData; + } + + /** + * Returns the {@link MpoIfdData} object corresponding to a given IFD. + */ + protected MpoIfdData getMpIfdData(int ifdId) { + return (ifdId == MpoIfdData.TYPE_MP_INDEX_IFD) ? mMpIndexIfdData : mMpAttribIfdData; + } + + /** + * Returns the tag with a given tag ID in the given IFD if the tag exists. + * Otherwise returns null. + */ + protected MpoTag getTag(short tag, int ifd) { + MpoIfdData mpIfdData = getMpIfdData(ifd); + return mpIfdData.getTag(tag); + } + + /** + * Adds the given MpoTag to its default IFD and returns an existing MpoTag + * with the same TID or null if none exist. + */ + protected MpoTag addTag(MpoTag tag) { + if (tag != null) { + int ifd = tag.getIfd(); + return addTag(tag, ifd); + } + return null; + } + + /** + * Adds the given MpoTag to the given IFD and returns an existing MpoTag + * with the same tag ID or null if none exist. + */ + protected MpoTag addTag(MpoTag tag, int ifdId) { + if (tag != null && MpoTag.isValidIfd(ifdId)) { + return getMpIfdData(ifdId).setTag(tag); + } + return null; + } + + /** + * Removes the tag with a given tag ID and IFD. + */ + protected void removeTag(short tagId, int ifdId) { + getMpIfdData(ifdId).removeTag(tagId); + } + + /** + * Returns a list of all {@link MpoTag}s in the ExifData or null if there + * are none. + */ + protected List<MpoTag> getAllTags() { + ArrayList<MpoTag> ret = new ArrayList<MpoTag>(); + MpoTag[] tags = mMpIndexIfdData.getAllTags(); + if (tags != null) { + for (MpoTag t : tags) { + ret.add(t); + } + } + + tags = mMpAttribIfdData.getAllTags(); + if (tags != null) { + for (MpoTag t : tags) { + ret.add(t); + } + } + + if (ret.size() == 0) { + return null; + } + return ret; + } + + /** + * Returns a list of all {@link MpoTag}s in a given IFD or null if there are + * none. + */ + protected List<MpoTag> getAllTagsForIfd(int ifd) { + MpoTag[] tags = getMpIfdData(ifd).getAllTags(); + if (tags == null) { + return null; + } + ArrayList<MpoTag> ret = new ArrayList<MpoTag>(tags.length); + for (MpoTag t : tags) { + ret.add(t); + } + if (ret.size() == 0) { + return null; + } + return ret; + } + + /** + * Returns a list of all {@link MpoTag}s with a given TID or null if there + * are none. + */ + protected List<MpoTag> getAllTagsForTagId(short tag) { + ArrayList<MpoTag> ret = new ArrayList<MpoTag>(); + MpoTag t = mMpIndexIfdData.getTag(tag); + if (t != null) { + ret.add(t); + } + + t = mMpAttribIfdData.getTag(tag); + if (t != null) { + ret.add(t); + } + + if (ret.size() == 0) { + return null; + } + return ret; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (obj instanceof MpoImageData) { + MpoImageData data = (MpoImageData) obj; + if (data.mByteOrder != mByteOrder) { + return false; + } + + MpoIfdData indexIfd1 = data.getMpIfdData(MpoIfdData.TYPE_MP_INDEX_IFD); + MpoIfdData indexIfd2 = getMpIfdData(MpoIfdData.TYPE_MP_INDEX_IFD); + if (indexIfd1 != indexIfd2 && indexIfd1 != null && !indexIfd1.equals(indexIfd2)) { + return false; + } + + MpoIfdData attribIfd1 = data.getMpIfdData(MpoIfdData.TYPE_MP_ATTRIB_IFD); + MpoIfdData attribIfd2 = getMpIfdData(MpoIfdData.TYPE_MP_ATTRIB_IFD); + if (attribIfd1 != attribIfd2 && attribIfd1 != null && !attribIfd1.equals(attribIfd2)) { + return false; + } + return true; + } + return false; + } + + private int calculateOffsetOfIfd(MpoIfdData ifd, int offset) { + offset += 2 + ifd.getTagCount() * MpoTag.TAG_SIZE + 4; + MpoTag[] tags = ifd.getAllTags(); + for (MpoTag tag : tags) { + if (tag.getDataSize() > 4) { + tag.setOffset(offset); + offset += tag.getDataSize(); + } + } + return offset; + } + + public int calculateAllIfdOffsets() { + int offset = MP_HEADER_SIZE; + MpoIfdData indexIfd = getIndexIfdData(); + if (indexIfd.getTagCount() > 0) + offset = calculateOffsetOfIfd(indexIfd, offset); + + MpoIfdData attribIfd = getAttribIfdData(); + if (attribIfd.getTagCount() > 0) { + indexIfd.setOffsetToNextIfd(offset); + offset = calculateOffsetOfIfd(attribIfd, offset); + } + + return offset; + } + + public int calculateImageSize() { + return 2 + APP_HEADER_SIZE + calculateAllIfdOffsets() + mJpegData.length; + } +} diff --git a/src/com/android/camera/mpo/MpoInterface.java b/src/com/android/camera/mpo/MpoInterface.java new file mode 100644 index 000000000..3e2d9a4e2 --- /dev/null +++ b/src/com/android/camera/mpo/MpoInterface.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Not a contribution. + * + * 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.camera.mpo; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import android.util.Log; + +import com.android.camera.exif.ExifInterface; +import com.android.camera.util.CameraUtil; + +public class MpoInterface { + private static final String TAG = "MpoInterface"; + private static final String NULL_ARGUMENT_STRING = "Argument is null"; + + // Index IFD + public static final int TAG_MP_FORMAT_VERSION = ExifInterface.defineTag( + MpoIfdData.TYPE_MP_INDEX_IFD + MpoIfdData.TYPE_MP_ATTRIB_IFD, (short) 0xB000); + public static final int TAG_NUM_IMAGES = ExifInterface.defineTag(MpoIfdData.TYPE_MP_INDEX_IFD, + (short) 0xB001); + public static final int TAG_MP_ENTRY = ExifInterface.defineTag(MpoIfdData.TYPE_MP_INDEX_IFD, + (short) 0xB002); + public static final int TAG_IMAGE_UNIQUE_ID_LIST = ExifInterface.defineTag( + MpoIfdData.TYPE_MP_INDEX_IFD, (short) 0xB003); + public static final int TAG_NUM_CAPTURED_FRAMES = ExifInterface.defineTag( + MpoIfdData.TYPE_MP_INDEX_IFD, (short) 0xB004); + + // Attrib IFD + public static final int TAG_IMAGE_NUMBER = ExifInterface.defineTag( + MpoIfdData.TYPE_MP_ATTRIB_IFD, (short) 0xB101); + public static final int TAG_PAN_ORIENTATION = ExifInterface.defineTag( + MpoIfdData.TYPE_MP_ATTRIB_IFD, (short) 0xB201); + public static final int TAG_PAN_OVERLAP_H = ExifInterface.defineTag( + MpoIfdData.TYPE_MP_ATTRIB_IFD, (short) 0xB202); + public static final int TAG_PAN_OVERLAP_V = ExifInterface.defineTag( + MpoIfdData.TYPE_MP_ATTRIB_IFD, (short) 0xB203); + public static final int TAG_BASE_VIEWPOINT_NUM = ExifInterface.defineTag( + MpoIfdData.TYPE_MP_ATTRIB_IFD, (short) 0xB204); + public static final int TAG_CONVERGE_ANGLE = ExifInterface.defineTag( + MpoIfdData.TYPE_MP_ATTRIB_IFD, (short) 0xB205); + public static final int TAG_BASELINE_LEN = ExifInterface.defineTag( + MpoIfdData.TYPE_MP_ATTRIB_IFD, (short) 0xB206); + public static final int TAG_DIVERGE_ANGLE = ExifInterface.defineTag( + MpoIfdData.TYPE_MP_ATTRIB_IFD, (short) 0xB207); + public static final int TAG_AXIS_DISTANCE_X = ExifInterface.defineTag( + MpoIfdData.TYPE_MP_ATTRIB_IFD, (short) 0xB208); + public static final int TAG_AXIS_DISTANCE_Y = ExifInterface.defineTag( + MpoIfdData.TYPE_MP_ATTRIB_IFD, (short) 0xB209); + public static final int TAG_AXIS_DISTANCE_Z = ExifInterface.defineTag( + MpoIfdData.TYPE_MP_ATTRIB_IFD, (short) 0xB20A); + public static final int TAG_YAW_ANGLE = ExifInterface.defineTag(MpoIfdData.TYPE_MP_ATTRIB_IFD, + (short) 0xB20B); + public static final int TAG_PITCH_ANGLE = ExifInterface.defineTag( + MpoIfdData.TYPE_MP_ATTRIB_IFD, (short) 0xB20C); + public static final int TAG_ROLL_ANGLE = ExifInterface.defineTag(MpoIfdData.TYPE_MP_ATTRIB_IFD, + (short) 0xB20D); + + public static int writeMpo(MpoData mpo, OutputStream out) { + if (mpo == null || out == null) + throw new IllegalArgumentException(NULL_ARGUMENT_STRING); + + MpoOutputStream s = getMpoWriterStream(out); + s.setMpoData(mpo); + + // check and write mpo file + try { + s.writeMpoFile(); + } catch (IOException e) { + CameraUtil.closeSilently(s); + Log.w(TAG, "IO Exception when writing mpo image"); + return -1; + } + + // close stream + CameraUtil.closeSilently(s); + return s.size(); + } + + public static int writeMpo(MpoData mpo, String outFilename) { + if (mpo == null || outFilename == null) + throw new IllegalArgumentException(NULL_ARGUMENT_STRING); + + return writeMpo(mpo, getFileWriterStream(outFilename)); + } + + /** + * Wraps an OutputStream object with an MpoOutputStream. + * + * @param outStream + * an OutputStream to wrap. + * @return an MpoOutputStream that wraps the outStream parameter, and adds + * mpo metadata. A jpeg image should be written to this stream. + */ + private static MpoOutputStream getMpoWriterStream(OutputStream outStream) { + if (outStream == null) { + throw new IllegalArgumentException(NULL_ARGUMENT_STRING); + } + MpoOutputStream mos = new MpoOutputStream(outStream); + return mos; + } + + /** + * Returns an FileOutputStream object that writes to a file. + * + * @param outFileName + * an String containing a filepath for a file. + * @return an FileOutputStream that writes to the outFileName file. + * @throws FileNotFoundException + */ + private static OutputStream getFileWriterStream(String outFileName) { + if (outFileName == null) { + throw new IllegalArgumentException(NULL_ARGUMENT_STRING); + } + OutputStream out = null; + try { + out = new FileOutputStream(outFileName); + } catch (FileNotFoundException e) { + CameraUtil.closeSilently(out); + Log.w(TAG, "File not found"); + } + return out; + } +} diff --git a/src/com/android/camera/mpo/MpoOutputStream.java b/src/com/android/camera/mpo/MpoOutputStream.java new file mode 100644 index 000000000..6e8e72fd9 --- /dev/null +++ b/src/com/android/camera/mpo/MpoOutputStream.java @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Not a contribution/ + * + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.mpo; + +import java.io.BufferedOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.List; + +import android.util.Log; + +import com.android.camera.exif.JpegHeader; +import com.android.camera.exif.OrderedDataOutputStream; +import com.android.camera.mpo.MpoTag.MpEntry; + +class MpoOutputStream extends FilterOutputStream { + private static final String TAG = "MpoOutputStream"; + private static final boolean DEBUG = true; + private static final int STREAMBUFFER_SIZE = 0x00010000; // 64Kb + + private static final int STATE_SOI = 0; + private static final int STATE_FRAME_HEADER = 1; + private static final int STATE_JPEG_DATA = 3; + + private static final short TIFF_HEADER = 0x002A; + private static final short TIFF_BIG_ENDIAN = 0x4d4d; + private static final short TIFF_LITTLE_ENDIAN = 0x4949; + private static final int MAX_EXIF_SIZE = 65535; + + private MpoData mMpoData; + private MpoImageData mCurrentImageData; + private int mState = STATE_SOI; + private int mByteToSkip; + private int mByteToCopy; + private byte[] mSingleByteArray = new byte[1]; + private ByteBuffer mBuffer = ByteBuffer.allocate(4); + private int mMpoOffsetStart = -1; + private int mSize = 0; + + protected MpoOutputStream(OutputStream ou) { + super(new BufferedOutputStream(ou, STREAMBUFFER_SIZE)); + } + + /** + * Sets the ExifData to be written into the JPEG file. Should be called + * before writing image data. + */ + protected void setMpoData(MpoData mpoData) { + mMpoData = mpoData; + mMpoData.updateAllTags(); + } + + private void resetStates() { + mState = STATE_SOI; + mByteToSkip = 0; + mByteToCopy = 0; + mBuffer.rewind(); + } + + private int requestByteToBuffer(int requestByteCount, byte[] buffer, int offset, int length) { + int byteNeeded = requestByteCount - mBuffer.position(); + int byteToRead = length > byteNeeded ? byteNeeded : length; + mBuffer.put(buffer, offset, byteToRead); + return byteToRead; + } + + void writeMpoFile() throws IOException { + // check and write primary image + mCurrentImageData = mMpoData.getPrimaryMpoImage(); + write(mCurrentImageData.getJpegData()); + flush(); + + // check and write auxiliary images + for (MpoImageData image : mMpoData.getAuxiliaryMpoImages()) { + resetStates(); + mCurrentImageData = image; + write(mCurrentImageData.getJpegData()); + flush(); + } + } + + /** + * Writes the image out. The input data should be a valid JPEG format. After + * writing, it's Exif header will be replaced by the given header. + */ + @Override + public void write(byte[] buffer, int offset, int length) throws IOException { + while ((mByteToSkip > 0 || mByteToCopy > 0 || mState != STATE_JPEG_DATA) && length > 0) { + if (mByteToSkip > 0) { + int byteToProcess = length > mByteToSkip ? mByteToSkip : length; + length -= byteToProcess; + mByteToSkip -= byteToProcess; + offset += byteToProcess; + } + if (mByteToCopy > 0) { + int byteToProcess = length > mByteToCopy ? mByteToCopy : length; + out.write(buffer, offset, byteToProcess); + mSize += byteToProcess; + length -= byteToProcess; + mByteToCopy -= byteToProcess; + offset += byteToProcess; + } + if (length == 0) { + return; + } + switch (mState) { + case STATE_SOI: + int byteRead = requestByteToBuffer(2, buffer, offset, length); + offset += byteRead; + length -= byteRead; + if (mBuffer.position() < 2) { + return; + } + mBuffer.rewind(); + if (mBuffer.getShort() != JpegHeader.SOI) { + throw new IOException("Not a valid jpeg image, cannot write exif"); + } + out.write(mBuffer.array(), 0, 2); + mSize += 2; + mState = STATE_FRAME_HEADER; + mBuffer.rewind(); + break; + case STATE_FRAME_HEADER: + // Copy APP1 if it exists + // Insert MPO data + // Copy remainder of image + byteRead = requestByteToBuffer(4, buffer, offset, length); + offset += byteRead; + length -= byteRead; + // Check if this image data doesn't contain SOF. + if (mBuffer.position() == 2) { + short tag = mBuffer.getShort(); + if (tag == JpegHeader.EOI) { + out.write(mBuffer.array(), 0, 2); + mSize += 2; + mBuffer.rewind(); + } + } + if (mBuffer.position() < 4) { + return; + } + mBuffer.rewind(); + short marker = mBuffer.getShort(); + if (marker == JpegHeader.APP1 || marker == JpegHeader.APP0) { + out.write(mBuffer.array(), 0, 4); + mSize += 4; + mByteToCopy = (mBuffer.getShort() & 0x0000ffff) - 2; + } else { + writeMpoData(); + out.write(mBuffer.array(), 0, 4); + mSize += 4; + mState = STATE_JPEG_DATA; + } + mBuffer.rewind(); + break; + } + } + if (length > 0) { + out.write(buffer, offset, length); + mSize += length; + } + } + + /** + * Writes the one bytes out. The input data should be a valid JPEG format. + * After writing, it's Exif header will be replaced by the given header. + */ + @Override + public void write(int oneByte) throws IOException { + mSingleByteArray[0] = (byte) (0xff & oneByte); + write(mSingleByteArray); + } + + /** + * Equivalent to calling write(buffer, 0, buffer.length). + */ + @Override + public void write(byte[] buffer) throws IOException { + write(buffer, 0, buffer.length); + } + + private void writeMpoData() throws IOException { + if (mMpoData == null) { + return; + } + if (DEBUG) { + Log.v(TAG, "Writing mpo data..."); + } + int exifSize = mCurrentImageData.calculateAllIfdOffsets() + MpoImageData.APP_HEADER_SIZE; + if (exifSize > MAX_EXIF_SIZE) { + throw new IOException("Exif header is too large (>64Kb)"); + } + OrderedDataOutputStream dataOutputStream = new OrderedDataOutputStream(out); + dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN); + dataOutputStream.writeShort(JpegHeader.APP2); + dataOutputStream.writeShort((short) (exifSize)); + dataOutputStream.writeInt(MpoImageData.MP_FORMAT_IDENTIFIER); + if (mMpoOffsetStart == -1) { + mMpoOffsetStart = mSize + dataOutputStream.size(); + } + if (mCurrentImageData.getByteOrder() == ByteOrder.BIG_ENDIAN) { + dataOutputStream.writeShort(TIFF_BIG_ENDIAN); + } else { + dataOutputStream.writeShort(TIFF_LITTLE_ENDIAN); + } + dataOutputStream.setByteOrder(mCurrentImageData.getByteOrder()); + dataOutputStream.writeShort(TIFF_HEADER); + if (exifSize > MpoImageData.MP_HEADER_SIZE + MpoImageData.APP_HEADER_SIZE) { + dataOutputStream.writeInt(MpoImageData.OFFSET_TO_FIRST_IFD); + writeAllTags(dataOutputStream); + } else + dataOutputStream.writeInt(0); + + mSize += dataOutputStream.size(); + } + + private void updateIndexIfdOffsets(MpoIfdData indexIfd, int mpoOffset) { + // update offsets + MpoTag mpEntryTag = mMpoData.getPrimaryMpoImage().getTag((short) MpoInterface.TAG_MP_ENTRY, + MpoIfdData.TYPE_MP_INDEX_IFD); + List<MpEntry> mpEntries = mpEntryTag.getMpEntryValue(); + for (int i = 1; i < mpEntries.size(); i++) { // primary offset is always + // 0 + MpEntry entry = mpEntries.get(i); + entry.setImageOffset(entry.getImageOffset() - mpoOffset); + } + + mpEntryTag.setValue(mpEntries); + } + + private void writeAllTags(OrderedDataOutputStream dataOutputStream) throws IOException { + MpoIfdData indexIfd = mCurrentImageData.getIndexIfdData(); + if (indexIfd.getTagCount() > 0) { + updateIndexIfdOffsets(indexIfd, mMpoOffsetStart); + writeIfd(indexIfd, dataOutputStream); + } + + MpoIfdData attribIfd = mCurrentImageData.getAttribIfdData(); + if (attribIfd.getTagCount() > 0) + writeIfd(attribIfd, dataOutputStream); + } + + private void writeIfd(MpoIfdData ifd, OrderedDataOutputStream dataOutputStream) + throws IOException { + MpoTag[] tags = ifd.getAllTags(); + dataOutputStream.writeShort((short) tags.length); + for (MpoTag tag : tags) { + dataOutputStream.writeShort(tag.getTagId()); + dataOutputStream.writeShort(tag.getDataType()); + dataOutputStream.writeInt(tag.getComponentCount()); + if (DEBUG) { + Log.v(TAG, "\n" + tag.toString()); + } + if (tag.getDataSize() > 4) { + dataOutputStream.writeInt(tag.getOffset()); + } else { + MpoOutputStream.writeTagValue(tag, dataOutputStream); + for (int i = 0, n = 4 - tag.getDataSize(); i < n; i++) { + dataOutputStream.write(0); + } + } + } + dataOutputStream.writeInt(ifd.getOffsetToNextIfd()); + for (MpoTag tag : tags) { + if (tag.getDataSize() > 4) { + MpoOutputStream.writeTagValue(tag, dataOutputStream); + } + } + } + + static void writeTagValue(MpoTag tag, OrderedDataOutputStream dataOutputStream) + throws IOException { + switch (tag.getDataType()) { + case MpoTag.TYPE_ASCII: + byte buf[] = tag.getStringByte(); + if (buf.length == tag.getComponentCount()) { + buf[buf.length - 1] = 0; + dataOutputStream.write(buf); + } else { + dataOutputStream.write(buf); + dataOutputStream.write(0); + } + break; + case MpoTag.TYPE_LONG: + case MpoTag.TYPE_UNSIGNED_LONG: + for (int i = 0, n = tag.getComponentCount(); i < n; i++) { + dataOutputStream.writeInt((int) tag.getValueAt(i)); + } + break; + case MpoTag.TYPE_RATIONAL: + case MpoTag.TYPE_UNSIGNED_RATIONAL: + for (int i = 0, n = tag.getComponentCount(); i < n; i++) { + dataOutputStream.writeRational(tag.getRational(i)); + } + break; + case MpoTag.TYPE_UNDEFINED: + case MpoTag.TYPE_UNSIGNED_BYTE: + buf = new byte[tag.getComponentCount()]; + tag.getBytes(buf); + dataOutputStream.write(buf); + break; + case MpoTag.TYPE_UNSIGNED_SHORT: + for (int i = 0, n = tag.getComponentCount(); i < n; i++) { + dataOutputStream.writeShort((short) tag.getValueAt(i)); + } + break; + } + } + + int size() { + return mSize; + } +} diff --git a/src/com/android/camera/mpo/MpoTag.java b/src/com/android/camera/mpo/MpoTag.java new file mode 100644 index 000000000..bc0e6ce30 --- /dev/null +++ b/src/com/android/camera/mpo/MpoTag.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.camera.mpo; + +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +import android.util.Log; + +import com.android.camera.exif.ExifTag; + +public class MpoTag extends ExifTag { + private static final String TAG = "MpoTag"; + static final int TAG_SIZE = 12; + + MpoTag(short tagId, short type, int componentCount, int ifd, boolean hasDefinedComponentCount) { + super(tagId, type, componentCount, ifd, hasDefinedComponentCount); + } + + public boolean setValue(List<MpEntry> entries) { + if (getTagId() != (short) MpoInterface.TAG_MP_ENTRY) { + return false; + } + + byte[] bytes = new byte[entries.size() * MpEntry.SIZE]; + for (int i = 0; i < entries.size(); i++) { + MpEntry entry = entries.get(i); + entry.getBytes(ByteBuffer.wrap(bytes, i * MpEntry.SIZE, MpEntry.SIZE)); + } + return setValue(bytes); + } + + public List<MpEntry> getMpEntryValue() { + if (getTagId() != (short) MpoInterface.TAG_MP_ENTRY) { + return null; + } + + byte[] bytes = getValueAsBytes(); + List<MpEntry> entries = new ArrayList<MpEntry>(bytes.length / MpEntry.SIZE); + for (int i = 0; i < bytes.length; i += MpEntry.SIZE) { + entries.add(new MpEntry(ByteBuffer.wrap(bytes, i, MpEntry.SIZE))); + } + return entries; + } + + static class MpEntry { + static final int SIZE = 16; + private int mImageAttrib; + private int mImageSize; + private int mImageOffset; + private short mDependantImage1; + private short mDependantImage2; + + public MpEntry() { + this(0, 0, 0, (short) 0, (short) 0); + } + + public MpEntry(int imageAttrib, int imageSize, int imageOffset) { + this(imageAttrib, imageSize, imageOffset, (short) 0, (short) 0); + } + + public MpEntry(int imageAttrib, int imageSize, int imageOffset, short dependantImage1, + short dependantImage2) { + mImageAttrib = imageAttrib; + mImageSize = imageSize; + mImageOffset = imageOffset; + mDependantImage1 = dependantImage1; + mDependantImage2 = dependantImage2; + } + + public MpEntry(ByteBuffer buffer) { + mImageAttrib = buffer.getInt(); + mImageSize = buffer.getInt(); + mImageOffset = buffer.getInt(); + mDependantImage1 = buffer.getShort(); + mDependantImage2 = buffer.getShort(); + } + + public int getImageAttrib() { + return mImageAttrib; + } + + public int getImageSize() { + return mImageSize; + } + + public int getImageOffset() { + return mImageOffset; + } + + public short getDependantImage1() { + return mDependantImage1; + } + + public short getDependantImage2() { + return mDependantImage2; + } + + public void setImageAttrib(int imageAttrib) { + mImageAttrib = imageAttrib; + } + + public void setImageSize(int imageSize) { + mImageSize = imageSize; + } + + public void setImageOffset(int imageOffset) { + mImageOffset = imageOffset; + } + + public void setDependantImage1(short depImage1) { + mDependantImage1 = depImage1; + } + + public void setDependantImage2(short depImage2) { + mDependantImage2 = depImage2; + } + + public boolean getBytes(ByteBuffer buffer) { + try { + buffer.putInt(mImageAttrib); + buffer.putInt(mImageSize); + buffer.putInt(mImageOffset); + buffer.putShort(mDependantImage1); + buffer.putShort(mDependantImage2); + } catch (BufferOverflowException e) { + Log.w(TAG, "Buffer size too small"); + return false; + } + + return true; + } + } +}
\ No newline at end of file |