summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt5
-rw-r--r--Depends.cmake1
-rw-r--r--README.md1
-rw-r--r--res/about/version.gmi4
-rw-r--r--src/gmdocument.c5
-rw-r--r--src/gmutil.c54
-rw-r--r--src/gmutil.h1
-rw-r--r--src/media.c16
-rw-r--r--src/ui/documentwidget.c30
9 files changed, 85 insertions, 32 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 73e2b8d4..262bbcb9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -48,6 +48,7 @@ option (ENABLE_MPG123 "Use mpg123 for decoding MPEG audio" ON)
48option (ENABLE_RELATIVE_EMBED "Resources should always be found via relative path" OFF) 48option (ENABLE_RELATIVE_EMBED "Resources should always be found via relative path" OFF)
49option (ENABLE_RESIZE_DRAW "Force window to redraw during resizing" ${DEFAULT_RESIZE_DRAW}) 49option (ENABLE_RESIZE_DRAW "Force window to redraw during resizing" ${DEFAULT_RESIZE_DRAW})
50option (ENABLE_RESOURCE_EMBED "Embed resources inside the executable" OFF) 50option (ENABLE_RESOURCE_EMBED "Embed resources inside the executable" OFF)
51option (ENABLE_WEBP "Use libwebp to decode .webp images (via pkg-config)" ON)
51option (ENABLE_WINDOWPOS_FIX "Set position after showing window (workaround for SDL bug)" OFF) 52option (ENABLE_WINDOWPOS_FIX "Set position after showing window (workaround for SDL bug)" OFF)
52option (ENABLE_X11_SWRENDER "Use software rendering under X11" OFF) 53option (ENABLE_X11_SWRENDER "Use software rendering under X11" OFF)
53 54
@@ -315,6 +316,10 @@ if (ENABLE_MPG123 AND MPG123_FOUND)
315 target_compile_definitions (app PUBLIC LAGRANGE_ENABLE_MPG123=1) 316 target_compile_definitions (app PUBLIC LAGRANGE_ENABLE_MPG123=1)
316 target_link_libraries (app PUBLIC PkgConfig::MPG123) 317 target_link_libraries (app PUBLIC PkgConfig::MPG123)
317endif () 318endif ()
319if (ENABLE_WEBP AND WEBP_FOUND)
320 target_compile_definitions (app PUBLIC LAGRANGE_ENABLE_WEBP=1)
321 target_link_libraries (app PUBLIC PkgConfig::WEBP)
322endif ()
318if (ENABLE_IDLE_SLEEP) 323if (ENABLE_IDLE_SLEEP)
319 target_compile_definitions (app PUBLIC LAGRANGE_ENABLE_IDLE_SLEEP=1) 324 target_compile_definitions (app PUBLIC LAGRANGE_ENABLE_IDLE_SLEEP=1)
320endif () 325endif ()
diff --git a/Depends.cmake b/Depends.cmake
index ef95c9f4..86a1b47d 100644
--- a/Depends.cmake
+++ b/Depends.cmake
@@ -149,3 +149,4 @@ endif ()
149find_package (PkgConfig REQUIRED) 149find_package (PkgConfig REQUIRED)
150pkg_check_modules (SDL2 REQUIRED sdl2) 150pkg_check_modules (SDL2 REQUIRED sdl2)
151pkg_check_modules (MPG123 IMPORTED_TARGET libmpg123) 151pkg_check_modules (MPG123 IMPORTED_TARGET libmpg123)
152pkg_check_modules (WEBP IMPORTED_TARGET libwebp)
diff --git a/README.md b/README.md
index 3d9df1db..13b3f9d2 100644
--- a/README.md
+++ b/README.md
@@ -88,6 +88,7 @@ Note that the `install` target also deploys an XDG .desktop file for launching t
88| `ENABLE_MPG123` | Use the mpg123 library for decoding MPEG audio files. | 88| `ENABLE_MPG123` | Use the mpg123 library for decoding MPEG audio files. |
89| `ENABLE_RELATIVE_EMBED` | Locate resources only in relation to the executable. Useful when any system/predefined directories are not supposed to be accessed, e.g., in the Windows portable build. | 89| `ENABLE_RELATIVE_EMBED` | Locate resources only in relation to the executable. Useful when any system/predefined directories are not supposed to be accessed, e.g., in the Windows portable build. |
90| `ENABLE_RESOURCE_EMBED` | Embed all resource files into the Lagrange executable instead of keeping them in a separate file that gets loaded at launch. Setting this **ON** makes it much slower to run CMake and to compile Lagrange. | 90| `ENABLE_RESOURCE_EMBED` | Embed all resource files into the Lagrange executable instead of keeping them in a separate file that gets loaded at launch. Setting this **ON** makes it much slower to run CMake and to compile Lagrange. |
91| `ENABLE_WEBP` | Use libwebp to decode .webp images, if `pkg-config` can find the library. |
91| `ENABLE_WINDOWPOS_FIX` | Set correct window position after the window has already been shown. This may be necessary on some platforms to prevent the window from being restored to the wrong position. | 92| `ENABLE_WINDOWPOS_FIX` | Set correct window position after the window has already been shown. This may be necessary on some platforms to prevent the window from being restored to the wrong position. |
92| `ENABLE_X11_SWRENDER` | Default to software rendering when running under X11. By default Lagrange attempts to use the GPU for rendering the user interface. You can also use the `--sw` option at launch to force software rendering. | 93| `ENABLE_X11_SWRENDER` | Default to software rendering when running under X11. By default Lagrange attempts to use the GPU for rendering the user interface. You can also use the `--sw` option at launch to force software rendering. |
93 94
diff --git a/res/about/version.gmi b/res/about/version.gmi
index 89e81047..5141a4b1 100644
--- a/res/about/version.gmi
+++ b/res/about/version.gmi
@@ -6,6 +6,10 @@
6``` 6```
7# Release notes 7# Release notes
8 8
9## 1.7
10* Added support for viewing WebP images. The libwebp library is an optional dependency and will be included in the build if found via pkg-config.
11* Added a footer action to view `application/octet-stream` content depending on recognized file extensions.
12
9## 1.6.4 13## 1.6.4
10* Local files containing UTF-8 text can be viewed regardless of their file extension. 14* Local files containing UTF-8 text can be viewed regardless of their file extension.
11* Fixed input field cursor positioning and insertion problems around Emoji variation selectors. 15* Fixed input field cursor positioning and insertion problems around Emoji variation selectors.
diff --git a/src/gmdocument.c b/src/gmdocument.c
index 6ed628de..75f6f06b 100644
--- a/src/gmdocument.c
+++ b/src/gmdocument.c
@@ -240,7 +240,10 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li
240 iString *path = newRange_String(parts.path); 240 iString *path = newRange_String(parts.path);
241 if (endsWithCase_String(path, ".gif") || endsWithCase_String(path, ".jpg") || 241 if (endsWithCase_String(path, ".gif") || endsWithCase_String(path, ".jpg") ||
242 endsWithCase_String(path, ".jpeg") || endsWithCase_String(path, ".png") || 242 endsWithCase_String(path, ".jpeg") || endsWithCase_String(path, ".png") ||
243 endsWithCase_String(path, ".tga") || endsWithCase_String(path, ".psd") || 243 endsWithCase_String(path, ".tga") || endsWithCase_String(path, ".psd") ||
244#if defined (LAGRANGE_ENABLE_WEBP)
245 endsWithCase_String(path, ".webp") ||
246#endif
244 endsWithCase_String(path, ".hdr") || endsWithCase_String(path, ".pic")) { 247 endsWithCase_String(path, ".hdr") || endsWithCase_String(path, ".pic")) {
245 link->flags |= imageFileExtension_GmLinkFlag; 248 link->flags |= imageFileExtension_GmLinkFlag;
246 } 249 }
diff --git a/src/gmutil.c b/src/gmutil.c
index 9bd74ee0..d547d27d 100644
--- a/src/gmutil.c
+++ b/src/gmutil.c
@@ -511,54 +511,64 @@ const iString *findContainerArchive_Path(const iString *path) {
511 return NULL; 511 return NULL;
512} 512}
513 513
514const char *mediaType_Path(const iString *path) { 514const char *mediaTypeFromFileExtension_String(const iString *d) {
515 if (endsWithCase_String(path, ".gmi") || endsWithCase_String(path, ".gemini")) { 515 if (endsWithCase_String(d, ".gmi") || endsWithCase_String(d, ".gemini")) {
516 return "text/gemini; charset=utf-8"; 516 return "text/gemini; charset=utf-8";
517 } 517 }
518 else if (endsWithCase_String(path, ".pem")) { 518 else if (endsWithCase_String(d, ".pem")) {
519 return "application/x-pem-file"; 519 return "application/x-pem-file";
520 } 520 }
521 else if (endsWithCase_String(path, ".zip")) { 521 else if (endsWithCase_String(d, ".zip")) {
522 return "application/zip"; 522 return "application/zip";
523 } 523 }
524 else if (endsWithCase_String(path, ".gpub")) { 524 else if (endsWithCase_String(d, ".gpub")) {
525 return "application/gpub+zip"; 525 return "application/gpub+zip";
526 } 526 }
527 else if (endsWithCase_String(path, ".xml")) { 527 else if (endsWithCase_String(d, ".xml")) {
528 return "text/xml"; 528 return "text/xml";
529 } 529 }
530 else if (endsWithCase_String(path, ".png")) { 530 else if (endsWithCase_String(d, ".png")) {
531 return "image/png"; 531 return "image/png";
532 } 532 }
533 else if (endsWithCase_String(path, ".jpg") || endsWithCase_String(path, ".jpeg")) { 533 else if (endsWithCase_String(d, ".webp")) {
534 return "image/webp";
535 }
536 else if (endsWithCase_String(d, ".jpg") || endsWithCase_String(d, ".jpeg")) {
534 return "image/jpeg"; 537 return "image/jpeg";
535 } 538 }
536 else if (endsWithCase_String(path, ".gif")) { 539 else if (endsWithCase_String(d, ".gif")) {
537 return "image/gif"; 540 return "image/gif";
538 } 541 }
539 else if (endsWithCase_String(path, ".wav")) { 542 else if (endsWithCase_String(d, ".wav")) {
540 return "audio/wave"; 543 return "audio/wave";
541 } 544 }
542 else if (endsWithCase_String(path, ".ogg")) { 545 else if (endsWithCase_String(d, ".ogg")) {
543 return "audio/ogg"; 546 return "audio/ogg";
544 } 547 }
545 else if (endsWithCase_String(path, ".mp3")) { 548 else if (endsWithCase_String(d, ".mp3")) {
546 return "audio/mpeg"; 549 return "audio/mpeg";
547 } 550 }
548 else if (endsWithCase_String(path, ".mid")) { 551 else if (endsWithCase_String(d, ".mid")) {
549 return "audio/midi"; 552 return "audio/midi";
550 } 553 }
551 else if (endsWithCase_String(path, ".txt") || 554 else if (endsWithCase_String(d, ".txt") ||
552 endsWithCase_String(path, ".md") || 555 endsWithCase_String(d, ".md") ||
553 endsWithCase_String(path, ".c") || 556 endsWithCase_String(d, ".c") ||
554 endsWithCase_String(path, ".h") || 557 endsWithCase_String(d, ".h") ||
555 endsWithCase_String(path, ".cc") || 558 endsWithCase_String(d, ".cc") ||
556 endsWithCase_String(path, ".hh") || 559 endsWithCase_String(d, ".hh") ||
557 endsWithCase_String(path, ".cpp") || 560 endsWithCase_String(d, ".cpp") ||
558 endsWithCase_String(path, ".hpp")) { 561 endsWithCase_String(d, ".hpp")) {
559 return "text/plain"; 562 return "text/plain";
560 } 563 }
561 const char *mtype = "application/octet-stream"; 564 return "application/octet-stream";
565}
566
567const char *mediaType_Path(const iString *path) {
568 const char *mtype = mediaTypeFromFileExtension_String(path);
569 if (iCmpStr(mtype, "application/octet-stream")) {
570 return mtype; /* extension recognized */
571 }
562 /* If the file is reasonably small and looks like UTF-8, we'll display it as text/plain. */ 572 /* If the file is reasonably small and looks like UTF-8, we'll display it as text/plain. */
563 if (fileExists_FileInfo(path) && fileSize_FileInfo(path) <= 5000000) { 573 if (fileExists_FileInfo(path) && fileSize_FileInfo(path) <= 5000000) {
564 iFile *f = new_File(path); 574 iFile *f = new_File(path);
diff --git a/src/gmutil.h b/src/gmutil.h
index f8491781..3c10d45b 100644
--- a/src/gmutil.h
+++ b/src/gmutil.h
@@ -133,6 +133,7 @@ const iString * withSpacesEncoded_String(const iString *);
133const iString * canonicalUrl_String (const iString *); 133const iString * canonicalUrl_String (const iString *);
134 134
135const char * mediaType_Path (const iString *path); 135const char * mediaType_Path (const iString *path);
136const char * mediaTypeFromFileExtension_String (const iString *);
136iRangecc mediaTypeWithoutParameters_Rangecc (iRangecc mime); 137iRangecc mediaTypeWithoutParameters_Rangecc (iRangecc mime);
137 138
138const iString * findContainerArchive_Path (const iString *path); 139const iString * findContainerArchive_Path (const iString *path);
diff --git a/src/media.c b/src/media.c
index eb4a8311..5240ab5d 100644
--- a/src/media.c
+++ b/src/media.c
@@ -30,6 +30,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
30#include "stb_image.h" 30#include "stb_image.h"
31#include "stb_image_resize.h" 31#include "stb_image_resize.h"
32 32
33#if defined (LAGRANGE_ENABLE_WEBP)
34# include <webp/decode.h>
35#endif
36
33#include <the_Foundation/file.h> 37#include <the_Foundation/file.h>
34#include <the_Foundation/ptrarray.h> 38#include <the_Foundation/ptrarray.h>
35#include <SDL_hints.h> 39#include <SDL_hints.h>
@@ -86,8 +90,16 @@ void deinit_GmImage(iGmImage *d) {
86void makeTexture_GmImage(iGmImage *d) { 90void makeTexture_GmImage(iGmImage *d) {
87 iBlock *data = &d->partialData; 91 iBlock *data = &d->partialData;
88 d->numBytes = size_Block(data); 92 d->numBytes = size_Block(data);
89 uint8_t *imgData = stbi_load_from_memory( 93 uint8_t *imgData = NULL;
90 constData_Block(data), size_Block(data), &d->size.x, &d->size.y, NULL, 4); 94 if (cmp_String(&d->props.mime, "image/webp") == 0) {
95#if defined (LAGRANGE_ENABLE_WEBP)
96 imgData = WebPDecodeRGBA(constData_Block(data), size_Block(data), &d->size.x, &d->size.y);
97#endif
98 }
99 else {
100 imgData = stbi_load_from_memory(
101 constData_Block(data), size_Block(data), &d->size.x, &d->size.y, NULL, 4);
102 }
91 if (!imgData) { 103 if (!imgData) {
92 d->size = zero_I2(); 104 d->size = zero_I2();
93 d->texture = NULL; 105 d->texture = NULL;
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 3f655db5..83f38dee 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -1168,13 +1168,25 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode
1168 iString *key = collectNew_String(); 1168 iString *key = collectNew_String();
1169 toString_Sym(SDLK_s, KMOD_PRIMARY, key); 1169 toString_Sym(SDLK_s, KMOD_PRIMARY, key);
1170 appendFormat_String(src, "\n```\n%s\n```\n", cstr_String(meta)); 1170 appendFormat_String(src, "\n```\n%s\n```\n", cstr_String(meta));
1171 makeFooterButtons_DocumentWidget_( 1171 const char *mtype = mediaTypeFromFileExtension_String(d->mod.url);
1172 d, 1172 iArray items;
1173 (iMenuItem[]){ { translateCStr_Lang(download_Icon " " saveToDownloads_Label), 1173 init_Array(&items, sizeof(iMenuItem));
1174 0, 1174 if (iCmpStr(mtype, "application/octet-stream")) {
1175 0, 1175 pushBack_Array(
1176 "document.save" } }, 1176 &items,
1177 1); 1177 &(iMenuItem){ translateCStr_Lang(format_CStr("View as \"%s\"", mtype)),
1178 SDLK_RETURN,
1179 0,
1180 format_CStr("document.setmediatype mime:%s", mtype) });
1181 }
1182 pushBack_Array(
1183 &items,
1184 &(iMenuItem){ translateCStr_Lang(download_Icon " " saveToDownloads_Label),
1185 0,
1186 0,
1187 "document.save" });
1188 makeFooterButtons_DocumentWidget_(d, data_Array(&items), size_Array(&items));
1189 deinit_Array(&items);
1178 break; 1190 break;
1179 } 1191 }
1180 default: 1192 default:
@@ -3176,6 +3188,10 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
3176 document_App() == d) { 3188 document_App() == d) {
3177 return handleSwipe_DocumentWidget_(d, cmd); 3189 return handleSwipe_DocumentWidget_(d, cmd);
3178 } 3190 }
3191 else if (equal_Command(cmd, "document.setmediatype") && document_App() == d) {
3192 setUrlAndSource_DocumentWidget(d, d->mod.url, string_Command(cmd, "mime"), &d->sourceContent);
3193 return iTrue;
3194 }
3179 return iFalse; 3195 return iFalse;
3180} 3196}
3181 3197