Animation::Animation(FileType aftype, IStream *file, const ReadOptions& options) : fileType(aftype), inFile(file), outFile(NULL), readOptions(options) { float seconds = 0.0f; currentFrame = 0; switch(fileType) { case AVI: // state = Avi::ReadFileHeader(inFile, seconds, totalFrames, codec, width, height, readOptions, warnings); break; case MOV: state = Moov::ReadFileHeader(inFile, seconds, totalFrames, codec, width, height, readOptions, warnings); break; case MPEG: // state = Mpeg::ReadFileHeader(inFile, seconds, totalFrames, codec, width, height, readOptions, warnings); break; } if(state == NULL) throw POV_EXCEPTION(kCannotHandleDataErr, "Cannot read animation file header in the specified format!"); frameDuration = seconds / float(totalFrames); }
Image *Animation::ReadFrame(IStream *file) { POV_LONG bytes = 0; Image *image = NULL; Image::ReadOptions options; options.defaultGamma = PowerLawGammaCurve::GetByDecodingGamma(readOptions.gamma); options.gammacorrect = readOptions.gammacorrect; options.itype = Image::RGBFT_Float; switch(fileType) { case AVI: // Avi::PreReadFrame(file, currentFrame, bytes, codec, readOptions, warnings, state); break; case MOV: Moov::PreReadFrame(file, currentFrame, bytes, codec, readOptions, warnings, state); break; } POV_LONG prepos = file->tellg(); switch(codec) { case PNGCodec: image = Png::Read(file, options); break; case BMPCodec: image = Bmp::Read(file, options); break; case JPEGCodec: image = Jpeg::Read(file, options); break; case MPEG1Codec: case MPEG2Codec: // image = Mpeg::ReadFrame(file, currentFrame, codec, readOptions, warnings, state); break; } if(file->tellg() < (prepos + bytes)) warnings.push_back("Frame decompressor read fewer bytes than expected."); else if(file->tellg() > (prepos + bytes)) throw POV_EXCEPTION(kInvalidDataSizeErr, "Frame decompressor read more bytes than expected. The input file may be corrupted!"); file->seekg(prepos + bytes, SEEK_END); switch(fileType) { case AVI: // Avi::PostReadFrame(file, currentFrame, bytes, codec, readOptions, warnings, state); break; case MOV: Moov::PostReadFrame(file, currentFrame, bytes, codec, readOptions, warnings, state); break; } return image; }
OTextStream::OTextStream(const UCS2 *sname, unsigned int stype, bool append) { if(sname == NULL) throw POV_EXCEPTION_CODE(kParamErr); stream = NewOStream(sname, stype, append); if(stream == NULL) throw POV_EXCEPTION(kCannotOpenFileErr, string("Cannot open file '") + UCS2toASCIIString(sname) + "' for output."); filename = UCS2String(sname); }
void png_pov_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { OStream *file = reinterpret_cast<OStream *>(png_get_io_ptr(png_ptr)); if (!file->write (data, length)) { Messages *m = reinterpret_cast<Messages *>(png_get_error_ptr(png_ptr)); if (m) m->error = string("Cannot write PNG data"); throw POV_EXCEPTION(kFileDataErr, "Cannot write PNG data"); } }
void png_pov_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { OStream *file = (OStream *)png_get_io_ptr(png_ptr); if (!file->write ((char *)data, length)) { Messages *m = (Messages *)png_get_error_ptr(png_ptr); if (m) m->error = string("Cannot write PNG data"); throw POV_EXCEPTION(kFileDataErr, "Cannot write PNG data"); } }
IStream *NewIStream(const Path& p, unsigned int stype) { if (POV_ALLOW_FILE_READ(p().c_str(), stype) == false) // TODO FIXME - this is handled by the frontend, but that code isn't completely there yet [trf] { string str ("IO Restrictions prohibit read access to '") ; str += UCS2toASCIIString(p()); str += "'"; throw POV_EXCEPTION(kCannotOpenFileErr, str); } return new IFileStream(p().c_str()); }
OStream *NewOStream(const Path& p, unsigned int stype, bool sappend) { unsigned int Flags = IOBase::none; if(sappend) Flags |= IOBase::append; if (POV_ALLOW_FILE_WRITE(p().c_str(), stype) == false) // TODO FIXME - this is handled by the frontend, but that code isn't completely there yet [trf] { string str ("IO Restrictions prohibit write access to '") ; str += UCS2toASCIIString(p()); str += "'"; throw POV_EXCEPTION(kCannotOpenFileErr, str); } return new OStream(p().c_str(), Flags); }
IStream *NewIStream(const Path& p, const unsigned int stype) { Pointer<IStream> istreamptr(POV_PLATFORM_BASE.CreateIStream(stype)); if(istreamptr == NULL) return NULL; if (POV_ALLOW_FILE_READ(p().c_str(), stype) == false) // TODO FIXME - this is handled by the frontend, but that code isn't completely there yet [trf] { string str ("IO Restrictions prohibit read access to '") ; str += UCS2toASCIIString(p()); str += "'"; throw POV_EXCEPTION(kCannotOpenFileErr, str); } if(istreamptr->open(p().c_str()) == 0) return NULL; return istreamptr.release(); }
void PostWriteFrame(OStream *file, POV_LONG bytes, const Animation::WriteOptions&, vector<string>&, void *state) { PrivateData *pd = reinterpret_cast<PrivateData *>(state); if(pd == NULL) throw POV_EXCEPTION_CODE(kNullPointerErr); // update mdat size file->seekg(0, SEEK_END); pd->mdatsize = file->tellg() + 16; file->seekg(8, SEEK_SET); WriteInt8(file, pd->mdatsize); file->seekg(0, SEEK_END); if(bytes > 2147483647) // 2^31 - 1 throw POV_EXCEPTION(kInvalidDataSizeErr, "Cannot handle frame data larger than 2^31 bytes!"); pd->imagesizes.push_back(int(bytes)); }
Animation::Animation(FileType aftype, CodecType c, OStream *file, unsigned int w, unsigned int h, const WriteOptions& options) : fileType(aftype), inFile(NULL), outFile(file), width(w), height(h), writeOptions(options), codec(c) { totalFrames = 0; frameDuration = 1.0f / options.framespersecond; blurMatrixRadius = 7; switch(fileType) { case AVI: // state = Avi::WriteFileHeader(outFile, codec, width, height, writeOptions, warnings); break; case MOV: state = Moov::WriteFileHeader(outFile, codec, width, height, writeOptions, warnings); break; case MPEG: // state = Mpeg::WriteFileHeader(outFile, codec, width, height, writeOptions, warnings); break; } if(state == NULL) throw POV_EXCEPTION(kCannotHandleDataErr, "Cannot write animation file with the specified format and codec!"); // TODO FIXME - build blur matrix (this code only builds an identity matrix) for(size_t y = 0; y < 15; y++) { for(size_t x = 0; x < 15; x++) blurMatrix[x][y] = 0.0f; } blurMatrix[blurMatrixRadius + 1][blurMatrixRadius + 1] = 1.0f; }
OStream *NewOStream(const Path& p, const unsigned int stype, const bool sappend) { Pointer<OStream> ostreamptr(POV_PLATFORM_BASE.CreateOStream(stype)); unsigned int Flags = IOBase::none; if(ostreamptr == NULL) return NULL; if(sappend) Flags |= IOBase::append; if (POV_ALLOW_FILE_WRITE(p().c_str(), stype) == false) // TODO FIXME - this is handled by the frontend, but that code isn't completely there yet [trf] { string str ("IO Restrictions prohibit write access to '") ; str += UCS2toASCIIString(p()); str += "'"; throw POV_EXCEPTION(kCannotOpenFileErr, str); } if(ostreamptr->open(p().c_str(), Flags) == 0) return NULL; return ostreamptr.release(); }
ITextStream::ITextStream(const UCS2 *sname, unsigned int stype) { if(sname == NULL) throw POV_EXCEPTION_CODE(kParamErr); stream = NewIStream(sname, stype); if(stream == NULL) throw POV_EXCEPTION(kCannotOpenFileErr, string("Cannot open file '") + UCS2toASCIIString(sname) + "' for input."); filename = UCS2String(sname); lineno = 1; bufferoffset = 0; maxbufferoffset = 0; filelength = 0; ungetbuffer = EOF; curpos = 0 ; stream->seekg(0, IOBase::seek_end); filelength = stream->tellg(); stream->seekg(0, IOBase::seek_set); RefillBuffer(); }
// TODO: make sure we don't leak an image object if we throw an exception. Image *Read (IStream *file, const Image::ReadOptions& options, bool IsPOTFile) { int data ; int width; int height; Image *image ; unsigned char buffer[256]; vector<Image::RGBAMapEntry> colormap ; int alphaIdx = -1; // assume no transparency color // GIF files used to have no clearly defined gamma by default, but a W3C recommendation exists for them to use sRGB. // Anyway, use whatever the user has chosen as default. GammaCurvePtr gamma; if (options.gammacorrect) { if (options.defaultGamma) gamma = TranscodingGammaCurve::Get(options.workingGamma, options.defaultGamma); else gamma = TranscodingGammaCurve::Get(options.workingGamma, SRGBGammaCurve::Get()); } int status = 0; /* Get the screen description. */ if (!file->read (buffer, 13)) throw POV_EXCEPTION(kFileDataErr, "Cannot read GIF file header"); /* Use updated GIF specs. */ if (memcmp ((char *) buffer, "GIF", 3) != 0) throw POV_EXCEPTION(kFileDataErr, "File is not in GIF format"); if (buffer[3] != '8' || (buffer[4] != '7' && buffer[4] != '9') || buffer[5] < 'A' || buffer[5] > 'z') throw POV_EXCEPTION(kFileDataErr, "Unsupported GIF version"); int planes = ((unsigned) buffer [10] & 0x0F) + 1; int colourmap_size = (1 << planes); /* Color map (better be!) */ if ((buffer[10] & 0x80) == 0) throw POV_EXCEPTION(kFileDataErr, "Error in GIF color map"); for (int i = 0; i < colourmap_size ; i++) { Image::RGBAMapEntry entry; if (!file->read (buffer, 3)) throw POV_EXCEPTION(kFileDataErr, "Cannot read GIF colormap"); entry.red = IntDecode(gamma, buffer[0], 255); entry.green = IntDecode(gamma, buffer[1], 255); entry.blue = IntDecode(gamma, buffer[2], 255); entry.alpha = 1.0f; colormap.push_back(entry); } /* Now read one or more GIF objects. */ bool finished = false; while (!finished) { switch (file->Read_Byte()) { case EOF: throw POV_EXCEPTION(kFileDataErr, "Unexpected EOF reading GIF file"); finished = true; break ; case ';': /* End of the GIF dataset. */ finished = true; status = 0; break; case '!': /* GIF Extension Block. */ /* Read (and check) the ID. */ if (file->Read_Byte() == 0xF9) { if ((data = file->Read_Byte()) > 0) { if (!file->read (buffer, data)) throw POV_EXCEPTION(kFileDataErr, "Unexpected EOF reading GIF file"); // check transparency flag, and set transparency color index if appropriate if (data >= 3 && buffer[0] & 0x01) { int alphaIdx = buffer[3]; if (alphaIdx < colourmap_size) colormap[alphaIdx].alpha = 0.0f; } } else break; } while ((data = file->Read_Byte()) > 0) if (!file->read (buffer, data)) throw POV_EXCEPTION(kFileDataErr, "Unexpected EOF reading GIF file"); break; case ',': /* Start of image object. Get description. */ for (int i = 0; i < 9; i++) { if ((data = file->Read_Byte()) == EOF) throw POV_EXCEPTION(kFileDataErr, "Unexpected EOF reading GIF file"); buffer[i] = (unsigned char) data; } /* Check "interlaced" bit. */ if ((buffer[9] & 0x40) != 0) throw POV_EXCEPTION(kFileDataErr, "Interlacing in GIF image unsupported"); width = (int) buffer[4] | ((int) buffer[5] << 8); height = (int) buffer[6] | ((int) buffer[7] << 8); image = Image::Create (width, height, Image::Colour_Map, colormap) ; // [CLi] GIF only uses full opacity or full transparency, so premultiplied vs. non-premultiplied alpha is not an issue /* Get bytes */ Decode (file, image); finished = true; break; default: status = -1; finished = true; break; } } if (IsPOTFile == false) { if (!image) throw POV_EXCEPTION(kFileDataErr, "Cannot find GIF image data block"); return (image); } // POT files are GIF files where the right half of the image contains // a second byte for each pixel on the left, thus allowing 16-bit // indexes. In this case the palette data is ignored and we convert // the image into a 16-bit grayscale version. if ((width & 0x01) != 0) throw POV_EXCEPTION(kFileDataErr, "Invalid width for POT file"); int newWidth = width / 2 ; Image *newImage = Image::Create (newWidth, height, Image::Gray_Int16) ; for (int y = 0 ; y < height ; y++) for (int x = 0 ; x < newWidth ; x++) newImage->SetGrayValue (x, y, (unsigned int) image->GetIndexedValue (x, y) << 8 | image->GetIndexedValue (x + newWidth, y)) ; // NB: POT files don't use alpha, so premultiplied vs. non-premultiplied is not an issue // NB: No gamma adjustment happening here! delete image ; return (newImage) ; }