| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| DEFINE_CLASS_CODE(TImageReader, 5) |
| DEFINE_CLASS_CODE(TImageWriter, 6) |
| |
| |
| |
| std::map<QString, TImageReaderCreateProc *> ImageReaderTable; |
| std::map<QString, std::pair<TImageWriterCreateProc *, bool>> ImageWriterTable; |
| |
| |
| |
| bool TImageReader::m_safeMode = false; |
| |
| |
| |
| TImageReader::TImageReader(const TFilePath &path) |
| : TSmartObject(m_classCode) |
| , m_path(path) |
| , m_reader(0) |
| , m_vectorReader(0) |
| , m_readGreytones(true) |
| , m_file(NULL) |
| , m_is64BitEnabled(false) |
| , m_shrink(1) |
| , m_region(TRect()) {} |
| |
| |
| |
| void TImageReader::doReadGraytones(bool readThem) { |
| m_readGreytones = readThem; |
| } |
| |
| |
| |
| TImageReader::~TImageReader() { close(); } |
| |
| |
| |
| bool TImageReader::isOpen() const { return m_file != NULL; } |
| |
| |
| |
| void TImageReader::close() { |
| delete m_reader; |
| delete m_vectorReader; |
| |
| if (m_file != NULL) { |
| int err = 0; |
| err = fclose(m_file); |
| |
| assert(err == 0); |
| } |
| |
| m_file = NULL; |
| m_reader = 0; |
| m_vectorReader = 0; |
| } |
| |
| |
| |
| |
| |
| |
| void TImageReader::getTzpPaletteColorNames( |
| std::map<int, std::pair<std::string, std::string>> &pltColorNames) { |
| if (!m_file) open(); |
| |
| if (!m_file) return; |
| |
| assert(m_reader); |
| m_reader->getTzpPaletteColorNames(pltColorNames); |
| } |
| |
| |
| |
| void TImageReader::open() { |
| assert(m_file == NULL); |
| |
| std::string type = toLower(m_path.getType()); |
| m_file = fopen(m_path, "rb"); |
| |
| |
| |
| if (m_file == NULL) |
| close(); |
| else { |
| try { |
| m_reader = Tiio::makeReader(type); |
| if (m_reader) |
| m_reader->open(m_file); |
| else { |
| m_vectorReader = Tiio::makeVectorReader(type); |
| if (m_vectorReader) |
| m_vectorReader->open(m_file); |
| else |
| throw TImageException(m_path, "Image format not supported"); |
| } |
| } catch (TException &e) { |
| close(); |
| QString msg = QString::fromStdWString(e.getMessage()); |
| if (msg == QString("Old 4.1 Palette")) throw e; |
| } catch (std::string str) { |
| if (str == "Tiff file closed") m_file = NULL; |
| } |
| } |
| } |
| |
| |
| |
| void TImageReader::setShrink(int shrink) { m_shrink = shrink; } |
| |
| |
| |
| TImageReaderP::TImageReaderP(const TFilePath &path) { |
| m_pointer = new TImageReader(path); |
| m_pointer->addRef(); |
| m_pointer->open(); |
| } |
| |
| |
| |
| TImageP TImageReader::load() { |
| TImageP image = load0(); |
| if (!image) return TImageP(); |
| |
| TImageInfo info = m_reader->getImageInfo(); |
| if (info.m_lx <= 0 || info.m_ly <= 0) return TImageP(); |
| |
| return image; |
| } |
| |
| |
| |
| template <typename Pix> |
| struct pixel_traits {}; |
| |
| template <> |
| struct pixel_traits<TPixel32> { |
| typedef TPixel32 rgbm_pixel_type; |
| typedef char buffer_type; |
| }; |
| |
| template <> |
| struct pixel_traits<TPixel64> { |
| typedef TPixel64 rgbm_pixel_type; |
| typedef short buffer_type; |
| }; |
| |
| template <> |
| struct pixel_traits<TPixelGR8> { |
| typedef TPixel32 rgbm_pixel_type; |
| typedef char buffer_type; |
| }; |
| |
| template <> |
| struct pixel_traits<TPixelGR16> { |
| typedef TPixel64 rgbm_pixel_type; |
| typedef short buffer_type; |
| }; |
| |
| template <> |
| struct pixel_traits<TPixelCM32> { |
| typedef TPixel32 rgbm_pixel_type; |
| typedef char buffer_type; |
| }; |
| |
| |
| |
| template <typename Pix> |
| void copyLine(typename pixel_traits<Pix>::rgbm_pixel_type *lineIn, Pix *lineOut, |
| int x0, int length, int shrink) { |
| lineIn += x0; |
| |
| for (int i = 0; i < length; ++i, lineIn += shrink, ++lineOut) |
| memcpy(lineOut, lineIn, sizeof(Pix)); |
| } |
| |
| template <> |
| void copyLine<TPixelGR8>(TPixel32 *lineIn, TPixelGR8 *lineOut, int x0, |
| int length, int shrink) { |
| lineIn += x0; |
| |
| for (int i = 0; i < length; ++i, lineIn += shrink, ++lineOut) |
| lineOut->value = lineIn->r; |
| } |
| |
| template <> |
| void copyLine<TPixelGR16>(TPixel64 *lineIn, TPixelGR16 *lineOut, int x0, |
| int length, int shrink) { |
| lineIn += x0; |
| |
| for (int i = 0; i < length; ++i, lineIn += shrink, ++lineOut) |
| lineOut->value = lineIn->r; |
| } |
| |
| |
| |
| template <typename Pix> |
| void readRaster_copyLines(const TRasterPT<Pix> &ras, Tiio::Reader *reader, |
| int x0, int y0, int x1, int y1, int inLx, int inLy, |
| int shrink) { |
| typedef typename pixel_traits<Pix>::buffer_type buffer_type; |
| typedef typename pixel_traits<Pix>::rgbm_pixel_type rgbm_pixel_type; |
| |
| int linesToSkip = shrink - 1; |
| |
| buffer_type *lineBuffer = |
| (buffer_type *)malloc(inLx * sizeof(rgbm_pixel_type)); |
| if (!lineBuffer) return; |
| |
| rgbm_pixel_type *lineIn = (rgbm_pixel_type *)lineBuffer; |
| |
| if (reader->getRowOrder() == Tiio::BOTTOM2TOP) { |
| int start = reader->skipLines(y0); |
| int stop = y1 + 1; |
| |
| for (int y = start; y < stop; ++y) { |
| reader->readLine(lineBuffer, x0, x1, shrink); |
| |
| if (y >= y0 && y <= y1 && (y - y0) % shrink == 0) { |
| Pix *line = (Pix *)ras->getRawData(0, (y - y0) / shrink); |
| copyLine<Pix>(lineIn, line, x0, ras->getLx(), shrink); |
| } |
| |
| if (linesToSkip > 0 && y + linesToSkip < inLy) |
| y += reader->skipLines(linesToSkip); |
| } |
| } else |
| { |
| reader->skipLines(inLy - y1 - 1); |
| |
| for (int y = y1; y >= y0; --y) { |
| reader->readLine(lineBuffer, x0, x1, shrink); |
| |
| if ((y - y0) % shrink == 0) { |
| Pix *line = (Pix *)ras->getRawData(0, (y - y0) / shrink); |
| copyLine<Pix>(lineIn, line, x0, ras->getLx(), shrink); |
| } |
| |
| if (linesToSkip > 0 && y - linesToSkip > 0) |
| y -= reader->skipLines(linesToSkip); |
| } |
| } |
| |
| free(lineBuffer); |
| } |
| |
| |
| |
| template <typename Pix> |
| void readRaster(const TRasterPT<Pix> &ras, Tiio::Reader *reader, int x0, int y0, |
| int x1, int y1, int inLx, int inLy, int shrink) { |
| typedef typename pixel_traits<Pix>::buffer_type buffer_type; |
| |
| if (shrink == 1) { |
| |
| ras->lock(); |
| |
| ptrdiff_t linePad = -x0 * ras->getPixelSize(); |
| |
| if (reader->getRowOrder() == Tiio::BOTTOM2TOP) { |
| int start = reader->skipLines(y0); |
| int stop = y1 + 1; |
| |
| for (int y = start; y < stop; ++y) |
| if (y >= y0 && y <= y1) { |
| buffer_type *line = |
| (buffer_type *)(ras->getRawData(0, y - y0) + linePad); |
| reader->readLine(line, x0, x1, 1); |
| } |
| } else |
| { |
| reader->skipLines(inLy - y1 - 1); |
| |
| for (int y = y1; y >= y0; --y) { |
| buffer_type *line = |
| (buffer_type *)(ras->getRawData(0, y - y0) + linePad); |
| reader->readLine(line, x0, x1, 1); |
| } |
| } |
| |
| ras->unlock(); |
| } else |
| readRaster_copyLines(ras, reader, x0, y0, x1, y1, inLx, inLy, shrink); |
| } |
| |
| |
| |
| TImageP TImageReader::load0() { |
| if (!m_reader && !m_vectorReader) open(); |
| |
| if (m_reader) { |
| TImageInfo info = m_reader->getImageInfo(); |
| if (info.m_lx <= 0 || info.m_ly <= 0) return TImageP(); |
| |
| |
| assert(m_shrink > 0); |
| |
| |
| int x0 = 0; |
| int x1 = info.m_lx - 1; |
| int y0 = 0; |
| int y1 = info.m_ly - 1; |
| |
| if (!m_region.isEmpty()) { |
| |
| |
| x0 = std::max(x0, m_region.x0); |
| y0 = std::max(y0, m_region.y0); |
| x1 = std::min(x1, m_region.x1); |
| y1 = std::min(y1, m_region.y1); |
| |
| if (x0 >= x1 || y0 >= y1) return TImageP(); |
| } |
| |
| if (m_shrink > 1) { |
| |
| x1 -= (x1 - x0) % m_shrink; |
| y1 -= (y1 - y0) % m_shrink; |
| } |
| |
| assert(x0 <= x1 && y0 <= y1); |
| |
| TDimension imageDimension = |
| TDimension((x1 - x0) / m_shrink + 1, (y1 - y0) / m_shrink + 1); |
| |
| if (m_path.getType() == "tzp" || m_path.getType() == "tzu") { |
| |
| |
| TRasterCM32P ras(imageDimension); |
| readRaster(ras, m_reader, x0, y0, x1, y1, info.m_lx, info.m_ly, m_shrink); |
| |
| |
| TRect saveBox(info.m_x0, info.m_y0, info.m_x1, info.m_y1); |
| if (!m_region.isEmpty()) { |
| |
| if (m_region.overlaps(saveBox)) { |
| saveBox *= m_region; |
| |
| int sbx0 = saveBox.x0 - m_region.x0; |
| int sby0 = saveBox.y0 - m_region.y0; |
| int sbx1 = sbx0 + saveBox.getLx() - 1; |
| int sby1 = sby0 + saveBox.getLy() - 1; |
| assert(sbx0 >= 0); |
| assert(sby0 >= 0); |
| assert(sbx1 >= 0); |
| assert(sby1 >= 0); |
| |
| saveBox = TRect(sbx0, sby0, sbx1, sby1); |
| } else |
| saveBox = TRect(0, 0, 1, 1); |
| } |
| |
| if (m_shrink > 1) { |
| saveBox.x0 = saveBox.x0 / m_shrink; |
| saveBox.y0 = saveBox.y0 / m_shrink; |
| saveBox.x1 = saveBox.x1 / m_shrink; |
| saveBox.y1 = saveBox.y1 / m_shrink; |
| } |
| |
| TToonzImageP ti(ras, ras->getBounds() * saveBox); |
| ti->setDpi(info.m_dpix, info.m_dpiy); |
| |
| return ti; |
| } else if (info.m_bitsPerSample >= 8) { |
| |
| |
| |
| if (info.m_samplePerPixel == 1 && m_readGreytones) { |
| |
| |
| |
| |
| |
| |
| |
| TRasterGR8P ras(imageDimension); |
| readRaster_copyLines(ras, m_reader, x0, y0, x1, y1, info.m_lx, |
| info.m_ly, m_shrink); |
| |
| TRasterImageP ri(ras); |
| ri->setDpi(info.m_dpix, info.m_dpiy); |
| |
| return ri; |
| } |
| |
| |
| |
| TRasterP _ras; |
| if (info.m_bitsPerSample == 16) { |
| if (m_is64BitEnabled || m_path.getType() != "tif") { |
| |
| |
| |
| |
| |
| |
| TRaster64P ras(imageDimension); |
| readRaster(ras, m_reader, x0, y0, x1, y1, info.m_lx, info.m_ly, |
| m_shrink); |
| |
| _ras = ras; |
| } else { |
| |
| |
| |
| |
| |
| |
| TRaster32P ras(imageDimension); |
| readRaster(ras, m_reader, x0, y0, x1, y1, info.m_lx, info.m_ly, |
| m_shrink); |
| |
| _ras = ras; |
| } |
| } else if (info.m_bitsPerSample == 8) { |
| |
| TRaster32P ras(imageDimension); |
| readRaster(ras, m_reader, x0, y0, x1, y1, info.m_lx, info.m_ly, |
| m_shrink); |
| |
| _ras = ras; |
| } else |
| throw TImageException(m_path, "Image format not supported"); |
| |
| |
| if (!m_is64BitEnabled && (TRaster64P)_ras) { |
| TRaster32P raux(_ras->getLx(), _ras->getLy()); |
| TRop::convert(raux, _ras); |
| _ras = raux; |
| } |
| |
| |
| TRasterImageP ri(_ras); |
| ri->setDpi(info.m_dpix, info.m_dpiy); |
| |
| return ri; |
| } else if (info.m_samplePerPixel == 1 && info.m_valid == true) { |
| |
| |
| TRaster32P ras(imageDimension); |
| readRaster(ras, m_reader, x0, y0, x1, y1, info.m_lx, info.m_ly, m_shrink); |
| |
| TRasterImageP ri(ras); |
| ri->setDpi(info.m_dpix, info.m_dpiy); |
| |
| return ri; |
| } else if (info.m_samplePerPixel == 1 && m_readGreytones) { |
| |
| |
| |
| TRasterGR8P ras(imageDimension); |
| readRaster_copyLines(ras, m_reader, x0, y0, x1, y1, info.m_lx, info.m_ly, |
| m_shrink); |
| |
| TRasterImageP ri(ras); |
| ri->setDpi(info.m_dpix, info.m_dpiy); |
| |
| if (info.m_bitsPerSample == 1) |
| ri->setScanBWFlag(true); |
| |
| return ri; |
| } else |
| return TImageP(); |
| } else if (m_vectorReader) { |
| TVectorImage *vi = m_vectorReader->read(); |
| return TVectorImageP(vi); |
| } else |
| return TImageP(); |
| } |
| |
| |
| |
| TImageWriter::TImageWriter(const TFilePath &path) |
| : TSmartObject(m_classCode) |
| , m_path(path) |
| , m_writer(0) |
| , m_vectorWriter(0) |
| , m_properties(0) {} |
| |
| |
| |
| TImageWriter::~TImageWriter() { |
| delete m_writer; |
| delete m_vectorWriter; |
| delete m_properties; |
| } |
| |
| |
| |
| void TImageWriter::setProperties(const TPropertyGroup *g) { |
| if (m_properties) { |
| assert(m_properties != g); |
| delete m_properties; |
| } |
| m_properties = g ? g->clone() : 0; |
| } |
| |
| |
| |
| static void convertForWriting(TRasterP &ras, const TRasterP &rin, int bpp) { |
| switch (bpp) { |
| case 1: |
| case 8: |
| ras = TRasterGR8P(rin->getSize()); |
| TRop::convert(ras, rin); |
| break; |
| case 24: |
| case 32: |
| ras = TRaster32P(rin->getSize()); |
| TRop::convert(ras, rin); |
| break; |
| case 48: |
| case 64: |
| ras = TRaster64P(rin->getSize()); |
| TRop::convert(ras, rin); |
| break; |
| default: |
| assert(false); |
| } |
| } |
| |
| |
| |
| void TImageWriter::save(const TImageP &img) { |
| const std::string &type = toLower(m_path.getType()); |
| |
| Tiio::Writer *writer = Tiio::makeWriter(type); |
| if (!writer) |
| throw TImageException(m_path, "unsupported format for raster images"); |
| |
| writer->setProperties(m_properties); |
| |
| FILE *file = fopen(m_path, "wb"); |
| if (file == NULL) throw TImageException(m_path, "Can't write file"); |
| |
| if (TRasterImageP ri = img) { |
| TImageInfo info; |
| TRasterP ras; |
| |
| TRasterGR8P rasGr = ri->getRaster(); |
| TRaster32P ras32 = ri->getRaster(); |
| TRaster64P ras64 = ri->getRaster(); |
| |
| TEnumProperty *p = |
| m_properties |
| ? (TEnumProperty *)m_properties->getProperty("Bits Per Pixel") |
| : 0; |
| |
| if (p && ri->isScanBW()) { |
| const std::vector<std::wstring> &range = p->getRange(); |
| p->setValue(range[2]); |
| } |
| |
| int bpp = p ? std::stoi(p->getValue()) : 32; |
| |
| |
| int spp[] = { |
| 1, 1, 1, 4, 4, |
| 0, 4, 0, 4}; |
| int bps[] = { |
| 1, 8, 16, 8, 8, |
| 0, 16, 0, 16}; |
| |
| int bypp = bpp / 8; |
| assert(bypp < boost::size(spp) && spp[bypp] && bps[bypp]); |
| |
| info.m_samplePerPixel = spp[bypp]; |
| info.m_bitsPerSample = bps[bypp]; |
| |
| if (rasGr) { |
| if (bypp < 2) |
| ras = rasGr; |
| else |
| convertForWriting(ras, rasGr, bpp); |
| } else if (ras32) { |
| if (bpp == 32 || bpp == 24) |
| ras = ras32; |
| else |
| convertForWriting(ras, ras32, bpp); |
| } else if (ras64) { |
| if (bpp == 64 || bpp == 48) |
| ras = ras64; |
| else |
| convertForWriting(ras, ras64, bpp); |
| } else { |
| fclose(file); |
| throw TImageException(m_path, "unsupported raster type"); |
| } |
| |
| info.m_lx = ras->getLx(); |
| info.m_ly = ras->getLy(); |
| |
| ri->getDpi(info.m_dpix, info.m_dpiy); |
| |
| if (writer->getProperties() && m_properties) |
| writer->getProperties()->setProperties(m_properties); |
| |
| writer->open(file, info); |
| |
| ras->lock(); |
| if (writer->getRowOrder() == Tiio::BOTTOM2TOP) { |
| if (bpp == 1 || bpp == 8 || bpp == 24 || bpp == 32 || bpp == 16) |
| for (int i = 0; i < ras->getLy(); i++) |
| writer->writeLine((char *)ras->getRawData(0, i)); |
| else |
| for (int i = 0; i < ras->getLy(); i++) |
| writer->writeLine((short *)ras->getRawData(0, i)); |
| } else { |
| if (bpp == 1 || bpp == 8 || bpp == 24 || bpp == 32 || bpp == 16) |
| for (int i = ras->getLy() - 1; i >= 0; i--) |
| writer->writeLine((char *)ras->getRawData(0, i)); |
| else |
| for (int i = ras->getLy() - 1; i >= 0; i--) |
| writer->writeLine((short *)ras->getRawData(0, i)); |
| } |
| |
| ras->unlock(); |
| |
| writer->flush(); |
| delete writer; |
| } else if (TVectorImageP vi = img) { |
| Tiio::VectorWriter *writer = Tiio::makeVectorWriter(type); |
| if (!writer) { |
| fclose(file); |
| throw TImageException(m_path, "unsupported format for vector images"); |
| } |
| |
| writer->open(file); |
| writer->write(vi.getPointer()); |
| |
| delete writer; |
| } else { |
| fclose(file); |
| throw TImageException(m_path, "Can't write file"); |
| } |
| |
| fclose(file); |
| } |
| |
| |
| |
| TImageWriterP::TImageWriterP(const TFilePath &path) { |
| m_pointer = new TImageWriter(path); |
| m_pointer->addRef(); |
| } |
| |
| |
| |
| |
| |
| |
| |
| bool TImageReader::load(const TFilePath &path, TRasterP &raster) { |
| raster = TRasterP(); |
| TImageReaderP ir(path); |
| if (!ir) return false; |
| TImageP img = ir->load(); |
| if (!img) return false; |
| |
| TRasterImageP ri(img); |
| if (!ri) return false; |
| |
| raster = ri->getRaster(); |
| return true; |
| } |
| |
| |
| |
| |
| |
| void TImageReader::load(const TRasterP &ras, const TPoint &pos, int shrinkX, |
| int shrinkY) { |
| TImageP srcImage = load(); |
| TRasterImageP srcRasImage = srcImage; |
| TRaster32P srcRaster = srcRasImage->getRaster(); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| ras->copy(srcRaster); |
| } |
| |
| |
| |
| bool TImageReader::load(const TFilePath &path, TImageP &image) { |
| image = TImageReaderP(path)->load(); |
| return image; |
| } |
| |
| |
| |
| void TImageWriter::save(const TFilePath &path, TRasterP raster) { |
| TRasterImageP rasImage(raster); |
| TImageWriterP(path)->save(TImageP(rasImage)); |
| } |
| |
| |
| |
| void TImageWriter::save(const TFilePath &path, const TImageP &image) { |
| TImageWriterP(path)->save(image); |
| } |
| |
| |
| |
| |
| |
| |
| |
| void TImageReader::define(QString extension, TImageReaderCreateProc *proc) { |
| ImageReaderTable[extension] = proc; |
| } |
| |
| |
| |
| void TImageWriter::define(QString extension, TImageWriterCreateProc *proc, |
| bool isRenderFormat) { |
| ImageWriterTable[extension] = |
| std::pair<TImageWriterCreateProc *, bool>(proc, isRenderFormat); |
| } |
| |
| |
| |
| void TImageReader::getSupportedFormats(QStringList &names) { |
| for (std::map<QString, TImageReaderCreateProc *>::iterator it = |
| ImageReaderTable.begin(); |
| it != ImageReaderTable.end(); ++it) { |
| names.push_back(it->first); |
| } |
| } |
| |
| |
| |
| void TImageWriter::getSupportedFormats(QStringList &names, |
| bool onlyRenderFormat) { |
| for (std::map<QString, std::pair<TImageWriterCreateProc *, bool>>::iterator |
| it = ImageWriterTable.begin(); |
| it != ImageWriterTable.end(); ++it) { |
| if (!onlyRenderFormat || it->second.second) names.push_back(it->first); |
| } |
| } |
| |
| |
| |
| const TImageInfo *TImageReader::getImageInfo() const { |
| if (m_reader) |
| return &(m_reader->getImageInfo()); |
| else |
| return 0; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| TImageException::TImageException(const TFilePath &fp, const std::string &msg) |
| : TException(msg), m_fp(fp) {} |
| |
| |
| |
| TString TImageException::getMessage() const { |
| return m_fp.getWideString() + L": " + TException::getMessage(); |
| } |
| |
| |
| |
| TImageVersionException::TImageVersionException(const TFilePath &fp, int major, |
| int minor) |
| : TException( |
| L"The file " + fp.getWideString() + |
| L" was generated by a newer version of Toonz and cannot be loaded.") |
| , m_fp(fp) |
| , m_major(major) |
| , m_minor(minor) {} |
| |