diff --git a/Python/CMakeLists.txt b/Python/CMakeLists.txt
index 101245a17..b45e94f6e 100644
--- a/Python/CMakeLists.txt
+++ b/Python/CMakeLists.txt
@@ -684,10 +684,14 @@ set(SYS_HEADERS
set(UTIL_SOURCES
Util/pyBitVector.cpp
Util/pyDDSurface.cpp
+ Util/pyJPEG.cpp
+ Util/pyPNG.cpp
)
set(UTIL_HEADERS
Util/pyBitVector.h
Util/pyDDSurface.h
+ Util/pyJPEG.h
+ Util/pyPNG.h
)
set(VAULT_SOURCES
diff --git a/Python/Module.cpp b/Python/Module.cpp
index fb0c09152..c7bd3c259 100644
--- a/Python/Module.cpp
+++ b/Python/Module.cpp
@@ -29,6 +29,8 @@
#include "Sys/pyUnifiedTime.h"
#include "Util/pyBitVector.h"
#include "Util/pyDDSurface.h"
+#include "Util/pyJPEG.h"
+#include "Util/pyPNG.h"
#include "Vault/pyServerGuid.h"
#include "Vault/pyVaultNode.h"
#include "Vault/pyVaultStore.h"
@@ -487,6 +489,8 @@ PyMODINIT_FUNC PyInit_PyHSPlasma()
/* Util */
PyModule_AddObject(module, "hsBitVector", Init_pyBitVector_Type());
PyModule_AddObject(module, "plDDSurface", Init_pyDDSurface_Type());
+ PyModule_AddObject(module, "plJPEG", Init_pyJPEG_Type());
+ PyModule_AddObject(module, "plPNG", Init_pyPNG_Type());
/* Vault */
PyModule_AddObject(module, "plServerGuid", Init_pyServerGuid_Type());
diff --git a/Python/PyHSPlasma.pyi b/Python/PyHSPlasma.pyi
index cf72abc7f..0cbb4da26 100644
--- a/Python/PyHSPlasma.pyi
+++ b/Python/PyHSPlasma.pyi
@@ -3757,6 +3757,11 @@ class plInterfaceInfoModifier(plSingleModifier):
def clearIntfKeys(self) -> None: ...
def delIntfKey(self, idx: int) -> None: ...
+class plJPEG:
+ @staticmethod
+ def DecompressJPEG(stream: hsStream) -> plMipmap:
+ ...
+
class plKey(Generic[KeyedT]):
id: int = ...
location: plLocation = ...
@@ -4482,6 +4487,11 @@ class plPhysicalSndGroup(hsKeyedObject):
class plPickingDetector(plDetectorModifier):
...
+class plPNG:
+ @staticmethod
+ def DecompressPNG(stream: hsStream) -> plMipmap:
+ ...
+
class plPoint3Controller(plLeafController):
...
diff --git a/Python/Util/pyJPEG.cpp b/Python/Util/pyJPEG.cpp
new file mode 100644
index 000000000..8f8e946fa
--- /dev/null
+++ b/Python/Util/pyJPEG.cpp
@@ -0,0 +1,73 @@
+/* This file is part of HSPlasma.
+ *
+ * HSPlasma is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HSPlasma 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HSPlasma. If not, see .
+ */
+
+#include "pyJPEG.h"
+
+#include "PRP/Surface/pyBitmap.h"
+#include "Stream/pyStream.h"
+
+#include
+
+PY_PLASMA_EMPTY_INIT(JPEG)
+PY_PLASMA_NEW_MSG(JPEG, "plJPEG cannot be constructed")
+
+PY_METHOD_STATIC_VA(JPEG, DecompressJPEG,
+ "Params: stream\n"
+ "Read JPEG file from stream directly into a plMipmap")
+{
+ PyObject* streamObj;
+ if (!PyArg_ParseTuple(args, "O", &streamObj)) {
+ PyErr_SetString(PyExc_TypeError, "DecompressJPEG expects an hsStream");
+ return nullptr;
+ }
+ if (!pyStream_Check(streamObj)) {
+ PyErr_SetString(PyExc_TypeError, "DecompressJPEG expects an hsStream");
+ return nullptr;
+ }
+
+ plMipmap* mm = plJPEG::DecompressJPEG(((pyStream*)streamObj)->fThis);
+
+ // We're doing this manually because the new Mipmap object is being
+ // released to Python code.
+ pyMipmap* mmObj = nullptr;
+ try {
+ mmObj = PyObject_New(pyMipmap, &pyMipmap_Type);
+ } catch (const hsJPEGException& ex) {
+ PyErr_SetString(PyExc_RuntimeError, ex.what());
+ return nullptr;
+ }
+ mmObj->fPyOwned = true;
+ mmObj->fThis = mm;
+ return (PyObject*)mmObj;
+}
+
+static PyMethodDef pyJPEG_Methods[] = {
+ pyJPEG_DecompressJPEG_method,
+ PY_METHOD_TERMINATOR,
+};
+
+PY_PLASMA_TYPE(JPEG, plJPEG, "plJPEG wrapper")
+
+PY_PLASMA_TYPE_INIT(JPEG)
+{
+ pyJPEG_Type.tp_new = pyJPEG_new;
+ pyJPEG_Type.tp_methods = pyJPEG_Methods;
+ if (PyType_CheckAndReady(&pyJPEG_Type) < 0)
+ return nullptr;
+
+ Py_INCREF(&pyJPEG_Type);
+ return (PyObject*)&pyJPEG_Type;
+}
diff --git a/Python/Util/pyJPEG.h b/Python/Util/pyJPEG.h
new file mode 100644
index 000000000..041570c98
--- /dev/null
+++ b/Python/Util/pyJPEG.h
@@ -0,0 +1,24 @@
+/* This file is part of HSPlasma.
+ *
+ * HSPlasma is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HSPlasma 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HSPlasma. If not, see .
+ */
+
+#ifndef _PYJPEG_H
+#define _PYJPEG_H
+
+#include "PyPlasma.h"
+
+PY_WRAP_PLASMA(JPEG, class plJPEG);
+
+#endif
diff --git a/Python/Util/pyPNG.cpp b/Python/Util/pyPNG.cpp
new file mode 100644
index 000000000..5f9b53868
--- /dev/null
+++ b/Python/Util/pyPNG.cpp
@@ -0,0 +1,73 @@
+/* This file is part of HSPlasma.
+ *
+ * HSPlasma is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HSPlasma 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HSPlasma. If not, see .
+ */
+
+#include "pyPNG.h"
+
+#include "PRP/Surface/pyBitmap.h"
+#include "Stream/pyStream.h"
+
+#include
+
+PY_PLASMA_EMPTY_INIT(PNG)
+PY_PLASMA_NEW_MSG(PNG, "plPNG cannot be constructed")
+
+PY_METHOD_STATIC_VA(PNG, DecompressPNG,
+ "Params: stream\n"
+ "Read PNG file from stream directly into a plMipmap")
+{
+ PyObject* streamObj;
+ if (!PyArg_ParseTuple(args, "O", &streamObj)) {
+ PyErr_SetString(PyExc_TypeError, "DecompressPNG expects an hsStream");
+ return nullptr;
+ }
+ if (!pyStream_Check(streamObj)) {
+ PyErr_SetString(PyExc_TypeError, "DecompressPNG expects an hsStream");
+ return nullptr;
+ }
+
+ plMipmap* mm = nullptr;
+ try {
+ mm = plPNG::DecompressPNG(((pyStream*)streamObj)->fThis);
+ } catch (const hsPNGException& ex) {
+ PyErr_SetString(PyExc_RuntimeError, ex.what());
+ return nullptr;
+ }
+
+ // We're doing this manually because the new Mipmap object is being
+ // released to Python code.
+ pyMipmap* mmObj = PyObject_New(pyMipmap, &pyMipmap_Type);
+ mmObj->fPyOwned = true;
+ mmObj->fThis = mm;
+ return (PyObject*)mmObj;
+}
+
+static PyMethodDef pyPNG_Methods[] = {
+ pyPNG_DecompressPNG_method,
+ PY_METHOD_TERMINATOR,
+};
+
+PY_PLASMA_TYPE(PNG, plPNG, "plPNG wrapper")
+
+PY_PLASMA_TYPE_INIT(PNG)
+{
+ pyPNG_Type.tp_new = pyPNG_new;
+ pyPNG_Type.tp_methods = pyPNG_Methods;
+ if (PyType_CheckAndReady(&pyPNG_Type) < 0)
+ return nullptr;
+
+ Py_INCREF(&pyPNG_Type);
+ return (PyObject*)&pyPNG_Type;
+}
diff --git a/Python/Util/pyPNG.h b/Python/Util/pyPNG.h
new file mode 100644
index 000000000..3d478ecf6
--- /dev/null
+++ b/Python/Util/pyPNG.h
@@ -0,0 +1,24 @@
+/* This file is part of HSPlasma.
+ *
+ * HSPlasma is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HSPlasma 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HSPlasma. If not, see .
+ */
+
+#ifndef _PYPNG_H
+#define _PYPNG_H
+
+#include "PyPlasma.h"
+
+PY_WRAP_PLASMA(PNG, class plPNG);
+
+#endif
diff --git a/core/Util/plJPEG.cpp b/core/Util/plJPEG.cpp
index ca7f47037..bfaedb97a 100644
--- a/core/Util/plJPEG.cpp
+++ b/core/Util/plJPEG.cpp
@@ -22,6 +22,7 @@
#include
extern "C" {
+#include
#include
}
@@ -192,34 +193,58 @@ METHODDEF(void) plasma_output_message(j_common_ptr cinfo)
METHODDEF(void) plasma_error_exit(j_common_ptr cinfo)
{
(*cinfo->err->output_message)(cinfo);
- jpeg_destroy(cinfo);
throw hsJPEGException(__FILE__, __LINE__, jpeg_error_buf);
}
-
/* plJPEG */
-plJPEG& plJPEG::Instance()
+class plJPEG_CInfo
{
- static plJPEG s_instance;
- return s_instance;
-}
+ jpeg_compress_struct fStruct;
+ jpeg_error_mgr fErrorMgr;
-plJPEG::plJPEG()
-{
- cinfo.err = jpeg_std_error(&jerr);
- dinfo.err = cinfo.err;
- cinfo.err->error_exit = plasma_error_exit;
- cinfo.err->output_message = plasma_output_message;
+public:
+ plJPEG_CInfo()
+ {
+ fStruct.err = jpeg_std_error(&fErrorMgr);
+ fErrorMgr.error_exit = plasma_error_exit;
+ fErrorMgr.output_message = plasma_output_message;
- jpeg_create_compress(&cinfo);
- jpeg_create_decompress(&dinfo);
-}
+ jpeg_create_compress(&fStruct);
+ }
-plJPEG::~plJPEG()
+ ~plJPEG_CInfo()
+ {
+ jpeg_destroy_compress(&fStruct);
+ }
+
+ j_compress_ptr operator &() { return &fStruct; }
+ j_compress_ptr operator ->() { return &fStruct; }
+};
+
+class plJPEG_DInfo
{
- jpeg_destroy_compress(&cinfo);
- jpeg_destroy_decompress(&dinfo);
-}
+ jpeg_decompress_struct fStruct;
+ jpeg_error_mgr fErrorMgr;
+
+public:
+ plJPEG_DInfo()
+ {
+ fStruct.err = jpeg_std_error(&fErrorMgr);
+ fErrorMgr.error_exit = plasma_error_exit;
+ fErrorMgr.output_message = plasma_output_message;
+
+ jpeg_create_decompress(&fStruct);
+ }
+
+ ~plJPEG_DInfo()
+ {
+ jpeg_destroy_decompress(&fStruct);
+ }
+
+ j_decompress_ptr operator &() { return &fStruct; }
+ j_decompress_ptr operator ->() { return &fStruct; }
+};
+
template
struct RAII_JSAMPROW
@@ -241,65 +266,65 @@ struct RAII_JSAMPROW
void plJPEG::DecompressJPEG(hsStream* S, void* buf, size_t size)
{
- plJPEG& ji = Instance();
+ plJPEG_DInfo dinfo;
- jpeg_hsStream_src(&ji.dinfo, S);
- jpeg_read_header(&ji.dinfo, TRUE);
- ji.dinfo.out_color_space = JCS_EXT_BGRA; // Data stored as RGB on disk but Plasma uses BGR
- jpeg_start_decompress(&ji.dinfo);
+ jpeg_hsStream_src(&dinfo, S);
+ jpeg_read_header(&dinfo, TRUE);
+ dinfo->out_color_space = JCS_EXT_BGRA; // Data stored as RGB on disk but Plasma uses BGR
+ jpeg_start_decompress(&dinfo);
- int row_stride = ji.dinfo.output_width * ji.dinfo.out_color_components;
+ int row_stride = dinfo->output_width * dinfo->out_color_components;
int out_stride = row_stride;
RAII_JSAMPROW<1> jbuffer(row_stride);
size_t offs = 0;
- while (ji.dinfo.output_scanline < ji.dinfo.output_height) {
+ while (dinfo->output_scanline < dinfo->output_height) {
if (offs + out_stride > size)
throw hsJPEGException(__FILE__, __LINE__, "buffer overflow");
- jpeg_read_scanlines(&ji.dinfo, jbuffer.data, 1);
- for (size_t x = 0; xoutput_width; x++) {
memcpy(((unsigned char*)buf) + offs + (x * 4),
- jbuffer.data[0] + (x * ji.dinfo.out_color_components),
- ji.dinfo.out_color_components);
+ jbuffer.data[0] + (x * dinfo->out_color_components),
+ dinfo->out_color_components);
}
offs += out_stride;
}
- jpeg_finish_decompress(&ji.dinfo);
+ jpeg_finish_decompress(&dinfo);
}
plMipmap* plJPEG::DecompressJPEG(hsStream* S)
{
- plJPEG& ji = Instance();
+ plJPEG_DInfo dinfo;
- jpeg_hsStream_src(&ji.dinfo, S);
- jpeg_read_header(&ji.dinfo, TRUE);
- ji.dinfo.out_color_space = JCS_EXT_BGRA; // Data stored as RGB on disk but Plasma uses BGR
- jpeg_start_decompress(&ji.dinfo);
+ jpeg_hsStream_src(&dinfo, S);
+ jpeg_read_header(&dinfo, TRUE);
+ dinfo->out_color_space = JCS_EXT_BGRA; // Data stored as RGB on disk but Plasma uses BGR
+ jpeg_start_decompress(&dinfo);
- int row_stride = ji.dinfo.output_width * ji.dinfo.out_color_components;
+ int row_stride = dinfo->output_width * dinfo->out_color_components;
int out_stride = row_stride;
RAII_JSAMPROW<1> jbuffer(row_stride);
// Start with a reasonable size for the buffer
- auto buffer_size = ji.dinfo.output_width * ji.dinfo.output_height * ji.dinfo.out_color_components;
+ auto buffer_size = dinfo->output_width * dinfo->output_height * dinfo->out_color_components;
auto buffer = std::make_unique(buffer_size);
size_t offs = 0;
- while (ji.dinfo.output_scanline < ji.dinfo.output_height) {
- jpeg_read_scanlines(&ji.dinfo, jbuffer.data, 1);
- for (size_t x = 0; x < ji.dinfo.output_width; x++) {
+ while (dinfo->output_scanline < dinfo->output_height) {
+ jpeg_read_scanlines(&dinfo, jbuffer.data, 1);
+ for (size_t x = 0; x < dinfo->output_width; x++) {
memcpy(buffer.get() + offs + (x * 4),
- jbuffer.data[0] + (x * ji.dinfo.out_color_components),
- ji.dinfo.out_color_components);
+ jbuffer.data[0] + (x * dinfo->out_color_components),
+ dinfo->out_color_components);
}
offs += out_stride;
}
- jpeg_finish_decompress(&ji.dinfo);
+ jpeg_finish_decompress(&dinfo);
- plMipmap* newMipmap = new plMipmap(ji.dinfo.output_width, ji.dinfo.output_height, 1, plMipmap::kUncompressed, plMipmap::kRGB8888);
+ plMipmap* newMipmap = new plMipmap(dinfo->output_width, dinfo->output_height, 1, plMipmap::kUncompressed, plMipmap::kRGB8888);
newMipmap->setImageData(buffer.get(), buffer_size);
return newMipmap;
@@ -307,42 +332,40 @@ plMipmap* plJPEG::DecompressJPEG(hsStream* S)
void plJPEG::CompressJPEG(hsStream* S, void* buf, size_t size, uint32_t width, uint32_t height, uint32_t bpp)
{
- plJPEG& ji = Instance();
+ plJPEG_CInfo cinfo;
JSAMPLE* image_buffer = reinterpret_cast(buf);
- jpeg_create_compress(&ji.cinfo);
- jpeg_hsStream_dest(&ji.cinfo, S);
+ jpeg_hsStream_dest(&cinfo, S);
- ji.cinfo.image_width = width;
- ji.cinfo.image_height = height;
- ji.cinfo.input_components = bpp / 8;
- if (ji.cinfo.input_components == 4)
- ji.cinfo.in_color_space = JCS_EXT_RGBX;
+ cinfo->image_width = width;
+ cinfo->image_height = height;
+ cinfo->input_components = bpp / 8;
+ if (cinfo->input_components == 4)
+ cinfo->in_color_space = JCS_EXT_RGBX;
else
- ji.cinfo.in_color_space = JCS_RGB;
+ cinfo->in_color_space = JCS_RGB;
+
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, 100, TRUE);
+ jpeg_start_compress(&cinfo, TRUE);
- jpeg_set_defaults(&ji.cinfo);
- jpeg_set_quality(&ji.cinfo, 100, TRUE);
- jpeg_start_compress(&ji.cinfo, TRUE);
-
- uint32_t row_stride = ji.cinfo.image_width * ji.cinfo.input_components;
+ uint32_t row_stride = cinfo->image_width * cinfo->input_components;
RAII_JSAMPROW<1> jbuffer(row_stride);
size_t offs = 0;
- while (ji.cinfo.next_scanline < ji.cinfo.image_height) {
+ while (cinfo->next_scanline < cinfo->image_height) {
if (offs + row_stride > size)
throw hsJPEGException(__FILE__, __LINE__, "buffer overread");
- for (size_t x = 0; x < ji.cinfo.image_width; x++) {
- memcpy(jbuffer.data[0] + (x * ji.cinfo.input_components),
- ((unsigned char*)image_buffer) + offs + (x * ji.cinfo.input_components),
- ji.cinfo.input_components);
+ for (size_t x = 0; x < cinfo->image_width; x++) {
+ memcpy(jbuffer.data[0] + (x * cinfo->input_components),
+ ((unsigned char*)image_buffer) + offs + (x * cinfo->input_components),
+ cinfo->input_components);
}
- (void)jpeg_write_scanlines(&ji.cinfo, jbuffer.data, 1);
+ (void)jpeg_write_scanlines(&cinfo, jbuffer.data, 1);
offs += row_stride;
}
- jpeg_finish_compress(&ji.cinfo);
- jpeg_destroy_compress(&ji.cinfo);
+ jpeg_finish_compress(&cinfo);
}
diff --git a/core/Util/plJPEG.h b/core/Util/plJPEG.h
index 06966579f..a80c6fd86 100644
--- a/core/Util/plJPEG.h
+++ b/core/Util/plJPEG.h
@@ -20,10 +20,6 @@
#include "PlasmaDefs.h"
#include "Debug/hsExceptions.hpp"
-extern "C" {
-#include
-}
-
class hsJPEGException : public hsException
{
public:
@@ -42,11 +38,6 @@ class plMipmap;
class HSPLASMA_EXPORT plJPEG
{
-private:
- jpeg_compress_struct cinfo;
- jpeg_decompress_struct dinfo;
- jpeg_error_mgr jerr;
-
public:
/* Read JPEG file from stream into buffer as bitmap data. */
static void DecompressJPEG(hsStream* S, void* buf, size_t size);
@@ -59,10 +50,7 @@ class HSPLASMA_EXPORT plJPEG
uint32_t width, uint32_t height, uint32_t bpp);
private:
- plJPEG();
- ~plJPEG();
-
- static plJPEG& Instance();
+ plJPEG() = delete;
};
#endif
diff --git a/core/Util/plPNG.cpp b/core/Util/plPNG.cpp
index ee1c8b3ce..7cb076d7b 100644
--- a/core/Util/plPNG.cpp
+++ b/core/Util/plPNG.cpp
@@ -20,6 +20,8 @@
#include
+#include
+
/* Don't know why this isn't provided by libpng itself... */
#define PNG_SIG_LENGTH (8)
diff --git a/core/Util/plPNG.h b/core/Util/plPNG.h
index 7daae6372..6ce40b8c2 100644
--- a/core/Util/plPNG.h
+++ b/core/Util/plPNG.h
@@ -20,8 +20,6 @@
#include "PlasmaDefs.h"
#include "Debug/hsExceptions.hpp"
-#include
-
class hsPNGException : public hsException
{
public: