1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.imaging.formats.bmp;
18
19 import java.io.IOException;
20 import java.util.logging.Logger;
21
22 import org.apache.commons.imaging.ImagingException;
23 import org.apache.commons.imaging.common.BinaryFunctions;
24 import org.apache.commons.imaging.common.ImageBuilder;
25
26 final class PixelParserRle extends AbstractPixelParser {
27
28 private static final Logger LOGGER = Logger.getLogger(PixelParserRle.class.getName());
29
30 PixelParserRle(final BmpHeaderInfo bhi, final byte[] colorTable, final byte[] imageData) {
31 super(bhi, colorTable, imageData);
32 }
33
34 private int[] convertDataToSamples(final int data) throws ImagingException {
35 int[] rgbs;
36 if (bhi.bitsPerPixel == 8) {
37 rgbs = new int[1];
38 rgbs[0] = getColorTableRgb(data);
39
40 } else if (bhi.bitsPerPixel == 4) {
41 rgbs = new int[2];
42 final int sample1 = data >> 4;
43 final int sample2 = 0x0f & data;
44 rgbs[0] = getColorTableRgb(sample1);
45 rgbs[1] = getColorTableRgb(sample2);
46
47 } else {
48 throw new ImagingException("BMP RLE: bad BitsPerPixel: " + bhi.bitsPerPixel);
49 }
50
51 return rgbs;
52 }
53
54 private int getSamplesPerByte() throws ImagingException {
55 if (bhi.bitsPerPixel == 8) {
56 return 1;
57 }
58 if (bhi.bitsPerPixel == 4) {
59 return 2;
60 }
61 throw new ImagingException("BMP RLE: bad BitsPerPixel: " + bhi.bitsPerPixel);
62 }
63
64 private int processByteOfData(final int[] rgbs, final int repeat, int x, final int y, final int width, final int height, final ImageBuilder imageBuilder) {
65
66 int pixelsWritten = 0;
67 for (int i = 0; i < repeat; i++) {
68
69 if (x >= 0 && x < width && y >= 0 && y < height) {
70
71
72 final int rgb = rgbs[i % rgbs.length];
73
74 imageBuilder.setRgb(x, y, rgb);
75
76 } else {
77 LOGGER.fine("skipping bad pixel (" + x + "," + y + ")");
78 }
79
80 x++;
81 pixelsWritten++;
82 }
83
84 return pixelsWritten;
85 }
86
87 @Override
88 public void processImage(final ImageBuilder imageBuilder) throws ImagingException, IOException {
89 final int width = bhi.width;
90 final int height = bhi.height;
91 int x = 0;
92 int y = height - 1;
93
94 boolean done = false;
95 while (!done) {
96 final int a = 0xff & BinaryFunctions.readByte("RLE (" + x + "," + y + ") a", is, "BMP: Bad RLE");
97 final int b = 0xff & BinaryFunctions.readByte("RLE (" + x + "," + y + ") b", is, "BMP: Bad RLE");
98
99 if (a == 0) {
100 switch (b) {
101 case 0: {
102
103 y--;
104 x = 0;
105 break;
106 }
107 case 1:
108
109 done = true;
110 break;
111 case 2: {
112 final int deltaX = 0xff & BinaryFunctions.readByte("RLE deltaX", is, "BMP: Bad RLE");
113 final int deltaY = 0xff & BinaryFunctions.readByte("RLE deltaY", is, "BMP: Bad RLE");
114 x += deltaX;
115 y -= deltaY;
116 break;
117 }
118 default: {
119 final int samplesPerByte = getSamplesPerByte();
120 int size = b / samplesPerByte;
121 if (b % samplesPerByte > 0) {
122 size++;
123 }
124 if (size % 2 != 0) {
125 size++;
126 }
127
128
129
130
131
132 final byte[] bytes = BinaryFunctions.readBytes("bytes", is, size, "RLE: Absolute Mode");
133
134 int remaining = b;
135
136 for (int i = 0; remaining > 0; i++) {
137
138 final int[] samples = convertDataToSamples(0xff & bytes[i]);
139 final int towrite = Math.min(remaining, samplesPerByte);
140
141
142
143
144 final int written = processByteOfData(samples, towrite, x, y, width, height, imageBuilder);
145
146
147 x += written;
148 remaining -= written;
149 }
150 break;
151 }
152 }
153 } else {
154 final int[] rgbs = convertDataToSamples(b);
155
156 x += processByteOfData(rgbs, a, x, y, width, height, imageBuilder);
157 }
158 }
159 }
160 }