• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Objects;
20 import java.util.regex.Matcher;
21 import java.util.regex.Pattern;
22 
23 /**
24  * The pixel size or range of pixel sizes in which the image is available
25  *
26  * <p>A FIXED size is represented as the following, where W is width and H is height. The domain of
27  * values is [0, 65535]
28  *
29  * <p>W*H
30  *
31  * <p>A RESIZABLE size that allows a modified aspect ratio is represented as the following, where
32  * W_1*H_1 is the minimum width and height pair and W2*H2 is the maximum width and height pair. The
33  * domain of values is [0, 65535]
34  *
35  * <p>W_1*H_1-W2*H2
36  *
37  * <p>A RESIZABLE size that allows a fixed aspect ratio is represented as the following, where W_1
38  * is the minimum width and W2*H2 is the maximum width and height pair. The domain of values is [0,
39  * 65535]
40  *
41  * <p>W_1**-W2*H2
42  *
43  * <p>For each possible intermediate width value, the corresponding height is calculated using the
44  * formula
45  *
46  * <p>H=(W*H2)/W2
47  */
48 public class BipPixel {
49     private static final String TAG =
50             AvrcpControllerUtils.TAG_PREFIX_AVRCP_CONTROLLER + BipPixel.class.getSimpleName();
51 
52     // The BIP specification declares this as the max size to be transferred. You can optionally
53     // use this value to indicate there is no upper bound on pixel size.
54     public static final int PIXEL_MAX = 65535;
55 
56     // Note that the integer values also map to the number of '*' delimiters that exist in each
57     // formatted string
58     public static final int TYPE_UNKNOWN = 0;
59     public static final int TYPE_FIXED = 1;
60     public static final int TYPE_RESIZE_MODIFIED_ASPECT_RATIO = 2;
61     public static final int TYPE_RESIZE_FIXED_ASPECT_RATIO = 3;
62 
63     private final int mType;
64     private final int mMinWidth;
65     private final int mMinHeight;
66     private final int mMaxWidth;
67     private final int mMaxHeight;
68 
69     /** Create a fixed size BipPixel object */
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     /** Create a resizable modifiable aspect ratio BipPixel object */
createResizableModified( int minWidth, int minHeight, int maxWidth, int maxHeight)75     public static BipPixel createResizableModified(
76             int minWidth, int minHeight, int maxWidth, int maxHeight) {
77         return new BipPixel(
78                 TYPE_RESIZE_MODIFIED_ASPECT_RATIO, minWidth, minHeight, maxWidth, maxHeight);
79     }
80 
81     /** Create a resizable fixed aspect ratio BipPixel object */
createResizableFixed(int minWidth, int maxWidth, int maxHeight)82     public static BipPixel createResizableFixed(int minWidth, int maxWidth, int maxHeight) {
83         int minHeight = (minWidth * maxHeight) / maxWidth;
84         return new BipPixel(
85                 TYPE_RESIZE_FIXED_ASPECT_RATIO, minWidth, minHeight, maxWidth, maxHeight);
86     }
87 
88     /**
89      * Directly create a BipPixel object knowing your exact type and dimensions. Internal use only
90      */
BipPixel(int type, int minWidth, int minHeight, int maxWidth, int maxHeight)91     private BipPixel(int type, int minWidth, int minHeight, int maxWidth, int maxHeight) {
92         if (isDimensionInvalid(minWidth)
93                 || isDimensionInvalid(maxWidth)
94                 || isDimensionInvalid(minHeight)
95                 || isDimensionInvalid(maxHeight)) {
96             throw new IllegalArgumentException("Dimension's must be in [0, " + PIXEL_MAX + "]");
97         }
98 
99         mType = type;
100         mMinWidth = minWidth;
101         mMinHeight = minHeight;
102         mMaxWidth = maxWidth;
103         mMaxHeight = maxHeight;
104     }
105 
106     /** Create a BipPixel object from an Image Format pixel attribute string */
BipPixel(String pixel)107     public BipPixel(String pixel) {
108         int type = TYPE_UNKNOWN;
109         int minWidth = -1;
110         int minHeight = -1;
111         int maxWidth = -1;
112         int maxHeight = -1;
113 
114         int typeHint = determinePixelType(pixel);
115         switch (typeHint) {
116             case TYPE_FIXED:
117                 Pattern fixed = Pattern.compile("^(\\d{1,5})\\*(\\d{1,5})$");
118                 Matcher m1 = fixed.matcher(pixel);
119                 if (m1.matches()) {
120                     type = TYPE_FIXED;
121                     minWidth = Integer.parseInt(m1.group(1));
122                     maxWidth = Integer.parseInt(m1.group(1));
123                     minHeight = Integer.parseInt(m1.group(2));
124                     maxHeight = Integer.parseInt(m1.group(2));
125                 }
126                 break;
127             case TYPE_RESIZE_MODIFIED_ASPECT_RATIO:
128                 Pattern modifiedRatio =
129                         Pattern.compile("^(\\d{1,5})\\*(\\d{1,5})-(\\d{1,5})\\*(\\d{1,5})$");
130                 Matcher m2 = modifiedRatio.matcher(pixel);
131                 if (m2.matches()) {
132                     type = TYPE_RESIZE_MODIFIED_ASPECT_RATIO;
133                     minWidth = Integer.parseInt(m2.group(1));
134                     minHeight = Integer.parseInt(m2.group(2));
135                     maxWidth = Integer.parseInt(m2.group(3));
136                     maxHeight = Integer.parseInt(m2.group(4));
137                 }
138                 break;
139             case TYPE_RESIZE_FIXED_ASPECT_RATIO:
140                 Pattern fixedRatio = Pattern.compile("^(\\d{1,5})\\*\\*-(\\d{1,5})\\*(\\d{1,5})$");
141                 Matcher m3 = fixedRatio.matcher(pixel);
142                 if (m3.matches()) {
143                     type = TYPE_RESIZE_FIXED_ASPECT_RATIO;
144                     minWidth = Integer.parseInt(m3.group(1));
145                     maxWidth = Integer.parseInt(m3.group(2));
146                     maxHeight = Integer.parseInt(m3.group(3));
147                     minHeight = (minWidth * maxHeight) / maxWidth;
148                 }
149                 break;
150             default:
151                 break;
152         }
153         if (type == TYPE_UNKNOWN) {
154             throw new ParseException("Failed to determine type of '" + pixel + "'");
155         }
156         if (isDimensionInvalid(minWidth)
157                 || isDimensionInvalid(maxWidth)
158                 || isDimensionInvalid(minHeight)
159                 || isDimensionInvalid(maxHeight)) {
160             throw new ParseException("Parsed dimensions must be in [0, " + PIXEL_MAX + "]");
161         }
162 
163         mType = type;
164         mMinWidth = minWidth;
165         mMinHeight = minHeight;
166         mMaxWidth = maxWidth;
167         mMaxHeight = maxHeight;
168     }
169 
getType()170     public int getType() {
171         return mType;
172     }
173 
getMinWidth()174     public int getMinWidth() {
175         return mMinWidth;
176     }
177 
getMaxWidth()178     public int getMaxWidth() {
179         return mMaxWidth;
180     }
181 
getMinHeight()182     public int getMinHeight() {
183         return mMinHeight;
184     }
185 
getMaxHeight()186     public int getMaxHeight() {
187         return mMaxHeight;
188     }
189 
190     /**
191      * Determines the type of the pixel string by counting the number of '*' delimiters in the
192      * string.
193      *
194      * <p>Note that the overall maximum size of any pixel string is 23 characters in length due to
195      * the max size of each dimension
196      *
197      * @return The corresponding type we should assume the given pixel string is
198      */
determinePixelType(String pixel)199     private static int determinePixelType(String pixel) {
200         if (pixel == null || pixel.length() > 23) return TYPE_UNKNOWN;
201         int delimCount = 0;
202         for (int i = 0; i < pixel.length(); i++) {
203             char c = pixel.charAt(i);
204             if (c == '*') delimCount++;
205         }
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) {
217             return true;
218         }
219         if (!(o instanceof BipPixel p)) {
220             return false;
221         }
222 
223         return p.getType() == getType()
224                 && p.getMinWidth() == getMinWidth()
225                 && p.getMaxWidth() == getMaxWidth()
226                 && p.getMinHeight() == getMinHeight()
227                 && p.getMaxHeight() == getMaxHeight();
228     }
229 
230     @Override
hashCode()231     public int hashCode() {
232         return Objects.hash(
233                 getType(), getMinWidth(), getMaxWidth(), getMinHeight(), getMaxHeight());
234     }
235 
236     @Override
toString()237     public String toString() {
238         String s = null;
239         switch (mType) {
240             case TYPE_FIXED:
241                 s = mMaxWidth + "*" + mMaxHeight;
242                 break;
243             case TYPE_RESIZE_MODIFIED_ASPECT_RATIO:
244                 s = mMinWidth + "*" + mMinHeight + "-" + mMaxWidth + "*" + mMaxHeight;
245                 break;
246             case TYPE_RESIZE_FIXED_ASPECT_RATIO:
247                 s = mMinWidth + "**-" + mMaxWidth + "*" + mMaxHeight;
248                 break;
249         }
250         return s;
251     }
252 }
253