1 | /** |
2 | * QImageIO Routines to read/write XV images. |
3 | * copyright (c) 1998 Torben Weis <weis@kde.org> |
4 | * copyright (c) 1999 Oliver Eiden <o.eiden@pop.ruhr.de> |
5 | * |
6 | * This library is distributed under the conditions of the GNU LGPL. |
7 | */ |
8 | |
9 | #include "xview.h" |
10 | |
11 | #include <stdio.h> |
12 | #include <string.h> |
13 | #include <stdlib.h> |
14 | #include <QtGui/QImage> |
15 | |
16 | #define BUFSIZE 1024 |
17 | |
18 | static const int b_255_3[]= {0,85,170,255}, // index*255/3 |
19 | rg_255_7[]={0,36,72,109,145,182,218,255}; // index *255/7 |
20 | |
21 | |
22 | XVHandler::XVHandler() |
23 | { |
24 | } |
25 | |
26 | bool XVHandler::canRead() const |
27 | { |
28 | if (canRead(device())) { |
29 | setFormat("xv" ); |
30 | return true; |
31 | } |
32 | return false; |
33 | } |
34 | |
35 | bool XVHandler::read(QImage *retImage) |
36 | { |
37 | int x=-1; |
38 | int y=-1; |
39 | int maxval=-1; |
40 | QIODevice *iodev = device(); |
41 | |
42 | char str[ BUFSIZE ]; |
43 | |
44 | // magic number must be "P7 332" |
45 | iodev->readLine( str, BUFSIZE ); |
46 | if (strncmp(str,"P7 332" ,6)) |
47 | return false; |
48 | |
49 | // next line #XVVERSION |
50 | iodev->readLine( str, BUFSIZE ); |
51 | if (strncmp(str, "#XVVERSION" , 10)) |
52 | return false; |
53 | |
54 | // now it gets interesting, #BUILTIN means we are out. |
55 | // if IMGINFO comes, we are happy! |
56 | iodev->readLine( str, BUFSIZE ); |
57 | if (strncmp(str, "#IMGINFO:" , 9)) |
58 | return false; |
59 | |
60 | // after this an #END_OF_COMMENTS signals everything to be ok! |
61 | iodev->readLine( str, BUFSIZE ); |
62 | if (strncmp(str, "#END_OF" , 7)) |
63 | return false; |
64 | |
65 | // now a last line with width, height, maxval which is |
66 | // supposed to be 255 |
67 | iodev->readLine( str, BUFSIZE ); |
68 | sscanf(str, "%d %d %d" , &x, &y, &maxval); |
69 | |
70 | if (maxval != 255) |
71 | return false; |
72 | int blocksize = x*y; |
73 | if(x < 0 || y < 0 || blocksize < x || blocksize < y) |
74 | return false; |
75 | |
76 | // now follows a binary block of x*y bytes. |
77 | char *block = (char*) malloc(blocksize); |
78 | if(!block) |
79 | return false; |
80 | |
81 | if (iodev->read(block, blocksize) != blocksize ) |
82 | { |
83 | free(block); |
84 | return false; |
85 | } |
86 | |
87 | // Create the image |
88 | QImage image( x, y, QImage::Format_Indexed8 ); |
89 | int numColors; |
90 | numColors = qMin( maxval + 1, 0 ); |
91 | numColors = qMax( 0, maxval + 1 ); |
92 | image.setNumColors( numColors ); |
93 | |
94 | // how do the color handling? they are absolute 24bpp |
95 | // or at least can be calculated as such. |
96 | int r,g,b; |
97 | |
98 | for ( int j = 0; j < 256; j++ ) |
99 | { |
100 | r = rg_255_7[((j >> 5) & 0x07)]; |
101 | g = rg_255_7[((j >> 2) & 0x07)]; |
102 | b = b_255_3[((j >> 0) & 0x03)]; |
103 | image.setColor( j, qRgb( r, g, b ) ); |
104 | } |
105 | |
106 | for ( int py = 0; py < y; py++ ) |
107 | { |
108 | uchar *data = image.scanLine( py ); |
109 | memcpy( data, block + py * x, x ); |
110 | } |
111 | |
112 | *retImage = image; |
113 | |
114 | free(block); |
115 | return true; |
116 | } |
117 | |
118 | bool XVHandler::write(const QImage &image) |
119 | { |
120 | QIODevice& f = *( device() ); |
121 | |
122 | // Removed "f.open(...)" and "f.close()" (tanghus) |
123 | |
124 | int w = image.width(), h = image.height(); |
125 | |
126 | char str[ 1024 ]; |
127 | |
128 | // magic number must be "P7 332" |
129 | f.write( "P7 332\n" , 7 ); |
130 | |
131 | // next line #XVVERSION |
132 | f.write( "#XVVERSION:\n" , 12 ); |
133 | |
134 | // now it gets interesting, #BUILTIN means we are out. |
135 | // if IMGINFO comes, we are happy! |
136 | f.write( "#IMGINFO:\n" , 10 ); |
137 | |
138 | // after this an #END_OF_COMMENTS signals everything to be ok! |
139 | f.write( "#END_OF_COMMENTS:\n" , 18 ); |
140 | |
141 | // now a last line with width, height, maxval which is supposed to be 255 |
142 | sprintf( str, "%i %i 255\n" , w, h ); |
143 | f.write( str, strlen( str ) ); |
144 | |
145 | |
146 | QImage tmpImage( image ); |
147 | if ( image.depth() == 1 ) |
148 | tmpImage = image.convertToFormat( QImage::Format_Indexed8, Qt::AutoColor ); |
149 | |
150 | uchar* buffer = new uchar[ w ]; |
151 | |
152 | for ( int py = 0; py < h; py++ ) |
153 | { |
154 | const uchar *data = tmpImage.scanLine( py ); |
155 | for ( int px = 0; px < w; px++ ) |
156 | { |
157 | int r, g, b; |
158 | if ( tmpImage.depth() == 32 ) |
159 | { |
160 | const QRgb *data32 = (QRgb*) data; |
161 | r = qRed( *data32 ) >> 5; |
162 | g = qGreen( *data32 ) >> 5; |
163 | b = qBlue( *data32 ) >> 6; |
164 | data += sizeof( QRgb ); |
165 | } |
166 | else |
167 | { |
168 | QRgb color = tmpImage.color( *data ); |
169 | r = qRed( color ) >> 5; |
170 | g = qGreen( color ) >> 5; |
171 | b = qBlue( color ) >> 6; |
172 | data++; |
173 | } |
174 | buffer[ px ] = ( r << 5 ) | ( g << 2 ) | b; |
175 | } |
176 | f.write( (const char*)buffer, w ); |
177 | } |
178 | delete[] buffer; |
179 | |
180 | return true; |
181 | } |
182 | |
183 | QByteArray XVHandler::name() const |
184 | { |
185 | return "xv" ; |
186 | } |
187 | |
188 | bool XVHandler::canRead(QIODevice *device) |
189 | { |
190 | if (!device) { |
191 | qWarning("XVHandler::canRead() called with no device" ); |
192 | return false; |
193 | } |
194 | |
195 | qint64 oldPos = device->pos(); |
196 | |
197 | char head[6]; |
198 | qint64 readBytes = device->read(head, sizeof(head)); |
199 | if (readBytes != sizeof(head)) { |
200 | if (device->isSequential()) { |
201 | while (readBytes > 0) |
202 | device->ungetChar(head[readBytes-- - 1]); |
203 | } else { |
204 | device->seek(oldPos); |
205 | } |
206 | return false; |
207 | } |
208 | |
209 | if (device->isSequential()) { |
210 | while (readBytes > 0) |
211 | device->ungetChar(head[readBytes-- - 1]); |
212 | } else { |
213 | device->seek(oldPos); |
214 | } |
215 | |
216 | return qstrncmp(head, "P7 332" , 6) == 0; |
217 | } |
218 | |
219 | |
220 | class XVPlugin : public QImageIOPlugin |
221 | { |
222 | public: |
223 | QStringList keys() const; |
224 | Capabilities capabilities(QIODevice *device, const QByteArray &format) const; |
225 | QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; |
226 | }; |
227 | |
228 | QStringList XVPlugin::keys() const |
229 | { |
230 | return QStringList() << "xv" ; |
231 | } |
232 | |
233 | QImageIOPlugin::Capabilities XVPlugin::capabilities(QIODevice *device, const QByteArray &format) const |
234 | { |
235 | if (format == "xv" ) |
236 | return Capabilities(CanRead | CanWrite); |
237 | if (!format.isEmpty()) |
238 | return 0; |
239 | if (!device->isOpen()) |
240 | return 0; |
241 | |
242 | Capabilities cap; |
243 | if (device->isReadable() && XVHandler::canRead(device)) |
244 | cap |= CanRead; |
245 | if (device->isWritable()) |
246 | cap |= CanWrite; |
247 | return cap; |
248 | } |
249 | |
250 | QImageIOHandler *XVPlugin::create(QIODevice *device, const QByteArray &format) const |
251 | { |
252 | QImageIOHandler *handler = new XVHandler; |
253 | handler->setDevice(device); |
254 | handler->setFormat(format); |
255 | return handler; |
256 | } |
257 | |
258 | Q_EXPORT_STATIC_PLUGIN(XVPlugin) |
259 | Q_EXPORT_PLUGIN2(xv, XVPlugin) |
260 | |