OPENMPT_NAMESPACE_BEGIN PNG::Bitmap *PNG::ReadPNG(FileReader &file) //----------------------------------------- { file.Rewind(); if(!file.ReadMagic("\211PNG\r\n\032\n")) { return nullptr; } uint32_t width = 0; uint32_t height = 0; uint8_t bitDepth; uint8_t colorType; uint8_t compressionMethod; uint8_t filterMethod; uint8_t interlaceMethod; std::vector<uint8_t> dataIn; std::vector<Pixel> palette; while(file.AreBytesLeft()) { uint32_t chunkLength = file.ReadUint32BE(); char magic[4]; file.ReadArray(magic); FileReader chunk = file.ReadChunk(chunkLength); file.Skip(4); // CRC32 if(!memcmp(magic, "IHDR", 4)) { // Image header width = chunk.ReadUint32BE(); height = chunk.ReadUint32BE(); bitDepth = chunk.ReadUint8(); colorType = chunk.ReadUint8(); compressionMethod = chunk.ReadUint8(); filterMethod = chunk.ReadUint8(); interlaceMethod = chunk.ReadUint8(); ASSERT(!filterMethod && !interlaceMethod); } else if(!memcmp(magic, "IDAT", 4)) { // Data block(s) z_stream strm; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = static_cast<uInt>(chunk.GetLength()); strm.next_in = (Bytef *)(chunk.GetRawData()); if(inflateInit2(&strm, 15) != Z_OK) { break; } int retVal; do { dataIn.resize(dataIn.size() + 4096); strm.avail_out = 4096; strm.next_out = (Bytef *)&dataIn[dataIn.size() - 4096]; retVal = inflate(&strm, Z_NO_FLUSH); } while(retVal == Z_OK); inflateEnd(&strm); } else if(!memcmp(magic, "PLTE", 4)) { // Palette for <= 8-bit images palette.resize(256); size_t numEntries = std::min<size_t>(256u, chunk.GetLength() / 3u); for(size_t i = 0; i < numEntries; i++) { uint8_t p[3]; chunk.ReadArray(p); palette[i] = Pixel(p[0], p[1], p[2], 255); } } } // LUT for translating the color type into a number of color samples const uint32_t sampleTable[] = { 1, // 0: Grayscale 0, 3, // 2: RGB 1, // 3: Palette bitmap 2, // 4: Grayscale + Alpha 0, 4 // 6: RGBA }; const uint32_t bitsPerPixel = colorType < CountOf(sampleTable) ? sampleTable[colorType] * bitDepth : 0; if(!width || !height || !bitsPerPixel || (colorType != 2 && colorType != 3 && colorType != 6) || bitDepth != 8 // Only RGB(A) and 8-bit palette PNGs for now. || compressionMethod || interlaceMethod || (colorType == 3 && palette.empty()) || dataIn.size() < (bitsPerPixel * width * height) / 8 + height) // Enough data present? { return nullptr; } Bitmap *bitmap = new (std::nothrow) Bitmap(width, height); Pixel *pixelOut = bitmap->GetPixels(); uint32_t x = 0, y = 0; size_t offset = 0; while(y < height) { if(x == 0) { filterMethod = dataIn[offset++]; ASSERT(!filterMethod); } if(colorType == 6) { // RGBA pixelOut->r = dataIn[offset++]; pixelOut->g = dataIn[offset++]; pixelOut->b = dataIn[offset++]; pixelOut->a = dataIn[offset++]; } else if(colorType == 2) { // RGB pixelOut->r = dataIn[offset++]; pixelOut->g = dataIn[offset++]; pixelOut->b = dataIn[offset++]; pixelOut->a = 255; } else if(colorType == 3) { // Palette *pixelOut = palette[dataIn[offset++]]; } pixelOut++; x++; if(x == width) { y++; x = 0; } } return bitmap; }
VSTPresets::ErrorCode VSTPresets::LoadFile(FileReader &file, CVstPlugin &plugin) //------------------------------------------------------------------------------ { const bool firstChunk = file.GetPosition() == 0; ChunkHeader header; if(!file.ReadConvertEndianness(header) || header.chunkMagic != cMagic) { return invalidFile; } if(header.fxID != plugin.GetUID()) { return wrongPlugin; } if(header.fxMagic == fMagic || header.fxMagic == chunkPresetMagic) { // Program PlugParamIndex numParams = file.ReadUint32BE(); VstPatchChunkInfo info; info.version = 1; info.pluginUniqueID = header.fxID; info.pluginVersion = header.fxVersion; info.numElements = numParams; MemsetZero(info.future); plugin.Dispatch(effBeginLoadProgram, 0, 0, &info, 0.0f); plugin.Dispatch(effBeginSetProgram, 0, 0, nullptr, 0.0f); char prgName[28]; file.ReadString<mpt::String::maybeNullTerminated>(prgName, 28); plugin.Dispatch(effSetProgramName, 0, 0, prgName, 0.0f); if(header.fxMagic == fMagic) { if(plugin.GetNumParameters() != numParams) { return wrongParameters; } for(PlugParamIndex p = 0; p < numParams; p++) { plugin.SetParameter(p, file.ReadFloatBE()); } } else { FileReader chunk = file.ReadChunk(file.ReadUint32BE()); plugin.Dispatch(effSetChunk, 1, chunk.GetLength(), const_cast<char *>(chunk.GetRawData()), 0); } plugin.Dispatch(effEndSetProgram, 0, 0, nullptr, 0.0f); } else if((header.fxMagic == bankMagic || header.fxMagic == chunkBankMagic) && firstChunk) { // Bank - only read if it's the first chunk in the file, not if it's a sub chunk. uint32 numProgs = file.ReadUint32BE(); uint32 currentProgram = file.ReadUint32BE(); file.Skip(124); VstPatchChunkInfo info; info.version = 1; info.pluginUniqueID = header.fxID; info.pluginVersion = header.fxVersion; info.numElements = numProgs; MemsetZero(info.future); plugin.Dispatch(effBeginLoadBank, 0, 0, &info, 0.0f); if(header.fxMagic == bankMagic) { VstInt32 oldCurrentProgram = plugin.GetCurrentProgram(); for(uint32 p = 0; p < numProgs; p++) { plugin.Dispatch(effBeginSetProgram, 0, 0, nullptr, 0.0f); plugin.Dispatch(effSetProgram, 0, 0, nullptr, 0.0f); ErrorCode retVal = LoadFile(file, plugin); if(retVal != noError) { return retVal; } plugin.Dispatch(effEndSetProgram, 0, 0, nullptr, 0.0f); } plugin.SetCurrentProgram(oldCurrentProgram); } else { FileReader chunk = file.ReadChunk(file.ReadUint32BE()); plugin.Dispatch(effSetChunk, 0, chunk.GetLength(), const_cast<char *>(chunk.GetRawData()), 0); } if(header.version >= 2) { plugin.SetCurrentProgram(currentProgram); } } return noError; }