diff options
-rw-r--r-- | CMakeLists.txt | 5 | ||||
-rw-r--r-- | Depends.cmake | 1 | ||||
-rw-r--r-- | README.md | 1 | ||||
-rw-r--r-- | res/about/version.gmi | 4 | ||||
-rw-r--r-- | src/gmdocument.c | 5 | ||||
-rw-r--r-- | src/gmutil.c | 54 | ||||
-rw-r--r-- | src/gmutil.h | 1 | ||||
-rw-r--r-- | src/media.c | 16 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 30 |
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) | |||
48 | option (ENABLE_RELATIVE_EMBED "Resources should always be found via relative path" OFF) | 48 | option (ENABLE_RELATIVE_EMBED "Resources should always be found via relative path" OFF) |
49 | option (ENABLE_RESIZE_DRAW "Force window to redraw during resizing" ${DEFAULT_RESIZE_DRAW}) | 49 | option (ENABLE_RESIZE_DRAW "Force window to redraw during resizing" ${DEFAULT_RESIZE_DRAW}) |
50 | option (ENABLE_RESOURCE_EMBED "Embed resources inside the executable" OFF) | 50 | option (ENABLE_RESOURCE_EMBED "Embed resources inside the executable" OFF) |
51 | option (ENABLE_WEBP "Use libwebp to decode .webp images (via pkg-config)" ON) | ||
51 | option (ENABLE_WINDOWPOS_FIX "Set position after showing window (workaround for SDL bug)" OFF) | 52 | option (ENABLE_WINDOWPOS_FIX "Set position after showing window (workaround for SDL bug)" OFF) |
52 | option (ENABLE_X11_SWRENDER "Use software rendering under X11" OFF) | 53 | option (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) |
317 | endif () | 318 | endif () |
319 | if (ENABLE_WEBP AND WEBP_FOUND) | ||
320 | target_compile_definitions (app PUBLIC LAGRANGE_ENABLE_WEBP=1) | ||
321 | target_link_libraries (app PUBLIC PkgConfig::WEBP) | ||
322 | endif () | ||
318 | if (ENABLE_IDLE_SLEEP) | 323 | if (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) |
320 | endif () | 325 | endif () |
diff --git a/Depends.cmake b/Depends.cmake index ef95c9f4..86a1b47d 100644 --- a/Depends.cmake +++ b/Depends.cmake | |||
@@ -149,3 +149,4 @@ endif () | |||
149 | find_package (PkgConfig REQUIRED) | 149 | find_package (PkgConfig REQUIRED) |
150 | pkg_check_modules (SDL2 REQUIRED sdl2) | 150 | pkg_check_modules (SDL2 REQUIRED sdl2) |
151 | pkg_check_modules (MPG123 IMPORTED_TARGET libmpg123) | 151 | pkg_check_modules (MPG123 IMPORTED_TARGET libmpg123) |
152 | pkg_check_modules (WEBP IMPORTED_TARGET libwebp) | ||
@@ -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 | ||
514 | const char *mediaType_Path(const iString *path) { | 514 | const 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 | |||
567 | const 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 *); | |||
133 | const iString * canonicalUrl_String (const iString *); | 133 | const iString * canonicalUrl_String (const iString *); |
134 | 134 | ||
135 | const char * mediaType_Path (const iString *path); | 135 | const char * mediaType_Path (const iString *path); |
136 | const char * mediaTypeFromFileExtension_String (const iString *); | ||
136 | iRangecc mediaTypeWithoutParameters_Rangecc (iRangecc mime); | 137 | iRangecc mediaTypeWithoutParameters_Rangecc (iRangecc mime); |
137 | 138 | ||
138 | const iString * findContainerArchive_Path (const iString *path); | 139 | const 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) { | |||
86 | void makeTexture_GmImage(iGmImage *d) { | 90 | void 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 | ||