/* This file is part of the KDE libraries Copyright (C) 2003 Fredrik Höglund Copyright (C) 2005 Lubos Lunak This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "xcursor.h" #include #include #include #ifdef GV_HAVE_XCURSOR #include #include #include #endif namespace Gwenview { #ifdef GV_HAVE_XCURSOR class XCursorFormat : public TQImageFormat { public: XCursorFormat(); virtual int decode(TQImage& img, TQImageConsumer* consumer, const uchar* buffer, int length); TQByteArray array; int pos; bool was_seek_error; }; extern "C" int xcursor_read( XcursorFile* file, unsigned char* buf, int len ) { XCursorFormat* data = reinterpret_cast< XCursorFormat* >( file->closure ); if( int( data->array.size()) - data->pos < len ) { data->was_seek_error = true; len = data->array.size() - data->pos; } memcpy( buf, data->array.data() + data->pos, len ); data->pos += len; return len; } extern "C" int xcursor_write( XcursorFile*, unsigned char*, int ) { assert( false ); // not write support return 0; } extern "C" int xcursor_seek( XcursorFile* file, long offset, int whence ) { XCursorFormat* data = reinterpret_cast< XCursorFormat* >( file->closure ); if( whence == SEEK_CUR ) offset += data->pos; else if( whence == SEEK_END ) offset = data->array.size() + offset; if( offset < 0 || offset >= int( data->array.size())) { data->was_seek_error = true; return -1; } data->pos = offset; return 0; } XCursorFormat::XCursorFormat() { } int XCursorFormat::decode(TQImage& img, TQImageConsumer* cons, const uchar* buffer, int length) { if( length > 0 ) { int old_size = array.size(); array.resize( old_size + length ); memcpy( array.data() + old_size, buffer, length ); } // TQt's image async IO and Xcursor library have so nicely incompatible data reading :-/ // Xcursor can read a file only as a whole. One can provide XcursorFile with functions // for reading, writing and seeking. And seeking is the stupid part on Xcursor's side, // as there's no way to suspend reading until more data is available. This means we // basically have to read the whole file first. However, TQt's image async IO just keeps // feeding data and when there's end of the data it just does nothing and doesn't tell // the decoder. // So the trick will be calling XcursorXcFileLoadImage() but whenever there will be // a seeking/reading error, then the function will fail and we'll try again when there's // more data. Also, reading everything first means we can't return without processing // further data after reaching end of a frame in the input as decode() doc says, but oh well. XcursorFile file; file.closure = this; file.read = xcursor_read; file.write = xcursor_write; file.seek = xcursor_seek; pos = 0; was_seek_error = false; XcursorImages *cursors = XcursorXcFileLoadImages( &file, 1024 ); // largest possible cursor size if ( cursors ) { for( int cur = 0; cur < cursors->nimage; ++cur ) { XcursorImage* cursor = cursors->images[ cur ]; img = TQImage( reinterpret_cast( cursor->pixels ), cursor->width, cursor->height, 32, NULL, 0, TQImage::BigEndian ); img.setAlphaBuffer( true ); // Convert the image to non-premultiplied alpha TQ_UINT32 *pixels = reinterpret_cast( img.bits() ); for ( int i = 0; i < (img.width() * img.height()); i++ ) { float alpha = tqAlpha( pixels[i] ) / 255.0; if ( alpha > 0.0 && alpha < 1.0 ) pixels[i] = tqRgba( int( tqRed(pixels[i]) / alpha ), int( tqGreen(pixels[i]) / alpha ), int( tqBlue(pixels[i]) / alpha ), tqAlpha(pixels[i]) ); } // Create a deep copy of the image so the image data is preserved img = img.copy(); if( cons ) { if( cur == 0 ) { cons->setSize( img.width(), img.height()); if( cursors->nimage > 1 ) cons->setLooping( 0 ); } cons->changed( TQRect( TQPoint( 0, 0 ), img.size())); cons->frameDone(); cons->setFramePeriod( cursor->delay ); } } XcursorImagesDestroy( cursors ); if( cons ) cons->end(); return length; } else if( was_seek_error ) // try again next time return length; return -1; // failure } #endif TQImageFormat* XCursorFormatType::decoderFor( const uchar* buffer, int length) { #ifdef GV_HAVE_XCURSOR if (length < 4) return 0; if (buffer[0]=='X' && buffer[1]=='c' && buffer[2]=='u' && buffer[3]=='r') return new XCursorFormat; #else Q_UNUSED( buffer ); Q_UNUSED( length ); #endif return 0; } const char* XCursorFormatType::formatName() const { return "XCursor"; } } // namespace