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.
402 lines
13 KiB
402 lines
13 KiB
/*************************************************************************** |
|
* Copyright (C) 2006 by Luigi Toscano <luigi.toscano@tiscali.it> * |
|
* Copyright (C) 2008 by Pino Toscano <pino@kde.org> * |
|
* * |
|
* 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. * |
|
***************************************************************************/ |
|
|
|
#include "utils.h" |
|
#include "utils_p.h" |
|
|
|
#include <QtCore/QRect> |
|
#include <QApplication> |
|
#include <QDesktopWidget> |
|
#include <QImage> |
|
#include <QIODevice> |
|
|
|
#ifdef Q_WS_X11 |
|
#include "config-okular.h" |
|
#if HAVE_LIBKSCREEN |
|
#include <kscreen/config.h> |
|
#include <kscreen/edid.h> |
|
#endif |
|
#include <QX11Info> |
|
#endif |
|
|
|
#ifdef Q_OS_MAC |
|
#include <ApplicationServices/ApplicationServices.h> |
|
#include <IOKit/graphics/IOGraphicsLib.h> |
|
#endif |
|
|
|
|
|
|
|
using namespace Okular; |
|
|
|
QRect Utils::rotateRect( const QRect & source, int width, int height, int orientation ) |
|
{ |
|
QRect ret; |
|
|
|
// adapt the coordinates of the boxes to the rotation |
|
switch ( orientation ) |
|
{ |
|
case 1: |
|
ret = QRect( width - source.y() - source.height(), source.x(), |
|
source.height(), source.width() ); |
|
break; |
|
case 2: |
|
ret = QRect( width - source.x() - source.width(), height - source.y() - source.height(), |
|
source.width(), source.height() ); |
|
break; |
|
case 3: |
|
ret = QRect( source.y(), height - source.x() - source.width(), |
|
source.height(), source.width() ); |
|
break; |
|
case 0: // no modifications |
|
default: // other cases |
|
ret = source; |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
#if defined(Q_WS_X11) |
|
|
|
double Utils::dpiX() |
|
{ |
|
return QX11Info::appDpiX(); |
|
} |
|
|
|
double Utils::dpiY() |
|
{ |
|
return QX11Info::appDpiY(); |
|
} |
|
|
|
double Utils::realDpiX() |
|
{ |
|
const QDesktopWidget* w = QApplication::desktop(); |
|
if (w->width() > 0 && w->widthMM() > 0) { |
|
qCDebug(OkularCoreDebug) << "Pix:" << w->width() << "MM:" << w->widthMM(); |
|
return (double(w->width()) * 25.4) / double(w->widthMM()); |
|
} else { |
|
return dpiX(); |
|
} |
|
} |
|
|
|
double Utils::realDpiY() |
|
{ |
|
const QDesktopWidget* w = QApplication::desktop(); |
|
if (w->height() > 0 && w->heightMM() > 0) { |
|
qCDebug(OkularCoreDebug) << "Pix:" << w->height() << "MM:" << w->heightMM(); |
|
return (double(w->height()) * 25.4) / double(w->heightMM()); |
|
} else { |
|
return dpiY(); |
|
} |
|
} |
|
|
|
QSizeF Utils::realDpi(QWidget* widgetOnScreen) |
|
{ |
|
if (widgetOnScreen) |
|
{ |
|
// Firstly try to retrieve DPI via LibKScreen |
|
#if HAVE_LIBKSCREEN |
|
KScreen::Config* config = KScreen::Config::current(); |
|
if (config) { |
|
KScreen::OutputList outputs = config->outputs(); |
|
QPoint globalPos = widgetOnScreen->parentWidget() ? |
|
widgetOnScreen->mapToGlobal(widgetOnScreen->pos()): |
|
widgetOnScreen->pos(); |
|
QRect widgetRect(globalPos, widgetOnScreen->size()); |
|
|
|
KScreen::Output* selectedOutput = 0; |
|
int maxArea = 0; |
|
Q_FOREACH(KScreen::Output *output, outputs) |
|
{ |
|
if (output->currentMode()) |
|
{ |
|
QRect outputRect(output->pos(),output->currentMode()->size()); |
|
QRect intersection = outputRect.intersected(widgetRect); |
|
int area = intersection.width()*intersection.height(); |
|
if (area > maxArea) |
|
{ |
|
maxArea = area; |
|
selectedOutput = output; |
|
} |
|
} |
|
} |
|
|
|
if (selectedOutput) |
|
{ |
|
qCDebug(OkularCoreDebug) << "Found widget at output #" << selectedOutput->id(); |
|
QRect outputRect(selectedOutput->pos(),selectedOutput->currentMode()->size()); |
|
QSize szMM = selectedOutput->sizeMm(); |
|
qCDebug(OkularCoreDebug) << "Output size is (mm) " << szMM; |
|
qCDebug(OkularCoreDebug) << "Output rect is " << outputRect; |
|
if (selectedOutput->edid()) { |
|
qCDebug(OkularCoreDebug) << "EDID WxH (cm): " << selectedOutput->edid()->width() << 'x' << selectedOutput->edid()->height(); |
|
} |
|
if (szMM.width() > 0 && szMM.height() > 0 && outputRect.width() > 0 && outputRect.height() > 0 |
|
&& selectedOutput->edid() |
|
&& qAbs(static_cast<int>(selectedOutput->edid()->width()*10) - szMM.width()) < 10 |
|
&& qAbs(static_cast<int>(selectedOutput->edid()->height()*10) - szMM.height()) < 10) |
|
{ |
|
// sizes in EDID seem to be consistent |
|
QSizeF res(static_cast<qreal>(outputRect.width())*25.4/szMM.width(), |
|
static_cast<qreal>(outputRect.height())*25.4/szMM.height()); |
|
if (!selectedOutput->isHorizontal()) |
|
{ |
|
qCDebug(OkularCoreDebug) << "Output is vertical, transposing DPI rect"; |
|
res.transpose(); |
|
} |
|
if (qAbs(res.width() - res.height()) / qMin(res.height(), res.width()) < 0.05) { |
|
return res; |
|
} else { |
|
qCDebug(OkularCoreDebug) << "KScreen calculation returned a non square dpi." << res << ". Falling back"; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
qCDebug(OkularCoreDebug) << "Didn't find a KScreen selectedOutput to calculate DPI. Falling back"; |
|
} |
|
} |
|
else |
|
{ |
|
qCDebug(OkularCoreDebug) << "Didn't find a KScreen config to calculate DPI. Falling back"; |
|
} |
|
#endif |
|
} |
|
// this is also fallback for LibKScreen branch if KScreen::Output |
|
// for particular widget was not found |
|
QSizeF res = QSizeF(realDpiX(), realDpiY()); |
|
if (qAbs(res.width() - res.height()) / qMin(res.height(), res.width()) < 0.05) { |
|
return res; |
|
} else { |
|
qCDebug(OkularCoreDebug) << "QDesktopWidget calculation returned a non square dpi." << res << ". Falling back"; |
|
} |
|
|
|
res = QSizeF(dpiX(), dpiY()); |
|
if (qAbs(res.width() - res.height()) / qMin(res.height(), res.width()) < 0.05) { |
|
return res; |
|
} else { |
|
qCDebug(OkularCoreDebug) << "QX11Info returned a non square dpi." << res << ". Falling back"; |
|
} |
|
|
|
res = QSizeF(72, 72); |
|
return res; |
|
} |
|
|
|
#elif defined(Q_OS_MAC) |
|
/* |
|
* Code copied from http://developer.apple.com/qa/qa2001/qa1217.html |
|
*/ |
|
// Handy utility function for retrieving an int from a CFDictionaryRef |
|
static int GetIntFromDictionaryForKey( CFDictionaryRef desc, CFStringRef key ) |
|
{ |
|
CFNumberRef value; |
|
int num = 0; |
|
if ( (value = (CFNumberRef)CFDictionaryGetValue(desc, key)) == NULL || CFGetTypeID(value) != CFNumberGetTypeID()) |
|
return 0; |
|
CFNumberGetValue(value, kCFNumberIntType, &num); |
|
return num; |
|
} |
|
|
|
static CGDisplayErr GetDisplayDPI( CFDictionaryRef displayModeDict, CGDirectDisplayID displayID, |
|
double *horizontalDPI, double *verticalDPI ) |
|
{ |
|
CGDisplayErr err = kCGErrorFailure; |
|
io_connect_t displayPort; |
|
CFDictionaryRef displayDict; |
|
|
|
// Grab a connection to IOKit for the requested display |
|
displayPort = CGDisplayIOServicePort( displayID ); |
|
if ( displayPort != MACH_PORT_NULL ) |
|
{ |
|
// Find out what IOKit knows about this display |
|
displayDict = IODisplayCreateInfoDictionary(displayPort, 0); |
|
if ( displayDict != NULL ) |
|
{ |
|
const double mmPerInch = 25.4; |
|
double horizontalSizeInInches = |
|
(double)GetIntFromDictionaryForKey(displayDict, |
|
CFSTR(kDisplayHorizontalImageSize)) / mmPerInch; |
|
double verticalSizeInInches = |
|
(double)GetIntFromDictionaryForKey(displayDict, |
|
CFSTR(kDisplayVerticalImageSize)) / mmPerInch; |
|
|
|
// Make sure to release the dictionary we got from IOKit |
|
CFRelease(displayDict); |
|
|
|
// Now we can calculate the actual DPI |
|
// with information from the displayModeDict |
|
*horizontalDPI = |
|
(double)GetIntFromDictionaryForKey( displayModeDict, kCGDisplayWidth ) |
|
/ horizontalSizeInInches; |
|
*verticalDPI = (double)GetIntFromDictionaryForKey( displayModeDict, |
|
kCGDisplayHeight ) / verticalSizeInInches; |
|
err = CGDisplayNoErr; |
|
} |
|
} |
|
return err; |
|
} |
|
|
|
double Utils::dpiX() |
|
{ |
|
double x,y; |
|
CGDisplayErr err = GetDisplayDPI( CGDisplayCurrentMode(kCGDirectMainDisplay), |
|
kCGDirectMainDisplay, |
|
&x, &y ); |
|
|
|
return err == CGDisplayNoErr ? x : 72.0; |
|
} |
|
|
|
double Utils::dpiY() |
|
{ |
|
double x,y; |
|
CGDisplayErr err = GetDisplayDPI( CGDisplayCurrentMode(kCGDirectMainDisplay), |
|
kCGDirectMainDisplay, |
|
&x, &y ); |
|
|
|
return err == CGDisplayNoErr ? y : 72.0; |
|
} |
|
|
|
double Utils::realDpiX() |
|
{ |
|
return dpiX(); |
|
} |
|
|
|
double Utils::realDpiY() |
|
{ |
|
return dpiY(); |
|
} |
|
|
|
QSizeF Utils::realDpi(QWidget*) |
|
{ |
|
return QSizeF(realDpiX(), realDpiY()); |
|
} |
|
#else |
|
|
|
double Utils::dpiX() |
|
{ |
|
return QDesktopWidget().physicalDpiX(); |
|
} |
|
|
|
double Utils::dpiY() |
|
{ |
|
return QDesktopWidget().physicalDpiY(); |
|
} |
|
|
|
double Utils::realDpiX() |
|
{ |
|
return dpiX(); |
|
} |
|
|
|
double Utils::realDpiY() |
|
{ |
|
return dpiY(); |
|
} |
|
|
|
QSizeF Utils::realDpi(QWidget*) |
|
{ |
|
return QSizeF(realDpiX(), realDpiY()); |
|
} |
|
#endif |
|
|
|
inline static bool isWhite( QRgb argb ) { |
|
return ( argb & 0xFFFFFF ) == 0xFFFFFF; // ignore alpha |
|
} |
|
|
|
NormalizedRect Utils::imageBoundingBox( const QImage * image ) |
|
{ |
|
if ( !image ) |
|
return NormalizedRect(); |
|
|
|
int width = image->width(); |
|
int height = image->height(); |
|
int left, top, bottom, right, x, y; |
|
|
|
#ifdef BBOX_DEBUG |
|
QTime time; |
|
time.start(); |
|
#endif |
|
|
|
// Scan pixels for top non-white |
|
for ( top = 0; top < height; ++top ) |
|
for ( x = 0; x < width; ++x ) |
|
if ( !isWhite( image->pixel( x, top ) ) ) |
|
goto got_top; |
|
return NormalizedRect( 0, 0, 0, 0 ); // the image is blank |
|
got_top: |
|
left = right = x; |
|
|
|
// Scan pixels for bottom non-white |
|
for ( bottom = height-1; bottom >= top; --bottom ) |
|
for ( x = width-1; x >= 0; --x ) |
|
if ( !isWhite( image->pixel( x, bottom ) ) ) |
|
goto got_bottom; |
|
Q_ASSERT( 0 ); // image changed?! |
|
got_bottom: |
|
if ( x < left ) |
|
left = x; |
|
if ( x > right ) |
|
right = x; |
|
|
|
// Scan for leftmost and rightmost (we already found some bounds on these): |
|
for ( y = top; y <= bottom && ( left > 0 || right < width-1 ); ++y ) |
|
{ |
|
for ( x = 0; x < left; ++x ) |
|
if ( !isWhite( image->pixel( x, y ) ) ) |
|
left = x; |
|
for ( x = width-1; x > right+1; --x ) |
|
if ( !isWhite( image->pixel( x, y ) ) ) |
|
right = x; |
|
} |
|
|
|
NormalizedRect bbox( QRect( left, top, ( right - left + 1), ( bottom - top + 1 ) ), |
|
image->width(), image->height() ); |
|
|
|
#ifdef BBOX_DEBUG |
|
qCDebug(OkularCoreDebug) << "Computed bounding box" << bbox << "in" << time.elapsed() << "ms"; |
|
#endif |
|
|
|
return bbox; |
|
} |
|
|
|
void Okular::copyQIODevice( QIODevice *from, QIODevice *to ) |
|
{ |
|
QByteArray buffer( 65536, '\0' ); |
|
qint64 read = 0; |
|
qint64 written = 0; |
|
while ( ( read = from->read( buffer.data(), buffer.size() ) ) > 0 ) |
|
{ |
|
written = to->write( buffer.constData(), read ); |
|
if ( read != written ) |
|
break; |
|
} |
|
} |
|
|
|
QTransform Okular::buildRotationMatrix(Rotation rotation) |
|
{ |
|
QTransform matrix; |
|
matrix.rotate( (int)rotation * 90 ); |
|
|
|
switch ( rotation ) |
|
{ |
|
case Rotation90: |
|
matrix.translate( 0, -1 ); |
|
break; |
|
case Rotation180: |
|
matrix.translate( -1, -1 ); |
|
break; |
|
case Rotation270: |
|
matrix.translate( -1, 0 ); |
|
break; |
|
default: ; |
|
} |
|
|
|
return matrix; |
|
} |
|
|
|
/* kate: replace-tabs on; indent-width 4; */
|
|
|