-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDIBCodec.java
More file actions
206 lines (193 loc) · 8.48 KB
/
DIBCodec.java
File metadata and controls
206 lines (193 loc) · 8.48 KB
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/*
* @(#)DIBCodec.java
*
* Copyright © 2011-2012 Werner Randelshofer, Immensee, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.IOException;
import java.io.OutputStream;
/**
* {@code DIBCodec} encodes a BufferedImage as a Microsoft Device Independent
* Bitmap (DIB) into a byte array.
* <p>
* The DIB codec only works with the AVI file format. Other file formats, such
* as QuickTime, use a different encoding for uncompressed video.
* <p>
* This codec currently only supports encoding from a {@code BufferedImage} into
* the file format. Decoding support may be added in the future.
* <p>
* This codec does not encode the color palette of an image. This must be done
* separately.
* <p>
* The pixels of a frame are written row by row from bottom to top and from
* the left to the right. 24-bit pixels are encoded as BGR.
* <p>
* Supported input formats:
* <ul>
* {@code Format} with {@code BufferedImage.class}, any width, any height,
* depth=4.
* </ul>
* Supported output formats:
* <ul>
* {@code Format} with {@code byte[].class}, same width and height as input
* format, depth=4.
* </ul>
*
* @author Werner Randelshofer
* @version $Id: DIBCodec.java 186 2012-03-28 11:18:42Z werner $
*/
public class DIBCodec extends AbstractVideoCodec {
public DIBCodec() {
super(new Format[]{
new Format(FormatKeys.MediaTypeKey, FormatKeys.MediaType.VIDEO, FormatKeys.MimeTypeKey, FormatKeys.MIME_JAVA,
FormatKeys.EncodingKey, VideoFormatKeys.ENCODING_BUFFERED_IMAGE, VideoFormatKeys.FixedFrameRateKey, true), //
},
new Format[]{
new Format(FormatKeys.MediaTypeKey, FormatKeys.MediaType.VIDEO, FormatKeys.MimeTypeKey, VideoFormatKeys.MIME_AVI,
FormatKeys.EncodingKey, VideoFormatKeys.ENCODING_AVI_DIB, VideoFormatKeys.DataClassKey, byte[].class,
VideoFormatKeys.FixedFrameRateKey, true, VideoFormatKeys.DepthKey,4), //
new Format(FormatKeys.MediaTypeKey, FormatKeys.MediaType.VIDEO, FormatKeys.MimeTypeKey, VideoFormatKeys.MIME_AVI,
FormatKeys.EncodingKey, VideoFormatKeys.ENCODING_AVI_DIB, VideoFormatKeys.DataClassKey, byte[].class,
VideoFormatKeys.FixedFrameRateKey, true, VideoFormatKeys.DepthKey,8), //
new Format(FormatKeys.MediaTypeKey, FormatKeys.MediaType.VIDEO, FormatKeys.MimeTypeKey, VideoFormatKeys.MIME_AVI,
FormatKeys.EncodingKey, VideoFormatKeys.ENCODING_AVI_DIB, VideoFormatKeys.DataClassKey, byte[].class,
VideoFormatKeys.FixedFrameRateKey, true, VideoFormatKeys.DepthKey,24), //
});
}
@Override
public int process(Buffer in, Buffer out) {
out.setMetaTo(in);
out.format = outputFormat;
if (in.isFlag(BufferFlag.DISCARD)) {
return CODEC_OK;
}
SeekableByteArrayOutputStream tmp;
if (out.data instanceof byte[]) {
tmp = new SeekableByteArrayOutputStream((byte[]) out.data);
} else {
tmp = new SeekableByteArrayOutputStream();
}
// Handle sub-image
// FIXME - Scanline stride must be a multiple of four.
Rectangle r;
int scanlineStride;
if (in.data instanceof BufferedImage) {
BufferedImage image = (BufferedImage) in.data;
WritableRaster raster = image.getRaster();
scanlineStride = raster.getSampleModel().getWidth();
r = raster.getBounds();
r.x -= raster.getSampleModelTranslateX();
r.y -= raster.getSampleModelTranslateY();
out.header=image.getColorModel();
} else {
r = new Rectangle(0, 0, outputFormat.get(VideoFormatKeys.WidthKey), outputFormat.get(VideoFormatKeys.HeightKey));
scanlineStride = outputFormat.get(VideoFormatKeys.WidthKey);
out.header=null;
}
try {
switch (outputFormat.get(VideoFormatKeys.DepthKey)) {
case 4: {
byte[] pixels = getIndexed8(in);
if (pixels == null) {
out.setFlag(BufferFlag.DISCARD);
return CODEC_OK;
}
writeKey4(tmp, pixels, r.width, r.height, r.x + r.y * scanlineStride, scanlineStride);
break;
}
case 8: {
byte[] pixels = getIndexed8(in);
if (pixels == null) {
out.setFlag(BufferFlag.DISCARD);
return CODEC_OK;
}
writeKey8(tmp, pixels, r.width, r.height, r.x + r.y * scanlineStride, scanlineStride);
break;
}
case 24: {
int[] pixels = getRGB24(in);
if (pixels == null) {
out.setFlag(BufferFlag.DISCARD);
return CODEC_OK;
}
writeKey24(tmp, pixels, r.width, r.height, r.x + r.y * scanlineStride, scanlineStride);
break;
}
default:
out.setFlag(BufferFlag.DISCARD);
return CODEC_OK;
}
out.setFlag(BufferFlag.KEYFRAME);
out.data = tmp.getBuffer();
out.sampleCount = 1;
out.offset = 0;
out.length = (int) tmp.getStreamPosition();
return CODEC_OK;
} catch (IOException ex) {
ex.printStackTrace();
out.setFlag(BufferFlag.DISCARD);
return CODEC_FAILED;
}
}
/** Encodes a 4-bit key frame.
*
* @param out The output stream.
* @param pixels The image data.
* @param offset The offset to the first pixel in the data array.
* @param width The width of the image in data elements.
* @param scanlineStride The number to append to offset to get to the next scanline.
*/
public void writeKey4(OutputStream out, byte[] pixels, int width, int height, int offset, int scanlineStride)
throws IOException {
byte[] bytes = new byte[width];
for (int y = (height - 1) * scanlineStride; y >= 0; y -= scanlineStride) { // Upside down
for (int x = offset, xx = 0, n = offset + width; x < n; x += 2, ++xx) {
bytes[xx] = (byte) (((pixels[y + x] & 0xf) << 4) | (pixels[y + x + 1] & 0xf));
}
out.write(bytes);
}
}
/** Encodes an 8-bit key frame.
*
* @param out The output stream.
* @param pixels The image data.
* @param offset The offset to the first pixel in the data array.
* @param width The width of the image in data elements.
* @param scanlineStride The number to append to offset to get to the next scanline.
*/
public void writeKey8(OutputStream out, byte[] pixels, int width, int height, int offset, int scanlineStride)
throws IOException {
for (int y = (height - 1) * scanlineStride; y >= 0; y -= scanlineStride) { // Upside down
out.write(pixels, y + offset, width);
}
}
/** Encodes a 24-bit key frame.
*
* @param out The output stream.
* @param pixels The image data.
* @param offset The offset to the first pixel in the data array.
* @param width The width of the image in data elements.
* @param scanlineStride The number to append to offset to get to the next scanline.
*/
public void writeKey24(OutputStream out, int[] pixels, int width, int height, int offset, int scanlineStride)
throws IOException {
int w3 = width * 3;
byte[] bytes = new byte[w3]; // holds a scanline of raw image data with 3 channels of 8 bit data
for (int xy = (height - 1) * scanlineStride + offset; xy >= offset; xy -= scanlineStride) { // Upside down
for (int x = 0, xp = 0; x < w3; x += 3, ++xp) {
int p = pixels[xy + xp];
bytes[x] = (byte) (p); // Blue
bytes[x + 1] = (byte) (p >> 8); // Green
bytes[x + 2] = (byte) (p >> 16); // Red
}
out.write(bytes);
}
}
}