tools/qvfb/qanimationwriter.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the tools applications of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    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 #ifndef QT_LINUXBASE
       
   116         // LSB disallows accessing the info_ptr directly. LSB's png_set_IHDR sets
       
   117         // the channels anyways, so just comment it out for LSB usage
       
   118         info_ptr->channels = 4;
       
   119 #endif
       
   120         png_set_sig_bytes(png_ptr, 8); // Pretend we already wrote the sig
       
   121         png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(),
       
   122                      8, image.hasAlphaChannel()
       
   123                      ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB,
       
   124                      0, 0, 0);
       
   125         png_write_info(png_ptr, info_ptr);
       
   126         if (!image.hasAlphaChannel())
       
   127             png_set_filler(png_ptr, 0,
       
   128                            QSysInfo::ByteOrder == QSysInfo::BigEndian ?
       
   129                            PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
       
   130         //if ( QImage::systemByteOrder() == QImage::BigEndian ) {
       
   131         //png_set_swap_alpha(png_ptr);
       
   132         //}
       
   133         if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
       
   134             png_set_bgr(png_ptr);
       
   135         }
       
   136 
       
   137         int height = image.height();
       
   138         png_bytep *row_pointers = new png_bytep[height];
       
   139         for (int i = 0; i < height; ++i)
       
   140             row_pointers[i] = (png_bytep)image.scanLine(i);
       
   141         png_write_image(png_ptr, row_pointers);
       
   142         delete [] row_pointers;
       
   143         png_write_end(png_ptr, info_ptr);
       
   144         end_png();
       
   145         begin_png();
       
   146     }
       
   147 
       
   148     void writeMHDR(const QSize& size, int framerate)
       
   149     {
       
   150         dev->write("\212MNG\r\n\032\n", 8);
       
   151 
       
   152         struct {
       
   153             int width;
       
   154             int height;
       
   155             int framerate;
       
   156             int a,b,c;
       
   157             int profile;
       
   158         } chunk;
       
   159         chunk.width = htonl(size.width());
       
   160         chunk.height = htonl(size.height());
       
   161         chunk.framerate = htonl(framerate);
       
   162         chunk.a=0;
       
   163         chunk.b=0;
       
   164         chunk.c=0;
       
   165         chunk.profile = htonl(0x00000003);
       
   166 
       
   167         png_write_chunk(png_ptr, (png_byte*)"MHDR", (png_byte*)&chunk, sizeof(chunk));
       
   168     }
       
   169 
       
   170     void writeMEND()
       
   171     {
       
   172         png_write_chunk(png_ptr, (png_byte*)"MEND", 0, 0);
       
   173     }
       
   174 
       
   175     void writeDEFI(const QPoint& offset, const QSize& /*size*/)
       
   176     {
       
   177         struct {
       
   178             ushort o;
       
   179             uchar s;
       
   180             uchar concrete;
       
   181             int x,y;
       
   182             int lc,rc,tc,bc;
       
   183         } chunk;
       
   184         chunk.o=0;
       
   185         chunk.s=0;
       
   186         chunk.concrete=1;
       
   187         chunk.x=htonl(offset.x());
       
   188         chunk.y=htonl(offset.y());
       
   189         chunk.lc=0;
       
   190         chunk.rc=0;
       
   191         chunk.tc=htonl(INT_MAX);
       
   192         chunk.bc=htonl(INT_MAX);
       
   193 
       
   194         png_write_chunk(png_ptr, (png_byte*)"DEFI", (png_byte*)&chunk, sizeof(chunk));
       
   195     }
       
   196 
       
   197     void writeFRAM(const QSize& size)
       
   198     {
       
   199         struct {
       
   200             uchar mode;
       
   201             uchar n;
       
   202             uchar nu;
       
   203             uchar d;
       
   204             uchar t;
       
   205             uchar clip;
       
   206             uchar s;
       
   207             uchar deltatype;
       
   208             uint left;
       
   209             uint right;
       
   210             uint top;
       
   211             uint bottom;
       
   212         } chunk;
       
   213         chunk.mode=1;
       
   214         chunk.n='a';
       
   215         chunk.nu=0;
       
   216         chunk.d=0;
       
   217         chunk.clip=1;
       
   218         chunk.t=0;
       
   219         chunk.s=0;
       
   220         chunk.deltatype=0;
       
   221         chunk.left=0;
       
   222         chunk.right=htonl(size.width());
       
   223         chunk.top=0;
       
   224         chunk.bottom=htonl(size.height());
       
   225 
       
   226         png_write_chunk(png_ptr, (png_byte*)"FRAM", (png_byte*)&chunk, sizeof(chunk));
       
   227     }
       
   228 
       
   229     void writeMOVE(const QPoint& offset)
       
   230     {
       
   231         struct {
       
   232             uchar filler[3];
       
   233             uchar z[5];
       
   234             int x,y;
       
   235         } chunk;
       
   236         memset(chunk.z,0,5);
       
   237         chunk.x=htonl(offset.x());
       
   238         chunk.y=htonl(offset.y());
       
   239 
       
   240         png_write_chunk(png_ptr, (png_byte*)"MOVE", ((png_byte*)&chunk)+3, sizeof(chunk)-3);
       
   241     }
       
   242 
       
   243     void setImage(const QImage& src)
       
   244     {
       
   245         if (first) {
       
   246             first = false;
       
   247             writeMHDR(src.size(),framerate);
       
   248         }
       
   249         composeImage(src,QPoint(0,0));
       
   250     }
       
   251 
       
   252     bool canCompose() const { return true; }
       
   253 
       
   254     void composeImage(const QImage& src, const QPoint& offset)
       
   255     {
       
   256         writeMOVE(offset);
       
   257         //writeFRAM(src.size());
       
   258         writePNG(src);
       
   259     }
       
   260 };
       
   261 
       
   262 QAnimationWriter::QAnimationWriter(const QString& filename, const char* format)
       
   263 {
       
   264     if (qstrncmp(format, "MNG", 4)) {
       
   265 	qWarning("Format \"%s\" not supported, only MNG", format);
       
   266 	dev = 0;
       
   267 	d = 0;
       
   268     } else {
       
   269 	QFile *f = new QFile(filename);
       
   270 	f->open(QIODevice::WriteOnly);
       
   271 	dev = f;
       
   272 	d = new QAnimationWriterMNG(dev);
       
   273     }
       
   274 }
       
   275 
       
   276 bool QAnimationWriter::okay() const
       
   277 {
       
   278     if (!dev)
       
   279         return false;
       
   280     QFile *file = qobject_cast<QFile*>(dev);
       
   281     Q_ASSERT(file);
       
   282     return (file->error() == QFile::NoError);
       
   283 }
       
   284 
       
   285 QAnimationWriter::~QAnimationWriter()
       
   286 {
       
   287     delete d;
       
   288     delete dev;
       
   289 }
       
   290 
       
   291 void QAnimationWriter::setFrameRate(int r)
       
   292 {
       
   293     if (d)
       
   294         d->setFrameRate(r);
       
   295 }
       
   296 
       
   297 void QAnimationWriter::appendFrame(const QImage& frm, const QPoint& offset)
       
   298 {
       
   299     if (!dev)
       
   300         return;
       
   301 
       
   302     const QImage frame = frm.convertToFormat(QImage::Format_RGB32);
       
   303     const int alignx = 1;
       
   304     if (prev.isNull() || !d->canCompose()) {
       
   305         d->setImage(frame);
       
   306     } else {
       
   307         bool done;
       
   308         int minx, maxx, miny, maxy;
       
   309         int w = frame.width();
       
   310         int h = frame.height();
       
   311 
       
   312         const quint32 *framePtr = reinterpret_cast<const quint32*>(frame.bits());
       
   313         const quint32 *prevPtr = reinterpret_cast<const quint32*>(prev.bits());
       
   314         const int frameStride = frame.bytesPerLine() / sizeof(quint32);
       
   315         const int prevStride = prev.bytesPerLine() / sizeof(quint32);
       
   316 
       
   317         // Find left edge of change
       
   318         done = false;
       
   319         for (minx = 0; minx < w && !done; ++minx) {
       
   320             const quint32 *p1 = framePtr + minx;
       
   321             const quint32 *p2 = prevPtr + minx + offset.x();
       
   322             for (int y = 0; y < h; ++y) {
       
   323                 if (*p1 != *p2) {
       
   324                     done = true;
       
   325                     break;
       
   326                 }
       
   327                 p1 += frameStride;
       
   328                 p2 += prevStride;
       
   329             }
       
   330         }
       
   331         --minx;
       
   332 
       
   333         // Find right edge of change
       
   334         done = false;
       
   335         for (maxx = w-1; maxx >= 0 && !done; --maxx) {
       
   336             const quint32 *p1 = framePtr + maxx;
       
   337             const quint32 *p2 = prevPtr + maxx + offset.x();
       
   338             for (int y = 0; y < h; ++y) {
       
   339                 if (*p1 != *p2) {
       
   340                     done = true;
       
   341                     break;
       
   342                 }
       
   343                 p1 += frameStride;
       
   344                 p2 += prevStride;
       
   345             }
       
   346         }
       
   347         ++maxx;
       
   348 
       
   349         // Find top edge of change
       
   350         done = false;
       
   351         for (miny = 0; miny < h && !done; ++miny) {
       
   352             const quint32 *p1 = framePtr + miny * frameStride;
       
   353             const quint32 *p2 = prevPtr + miny * prevStride + offset.x();
       
   354             for (int x = 0; x < w; ++x) {
       
   355                 if (*p1 != *p2) {
       
   356                     done = true;
       
   357                     break;
       
   358                 }
       
   359                 ++p1;
       
   360                 ++p2;
       
   361             }
       
   362         }
       
   363         --miny;
       
   364 
       
   365         // Find right edge of change
       
   366         done = false;
       
   367         for (maxy = h-1; maxy >= 0 && !done; --maxy) {
       
   368             const quint32 *p1 = framePtr + maxy * frameStride;
       
   369             const quint32 *p2 = prevPtr + maxy * prevStride + offset.x();
       
   370             for (int x = 0; x < w; ++x) {
       
   371                 if (*p1 != *p2) {
       
   372                     done = true;
       
   373                     break;
       
   374                 }
       
   375                 ++p1;
       
   376                 ++p2;
       
   377             }
       
   378         }
       
   379         ++maxy;
       
   380 
       
   381         if (minx > maxx)
       
   382             minx = maxx = 0;
       
   383         if (miny > maxy)
       
   384             miny = maxy = 0;
       
   385 
       
   386         if (alignx > 1) {
       
   387             minx -= minx % alignx;
       
   388             maxx = maxx - maxx % alignx + alignx - 1;
       
   389         }
       
   390 
       
   391         int dw = maxx - minx + 1;
       
   392         int dh = maxy - miny + 1;
       
   393 
       
   394         QImage diff(dw, dh, QImage::Format_ARGB32);
       
   395 
       
   396         int x, y;
       
   397         for (y = 0; y < dh; ++y) {
       
   398             QRgb* li = (QRgb*)frame.scanLine(y+miny)+minx;
       
   399             QRgb* lp = (QRgb*)prev.scanLine(y+miny+offset.y())+minx+offset.x();
       
   400             QRgb* ld = (QRgb*)diff.scanLine(y);
       
   401             if (alignx) {
       
   402                 for (x = 0; x < dw; x += alignx) {
       
   403                     int i;
       
   404                     for (i = 0; i < alignx; ++i) {
       
   405                         if (li[x+i] != lp[x+i])
       
   406                             break;
       
   407                     }
       
   408                     if (i == alignx) {
       
   409                         // All the same
       
   410                         for (i = 0; i < alignx; ++i)
       
   411                             ld[x+i] = qRgba(0,0,0,0);
       
   412                     } else {
       
   413                         // Some different
       
   414                         for (i = 0; i < alignx; ++i)
       
   415                             ld[x+i] = 0xff000000 | li[x+i];
       
   416                     }
       
   417                 }
       
   418             } else {
       
   419                 for (x = 0; x < dw; ++x) {
       
   420                     if (li[x] != lp[x])
       
   421                         ld[x] = 0xff000000 | li[x];
       
   422                     else
       
   423                         ld[x] = qRgba(0,0,0,0);
       
   424                 }
       
   425             }
       
   426         }
       
   427 
       
   428         d->composeImage(diff, QPoint(minx, miny) + offset);
       
   429     }
       
   430     if (prev.isNull() || (prev.size() == frame.size() && offset == QPoint(0,0))) {
       
   431         prev = frame;
       
   432     } else {
       
   433         QPainter p(&prev);
       
   434         p.drawImage(offset.x(), offset.y(), frame, 0, 0,
       
   435                     frame.width(), frame.height());
       
   436     }
       
   437 }
       
   438 
       
   439 void QAnimationWriter::appendFrame(const QImage& frm)
       
   440 {
       
   441     appendFrame(frm, QPoint(0,0));
       
   442 }
       
   443 
       
   444 void QAnimationWriter::appendBlankFrame()
       
   445 {
       
   446     QImage i(1,1,QImage::Format_ARGB32);
       
   447     i.fill(0);
       
   448     d->composeImage(i, QPoint(0,0));
       
   449 }
       
   450 
       
   451 QT_END_NAMESPACE