1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.imaging.formats.webp.chunks;
18
19 import java.io.IOException;
20 import java.io.PrintWriter;
21
22 import org.apache.commons.imaging.ImagingException;
23
24 /**
25 * VP8 (bitstream) chunk.
26 *
27 * <pre>{@code
28 * 0 1 2 3
29 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
30 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
31 * | ChunkHeader('VP8 ') |
32 * | |
33 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
34 * : VP8 data :
35 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
36 * }</pre>
37 *
38 * @see <a href="https://developers.google.com/speed/webp/docs/riff_container#simple_file_format_lossy">Simple File Format (Lossy)</a>
39 * @see <a href="https://datatracker.ietf.org/doc/html/rfc6386">VP8 Data Format and Decoding Guide</a>
40 * @since 1.0.0-alpha4
41 */
42 public final class WebPChunkVp8 extends AbstractWebPChunk {
43 private final int versionNumber;
44 private final int width;
45 private final int height;
46 private final int horizontalScale;
47 private final int verticalScale;
48
49 /**
50 * Create a VP8 chunk.
51 *
52 * @param type chunk type.
53 * @param size chunk size.
54 * @param bytes chunk data.
55 * @throws ImagingException if the chunk data and the size provided do not match.
56 */
57 public WebPChunkVp8(final int type, final int size, final byte[] bytes) throws ImagingException {
58 super(type, size, bytes);
59
60 if (size < 10) {
61 throw new ImagingException("Invalid VP8 chunk");
62 }
63
64 /*
65 * https://datatracker.ietf.org/doc/html/rfc6386#section-9
66 */
67
68 /*
69 * Frame Header:
70 *
71 * 1. A 1-bit frame type (0 for key frames, 1 for interframes).
72 *
73 * 2. A 3-bit version number (0 - 3 are defined as four different profiles with different decoding complexity; other values may be defined for future
74 * variants of the VP8 data format).
75 *
76 * 3. A 1-bit show_frame flag (0 when current frame is not for display, 1 when current frame is for display).
77 *
78 * 4. A 19-bit field containing the size of the first data partition in bytes.
79 */
80
81 final int b0 = bytes[0] & 0xFF;
82 // int b1 = bytes[1] & 0xFF;
83 // int b2 = bytes[2] & 0xFF;
84
85 if ((b0 & 0b1) != 0) {
86 throw new ImagingException("Invalid VP8 chunk: should be key frame");
87 }
88
89 this.versionNumber = (b0 & 0b1110) >> 1;
90 if ((b0 & 0b0001_0000) == 0) {
91 throw new ImagingException("Invalid VP8 chunk: frame should to be display");
92 }
93
94 // int firstDataPartitionSize = (b0 >>> 5) + (b1 << 3) + (b2 << 11);
95
96 /*
97 * Key Frame:
98 *
99 * Start code byte 0 0x9d Start code byte 1 0x01 Start code byte 2 0x2a
100 *
101 * 16 bits : (2 bits Horizontal Scale << 14) | Width (14 bits) 16 bits : (2 bits Vertical Scale << 14) | Height (14 bits)
102 */
103
104 final int b3 = bytes[3] & 0xFF;
105 final int b4 = bytes[4] & 0xFF;
106 final int b5 = bytes[5] & 0xFF;
107 final int b6 = bytes[6] & 0xFF;
108 final int b7 = bytes[7] & 0xFF;
109 final int b8 = bytes[8] & 0xFF;
110 final int b9 = bytes[9] & 0xFF;
111
112 if (b3 != 0x9D || b4 != 0x01 || b5 != 0x2A) {
113 throw new ImagingException("Invalid VP8 chunk: invalid signature");
114 }
115
116 this.width = b6 + ((b7 & 0b0011_1111) << 8);
117 this.horizontalScale = b7 >> 6;
118 this.height = b8 + ((b9 & 0b0011_1111) << 8);
119 this.verticalScale = b9 >> 6;
120 }
121
122 @Override
123 public void dump(final PrintWriter pw, final int offset) throws ImagingException, IOException {
124 super.dump(pw, offset);
125 pw.println(" Version Number: " + getVersionNumber());
126 pw.println(" Width: " + getWidth());
127 pw.println(" Height: " + getHeight());
128 pw.println(" Horizontal Scale: " + getHorizontalScale());
129 pw.println(" Vertical Scale: " + getVerticalScale());
130 }
131
132 /**
133 * @return the height.
134 */
135 public int getHeight() {
136 return height;
137 }
138
139 /**
140 * @return the horizontal scale.
141 */
142 public int getHorizontalScale() {
143 return horizontalScale;
144 }
145
146 /**
147 * @return the version number.
148 */
149 public int getVersionNumber() {
150 return versionNumber;
151 }
152
153 /**
154 * @return the vertical scale.
155 */
156 public int getVerticalScale() {
157 return verticalScale;
158 }
159
160 /**
161 * @return the width.
162 */
163 public int getWidth() {
164 return width;
165 }
166 }