1 /* 2 * Copyright (C) 2023 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 package com.android.internal.widget.remotecompose.core.operations; 17 18 import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT; 19 import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY; 20 import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.SHORT; 21 22 import android.annotation.NonNull; 23 24 import com.android.internal.widget.remotecompose.core.Operation; 25 import com.android.internal.widget.remotecompose.core.Operations; 26 import com.android.internal.widget.remotecompose.core.RemoteContext; 27 import com.android.internal.widget.remotecompose.core.SerializableToString; 28 import com.android.internal.widget.remotecompose.core.WireBuffer; 29 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; 30 import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; 31 import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; 32 import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; 33 import com.android.internal.widget.remotecompose.core.serialize.Serializable; 34 35 import java.util.List; 36 37 /** 38 * Operation to deal with bitmap data On getting an Image during a draw call the bitmap is 39 * compressed and saved in playback the image is decompressed 40 */ 41 public class BitmapData extends Operation implements SerializableToString, Serializable { 42 private static final int OP_CODE = Operations.DATA_BITMAP; 43 private static final String CLASS_NAME = "BitmapData"; 44 public final int mImageId; 45 int mImageWidth; 46 int mImageHeight; 47 short mType; 48 short mEncoding; 49 @NonNull byte[] mBitmap; 50 51 /** The max size of width or height */ 52 public static final int MAX_IMAGE_DIMENSION = 8000; 53 54 /** The data is encoded in the file (default) */ 55 public static final short ENCODING_INLINE = 0; 56 57 /** The data is encoded in the url */ 58 public static final short ENCODING_URL = 1; 59 60 /** The data is encoded as a reference to file */ 61 public static final short ENCODING_FILE = 2; 62 63 /** The data is encoded as PNG_8888 (default) */ 64 public static final short TYPE_PNG_8888 = 0; 65 66 /** The data is encoded as PNG */ 67 public static final short TYPE_PNG = 1; 68 69 /** The data is encoded as RAW 8 bit */ 70 public static final short TYPE_RAW8 = 2; 71 72 /** The data is encoded as RAW 8888 bit */ 73 public static final short TYPE_RAW8888 = 3; 74 75 /** The data is encoded as PNG_8888 but decoded as ALPHA_8 */ 76 public static final short TYPE_PNG_ALPHA_8 = 4; 77 78 /** 79 * create a bitmap structure 80 * 81 * @param imageId the id to store the image 82 * @param width the width of the image 83 * @param height the height of the image 84 * @param bitmap the data 85 */ BitmapData(int imageId, int width, int height, @NonNull byte[] bitmap)86 public BitmapData(int imageId, int width, int height, @NonNull byte[] bitmap) { 87 this.mImageId = imageId; 88 this.mImageWidth = width; 89 this.mImageHeight = height; 90 this.mBitmap = bitmap; 91 } 92 93 /** 94 * Update the bitmap data 95 * 96 * @param from the bitmap to copy 97 */ update(BitmapData from)98 public void update(BitmapData from) { 99 this.mImageWidth = from.mImageWidth; 100 this.mImageHeight = from.mImageHeight; 101 this.mBitmap = from.mBitmap; 102 this.mType = from.mType; 103 this.mEncoding = from.mEncoding; 104 } 105 106 /** 107 * The width of the image 108 * 109 * @return the width 110 */ getWidth()111 public int getWidth() { 112 return mImageWidth; 113 } 114 115 /** 116 * The height of the image 117 * 118 * @return the height 119 */ getHeight()120 public int getHeight() { 121 return mImageHeight; 122 } 123 124 @Override write(@onNull WireBuffer buffer)125 public void write(@NonNull WireBuffer buffer) { 126 apply(buffer, mImageId, mImageWidth, mImageHeight, mBitmap); 127 } 128 129 @NonNull 130 @Override toString()131 public String toString() { 132 return "BITMAP DATA " + mImageId; 133 } 134 135 /** 136 * The name of the class 137 * 138 * @return the name 139 */ 140 @NonNull name()141 public static String name() { 142 return CLASS_NAME; 143 } 144 145 /** 146 * The OP_CODE for this command 147 * 148 * @return the opcode 149 */ id()150 public static int id() { 151 return OP_CODE; 152 } 153 154 /** 155 * The type of the image 156 * 157 * @return the type of the image 158 */ getType()159 public int getType() { 160 return mType; 161 } 162 163 /** 164 * Add the image to the document 165 * 166 * @param buffer document to write to 167 * @param imageId the id the image will be stored under 168 * @param width the width of the image 169 * @param height the height of the image 170 * @param bitmap the data used to store/encode the image 171 */ apply( @onNull WireBuffer buffer, int imageId, int width, int height, @NonNull byte[] bitmap)172 public static void apply( 173 @NonNull WireBuffer buffer, 174 int imageId, 175 int width, 176 int height, 177 @NonNull byte[] bitmap) { 178 buffer.start(OP_CODE); 179 buffer.writeInt(imageId); 180 buffer.writeInt(width); 181 buffer.writeInt(height); 182 buffer.writeBuffer(bitmap); 183 } 184 185 /** 186 * Add the image to the document (using the ehanced encoding) 187 * 188 * @param buffer document to write to 189 * @param imageId the id the image will be stored under 190 * @param type the type of image 191 * @param width the width of the image 192 * @param encoding the encoding 193 * @param height the height of the image 194 * @param bitmap the data used to store/encode the image 195 */ apply( @onNull WireBuffer buffer, int imageId, short type, short width, short encoding, short height, @NonNull byte[] bitmap)196 public static void apply( 197 @NonNull WireBuffer buffer, 198 int imageId, 199 short type, 200 short width, 201 short encoding, 202 short height, 203 @NonNull byte[] bitmap) { 204 buffer.start(OP_CODE); 205 buffer.writeInt(imageId); 206 int w = (((int) type) << 16) | width; 207 int h = (((int) encoding) << 16) | height; 208 buffer.writeInt(w); 209 buffer.writeInt(h); 210 buffer.writeBuffer(bitmap); 211 } 212 213 /** 214 * Read this operation and add it to the list of operations 215 * 216 * @param buffer the buffer to read 217 * @param operations the list of operations that will be added to 218 */ read(@onNull WireBuffer buffer, @NonNull List<Operation> operations)219 public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { 220 int imageId = buffer.readInt(); 221 int width = buffer.readInt(); 222 int height = buffer.readInt(); 223 int type; 224 if (width > 0xffff) { 225 type = width >> 16; 226 width = width & 0xffff; 227 } else { 228 type = TYPE_PNG_8888; 229 } 230 231 int encoding; 232 if (height > 0xffff) { 233 encoding = height >> 16; 234 height = height & 0xffff; 235 } else { 236 encoding = ENCODING_INLINE; 237 } 238 if (width < 1 239 || height < 1 240 || height > MAX_IMAGE_DIMENSION 241 || width > MAX_IMAGE_DIMENSION) { 242 throw new RuntimeException("Dimension of image is invalid " + width + "x" + height); 243 } 244 byte[] bitmap = buffer.readBuffer(); 245 BitmapData bitmapData = new BitmapData(imageId, width, height, bitmap); 246 bitmapData.mType = (short) type; 247 bitmapData.mEncoding = (short) encoding; 248 operations.add(bitmapData); 249 } 250 251 /** 252 * Populate the documentation with a description of this operation 253 * 254 * @param doc to append the description to. 255 */ documentation(@onNull DocumentationBuilder doc)256 public static void documentation(@NonNull DocumentationBuilder doc) { 257 doc.operation("Data Operations", OP_CODE, CLASS_NAME) 258 .description("Bitmap data") 259 .field(DocumentedOperation.INT, "id", "id of bitmap data") 260 .field(SHORT, "type", "width of the image") 261 .field(SHORT, "width", "width of the image") 262 .field(SHORT, "encoding", "height of the image") 263 .field(INT, "width", "width of the image") 264 .field(SHORT, "height", "height of the image") 265 .field(INT_ARRAY, "values", "length", "Array of ints"); 266 } 267 268 @Override apply(@onNull RemoteContext context)269 public void apply(@NonNull RemoteContext context) { 270 context.putObject(mImageId, this); 271 context.loadBitmap(mImageId, mEncoding, mType, mImageWidth, mImageHeight, mBitmap); 272 } 273 274 @NonNull 275 @Override deepToString(@onNull String indent)276 public String deepToString(@NonNull String indent) { 277 return indent + toString(); 278 } 279 280 @Override serializeToString(int indent, @NonNull StringSerializer serializer)281 public void serializeToString(int indent, @NonNull StringSerializer serializer) { 282 serializer.append( 283 indent, 284 CLASS_NAME + " id " + mImageId + " (" + mImageWidth + "x" + mImageHeight + ")"); 285 } 286 287 @Override serialize(MapSerializer serializer)288 public void serialize(MapSerializer serializer) { 289 serializer 290 .addType(CLASS_NAME) 291 .add("imageId", mImageId) 292 .add("imageWidth", mImageWidth) 293 .add("imageHeight", mImageHeight) 294 .add("imageType", getImageTypeString(mType)) 295 .add("encoding", getEncodingString(mEncoding)); 296 } 297 getEncodingString(short encoding)298 private String getEncodingString(short encoding) { 299 switch (encoding) { 300 case ENCODING_INLINE: 301 return "ENCODING_INLINE"; 302 case ENCODING_URL: 303 return "ENCODING_URL"; 304 case ENCODING_FILE: 305 return "ENCODING_FILE"; 306 default: 307 return "ENCODING_INVALID"; 308 } 309 } 310 getImageTypeString(short type)311 private String getImageTypeString(short type) { 312 switch (type) { 313 case TYPE_PNG_8888: 314 return "TYPE_PNG_8888"; 315 case TYPE_PNG: 316 return "TYPE_PNG"; 317 case TYPE_RAW8: 318 return "TYPE_RAW8"; 319 case TYPE_RAW8888: 320 return "TYPE_RAW8888"; 321 case TYPE_PNG_ALPHA_8: 322 return "TYPE_PNG_ALPHA_8"; 323 default: 324 return "TYPE_INVALID"; 325 } 326 } 327 } 328