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
18static 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
22XVHandler::XVHandler()
23{
24}
25
26bool XVHandler::canRead() const
27{
28 if (canRead(device())) {
29 setFormat("xv");
30 return true;
31 }
32 return false;
33}
34
35bool 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
118bool 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
183QByteArray XVHandler::name() const
184{
185 return "xv";
186}
187
188bool 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
220class XVPlugin : public QImageIOPlugin
221{
222public:
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
228QStringList XVPlugin::keys() const
229{
230 return QStringList() << "xv";
231}
232
233QImageIOPlugin::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
250QImageIOHandler *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
258Q_EXPORT_STATIC_PLUGIN(XVPlugin)
259Q_EXPORT_PLUGIN2(xv, XVPlugin)
260