| |
| |
| #include "trop.h" |
| #include "tconvert.h" |
| |
| #ifndef TNZCORE_LIGHT |
| #include "timagecache.h" |
| #include "ttile.h" |
| #include "trasterimage.h" |
| #include "ttoonzimage.h" |
| #endif |
| |
| TString TRopException::getMessage() const { return ::to_wstring(message); } |
| |
| namespace { |
| |
| bool isOpaque32(TRaster32P &ras) { |
| ras->lock(); |
| UCHAR *m0 = &(ras->pixels()->m); |
| if (0 < m0[0] && m0[0] < 255) return false; |
| |
| int wrap4 = ras->getWrap() * 4; |
| int lx4 = ras->getLx() * 4; |
| const UCHAR cm = *m0; |
| int nrows = ras->getLy(); |
| while (nrows-- > 0) { |
| UCHAR *m1 = m0 + lx4; |
| UCHAR *m = m0; |
| while (m < m1 && *m == cm) m += 4; |
| if (m < m1) break; |
| m0 += wrap4; |
| } |
| ras->unlock(); |
| return (nrows <= 0); |
| |
| |
| } |
| |
| } |
| |
| bool TRop::isOpaque(TRasterP ras) { |
| TRaster32P ras32 = ras; |
| if (ras32) |
| return isOpaque32(ras32); |
| else if (TRasterGR8P(ras)) |
| return true; |
| else |
| throw TRopException("isOpaque: unsupported pixel type"); |
| } |
| |
| #ifdef TNZ_MACHINE_CHANNEL_ORDER_MRGB |
| void TRop::swapRBChannels(const TRaster32P &r) { |
| int lx = r->getLx(); |
| int y = r->getLy(); |
| r->lock(); |
| while (--y >= 0) { |
| TPixel32 *pix = r->pixels(y); |
| TPixel32 *endPix = pix + lx; |
| while (pix < endPix) { |
| tswap(pix->r, pix->b); |
| ++pix; |
| } |
| } |
| r->unlock(); |
| } |
| #endif |
| |
| TRaster32P TRop::copyAndSwapRBChannels(const TRaster32P &srcRaster) { |
| TRaster32P newRaster(srcRaster->getSize()); |
| int lx = srcRaster->getLx(); |
| int y = srcRaster->getLy(); |
| srcRaster->lock(); |
| newRaster->lock(); |
| while (--y >= 0) { |
| TPixel32 *pix = srcRaster->pixels(y); |
| TPixel32 *newpix = newRaster->pixels(y); |
| TPixel32 *endPix = pix + lx; |
| while (pix < endPix) { |
| newpix->r = pix->b; |
| newpix->g = pix->g; |
| newpix->b = pix->r; |
| newpix->m = pix->m; |
| ++pix; |
| ++newpix; |
| } |
| } |
| srcRaster->unlock(); |
| newRaster->unlock(); |
| |
| return newRaster; |
| } |
| |
| void TRop::copy(TRasterP dst, const TRasterP &src) { |
| assert(!((TRasterCM32P)src) || (TRasterCM32P)dst); |
| if (dst->getPixelSize() == src->getPixelSize()) |
| dst->copy(src); |
| else { |
| if (dst->getBounds() != src->getBounds()) { |
| TRect rect = dst->getBounds() * src->getBounds(); |
| if (rect.isEmpty()) return; |
| TRop::convert(dst->extract(rect), src->extract(rect)); |
| } else |
| TRop::convert(dst, src); |
| } |
| } |
| |
| |
| |
| namespace { |
| template <class Q> |
| class Gamma_Lut { |
| public: |
| std::vector<Q> m_table; |
| Gamma_Lut(int insteps, int outsteps, double gamma) { |
| double inspace = (double)(insteps); |
| for (int i = 0; i <= insteps; i++) |
| m_table.push_back( |
| (Q)((outsteps) * (pow(i / inspace, 1.0 / gamma)) + 0.5)); |
| } |
| }; |
| |
| template <class T, class Q> |
| void doGammaCorrect(TRasterPT<T> raster, double gamma) { |
| Gamma_Lut<Q> lut(T::maxChannelValue, T::maxChannelValue, gamma); |
| |
| int j; |
| for (j = 0; j < raster->getLy(); j++) { |
| T *pix = raster->pixels(j); |
| T *endPix = pix + raster->getLx(); |
| while (pix < endPix) { |
| pix->r = lut.m_table[pix->r]; |
| pix->b = lut.m_table[pix->b]; |
| pix->g = lut.m_table[pix->g]; |
| |
| |
| |
| |
| |
| |
| *pix++; |
| } |
| } |
| } |
| template <class T, class Q> |
| void doGammaCorrectRGBM(TRasterPT<T> raster, double gammar, double gammag, |
| double gammab, double gammam) { |
| Gamma_Lut<Q> lutr(T::maxChannelValue, T::maxChannelValue, gammar); |
| Gamma_Lut<Q> lutg(T::maxChannelValue, T::maxChannelValue, gammag); |
| Gamma_Lut<Q> lutb(T::maxChannelValue, T::maxChannelValue, gammab); |
| Gamma_Lut<Q> lutm(T::maxChannelValue, T::maxChannelValue, gammam); |
| int j; |
| for (j = 0; j < raster->getLy(); j++) { |
| T *pix = raster->pixels(j); |
| T *endPix = pix + raster->getLx(); |
| while (pix < endPix) { |
| pix->r = lutr.m_table[pix->r]; |
| pix->g = lutg.m_table[pix->g]; |
| pix->b = lutb.m_table[pix->b]; |
| pix->m = lutm.m_table[pix->m]; |
| |
| |
| |
| |
| |
| |
| *pix++; |
| } |
| } |
| } |
| } |
| |
| |
| void TRop::gammaCorrect(TRasterP raster, double gamma) { |
| if (gamma <= 0) gamma = 0.01; |
| raster->lock(); |
| |
| if ((TRaster32P)raster) |
| doGammaCorrect<TPixel32, UCHAR>(raster, gamma); |
| else if ((TRaster64P)raster) |
| doGammaCorrect<TPixel64, USHORT>(raster, gamma); |
| else { |
| raster->unlock(); |
| throw TRopException("isOpaque: unsupported pixel type"); |
| } |
| raster->unlock(); |
| } |
| |
| |
| void TRop::gammaCorrectRGBM(TRasterP raster, double gammar, double gammag, |
| double gammab, double gammam) { |
| if (gammar <= 0) gammar = 0.01; |
| if (gammag <= 0) gammag = 0.01; |
| if (gammab <= 0) gammab = 0.01; |
| if (gammam <= 0) gammam = 0.01; |
| |
| raster->lock(); |
| |
| if ((TRaster32P)raster) |
| doGammaCorrectRGBM<TPixel32, UCHAR>(raster, gammar, gammag, gammab, gammam); |
| else if ((TRaster64P)raster) |
| doGammaCorrectRGBM<TPixel64, USHORT>(raster, gammar, gammag, gammab, |
| gammam); |
| else { |
| raster->unlock(); |
| throw TRopException("isOpaque: unsupported pixel type"); |
| } |
| raster->unlock(); |
| } |
| |
| |
| template <class T> |
| void doSetChannel(const TRasterPT<T> &rin, const TRasterPT<T> &rout, |
| UCHAR channel, bool greytones) { |
| int lx = rin->getLx(); |
| int ly = rout->getLy(); |
| |
| int i, j; |
| for (i = 0; i < ly; i++) { |
| T *pixin = rin->pixels(i); |
| T *pixout = rout->pixels(i); |
| if (greytones || channel == TRop::MChan) { |
| switch (channel) { |
| case TRop::RChan: |
| for (j = 0; j < lx; j++, pixin++, pixout++) |
| pixout->r = pixout->g = pixout->b = pixout->m = pixin->r; |
| break; |
| case TRop::GChan: |
| for (j = 0; j < lx; j++, pixin++, pixout++) |
| pixout->r = pixout->g = pixout->b = pixout->m = pixin->g; |
| break; |
| case TRop::BChan: |
| for (j = 0; j < lx; j++, pixin++, pixout++) |
| pixout->r = pixout->g = pixout->b = pixout->m = pixin->b; |
| break; |
| case TRop::MChan: |
| for (j = 0; j < lx; j++, pixin++, pixout++) |
| pixout->r = pixout->g = pixout->b = pixout->m = pixin->m; |
| break; |
| default: |
| assert(false); |
| } |
| } else { |
| for (j = 0; j < lx; j++, pixin++, pixout++) { |
| pixout->r = channel & TRop::RChan ? pixin->r : 0; |
| pixout->b = channel & TRop::BChan ? pixin->b : 0; |
| pixout->g = channel & TRop::GChan ? pixin->g : 0; |
| } |
| } |
| } |
| } |
| |
| |
| |
| void TRop::setChannel(const TRasterP &rin, TRasterP rout, UCHAR chan, |
| bool greytones) { |
| assert(rin->getSize() == rout->getSize()); |
| |
| rout->lock(); |
| |
| if ((TRaster32P)rin && (TRaster32P)rout) |
| doSetChannel<TPixel32>(rin, rout, chan, greytones); |
| else if ((TRaster64P)rin && (TRaster64P)rout) |
| doSetChannel<TPixel64>(rin, rout, chan, greytones); |
| else { |
| rout->unlock(); |
| throw TRopException("setChannel: unsupported pixel type"); |
| } |
| |
| rout->unlock(); |
| } |
| |
| |
| |
| TRasterP TRop::shrink(TRasterP rin, int shrink) { |
| int pixelSize = rin->getPixelSize(); |
| |
| int lx = (rin->getLx() - 1) / shrink + 1; |
| int ly = (rin->getLy() - 1) / shrink + 1; |
| |
| TRasterP rout; |
| |
| if ((TRaster32P)rin) |
| rout = TRaster32P(lx, ly); |
| else if ((TRaster64P)rin) |
| rout = TRaster64P(lx, ly); |
| if ((TRasterCM32P)rin) rout = TRasterCM32P(lx, ly); |
| if ((TRasterGR8P)rin) rout = TRasterGR8P(lx, ly); |
| |
| int i, j; |
| |
| for (i = 0; i < ly; i++) { |
| UCHAR *bufin = |
| (UCHAR *)rin->getRawData() + (i * shrink) * rin->getWrap() * pixelSize; |
| UCHAR *bufout = |
| (UCHAR *)rout->getRawData() + i * rout->getWrap() * pixelSize; |
| for (j = 0; j < lx; j++) { |
| memcpy(bufout, bufin, pixelSize); |
| bufin += shrink * pixelSize; |
| bufout += pixelSize; |
| } |
| } |
| return rout; |
| } |
| |
| |
| |
| template <class T> |
| void doMakeStereoRaster(const TRasterPT<T> &rleft, const TRasterPT<T> &rright) { |
| int lx = rleft->getLx(); |
| int ly = rright->getLy(); |
| |
| for (int i = 0; i < ly; i++) { |
| T *pixl = rleft->pixels(i); |
| T *pixr = rright->pixels(i); |
| |
| for (int j = 0; j < lx; j++, pixl++, pixr++) { |
| pixl->g = pixr->g; |
| pixl->b = pixr->b; |
| } |
| } |
| } |
| |
| |
| |
| void TRop::makeStereoRaster(const TRasterP &left, const TRasterP &right) { |
| assert(left->getSize() == right->getSize()); |
| |
| left->lock(); |
| |
| if ((TRaster32P)left && (TRaster32P)right) |
| doMakeStereoRaster<TPixel32>(left, right); |
| else if ((TRaster64P)left && (TRaster64P)right) |
| doMakeStereoRaster<TPixel64>(left, right); |
| else { |
| left->unlock(); |
| throw TRopException("setChannel: unsupported pixel type"); |
| } |
| |
| left->unlock(); |
| } |
| |
| |
| #ifndef TNZCORE_LIGHT |
| |
| void TTile::addInCache(const TRasterP &raster) { |
| if (!raster) { |
| m_rasterId = ""; |
| return; |
| } |
| TRasterP rin; |
| |
| m_rasterId = TImageCache::instance()->getUniqueId(); |
| if (raster->getParent()) { |
| rin = raster->getParent(); |
| unsigned long offs = |
| (raster->getRawData() - raster->getParent()->getRawData()) / |
| raster->getPixelSize(); |
| m_subRect = |
| TRect(TPoint(offs % raster->getWrap(), offs / raster->getWrap()), |
| raster->getSize()); |
| } else { |
| m_subRect = raster->getBounds(); |
| rin = raster; |
| } |
| |
| if ((TRasterCM32P)rin) |
| TImageCache::instance()->add(m_rasterId, |
| TToonzImageP(rin, rin->getBounds())); |
| else if ((TRaster32P)rin || (TRaster64P)rin) |
| TImageCache::instance()->add(m_rasterId, TRasterImageP(rin)); |
| else if ((TRasterGR8P)rin || (TRasterGR16P)rin) |
| TImageCache::instance()->add(m_rasterId, TRasterImageP(rin)); |
| else |
| assert(false); |
| } |
| |
| TTile::TTile(const TRasterP &raster) : m_pos(), m_subRect() { |
| addInCache(raster); |
| } |
| |
| TTile::TTile(const TRasterP &raster, TPointD pos) : m_pos(pos), m_subRect() { |
| addInCache(raster); |
| } |
| |
| void TTile::setRaster(const TRasterP &raster) { |
| if (m_rasterId != "") TImageCache::instance()->remove(m_rasterId); |
| m_subRect = TRect(); |
| addInCache(raster); |
| } |
| |
| TTile::~TTile() { |
| if (!m_rasterId.empty()) TImageCache::instance()->remove(m_rasterId); |
| } |
| |
| #endif |
| |