You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gwenview/src/gvcore/xcursor.cpp

191 lines
6.0 KiB

/* This file is part of the KDE libraries
Copyright (C) 2003 Fredrik Höglund <fredrik@kde.org>
Copyright (C) 2005 Lubos Lunak <l.lunak@kde.org>
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 <config.h>
#include <assert.h>
#include "xcursor.h"
#include <tqimage.h>
#include <kdebug.h>
#include <tqvaluevector.h>
#ifdef GV_HAVE_XCURSOR
#include <X11/Xlib.h>
#include <X11/Xcursor/Xcursor.h>
#include <fixx11h.h>
#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<uchar *>( 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<TQ_UINT32 *>( 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