1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.bluetooth.avrcpcontroller; 18 19 import java.util.regex.Matcher; 20 import java.util.regex.Pattern; 21 22 /** 23 * The pixel size or range of pixel sizes in which the image is available 24 * 25 * <p>A FIXED size is represented as the following, where W is width and H is height. The domain of 26 * values is [0, 65535] 27 * 28 * <p>W*H 29 * 30 * <p>A RESIZABLE size that allows a modified aspect ratio is represented as the following, where 31 * W_1*H_1 is the minimum width and height pair and W2*H2 is the maximum width and height pair. The 32 * domain of values is [0, 65535] 33 * 34 * <p>W_1*H_1-W2*H2 35 * 36 * <p>A RESIZABLE size that allows a fixed aspect ratio is represented as the following, where W_1 37 * is the minimum width and W2*H2 is the maximum width and height pair. The domain of values is [0, 38 * 65535] 39 * 40 * <p>W_1**-W2*H2 41 * 42 * <p>For each possible intermediate width value, the corresponding height is calculated using the 43 * formula 44 * 45 * <p>H=(W*H2)/W2 46 */ 47 public class BipPixel { 48 private static final String TAG = "avrcpcontroller.BipPixel"; 49 50 // The BIP specification declares this as the max size to be transferred. You can optionally 51 // use this value to indicate there is no upper bound on pixel size. 52 public static final int PIXEL_MAX = 65535; 53 54 // Note that the integer values also map to the number of '*' delimiters that exist in each 55 // formatted string 56 public static final int TYPE_UNKNOWN = 0; 57 public static final int TYPE_FIXED = 1; 58 public static final int TYPE_RESIZE_MODIFIED_ASPECT_RATIO = 2; 59 public static final int TYPE_RESIZE_FIXED_ASPECT_RATIO = 3; 60 61 private final int mType; 62 private final int mMinWidth; 63 private final int mMinHeight; 64 private final int mMaxWidth; 65 private final int mMaxHeight; 66 67 /** Create a fixed size BipPixel object */ createFixed(int width, int height)68 public static BipPixel createFixed(int width, int height) { 69 return new BipPixel(TYPE_FIXED, width, height, width, height); 70 } 71 72 /** Create a resizable modifiable aspect ratio BipPixel object */ createResizableModified( int minWidth, int minHeight, int maxWidth, int maxHeight)73 public static BipPixel createResizableModified( 74 int minWidth, int minHeight, int maxWidth, int maxHeight) { 75 return new BipPixel( 76 TYPE_RESIZE_MODIFIED_ASPECT_RATIO, minWidth, minHeight, maxWidth, maxHeight); 77 } 78 79 /** Create a resizable fixed aspect ratio BipPixel object */ createResizableFixed(int minWidth, int maxWidth, int maxHeight)80 public static BipPixel createResizableFixed(int minWidth, int maxWidth, int maxHeight) { 81 int minHeight = (minWidth * maxHeight) / maxWidth; 82 return new BipPixel( 83 TYPE_RESIZE_FIXED_ASPECT_RATIO, minWidth, minHeight, maxWidth, maxHeight); 84 } 85 86 /** 87 * Directly create a BipPixel object knowing your exact type and dimensions. Internal use only 88 */ BipPixel(int type, int minWidth, int minHeight, int maxWidth, int maxHeight)89 private BipPixel(int type, int minWidth, int minHeight, int maxWidth, int maxHeight) { 90 if (isDimensionInvalid(minWidth) 91 || isDimensionInvalid(maxWidth) 92 || isDimensionInvalid(minHeight) 93 || isDimensionInvalid(maxHeight)) { 94 throw new IllegalArgumentException("Dimension's must be in [0, " + PIXEL_MAX + "]"); 95 } 96 97 mType = type; 98 mMinWidth = minWidth; 99 mMinHeight = minHeight; 100 mMaxWidth = maxWidth; 101 mMaxHeight = maxHeight; 102 } 103 104 /** Create a BipPixel object from an Image Format pixel attribute string */ BipPixel(String pixel)105 public BipPixel(String pixel) { 106 int type = TYPE_UNKNOWN; 107 int minWidth = -1; 108 int minHeight = -1; 109 int maxWidth = -1; 110 int maxHeight = -1; 111 112 int typeHint = determinePixelType(pixel); 113 switch (typeHint) { 114 case TYPE_FIXED: 115 Pattern fixed = Pattern.compile("^(\\d{1,5})\\*(\\d{1,5})$"); 116 Matcher m1 = fixed.matcher(pixel); 117 if (m1.matches()) { 118 type = TYPE_FIXED; 119 minWidth = Integer.parseInt(m1.group(1)); 120 maxWidth = Integer.parseInt(m1.group(1)); 121 minHeight = Integer.parseInt(m1.group(2)); 122 maxHeight = Integer.parseInt(m1.group(2)); 123 } 124 break; 125 case TYPE_RESIZE_MODIFIED_ASPECT_RATIO: 126 Pattern modifiedRatio = 127 Pattern.compile("^(\\d{1,5})\\*(\\d{1,5})-(\\d{1,5})\\*(\\d{1,5})$"); 128 Matcher m2 = modifiedRatio.matcher(pixel); 129 if (m2.matches()) { 130 type = TYPE_RESIZE_MODIFIED_ASPECT_RATIO; 131 minWidth = Integer.parseInt(m2.group(1)); 132 minHeight = Integer.parseInt(m2.group(2)); 133 maxWidth = Integer.parseInt(m2.group(3)); 134 maxHeight = Integer.parseInt(m2.group(4)); 135 } 136 break; 137 case TYPE_RESIZE_FIXED_ASPECT_RATIO: 138 Pattern fixedRatio = Pattern.compile("^(\\d{1,5})\\*\\*-(\\d{1,5})\\*(\\d{1,5})$"); 139 Matcher m3 = fixedRatio.matcher(pixel); 140 if (m3.matches()) { 141 type = TYPE_RESIZE_FIXED_ASPECT_RATIO; 142 minWidth = Integer.parseInt(m3.group(1)); 143 maxWidth = Integer.parseInt(m3.group(2)); 144 maxHeight = Integer.parseInt(m3.group(3)); 145 minHeight = (minWidth * maxHeight) / maxWidth; 146 } 147 break; 148 default: 149 break; 150 } 151 if (type == TYPE_UNKNOWN) { 152 throw new ParseException("Failed to determine type of '" + pixel + "'"); 153 } 154 if (isDimensionInvalid(minWidth) 155 || isDimensionInvalid(maxWidth) 156 || isDimensionInvalid(minHeight) 157 || isDimensionInvalid(maxHeight)) { 158 throw new ParseException("Parsed dimensions must be in [0, " + PIXEL_MAX + "]"); 159 } 160 161 mType = type; 162 mMinWidth = minWidth; 163 mMinHeight = minHeight; 164 mMaxWidth = maxWidth; 165 mMaxHeight = maxHeight; 166 } 167 getType()168 public int getType() { 169 return mType; 170 } 171 getMinWidth()172 public int getMinWidth() { 173 return mMinWidth; 174 } 175 getMaxWidth()176 public int getMaxWidth() { 177 return mMaxWidth; 178 } 179 getMinHeight()180 public int getMinHeight() { 181 return mMinHeight; 182 } 183 getMaxHeight()184 public int getMaxHeight() { 185 return mMaxHeight; 186 } 187 188 /** 189 * Determines the type of the pixel string by counting the number of '*' delimiters in the 190 * string. 191 * 192 * <p>Note that the overall maximum size of any pixel string is 23 characters in length due to 193 * the max size of each dimension 194 * 195 * @return The corresponding type we should assume the given pixel string is 196 */ determinePixelType(String pixel)197 private static int determinePixelType(String pixel) { 198 if (pixel == null || pixel.length() > 23) return TYPE_UNKNOWN; 199 int delimCount = 0; 200 for (char c : pixel.toCharArray()) { 201 if (c == '*') delimCount++; 202 } 203 return delimCount > 0 && delimCount <= 3 ? delimCount : TYPE_UNKNOWN; 204 } 205 isDimensionInvalid(int dimension)206 protected static boolean isDimensionInvalid(int dimension) { 207 return dimension < 0 || dimension > PIXEL_MAX; 208 } 209 210 @Override equals(Object o)211 public boolean equals(Object o) { 212 if (o == this) return true; 213 if (!(o instanceof BipPixel)) return false; 214 215 BipPixel p = (BipPixel) o; 216 return p.getType() == getType() 217 && p.getMinWidth() == getMinWidth() 218 && p.getMaxWidth() == getMaxWidth() 219 && p.getMinHeight() == getMinHeight() 220 && p.getMaxHeight() == getMaxHeight(); 221 } 222 223 @Override toString()224 public String toString() { 225 String s = null; 226 switch (mType) { 227 case TYPE_FIXED: 228 s = mMaxWidth + "*" + mMaxHeight; 229 break; 230 case TYPE_RESIZE_MODIFIED_ASPECT_RATIO: 231 s = mMinWidth + "*" + mMinHeight + "-" + mMaxWidth + "*" + mMaxHeight; 232 break; 233 case TYPE_RESIZE_FIXED_ASPECT_RATIO: 234 s = mMinWidth + "**-" + mMaxWidth + "*" + mMaxHeight; 235 break; 236 } 237 return s; 238 } 239 } 240