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 | |
33 | /** |
34 | * Writes the PIC header info. |
35 | * @param dev IO Device |
36 | * @param msg Header message |
37 | * @param width Image width |
38 | * @param height Image height |
39 | * @param alpha Image has alpha? |
40 | * @return True on success |
41 | */ |
42 | static bool (QIODevice *dev, std::string msg, unsigned width, unsigned height, bool alpha) { |
43 | PICHeader h; |
44 | PICChannel c; |
45 | unsigned count = 0; |
46 | |
47 | memset(&h, 0, sizeof (PICHeader)); |
48 | h.magic = htonl(PIC_MAGIC_NUMBER); |
49 | h.version = 3.71f; |
50 | strcpy(h.comment, msg.c_str()); |
51 | strncpy(h.id, "PICT" , 4); |
52 | h.width = htons(width); |
53 | h.height = htons(height); |
54 | h.ratio = 1.0f; |
55 | h.fields = htons(BOTH); |
56 | count = dev->write((const char*) & h, sizeof (PICHeader)); |
57 | if (count != sizeof (PICHeader)) { |
58 | return false; |
59 | } |
60 | |
61 | memset(&c, 0, sizeof (PICChannel)); |
62 | c.size = 8; |
63 | c.type = RLE; |
64 | c.channel = RED | GREEN | BLUE; |
65 | if (alpha) { |
66 | c.chained = 1; |
67 | } |
68 | count = dev->write((const char*) & c, sizeof (PICChannel)); |
69 | if (count != sizeof (PICChannel)) { |
70 | return false; |
71 | } |
72 | |
73 | if (alpha) { |
74 | c.channel = ALPHA; |
75 | c.chained = 0; |
76 | count = dev->write((const char*) & c, sizeof (PICChannel)); |
77 | if (count != sizeof (PICChannel)) { |
78 | return false; |
79 | } |
80 | } |
81 | return true; |
82 | } |
83 | |
84 | inline unsigned convertABGRtoRGBA(unsigned pixel) { |
85 | unsigned r = pixel & 0xFF; |
86 | unsigned g = (pixel >> 8) & 0xFF; |
87 | unsigned b = (pixel >> 16) & 0xFF; |
88 | unsigned a = (pixel >> 24) & 0xFF; |
89 | return a | (b << 8) | (g << 16) | (r << 24); |
90 | } |
91 | |
92 | /** |
93 | * Encodes a portion of the image in RLE coding |
94 | * @param image The image that we want to encode |
95 | * @param output The output buffer |
96 | * @param channels The number of channels to write |
97 | * @param offset Offset in bytes to copy |
98 | * @param max The maximum number of pixels to write |
99 | * @param oConsumed The number of pixels consumed from image |
100 | * @param oProduced The number of bytes produced in out |
101 | * @return True on success |
102 | */ |
103 | static bool encodeRLE(const unsigned *image, unsigned char *output, bool rgb, unsigned max, unsigned &oConsumed, unsigned &oProduced) { |
104 | const unsigned *in = image; |
105 | unsigned char *out = output; |
106 | unsigned count = 0; |
107 | unsigned channels = 3; |
108 | unsigned offset = 1; |
109 | unsigned mask = 0x00FFFFFF; |
110 | if (!rgb) { |
111 | channels = 1; |
112 | offset = 0; |
113 | mask = 0xFF000000; |
114 | } |
115 | for (; (*in & mask) == (*image & mask) && count < 65536 && count < max; in++, count++) { |
116 | } |
117 | if (count > 127) { |
118 | /* Sequence of > 127 identical pixels */ |
119 | *out++ = 128; |
120 | *out++ = count >> 8; |
121 | *out++ = count & 0xFF; |
122 | unsigned pixel = convertABGRtoRGBA(*image); |
123 | memcpy(out, ((char*) & pixel) + offset, channels); |
124 | out += channels; |
125 | oConsumed = count; |
126 | oProduced = out - output; |
127 | } |
128 | else if (count > 1) { |
129 | /* Sequece of < 128 identical pixels */ |
130 | *out++ = (count + 127); |
131 | unsigned pixel = convertABGRtoRGBA(*image); |
132 | memcpy(out, ((char*) & pixel) + offset, channels); |
133 | out += channels; |
134 | oConsumed = count; |
135 | oProduced = out - output; |
136 | } |
137 | else { |
138 | in = image + 1; |
139 | unsigned previous = *image; |
140 | count = 0; |
141 | while ((*in & mask) != (previous & mask) && count < 128 && count < max) { |
142 | previous = *in; |
143 | in++; |
144 | count++; |
145 | } |
146 | // This only happens when it is the end of the row, and it is ok |
147 | if (count == 0) { |
148 | count = 1; |
149 | } |
150 | *out++ = (count - 1); |
151 | in = image; |
152 | for (unsigned c = 0; c < count; ++c) { |
153 | unsigned pixel = convertABGRtoRGBA(*in); |
154 | memcpy(out, ((char*) & pixel) + offset, channels); |
155 | out += channels; |
156 | in++; |
157 | } |
158 | oConsumed = count; |
159 | oProduced = out - output; |
160 | } |
161 | return true; |
162 | } |
163 | |
164 | /** |
165 | * Writes a row to the file |
166 | * @return True on success |
167 | */ |
168 | static bool writeRow(QIODevice *dev, unsigned *row, unsigned width, bool alpha) { |
169 | unsigned char *buf = new unsigned char[width * 4]; |
170 | unsigned posIn = 0; |
171 | unsigned posOut = 0; |
172 | |
173 | memset(buf, 0, width * 4); |
174 | |
175 | unsigned consumed = 0; |
176 | unsigned produced = 0; |
177 | |
178 | /* Write the RGB part of the scanline */ |
179 | while (posIn < width) { |
180 | if (!encodeRLE(row + posIn, buf + posOut, true, width - posIn, consumed, produced)) { |
181 | delete[] buf; |
182 | return false; |
183 | } |
184 | posIn += consumed; |
185 | posOut += produced; |
186 | } |
187 | |
188 | /* Write the alpha channel */ |
189 | if (alpha) { |
190 | posIn = 0; |
191 | while (posIn < width) { |
192 | if (!encodeRLE(row + posIn, buf + posOut, false, width - posIn, consumed, produced)) { |
193 | delete[] buf; |
194 | return false; |
195 | } |
196 | posIn += consumed; |
197 | posOut += produced; |
198 | } |
199 | } |
200 | |
201 | dev->write((const char*) buf, posOut); |
202 | delete[] buf; |
203 | return true; |
204 | } |
205 | |
206 | #define FAIL() { \ |
207 | std::cout << "ERROR Writing PIC!" << std::endl; \ |
208 | return; \ |
209 | } |
210 | |
211 | /// Pic write handler for Qt / KDE |
212 | |
213 | void pic_write(QIODevice *dev, const QImage *img) { |
214 | bool alpha = img->hasAlphaChannel(); |
215 | if (!writeHeader(dev, "Created with KDE" , img->width(), img->height(), alpha)) { |
216 | FAIL(); |
217 | } |
218 | |
219 | for (int r = 0; r < img->height(); r++) { |
220 | unsigned *row = (unsigned*) img->scanLine(r); |
221 | if (!writeRow(dev, row, img->width(), alpha)) { |
222 | FAIL(); |
223 | } |
224 | } |
225 | } |
226 | |