Commit 6bec7db1 authored by Boudewijn Rempt's avatar Boudewijn Rempt

Merge remote-tracking branch 'origin' into krita-psd-rempt

parents 14104d26 39b5d580
...@@ -131,12 +131,15 @@ struct exrConverter::Private { ...@@ -131,12 +131,15 @@ struct exrConverter::Private {
bool showNotifications; bool showNotifications;
template <typename T> template <class WrapperType>
void unmultiplyAlpha(Rgba<T> *pixel); void unmultiplyAlpha(typename WrapperType::pixel_type *pixel);
template<typename _T_> template<typename _T_>
void decodeData4(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype); void decodeData4(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype);
template<typename _T_>
void decodeData1(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype);
QDomDocument loadExtraLayersInfo(const Imf::Header &header); QDomDocument loadExtraLayersInfo(const Imf::Header &header);
bool checkExtraLayersInfoConsistent(const QDomDocument &doc, std::set<std::string> exrLayerNames); bool checkExtraLayersInfoConsistent(const QDomDocument &doc, std::set<std::string> exrLayerNames);
...@@ -189,93 +192,120 @@ const KoColorSpace* kisTypeToColorSpace(QString model, ImageType imageType) ...@@ -189,93 +192,120 @@ const KoColorSpace* kisTypeToColorSpace(QString model, ImageType imageType)
} }
} }
template<typename _T_> template <typename T>
void decodeData1(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype) static inline T alphaEpsilon()
{ {
QVector<_T_> pixels(width*height); return static_cast<T>(HALF_EPSILON);
}
Q_ASSERT(info.channelMap.contains("G")); template <typename T>
dbgFile << "G -> " << info.channelMap["G"]; static inline T alphaNoiseThreshold()
{
return static_cast<T>(0.01); // 1%
}
for (int y = 0; y < height; ++y) { template <typename T>
Imf::FrameBuffer frameBuffer; struct RgbPixelWrapper
_T_* frameBufferData = (pixels.data()) - xstart - (ystart + y) * width; {
frameBuffer.insert(info.channelMap["G"].toLatin1().constData(), typedef T channel_type;
Imf::Slice(ptype, (char *) frameBufferData, typedef Rgba<T> pixel_type;
sizeof(_T_) * 1,
sizeof(_T_) * width));
file.setFrameBuffer(frameBuffer); RgbPixelWrapper(Rgba<T> &_pixel) : pixel(_pixel) {}
file.readPixels(ystart + y);
_T_ *rgba = pixels.data();
KisHLineIteratorSP it = layer->paintDevice()->createHLineIteratorNG(0, y, width);
do {
_T_ unmultipliedRed = *rgba; inline T alpha() const {
return pixel.a;
}
_T_* dst = reinterpret_cast<_T_*>(it->rawData()); inline bool checkMultipliedColorsConsistent() const {
return !(pixel.a < alphaEpsilon<T>() &&
(pixel.r > 0.0 ||
pixel.g > 0.0 ||
pixel.b > 0.0));
}
*dst = unmultipliedRed; inline bool checkUnmultipliedColorsConsistent(const Rgba<T> &mult) const {
const T alpha = pixel.a;
return alpha >= alphaNoiseThreshold<T>() ||
(pixel.r * alpha == mult.r &&
pixel.g * alpha == mult.g &&
pixel.b * alpha == mult.b);
}
++rgba; inline void setUnmultiplied(const Rgba<T> &mult, qreal newAlpha) {
} while (it->nextPixel()); pixel.r = mult.r / newAlpha;
pixel.g = mult.g / newAlpha;
pixel.b = mult.b / newAlpha;
pixel.a = newAlpha;
} }
} Rgba<T> &pixel;
};
template <typename T> template <typename T>
static inline T alphaEpsilon() struct GrayPixelWrapper
{ {
return static_cast<T>(HALF_EPSILON); typedef T channel_type;
} typedef typename KoGrayTraits<T>::Pixel pixel_type;
template <typename T> GrayPixelWrapper(pixel_type &_pixel) : pixel(_pixel) {}
static inline T alphaNoiseThreshold()
{
return static_cast<T>(0.01); // 1%
}
template <typename T> inline T alpha() const {
void exrConverter::Private::unmultiplyAlpha(Rgba<T> *pixel) return pixel.alpha;
}
inline bool checkMultipliedColorsConsistent() const {
return !(pixel.alpha < alphaEpsilon<T>() &&
pixel.gray > 0.0);
}
inline bool checkUnmultipliedColorsConsistent(const pixel_type &mult) const {
const T alpha = pixel.alpha;
return alpha >= alphaNoiseThreshold<T>() ||
pixel.gray * alpha == mult.gray;
}
inline void setUnmultiplied(const pixel_type &mult, qreal newAlpha) {
pixel.gray = mult.gray / newAlpha;
pixel.alpha = newAlpha;
}
pixel_type &pixel;
};
template <class WrapperType>
void exrConverter::Private::unmultiplyAlpha(typename WrapperType::pixel_type *pixel)
{ {
if (pixel->a < alphaEpsilon<T>() && typedef typename WrapperType::pixel_type pixel_type;
(pixel->r > 0.0 || typedef typename WrapperType::channel_type channel_type;
pixel->g > 0.0 ||
pixel->b > 0.0)) { WrapperType srcPixel(*pixel);
if (!srcPixel.checkMultipliedColorsConsistent()) {
bool alphaWasModified = false; bool alphaWasModified = false;
T newAlpha = 0.0; channel_type newAlpha = srcPixel.alpha();
T r; pixel_type __dstPixelData;
T g; WrapperType dstPixel(__dstPixelData);
T b;
/** /**
* Division by a tiny alpha may result in an overflow of half * Division by a tiny alpha may result in an overflow of half
* value. That is why we use safe iterational approach. * value. That is why we use safe iterational approach.
*/ */
while (1) { while (1) {
r = pixel->r / newAlpha; dstPixel.setUnmultiplied(srcPixel.pixel, newAlpha);
g = pixel->g / newAlpha;
b = pixel->b / newAlpha;
if (newAlpha >= alphaNoiseThreshold<T>() ||
(r * newAlpha == pixel->r &&
g * newAlpha == pixel->g &&
b * newAlpha == pixel->b)) {
if (dstPixel.checkUnmultipliedColorsConsistent(srcPixel.pixel)) {
break; break;
} }
newAlpha += alphaEpsilon<T>(); newAlpha += alphaEpsilon<channel_type>();
alphaWasModified = true; alphaWasModified = true;
} }
pixel->r = r; *pixel = dstPixel.pixel;
pixel->g = g;
pixel->b = b;
pixel->a = newAlpha;
if (alphaWasModified && if (alphaWasModified &&
!this->warnedAboutChangedAlpha) { !this->warnedAboutChangedAlpha) {
...@@ -289,7 +319,9 @@ void exrConverter::Private::unmultiplyAlpha(Rgba<T> *pixel) ...@@ -289,7 +319,9 @@ void exrConverter::Private::unmultiplyAlpha(Rgba<T> *pixel)
"<nl/><nl/>" "<nl/><nl/>"
"This will hardly make any visual difference just keep it in mind." "This will hardly make any visual difference just keep it in mind."
"<nl/><nl/>" "<nl/><nl/>"
"<note>Modified alpha will have a range from <numid>%1</numid> to <numid>%2</numid></note>", alphaEpsilon<T>(), alphaNoiseThreshold<T>()); "<note>Modified alpha will have a range from <numid>%1</numid> to <numid>%2</numid></note>",
alphaEpsilon<channel_type>(),
alphaNoiseThreshold<channel_type>());
if (this->showNotifications) { if (this->showNotifications) {
KMessageBox::information(0, msg, i18nc("@title:window", "EXR image will be modified"), "dontNotifyEXRChangedAgain"); KMessageBox::information(0, msg, i18nc("@title:window", "EXR image will be modified"), "dontNotifyEXRChangedAgain");
...@@ -300,10 +332,8 @@ void exrConverter::Private::unmultiplyAlpha(Rgba<T> *pixel) ...@@ -300,10 +332,8 @@ void exrConverter::Private::unmultiplyAlpha(Rgba<T> *pixel)
this->warnedAboutChangedAlpha = true; this->warnedAboutChangedAlpha = true;
} }
} else if (pixel->a > 0.0) { } else if (srcPixel.alpha() > 0.0) {
pixel->r /= pixel->a; srcPixel.setUnmultiplied(srcPixel.pixel, srcPixel.alpha());
pixel->g /= pixel->a;
pixel->b /= pixel->a;
} }
} }
...@@ -361,7 +391,7 @@ void exrConverter::Private::decodeData4(Imf::InputFile& file, ExrPaintLayerInfo& ...@@ -361,7 +391,7 @@ void exrConverter::Private::decodeData4(Imf::InputFile& file, ExrPaintLayerInfo&
do { do {
if (hasAlpha) { if (hasAlpha) {
unmultiplyAlpha(rgba); unmultiplyAlpha<RgbPixelWrapper<_T_> >(rgba);
} }
typename KoRgbTraits<_T_>::Pixel* dst = reinterpret_cast<typename KoRgbTraits<_T_>::Pixel*>(it->rawData()); typename KoRgbTraits<_T_>::Pixel* dst = reinterpret_cast<typename KoRgbTraits<_T_>::Pixel*>(it->rawData());
...@@ -382,6 +412,61 @@ void exrConverter::Private::decodeData4(Imf::InputFile& file, ExrPaintLayerInfo& ...@@ -382,6 +412,61 @@ void exrConverter::Private::decodeData4(Imf::InputFile& file, ExrPaintLayerInfo&
} }
template<typename _T_>
void exrConverter::Private::decodeData1(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype)
{
typedef typename GrayPixelWrapper<_T_>::channel_type channel_type;
typedef typename GrayPixelWrapper<_T_>::pixel_type pixel_type;
KIS_ASSERT_RECOVER_RETURN(
layer->paintDevice()->colorSpace()->colorModelId() == GrayAColorModelID);
QVector<pixel_type> pixels(width);
Q_ASSERT(info.channelMap.contains("G"));
dbgFile << "G -> " << info.channelMap["G"];
bool hasAlpha = info.channelMap.contains("A");
dbgFile << "Has Alpha:" << hasAlpha;
for (int y = 0; y < height; ++y) {
Imf::FrameBuffer frameBuffer;
pixel_type* frameBufferData = (pixels.data()) - xstart - (ystart + y) * width;
frameBuffer.insert(info.channelMap["G"].toLatin1().constData(),
Imf::Slice(ptype, (char *) &frameBufferData->gray,
sizeof(pixel_type) * 1,
sizeof(pixel_type) * width));
if (hasAlpha) {
frameBuffer.insert(info.channelMap["A"].toLatin1().constData(),
Imf::Slice(ptype, (char *) &frameBufferData->alpha,
sizeof(pixel_type) * 1,
sizeof(pixel_type) * width));
}
file.setFrameBuffer(frameBuffer);
file.readPixels(ystart + y);
pixel_type *srcPtr = pixels.data();
KisHLineIteratorSP it = layer->paintDevice()->createHLineIteratorNG(0, y, width);
do {
if (hasAlpha) {
unmultiplyAlpha<GrayPixelWrapper<_T_> >(srcPtr);
}
pixel_type* dstPtr = reinterpret_cast<pixel_type*>(it->rawData());
dstPtr->gray = srcPtr->gray;
dstPtr->alpha = hasAlpha ? srcPtr->alpha : channel_type(1.0);
++srcPtr;
} while (it->nextPixel());
}
}
bool recCheckGroup(const ExrGroupLayerInfo& group, QStringList list, int idx1, int idx2) bool recCheckGroup(const ExrGroupLayerInfo& group, QStringList list, int idx1, int idx2)
{ {
if (idx1 > idx2) return true; if (idx1 > idx2) return true;
...@@ -564,7 +649,7 @@ KisImageBuilder_Result exrConverter::decode(const KUrl& uri) ...@@ -564,7 +649,7 @@ KisImageBuilder_Result exrConverter::decode(const KUrl& uri)
QString modelId; QString modelId;
if (info.channelMap.size() == 1) { if (info.channelMap.size() == 1) {
modelId = GrayColorModelID.id(); modelId = GrayAColorModelID.id();
QString key = info.channelMap.begin().key(); QString key = info.channelMap.begin().key();
if (key != "G") { if (key != "G") {
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(key, "G")); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(key, "G"));
...@@ -572,8 +657,29 @@ KisImageBuilder_Result exrConverter::decode(const KUrl& uri) ...@@ -572,8 +657,29 @@ KisImageBuilder_Result exrConverter::decode(const KUrl& uri)
info.channelMap.clear(); info.channelMap.clear();
info.channelMap["G"] = channel; info.channelMap["G"] = channel;
} }
} } else if (info.channelMap.size() == 2) {
else if (info.channelMap.size() == 3 || info.channelMap.size() == 4) { modelId = GrayAColorModelID.id();
QMap<QString,QString>::iterator it = info.channelMap.begin();
QMap<QString,QString>::iterator end = info.channelMap.end();
QString failingChannelKey;
for (; it != end; ++it) {
if (it.key() != "G" && it.key() != "A") {
failingChannelKey = it.key();
break;
}
}
info.remappedChannels.push_back(
ExrPaintLayerInfo::Remap(failingChannelKey, "G"));
QString failingChannelValue = info.channelMap[failingChannelKey];
info.channelMap.remove(failingChannelKey);
info.channelMap["G"] = failingChannelValue;
} else if (info.channelMap.size() == 3 || info.channelMap.size() == 4) {
if (info.channelMap.contains("R") && info.channelMap.contains("G") && info.channelMap.contains("B")) { if (info.channelMap.contains("R") && info.channelMap.contains("G") && info.channelMap.contains("B")) {
modelId = RGBAColorModelID.id(); modelId = RGBAColorModelID.id();
...@@ -672,13 +778,14 @@ KisImageBuilder_Result exrConverter::decode(const KUrl& uri) ...@@ -672,13 +778,14 @@ KisImageBuilder_Result exrConverter::decode(const KUrl& uri)
switch (info.channelMap.size()) { switch (info.channelMap.size()) {
case 1: case 1:
case 2:
// Decode the data // Decode the data
switch (imageType) { switch (imageType) {
case IT_FLOAT16: case IT_FLOAT16:
decodeData1<half>(file, info, layer, width, dx, dy, height, Imf::HALF); m_d->decodeData1<half>(file, info, layer, width, dx, dy, height, Imf::HALF);
break; break;
case IT_FLOAT32: case IT_FLOAT32:
decodeData1<float>(file, info, layer, width, dx, dy, height, Imf::FLOAT); m_d->decodeData1<float>(file, info, layer, width, dx, dy, height, Imf::FLOAT);
break; break;
case IT_UNKNOWN: case IT_UNKNOWN:
case IT_UNSUPPORTED: case IT_UNSUPPORTED:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment