Add image processing scanline by scanline using iterators

This commit is contained in:
Joshua Ogunyinka 2021-10-21 13:13:58 +04:00 committed by David Capello
parent 7745921a2b
commit 6e00c74f5b
2 changed files with 70 additions and 86 deletions

View File

@ -192,7 +192,6 @@ public:
} }
} }
// This method is called on images stored in RLE format
void onImageScanline(const psd::ImageData& img, void onImageScanline(const psd::ImageData& img,
const int y, const int y,
const psd::ChannelID chanID, const psd::ChannelID chanID,
@ -203,20 +202,63 @@ public:
return; return;
const int dataCount = bytes / (img.depth >= 8 ? (img.depth / 8) : 1); const int dataCount = bytes / (img.depth >= 8 ? (img.depth / 8) : 1);
for (int x = 0; x < dataCount && x < m_currentImage->width(); ++x) { uint8_t* dstGenericAddress = m_currentImage->getPixelAddress(0, y);
const color_t c = m_currentImage->getPixel(x, y);
const uint32_t pixel = getValue(data, img.depth); if (m_pixelFormat == doc::PixelFormat::IMAGE_INDEXED) {
putPixel(x, y, c, pixel, chanID, m_pixelFormat); IndexedTraits::address_t dstAddress =
(IndexedTraits::address_t)dstGenericAddress;
for (int x = 0; x < dataCount && x < m_currentImage->width(); ++x) {
*(dstAddress)++ = getNormalizedPixelValue(data, img.depth);
}
}
else if (m_pixelFormat == doc::PixelFormat::IMAGE_GRAYSCALE) {
GrayscaleTraits::address_t dstAddress =
(GrayscaleTraits::address_t)dstGenericAddress;
uint8_t v = 0, a = 0;
for (int x = 0; x < dataCount && x < m_currentImage->width(); ++x) {
const GrayscaleTraits::pixel_t pixel = *dstAddress;
const uint8_t newPixelValue = getNormalizedPixelValue(data, img.depth);
if (chanID == psd::ChannelID::Red) {
v = newPixelValue;
a = m_layerHasTransparentChannel ? graya_geta(pixel) : 255;
}
else if (chanID == psd::ChannelID::Alpha ||
chanID == psd::ChannelID::TransparencyMask) {
a = newPixelValue;
v = graya_getv(pixel);
}
*(dstAddress++) = graya(v, a);
}
}
else if (m_pixelFormat == doc::PixelFormat::IMAGE_RGB) {
RgbTraits::address_t dstAddress = (RgbTraits::address_t)dstGenericAddress;
uint8_t r, g, b, a;
for (int x = 0; x < dataCount && x < m_currentImage->width(); ++x) {
const uint8_t newPixelValue = getNormalizedPixelValue(data, img.depth);
const color_t c = *(dstAddress);
r = rgba_getr(c);
g = rgba_getg(c);
b = rgba_getb(c);
a = m_layerHasTransparentChannel ? rgba_geta(c) : 255;
if (chanID == psd::ChannelID::Red) {
r = newPixelValue;
}
else if (chanID == psd::ChannelID::Green) {
g = newPixelValue;
}
else if (chanID == psd::ChannelID::Blue) {
b = newPixelValue;
}
else if (chanID == psd::ChannelID::Alpha ||
chanID == psd::ChannelID::TransparencyMask) {
a = newPixelValue;
}
*(dstAddress++) = rgba(r, g, b, a);
}
} }
} }
private: private:
// TODO: This is meant to convert values of a channel based on its depth
std::uint32_t normalizeValue(const uint32_t value, const int depth)
{
return value;
}
inline bool hasTransparency(const size_t nchannels) inline bool hasTransparency(const size_t nchannels)
{ {
// RGBA or grayscale image with alpha channel // RGBA or grayscale image with alpha channel
@ -246,77 +288,25 @@ private:
return m_sprite; return m_sprite;
} }
std::uint32_t getValue(const std::uint8_t*& data, const int depth) std::uint8_t getNormalizedPixelValue(const std::uint8_t*& data,
const int depth)
{ {
uint32_t value = 0; if (depth == 1 || depth == 8) {
switch (depth) { return *(data++);
case 1:
case 8:
value = data[0];
++data;
return value;
case 16:
value = int(data[0]) | int(data[1] << 8);
data += 2;
return value;
case 32:
value = int(data[0] << 24) | int(data[1] << 16) | int(data[2] << 8) |
int(data[3]);
data += 4;
return value;
default:
throw std::runtime_error("invalid image depth");
} }
} else if (depth == 16) {
const uint16_t value = int(data[0]) | int(data[1] << 8);
template<typename NewPixel> data += 2;
void putPixel(const int x, return value >> 8;
const int y,
const color_t prevPixelValue,
const NewPixel pixelValue,
const psd::ChannelID chanID,
const PixelFormat pixelFormat)
{
if (pixelFormat == doc::PixelFormat::IMAGE_RGB) {
int r = rgba_getr(prevPixelValue);
int g = rgba_getg(prevPixelValue);
int b = rgba_getb(prevPixelValue);
int a = m_layerHasTransparentChannel ? rgba_geta(prevPixelValue) : 255;
if (chanID == psd::ChannelID::Red) {
r = pixelValue;
}
else if (chanID == psd::ChannelID::Green) {
g = pixelValue;
}
else if (chanID == psd::ChannelID::Blue) {
b = pixelValue;
}
else if (chanID == psd::ChannelID::Alpha ||
chanID == psd::ChannelID::TransparencyMask) {
a = pixelValue;
}
m_currentImage->putPixel(x, y, rgba(r, g, b, a));
} }
else if (pixelFormat == doc::PixelFormat::IMAGE_GRAYSCALE) { else if (depth == 32) {
int v = graya_getv(prevPixelValue); const uint32_t value = int(data[0] << 24) | int(data[1] << 16) |
int a = m_layerHasTransparentChannel ? graya_geta(prevPixelValue) : 255; int(data[2] << 8) | int(data[3]);
if (chanID == psd::ChannelID::Red) { data += 4;
v = pixelValue; return value >> 24;
}
else if (chanID == psd::ChannelID::Alpha ||
chanID == psd::ChannelID::TransparencyMask) {
a = pixelValue;
}
m_currentImage->putPixel(x, y, graya(v, a));
}
else if (pixelFormat == doc::PixelFormat::IMAGE_INDEXED) {
m_currentImage->putPixel(x, y, (color_t)pixelValue);
}
else {
throw std::runtime_error(
"Only RGB/Grayscale/Indexed format is supported");
} }
else
throw std::runtime_error("invalid image depth");
} }
void createNewImage(const int width, const int height) void createNewImage(const int width, const int height)
@ -357,13 +347,7 @@ bool PsdFormat::onLoad(FileOp* fop)
header.colorMode != psd::ColorMode::Indexed && header.colorMode != psd::ColorMode::Indexed &&
header.colorMode != psd::ColorMode::Grayscale) { header.colorMode != psd::ColorMode::Grayscale) {
fop->setError("This preliminary work only supports " fop->setError("This preliminary work only supports "
"RGB, Grayscale & Indexed\n"); "RGB, Grayscale & Indexed images\n");
return false;
}
// This would be removed when support for 32bit per channel is supported
if (header.depth >= 32) {
fop->setError("Support for 32bit per channel isn't supported yet");
return false; return false;
} }

@ -1 +1 @@
Subproject commit 954ac6986a65e53a708cc4fd618d45160fa6497b Subproject commit 9d4cd1fe3eab23b1fefd618700b3c6e964194f1e