|
|
|
/***************************************************************************
|
|
|
|
* Copyright (C) 2005 by Enrico Ros <eros.kde@email.it> *
|
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
* it under the terms of the GNU General Public License as published by *
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
|
|
* (at your option) any later version. *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
// qt / kde includes
|
|
|
|
#include <tqrect.h>
|
|
|
|
#include <tqpainter.h>
|
|
|
|
#include <tqpixmap.h>
|
|
|
|
#include <tqimage.h>
|
|
|
|
#include <tqapplication.h>
|
|
|
|
#include <kimageeffect.h>
|
|
|
|
|
|
|
|
// local includes
|
|
|
|
#include "pagepainter.h"
|
|
|
|
#include "core/page.h"
|
|
|
|
#include "conf/settings.h"
|
|
|
|
|
|
|
|
void PagePainter::paintPageOnPainter( const KPDFPage * page, int id, int flags,
|
|
|
|
TQPainter * destPainter, const TQRect & limits, int width, int height )
|
|
|
|
{
|
|
|
|
TQPixmap * pixmap = 0;
|
|
|
|
|
|
|
|
// if a pixmap is present for given id, use it
|
|
|
|
if ( page->m_pixmaps.contains( id ) )
|
|
|
|
pixmap = page->m_pixmaps[ id ];
|
|
|
|
|
|
|
|
// else find the closest match using pixmaps of other IDs (great optim!)
|
|
|
|
else if ( !page->m_pixmaps.isEmpty() && width != -1 )
|
|
|
|
{
|
|
|
|
int minDistance = -1;
|
|
|
|
TQMap< int,TQPixmap * >::const_iterator it = page->m_pixmaps.begin(), end = page->m_pixmaps.end();
|
|
|
|
for ( ; it != end; ++it )
|
|
|
|
{
|
|
|
|
int pixWidth = (*it)->width(),
|
|
|
|
distance = pixWidth > width ? pixWidth - width : width - pixWidth;
|
|
|
|
if ( minDistance == -1 || distance < minDistance )
|
|
|
|
{
|
|
|
|
pixmap = *it;
|
|
|
|
minDistance = distance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if have no pixmap, draw blank page with gray cross and exit
|
|
|
|
if ( !pixmap )
|
|
|
|
{
|
|
|
|
TQColor color = TQt::white;
|
|
|
|
if ( KpdfSettings::changeColors() )
|
|
|
|
{
|
|
|
|
switch ( KpdfSettings::renderMode() )
|
|
|
|
{
|
|
|
|
case KpdfSettings::EnumRenderMode::Inverted:
|
|
|
|
color = TQt::black;
|
|
|
|
break;
|
|
|
|
case KpdfSettings::EnumRenderMode::Paper:
|
|
|
|
color = KpdfSettings::paperColor();
|
|
|
|
break;
|
|
|
|
case KpdfSettings::EnumRenderMode::Recolor:
|
|
|
|
color = KpdfSettings::recolorBackground();
|
|
|
|
break;
|
|
|
|
default: ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
destPainter->fillRect( limits, color );
|
|
|
|
|
|
|
|
// draw a cross (to that the pixmap as not yet been loaded)
|
|
|
|
// helps a lot on pages that take much to render
|
|
|
|
destPainter->setPen( TQt::gray );
|
|
|
|
destPainter->drawLine( 0, 0, width-1, height-1 );
|
|
|
|
destPainter->drawLine( 0, height-1, width-1, 0 );
|
|
|
|
// idea here: draw a hourglass (or kpdf icon :-) on top-left corner
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// find out what to paint over the pixmap (manipulations / overlays)
|
|
|
|
bool paintAccessibility = (flags & Accessibility) && KpdfSettings::changeColors() && (KpdfSettings::renderMode() != KpdfSettings::EnumRenderMode::Paper);
|
|
|
|
bool paintHighlights = (flags & Highlights) && !page->m_highlights.isEmpty();
|
|
|
|
bool enhanceLinks = (flags & EnhanceLinks) && KpdfSettings::highlightLinks();
|
|
|
|
bool enhanceImages = (flags & EnhanceImages) && KpdfSettings::highlightImages();
|
|
|
|
// check if there are really some highlightRects to paint
|
|
|
|
if ( paintHighlights )
|
|
|
|
{
|
|
|
|
// precalc normalized 'limits rect' for intersection
|
|
|
|
double nXMin = (double)limits.left() / (double)width,
|
|
|
|
nXMax = (double)limits.right() / (double)width,
|
|
|
|
nYMin = (double)limits.top() / (double)height,
|
|
|
|
nYMax = (double)limits.bottom() / (double)height;
|
|
|
|
// if no rect intersects limits, disable paintHighlights
|
|
|
|
paintHighlights = false;
|
|
|
|
TQValueList< HighlightRect * >::const_iterator hIt = page->m_highlights.begin(), hEnd = page->m_highlights.end();
|
|
|
|
for ( ; hIt != hEnd; ++hIt )
|
|
|
|
{
|
|
|
|
if ( (*hIt)->intersects( nXMin, nYMin, nXMax, nYMax ) )
|
|
|
|
{
|
|
|
|
paintHighlights = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// use backBuffer if 'pixmap direct manipulation' is needed
|
|
|
|
bool backBuffer = paintAccessibility || paintHighlights;
|
|
|
|
TQPixmap * backPixmap = 0;
|
|
|
|
TQPainter * p = destPainter;
|
|
|
|
if ( backBuffer )
|
|
|
|
{
|
|
|
|
// let's paint using a buffered painter
|
|
|
|
backPixmap = new TQPixmap( limits.width(), limits.height() );
|
|
|
|
p = new TQPainter( backPixmap );
|
|
|
|
p->translate( -limits.left(), -limits.top() );
|
|
|
|
}
|
|
|
|
|
|
|
|
// 1. fast blit the pixmap if it has the right size..
|
|
|
|
if ( pixmap->width() == width && pixmap->height() == height )
|
|
|
|
p->drawPixmap( limits.topLeft(), *pixmap, limits );
|
|
|
|
// ..else set a scale matrix to the painter and paint a quick 'zoomed' pixmap
|
|
|
|
else
|
|
|
|
{
|
|
|
|
p->save();
|
|
|
|
// TODO paint only the needed part (note: hope that TQt4 transforms are faster)
|
|
|
|
p->scale( width / (double)pixmap->width(), height / (double)pixmap->height() );
|
|
|
|
p->drawPixmap( 0,0, *pixmap, 0,0, pixmap->width(), pixmap->height() );
|
|
|
|
p->restore();
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2. mangle pixmap: convert it to 32-bit qimage and perform pixel-level manipulations
|
|
|
|
if ( backBuffer )
|
|
|
|
{
|
|
|
|
TQImage backImage = backPixmap->convertToImage();
|
|
|
|
// 2.1. modify pixmap following accessibility settings
|
|
|
|
if ( paintAccessibility )
|
|
|
|
{
|
|
|
|
switch ( KpdfSettings::renderMode() )
|
|
|
|
{
|
|
|
|
case KpdfSettings::EnumRenderMode::Inverted:
|
|
|
|
// Invert image pixels using TQImage internal function
|
|
|
|
backImage.invertPixels(false);
|
|
|
|
break;
|
|
|
|
case KpdfSettings::EnumRenderMode::Recolor:
|
|
|
|
// Recolor image using KImageEffect::flatten with dither:0
|
|
|
|
KImageEffect::flatten( backImage, KpdfSettings::recolorForeground(), KpdfSettings::recolorBackground() );
|
|
|
|
break;
|
|
|
|
case KpdfSettings::EnumRenderMode::BlackWhite:
|
|
|
|
// Manual Gray and Contrast
|
|
|
|
unsigned int * data = (unsigned int *)backImage.bits();
|
|
|
|
int val, pixels = backImage.width() * backImage.height(),
|
|
|
|
con = KpdfSettings::bWContrast(), thr = 255 - KpdfSettings::bWThreshold();
|
|
|
|
for( int i = 0; i < pixels; ++i )
|
|
|
|
{
|
|
|
|
val = tqGray( data[i] );
|
|
|
|
if ( val > thr )
|
|
|
|
val = 128 + (127 * (val - thr)) / (255 - thr);
|
|
|
|
else if ( val < thr )
|
|
|
|
val = (128 * val) / thr;
|
|
|
|
if ( con > 2 )
|
|
|
|
{
|
|
|
|
val = con * ( val - thr ) / 2 + thr;
|
|
|
|
if ( val > 255 )
|
|
|
|
val = 255;
|
|
|
|
else if ( val < 0 )
|
|
|
|
val = 0;
|
|
|
|
}
|
|
|
|
data[i] = tqRgba( val, val, val, 255 );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 2.2. highlight rects in page
|
|
|
|
if ( paintHighlights )
|
|
|
|
{
|
|
|
|
// draw highlights that are inside the 'limits' paint region
|
|
|
|
TQValueList< HighlightRect * >::const_iterator hIt = page->m_highlights.begin(), hEnd = page->m_highlights.end();
|
|
|
|
for ( ; hIt != hEnd; ++hIt )
|
|
|
|
{
|
|
|
|
HighlightRect * r = *hIt;
|
|
|
|
TQRect highlightRect = r->tqgeometry( width, height );
|
|
|
|
if ( highlightRect.isValid() && highlightRect.intersects( limits ) )
|
|
|
|
{
|
|
|
|
// find out the rect to highlight on pixmap
|
|
|
|
highlightRect = highlightRect.intersect( limits );
|
|
|
|
highlightRect.moveBy( -limits.left(), -limits.top() );
|
|
|
|
|
|
|
|
// highlight composition (product: highlight color * destcolor)
|
|
|
|
unsigned int * data = (unsigned int *)backImage.bits();
|
|
|
|
int val, newR, newG, newB,
|
|
|
|
rh = r->color.red(),
|
|
|
|
gh = r->color.green(),
|
|
|
|
bh = r->color.blue(),
|
|
|
|
offset = highlightRect.top() * backImage.width();
|
|
|
|
for( int y = highlightRect.top(); y <= highlightRect.bottom(); ++y )
|
|
|
|
{
|
|
|
|
for( int x = highlightRect.left(); x <= highlightRect.right(); ++x )
|
|
|
|
{
|
|
|
|
val = data[ x + offset ];
|
|
|
|
newR = (tqRed(val) * rh) / 255;
|
|
|
|
newG = (tqGreen(val) * gh) / 255;
|
|
|
|
newB = (tqBlue(val) * bh) / 255;
|
|
|
|
data[ x + offset ] = tqRgba( newR, newG, newB, 255 );
|
|
|
|
}
|
|
|
|
offset += backImage.width();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
backPixmap->convertFromImage( backImage );
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. visually enchance links and images if requested
|
|
|
|
if ( enhanceLinks || enhanceImages )
|
|
|
|
{
|
|
|
|
TQColor normalColor = TQApplication::tqpalette().active().highlight();
|
|
|
|
TQColor lightColor = normalColor.light( 140 );
|
|
|
|
// enlarging limits for intersection is like growing the 'rectGeometry' below
|
|
|
|
TQRect limitsEnlarged = limits;
|
|
|
|
limitsEnlarged.addCoords( -2, -2, 2, 2 );
|
|
|
|
// draw rects that are inside the 'limits' paint region as opaque rects
|
|
|
|
TQValueList< ObjectRect * >::const_iterator lIt = page->m_rects.begin(), lEnd = page->m_rects.end();
|
|
|
|
for ( ; lIt != lEnd; ++lIt )
|
|
|
|
{
|
|
|
|
ObjectRect * rect = *lIt;
|
|
|
|
if ( (enhanceLinks && rect->objectType() == ObjectRect::Link) ||
|
|
|
|
(enhanceImages && rect->objectType() == ObjectRect::Image) )
|
|
|
|
{
|
|
|
|
TQRect rectGeometry = rect->tqgeometry( width, height );
|
|
|
|
if ( rectGeometry.intersects( limitsEnlarged ) )
|
|
|
|
{
|
|
|
|
// expand rect and draw inner border
|
|
|
|
rectGeometry.addCoords( -1,-1,1,1 );
|
|
|
|
p->setPen( lightColor );
|
|
|
|
p->drawRect( rectGeometry );
|
|
|
|
// expand rect to draw outer border
|
|
|
|
rectGeometry.addCoords( -1,-1,1,1 );
|
|
|
|
p->setPen( normalColor );
|
|
|
|
p->drawRect( rectGeometry );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 4. if was backbuffering, copy the backPixmap to destination
|
|
|
|
if ( backBuffer )
|
|
|
|
{
|
|
|
|
delete p;
|
|
|
|
destPainter->drawPixmap( limits.left(), limits.top(), *backPixmap );
|
|
|
|
delete backPixmap;
|
|
|
|
}
|
|
|
|
}
|