    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.imaging.formats.webp.chunks;
    018
    019import java.io.IOException;
    020import java.io.PrintWriter;
    021
    022import org.apache.commons.imaging.ImagingException;
    023
    024/**
    025 * VP8 (bitstream) chunk.
    026 *
    027 * <pre>{@code
    028 *  0                   1                   2                   3
    029 *  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
    030 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    031 * |                      ChunkHeader('VP8 ')                      |
    032 * |                                                               |
    033 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    034 * :                           VP8 data                            :
    035 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    036 * }</pre>
    037 *
    038 * @see <a href="https://developers.google.com/speed/webp/docs/riff_container#simple_file_format_lossy">Simple File Format (Lossy)</a>
    039 * @see <a href="https://datatracker.ietf.org/doc/html/rfc6386">VP8 Data Format and Decoding Guide</a>
    040 * @since 1.0.0-alpha4
    041 */
    042public final class WebPChunkVp8 extends AbstractWebPChunk {
    043    private final int versionNumber;
    044    private final int width;
    045    private final int height;
    046    private final int horizontalScale;
    047    private final int verticalScale;
    048
    049    /**
    050     * Create a VP8 chunk.
    051     *
    052     * @param type  chunk type.
    053     * @param size  chunk size.
    054     * @param bytes chunk data.
    055     * @throws ImagingException if the chunk data and the size provided do not match.
    056     */
    057    public WebPChunkVp8(final int type, final int size, final byte[] bytes) throws ImagingException {
    058        super(type, size, bytes);
    059
    060        if (size < 10) {
    061            throw new ImagingException("Invalid VP8 chunk");
    062        }
    063
    064        /*
    065         * https://datatracker.ietf.org/doc/html/rfc6386#section-9
    066         */
    067
    068        /*
    069         * Frame Header:
    070         *
    071         * 1. A 1-bit frame type (0 for key frames, 1 for interframes).
    072         *
    073         * 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
    074         * variants of the VP8 data format).
    075         *
    076         * 3. A 1-bit show_frame flag (0 when current frame is not for display, 1 when current frame is for display).
    077         *
    078         * 4. A 19-bit field containing the size of the first data partition in bytes.
    079         */
    080
    081        final int b0 = bytes[0] & 0xFF;
    082        // int b1 = bytes[1] & 0xFF;
    083        // int b2 = bytes[2] & 0xFF;
    084
    085        if ((b0 & 0b1) != 0) {
    086            throw new ImagingException("Invalid VP8 chunk: should be key frame");
    087        }
    088
    089        this.versionNumber = (b0 & 0b1110) >> 1;
    090        if ((b0 & 0b0001_0000) == 0) {
    091            throw new ImagingException("Invalid VP8 chunk: frame should to be display");
    092        }
    093
    094        // int firstDataPartitionSize = (b0 >>> 5) + (b1 << 3) + (b2 << 11);
    095
    096        /*
    097         * Key Frame:
    098         *
    099         * 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}