mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2026-06-10 01:59:11 -04:00
Resolution calculations performed by functions
Added functions for dpi <-> ppm transformations and used in all plugins.
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "chunks_p.h"
|
||||
#include "packbits_p.h"
|
||||
#include "util_p.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QColor>
|
||||
@@ -656,7 +657,7 @@ quint16 DPIChunk::dpiX() const
|
||||
if (bytes() < 4) {
|
||||
return 0;
|
||||
}
|
||||
return i16(data().at(1), data().at(0));
|
||||
return ui16(data().at(1), data().at(0));
|
||||
}
|
||||
|
||||
quint16 DPIChunk::dpiY() const
|
||||
@@ -664,17 +665,17 @@ quint16 DPIChunk::dpiY() const
|
||||
if (bytes() < 4) {
|
||||
return 0;
|
||||
}
|
||||
return i16(data().at(3), data().at(2));
|
||||
return ui16(data().at(3), data().at(2));
|
||||
}
|
||||
|
||||
qint32 DPIChunk::dotsPerMeterX() const
|
||||
{
|
||||
return qRound(dpiX() / 25.4 * 1000);
|
||||
return dpi2ppm(dpiX());
|
||||
}
|
||||
|
||||
qint32 DPIChunk::dotsPerMeterY() const
|
||||
{
|
||||
return qRound(dpiY() / 25.4 * 1000);
|
||||
return dpi2ppm(dpiY());
|
||||
}
|
||||
|
||||
bool DPIChunk::innerReadStructure(QIODevice *d)
|
||||
@@ -708,7 +709,7 @@ quint16 XBMIChunk::dpiX() const
|
||||
if (bytes() < 6) {
|
||||
return 0;
|
||||
}
|
||||
return i16(data().at(3), data().at(2));
|
||||
return ui16(data().at(3), data().at(2));
|
||||
}
|
||||
|
||||
quint16 XBMIChunk::dpiY() const
|
||||
@@ -716,7 +717,7 @@ quint16 XBMIChunk::dpiY() const
|
||||
if (bytes() < 6) {
|
||||
return 0;
|
||||
}
|
||||
return i16(data().at(5), data().at(4));
|
||||
return ui16(data().at(5), data().at(4));
|
||||
}
|
||||
|
||||
XBMIChunk::PictureType XBMIChunk::pictureType() const
|
||||
@@ -2886,6 +2887,7 @@ static QByteArray pchgFastDecomp(const QByteArray& input, int treeSize, int orig
|
||||
|
||||
bool PCHGChunk::initialize(const QList<QRgb> &cmapPalette, qint32 height)
|
||||
{
|
||||
Q_UNUSED(height)
|
||||
auto dt = data().mid(20);
|
||||
if (compression() == PCHGChunk::Compression::Huffman) {
|
||||
QDataStream ds(dt);
|
||||
|
||||
@@ -288,8 +288,14 @@ static void readMetadata(const Imf::Header &header, QImage &image)
|
||||
if (auto pixelAspectRatio = header.findTypedAttribute<Imf::FloatAttribute>("pixelAspectRatio")) {
|
||||
par = pixelAspectRatio->value();
|
||||
}
|
||||
image.setDotsPerMeterX(qRound(xDensity->value() * 100.0 / 2.54));
|
||||
image.setDotsPerMeterY(qRound(xDensity->value() * par * 100.0 / 2.54));
|
||||
auto hres = dpi2ppm(xDensity->value());
|
||||
if (hres > 0) {
|
||||
image.setDotsPerMeterX(hres);
|
||||
}
|
||||
auto vres = dpi2ppm(xDensity->value() * par);
|
||||
if (vres > 0) {
|
||||
image.setDotsPerMeterY(vres);
|
||||
}
|
||||
}
|
||||
|
||||
// Non-standard attribute
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
#include <QTemporaryDir>
|
||||
|
||||
#include <JXRGlue.h>
|
||||
#include <cfenv>
|
||||
#include <cstring>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(LOG_JXRPLUGIN)
|
||||
@@ -978,13 +977,12 @@ bool JXRHandler::read(QImage *outImage)
|
||||
if (auto err = d->pDecoder->GetResolution(d->pDecoder, &hres, &vres)) {
|
||||
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() error while reading resolution:" << err;
|
||||
} else {
|
||||
std::feclearexcept(FE_ALL_EXCEPT);
|
||||
const int hdpm = std::lround(hres * 1000 / 25.4);
|
||||
const int vdpm = std::lround(vres * 1000 / 25.4);
|
||||
if (std::fetestexcept(FE_INVALID)) {
|
||||
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() resolution is out of range:" << hres << vres;
|
||||
} else {
|
||||
const qint32 hdpm = dpi2ppm(hres);
|
||||
if (hdpm > 0) {
|
||||
img.setDotsPerMeterX(hdpm);
|
||||
}
|
||||
const qint32 vdpm = dpi2ppm(vres);
|
||||
if (vdpm > 0) {
|
||||
img.setDotsPerMeterY(vdpm);
|
||||
}
|
||||
}
|
||||
@@ -1132,7 +1130,7 @@ bool JXRHandler::write(const QImage &image)
|
||||
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::write() error while setting the image size:" << err;
|
||||
return false;
|
||||
}
|
||||
if (auto err = d->pEncoder->SetResolution(d->pEncoder, qi.dotsPerMeterX() * 25.4 / 1000, qi.dotsPerMeterY() * 25.4 / 1000)) {
|
||||
if (auto err = d->pEncoder->SetResolution(d->pEncoder, dppm2dpi(qi.dotsPerMeterX()), dppm2dpi(qi.dotsPerMeterY()))) {
|
||||
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::write() error while setting the image resolution:" << err;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
#include <QStringDecoder>
|
||||
#include <QTimeZone>
|
||||
|
||||
#include <cfenv>
|
||||
|
||||
// TIFF 6 specs
|
||||
#define TIFF_IMAGEWIDTH 0x100
|
||||
#define TIFF_IMAGEHEIGHT 0x101
|
||||
@@ -1195,30 +1193,17 @@ void MicroExif::updateImageMetadata(QImage &targetImage, bool replaceExisting) c
|
||||
}
|
||||
}
|
||||
|
||||
static std::optional<int> convertToDotsPerMeter(const double value)
|
||||
{
|
||||
if (value <= 0) {
|
||||
return {};
|
||||
}
|
||||
std::feclearexcept(FE_ALL_EXCEPT);
|
||||
const int rounded = std::lround(value / 25.4 * 1000);
|
||||
if (std::fetestexcept(FE_INVALID)) {
|
||||
return {};
|
||||
}
|
||||
return rounded;
|
||||
}
|
||||
|
||||
bool MicroExif::updateImageResolution(QImage &targetImage)
|
||||
{
|
||||
const std::optional<int> hdpm = convertToDotsPerMeter(horizontalResolution());
|
||||
const std::optional<int> vdpm = convertToDotsPerMeter(verticalResolution());
|
||||
if (hdpm) {
|
||||
targetImage.setDotsPerMeterX(*hdpm);
|
||||
const auto hdpm = dpi2ppm(horizontalResolution());
|
||||
if (hdpm > 0) {
|
||||
targetImage.setDotsPerMeterX(hdpm);
|
||||
}
|
||||
if (vdpm) {
|
||||
targetImage.setDotsPerMeterY(*vdpm);
|
||||
const auto vdpm = dpi2ppm(verticalResolution());
|
||||
if (vdpm > 0) {
|
||||
targetImage.setDotsPerMeterY(vdpm);
|
||||
}
|
||||
return hdpm || vdpm;
|
||||
return (hdpm > 0) || (vdpm > 0);
|
||||
}
|
||||
|
||||
MicroExif MicroExif::fromByteArray(const QByteArray &ba, bool searchHeader)
|
||||
@@ -1288,8 +1273,8 @@ MicroExif MicroExif::fromImage(const QImage &image)
|
||||
// Image properties
|
||||
exif.setWidth(image.width());
|
||||
exif.setHeight(image.height());
|
||||
exif.setHorizontalResolution(image.dotsPerMeterX() * 25.4 / 1000);
|
||||
exif.setVerticalResolution(image.dotsPerMeterY() * 25.4 / 1000);
|
||||
exif.setHorizontalResolution(dppm2dpi(image.dotsPerMeterX()));
|
||||
exif.setVerticalResolution(dppm2dpi(image.dotsPerMeterY()));
|
||||
exif.setColorSpace(image.colorSpace());
|
||||
|
||||
// TIFF strings
|
||||
|
||||
@@ -888,8 +888,14 @@ bool PCXHandler::read(QImage *outImage)
|
||||
return false;
|
||||
}
|
||||
|
||||
img.setDotsPerMeterX(qRound(header.HDpi / 25.4 * 1000));
|
||||
img.setDotsPerMeterY(qRound(header.YDpi / 25.4 * 1000));
|
||||
auto hres = dpi2ppm(header.HDpi);
|
||||
if (hres > 0) {
|
||||
img.setDotsPerMeterX(hres);
|
||||
}
|
||||
auto vres = dpi2ppm(header.YDpi);
|
||||
if (vres > 0) {
|
||||
img.setDotsPerMeterY(vres);
|
||||
}
|
||||
*outImage = img;
|
||||
return true;
|
||||
}
|
||||
@@ -915,8 +921,8 @@ bool PCXHandler::write(const QImage &image)
|
||||
header.YMin = 0;
|
||||
header.XMax = w - 1;
|
||||
header.YMax = h - 1;
|
||||
header.HDpi = qRound(image.dotsPerMeterX() * 25.4 / 1000);
|
||||
header.YDpi = qRound(image.dotsPerMeterY() * 25.4 / 1000);
|
||||
header.HDpi = qRoundOrZero_T<quint16>(dppm2dpi(image.dotsPerMeterX()));
|
||||
header.YDpi = qRoundOrZero_T<quint16>(dppm2dpi(image.dotsPerMeterY()));
|
||||
header.Reserved = 0;
|
||||
header.PaletteInfo = 1;
|
||||
|
||||
|
||||
@@ -596,17 +596,21 @@ static bool setResolution(QImage &img, const PSDImageResourceSection &irs)
|
||||
s >> i32; // Horizontal resolution in pixels per inch.
|
||||
if (i32 <= 0)
|
||||
return false;
|
||||
auto hres = fixedPointToDouble(i32);
|
||||
auto hres = dpi2ppm(fixedPointToDouble(i32));
|
||||
|
||||
s.skipRawData(4); // Display data (not used here)
|
||||
|
||||
s >> i32; // Vertial resolution in pixels per inch.
|
||||
if (i32 <= 0)
|
||||
return false;
|
||||
auto vres = fixedPointToDouble(i32);
|
||||
auto vres = dpi2ppm(fixedPointToDouble(i32));
|
||||
|
||||
img.setDotsPerMeterX(hres * 1000 / 25.4);
|
||||
img.setDotsPerMeterY(vres * 1000 / 25.4);
|
||||
if (hres > 0) {
|
||||
img.setDotsPerMeterX(hres);
|
||||
}
|
||||
if (vres > 0) {
|
||||
img.setDotsPerMeterY(vres);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ public:
|
||||
auto v = QString::fromLatin1(pchar_t(res.data()), res.size()).toDouble(&ok);
|
||||
if (ok && v > 0) {
|
||||
if (m_pb._unitsOfMeasurement) { // Inches
|
||||
return qRoundOrZero(width() / v / 25.4 * 1000);
|
||||
return dpi2ppm(width() / v);
|
||||
}
|
||||
// Millimeters
|
||||
return qRoundOrZero(width() / v * 1000);
|
||||
@@ -230,7 +230,7 @@ public:
|
||||
auto v = QString::fromLatin1(pchar_t(res.data()), res.size()).toDouble(&ok);
|
||||
if (ok && v > 0) {
|
||||
if (m_pb._unitsOfMeasurement) { // Inches
|
||||
return qRoundOrZero(height() / v / 25.4 * 1000);
|
||||
return dpi2ppm(height() / v);
|
||||
}
|
||||
// Millimeters
|
||||
return qRoundOrZero(height() / v * 1000);
|
||||
|
||||
@@ -66,13 +66,70 @@ inline QImage imageAlloc(qint32 width, qint32 height, const QImage::Format &form
|
||||
return imageAlloc(QSize(width, height), format);
|
||||
}
|
||||
|
||||
inline double qRoundOrZero(double d)
|
||||
template<class TI, class SF> // SF = source FP, TI = target INT
|
||||
TI qRoundOrZero_T(SF d, bool *ok = nullptr)
|
||||
{
|
||||
// If the value d is outside the range of int, the behavior is undefined.
|
||||
if (d > std::numeric_limits<int>::max()) {
|
||||
// checks for undefined behavior
|
||||
if (qIsNaN(d) || qIsInf(d) || d < SF() || d > SF(std::numeric_limits<TI>::max())) {
|
||||
if (ok) {
|
||||
*ok = false;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (ok) {
|
||||
*ok = true;
|
||||
}
|
||||
return qRound(d);
|
||||
}
|
||||
|
||||
inline qint32 qRoundOrZero(double d, bool *ok = nullptr)
|
||||
{
|
||||
return qRoundOrZero_T<qint32>(d, ok);
|
||||
}
|
||||
inline qint32 qRoundOrZero(float d, bool *ok = nullptr)
|
||||
{
|
||||
return qRoundOrZero_T<qint32>(d, ok);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief dpi2ppm
|
||||
* Converts a value from DPI to PPM.
|
||||
* \return \a dpi converted to pixel per meter.
|
||||
*/
|
||||
inline qint32 dpi2ppm(double dpi, bool *ok = nullptr)
|
||||
{
|
||||
return qRoundOrZero(dpi / double(25.4) * double(1000), ok);
|
||||
}
|
||||
inline qint32 dpi2ppm(float dpi, bool *ok = nullptr)
|
||||
{
|
||||
return qRoundOrZero(dpi / float(25.4) * float(1000), ok);
|
||||
}
|
||||
inline qint32 dpi2ppm(quint16 dpi, bool *ok = nullptr)
|
||||
{
|
||||
return qRoundOrZero(dpi / double(25.4) * double(1000), ok);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief ppm2dpi
|
||||
* Converts a value from PPM to DPI.
|
||||
* \return \a ppm converted to dot per inch.
|
||||
*/
|
||||
template<class TF, class SI> // SI = source INT, TF = target FP
|
||||
TF ppm2dpi_T(SI ppm, bool *ok = nullptr)
|
||||
{
|
||||
if (ok) {
|
||||
*ok = ppm > 0;
|
||||
}
|
||||
return ppm > 0 ? ppm * TF(25.4) / TF(1000) : TF();
|
||||
}
|
||||
|
||||
inline double dppm2dpi(qint32 ppm, bool *ok = nullptr)
|
||||
{
|
||||
return ppm2dpi_T<double>(ppm, ok);
|
||||
}
|
||||
inline float fppm2dpi(qint32 ppm, bool *ok = nullptr)
|
||||
{
|
||||
return ppm2dpi_T<float>(ppm, ok);
|
||||
}
|
||||
|
||||
#endif // UTIL_P_H
|
||||
|
||||
@@ -55,8 +55,6 @@ Q_LOGGING_CATEGORY(XCFPLUGIN, "kf.imageformats.plugins.xcf", QtWarningMsg)
|
||||
#define DISABLE_TILE_PROFILE_CONV // default uncommented (comment to use the conversion as intended by Martin)
|
||||
#define DISABLE_IMAGE_PROFILE_CONV // default uncommented (comment to use the conversion as intended by Martin)
|
||||
|
||||
const float INCHESPERMETER = (100.0f / 2.54f);
|
||||
|
||||
namespace
|
||||
{
|
||||
struct RandomTable {
|
||||
@@ -2722,17 +2720,13 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (xcf_image.x_resolution > 0 && xcf_image.y_resolution > 0) {
|
||||
const float dpmx = xcf_image.x_resolution * INCHESPERMETER;
|
||||
if (dpmx > float(std::numeric_limits<int>::max())) {
|
||||
return false;
|
||||
}
|
||||
const float dpmy = xcf_image.y_resolution * INCHESPERMETER;
|
||||
if (dpmy > float(std::numeric_limits<int>::max())) {
|
||||
return false;
|
||||
}
|
||||
image.setDotsPerMeterX((int)dpmx);
|
||||
image.setDotsPerMeterY((int)dpmy);
|
||||
const qint32 dpmx = dpi2ppm(xcf_image.x_resolution);
|
||||
if (dpmx > 0) {
|
||||
image.setDotsPerMeterX(dpmx);
|
||||
}
|
||||
const qint32 dpmy = dpi2ppm(xcf_image.y_resolution);
|
||||
if (dpmy > 0) {
|
||||
image.setDotsPerMeterY(dpmy);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user