1 | /** |
2 | * PIC_RW - Qt PIC Support |
3 | * Copyright (C) 2007 Ruben Lopez <r.lopez@bren.es> |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this library; if not, write to the Free Software |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
18 | * ---------------------------------------------------------------------------- |
19 | */ |
20 | |
21 | /* This code is based on the GIMP-PIC plugin by Halfdan Ingvarsson, |
22 | * and relicensed from GPL to LGPL to accomodate the KDE licensing policy |
23 | * with his permission. |
24 | * These is the original copyright: |
25 | * Copyright (C) 1998 Halfdan Ingvarsson |
26 | */ |
27 | |
28 | #include "pic_rw.h" |
29 | #include <netinet/in.h> |
30 | #include <iostream> |
31 | #include <qimage.h> |
32 | #include <algorithm> |
33 | |
34 | /** |
35 | * Reads the PIC header and checks that it is OK |
36 | * @param dev The QT device to read from |
37 | * @param hdr A pointer to the PIC header |
38 | * @param peek Keep bytes in the device |
39 | * @return true on success |
40 | */ |
41 | bool (QIODevice *dev, PICHeader *hdr, bool peek) { |
42 | int result = 0; |
43 | if (peek) { |
44 | result = dev->peek((char*) hdr, HEADER_SIZE); |
45 | } else { |
46 | result = dev->read((char*) hdr, HEADER_SIZE); |
47 | } |
48 | |
49 | hdr->magic = ntohl(hdr->magic); |
50 | hdr->width = ntohs(hdr->width); |
51 | hdr->height = ntohs(hdr->height); |
52 | hdr->fields = ntohs(hdr->fields); |
53 | |
54 | if (hdr->magic != PIC_MAGIC_NUMBER || strncmp(hdr->id, "PICT" , 4)) { |
55 | return false; |
56 | } |
57 | |
58 | return result == HEADER_SIZE; |
59 | } |
60 | |
61 | #define CHANNEL_BYTE(ch, mask) (( ch & mask) ? 1 : 0) |
62 | |
63 | /** |
64 | * Gets the channels definition and returns the number of bytes per pixel |
65 | * @param channels The channels bitfield |
66 | * @return The number of bytes per pixel |
67 | */ |
68 | static int channels2bpp(char channels) { |
69 | return CHANNEL_BYTE(channels, RED) |
70 | + CHANNEL_BYTE(channels, GREEN) |
71 | + CHANNEL_BYTE(channels, BLUE) |
72 | + CHANNEL_BYTE(channels, ALPHA); |
73 | } |
74 | |
75 | /** |
76 | * Reads the channels info |
77 | * @param dev The QT device to read from |
78 | * @param channels A pointer to 8 channels |
79 | * @return true on success |
80 | */ |
81 | static bool readChannels(QIODevice *dev, PICChannel *channels, int &bpp) { |
82 | int c = 0; |
83 | memset(channels, 0, sizeof ( PICChannel) *8); |
84 | do { |
85 | int result = dev->read((char*) & channels[c], CHANNEL_SIZE); |
86 | if (result != CHANNEL_SIZE) { |
87 | return false; |
88 | } else { |
89 | bpp += channels2bpp(channels[c].channel); |
90 | c++; |
91 | } |
92 | } while (channels[c - 1].chained); |
93 | return true; |
94 | } |
95 | |
96 | /** |
97 | * Makes a component map based on the channels info |
98 | * @param channels The channel information |
99 | * @param cmap The component map to be built |
100 | */ |
101 | inline static void makeComponentMap(unsigned channel, unsigned char *cmap) { |
102 | std::fill(cmap, cmap + 8, 0); |
103 | |
104 | unsigned compos[] = {ALPHA, BLUE, GREEN, RED}; |
105 | unsigned rgba[] = {3, 2, 1, 0}; |
106 | unsigned pos = 0; |
107 | for (unsigned compo = 0; compo < 4; compo++) { |
108 | if (CHANNEL_BYTE(channel, compos[compo])) { |
109 | cmap[pos++] = rgba[compo]; |
110 | } |
111 | } |
112 | } |
113 | |
114 | /** |
115 | * Converts a PIC pixel to 32bits RGBA |
116 | * @param src_pixel The source PIC pixel as readed from file |
117 | * @param target_pixel The target buffer where to write the pixel info |
118 | * @param cmap The component map that maps each component in PIC format to RGBA format |
119 | * @param components The number of components in the source pixel |
120 | */ |
121 | inline static void pic2RGBA(unsigned char *src_pixel, unsigned char *target_pixel, unsigned char *cmap, unsigned components) { |
122 | for (unsigned i = 0; i < components; i++) { |
123 | target_pixel[cmap[i]] = src_pixel[i]; |
124 | } |
125 | } |
126 | |
127 | /** |
128 | * Counts the number of channels in the PICChannel header |
129 | * @param channels The header |
130 | * @return The number of used channels |
131 | */ |
132 | inline static unsigned getNumChannels(PICChannel *channels) { |
133 | unsigned result = 0; |
134 | for (unsigned i = 0; i < 8; i++) { |
135 | if (channels[i].channel != 0) { |
136 | result++; |
137 | } else { |
138 | return result; |
139 | } |
140 | } |
141 | return result; |
142 | } |
143 | |
144 | /** |
145 | * Decodes a Run-lenght encoded chunk |
146 | * @param dev The device to read from |
147 | * @param row The row pointer to write to |
148 | * @param max The maximum length to write |
149 | * @param channels The channels header |
150 | * @return The number of generated pixels |
151 | */ |
152 | static int decodeRLE(QIODevice *dev, void *row, unsigned max, unsigned bpp, unsigned channels) { |
153 | unsigned char buf[512]; |
154 | unsigned *ptr = (unsigned *) row; |
155 | unsigned char component_map[8]; |
156 | unsigned len = 0; |
157 | |
158 | makeComponentMap(channels, component_map); |
159 | |
160 | if (dev->read((char*) buf, 1) != 1) { |
161 | return -1; |
162 | } |
163 | |
164 | /* If last bit is 1, then it is 2 to 127 repetitions */ |
165 | if (buf[0] > 128) { |
166 | len = buf[0] - 127; |
167 | if (len > max) { |
168 | return -1; |
169 | } |
170 | unsigned count = dev->read((char*) buf, bpp); |
171 | if (count != bpp) { |
172 | return -1; |
173 | } |
174 | for (unsigned i = 0; i < len; i++) { |
175 | pic2RGBA(buf, (unsigned char*) (ptr + i), component_map, bpp); |
176 | } |
177 | } /* If the value is exactly 10000000, it means that it is more than 127 repetitions */ |
178 | else if (buf[0] == 128) { |
179 | unsigned count = dev->read((char*) buf, bpp + 2); |
180 | if (count != bpp + 2) { |
181 | return -1; |
182 | } |
183 | len = (buf[0] << 8) | buf[1]; |
184 | if (len > max) { |
185 | return -1; |
186 | } |
187 | for (unsigned i = 0; i < len; i++) { |
188 | pic2RGBA(buf + 2, (unsigned char*) (ptr + i), component_map, bpp); |
189 | } |
190 | } /** No repetitions */ |
191 | else { |
192 | len = buf[0] + 1; |
193 | if (len > max) { |
194 | return -1; |
195 | } |
196 | unsigned count = dev->read((char*) buf, len * bpp); |
197 | if (count != len * bpp) { |
198 | return -1; |
199 | } |
200 | for (unsigned i = 0; i < len; i++) { |
201 | pic2RGBA(buf + (i * bpp), (unsigned char*) (ptr + i), component_map, bpp); |
202 | } |
203 | } |
204 | return len; |
205 | } |
206 | |
207 | /** |
208 | * Reads a row from the file |
209 | * @param dev The device to read from |
210 | * @param row The row pointer to write to |
211 | * @param width The image width |
212 | * @param bpp The bytes per pixel |
213 | * @param channels The channels header info |
214 | */ |
215 | static bool readRow(QIODevice *dev, unsigned *row, unsigned width, PICChannel *channels) { |
216 | for (int c = 0; channels[c].channel != 0; c++) { |
217 | unsigned remain = width; |
218 | unsigned bpp = channels2bpp(channels[c].channel); |
219 | if (channels[c].type == (int) RLE) { |
220 | unsigned *rowpos = row; |
221 | while (remain > 0) { |
222 | int readed = decodeRLE(dev, rowpos, remain, bpp, channels[c].channel); |
223 | if (readed < 0) { |
224 | return false; |
225 | } |
226 | remain -= readed; |
227 | rowpos += readed; |
228 | } |
229 | } else { |
230 | unsigned char component_map[8]; |
231 | unsigned count = dev->read((char*) row, width * bpp); |
232 | if (count != width * bpp) { |
233 | return false; |
234 | } |
235 | |
236 | makeComponentMap(channels[c].channel, component_map); |
237 | for (unsigned i = 0; i < width; i++) { |
238 | pic2RGBA(((unsigned char*) row) + (i * bpp), (unsigned char*) (row + i), component_map, bpp); |
239 | } |
240 | } |
241 | } |
242 | return true; |
243 | } |
244 | |
245 | #define FAIL() { \ |
246 | std::cout << "ERROR Reading PIC!" << std::endl; \ |
247 | return; \ |
248 | } |
249 | |
250 | bool hasAlpha(PICChannel *channels) { |
251 | int channel = 0; |
252 | do { |
253 | if (CHANNEL_BYTE(channels[channel].channel, ALPHA)) { |
254 | return true; |
255 | } |
256 | channel++; |
257 | } while (channels[channel - 1].chained); |
258 | return false; |
259 | } |
260 | |
261 | /** |
262 | * KDE image reading function. Must have this exact name in order to work |
263 | */ |
264 | void pic_read(QIODevice *dev, QImage *result) { |
265 | PICHeader ; |
266 | PICChannel channels[8]; |
267 | int bpp = 0; |
268 | if (!picReadHeader(dev, &header) || !readChannels(dev, channels, bpp)) { |
269 | FAIL(); |
270 | } |
271 | QImage img(header.width, header.height, QImage::Format_ARGB32); |
272 | |
273 | for (int r = 0; r < header.height; r++) { |
274 | unsigned *row = (unsigned*) img.scanLine(r); |
275 | std::fill(row, row + header.width, 0); |
276 | if (!readRow(dev, row, header.width, channels)) { |
277 | FAIL(); |
278 | } |
279 | } |
280 | // img->setAlphaBuffer(hasAlpha(channels)); |
281 | *result = img; |
282 | } |
283 | |