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.
265 lines
11 KiB
265 lines
11 KiB
/*************************************************************************** |
|
* 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 <qrect.h> |
|
#include <qpainter.h> |
|
#include <qpixmap.h> |
|
#include <qimage.h> |
|
#include <qapplication.h> |
|
#include <kimageeffect.h> |
|
|
|
// local includes |
|
#include "pagepainter.h" |
|
#include "core/page.h" |
|
#include "core/annotations.h" |
|
#include "conf/settings.h" |
|
|
|
void PagePainter::paintPageOnPainter( const KPDFPage * page, int id, int flags, |
|
QPainter * destPainter, const QRect & limits, int width, int height ) |
|
{ |
|
QPixmap * 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; |
|
QMap< int,QPixmap * >::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 ) |
|
{ |
|
if ( Settings::changeColors() && |
|
Settings::renderMode() == Settings::EnumRenderMode::Paper ) |
|
destPainter->fillRect( limits, Settings::paperColor() ); |
|
else |
|
destPainter->fillRect( limits, Qt::white ); |
|
|
|
// draw a cross (to that the pixmap as not yet been loaded) |
|
// helps a lot on pages that take much to render |
|
destPainter->setPen( Qt::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) && Settings::changeColors() && (Settings::renderMode() != Settings::EnumRenderMode::Paper); |
|
bool paintHighlights = (flags & Highlights) && !page->m_highlights.isEmpty(); |
|
bool paintAnnotations = (flags & Annotations) && !page->m_annotations.isEmpty(); |
|
bool enhanceLinks = (flags & EnhanceLinks) && Settings::highlightLinks(); |
|
bool enhanceImages = (flags & EnhanceImages) && Settings::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; |
|
QValueList< 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; |
|
} |
|
} |
|
} |
|
// check if there are really some annotations to paint |
|
if ( paintAnnotations ) |
|
{ |
|
// TODO |
|
} |
|
|
|
// use backBuffer if 'pixmap direct manipulation' is needed |
|
bool backBuffer = paintAccessibility || paintHighlights || paintAnnotations; |
|
QPixmap * backPixmap = 0; |
|
QPainter * p = destPainter; |
|
if ( backBuffer ) |
|
{ |
|
// let's paint using a buffered painter |
|
backPixmap = new QPixmap( limits.width(), limits.height() ); |
|
p = new QPainter( 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 Qt4 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 ) |
|
{ |
|
QImage backImage = backPixmap->convertToImage(); |
|
// 2.1. modify pixmap following accessibility settings |
|
if ( paintAccessibility ) |
|
{ |
|
switch ( Settings::renderMode() ) |
|
{ |
|
case Settings::EnumRenderMode::Inverted: |
|
// Invert image pixels using QImage internal function |
|
backImage.invertPixels(false); |
|
break; |
|
case Settings::EnumRenderMode::Recolor: |
|
// Recolor image using KImageEffect::flatten with dither:0 |
|
KImageEffect::flatten( backImage, Settings::recolorForeground(), Settings::recolorBackground() ); |
|
break; |
|
case Settings::EnumRenderMode::BlackWhite: |
|
// Manual Gray and Contrast |
|
unsigned int * data = (unsigned int *)backImage.bits(); |
|
int val, pixels = backImage.width() * backImage.height(), |
|
con = Settings::bWContrast(), thr = 255 - Settings::bWThreshold(); |
|
for( int i = 0; i < pixels; ++i ) |
|
{ |
|
val = qGray( 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] = qRgba( val, val, val, 255 ); |
|
} |
|
break; |
|
} |
|
} |
|
// 2.2. highlight rects in page |
|
if ( paintHighlights ) |
|
{ |
|
// draw highlights that are inside the 'limits' paint region |
|
QValueList< HighlightRect * >::const_iterator hIt = page->m_highlights.begin(), hEnd = page->m_highlights.end(); |
|
for ( ; hIt != hEnd; ++hIt ) |
|
{ |
|
HighlightRect * r = *hIt; |
|
QRect highlightRect = r->geometry( 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 = (qRed(val) * rh) / 255; |
|
newG = (qGreen(val) * gh) / 255; |
|
newB = (qBlue(val) * bh) / 255; |
|
data[ x + offset ] = qRgba( newR, newG, newB, 255 ); |
|
} |
|
offset += backImage.width(); |
|
} |
|
} |
|
} |
|
} |
|
backPixmap->convertFromImage( backImage ); |
|
} |
|
|
|
// 2.3. annotations OVERLAY FIXME MOD in page |
|
if ( paintAnnotations ) |
|
{ |
|
// draw annotations that are inside the 'limits' paint region |
|
QValueList< Annotation * >::const_iterator aIt = page->m_annotations.begin(), aEnd = page->m_annotations.end(); |
|
for ( ; aIt != aEnd; ++aIt ) |
|
{ |
|
Annotation * a = *aIt; |
|
QRect annotRect = a->geometry( width, height ); |
|
if ( annotRect.isValid() && annotRect.intersects( limits ) ) |
|
{ |
|
// find out the annotation rect on pixmap |
|
annotRect = annotRect.intersect( limits ); |
|
a->paintOverlay( p, width, height, limits ); |
|
} |
|
} |
|
} |
|
|
|
|
|
// 3. visually enchance links and images if requested |
|
if ( enhanceLinks || enhanceImages ) |
|
{ |
|
QColor normalColor = QApplication::palette().active().highlight(); |
|
QColor lightColor = normalColor.light( 140 ); |
|
// enlarging limits for intersection is like growing the 'rectGeometry' below |
|
QRect limitsEnlarged = limits; |
|
limitsEnlarged.addCoords( -2, -2, 2, 2 ); |
|
// draw rects that are inside the 'limits' paint region as opaque rects |
|
QValueList< 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) ) |
|
{ |
|
QRect rectGeometry = rect->geometry( 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; |
|
} |
|
}
|
|
|