You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
636 lines
16 KiB
636 lines
16 KiB
15 years ago
|
/* This file is part of ksquirrel-libs (http://ksquirrel.sf.net)
|
||
|
|
||
|
Copyright (c) 2005 Dmitry Baryshev <ksquirrel@tut.by>
|
||
|
|
||
|
This library is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU Library General Public
|
||
|
License as published by the Free Software Foundation;
|
||
|
either version 2 of the License, or (at your option) any later
|
||
|
version.
|
||
|
|
||
|
This library is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
Library General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Library General Public License
|
||
|
along with this library; see the file COPYING. If not, write to
|
||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
|
Boston, MA 02110-1301, USA.
|
||
|
*/
|
||
|
|
||
|
#include <iostream>
|
||
|
#include <cstdlib>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <algorithm>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#include "ksquirrel-libs/fmt_types.h"
|
||
|
#include "ksquirrel-libs/fileio.h"
|
||
|
#include "ksquirrel-libs/fmt_utils.h"
|
||
|
|
||
|
#include "fmt_codec_psp_defs.h"
|
||
|
#include "fmt_codec_psp.h"
|
||
|
|
||
|
#include "ksquirrel-libs/error.h"
|
||
|
|
||
|
#include "../xpm/codec_psp.xpm"
|
||
|
|
||
|
fmt_codec::fmt_codec() : fmt_codec_base()
|
||
|
{}
|
||
|
|
||
|
fmt_codec::~fmt_codec()
|
||
|
{}
|
||
|
|
||
|
void fmt_codec::options(codec_options *o)
|
||
|
{
|
||
|
o->version = "0.1.0";
|
||
|
o->name = "PaintShop Pro";
|
||
|
o->filter = "*.psp ";
|
||
|
o->config = "";
|
||
|
o->mime = "";
|
||
|
o->mimetype = "image/x-psp";
|
||
|
o->pixmap = codec_psp;
|
||
|
o->readable = true;
|
||
|
o->canbemultiple = false;
|
||
|
o->writestatic = false;
|
||
|
o->writeanimated = false;
|
||
|
o->needtempfile = false;
|
||
|
}
|
||
|
|
||
|
/************** Utility functions from DevIL *********************/
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
//
|
||
|
// ImageLib Sources
|
||
|
// Copyright (C) 2000-2002 by Denton Woods
|
||
|
// Last modified: 05/04/2002 <--Y2K Compliant! =]
|
||
|
//
|
||
|
// Filename: src-IL/src/il_psp.c
|
||
|
//
|
||
|
// Description: Reads a Paint Shop Pro file.
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
bool fmt_codec::iGetPspHead()
|
||
|
{
|
||
|
if(!frs.readK(Header.FileSig, 32))
|
||
|
return false;
|
||
|
|
||
|
if(!frs.readK(&Header.MajorVersion, sizeof(Header.MajorVersion)))
|
||
|
return false;
|
||
|
|
||
|
if(!frs.readK(&Header.MinorVersion, sizeof(Header.MinorVersion)))
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool fmt_codec::iCheckPsp()
|
||
|
{
|
||
|
if (strcmp(Header.FileSig, "Paint Shop Pro Image File\n\x1a")) // "i"
|
||
|
return false;
|
||
|
if (Header.MajorVersion < 3 || Header.MajorVersion > 5)
|
||
|
return false;
|
||
|
if (Header.MinorVersion != 0)
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool fmt_codec::ReadGenAttributes()
|
||
|
{
|
||
|
BLOCKHEAD AttHead;
|
||
|
ILint Padding;
|
||
|
ILuint ChunkLen;
|
||
|
|
||
|
if(!frs.readK(&AttHead, sizeof(AttHead)))
|
||
|
return false;
|
||
|
|
||
|
if (AttHead.HeadID[0] != 0x7E || AttHead.HeadID[1] != 0x42 || AttHead.HeadID[2] != 0x4B || AttHead.HeadID[3] != 0x00)
|
||
|
return false;
|
||
|
|
||
|
if (AttHead.BlockID != PSP_IMAGE_BLOCK)
|
||
|
return false;
|
||
|
|
||
|
if(!frs.readK(&ChunkLen, sizeof(ChunkLen)))
|
||
|
return false;
|
||
|
|
||
|
if (Header.MajorVersion != 3)
|
||
|
ChunkLen -= 4;
|
||
|
|
||
|
if (!frs.readK(&AttChunk, std::min(sizeof(AttChunk), size_t(ChunkLen))))
|
||
|
return false;
|
||
|
|
||
|
// Can have new entries in newer versions of the spec (4.0).
|
||
|
Padding = (ChunkLen) - sizeof(AttChunk);
|
||
|
|
||
|
if (Padding > 0)
|
||
|
frs.seekg(Padding, ios::cur);
|
||
|
|
||
|
// @TODO: Anything but 24 not supported yet...
|
||
|
if (AttChunk.BitDepth != 24 && AttChunk.BitDepth != 8)
|
||
|
return false;
|
||
|
|
||
|
// @TODO; Add support for compression...
|
||
|
if (AttChunk.Compression != PSP_COMP_NONE && AttChunk.Compression != PSP_COMP_RLE)
|
||
|
return false;
|
||
|
|
||
|
// @TODO: Check more things in the general attributes chunk here.
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool fmt_codec::ParseChunks()
|
||
|
{
|
||
|
BLOCKHEAD Block;
|
||
|
size_t Pos;
|
||
|
|
||
|
do {
|
||
|
if (!frs.readK(&Block, sizeof(Block)))
|
||
|
return true;
|
||
|
|
||
|
if (Header.MajorVersion == 3)
|
||
|
frs.readK(&Block.BlockLen, sizeof(Block.BlockLen));
|
||
|
|
||
|
if (Block.HeadID[0] != 0x7E || Block.HeadID[1] != 0x42 || Block.HeadID[2] != 0x4B || Block.HeadID[3] != 0x00)
|
||
|
return true;
|
||
|
|
||
|
Pos = frs.tellg();
|
||
|
|
||
|
switch (Block.BlockID)
|
||
|
{
|
||
|
case PSP_LAYER_START_BLOCK:
|
||
|
if (!ReadLayerBlock())
|
||
|
return false;
|
||
|
break;
|
||
|
|
||
|
case PSP_ALPHA_BANK_BLOCK:
|
||
|
if (!ReadAlphaBlock())
|
||
|
return false;
|
||
|
break;
|
||
|
|
||
|
case PSP_COLOR_BLOCK:
|
||
|
if (!ReadPalette())
|
||
|
return false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Skip to next block just in case we didn't read the entire block.
|
||
|
frs.seekg(Pos + Block.BlockLen, ios::beg);
|
||
|
|
||
|
}
|
||
|
while (1);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool fmt_codec::ReadLayerBlock()
|
||
|
{
|
||
|
BLOCKHEAD Block;
|
||
|
LAYERINFO_CHUNK LayerInfo;
|
||
|
LAYERBITMAP_CHUNK Bitmap;
|
||
|
ILuint ChunkSize, Padding, i;
|
||
|
ILushort NumChars;
|
||
|
|
||
|
// Layer sub-block header
|
||
|
if (!frs.readK(&Block, sizeof(Block)))
|
||
|
return false;
|
||
|
|
||
|
if(Header.MajorVersion == 3)
|
||
|
frs.readK(&Block.BlockLen, sizeof(Block.BlockLen));
|
||
|
|
||
|
if (Block.HeadID[0] != 0x7E || Block.HeadID[1] != 0x42 || Block.HeadID[2] != 0x4B || Block.HeadID[3] != 0x00)
|
||
|
return false;
|
||
|
|
||
|
if (Block.BlockID != PSP_LAYER_BLOCK)
|
||
|
return false;
|
||
|
|
||
|
if (Header.MajorVersion == 3)
|
||
|
{
|
||
|
frs.seekg(256, ios::cur); // We don't care about the name of the layer.
|
||
|
frs.readK(&LayerInfo, sizeof(LayerInfo));
|
||
|
|
||
|
if(!frs.readK(&Bitmap, sizeof(Bitmap)))
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
{ // Header.MajorVersion >= 4
|
||
|
frs.readK(&ChunkSize, sizeof(ChunkSize));
|
||
|
frs.readK(&NumChars, sizeof(NumChars));
|
||
|
frs.seekg(NumChars, ios::cur); // We don't care about the layer's name.
|
||
|
|
||
|
ChunkSize -= (2 + 4 + NumChars);
|
||
|
|
||
|
if (!frs.readK(&LayerInfo, std::min(sizeof(LayerInfo), size_t(ChunkSize))))
|
||
|
return false;
|
||
|
|
||
|
// Can have new entries in newer versions of the spec (5.0).
|
||
|
Padding = (ChunkSize) - sizeof(LayerInfo);
|
||
|
|
||
|
if (Padding > 0)
|
||
|
frs.seekg(Padding, ios::cur);
|
||
|
|
||
|
frs.readK(&ChunkSize, sizeof(ChunkSize));
|
||
|
|
||
|
if (!frs.readK(&Bitmap, sizeof(Bitmap)))
|
||
|
return false;
|
||
|
|
||
|
Padding = (ChunkSize - 4) - sizeof(Bitmap);
|
||
|
|
||
|
if (Padding > 0)
|
||
|
frs.seekg(Padding, ios::cur);
|
||
|
}
|
||
|
|
||
|
Channels = new ILubyte* [Bitmap.NumChannels];
|
||
|
|
||
|
if (!Channels)
|
||
|
return false;
|
||
|
|
||
|
NumChannels = Bitmap.NumChannels;
|
||
|
|
||
|
for (i = 0; i < NumChannels; i++)
|
||
|
Channels[i] = 0;
|
||
|
|
||
|
for (i = 0; i < NumChannels; i++)
|
||
|
{
|
||
|
Channels[i] = GetChannel();
|
||
|
|
||
|
if(!Channels[i])
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool fmt_codec::ReadAlphaBlock()
|
||
|
{
|
||
|
BLOCKHEAD Block;
|
||
|
ALPHAINFO_CHUNK AlphaInfo;
|
||
|
ALPHA_CHUNK AlphaChunk;
|
||
|
ILushort NumAlpha, StringSize;
|
||
|
ILuint ChunkSize, Padding;
|
||
|
|
||
|
if (Header.MajorVersion == 3) {
|
||
|
frs.readK(&NumAlpha, sizeof(NumAlpha));
|
||
|
}
|
||
|
else {
|
||
|
frs.readK(&ChunkSize, sizeof(ChunkSize));
|
||
|
frs.readK(&NumAlpha, sizeof(NumAlpha));
|
||
|
|
||
|
Padding = (ChunkSize - 4 - 2);
|
||
|
|
||
|
if (Padding > 0)
|
||
|
frs.seekg(Padding, ios::cur);
|
||
|
}
|
||
|
|
||
|
// Alpha channel header
|
||
|
if (!frs.readK(&Block, sizeof(Block)))
|
||
|
return false;
|
||
|
|
||
|
if (Header.MajorVersion == 3)
|
||
|
frs.readK(&Block.BlockLen, sizeof(Block.BlockLen));
|
||
|
|
||
|
if (Block.HeadID[0] != 0x7E || Block.HeadID[1] != 0x42 ||
|
||
|
Block.HeadID[2] != 0x4B || Block.HeadID[3] != 0x00) {
|
||
|
return false;
|
||
|
}
|
||
|
if (Block.BlockID != PSP_ALPHA_CHANNEL_BLOCK)
|
||
|
return false;
|
||
|
|
||
|
if (Header.MajorVersion >= 4)
|
||
|
{
|
||
|
frs.readK(&ChunkSize, sizeof(ChunkSize));
|
||
|
frs.readK(&StringSize, sizeof(StringSize));
|
||
|
|
||
|
frs.seekg(StringSize, ios::cur);
|
||
|
|
||
|
if (!frs.readK(&AlphaInfo, sizeof(AlphaInfo)))
|
||
|
return false;
|
||
|
|
||
|
Padding = (ChunkSize - 4 - 2 - StringSize - sizeof(AlphaInfo));
|
||
|
|
||
|
if (Padding > 0)
|
||
|
frs.seekg(Padding, ios::cur);
|
||
|
|
||
|
frs.readK(&ChunkSize, sizeof(ChunkSize));
|
||
|
|
||
|
if (!frs.readK(&AlphaChunk, sizeof(AlphaChunk)))
|
||
|
return false;
|
||
|
|
||
|
Padding = (ChunkSize - 4 - sizeof(AlphaChunk));
|
||
|
|
||
|
if (Padding > 0)
|
||
|
frs.seekg(Padding, ios::cur);
|
||
|
}
|
||
|
else {
|
||
|
frs.seekg(256, ios::cur);
|
||
|
frs.readK(&AlphaInfo, sizeof(AlphaInfo));
|
||
|
|
||
|
if(!frs.readK(&AlphaChunk, sizeof(AlphaChunk)))
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
Alpha = GetChannel();
|
||
|
|
||
|
if (!Alpha)
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
ILubyte *fmt_codec::GetChannel()
|
||
|
{
|
||
|
BLOCKHEAD Block;
|
||
|
CHANNEL_CHUNK Channel;
|
||
|
ILubyte *CompData = 0, *Data = 0;
|
||
|
ILuint ChunkSize, Padding;
|
||
|
|
||
|
if (!frs.readK(&Block, sizeof(Block)))
|
||
|
return 0;
|
||
|
|
||
|
if (Header.MajorVersion == 3)
|
||
|
frs.readK(&Block.BlockLen, sizeof(Block.BlockLen));
|
||
|
|
||
|
if (Block.HeadID[0] != 0x7E || Block.HeadID[1] != 0x42 || Block.HeadID[2] != 0x4B || Block.HeadID[3] != 0x00)
|
||
|
return 0;
|
||
|
|
||
|
if (Block.BlockID != PSP_CHANNEL_BLOCK)
|
||
|
return 0;
|
||
|
|
||
|
if (Header.MajorVersion >= 4)
|
||
|
{
|
||
|
frs.readK(&ChunkSize, sizeof(ChunkSize));
|
||
|
|
||
|
if (!frs.readK(&Channel, sizeof(Channel)))
|
||
|
return 0;
|
||
|
|
||
|
Padding = (ChunkSize - 4) - sizeof(Channel);
|
||
|
|
||
|
if (Padding > 0)
|
||
|
frs.seekg(Padding, ios::cur);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!frs.readK(&Channel, sizeof(Channel)))
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
CompData = new ILubyte [Channel.CompLen];
|
||
|
|
||
|
if (!CompData)
|
||
|
return 0;
|
||
|
|
||
|
if (!frs.readK(CompData, Channel.CompLen))
|
||
|
{
|
||
|
delete CompData;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if(AttChunk.Compression != PSP_COMP_NONE)
|
||
|
{
|
||
|
Data = new ILubyte [AttChunk.Width * AttChunk.Height];
|
||
|
|
||
|
if (!Data)
|
||
|
{
|
||
|
delete CompData;
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch (AttChunk.Compression)
|
||
|
{
|
||
|
case PSP_COMP_NONE:
|
||
|
return CompData;
|
||
|
|
||
|
case PSP_COMP_RLE:
|
||
|
if (!UncompRLE(CompData, Data, Channel.CompLen))
|
||
|
{
|
||
|
delete Data;
|
||
|
delete CompData;
|
||
|
return 0;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
delete Data;
|
||
|
delete CompData;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
delete CompData;
|
||
|
|
||
|
return Data;
|
||
|
}
|
||
|
|
||
|
bool fmt_codec::UncompRLE(ILubyte *CompData, ILubyte *Data, ILuint CompLen)
|
||
|
{
|
||
|
ILubyte Run, Colour;
|
||
|
ILint i, Count;
|
||
|
|
||
|
for (i = 0, Count = 0; i < (ILint)CompLen; ) {
|
||
|
Run = *CompData++;
|
||
|
i++;
|
||
|
if (Run > 128) {
|
||
|
Run -= 128;
|
||
|
Colour = *CompData++;
|
||
|
i++;
|
||
|
memset(Data, Colour, Run);
|
||
|
}
|
||
|
else {
|
||
|
memcpy(Data, CompData, Run);
|
||
|
CompData += Run;
|
||
|
i += Run;
|
||
|
}
|
||
|
Data += Run;
|
||
|
Count += Run;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool fmt_codec::ReadPalette()
|
||
|
{
|
||
|
ILuint ChunkSize, PalCount, Padding;
|
||
|
RGBA rgba;
|
||
|
|
||
|
if (Header.MajorVersion >= 4)
|
||
|
{
|
||
|
frs.readK(&ChunkSize, sizeof(ChunkSize));
|
||
|
frs.readK(&PalCount, sizeof(PalCount));
|
||
|
|
||
|
Padding = (ChunkSize - 4 - 4);
|
||
|
|
||
|
if (Padding > 0)
|
||
|
frs.seekg(Padding, ios::cur);
|
||
|
}
|
||
|
else
|
||
|
frs.readK(&PalCount, sizeof(PalCount));
|
||
|
|
||
|
pal = new RGBA [PalCount];
|
||
|
|
||
|
if(!pal)
|
||
|
return false;
|
||
|
|
||
|
RGBA *ppal = pal;
|
||
|
|
||
|
for(u32 i = 0;i < PalCount;i++)
|
||
|
{
|
||
|
if(!frs.readK(&rgba, sizeof(RGBA)))
|
||
|
return false;
|
||
|
|
||
|
ppal->r = rgba.b;
|
||
|
ppal->g = rgba.g;
|
||
|
ppal->b = rgba.r;
|
||
|
ppal->a = rgba.a;
|
||
|
|
||
|
ppal++;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/******************** ksquirrel-libs stuff ****************************/
|
||
|
|
||
|
s32 fmt_codec::read_init(const std::string &file)
|
||
|
{
|
||
|
Channels = 0;
|
||
|
Alpha = 0;
|
||
|
pal = 0;
|
||
|
|
||
|
frs.open(file.c_str(), ios::binary | ios::in);
|
||
|
|
||
|
if(!frs.good())
|
||
|
return SQE_R_NOFILE;
|
||
|
|
||
|
currentImage = -1;
|
||
|
read_error = false;
|
||
|
finfo.animated = false;
|
||
|
|
||
|
if(!iGetPspHead())
|
||
|
return SQE_R_BADFILE;
|
||
|
|
||
|
if(!iCheckPsp())
|
||
|
return SQE_R_BADFILE;
|
||
|
|
||
|
return SQE_OK;
|
||
|
}
|
||
|
|
||
|
s32 fmt_codec::read_next()
|
||
|
{
|
||
|
currentImage++;
|
||
|
|
||
|
if(currentImage)
|
||
|
return SQE_NOTOK;
|
||
|
|
||
|
if(!ReadGenAttributes())
|
||
|
return SQE_R_BADFILE;
|
||
|
|
||
|
if(!ParseChunks())
|
||
|
return SQE_R_BADFILE;
|
||
|
|
||
|
// if(!AssembleImage())
|
||
|
// return SQE_R_BADFILE;
|
||
|
|
||
|
fmt_image image;
|
||
|
|
||
|
image.w = AttChunk.Width;
|
||
|
image.h = AttChunk.Height;
|
||
|
image.compression = (AttChunk.Compression == PSP_COMP_RLE ? "RLE" : "-");
|
||
|
image.bpp = (NumChannels == 1 ? 8 : ((Alpha || NumChannels == 4) ? 32 : 24));
|
||
|
image.colorspace = fmt_utils::colorSpaceByBpp(image.bpp);
|
||
|
|
||
|
finfo.image.push_back(image);
|
||
|
|
||
|
return SQE_OK;
|
||
|
}
|
||
|
|
||
|
s32 fmt_codec::read_next_pass()
|
||
|
{
|
||
|
line = -1;
|
||
|
|
||
|
return SQE_OK;
|
||
|
}
|
||
|
|
||
|
s32 fmt_codec::read_scanline(RGBA *scan)
|
||
|
{
|
||
|
line++;
|
||
|
|
||
|
fmt_image *im = image(currentImage);
|
||
|
|
||
|
u32 i, j;
|
||
|
u32 ifrom = line * im->w;
|
||
|
u32 ito = ifrom + im->w;
|
||
|
|
||
|
if (NumChannels == 1)
|
||
|
{
|
||
|
memset(scan, 0, im->w * sizeof(RGBA));
|
||
|
|
||
|
for (i = ifrom, j = 0; i < ito; i++, j++)
|
||
|
{
|
||
|
scan[j] = pal[Channels[0][i]];
|
||
|
scan[j].a = 255;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (Alpha)
|
||
|
{
|
||
|
memset(scan, 0, im->w * sizeof(RGBA));
|
||
|
u8 *data = (u8 *)scan;
|
||
|
|
||
|
for (i = ifrom, j = 0; i < ito; i++, j += 4)
|
||
|
{
|
||
|
data[j ] = Channels[0][i];
|
||
|
data[j+1] = Channels[1][i];
|
||
|
data[j+2] = Channels[2][i];
|
||
|
data[j+3] = Alpha[i];
|
||
|
}
|
||
|
}
|
||
|
// 3 channels, or 4 without alpha
|
||
|
else if (NumChannels == 3 || NumChannels == 4)
|
||
|
{
|
||
|
memset(scan, 0, im->w * sizeof(RGBA));
|
||
|
u8 *data = (u8 *)scan;
|
||
|
|
||
|
for (i = ifrom, j = 0; i < ito; i++, j += 4)
|
||
|
{
|
||
|
data[j ] = Channels[0][i];
|
||
|
data[j+1] = Channels[1][i];
|
||
|
data[j+2] = Channels[2][i];
|
||
|
data[j+3] = 255;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
return SQE_R_BADFILE;
|
||
|
}
|
||
|
|
||
|
return SQE_OK;
|
||
|
}
|
||
|
|
||
|
void fmt_codec::read_close()
|
||
|
{
|
||
|
frs.close();
|
||
|
|
||
|
if(Channels)
|
||
|
{
|
||
|
for (u32 i = 0; i < NumChannels; i++)
|
||
|
delete Channels[i];
|
||
|
|
||
|
delete Channels;
|
||
|
}
|
||
|
|
||
|
delete Alpha;
|
||
|
delete pal;
|
||
|
|
||
|
Channels = 0;
|
||
|
Alpha = 0;
|
||
|
pal = 0;
|
||
|
|
||
|
finfo.meta.clear();
|
||
|
finfo.image.clear();
|
||
|
}
|
||
|
|
||
|
#include "fmt_codec_cd_func.h"
|