View Javadoc
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-alpha4
41   */
42  public final class WebPChunkVp8 extends WebPChunk {
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 }