rpcsx/rpcs3/Emu/SysCalls/Modules/cellJpgDec.cpp

358 lines
11 KiB
C++
Raw Normal View History

#include "stdafx.h"
#include "Emu/Memory/Memory.h"
2015-03-06 23:58:42 +01:00
#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "Emu/SysCalls/Modules.h"
2014-08-23 22:40:04 +02:00
2015-04-12 22:16:30 +02:00
extern "C"
{
#include "stblib/stb_image.h"
2015-04-12 22:16:30 +02:00
}
2015-03-12 20:02:02 +01:00
#include "Emu/FS/VFS.h"
#include "Emu/FS/vfsFileBase.h"
#include "Emu/SysCalls/lv2/sys_fs.h"
2015-03-12 20:02:02 +01:00
2014-08-23 22:40:04 +02:00
#include "cellJpgDec.h"
extern Module cellJpgDec;
2015-04-14 04:00:31 +02:00
s32 cellJpgDecCreate(u32 mainHandle, u32 threadInParam, u32 threadOutParam)
{
UNIMPLEMENTED_FUNC(cellJpgDec);
return CELL_OK;
}
2015-04-14 04:00:31 +02:00
s32 cellJpgDecExtCreate(u32 mainHandle, u32 threadInParam, u32 threadOutParam, u32 extThreadInParam, u32 extThreadOutParam)
{
UNIMPLEMENTED_FUNC(cellJpgDec);
return CELL_OK;
}
2015-04-14 04:00:31 +02:00
s32 cellJpgDecDestroy(u32 mainHandle)
{
UNIMPLEMENTED_FUNC(cellJpgDec);
return CELL_OK;
}
2015-04-14 04:00:31 +02:00
s32 cellJpgDecOpen(u32 mainHandle, vm::ptr<u32> subHandle, vm::ptr<CellJpgDecSrc> src, vm::ptr<CellJpgDecOpnInfo> openInfo)
{
2015-04-14 04:00:31 +02:00
cellJpgDec.Warning("cellJpgDecOpen(mainHandle=0x%x, subHandle=*0x%x, src=*0x%x, openInfo=*0x%x)", mainHandle, subHandle, src, openInfo);
2015-05-28 21:13:35 +02:00
auto current_subHandle = std::make_shared<CellJpgDecSubHandle>();
current_subHandle->fd = 0;
current_subHandle->src = *src;
switch(src->srcSelect.data())
{
case se32(CELL_JPGDEC_BUFFER):
2015-01-14 00:08:00 +01:00
current_subHandle->fileSize = src->streamSize;
break;
case se32(CELL_JPGDEC_FILE):
2015-03-12 20:02:02 +01:00
{
// Get file descriptor and size
2015-03-15 01:41:08 +01:00
std::shared_ptr<vfsStream> file_s(Emu.GetVFS().OpenFile(src->fileName.get_ptr(), vfsRead));
if (!file_s) return CELL_JPGDEC_ERROR_OPEN_FILE;
current_subHandle->fd = Emu.GetIdManager().make<lv2_file_t>(file_s, 0, 0);
current_subHandle->fileSize = file_s->GetSize();
break;
}
2015-03-12 20:02:02 +01:00
}
// From now, every u32 subHandle argument is a pointer to a CellJpgDecSubHandle struct.
2015-05-28 21:13:35 +02:00
*subHandle = Emu.GetIdManager().add(std::move(current_subHandle));
return CELL_OK;
}
2015-04-14 04:00:31 +02:00
s32 cellJpgDecClose(u32 mainHandle, u32 subHandle)
{
2015-04-14 04:00:31 +02:00
cellJpgDec.Warning("cellJpgDecOpen(mainHandle=0x%x, subHandle=0x%x)", mainHandle, subHandle);
2014-08-06 00:19:33 +02:00
const auto subHandle_data = Emu.GetIdManager().get<CellJpgDecSubHandle>(subHandle);
2015-04-14 04:00:31 +02:00
if (!subHandle_data)
{
return CELL_JPGDEC_ERROR_FATAL;
2015-04-14 04:00:31 +02:00
}
Emu.GetIdManager().remove<lv2_file_t>(subHandle_data->fd);
Emu.GetIdManager().remove<CellJpgDecSubHandle>(subHandle);
return CELL_OK;
}
2015-04-14 04:00:31 +02:00
s32 cellJpgDecReadHeader(u32 mainHandle, u32 subHandle, vm::ptr<CellJpgDecInfo> info)
{
2015-04-14 04:00:31 +02:00
cellJpgDec.Log("cellJpgDecReadHeader(mainHandle=0x%x, subHandle=0x%x, info=*0x%x)", mainHandle, subHandle, info);
const auto subHandle_data = Emu.GetIdManager().get<CellJpgDecSubHandle>(subHandle);
2015-04-14 04:00:31 +02:00
if (!subHandle_data)
{
return CELL_JPGDEC_ERROR_FATAL;
2015-04-14 04:00:31 +02:00
}
const u32& fd = subHandle_data->fd;
const u64& fileSize = subHandle_data->fileSize;
CellJpgDecInfo& current_info = subHandle_data->info;
//Write the header to buffer
2014-08-31 00:15:11 +02:00
vm::var<u8[]> buffer((u32)fileSize);
switch(subHandle_data->src.srcSelect.data())
{
case se32(CELL_JPGDEC_BUFFER):
2014-09-05 22:26:36 +02:00
memmove(buffer.begin(), vm::get_ptr<void>(subHandle_data->src.streamPtr), buffer.size());
break;
case se32(CELL_JPGDEC_FILE):
2015-03-12 20:02:02 +01:00
{
auto file = Emu.GetIdManager().get<lv2_file_t>(fd);
file->file->Seek(0);
file->file->Read(buffer.ptr(), buffer.size());
break;
}
2015-03-12 20:02:02 +01:00
}
2014-04-04 15:25:38 +02:00
if (*buffer.To<u32>(0) != 0xE0FFD8FF || // Error: Not a valid SOI header
*buffer.To<u32>(6) != 0x4649464A) // Error: Not a valid JFIF string
{
return CELL_JPGDEC_ERROR_HEADER;
}
u32 i = 4;
if(i >= fileSize)
return CELL_JPGDEC_ERROR_HEADER;
u16 block_length = buffer[i] * 0xFF + buffer[i+1];
while(true)
{
2014-04-04 15:25:38 +02:00
i += block_length; // Increase the file index to get to the next block
if (i >= fileSize || // Check to protect against segmentation faults
buffer[i] != 0xFF) // Check that we are truly at the start of another block
{
return CELL_JPGDEC_ERROR_HEADER;
}
if(buffer[i+1] == 0xC0)
2014-04-04 15:25:38 +02:00
break; // 0xFFC0 is the "Start of frame" marker which contains the file size
2014-04-04 15:25:38 +02:00
i += 2; // Skip the block marker
block_length = buffer[i] * 0xFF + buffer[i+1]; // Go to the next block
}
2014-04-04 15:25:38 +02:00
current_info.imageWidth = buffer[i+7]*0x100 + buffer[i+8];
current_info.imageHeight = buffer[i+5]*0x100 + buffer[i+6];
current_info.numComponents = 3; // Unimplemented
current_info.colorSpace = CELL_JPG_RGB;
*info = current_info;
return CELL_OK;
}
s32 cellJpgDecDecodeData(u32 mainHandle, u32 subHandle, vm::ptr<u8> data, vm::cptr<CellJpgDecDataCtrlParam> dataCtrlParam, vm::ptr<CellJpgDecDataOutInfo> dataOutInfo)
{
2015-04-14 04:00:31 +02:00
cellJpgDec.Log("cellJpgDecDecodeData(mainHandle=0x%x, subHandle=0x%x, data=*0x%x, dataCtrlParam=*0x%x, dataOutInfo=*0x%x)", mainHandle, subHandle, data, dataCtrlParam, dataOutInfo);
2014-08-06 00:19:33 +02:00
dataOutInfo->status = CELL_JPGDEC_DEC_STATUS_STOP;
2015-04-14 04:00:31 +02:00
const auto subHandle_data = Emu.GetIdManager().get<CellJpgDecSubHandle>(subHandle);
2015-04-14 04:00:31 +02:00
if (!subHandle_data)
{
return CELL_JPGDEC_ERROR_FATAL;
2015-04-14 04:00:31 +02:00
}
const u32& fd = subHandle_data->fd;
const u64& fileSize = subHandle_data->fileSize;
const CellJpgDecOutParam& current_outParam = subHandle_data->outParam;
//Copy the JPG file to a buffer
2014-08-31 00:15:11 +02:00
vm::var<unsigned char[]> jpg((u32)fileSize);
switch(subHandle_data->src.srcSelect.data())
{
case se32(CELL_JPGDEC_BUFFER):
2014-09-05 22:26:36 +02:00
memmove(jpg.begin(), vm::get_ptr<void>(subHandle_data->src.streamPtr), jpg.size());
break;
case se32(CELL_JPGDEC_FILE):
2015-03-12 20:02:02 +01:00
{
auto file = Emu.GetIdManager().get<lv2_file_t>(fd);
file->file->Seek(0);
file->file->Read(jpg.ptr(), jpg.size());
break;
}
2015-03-12 20:02:02 +01:00
}
//Decode JPG file. (TODO: Is there any faster alternative? Can we do it without external libraries?)
int width, height, actual_components;
auto image = std::unique_ptr<unsigned char,decltype(&::free)>
(
2014-08-31 00:15:11 +02:00
stbi_load_from_memory(jpg.ptr(), (s32)fileSize, &width, &height, &actual_components, 4),
&::free
);
2014-07-10 21:07:46 +02:00
if (!image)
return CELL_JPGDEC_ERROR_STREAM_FORMAT;
const bool flip = current_outParam.outputMode == CELL_JPGDEC_BOTTOM_TO_TOP;
2014-08-30 22:41:01 +02:00
const int bytesPerLine = (u32)dataCtrlParam->outputBytesPerLine;
size_t image_size = width * height;
switch((u32)current_outParam.outputColorSpace)
{
case CELL_JPG_RGB:
case CELL_JPG_RGBA:
{
const char nComponents = current_outParam.outputColorSpace == CELL_JPG_RGBA ? 4 : 3;
image_size *= nComponents;
if (bytesPerLine > width * nComponents || flip) //check if we need padding
2014-07-11 11:18:23 +02:00
{
const int linesize = std::min(bytesPerLine, width * nComponents);
for (int i = 0; i < height; i++)
{
const int dstOffset = i * bytesPerLine;
const int srcOffset = width * nComponents * (flip ? height - i - 1 : i);
2014-09-05 22:26:36 +02:00
memcpy(&data[dstOffset], &image.get()[srcOffset], linesize);
}
}
else
{
2014-09-05 22:26:36 +02:00
memcpy(data.get_ptr(), image.get(), image_size);
2014-07-11 11:18:23 +02:00
}
}
break;
case CELL_JPG_ARGB:
{
const int nComponents = 4;
image_size *= nComponents;
if (bytesPerLine > width * nComponents || flip) //check if we need padding
{
//TODO: Find out if we can't do padding without an extra copy
const int linesize = std::min(bytesPerLine, width * nComponents);
char *output = (char *) malloc(linesize);
for (int i = 0; i < height; i++)
{
const int dstOffset = i * bytesPerLine;
const int srcOffset = width * nComponents * (flip ? height - i - 1 : i);
for (int j = 0; j < linesize; j += nComponents)
{
output[j + 0] = image.get()[srcOffset + j + 3];
output[j + 1] = image.get()[srcOffset + j + 0];
output[j + 2] = image.get()[srcOffset + j + 1];
output[j + 3] = image.get()[srcOffset + j + 2];
}
2014-09-05 22:26:36 +02:00
memcpy(&data[dstOffset], output, linesize);
}
free(output);
}
else
{
uint* img = (uint*)new char[image_size];
uint* source_current = (uint*)&(image.get()[0]);
uint* dest_current = img;
for (uint i = 0; i < image_size / nComponents; i++)
{
uint val = *source_current;
*dest_current = (val >> 24) | (val << 8); // set alpha (A8) as leftmost byte
source_current++;
dest_current++;
}
memcpy(data.get_ptr(), img, image_size);
delete[] img;
}
}
break;
case CELL_JPG_GRAYSCALE:
case CELL_JPG_YCbCr:
case CELL_JPG_UPSAMPLE_ONLY:
case CELL_JPG_GRAYSCALE_TO_ALPHA_RGBA:
case CELL_JPG_GRAYSCALE_TO_ALPHA_ARGB:
cellJpgDec.Error("cellJpgDecDecodeData: Unsupported color space (%d)", current_outParam.outputColorSpace);
break;
default:
return CELL_JPGDEC_ERROR_ARG;
}
dataOutInfo->status = CELL_JPGDEC_DEC_STATUS_FINISH;
if(dataCtrlParam->outputBytesPerLine)
2014-08-30 22:41:01 +02:00
dataOutInfo->outputLines = (u32)(image_size / dataCtrlParam->outputBytesPerLine);
return CELL_OK;
}
s32 cellJpgDecSetParameter(u32 mainHandle, u32 subHandle, vm::cptr<CellJpgDecInParam> inParam, vm::ptr<CellJpgDecOutParam> outParam)
{
2015-04-14 04:00:31 +02:00
cellJpgDec.Log("cellJpgDecSetParameter(mainHandle=0x%x, subHandle=0x%x, inParam=*0x%x, outParam=*0x%x)", mainHandle, subHandle, inParam, outParam);
2014-08-06 00:19:33 +02:00
const auto subHandle_data = Emu.GetIdManager().get<CellJpgDecSubHandle>(subHandle);
2015-04-14 04:00:31 +02:00
if (!subHandle_data)
{
return CELL_JPGDEC_ERROR_FATAL;
2015-04-14 04:00:31 +02:00
}
CellJpgDecInfo& current_info = subHandle_data->info;
CellJpgDecOutParam& current_outParam = subHandle_data->outParam;
2014-04-04 15:25:38 +02:00
current_outParam.outputWidthByte = (current_info.imageWidth * current_info.numComponents);
current_outParam.outputWidth = current_info.imageWidth;
current_outParam.outputHeight = current_info.imageHeight;
current_outParam.outputColorSpace = inParam->outputColorSpace;
switch ((u32)current_outParam.outputColorSpace)
{
2014-04-04 15:25:38 +02:00
case CELL_JPG_GRAYSCALE: current_outParam.outputComponents = 1; break;
case CELL_JPG_RGB:
2014-04-04 15:25:38 +02:00
case CELL_JPG_YCbCr: current_outParam.outputComponents = 3; break;
2014-04-04 15:25:38 +02:00
case CELL_JPG_UPSAMPLE_ONLY: current_outParam.outputComponents = current_info.numComponents; break;
case CELL_JPG_RGBA:
case CELL_JPG_ARGB:
case CELL_JPG_GRAYSCALE_TO_ALPHA_RGBA:
2014-04-04 15:25:38 +02:00
case CELL_JPG_GRAYSCALE_TO_ALPHA_ARGB: current_outParam.outputComponents = 4; break;
2014-04-04 15:25:38 +02:00
default: return CELL_JPGDEC_ERROR_ARG; // Not supported color space
}
2014-04-04 15:25:38 +02:00
current_outParam.outputMode = inParam->outputMode;
current_outParam.downScale = inParam->downScale;
current_outParam.useMemorySpace = 0; // Unimplemented
*outParam = current_outParam;
return CELL_OK;
}
Module cellJpgDec("cellJpgDec", []()
{
REG_FUNC(cellJpgDec, cellJpgDecCreate);
REG_FUNC(cellJpgDec, cellJpgDecExtCreate);
REG_FUNC(cellJpgDec, cellJpgDecOpen);
REG_FUNC(cellJpgDec, cellJpgDecReadHeader);
REG_FUNC(cellJpgDec, cellJpgDecSetParameter);
REG_FUNC(cellJpgDec, cellJpgDecDecodeData);
REG_FUNC(cellJpgDec, cellJpgDecClose);
REG_FUNC(cellJpgDec, cellJpgDecDestroy);
/*REG_FUNC(cellJpgDec, cellJpgDecExtOpen);
REG_FUNC(cellJpgDec, cellJpgDecExtReadHeader);
REG_FUNC(cellJpgDec, cellJpgDecExtSetParameter);
REG_FUNC(cellJpgDec, cellJpgDecExtDecodeData);*/
});