2
0

pythonTexturePoolFilter.cxx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /**
  2. * PANDA 3D SOFTWARE
  3. * Copyright (c) Carnegie Mellon University. All rights reserved.
  4. *
  5. * All use of this software is subject to the terms of the revised BSD
  6. * license. You should have received a copy of this license along
  7. * with this source code in a file named "LICENSE."
  8. *
  9. * @file pythonTexturePoolFilter.cxx
  10. * @author Derzsi Daniel
  11. * @date 2020-06-14
  12. */
  13. #include "pythonTexturePoolFilter.h"
  14. #ifdef HAVE_PYTHON
  15. #include "pythonThread.h"
  16. #include "py_panda.h"
  17. extern struct Dtool_PyTypedObject Dtool_Filename;
  18. extern struct Dtool_PyTypedObject Dtool_LoaderOptions;
  19. extern struct Dtool_PyTypedObject Dtool_Texture;
  20. TypeHandle PythonTexturePoolFilter::_type_handle;
  21. /**
  22. * Constructor.
  23. */
  24. PythonTexturePoolFilter::
  25. PythonTexturePoolFilter() {
  26. init_type();
  27. }
  28. /**
  29. * Destructor.
  30. */
  31. PythonTexturePoolFilter::
  32. ~PythonTexturePoolFilter() {
  33. if (_pre_load_func != nullptr || _post_load_func != nullptr) {
  34. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  35. PyGILState_STATE gstate;
  36. gstate = PyGILState_Ensure();
  37. #endif
  38. Py_CLEAR(_entry_point);
  39. Py_CLEAR(_pre_load_func);
  40. Py_CLEAR(_post_load_func);
  41. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  42. PyGILState_Release(gstate);
  43. #endif
  44. }
  45. }
  46. /**
  47. * Initializes the filter to use the given Python filter object.
  48. *
  49. * This method expects a Python object that implements either
  50. * the pre_load or the post_load method.
  51. */
  52. bool PythonTexturePoolFilter::
  53. init(PyObject *tex_filter) {
  54. nassertr(tex_filter != nullptr, false);
  55. nassertr(_entry_point == nullptr, false);
  56. nassertr(_pre_load_func == nullptr, false);
  57. nassertr(_post_load_func == nullptr, false);
  58. _pre_load_func = PyObject_GetAttrString(tex_filter, "pre_load");
  59. PyErr_Clear();
  60. _post_load_func = PyObject_GetAttrString(tex_filter, "post_load");
  61. PyErr_Clear();
  62. if (_pre_load_func == nullptr && _post_load_func == nullptr) {
  63. PyErr_Format(PyExc_TypeError,
  64. "texture pool filter plug-in %R does not define pre_load or post_load function",
  65. tex_filter);
  66. return false;
  67. }
  68. _entry_point = Py_NewRef(tex_filter);
  69. return true;
  70. }
  71. /**
  72. * This method is called before each texture is loaded from disk, via the
  73. * TexturePool, for the first time. We delegate this functionality to
  74. * our Python module, loaded through the init function.
  75. */
  76. PT(Texture) PythonTexturePoolFilter::
  77. pre_load(const Filename &orig_filename, const Filename &orig_alpha_filename,
  78. int primary_file_num_channels, int alpha_file_channel,
  79. bool read_mipmaps, const LoaderOptions &options) {
  80. if (_pre_load_func == nullptr) {
  81. return nullptr;
  82. }
  83. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  84. PyGILState_STATE gstate;
  85. gstate = PyGILState_Ensure();
  86. #endif
  87. // Wrap the arguments.
  88. PyObject *args[7];
  89. args[1] = DTool_CreatePyInstance((void *)&orig_filename, Dtool_Filename, false, true);
  90. args[2] = DTool_CreatePyInstance((void *)&orig_alpha_filename, Dtool_Filename, false, true);
  91. args[3] = PyLong_FromLong(primary_file_num_channels);
  92. args[4] = PyLong_FromLong(alpha_file_channel);
  93. args[5] = PyBool_FromLong(read_mipmaps);
  94. args[6] = DTool_CreatePyInstance((void *)&options, Dtool_LoaderOptions, false, true);
  95. PyObject *result = PythonThread::call_python_func(_pre_load_func, args + 1, 6 | PY_VECTORCALL_ARGUMENTS_OFFSET);
  96. for (size_t i = 1; i < 7; ++i) {
  97. Py_DECREF(args[i]);
  98. }
  99. PT(Texture) tex;
  100. if (result != nullptr) {
  101. if (result != Py_None) {
  102. if (DtoolInstance_Check(result)) {
  103. tex = (Texture *)DtoolInstance_UPCAST(result, Dtool_Texture);
  104. }
  105. if (tex == nullptr) {
  106. gobj_cat.error()
  107. << "Preloading " << orig_filename.get_basename() << " failed as "
  108. << "preloaded texture is not of Texture type.\n";
  109. }
  110. }
  111. Py_DECREF(result);
  112. } else {
  113. PyObject *exc_type = PyErr_Occurred();
  114. nassertr(exc_type != nullptr, nullptr);
  115. gobj_cat.error()
  116. << "Preloading " << orig_filename.get_basename() << " failed with "
  117. << ((PyTypeObject *)exc_type)->tp_name << " exception.\n";
  118. PyErr_Clear();
  119. }
  120. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  121. PyGILState_Release(gstate);
  122. #endif
  123. return tex;
  124. }
  125. /**
  126. * This method is called after each texture has been loaded from disk, via the
  127. * TexturePool, for the first time. We delegate this functionality to
  128. * our Python module, loaded through the init function.
  129. */
  130. PT(Texture) PythonTexturePoolFilter::
  131. post_load(Texture *tex) {
  132. if (_post_load_func == nullptr) {
  133. return tex;
  134. }
  135. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  136. PyGILState_STATE gstate;
  137. gstate = PyGILState_Ensure();
  138. #endif
  139. // Wrap the arguments.
  140. PyObject *args[2];
  141. args[1] = DTool_CreatePyInstance(tex, Dtool_Texture, true, false);
  142. PyObject *result = PythonThread::call_python_func(_post_load_func, args + 1, 1 | PY_VECTORCALL_ARGUMENTS_OFFSET);
  143. Py_DECREF(args[1]);
  144. PT(Texture) result_tex;
  145. if (result != nullptr) {
  146. if (result != Py_None) {
  147. // Check if the call returned a texture pointer.
  148. if (DtoolInstance_Check(result)) {
  149. result_tex = (Texture *)DtoolInstance_UPCAST(result, Dtool_Texture);
  150. }
  151. if (result_tex == nullptr) {
  152. // No valid texture was returned, use the original pointer.
  153. gobj_cat.error()
  154. << "Postloading failed as the returned texture is not of the Texture type.\n";
  155. result_tex = tex;
  156. }
  157. }
  158. } else {
  159. PyObject *exc_type = PyErr_Occurred();
  160. nassertr(exc_type != nullptr, result_tex);
  161. gobj_cat.error()
  162. << "Postloading texture failed with "
  163. << ((PyTypeObject *)exc_type)->tp_name << " exception.\n";
  164. PyErr_Clear();
  165. result_tex = tex;
  166. }
  167. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  168. PyGILState_Release(gstate);
  169. #endif
  170. return result_tex;
  171. }
  172. #endif // HAVE_PYTHON