diff --git a/docs/reST/c_api/rwobject.rst b/docs/reST/c_api/rwobject.rst index d8047dffdb..f1f5a9faf2 100644 --- a/docs/reST/c_api/rwobject.rst +++ b/docs/reST/c_api/rwobject.rst @@ -31,11 +31,17 @@ Header file: src_c/include/pygame.h If threads are available, the Python GIL is acquired before calling any of the *obj* methods. On error raise a Python exception and return ``NULL``. -.. c:function:: int pgRWops_CheckObject(SDL_RWops *rw) +.. c:function:: int pgRWops_IsFileObject(SDL_RWops *rw) Return true if *rw* is a Python file-like object wrapper returned by :c:func:`pgRWops_FromObject` or :c:func:`pgRWops_FromFileObject`. +.. c:function:: char* pgRWops_GetFileExtension(SDL_RWops *rw) + + Return a string that contains the file extension of the original file + loaded into the SDL_RWops object, or NULL if the SDL_RWops object comes + from a file object. + .. c:function:: int pgRWops_ReleaseObject(SDL_RWops *context) Free a SDL_RWops struct. If it is attached to a Python file-like object, decrement its diff --git a/src_c/_pygame.h b/src_c/_pygame.h index aad4f760d2..013c4a39ec 100644 --- a/src_c/_pygame.h +++ b/src_c/_pygame.h @@ -344,7 +344,7 @@ struct pgColorObject { #define PYGAMEAPI_DISPLAY_NUMSLOTS 2 #define PYGAMEAPI_SURFACE_NUMSLOTS 4 #define PYGAMEAPI_SURFLOCK_NUMSLOTS 8 -#define PYGAMEAPI_RWOBJECT_NUMSLOTS 6 +#define PYGAMEAPI_RWOBJECT_NUMSLOTS 7 #define PYGAMEAPI_PIXELARRAY_NUMSLOTS 2 #define PYGAMEAPI_COLOR_NUMSLOTS 5 #define PYGAMEAPI_MATH_NUMSLOTS 2 diff --git a/src_c/image.c b/src_c/image.c index 933783768e..665f5a2636 100644 --- a/src_c/image.c +++ b/src_c/image.c @@ -79,29 +79,14 @@ image_load_basic(PyObject *self, PyObject *obj) PyObject *final; PyObject *oencoded; SDL_Surface *surf; - SDL_RWops *rw; - oencoded = pg_EncodeString(obj, "UTF-8", NULL, pgExc_SDLError); - if (oencoded == NULL) { + SDL_RWops *rw = pgRWops_FromObject(obj); + if (rw == NULL) { return NULL; } - - if (oencoded != Py_None) { - Py_BEGIN_ALLOW_THREADS; - surf = SDL_LoadBMP(Bytes_AS_STRING(oencoded)); - Py_END_ALLOW_THREADS; - Py_DECREF(oencoded); - } - else { - Py_DECREF(oencoded); - rw = pgRWops_FromFileObject(obj); - if (rw == NULL) { - return NULL; - } - Py_BEGIN_ALLOW_THREADS; - surf = SDL_LoadBMP_RW(rw, 1); - Py_END_ALLOW_THREADS; - } + Py_BEGIN_ALLOW_THREADS; + surf = SDL_LoadBMP_RW(rw, 1); + Py_END_ALLOW_THREADS; if (surf == NULL) { return RAISE(pgExc_SDLError, SDL_GetError()); diff --git a/src_c/imageext.c b/src_c/imageext.c index b588e7bdaa..35a88aba40 100644 --- a/src_c/imageext.c +++ b/src_c/imageext.c @@ -108,127 +108,44 @@ image_load_ext(PyObject *self, PyObject *arg) { PyObject *obj; PyObject *final; - PyObject *oencoded; - PyObject *oname; - size_t namelen; const char *name = NULL; - const char *cext; char *ext = NULL; SDL_Surface *surf; - SDL_RWops *rw; + SDL_RWops *rw = NULL; + int lock_mutex = 0; if (!PyArg_ParseTuple(arg, "O|s", &obj, &name)) { return NULL; } - oencoded = pg_EncodeString(obj, "UTF-8", NULL, pgExc_SDLError); - if (oencoded == NULL) { + rw = pgRWops_FromObject(obj); + if (rw == NULL) /* stop on NULL, error already set */ return NULL; - } - if (oencoded != Py_None) { - name = Bytes_AS_STRING(oencoded); + ext = pgRWops_GetFileExtension(rw); + if (name) /* override extension with namehint if given */ + ext = find_extension(name); + #ifdef WITH_THREAD - namelen = Bytes_GET_SIZE(oencoded); - Py_BEGIN_ALLOW_THREADS; - if (namelen > 4 && !strcasecmp(name + namelen - 4, ".gif")) { - /* using multiple threads does not work for (at least) SDL_image <= 2.0.4 */ - SDL_LockMutex(_pg_img_mutex); - surf = IMG_Load(name); - SDL_UnlockMutex(_pg_img_mutex); - } - else { - surf = IMG_Load(name); - } - Py_END_ALLOW_THREADS; -#else /* ~WITH_THREAD */ - surf = IMG_Load(name); -#endif /* WITH_THREAD */ - Py_DECREF(oencoded); + if (ext) + lock_mutex = !strcasecmp(ext, "gif"); + Py_BEGIN_ALLOW_THREADS; + if (0) { + /* using multiple threads does not work for (at least) SDL_image <= 2.0.4 */ + SDL_LockMutex(_pg_img_mutex); + surf = IMG_LoadTyped_RW(rw, 1, ext); + SDL_UnlockMutex(_pg_img_mutex); } else { -#ifdef WITH_THREAD - int lock_mutex = 0; -#endif /* WITH_THREAD */ - Py_DECREF(oencoded); - oencoded = NULL; -#if PY2 - if (name == NULL && PyFile_Check(obj)) { - oencoded = PyFile_Name(obj); - if (oencoded == NULL) { - /* This should never happen */ - return NULL; - } - Py_INCREF(oencoded); - name = Bytes_AS_STRING(oencoded); - } -#endif - if (name == NULL) { - oname = PyObject_GetAttrString(obj, "name"); - if (oname != NULL) { - oencoded = pg_EncodeString(oname, "UTF-8", NULL, NULL); - Py_DECREF(oname); - if (oencoded == NULL) { - return NULL; - } - if (oencoded != Py_None) { - name = Bytes_AS_STRING(oencoded); - } - } - else { - PyErr_Clear(); - } - } - rw = pgRWops_FromFileObject(obj); - if (rw == NULL) { - Py_XDECREF(oencoded); - return NULL; - } - - cext = find_extension(name); - if (cext != NULL) { - ext = (char *)PyMem_Malloc(strlen(cext) + 1); - if (ext == NULL) { - Py_XDECREF(oencoded); - return PyErr_NoMemory(); - } - strcpy(ext, cext); -#ifdef WITH_THREAD - lock_mutex = !strcasecmp(ext, "gif"); -#endif /* WITH_THREAD */ - } - Py_XDECREF(oencoded); -#ifdef WITH_THREAD - Py_BEGIN_ALLOW_THREADS; - if (lock_mutex) { - /* using multiple threads does not work for (at least) SDL_image <= 2.0.4 */ - SDL_LockMutex(_pg_img_mutex); - surf = IMG_LoadTyped_RW(rw, 1, ext); - SDL_UnlockMutex(_pg_img_mutex); - } - else { - surf = IMG_LoadTyped_RW(rw, 1, ext); - } - Py_END_ALLOW_THREADS; -#else /* ~WITH_THREAD */ surf = IMG_LoadTyped_RW(rw, 1, ext); -#endif /* ~WITH_THREAD */ - PyMem_Free(ext); } + Py_END_ALLOW_THREADS; +#else /* ~WITH_THREAD */ + surf = IMG_LoadTyped_RW(rw, 1, ext); +#endif /* ~WITH_THREAD */ + + if (surf == NULL) + return RAISE(pgExc_SDLError, IMG_GetError()); - if (surf == NULL){ - if (!strncmp(IMG_GetError(), "Couldn't open", 12)){ - SDL_ClearError(); -#if PY3 - PyErr_SetString(PyExc_FileNotFoundError, "No such file or directory."); -#else - PyErr_SetString(PyExc_IOError, "No such file or directory."); -#endif - return NULL; - } - else{ - return RAISE(pgExc_SDLError, IMG_GetError()); - } - } final = (PyObject *)pgSurface_New(surf); if (final == NULL) { SDL_FreeSurface(surf); diff --git a/src_c/include/_pygame.h b/src_c/include/_pygame.h index 20ce93a5b2..8317cab6a2 100644 --- a/src_c/include/_pygame.h +++ b/src_c/include/_pygame.h @@ -538,6 +538,10 @@ typedef struct pgEventObject pgEventObject; (*(int (*)(SDL_RWops *)) \ PYGAMEAPI_GET_SLOT(rwobject, 5)) +#define pgRWops_GetFileExtension \ + (*(char * (*)(SDL_RWops *)) \ + PYGAMEAPI_GET_SLOT(rwobject, 6)) + #define import_pygame_rwobject() IMPORT_PYGAME_MODULE(rwobject) #endif diff --git a/src_c/music.c b/src_c/music.c index ba00e32db8..69dea0ac54 100644 --- a/src_c/music.c +++ b/src_c/music.c @@ -311,6 +311,9 @@ _get_type_from_hint(char *namehint) namehint = dot + 1; } } + else { + return type; + } /* Copied almost directly from SDL_mixer. Originally meant to check file extensions * to get a hint of what music type it should be. @@ -368,45 +371,33 @@ _get_type_from_hint(char *namehint) Mix_Music * _load_music(PyObject *obj, char *namehint) { - PyObject *oencoded; Mix_Music *new_music = NULL; - const char *name; - - oencoded = pg_EncodeString(obj, "UTF-8", NULL, pgExc_SDLError); - if (oencoded == Py_None) { - SDL_RWops *rw; - - Py_DECREF(oencoded); - if (!PG_CHECK_THREADS()) - return NULL; - rw = pgRWops_FromFileObject(obj); - if (rw == NULL) { - return NULL; - } - Py_BEGIN_ALLOW_THREADS -#if IS_SDLv1 - new_music = Mix_LoadMUS_RW(rw); -#else /* IS_SDLv2 */ - if (namehint) - new_music = Mix_LoadMUSType_RW(rw, _get_type_from_hint(namehint), SDL_TRUE); - else - new_music = Mix_LoadMUS_RW(rw, SDL_TRUE); -#endif /* IS_SDLv2 */ - Py_END_ALLOW_THREADS - } - else if (oencoded != NULL) { - name = Bytes_AS_STRING(oencoded); + char* ext = NULL; + SDL_RWops *rw = NULL; + PyObject* _type = NULL; + PyObject* error = NULL; + PyObject* _traceback = NULL; - Py_BEGIN_ALLOW_THREADS - new_music = Mix_LoadMUS(name); - Py_END_ALLOW_THREADS + MIXER_INIT_CHECK(); - Py_DECREF(oencoded); - } - else { + rw = pgRWops_FromObject(obj); + if (rw == NULL) { /* stop on NULL, error already set is what we SHOULD do */ + PyErr_Fetch(&_type, &error, &_traceback); + PyErr_SetObject(pgExc_SDLError, error); + Py_XDECREF(_type); + Py_XDECREF(_traceback); return NULL; + } + if (namehint) { + ext = namehint; + } else { + ext = pgRWops_GetFileExtension(rw); } + Py_BEGIN_ALLOW_THREADS + new_music = Mix_LoadMUSType_RW(rw, _get_type_from_hint(ext), SDL_TRUE); + Py_END_ALLOW_THREADS + if (!new_music) { PyErr_SetString(pgExc_SDLError, SDL_GetError()); return NULL; @@ -432,7 +423,9 @@ music_load(PyObject *self, PyObject *args, PyObject *keywds) if (new_music == NULL) // meaning it has an error to return return NULL; - Py_BEGIN_ALLOW_THREADS if (current_music != NULL) { + Py_BEGIN_ALLOW_THREADS + if (current_music != NULL) + { Mix_FreeMusic(current_music); current_music = NULL; } @@ -444,7 +437,6 @@ music_load(PyObject *self, PyObject *args, PyObject *keywds) Py_END_ALLOW_THREADS current_music = new_music; - Py_RETURN_NONE; } diff --git a/src_c/rwobject.c b/src_c/rwobject.c index 6d7d179f41..2cd87171f4 100644 --- a/src_c/rwobject.c +++ b/src_c/rwobject.c @@ -314,6 +314,17 @@ pgRWops_IsFileObject(SDL_RWops *rw) return rw->close == _pg_rw_close; } +char* +pgRWops_GetFileExtension(SDL_RWops* rw) +{ + if (pgRWops_IsFileObject(rw)) { + return NULL; + } + else { + return rw->hidden.unknown.data1; + } +} + #if IS_SDLv2 static Sint64 _pg_rw_size(SDL_RWops *context) @@ -509,6 +520,9 @@ pgRWops_FromFileObject(PyObject *obj) helper->file = obj; Py_INCREF(obj); + /* Adding a helper to the hidden data to support file-like object RWops + * RWops from actual files use this space to store the file extension + * for later use */ rw->hidden.unknown.data1 = (void *)helper; #if IS_SDLv2 rw->size = _pg_rw_size; @@ -567,6 +581,7 @@ pgRWops_ReleaseObject(SDL_RWops *context) #endif /* WITH_THREAD */ } else { + free(context->hidden.unknown.data1); ret = SDL_RWclose(context); if (ret < 0) PyErr_SetString(PyExc_IOError, SDL_GetError()); @@ -722,22 +737,41 @@ _rwops_from_pystr(PyObject *obj) if (obj != NULL) { SDL_RWops *rw = NULL; PyObject *oencoded; + char* encoded = NULL; + char* ext = NULL; + char* extension = NULL; + oencoded = pg_EncodeString(obj, "UTF-8", NULL, NULL); if (oencoded == NULL) { return NULL; } if (oencoded != Py_None) { - rw = SDL_RWFromFile(Bytes_AS_STRING(oencoded), "rb"); + encoded = Bytes_AS_STRING(oencoded); + rw = SDL_RWFromFile(encoded, "rb"); + ext = strrchr(encoded, '.'); + if (ext && strlen(ext) > 1) { + ext++; + extension = malloc(strlen(ext)+1); + if (extension == NULL) { + return (SDL_RWops*)PyErr_NoMemory(); + } + strcpy(extension, ext); + } } Py_DECREF(oencoded); if (rw) { + /* adding the extension to the hidden data for RWops from files */ + /* this is necessary to support loading functions that rely on + * file extensions in a convenient way. File-like objects use this + * field for a helper object. */ + rw->hidden.unknown.data1 = (void *)extension; return rw; } else { #if PY3 if (PyUnicode_Check(obj)) { - SDL_ClearError(); + SDL_ClearError(); PyErr_SetString(PyExc_FileNotFoundError, - "No such file or directory."); + "No such file or directory."); #else if (PyUnicode_Check(obj) || PyString_Check(obj)) { SDL_ClearError(); @@ -850,6 +884,7 @@ MODINIT_DEFINE(rwobject) c_api[3] = pg_EncodeString; c_api[4] = pgRWops_FromFileObject; c_api[5] = pgRWops_ReleaseObject; + c_api[6] = pgRWops_GetFileExtension; apiobj = encapsulate_api(c_api, "rwobject"); if (apiobj == NULL) { DECREF_MOD(module); diff --git a/test/mixer_music_test.py b/test/mixer_music_test.py index 1c870de897..9254e945e6 100644 --- a/test/mixer_music_test.py +++ b/test/mixer_music_test.py @@ -26,9 +26,6 @@ def setUp(cls): if pygame.mixer.get_init() is None: pygame.mixer.init() - @unittest.skipIf( - "Darwin" in platform.system(), "SDL2_mixer not loading mp3 on travisci" - ) def test_load_mp3(self): "|tags:music|" self.music_load("mp3")