From 713bff01e129b0beea9c538a76c31fb47dde7524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandro=20S=C3=A1nchez=20Bach?= Date: Tue, 22 Jul 2014 21:37:45 +0200 Subject: [PATCH] Flipping / padding on .JPG and .GIF decoding * Changes in cellJpgDec and cellGifDec come from cellPngDec. --- rpcs3/Emu/SysCalls/Modules/cellGifDec.cpp | 81 +++++++++++++++++--- rpcs3/Emu/SysCalls/Modules/cellJpgDec.cpp | 90 +++++++++++++++++++---- rpcs3/Emu/SysCalls/Modules/cellJpgDec.h | 10 ++- rpcs3/Emu/SysCalls/Modules/cellPngDec.cpp | 3 +- 4 files changed, 156 insertions(+), 28 deletions(-) diff --git a/rpcs3/Emu/SysCalls/Modules/cellGifDec.cpp b/rpcs3/Emu/SysCalls/Modules/cellGifDec.cpp index 3e39e91d54..d515a84bb8 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellGifDec.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellGifDec.cpp @@ -180,30 +180,91 @@ int cellGifDecDecodeData(u32 mainHandle, u32 subHandle, mem8_ptr_t data, const m //Decode GIF file. (TODO: Is there any faster alternative? Can we do it without external libraries?) int width, height, actual_components; - std::shared_ptr image(stbi_load_from_memory(gif, fileSize, &width, &height, &actual_components, 4)); + auto image = std::unique_ptr + ( + stbi_load_from_memory(gif.GetPtr(), fileSize, &width, &height, &actual_components, 4), + &::free + ); + if (!image) return CELL_GIFDEC_ERROR_STREAM_FORMAT; - uint image_size = width * height * 4; + const int bytesPerLine = dataCtrlParam->outputBytesPerLine; + const char nComponents = 4; + uint image_size = width * height * nComponents; switch((u32)current_outParam.outputColorSpace) { case CELL_GIFDEC_RGBA: - if (!Memory.CopyFromReal(data.GetAddr(), image.get(), image_size)) + { + if (bytesPerLine > width * nComponents) // Check if we need padding { - cellGifDec->Error("cellGifDecDecodeData() failed (dataa_addr=0x%x)", data.GetAddr()); - return CELL_EFAULT; + 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 * i; + if (!Memory.CopyFromReal(data.GetAddr() + dstOffset, &image.get()[srcOffset], linesize)) + { + cellGifDec->Error("cellGifDecDecodeData() failed (II)"); + return CELL_EFAULT; + } + } } + else + { + if (!Memory.CopyFromReal(data.GetAddr(), image.get(), image_size)) + { + cellGifDec->Error("cellGifDecDecodeData() failed (III)"); + return CELL_EFAULT; + } + } + } break; case CELL_GIFDEC_ARGB: - for(uint i = 0; i < image_size; i+=4) + { + if (bytesPerLine > width * nComponents) // Check if we need padding { - data += image.get()[i+3]; - data += image.get()[i+0]; - data += image.get()[i+1]; - data += image.get()[i+2]; + //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 * 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]; + } + if (!Memory.CopyFromReal(data.GetAddr() + dstOffset, output, linesize)) + { + cellGifDec->Error("cellGifDecDecodeData() failed (IV)"); + return CELL_EFAULT; + } + } + free(output); } + else + { + uint* dest = (uint*)new char[image_size]; + uint* source_current = (uint*)&(image.get()[0]); + uint* dest_current = dest; + 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++; + } + // NOTE: AppendRawBytes has diff side-effect vs Memory.CopyFromReal + data.AppendRawBytes((u8*)dest, image_size); + delete[] dest; + } + } break; default: diff --git a/rpcs3/Emu/SysCalls/Modules/cellJpgDec.cpp b/rpcs3/Emu/SysCalls/Modules/cellJpgDec.cpp index b3e5dd50af..a10c0c13d3 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellJpgDec.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellJpgDec.cpp @@ -188,34 +188,96 @@ int cellJpgDecDecodeData(u32 mainHandle, u32 subHandle, mem8_ptr_t data, const m //Decode JPG file. (TODO: Is there any faster alternative? Can we do it without external libraries?) int width, height, actual_components; - std::shared_ptr image(stbi_load_from_memory(jpg, fileSize, &width, &height, &actual_components, 4)); + auto image = std::unique_ptr + ( + stbi_load_from_memory(jpg.GetPtr(), fileSize, &width, &height, &actual_components, 4), + &::free + ); if (!image) return CELL_JPGDEC_ERROR_STREAM_FORMAT; - uint image_size = width * height; + const bool flip = current_outParam.outputMode == CELL_JPGDEC_BOTTOM_TO_TOP; + const int bytesPerLine = dataCtrlParam->outputBytesPerLine; + size_t image_size = width * height; + switch((u32)current_outParam.outputColorSpace) { - case CELL_JPG_RGBA: case CELL_JPG_RGB: - image_size *= current_outParam.outputColorSpace == CELL_JPG_RGBA ? 4 : 3; - if (!Memory.CopyFromReal(data.GetAddr(), image.get(), image_size)) + 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 { - cellJpgDec->Error("cellJpgDecDecodeData() failed (data_addr=0x%x)", data.GetAddr()); - return CELL_EFAULT; + 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); + if (!Memory.CopyFromReal(data.GetAddr() + dstOffset, &image.get()[srcOffset], linesize)) + { + cellJpgDec->Error("cellJpgDecDecodeData() failed (II)"); + return CELL_EFAULT; + } + } } + else + { + if (!Memory.CopyFromReal(data.GetAddr(), image.get(), image_size)) + { + cellJpgDec->Error("cellJpgDecDecodeData() failed (III)"); + return CELL_EFAULT; + } + } + } break; case CELL_JPG_ARGB: - image_size *= 4; - - for(u32 i = 0; i < image_size; i+=4) + { + const int nComponents = 4; + image_size *= nComponents; + if (bytesPerLine > width * nComponents || flip) //check if we need padding { - data += image.get()[i+3]; - data += image.get()[i+0]; - data += image.get()[i+1]; - data += image.get()[i+2]; + //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]; + } + if (!Memory.CopyFromReal(data.GetAddr() + dstOffset, output, linesize)) + { + cellJpgDec->Error("cellJpgDecDecodeData() failed (IV)"); + return CELL_EFAULT; + } + } + free(output); } + else + { + uint* dest = (uint*)new char[image_size]; + uint* source_current = (uint*)&(image.get()[0]); + uint* dest_current = dest; + 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++; + } + // NOTE: AppendRawBytes has diff side-effect vs Memory.CopyFromReal + data.AppendRawBytes((u8*)dest, image_size); + delete[] dest; + } + } break; case CELL_JPG_GRAYSCALE: diff --git a/rpcs3/Emu/SysCalls/Modules/cellJpgDec.h b/rpcs3/Emu/SysCalls/Modules/cellJpgDec.h index 4fa1438385..2afe342e30 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellJpgDec.h +++ b/rpcs3/Emu/SysCalls/Modules/cellJpgDec.h @@ -35,8 +35,14 @@ enum CellJpgDecStreamSrcSel enum CellJpgDecDecodeStatus { - CELL_JPGDEC_DEC_STATUS_FINISH = 0, //Decoding finished - CELL_JPGDEC_DEC_STATUS_STOP = 1, //Decoding halted + CELL_JPGDEC_DEC_STATUS_FINISH = 0, // Decoding finished + CELL_JPGDEC_DEC_STATUS_STOP = 1, // Decoding halted +}; + +enum CellJpgDecOutputMode +{ + CELL_JPGDEC_TOP_TO_BOTTOM = 0, // Top left to bottom right + CELL_JPGDEC_BOTTOM_TO_TOP = 1, // Bottom left to top right }; struct CellJpgDecInfo diff --git a/rpcs3/Emu/SysCalls/Modules/cellPngDec.cpp b/rpcs3/Emu/SysCalls/Modules/cellPngDec.cpp index adb503a8d3..b704303428 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellPngDec.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellPngDec.cpp @@ -237,12 +237,11 @@ int cellPngDecDecodeData(u32 mainHandle, u32 subHandle, mem8_ptr_t data, const m const bool flip = current_outParam.outputMode == CELL_PNGDEC_BOTTOM_TO_TOP; const int bytesPerLine = dataCtrlParam->outputBytesPerLine; - uint image_size = width * height; + switch((u32)current_outParam.outputColorSpace) { case CELL_PNGDEC_RGB: - image_size = width * height; case CELL_PNGDEC_RGBA: { const char nComponents = current_outParam.outputColorSpace == CELL_PNGDEC_RGBA ? 4 : 3;