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.
630 lines
15 KiB
630 lines
15 KiB
/*
|
|
Copyright (c) 2002 Malte Starostik <malte@kde.org>
|
|
(c) 2002,2003 Maksim Orlovich <maksim@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.
|
|
*/
|
|
|
|
// $Id$
|
|
|
|
#include <qapplication.h>
|
|
#include <qbitmap.h>
|
|
#include <qglobal.h>
|
|
#include <qimage.h>
|
|
#include <qpainter.h>
|
|
#include <qpixmap.h>
|
|
#include <qpixmapcache.h>
|
|
|
|
#include "pixmaploader.h"
|
|
|
|
|
|
#include "pixmaps.keramik"
|
|
|
|
using namespace Keramik;
|
|
|
|
PixmapLoader* PixmapLoader::s_instance = 0;
|
|
|
|
PixmapLoader::PixmapLoader(): m_pixmapCache(327680, 2017)
|
|
|
|
{
|
|
m_pixmapCache.setAutoDelete(true);
|
|
|
|
for (int c=0; c<256; c++)
|
|
clamp[c]=static_cast<unsigned char>(c);
|
|
|
|
for (int c=256; c<540; c++)
|
|
clamp[c] = 255;
|
|
|
|
}
|
|
|
|
void PixmapLoader::clear()
|
|
{
|
|
//m_cache.clear();
|
|
}
|
|
|
|
QImage* PixmapLoader::getDisabled(int name, const QColor& color, const QColor& back, bool blend)
|
|
{
|
|
KeramikEmbedImage* edata = KeramikGetDbImage(name);
|
|
if (!edata)
|
|
return 0;
|
|
|
|
//Like getColored, but desaturate a bit, and lower gamma..
|
|
|
|
//Create a real image...
|
|
QImage* img = new QImage(edata->width, edata->height, 32);
|
|
|
|
|
|
|
|
//OK, now, fill it in, using the color..
|
|
Q_UINT32 r, g,b;
|
|
Q_UINT32 i = qGray(color.rgb());
|
|
r = (3*color.red()+i)>>2;
|
|
g = (3*color.green()+i)>>2;
|
|
b = (3*color.blue()+i)>>2;
|
|
|
|
Q_UINT32 br = back.red(), bg = back.green(), bb = back.blue();
|
|
|
|
|
|
if (edata->haveAlpha)
|
|
{
|
|
if (blend)
|
|
{
|
|
img->setAlphaBuffer(false);
|
|
Q_UINT32* write = reinterpret_cast< Q_UINT32* >(img->bits() );
|
|
int size = img->width()*img->height() * 3;
|
|
|
|
for (int pos = 0; pos < size; pos+=3)
|
|
{
|
|
Q_UINT32 scale = edata->data[pos];
|
|
Q_UINT32 add = (edata->data[pos+1]*i+127)>>8;
|
|
Q_UINT32 alpha = edata->data[pos+2];
|
|
Q_UINT32 destAlpha = 256 - alpha;
|
|
|
|
Q_UINT32 rr = clamp[((r*scale+127)>>8) + add];
|
|
Q_UINT32 rg = clamp[((g*scale+127)>>8) + add];
|
|
Q_UINT32 rb = clamp[((b*scale+127)>>8) + add];
|
|
|
|
*write =qRgb(((rr*alpha+127)>>8) + ((br*destAlpha+127)>>8),
|
|
((rg*alpha+127)>>8) + ((bg*destAlpha+127)>>8),
|
|
((rb*alpha+127)>>8) + ((bb*destAlpha+127)>>8));
|
|
|
|
write++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
img->setAlphaBuffer(true);
|
|
Q_UINT32* write = reinterpret_cast< Q_UINT32* >(img->bits() );
|
|
int size = img->width()*img->height() * 3;
|
|
|
|
for (int pos = 0; pos < size; pos+=3)
|
|
{
|
|
Q_UINT32 scale = edata->data[pos];
|
|
Q_UINT32 add = (edata->data[pos+1]*i+127)>>8;
|
|
Q_UINT32 alpha = edata->data[pos+2];
|
|
|
|
Q_UINT32 rr = clamp[((r*scale+127)>>8) + add];
|
|
Q_UINT32 rg = clamp[((g*scale+127)>>8) + add];
|
|
Q_UINT32 rb = clamp[((b*scale+127)>>8) + add];
|
|
|
|
*write =qRgba(rr, rg, rb, alpha);
|
|
|
|
write++;
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
img->setAlphaBuffer(false);
|
|
Q_UINT32* write = reinterpret_cast< Q_UINT32* >(img->bits() );
|
|
int size = img->width()*img->height() * 2;
|
|
|
|
for (int pos = 0; pos < size; pos+=2)
|
|
{
|
|
Q_UINT32 scale = edata->data[pos];
|
|
Q_UINT32 add = (edata->data[pos+1]*i+127)>>8;
|
|
Q_UINT32 rr = clamp[((r*scale+127)>>8) + add];
|
|
Q_UINT32 rg = clamp[((g*scale+127)>>8) + add];
|
|
Q_UINT32 rb = clamp[((b*scale+127)>>8) + add];
|
|
*write =qRgb(rr, rg, rb);
|
|
write++;
|
|
}
|
|
}
|
|
|
|
return img;
|
|
}
|
|
|
|
QImage* PixmapLoader::getColored(int name, const QColor& color, const QColor& back, bool blend)
|
|
{
|
|
KeramikEmbedImage* edata = KeramikGetDbImage(name);
|
|
if (!edata)
|
|
return 0;
|
|
|
|
//Create a real image...
|
|
QImage* img = new QImage(edata->width, edata->height, 32);
|
|
|
|
//OK, now, fill it in, using the color..
|
|
Q_UINT32 r, g,b;
|
|
r = color.red() + 2;
|
|
g = color.green() + 2;
|
|
b = color.blue() + 2;
|
|
|
|
// int i = qGray(color.rgb());
|
|
|
|
Q_UINT32 br = back.red(), bg = back.green(), bb = back.blue();
|
|
|
|
if (edata->haveAlpha)
|
|
{
|
|
if (blend)
|
|
{
|
|
img->setAlphaBuffer(false);
|
|
|
|
Q_UINT32* write = reinterpret_cast< Q_UINT32* >(img->bits() );
|
|
int size = img->width()*img->height() * 3;
|
|
for (int pos = 0; pos < size; pos+=3)
|
|
{
|
|
Q_UINT32 scale = edata->data[pos];
|
|
Q_UINT32 add = edata->data[pos+1];
|
|
Q_UINT32 alpha = edata->data[pos+2];
|
|
Q_UINT32 destAlpha = 256 - alpha;
|
|
|
|
if (scale != 0)
|
|
add = add*5/4;
|
|
|
|
Q_UINT32 rr = clamp[((r*scale+127)>>8) + add];
|
|
Q_UINT32 rg = clamp[((g*scale+127)>>8) + add];
|
|
Q_UINT32 rb = clamp[((b*scale+127)>>8) + add];
|
|
|
|
*write =qRgb(((rr*alpha+127)>>8) + ((br*destAlpha+127)>>8),
|
|
((rg*alpha+127)>>8) + ((bg*destAlpha+127)>>8),
|
|
((rb*alpha+127)>>8) + ((bb*destAlpha+127)>>8));
|
|
|
|
write++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
img->setAlphaBuffer(true);
|
|
|
|
Q_UINT32* write = reinterpret_cast< Q_UINT32* >(img->bits() );
|
|
int size = img->width()*img->height() * 3;
|
|
|
|
for (int pos = 0; pos < size; pos+=3)
|
|
{
|
|
Q_UINT32 scale = edata->data[pos];
|
|
Q_UINT32 add = edata->data[pos+1];
|
|
Q_UINT32 alpha = edata->data[pos+2];
|
|
if (scale != 0)
|
|
add = add*5/4;
|
|
|
|
Q_UINT32 rr = clamp[((r*scale+127)>>8) + add];
|
|
Q_UINT32 rg = clamp[((g*scale+127)>>8) + add];
|
|
Q_UINT32 rb = clamp[((b*scale+127)>>8) + add];
|
|
|
|
*write =qRgba(rr, rg, rb, alpha);
|
|
write++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
img->setAlphaBuffer(false);
|
|
|
|
Q_UINT32* write = reinterpret_cast< Q_UINT32* >(img->bits() );
|
|
int size = img->width()*img->height() * 2;
|
|
|
|
for (int pos = 0; pos < size; pos+=2)
|
|
{
|
|
Q_UINT32 scale = edata->data[pos];
|
|
Q_UINT32 add = edata->data[pos+1];
|
|
if (scale != 0)
|
|
add = add*5/4;
|
|
|
|
Q_UINT32 rr = clamp[((r*scale+127)>>8) + add];
|
|
Q_UINT32 rg = clamp[((g*scale+127)>>8) + add];
|
|
Q_UINT32 rb = clamp[((b*scale+127)>>8) + add];
|
|
|
|
|
|
*write =qRgb(rr, rg, rb);
|
|
write++;
|
|
}
|
|
}
|
|
|
|
return img;
|
|
}
|
|
|
|
QPixmap PixmapLoader::pixmap( int name, const QColor& color, const QColor& bg, bool disabled, bool blend )
|
|
{
|
|
return scale(name, 0, 0, color, bg, disabled, blend);
|
|
}
|
|
|
|
|
|
QPixmap PixmapLoader::scale( int name, int width, int height, const QColor& color, const QColor& bg, bool disabled, bool blend )
|
|
{
|
|
KeramikCacheEntry entry(name, color, bg, disabled, blend, width, height);
|
|
KeramikCacheEntry* cacheEntry;
|
|
|
|
int key = entry.key();
|
|
|
|
if ((cacheEntry = m_pixmapCache.find(key, true)))
|
|
{
|
|
if (entry == *cacheEntry) //True match!
|
|
return *cacheEntry->m_pixmap;
|
|
else //Remove old entry in case of a conflict!
|
|
m_pixmapCache.remove(key);
|
|
}
|
|
|
|
|
|
QImage* img = 0;
|
|
QPixmap* result = 0;
|
|
|
|
if (disabled)
|
|
img = getDisabled(name, color, bg, blend);
|
|
else
|
|
img = getColored(name, color, bg, blend);
|
|
|
|
if (!img)
|
|
{
|
|
KeramikCacheEntry* toAdd = new KeramikCacheEntry(entry);
|
|
toAdd->m_pixmap = new QPixmap();
|
|
m_pixmapCache.insert(key, toAdd, 16);
|
|
return QPixmap();
|
|
}
|
|
|
|
if (width == 0 && height == 0)
|
|
result = new QPixmap(*img);
|
|
else
|
|
result = new QPixmap(img->smoothScale( width ? width : img->width(),
|
|
height ? height: img->height()));
|
|
delete img;
|
|
|
|
KeramikCacheEntry* toAdd = new KeramikCacheEntry(entry);
|
|
toAdd->m_pixmap = result;
|
|
|
|
if (!m_pixmapCache.insert(key, toAdd, result->width()*result->height()*result->depth()/8)) {
|
|
QPixmap toRet = *result;
|
|
delete toAdd;
|
|
return toRet;
|
|
}
|
|
|
|
return *result;
|
|
}
|
|
|
|
QSize PixmapLoader::size( int id )
|
|
{
|
|
KeramikEmbedImage* edata = KeramikGetDbImage(id);
|
|
if (!edata)
|
|
return QSize(0,0);
|
|
return QSize(edata->width, edata->height);
|
|
}
|
|
|
|
void TilePainter::draw( QPainter *p, int x, int y, int width, int height, const QColor& color, const QColor& bg, bool disabled, PaintMode mode )
|
|
{
|
|
if (mode == PaintTrivialMask)
|
|
{
|
|
p->fillRect(x, y, width, height, Qt::color1);
|
|
return;
|
|
}
|
|
|
|
bool swBlend = (mode != PaintFullBlend);
|
|
unsigned int scaledColumns = 0, scaledRows = 0, lastScaledColumn = 0, lastScaledRow = 0;
|
|
int scaleWidth = width, scaleHeight = height;
|
|
|
|
//scaleWidth, scaleHeight are calculated to contain the area available
|
|
//for all tiled and stretched columns/rows respectively.
|
|
//This is need to redistribute the area remaining after painting
|
|
//the "fixed" elements. We also keep track of the last col and row
|
|
//being scaled so rounding errors don't cause us to be short a pixel or so.
|
|
for ( unsigned int col = 0; col < columns(); ++col )
|
|
if ( columnMode( col ) != Fixed )
|
|
{
|
|
scaledColumns++;
|
|
lastScaledColumn = col;
|
|
}
|
|
else scaleWidth -= PixmapLoader::the().size (absTileName( col, 0 ) ).width();
|
|
|
|
for ( unsigned int row = 0; row < rows(); ++row )
|
|
if ( rowMode( row ) != Fixed )
|
|
{
|
|
scaledRows++;
|
|
lastScaledRow = row;
|
|
}
|
|
else scaleHeight -= PixmapLoader::the().size (absTileName( 0, row ) ).height();
|
|
|
|
|
|
if ( scaleWidth < 0 ) scaleWidth = 0;
|
|
if ( scaleHeight < 0 ) scaleHeight = 0;
|
|
|
|
|
|
int ypos = y;
|
|
|
|
//Center vertically if everything is fixed but there is extra room remaining
|
|
if ( scaleHeight && !scaledRows )
|
|
ypos += scaleHeight / 2;
|
|
|
|
for ( unsigned int row = 0; row < rows(); ++row )
|
|
{
|
|
int xpos = x;
|
|
|
|
//Center horizontally if extra space and no where to redistribute it to...
|
|
if ( scaleWidth && !scaledColumns )
|
|
xpos += scaleWidth / 2;
|
|
|
|
//If not fixed vertically, calculate our share of space available
|
|
//for scalable rows.
|
|
int h = rowMode( row ) == Fixed ? 0 : scaleHeight / scaledRows;
|
|
|
|
//Redistribute any "extra" pixels to the last scaleable row.
|
|
if ( scaledRows && row == lastScaledRow )
|
|
{
|
|
int allocatedEvenly = scaleHeight / scaledRows * scaledRows;
|
|
h += scaleHeight - allocatedEvenly;
|
|
}
|
|
|
|
|
|
//If we're fixed, get the height from the pixmap itself.
|
|
int realH = h ? h : PixmapLoader::the().size (absTileName( 0, row ) ).height();
|
|
|
|
//Skip non-fitting stretched/tiled rows, too.
|
|
if (rowMode( row ) != Fixed && h == 0)
|
|
continue;
|
|
|
|
|
|
//Set h to 0 to denote that we aren't scaling
|
|
if ( rowMode( row ) == Tiled )
|
|
h = 0;
|
|
|
|
for ( unsigned int col = 0; col < columns(); ++col )
|
|
{
|
|
//Calculate width for rows that aren't fixed.
|
|
int w = columnMode( col ) == Fixed ? 0 : scaleWidth / scaledColumns;
|
|
|
|
//Get the width of the pixmap..
|
|
int tileW = PixmapLoader::the().size (absTileName( col, row ) ).width();
|
|
|
|
//Redistribute any extra pixels..
|
|
if ( scaledColumns && col == lastScaledColumn ) w += scaleWidth - scaleWidth / scaledColumns * scaledColumns;
|
|
|
|
//The width to use...
|
|
int realW = w ? w : tileW;
|
|
|
|
//Skip any non-fitting stretched/tiled columns
|
|
if (columnMode( col ) != Fixed && w == 0)
|
|
continue;
|
|
|
|
//Set w to 0 to denote that we aren't scaling
|
|
if ( columnMode( col ) == Tiled )
|
|
w = 0;
|
|
|
|
//If we do indeed have a pixmap..
|
|
if ( tileW )
|
|
{
|
|
//If scaling in either direction.
|
|
if ( w || h )
|
|
{
|
|
if (mode != PaintMask)
|
|
{
|
|
p->drawTiledPixmap( xpos, ypos, realW, realH, scale( col, row, w, h, color, bg, disabled, swBlend ) );
|
|
}
|
|
else
|
|
{
|
|
const QBitmap* mask = scale( col, row, w, h, color, bg, disabled, false ).mask();
|
|
if (mask)
|
|
{
|
|
p->setBackgroundColor(Qt::color0);
|
|
p->setPen(Qt::color1);
|
|
p->drawTiledPixmap( xpos, ypos, realW, realH, *mask);
|
|
}
|
|
else
|
|
p->fillRect ( xpos, ypos, realW, realH, Qt::color1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Tiling (or fixed, the same really)
|
|
if (mode != PaintMask)
|
|
{
|
|
p->drawTiledPixmap( xpos, ypos, realW, realH, tile( col, row, color, bg, disabled, swBlend ) );
|
|
}
|
|
else
|
|
{
|
|
const QBitmap* mask = tile( col, row, color, bg, disabled, false ).mask();
|
|
if (mask)
|
|
{
|
|
p->setBackgroundColor(Qt::color0);
|
|
p->setPen(Qt::color1);
|
|
p->drawTiledPixmap( xpos, ypos, realW, realH, *mask);
|
|
}
|
|
else
|
|
p->fillRect ( xpos, ypos, realW, realH, Qt::color1);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
//Advance horizontal position
|
|
xpos += realW;
|
|
}
|
|
|
|
//Advance vertical position
|
|
ypos += realH;
|
|
}
|
|
}
|
|
|
|
RectTilePainter::RectTilePainter( int name,
|
|
bool scaleH, bool scaleV,
|
|
unsigned int columns, unsigned int rows )
|
|
: TilePainter( name ),
|
|
m_scaleH( scaleH ),
|
|
m_scaleV( scaleV )
|
|
{
|
|
m_columns = columns;
|
|
m_rows = rows;
|
|
|
|
TileMode mh = m_scaleH ? Scaled : Tiled;
|
|
TileMode mv = m_scaleV ? Scaled : Tiled;
|
|
for (int c=0; c<4; c++)
|
|
{
|
|
if (c != 1)
|
|
colMde[c] = Fixed;
|
|
else
|
|
colMde[c] = mh;
|
|
}
|
|
|
|
for (int c=0; c<4; c++)
|
|
{
|
|
if (c != 1)
|
|
rowMde[c] = Fixed;
|
|
else
|
|
rowMde[c] = mv;
|
|
}
|
|
|
|
}
|
|
|
|
int RectTilePainter::tileName( unsigned int column, unsigned int row ) const
|
|
{
|
|
return row *3 + column;
|
|
}
|
|
|
|
ActiveTabPainter::ActiveTabPainter( bool bottom )
|
|
: RectTilePainter( bottom? keramik_tab_bottom_active: keramik_tab_top_active, false),
|
|
m_bottom( bottom )
|
|
{
|
|
m_rows = 2;
|
|
if (m_bottom)
|
|
{
|
|
rowMde[0] = rowMde[2] = rowMde[3] = Scaled;
|
|
rowMde[1] = Fixed;
|
|
}
|
|
else
|
|
{
|
|
rowMde[0] = rowMde[2] = rowMde[3] = Fixed;
|
|
rowMde[1] = Scaled;
|
|
}
|
|
}
|
|
|
|
int ActiveTabPainter::tileName( unsigned int column, unsigned int row ) const
|
|
{
|
|
if ( m_bottom )
|
|
return RectTilePainter::tileName( column, row + 1 );
|
|
return RectTilePainter::tileName( column, row );
|
|
}
|
|
|
|
InactiveTabPainter::InactiveTabPainter( Mode mode, bool bottom )
|
|
: RectTilePainter( bottom? keramik_tab_bottom_inactive: keramik_tab_top_inactive, false),
|
|
m_mode( mode ), m_bottom( bottom )
|
|
{
|
|
m_rows = 2;
|
|
if (m_bottom)
|
|
{
|
|
rowMde[0] = Scaled;
|
|
rowMde[1] = Fixed;
|
|
}
|
|
else
|
|
{
|
|
rowMde[0] = Fixed;
|
|
rowMde[1] = Scaled;
|
|
}
|
|
|
|
/**
|
|
Most fully, inactive tabs look like this:
|
|
L C R
|
|
/ --- \
|
|
| === |
|
|
|
|
Where L,C, and R denote the tile positions. Of course, we don't want to draw all of the rounding for all the tabs.
|
|
|
|
We want the left-most tab to look like this:
|
|
|
|
L C
|
|
/ --
|
|
| ==
|
|
|
|
"Middle" tabs look like this:
|
|
|
|
L C
|
|
| --
|
|
| ==
|
|
|
|
And the right most tab looks like this:
|
|
|
|
L C R
|
|
| -- \
|
|
| == |
|
|
|
|
So we have to vary the number of columns, and for everything but left-most tab, the L tab gets the special separator
|
|
tile.
|
|
*/
|
|
|
|
Mode rightMost = QApplication::reverseLayout() ? First : Last;
|
|
m_columns = (m_mode == rightMost ? 3 : 2);
|
|
}
|
|
|
|
int InactiveTabPainter::tileName( unsigned int column, unsigned int row ) const
|
|
{
|
|
Mode leftMost = QApplication::reverseLayout() ? Last : First;
|
|
if ( column == 0 && m_mode != leftMost )
|
|
return KeramikTileSeparator;
|
|
if ( m_bottom )
|
|
return RectTilePainter::tileName( column, row + 1 );
|
|
return RectTilePainter::tileName( column, row );
|
|
}
|
|
|
|
ScrollBarPainter::ScrollBarPainter( int type, int count, bool horizontal )
|
|
: TilePainter( name( horizontal ) ),
|
|
m_type( type ),
|
|
m_count( count ),
|
|
m_horizontal( horizontal )
|
|
{
|
|
for (int c=0; c<5; c++)
|
|
{
|
|
if ( !m_horizontal || !( c % 2 ) ) colMde[c] = Fixed;
|
|
else colMde[c] = Tiled;
|
|
|
|
if ( m_horizontal || !( c % 2 ) ) rowMde[c] = Fixed;
|
|
else rowMde[c] = Tiled;
|
|
}
|
|
|
|
m_columns = m_horizontal ? m_count : 1;
|
|
m_rows = m_horizontal ? 1 : m_count;
|
|
|
|
}
|
|
|
|
int ScrollBarPainter::name( bool horizontal )
|
|
{
|
|
return horizontal? keramik_scrollbar_hbar: keramik_scrollbar_vbar;
|
|
}
|
|
|
|
int ScrollBarPainter::tileName( unsigned int column, unsigned int row ) const
|
|
{
|
|
unsigned int num = ( column ? column : row ) + 1;
|
|
if ( m_count == 5 )
|
|
if ( num == 3 ) num = 4;
|
|
else if ( num == 4 ) num = 2;
|
|
else if ( num == 5 ) num = 3;
|
|
|
|
return m_type + (num-1)*16;
|
|
}
|
|
|
|
int SpinBoxPainter::tileName( unsigned int column, unsigned int ) const
|
|
{
|
|
return column + 1;
|
|
}
|
|
|
|
// vim: ts=4 sw=4 noet
|
|
// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off;
|