aboutsummaryrefslogtreecommitdiff
path: root/third_party/gif_decoder/src/androidTest/java/com/bumptech/glide/gifdecoder/test/GifBytesTestUtil.java
blob: e389deed50c2ff9a4651f6ee1dc5fa1dd7508a85 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package com.bumptech.glide.gifdecoder.test;

import java.nio.ByteBuffer;

/**
 * Utils for writing the bytes of various parts of GIFs to byte buffers.
 */
public class GifBytesTestUtil {
    // Length in bytes.
    public static final int HEADER_LENGTH = 13;
    // Length in bytes.
    public static final int IMAGE_DESCRIPTOR_LENGTH = 10;

    public static int getColorTableLength(int numColors) {
        return 3 * numColors;
    }

    public static int getImageDataSize(int lzwMinCodeSize) {
        // TODO: fill this out.
        return 4;
    }

    public static void writeFakeImageData(ByteBuffer out, int lzwMinCodeSize) {
        // 1 for lzwMinCodeSize, 1 for length, 1 for min content, 1 for block terminator.
        verifyRemaining(out, 4);
        verifyShortValues(lzwMinCodeSize);

        out.put((byte) lzwMinCodeSize);
        // Block length.
        out.put((byte) 0x01);
        // Block content.
        out.put((byte) 0x01);
        // End of block.
        out.put((byte) 0x00);
    }

    public static void writeColorTable(ByteBuffer out, int numColors) {
        verifyRemaining(out, getColorTableLength(numColors));
        for (int i = 0; i < numColors; i++) {
            out.put((byte) (0xFF0000 & i));
            out.put((byte) (0x00FF00 & i));
            out.put((byte) (0x0000FF & i));
        }
    }

    public static void writeImageDescriptor(ByteBuffer out, int imageLeft, int imageTop, int imageWidth,
            int imageHeight, boolean hasLct, int numColors) {
        verifyRemaining(out, IMAGE_DESCRIPTOR_LENGTH);
        verifyShortValues(imageLeft, imageTop, imageWidth, imageHeight);

        final byte packed;
        if (hasLct) {
            int size = log2(numColors) - 1;
            packed = (byte) (0x80 | size);
        } else {
            packed = 0x00;
        }

        // Image separator
        out.put((byte) 0x2C);
        out
                .putShort((short) imageLeft)
                .putShort((short) imageTop)
                .putShort((short) imageWidth)
                .putShort((short) imageHeight)
                .put(packed);
    }

    private static int log2(int num) {
        return (int) Math.round(Math.log(num) / Math.log(2));
    }

    public static void writeHeaderAndLsd(ByteBuffer out, int width, int height, boolean hasGct, int gctSize) {
        verifyRemaining(out, HEADER_LENGTH);
        verifyShortValues(width, height);

        // GIF
        out.put((byte) 0x47).put((byte) 0x49).put((byte) 0x46);
        // Version - 89a.
        out.put((byte) 0x38).put((byte) 0x39).put((byte) 0x61);

        /** LSD (Logical Screen Descriptor) **/
          // Width.
        out.putShort((short) width);
        // Height.
        out.putShort((short) height);
        // Packed GCT (Global Color Table) flag + color resolution + sort flag + size of GCT.
        // GCT flag (false) - most significant bit.
        byte gctFlag = (byte) ((hasGct ? 1 : 0) << 7);
        // Color resolution - next three bits.
        byte colorResolution = 1 << 5;
        // Sort flag - next bit;
        byte sortFlag = 0 << 4;
        // exponent of size of color table, size = 2^(1 + exponent) - least significant 3 bits.
        byte size = (byte) gctSize;

        byte packed = (byte) (gctFlag | colorResolution | sortFlag | size);
        out.put(packed);

        // Background color index.
        out.put((byte) 0);

        // Pixel aspect ratio.
        out.put((byte) 0);
    }

    private static void verifyRemaining(ByteBuffer buffer, int expected) {
        if (buffer.remaining() < expected) {
            throw new IllegalArgumentException("Must have at least " + expected + " bytes to write");
        }
    }

    private static void verifyShortValues(int... shortValues) {
        for (int dimen : shortValues) {
            if (dimen > Short.MAX_VALUE || dimen < 0) {
                throw new IllegalArgumentException("Must pass in non-negative short dimensions, not: " + dimen);
            }
        }
    }
}