| /* |
| * Copyright (C) 2019 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.bluetooth.avrcpcontroller; |
| |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * The pixel size or range of pixel sizes in which the image is available |
| * |
| * A FIXED size is represented as the following, where W is width and H is height. The domain |
| * of values is [0, 65535] |
| * |
| * W*H |
| * |
| * A RESIZABLE size that allows a modified aspect ratio is represented as the following, where |
| * W_1*H_1 is the minimum width and height pair and W2*H2 is the maximum width and height pair. |
| * The domain of values is [0, 65535] |
| * |
| * W_1*H_1-W2*H2 |
| * |
| * A RESIZABLE size that allows a fixed aspect ratio is represented as the following, where |
| * W_1 is the minimum width and W2*H2 is the maximum width and height pair. |
| * The domain of values is [0, 65535] |
| * |
| * W_1**-W2*H2 |
| * |
| * For each possible intermediate width value, the corresponding height is calculated using the |
| * formula |
| * |
| * H=(W*H2)/W2 |
| */ |
| public class BipPixel { |
| private static final String TAG = "avrcpcontroller.BipPixel"; |
| |
| // The BIP specification declares this as the max size to be transferred. You can optionally |
| // use this value to indicate there is no upper bound on pixel size. |
| public static final int PIXEL_MAX = 65535; |
| |
| // Note that the integer values also map to the number of '*' delimiters that exist in each |
| // formatted string |
| public static final int TYPE_UNKNOWN = 0; |
| public static final int TYPE_FIXED = 1; |
| public static final int TYPE_RESIZE_MODIFIED_ASPECT_RATIO = 2; |
| public static final int TYPE_RESIZE_FIXED_ASPECT_RATIO = 3; |
| |
| private final int mType; |
| private final int mMinWidth; |
| private final int mMinHeight; |
| private final int mMaxWidth; |
| private final int mMaxHeight; |
| |
| /** |
| * Create a fixed size BipPixel object |
| */ |
| public static BipPixel createFixed(int width, int height) { |
| return new BipPixel(TYPE_FIXED, width, height, width, height); |
| } |
| |
| /** |
| * Create a resizable modifiable aspect ratio BipPixel object |
| */ |
| public static BipPixel createResizableModified(int minWidth, int minHeight, int maxWidth, |
| int maxHeight) { |
| return new BipPixel(TYPE_RESIZE_MODIFIED_ASPECT_RATIO, minWidth, minHeight, maxWidth, |
| maxHeight); |
| } |
| |
| /** |
| * Create a resizable fixed aspect ratio BipPixel object |
| */ |
| public static BipPixel createResizableFixed(int minWidth, int maxWidth, int maxHeight) { |
| int minHeight = (minWidth * maxHeight) / maxWidth; |
| return new BipPixel(TYPE_RESIZE_FIXED_ASPECT_RATIO, minWidth, minHeight, |
| maxWidth, maxHeight); |
| } |
| |
| /** |
| * Directly create a BipPixel object knowing your exact type and dimensions. Internal use only |
| */ |
| private BipPixel(int type, int minWidth, int minHeight, int maxWidth, int maxHeight) { |
| if (isDimensionInvalid(minWidth) || isDimensionInvalid(maxWidth) |
| || isDimensionInvalid(minHeight) || isDimensionInvalid(maxHeight)) { |
| throw new IllegalArgumentException("Dimension's must be in [0, " + PIXEL_MAX + "]"); |
| } |
| |
| mType = type; |
| mMinWidth = minWidth; |
| mMinHeight = minHeight; |
| mMaxWidth = maxWidth; |
| mMaxHeight = maxHeight; |
| } |
| |
| /** |
| * Create a BipPixel object from an Image Format pixel attribute string |
| */ |
| public BipPixel(String pixel) { |
| int type = TYPE_UNKNOWN; |
| int minWidth = -1; |
| int minHeight = -1; |
| int maxWidth = -1; |
| int maxHeight = -1; |
| |
| int typeHint = determinePixelType(pixel); |
| switch (typeHint) { |
| case TYPE_FIXED: |
| Pattern fixed = Pattern.compile("^(\\d{1,5})\\*(\\d{1,5})$"); |
| Matcher m1 = fixed.matcher(pixel); |
| if (m1.matches()) { |
| type = TYPE_FIXED; |
| minWidth = Integer.parseInt(m1.group(1)); |
| maxWidth = Integer.parseInt(m1.group(1)); |
| minHeight = Integer.parseInt(m1.group(2)); |
| maxHeight = Integer.parseInt(m1.group(2)); |
| } |
| break; |
| case TYPE_RESIZE_MODIFIED_ASPECT_RATIO: |
| Pattern modifiedRatio = Pattern.compile( |
| "^(\\d{1,5})\\*(\\d{1,5})-(\\d{1,5})\\*(\\d{1,5})$"); |
| Matcher m2 = modifiedRatio.matcher(pixel); |
| if (m2.matches()) { |
| type = TYPE_RESIZE_MODIFIED_ASPECT_RATIO; |
| minWidth = Integer.parseInt(m2.group(1)); |
| minHeight = Integer.parseInt(m2.group(2)); |
| maxWidth = Integer.parseInt(m2.group(3)); |
| maxHeight = Integer.parseInt(m2.group(4)); |
| } |
| break; |
| case TYPE_RESIZE_FIXED_ASPECT_RATIO: |
| Pattern fixedRatio = Pattern.compile("^(\\d{1,5})\\*\\*-(\\d{1,5})\\*(\\d{1,5})$"); |
| Matcher m3 = fixedRatio.matcher(pixel); |
| if (m3.matches()) { |
| type = TYPE_RESIZE_FIXED_ASPECT_RATIO; |
| minWidth = Integer.parseInt(m3.group(1)); |
| maxWidth = Integer.parseInt(m3.group(2)); |
| maxHeight = Integer.parseInt(m3.group(3)); |
| minHeight = (minWidth * maxHeight) / maxWidth; |
| } |
| break; |
| default: |
| break; |
| } |
| if (type == TYPE_UNKNOWN) { |
| throw new ParseException("Failed to determine type of '" + pixel + "'"); |
| } |
| if (isDimensionInvalid(minWidth) || isDimensionInvalid(maxWidth) |
| || isDimensionInvalid(minHeight) || isDimensionInvalid(maxHeight)) { |
| throw new ParseException("Parsed dimensions must be in [0, " + PIXEL_MAX + "]"); |
| } |
| |
| mType = type; |
| mMinWidth = minWidth; |
| mMinHeight = minHeight; |
| mMaxWidth = maxWidth; |
| mMaxHeight = maxHeight; |
| } |
| |
| public int getType() { |
| return mType; |
| } |
| |
| public int getMinWidth() { |
| return mMinWidth; |
| } |
| |
| public int getMaxWidth() { |
| return mMaxWidth; |
| } |
| |
| public int getMinHeight() { |
| return mMinHeight; |
| } |
| |
| public int getMaxHeight() { |
| return mMaxHeight; |
| } |
| |
| /** |
| * Determines the type of the pixel string by counting the number of '*' delimiters in the |
| * string. |
| * |
| * Note that the overall maximum size of any pixel string is 23 characters in length due to the |
| * max size of each dimension |
| * |
| * @return The corresponding type we should assume the given pixel string is |
| */ |
| private static int determinePixelType(String pixel) { |
| if (pixel == null || pixel.length() > 23) return TYPE_UNKNOWN; |
| int delimCount = 0; |
| for (char c : pixel.toCharArray()) { |
| if (c == '*') delimCount++; |
| } |
| return delimCount > 0 && delimCount <= 3 ? delimCount : TYPE_UNKNOWN; |
| } |
| |
| protected static boolean isDimensionInvalid(int dimension) { |
| return dimension < 0 || dimension > PIXEL_MAX; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (o == this) return true; |
| if (!(o instanceof BipPixel)) return false; |
| |
| BipPixel p = (BipPixel) o; |
| return p.getType() == getType() |
| && p.getMinWidth() == getMinWidth() |
| && p.getMaxWidth() == getMaxWidth() |
| && p.getMinHeight() == getMinHeight() |
| && p.getMaxHeight() == getMaxHeight(); |
| } |
| |
| @Override |
| public String toString() { |
| String s = null; |
| switch (mType) { |
| case TYPE_FIXED: |
| s = mMaxWidth + "*" + mMaxHeight; |
| break; |
| case TYPE_RESIZE_MODIFIED_ASPECT_RATIO: |
| s = mMinWidth + "*" + mMinHeight + "-" + mMaxWidth + "*" + mMaxHeight; |
| break; |
| case TYPE_RESIZE_FIXED_ASPECT_RATIO: |
| s = mMinWidth + "**-" + mMaxWidth + "*" + mMaxHeight; |
| break; |
| } |
| return s; |
| } |
| } |