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 * A FIXED size is represented as the following, where W is width and H is height. The domain 26 * of values is [0, 65535] 27 * 28 * W*H 29 * 30 * 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. 32 * The domain of values is [0, 65535] 33 * 34 * W_1*H_1-W2*H2 35 * 36 * A RESIZABLE size that allows a fixed aspect ratio is represented as the following, where 37 * W_1 is the minimum width and W2*H2 is the maximum width and height pair. 38 * The domain of values is [0, 65535] 39 * 40 * W_1**-W2*H2 41 * 42 * For each possible intermediate width value, the corresponding height is calculated using the 43 * formula 44 * 45 * 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 /** 68 * Create a fixed size BipPixel object 69 */ createFixed(int width, int height)70 public static BipPixel createFixed(int width, int height) { 71 return new BipPixel(TYPE_FIXED, width, height, width, height); 72 } 73 74 /** 75 * Create a resizable modifiable aspect ratio BipPixel object 76 */ createResizableModified(int minWidth, int minHeight, int maxWidth, int maxHeight)77 public static BipPixel createResizableModified(int minWidth, int minHeight, int maxWidth, 78 int maxHeight) { 79 return new BipPixel(TYPE_RESIZE_MODIFIED_ASPECT_RATIO, minWidth, minHeight, maxWidth, 80 maxHeight); 81 } 82 83 /** 84 * Create a resizable fixed aspect ratio BipPixel object 85 */ createResizableFixed(int minWidth, int maxWidth, int maxHeight)86 public static BipPixel createResizableFixed(int minWidth, int maxWidth, int maxHeight) { 87 int minHeight = (minWidth * maxHeight) / maxWidth; 88 return new BipPixel(TYPE_RESIZE_FIXED_ASPECT_RATIO, minWidth, minHeight, 89 maxWidth, maxHeight); 90 } 91 92 /** 93 * Directly create a BipPixel object knowing your exact type and dimensions. Internal use only 94 */ BipPixel(int type, int minWidth, int minHeight, int maxWidth, int maxHeight)95 private BipPixel(int type, int minWidth, int minHeight, int maxWidth, int maxHeight) { 96 if (isDimensionInvalid(minWidth) || isDimensionInvalid(maxWidth) 97 || isDimensionInvalid(minHeight) || isDimensionInvalid(maxHeight)) { 98 throw new IllegalArgumentException("Dimension's must be in [0, " + PIXEL_MAX + "]"); 99 } 100 101 mType = type; 102 mMinWidth = minWidth; 103 mMinHeight = minHeight; 104 mMaxWidth = maxWidth; 105 mMaxHeight = maxHeight; 106 } 107 108 /** 109 * Create a BipPixel object from an Image Format pixel attribute string 110 */ BipPixel(String pixel)111 public BipPixel(String pixel) { 112 int type = TYPE_UNKNOWN; 113 int minWidth = -1; 114 int minHeight = -1; 115 int maxWidth = -1; 116 int maxHeight = -1; 117 118 int typeHint = determinePixelType(pixel); 119 switch (typeHint) { 120 case TYPE_FIXED: 121 Pattern fixed = Pattern.compile("^(\\d{1,5})\\*(\\d{1,5})$"); 122 Matcher m1 = fixed.matcher(pixel); 123 if (m1.matches()) { 124 type = TYPE_FIXED; 125 minWidth = Integer.parseInt(m1.group(1)); 126 maxWidth = Integer.parseInt(m1.group(1)); 127 minHeight = Integer.parseInt(m1.group(2)); 128 maxHeight = Integer.parseInt(m1.group(2)); 129 } 130 break; 131 case TYPE_RESIZE_MODIFIED_ASPECT_RATIO: 132 Pattern modifiedRatio = Pattern.compile( 133 "^(\\d{1,5})\\*(\\d{1,5})-(\\d{1,5})\\*(\\d{1,5})$"); 134 Matcher m2 = modifiedRatio.matcher(pixel); 135 if (m2.matches()) { 136 type = TYPE_RESIZE_MODIFIED_ASPECT_RATIO; 137 minWidth = Integer.parseInt(m2.group(1)); 138 minHeight = Integer.parseInt(m2.group(2)); 139 maxWidth = Integer.parseInt(m2.group(3)); 140 maxHeight = Integer.parseInt(m2.group(4)); 141 } 142 break; 143 case TYPE_RESIZE_FIXED_ASPECT_RATIO: 144 Pattern fixedRatio = Pattern.compile("^(\\d{1,5})\\*\\*-(\\d{1,5})\\*(\\d{1,5})$"); 145 Matcher m3 = fixedRatio.matcher(pixel); 146 if (m3.matches()) { 147 type = TYPE_RESIZE_FIXED_ASPECT_RATIO; 148 minWidth = Integer.parseInt(m3.group(1)); 149 maxWidth = Integer.parseInt(m3.group(2)); 150 maxHeight = Integer.parseInt(m3.group(3)); 151 minHeight = (minWidth * maxHeight) / maxWidth; 152 } 153 break; 154 default: 155 break; 156 } 157 if (type == TYPE_UNKNOWN) { 158 throw new ParseException("Failed to determine type of '" + pixel + "'"); 159 } 160 if (isDimensionInvalid(minWidth) || isDimensionInvalid(maxWidth) 161 || isDimensionInvalid(minHeight) || isDimensionInvalid(maxHeight)) { 162 throw new ParseException("Parsed dimensions must be in [0, " + PIXEL_MAX + "]"); 163 } 164 165 mType = type; 166 mMinWidth = minWidth; 167 mMinHeight = minHeight; 168 mMaxWidth = maxWidth; 169 mMaxHeight = maxHeight; 170 } 171 getType()172 public int getType() { 173 return mType; 174 } 175 getMinWidth()176 public int getMinWidth() { 177 return mMinWidth; 178 } 179 getMaxWidth()180 public int getMaxWidth() { 181 return mMaxWidth; 182 } 183 getMinHeight()184 public int getMinHeight() { 185 return mMinHeight; 186 } 187 getMaxHeight()188 public int getMaxHeight() { 189 return mMaxHeight; 190 } 191 192 /** 193 * Determines the type of the pixel string by counting the number of '*' delimiters in the 194 * string. 195 * 196 * Note that the overall maximum size of any pixel string is 23 characters in length due to the 197 * max size of each dimension 198 * 199 * @return The corresponding type we should assume the given pixel string is 200 */ determinePixelType(String pixel)201 private static int determinePixelType(String pixel) { 202 if (pixel == null || pixel.length() > 23) return TYPE_UNKNOWN; 203 int delimCount = 0; 204 for (char c : pixel.toCharArray()) { 205 if (c == '*') delimCount++; 206 } 207 return delimCount > 0 && delimCount <= 3 ? delimCount : TYPE_UNKNOWN; 208 } 209 isDimensionInvalid(int dimension)210 protected static boolean isDimensionInvalid(int dimension) { 211 return dimension < 0 || dimension > PIXEL_MAX; 212 } 213 214 @Override equals(Object o)215 public boolean equals(Object o) { 216 if (o == this) return true; 217 if (!(o instanceof BipPixel)) return false; 218 219 BipPixel p = (BipPixel) o; 220 return p.getType() == getType() 221 && p.getMinWidth() == getMinWidth() 222 && p.getMaxWidth() == getMaxWidth() 223 && p.getMinHeight() == getMinHeight() 224 && p.getMaxHeight() == getMaxHeight(); 225 } 226 227 @Override toString()228 public String toString() { 229 String s = null; 230 switch (mType) { 231 case TYPE_FIXED: 232 s = mMaxWidth + "*" + mMaxHeight; 233 break; 234 case TYPE_RESIZE_MODIFIED_ASPECT_RATIO: 235 s = mMinWidth + "*" + mMinHeight + "-" + mMaxWidth + "*" + mMaxHeight; 236 break; 237 case TYPE_RESIZE_FIXED_ASPECT_RATIO: 238 s = mMinWidth + "**-" + mMaxWidth + "*" + mMaxHeight; 239 break; 240 } 241 return s; 242 } 243 } 244