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;
021import java.nio.ByteOrder;
022import java.nio.charset.StandardCharsets;
023
024import org.apache.commons.imaging.ImagingException;
025import org.apache.commons.imaging.common.BinaryFileParser;
026import org.apache.commons.imaging.internal.SafeOperations;
027
028/**
029 * A WebP image is composed of several chunks. This is the base class for the chunks, used by the parser.
030 *
031 * @see <a href="https://developers.google.com/speed/webp/docs/riff_container">WebP Container Specification</a>
032 * @since 1.0-alpha4
033 */
034public abstract class WebPChunk extends BinaryFileParser {
035    private final int type;
036    private final int size;
037    protected final byte[] bytes;
038    private final int chunkSize;
039
040    /**
041     * Create a new WebP chunk.
042     *
043     * @param type  chunk type.
044     * @param size  chunk size.
045     * @param bytes chunk data.
046     * @throws ImagingException if the chunk data and the size provided do not match.
047     */
048    WebPChunk(final int type, final int size, final byte[] bytes) throws ImagingException {
049        super(ByteOrder.LITTLE_ENDIAN);
050
051        if (size != bytes.length) {
052            throw new ImagingException("Chunk size must match bytes length");
053        }
054
055        this.type = type;
056        this.size = size;
057        this.bytes = bytes;
058
059        // if chunk size is odd, a single padding byte is added
060        final int padding = size % 2 != 0 ? 1 : 0;
061
062        // Chunk FourCC (4 bytes) + Chunk Size (4 bytes) + Chunk Payload (n bytes) + Padding
063        this.chunkSize = SafeOperations.add(4, 4, size, padding);
064    }
065
066    /**
067     * Print the chunk to the given stream.
068     *
069     * @param pw     a stream to write to.
070     * @param offset chunk offset.
071     * @throws ImagingException if the image is invalid.
072     * @throws IOException      if it fails to write to the given stream.
073     */
074    public void dump(final PrintWriter pw, final int offset) throws ImagingException, IOException {
075        pw.printf("Chunk %s at offset %s, length %d%n, payload size %d%n", getTypeDescription(), offset >= 0 ? String.valueOf(offset) : "unknown",
076                getChunkSize(), getPayloadSize());
077    }
078
079    /**
080     * @return a copy of the chunk data as bytes.
081     */
082    public byte[] getBytes() {
083        return bytes.clone();
084    }
085
086    /**
087     * @return the chunk size.
088     */
089    public int getChunkSize() {
090        return chunkSize;
091    }
092
093    /**
094     * @return the payload size.
095     */
096    public int getPayloadSize() {
097        return size;
098    }
099
100    /**
101     * @return the chunk type.
102     */
103    public int getType() {
104        return type;
105    }
106
107    /**
108     * @return the description of the chunk type.
109     */
110    public String getTypeDescription() {
111        return new String(new byte[] { (byte) (type & 0xff), (byte) (type >> 8 & 0xff), (byte) (type >> 16 & 0xff), (byte) (type >> 24 & 0xff) },
112                StandardCharsets.UTF_8);
113    }
114}