Warning: That file was not part of the compilation database. It may have many parsing errors.
1 | /**************************************************************************** |
---|---|
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the tools applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | |
42 | #include "qanimationwriter.h" |
43 | #include <QFile> |
44 | #include <QString> |
45 | #include <QPainter> |
46 | #include <png.h> |
47 | #include <limits.h> |
48 | #include <netinet/in.h> // for htonl |
49 | |
50 | #ifdef QT_LINUXBASE |
51 | # include <arpa/inet.h> // for htonl (LSB only) |
52 | #endif |
53 | |
54 | QT_BEGIN_NAMESPACE |
55 | |
56 | class QAnimationWriterData |
57 | { |
58 | public: |
59 | QAnimationWriterData(QIODevice* d) : framerate(1000), dev(d) {} |
60 | void setFrameRate(int d) { framerate = d; } |
61 | virtual ~QAnimationWriterData() { } |
62 | virtual void setImage(const QImage& src)=0; |
63 | virtual bool canCompose() const { return false; } |
64 | virtual void composeImage(const QImage&, const QPoint& ) {} |
65 | |
66 | protected: |
67 | int framerate; |
68 | QIODevice* dev; |
69 | }; |
70 | |
71 | |
72 | class QAnimationWriterMNG : public QAnimationWriterData { |
73 | bool first; |
74 | png_structp png_ptr; |
75 | png_infop info_ptr; |
76 | public: |
77 | QAnimationWriterMNG(QIODevice* d) : QAnimationWriterData(d) |
78 | { |
79 | first = true; |
80 | begin_png(); |
81 | } |
82 | |
83 | ~QAnimationWriterMNG() |
84 | { |
85 | if (first) { |
86 | // Eh? Not images. |
87 | QImage dummy(1,1,QImage::Format_RGB32); |
88 | setImage(dummy); |
89 | } |
90 | writeMEND(); |
91 | end_png(); |
92 | } |
93 | |
94 | void begin_png() |
95 | { |
96 | png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,0,0,0); |
97 | info_ptr = png_create_info_struct(png_ptr); |
98 | png_set_compression_level(png_ptr,9); |
99 | png_set_write_fn(png_ptr, (void*)this, write, 0); |
100 | } |
101 | |
102 | void end_png() |
103 | { |
104 | png_destroy_write_struct(&png_ptr, &info_ptr); |
105 | } |
106 | |
107 | static void write( png_structp png_ptr, png_bytep data, png_size_t length) |
108 | { |
109 | QAnimationWriterMNG* that = (QAnimationWriterMNG*)png_get_io_ptr(png_ptr); |
110 | /*uint nw =*/ that->dev->write((const char*)data,length); |
111 | } |
112 | |
113 | void writePNG(const QImage& image) |
114 | { |
115 | #if !defined(QT_LINUXBASE) && \ |
116 | (PNG_LIBPNG_VER_MAJOR < 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR <= 4)) |
117 | // LSB disallows accessing the info_ptr directly. LSB's png_set_IHDR sets |
118 | // the channels anyways, so just comment it out for LSB usage. |
119 | // In libpng >= 1.5, the png_info struct is no longer exported. |
120 | info_ptr->channels = 4; |
121 | #endif |
122 | png_set_sig_bytes(png_ptr, 8); // Pretend we already wrote the sig |
123 | png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(), |
124 | 8, image.hasAlphaChannel() |
125 | ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB, |
126 | 0, 0, 0); |
127 | png_write_info(png_ptr, info_ptr); |
128 | if (!image.hasAlphaChannel()) |
129 | png_set_filler(png_ptr, 0, |
130 | QSysInfo::ByteOrder == QSysInfo::BigEndian ? |
131 | PNG_FILLER_BEFORE : PNG_FILLER_AFTER); |
132 | //if ( QImage::systemByteOrder() == QImage::BigEndian ) { |
133 | //png_set_swap_alpha(png_ptr); |
134 | //} |
135 | if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { |
136 | png_set_bgr(png_ptr); |
137 | } |
138 | |
139 | int height = image.height(); |
140 | png_bytep *row_pointers = new png_bytep[height]; |
141 | for (int i = 0; i < height; ++i) |
142 | row_pointers[i] = (png_bytep)image.scanLine(i); |
143 | png_write_image(png_ptr, row_pointers); |
144 | delete [] row_pointers; |
145 | png_write_end(png_ptr, info_ptr); |
146 | end_png(); |
147 | begin_png(); |
148 | } |
149 | |
150 | void writeMHDR(const QSize& size, int framerate) |
151 | { |
152 | dev->write("\212MNG\r\n\032\n", 8); |
153 | |
154 | struct { |
155 | int width; |
156 | int height; |
157 | int framerate; |
158 | int a,b,c; |
159 | int profile; |
160 | } chunk; |
161 | chunk.width = htonl(size.width()); |
162 | chunk.height = htonl(size.height()); |
163 | chunk.framerate = htonl(framerate); |
164 | chunk.a=0; |
165 | chunk.b=0; |
166 | chunk.c=0; |
167 | chunk.profile = htonl(0x00000003); |
168 | |
169 | png_write_chunk(png_ptr, (png_byte*)"MHDR", (png_byte*)&chunk, sizeof(chunk)); |
170 | } |
171 | |
172 | void writeMEND() |
173 | { |
174 | png_write_chunk(png_ptr, (png_byte*)"MEND", 0, 0); |
175 | } |
176 | |
177 | void writeDEFI(const QPoint& offset, const QSize& /*size*/) |
178 | { |
179 | struct { |
180 | ushort o; |
181 | uchar s; |
182 | uchar concrete; |
183 | int x,y; |
184 | int lc,rc,tc,bc; |
185 | } chunk; |
186 | chunk.o=0; |
187 | chunk.s=0; |
188 | chunk.concrete=1; |
189 | chunk.x=htonl(offset.x()); |
190 | chunk.y=htonl(offset.y()); |
191 | chunk.lc=0; |
192 | chunk.rc=0; |
193 | chunk.tc=htonl(INT_MAX); |
194 | chunk.bc=htonl(INT_MAX); |
195 | |
196 | png_write_chunk(png_ptr, (png_byte*)"DEFI", (png_byte*)&chunk, sizeof(chunk)); |
197 | } |
198 | |
199 | void writeFRAM(const QSize& size) |
200 | { |
201 | struct { |
202 | uchar mode; |
203 | uchar n; |
204 | uchar nu; |
205 | uchar d; |
206 | uchar t; |
207 | uchar clip; |
208 | uchar s; |
209 | uchar deltatype; |
210 | uint left; |
211 | uint right; |
212 | uint top; |
213 | uint bottom; |
214 | } chunk; |
215 | chunk.mode=1; |
216 | chunk.n= |
217 | chunk.nu=0; |
218 | chunk.d=0; |
219 | chunk.clip=1; |
220 | chunk.t=0; |
221 | chunk.s=0; |
222 | chunk.deltatype=0; |
223 | chunk.left=0; |
224 | chunk.right=htonl(size.width()); |
225 | chunk.top=0; |
226 | chunk.bottom=htonl(size.height()); |
227 | |
228 | png_write_chunk(png_ptr, (png_byte*)"FRAM", (png_byte*)&chunk, sizeof(chunk)); |
229 | } |
230 | |
231 | void writeMOVE(const QPoint& offset) |
232 | { |
233 | struct { |
234 | uchar filler[3]; |
235 | uchar z[5]; |
236 | int x,y; |
237 | } chunk; |
238 | memset(chunk.z,0,5); |
239 | chunk.x=htonl(offset.x()); |
240 | chunk.y=htonl(offset.y()); |
241 | |
242 | png_write_chunk(png_ptr, (png_byte*)"MOVE", ((png_byte*)&chunk)+3, sizeof(chunk)-3); |
243 | } |
244 | |
245 | void setImage(const QImage& src) |
246 | { |
247 | if (first) { |
248 | first = false; |
249 | writeMHDR(src.size(),framerate); |
250 | } |
251 | composeImage(src,QPoint(0,0)); |
252 | } |
253 | |
254 | bool canCompose() const { return true; } |
255 | |
256 | void composeImage(const QImage& src, const QPoint& offset) |
257 | { |
258 | writeMOVE(offset); |
259 | //writeFRAM(src.size()); |
260 | writePNG(src); |
261 | } |
262 | }; |
263 | |
264 | QAnimationWriter::QAnimationWriter(const QString& filename, const char* format) |
265 | { |
266 | if (qstrncmp(format, "MNG", 4)) { |
267 | qWarning("Format \"%s\" not supported, only MNG", format); |
268 | dev = 0; |
269 | d = 0; |
270 | } else { |
271 | QFile *f = new QFile(filename); |
272 | f->open(QIODevice::WriteOnly); |
273 | dev = f; |
274 | d = new QAnimationWriterMNG(dev); |
275 | } |
276 | } |
277 | |
278 | bool QAnimationWriter::okay() const |
279 | { |
280 | if (!dev) |
281 | return false; |
282 | QFile *file = qobject_cast<QFile*>(dev); |
283 | Q_ASSERT(file); |
284 | return (file->error() == QFile::NoError); |
285 | } |
286 | |
287 | QAnimationWriter::~QAnimationWriter() |
288 | { |
289 | delete d; |
290 | delete dev; |
291 | } |
292 | |
293 | void QAnimationWriter::setFrameRate(int r) |
294 | { |
295 | if (d) |
296 | d->setFrameRate(r); |
297 | } |
298 | |
299 | void QAnimationWriter::appendFrame(const QImage& frm, const QPoint& offset) |
300 | { |
301 | if (!dev) |
302 | return; |
303 | |
304 | const QImage frame = frm.convertToFormat(QImage::Format_RGB32); |
305 | const int alignx = 1; |
306 | if (prev.isNull() || !d->canCompose()) { |
307 | d->setImage(frame); |
308 | } else { |
309 | bool done; |
310 | int minx, maxx, miny, maxy; |
311 | int w = frame.width(); |
312 | int h = frame.height(); |
313 | |
314 | const quint32 *framePtr = reinterpret_cast<const quint32*>(frame.bits()); |
315 | const quint32 *prevPtr = reinterpret_cast<const quint32*>(prev.bits()); |
316 | const int frameStride = frame.bytesPerLine() / sizeof(quint32); |
317 | const int prevStride = prev.bytesPerLine() / sizeof(quint32); |
318 | |
319 | // Find left edge of change |
320 | done = false; |
321 | for (minx = 0; minx < w && !done; ++minx) { |
322 | const quint32 *p1 = framePtr + minx; |
323 | const quint32 *p2 = prevPtr + minx + offset.x(); |
324 | for (int y = 0; y < h; ++y) { |
325 | if (*p1 != *p2) { |
326 | done = true; |
327 | break; |
328 | } |
329 | p1 += frameStride; |
330 | p2 += prevStride; |
331 | } |
332 | } |
333 | --minx; |
334 | |
335 | // Find right edge of change |
336 | done = false; |
337 | for (maxx = w-1; maxx >= 0 && !done; --maxx) { |
338 | const quint32 *p1 = framePtr + maxx; |
339 | const quint32 *p2 = prevPtr + maxx + offset.x(); |
340 | for (int y = 0; y < h; ++y) { |
341 | if (*p1 != *p2) { |
342 | done = true; |
343 | break; |
344 | } |
345 | p1 += frameStride; |
346 | p2 += prevStride; |
347 | } |
348 | } |
349 | ++maxx; |
350 | |
351 | // Find top edge of change |
352 | done = false; |
353 | for (miny = 0; miny < h && !done; ++miny) { |
354 | const quint32 *p1 = framePtr + miny * frameStride; |
355 | const quint32 *p2 = prevPtr + miny * prevStride + offset.x(); |
356 | for (int x = 0; x < w; ++x) { |
357 | if (*p1 != *p2) { |
358 | done = true; |
359 | break; |
360 | } |
361 | ++p1; |
362 | ++p2; |
363 | } |
364 | } |
365 | --miny; |
366 | |
367 | // Find right edge of change |
368 | done = false; |
369 | for (maxy = h-1; maxy >= 0 && !done; --maxy) { |
370 | const quint32 *p1 = framePtr + maxy * frameStride; |
371 | const quint32 *p2 = prevPtr + maxy * prevStride + offset.x(); |
372 | for (int x = 0; x < w; ++x) { |
373 | if (*p1 != *p2) { |
374 | done = true; |
375 | break; |
376 | } |
377 | ++p1; |
378 | ++p2; |
379 | } |
380 | } |
381 | ++maxy; |
382 | |
383 | if (minx > maxx) |
384 | minx = maxx = 0; |
385 | if (miny > maxy) |
386 | miny = maxy = 0; |
387 | |
388 | if (alignx > 1) { |
389 | minx -= minx % alignx; |
390 | maxx = maxx - maxx % alignx + alignx - 1; |
391 | } |
392 | |
393 | int dw = maxx - minx + 1; |
394 | int dh = maxy - miny + 1; |
395 | |
396 | QImage diff(dw, dh, QImage::Format_ARGB32); |
397 | |
398 | int x, y; |
399 | for (y = 0; y < dh; ++y) { |
400 | QRgb* li = (QRgb*)frame.scanLine(y+miny)+minx; |
401 | QRgb* lp = (QRgb*)prev.scanLine(y+miny+offset.y())+minx+offset.x(); |
402 | QRgb* ld = (QRgb*)diff.scanLine(y); |
403 | if (alignx) { |
404 | for (x = 0; x < dw; x += alignx) { |
405 | int i; |
406 | for (i = 0; i < alignx; ++i) { |
407 | if (li[x+i] != lp[x+i]) |
408 | break; |
409 | } |
410 | if (i == alignx) { |
411 | // All the same |
412 | for (i = 0; i < alignx; ++i) |
413 | ld[x+i] = qRgba(0,0,0,0); |
414 | } else { |
415 | // Some different |
416 | for (i = 0; i < alignx; ++i) |
417 | ld[x+i] = 0xff000000 | li[x+i]; |
418 | } |
419 | } |
420 | } else { |
421 | for (x = 0; x < dw; ++x) { |
422 | if (li[x] != lp[x]) |
423 | ld[x] = 0xff000000 | li[x]; |
424 | else |
425 | ld[x] = qRgba(0,0,0,0); |
426 | } |
427 | } |
428 | } |
429 | |
430 | d->composeImage(diff, QPoint(minx, miny) + offset); |
431 | } |
432 | if (prev.isNull() || (prev.size() == frame.size() && offset == QPoint(0,0))) { |
433 | prev = frame; |
434 | } else { |
435 | QPainter p(&prev); |
436 | p.drawImage(offset.x(), offset.y(), frame, 0, 0, |
437 | frame.width(), frame.height()); |
438 | } |
439 | } |
440 | |
441 | void QAnimationWriter::appendFrame(const QImage& frm) |
442 | { |
443 | appendFrame(frm, QPoint(0,0)); |
444 | } |
445 | |
446 | void QAnimationWriter::appendBlankFrame() |
447 | { |
448 | QImage i(1,1,QImage::Format_ARGB32); |
449 | i.fill(0); |
450 | d->composeImage(i, QPoint(0,0)); |
451 | } |
452 | |
453 | QT_END_NAMESPACE |
454 |
Warning: That file was not part of the compilation database. It may have many parsing errors.