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 */
42static bool writeHeader(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
84inline 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 */
103static 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 */
168static 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
213void 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