s32 pngDecClose(ppu_thread& ppu, PHandle handle, PStream stream) { // Remove the file descriptor, if a file descriptor was used for decoding if (stream->buffer->file) { idm::remove<lv2_fs_object, lv2_file>(stream->buffer->fd); } // Deallocate the PNG buffer structure used to decode from memory, if we decoded from memory if (stream->buffer) { if (handle->free_(ppu, stream->buffer, handle->free_arg) != 0) { cellPngDec.error("PNG buffer decoding structure deallocation failed."); return CELL_PNGDEC_ERROR_FATAL; } } // Free the memory allocated by libpng png_destroy_read_struct(&stream->png_ptr, &stream->info_ptr, nullptr); // Deallocate the stream memory if (handle->free_(ppu, stream, handle->free_arg) != 0) { cellPngDec.error("PNG stream deallocation failed."); return CELL_PNGDEC_ERROR_FATAL; } return CELL_OK; }
s32 pngDecDestroy(ppu_thread& ppu, PHandle handle) { // Deallocate the decoder handle memory if (handle->free_(ppu, handle, handle->free_arg) != 0) { cellPngDec.error("PNG decoder deallocation failed."); return CELL_PNGDEC_ERROR_FATAL; } return CELL_OK; }
s32 pngDecodeData(ppu_thread& ppu, PHandle handle, PStream stream, vm::ptr<u8> data, PDataControlParam data_control_param, PDataOutInfo data_out_info, PCbControlDisp cb_control_disp = vm::null, PDispParam disp_param = vm::null) { // Indicate, that the PNG decoding is stopped/failed. This is incase, we return an error code in the middle of decoding data_out_info->status = CELL_PNGDEC_DEC_STATUS_STOP; const u32 bytes_per_line = data_control_param->outputBytesPerLine; // Log this for now if (bytes_per_line < stream->out_param.outputWidthByte) { fmt::throw_exception("Bytes per line less than expected output! Got: %d, expected: %d" HERE, bytes_per_line, stream->out_param.outputWidthByte); } // partial decoding if (cb_control_disp && stream->outputCounts > 0) { // get data from cb auto streamInfo = vm::ptr<CellPngDecStrmInfo>::make(handle->malloc_(ppu, sizeof(CellPngDecStrmInfo), handle->malloc_arg).addr()); auto streamParam = vm::ptr<CellPngDecStrmParam>::make(handle->malloc_(ppu, sizeof(CellPngDecStrmParam), handle->malloc_arg).addr()); stream->cbDispInfo = vm::ptr<CellPngDecDispInfo>::make(handle->malloc_(ppu, sizeof(CellPngDecDispInfo), handle->malloc_arg).addr()); stream->cbDispParam = vm::ptr<CellPngDecDispParam>::make(handle->malloc_(ppu, sizeof(CellPngDecDispParam), handle->malloc_arg).addr()); auto freeMem = [&]() { handle->free_(ppu, streamInfo, handle->free_arg); handle->free_(ppu, streamParam, handle->free_arg); handle->free_(ppu, stream->cbDispInfo, handle->free_arg); handle->free_(ppu, stream->cbDispParam, handle->free_arg); }; // set things that won't change between callbacks stream->cbDispInfo->outputFrameWidthByte = bytes_per_line; stream->cbDispInfo->outputFrameHeight = stream->out_param.outputHeight; stream->cbDispInfo->outputWidthByte = stream->out_param.outputWidthByte; stream->cbDispInfo->outputBitDepth = stream->out_param.outputBitDepth; stream->cbDispInfo->outputComponents = stream->out_param.outputComponents; stream->cbDispInfo->outputHeight = stream->outputCounts; stream->cbDispInfo->outputStartXByte = 0; stream->cbDispInfo->outputStartY = 0; stream->cbDispInfo->scanPassCount = 0; stream->cbDispInfo->nextOutputStartY = 0; stream->ppuContext = &ppu; stream->nextRow = stream->cbDispInfo->outputHeight; stream->cbCtrlDisp.cbCtrlDispArg = cb_control_disp->cbCtrlDispArg; stream->cbCtrlDisp.cbCtrlDispFunc = cb_control_disp->cbCtrlDispFunc; stream->cbDispParam->nextOutputImage = disp_param->nextOutputImage; streamInfo->decodedStrmSize = stream->buffer->cursor; // push the rest of the buffer we have if (stream->buffer->length > stream->buffer->cursor) { u8* data = static_cast<u8*>(stream->buffer->data.get_ptr()) + stream->buffer->cursor; try { png_process_data(stream->png_ptr, stream->info_ptr, data, stream->buffer->length - stream->buffer->cursor); } catch (LibPngCustomException&) { freeMem(); return CELL_PNGDEC_ERROR_FATAL; } streamInfo->decodedStrmSize = stream->buffer->length; } // todo: commandPtr // then just loop until the end, the callbacks should take care of the rest while (stream->endOfFile != true) { stream->cbCtrlStream.cbCtrlStrmFunc(ppu, streamInfo, streamParam, stream->cbCtrlStream.cbCtrlStrmArg); streamInfo->decodedStrmSize += streamParam->strmSize; try { png_process_data(stream->png_ptr, stream->info_ptr, static_cast<u8*>(streamParam->strmPtr.get_ptr()), streamParam->strmSize); } catch (LibPngCustomException&) { freeMem(); return CELL_PNGDEC_ERROR_FATAL; } } freeMem(); } else { // Check if the image needs to be flipped const bool flip = stream->out_param.outputMode == CELL_PNGDEC_BOTTOM_TO_TOP; // Decode the image // todo: commandptr try { for (int j = 0; j < stream->passes; j++) { for (int i = 0; i < stream->out_param.outputHeight; ++i) { const u32 line = flip ? stream->out_param.outputHeight - i - 1 : i; png_read_row(stream->png_ptr, &data[line*bytes_per_line], nullptr); } } png_read_end(stream->png_ptr, stream->info_ptr); } catch (LibPngCustomException&) { return CELL_PNGDEC_ERROR_FATAL; } } // Get the number of iTXt, tEXt and zTXt chunks const s32 text_chunks = png_get_text(stream->png_ptr, stream->info_ptr, nullptr, nullptr); // Set the chunk information and the previously obtained number of text chunks data_out_info->numText = (u32)text_chunks; data_out_info->chunkInformation = pngDecGetChunkInformation(stream.get_ptr(), true); png_unknown_chunkp unknowns; const int num_unknowns = png_get_unknown_chunks(stream->png_ptr, stream->info_ptr, &unknowns); data_out_info->numUnknownChunk = num_unknowns; // Indicate that the decoding succeeded data_out_info->status = CELL_PNGDEC_DEC_STATUS_FINISH; return CELL_OK; }