diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-10-01 07:15:04 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-10-01 07:15:04 +0300 |
commit | d719e31a5d38c410e8e2d0795afe91fc59cf352e (patch) | |
tree | 7a4d2090c08d985c64df4e9168cc6d1097d8bc38 | |
parent | 4041ff10f50d4b6dc5c14b18f180b2738cbbadd0 (diff) |
Refactor: Separate media from GmDocument
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/gmdocument.c | 178 | ||||
-rw-r--r-- | src/gmdocument.h | 20 | ||||
-rw-r--r-- | src/media.c | 155 | ||||
-rw-r--r-- | src/media.h | 48 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 67 |
6 files changed, 283 insertions, 187 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e323d47..15ff9a0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -97,6 +97,8 @@ set (SOURCES | |||
97 | src/history.h | 97 | src/history.h |
98 | src/lookup.c | 98 | src/lookup.c |
99 | src/lookup.h | 99 | src/lookup.h |
100 | src/media.c | ||
101 | src/media.h | ||
100 | src/prefs.c | 102 | src/prefs.c |
101 | src/prefs.h | 103 | src/prefs.h |
102 | src/stb_image.h | 104 | src/stb_image.h |
diff --git a/src/gmdocument.c b/src/gmdocument.c index a273a8d5..6e139e45 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -32,9 +32,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
32 | #include <the_Foundation/ptrarray.h> | 32 | #include <the_Foundation/ptrarray.h> |
33 | #include <the_Foundation/regexp.h> | 33 | #include <the_Foundation/regexp.h> |
34 | 34 | ||
35 | #include <SDL_hints.h> | ||
36 | #include <SDL_render.h> | ||
37 | #include <stb_image.h> | ||
38 | #include <ctype.h> | 35 | #include <ctype.h> |
39 | 36 | ||
40 | iDeclareType(GmLink) | 37 | iDeclareType(GmLink) |
@@ -57,65 +54,24 @@ void deinit_GmLink(iGmLink *d) { | |||
57 | 54 | ||
58 | iDefineTypeConstruction(GmLink) | 55 | iDefineTypeConstruction(GmLink) |
59 | 56 | ||
60 | iDeclareType(GmImage) | ||
61 | |||
62 | struct Impl_GmImage { | ||
63 | iInt2 size; | ||
64 | size_t numBytes; | ||
65 | iString mime; | ||
66 | iGmLinkId linkId; | ||
67 | iBool isPermanent; | ||
68 | SDL_Texture *texture; | ||
69 | }; | ||
70 | |||
71 | void init_GmImage(iGmImage *d, const iBlock *data) { | ||
72 | init_String(&d->mime); | ||
73 | d->isPermanent = iFalse; | ||
74 | d->numBytes = size_Block(data); | ||
75 | uint8_t *imgData = stbi_load_from_memory( | ||
76 | constData_Block(data), size_Block(data), &d->size.x, &d->size.y, NULL, 4); | ||
77 | if (!imgData) { | ||
78 | d->size = zero_I2(); | ||
79 | d->texture = NULL; | ||
80 | } | ||
81 | else { | ||
82 | SDL_Surface *surface = SDL_CreateRGBSurfaceWithFormatFrom( | ||
83 | imgData, d->size.x, d->size.y, 32, d->size.x * 4, SDL_PIXELFORMAT_ABGR8888); | ||
84 | /* TODO: In multiwindow case, all windows must have the same shared renderer? | ||
85 | Or at least a shared context. */ | ||
86 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); /* linear scaling */ | ||
87 | d->texture = SDL_CreateTextureFromSurface(renderer_Window(get_Window()), surface); | ||
88 | SDL_FreeSurface(surface); | ||
89 | stbi_image_free(imgData); | ||
90 | } | ||
91 | d->linkId = 0; | ||
92 | } | ||
93 | |||
94 | void deinit_GmImage(iGmImage *d) { | ||
95 | SDL_DestroyTexture(d->texture); | ||
96 | deinit_String(&d->mime); | ||
97 | } | ||
98 | |||
99 | iDefineTypeConstructionArgs(GmImage, (const iBlock *data), data) | ||
100 | |||
101 | /*----------------------------------------------------------------------------------------------*/ | 57 | /*----------------------------------------------------------------------------------------------*/ |
102 | 58 | ||
103 | struct Impl_GmDocument { | 59 | struct Impl_GmDocument { |
104 | iObject object; | 60 | iObject object; |
105 | enum iGmDocumentFormat format; | 61 | enum iGmDocumentFormat format; |
106 | iString source; | 62 | iString source; |
107 | iString url; /* for resolving relative links */ | 63 | iString url; /* for resolving relative links */ |
108 | iString localHost; | 64 | iString localHost; |
109 | int forceBreakWidth; /* force breaks on very long preformatted lines */ | 65 | int forceBreakWidth; /* force breaks on very long preformatted lines */ |
110 | iInt2 size; | 66 | iInt2 size; |
111 | iArray layout; /* contents of source, laid out in document space */ | 67 | iArray layout; /* contents of source, laid out in document space */ |
112 | iPtrArray links; | 68 | iPtrArray links; |
113 | iString bannerText; | 69 | iString bannerText; |
114 | iString title; /* the first top-level title */ | 70 | iString title; /* the first top-level title */ |
115 | iArray headings; | 71 | iArray headings; |
116 | iPtrArray images; /* persistent across layouts, references links by ID */ | 72 | uint32_t themeSeed; |
117 | uint32_t themeSeed; | 73 | iChar siteIcon; |
118 | iChar siteIcon; | 74 | iMedia * media; |
119 | }; | 75 | }; |
120 | 76 | ||
121 | iDefineObjectConstruction(GmDocument) | 77 | iDefineObjectConstruction(GmDocument) |
@@ -180,7 +136,7 @@ static int lastVisibleRunBottom_GmDocument_(const iGmDocument *d) { | |||
180 | return 0; | 136 | return 0; |
181 | } | 137 | } |
182 | 138 | ||
183 | iInt2 measurePreformattedBlock_GmDocument_(const iGmDocument *d, const char *start, int font) { | 139 | static iInt2 measurePreformattedBlock_GmDocument_(const iGmDocument *d, const char *start, int font) { |
184 | const iRangecc content = { start, constEnd_String(&d->source) }; | 140 | const iRangecc content = { start, constEnd_String(&d->source) }; |
185 | iRangecc line = iNullRange; | 141 | iRangecc line = iNullRange; |
186 | nextSplit_Rangecc(content, "\n", &line); | 142 | nextSplit_Rangecc(content, "\n", &line); |
@@ -243,7 +199,7 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li | |||
243 | link->flags |= imageFileExtension_GmLinkFlag; | 199 | link->flags |= imageFileExtension_GmLinkFlag; |
244 | } | 200 | } |
245 | else if (endsWithCase_String(path, ".mp3") || endsWithCase_String(path, ".wav") || | 201 | else if (endsWithCase_String(path, ".mp3") || endsWithCase_String(path, ".wav") || |
246 | endsWithCase_String(path, ".mid")) { | 202 | endsWithCase_String(path, ".mid") || endsWithCase_String(path, ".ogg")) { |
247 | link->flags |= audioFileExtension_GmLinkFlag; | 203 | link->flags |= audioFileExtension_GmLinkFlag; |
248 | } | 204 | } |
249 | delete_String(path); | 205 | delete_String(path); |
@@ -278,17 +234,6 @@ static void clearLinks_GmDocument_(iGmDocument *d) { | |||
278 | clear_PtrArray(&d->links); | 234 | clear_PtrArray(&d->links); |
279 | } | 235 | } |
280 | 236 | ||
281 | static size_t findLinkImage_GmDocument_(const iGmDocument *d, iGmLinkId linkId) { | ||
282 | /* TODO: use a hash */ | ||
283 | iConstForEach(PtrArray, i, &d->images) { | ||
284 | const iGmImage *img = i.ptr; | ||
285 | if (img->linkId == linkId) { | ||
286 | return index_PtrArrayConstIterator(&i); | ||
287 | } | ||
288 | } | ||
289 | return iInvalidPos; | ||
290 | } | ||
291 | |||
292 | static iBool isGopher_GmDocument_(const iGmDocument *d) { | 237 | static iBool isGopher_GmDocument_(const iGmDocument *d) { |
293 | return equalCase_Rangecc(urlScheme_String(&d->url), "gopher"); | 238 | return equalCase_Rangecc(urlScheme_String(&d->url), "gopher"); |
294 | } | 239 | } |
@@ -576,13 +521,14 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
576 | ((iGmRun *) back_Array(&d->layout))->flags |= endOfLine_GmRunFlag; | 521 | ((iGmRun *) back_Array(&d->layout))->flags |= endOfLine_GmRunFlag; |
577 | /* Image content. */ | 522 | /* Image content. */ |
578 | if (type == link_GmLineType) { | 523 | if (type == link_GmLineType) { |
579 | const size_t imgIndex = findLinkImage_GmDocument_(d, run.linkId); | 524 | const iMediaId imageId = findLinkImage_Media(d->media, run.linkId); |
580 | if (imgIndex != iInvalidPos) { | 525 | if (imageId) { |
581 | const iGmImage *img = constAt_PtrArray(&d->images, imgIndex); | 526 | iGmImageInfo img; |
527 | imageInfo_Media(d->media, imageId, &img); | ||
582 | /* Mark the link as having content. */ { | 528 | /* Mark the link as having content. */ { |
583 | iGmLink *link = at_PtrArray(&d->links, run.linkId - 1); | 529 | iGmLink *link = at_PtrArray(&d->links, run.linkId - 1); |
584 | link->flags |= content_GmLinkFlag; | 530 | link->flags |= content_GmLinkFlag; |
585 | if (img->isPermanent) { | 531 | if (img.isPermanent) { |
586 | link->flags |= permanent_GmLinkFlag; | 532 | link->flags |= permanent_GmLinkFlag; |
587 | } | 533 | } |
588 | } | 534 | } |
@@ -590,10 +536,10 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
590 | pos.y += margin; | 536 | pos.y += margin; |
591 | run.bounds.pos = pos; | 537 | run.bounds.pos = pos; |
592 | run.bounds.size.x = d->size.x; | 538 | run.bounds.size.x = d->size.x; |
593 | const float aspect = (float) img->size.y / (float) img->size.x; | 539 | const float aspect = (float) img.size.y / (float) img.size.x; |
594 | run.bounds.size.y = d->size.x * aspect; | 540 | run.bounds.size.y = d->size.x * aspect; |
595 | run.visBounds = run.bounds; | 541 | run.visBounds = run.bounds; |
596 | const iInt2 maxSize = mulf_I2(img->size, get_Window()->pixelRatio); | 542 | const iInt2 maxSize = mulf_I2(img.size, get_Window()->pixelRatio); |
597 | if (width_Rect(run.visBounds) > maxSize.x) { | 543 | if (width_Rect(run.visBounds) > maxSize.x) { |
598 | /* Don't scale the image up. */ | 544 | /* Don't scale the image up. */ |
599 | run.visBounds.size.y = run.visBounds.size.y * maxSize.x / width_Rect(run.visBounds); | 545 | run.visBounds.size.y = run.visBounds.size.y * maxSize.x / width_Rect(run.visBounds); |
@@ -601,10 +547,10 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
601 | run.visBounds.pos.x = run.bounds.size.x / 2 - width_Rect(run.visBounds) / 2; | 547 | run.visBounds.pos.x = run.bounds.size.x / 2 - width_Rect(run.visBounds) / 2; |
602 | run.bounds.size.y = run.visBounds.size.y; | 548 | run.bounds.size.y = run.visBounds.size.y; |
603 | } | 549 | } |
604 | run.text = iNullRange; | 550 | run.text = iNullRange; |
605 | run.font = 0; | 551 | run.font = 0; |
606 | run.color = 0; | 552 | run.color = 0; |
607 | run.imageId = imgIndex + 1; | 553 | run.imageId = imageId; |
608 | pushBack_Array(&d->layout, &run); | 554 | pushBack_Array(&d->layout, &run); |
609 | pos.y += run.bounds.size.y + margin; | 555 | pos.y += run.bounds.size.y + margin; |
610 | } | 556 | } |
@@ -625,12 +571,13 @@ void init_GmDocument(iGmDocument *d) { | |||
625 | init_String(&d->bannerText); | 571 | init_String(&d->bannerText); |
626 | init_String(&d->title); | 572 | init_String(&d->title); |
627 | init_Array(&d->headings, sizeof(iGmHeading)); | 573 | init_Array(&d->headings, sizeof(iGmHeading)); |
628 | init_PtrArray(&d->images); | ||
629 | d->themeSeed = 0; | 574 | d->themeSeed = 0; |
630 | d->siteIcon = 0; | 575 | d->siteIcon = 0; |
576 | d->media = new_Media(); | ||
631 | } | 577 | } |
632 | 578 | ||
633 | void deinit_GmDocument(iGmDocument *d) { | 579 | void deinit_GmDocument(iGmDocument *d) { |
580 | delete_Media(d->media); | ||
634 | deinit_String(&d->bannerText); | 581 | deinit_String(&d->bannerText); |
635 | deinit_String(&d->title); | 582 | deinit_String(&d->title); |
636 | clearLinks_GmDocument_(d); | 583 | clearLinks_GmDocument_(d); |
@@ -642,12 +589,16 @@ void deinit_GmDocument(iGmDocument *d) { | |||
642 | deinit_String(&d->source); | 589 | deinit_String(&d->source); |
643 | } | 590 | } |
644 | 591 | ||
592 | iMedia *media_GmDocument(iGmDocument *d) { | ||
593 | return d->media; | ||
594 | } | ||
595 | |||
596 | const iMedia *constMedia_GmDocument(const iGmDocument *d) { | ||
597 | return d->media; | ||
598 | } | ||
599 | |||
645 | void reset_GmDocument(iGmDocument *d) { | 600 | void reset_GmDocument(iGmDocument *d) { |
646 | /* Free the loaded images. */ | 601 | clear_Media(d->media); |
647 | iForEach(PtrArray, i, &d->images) { | ||
648 | deinit_GmImage(i.ptr); | ||
649 | } | ||
650 | clear_PtrArray(&d->images); | ||
651 | clearLinks_GmDocument_(d); | 602 | clearLinks_GmDocument_(d); |
652 | clear_Array(&d->layout); | 603 | clear_Array(&d->layout); |
653 | clear_Array(&d->headings); | 604 | clear_Array(&d->headings); |
@@ -947,6 +898,10 @@ void setWidth_GmDocument(iGmDocument *d, int width, int forceBreakWidth) { | |||
947 | doLayout_GmDocument_(d); /* TODO: just flag need-layout and do it later */ | 898 | doLayout_GmDocument_(d); /* TODO: just flag need-layout and do it later */ |
948 | } | 899 | } |
949 | 900 | ||
901 | void redoLayout_GmDocument(iGmDocument *d) { | ||
902 | doLayout_GmDocument_(d); | ||
903 | } | ||
904 | |||
950 | iLocalDef iBool isNormalizableSpace_(char ch) { | 905 | iLocalDef iBool isNormalizableSpace_(char ch) { |
951 | return ch == ' ' || ch == '\t'; | 906 | return ch == ' ' || ch == '\t'; |
952 | } | 907 | } |
@@ -1021,32 +976,6 @@ void setSource_GmDocument(iGmDocument *d, const iString *source, int width, int | |||
1021 | setWidth_GmDocument(d, width, forceBreakWidth); /* re-do layout */ | 976 | setWidth_GmDocument(d, width, forceBreakWidth); /* re-do layout */ |
1022 | } | 977 | } |
1023 | 978 | ||
1024 | void setImage_GmDocument(iGmDocument *d, iGmLinkId linkId, const iString *mime, const iBlock *data, | ||
1025 | iBool allowHide) { | ||
1026 | if (!mime || !data) { | ||
1027 | iGmImage *img; | ||
1028 | if (take_PtrArray(&d->images, findLinkImage_GmDocument_(d, linkId), (void **) &img)) { | ||
1029 | delete_GmImage(img); | ||
1030 | } | ||
1031 | } | ||
1032 | else { | ||
1033 | /* TODO: check if we know this MIME type */ | ||
1034 | /* Upload the image. */ { | ||
1035 | iGmImage *img = new_GmImage(data); | ||
1036 | img->linkId = linkId; /* TODO: use a hash? */ | ||
1037 | img->isPermanent = !allowHide; | ||
1038 | set_String(&img->mime, mime); | ||
1039 | if (img->texture) { | ||
1040 | pushBack_PtrArray(&d->images, img); | ||
1041 | } | ||
1042 | else { | ||
1043 | delete_GmImage(img); | ||
1044 | } | ||
1045 | } | ||
1046 | } | ||
1047 | doLayout_GmDocument_(d); | ||
1048 | } | ||
1049 | |||
1050 | void render_GmDocument(const iGmDocument *d, iRangei visRangeY, iGmDocumentRenderFunc render, | 979 | void render_GmDocument(const iGmDocument *d, iRangei visRangeY, iGmDocumentRenderFunc render, |
1051 | void *context) { | 980 | void *context) { |
1052 | iBool isInside = iFalse; | 981 | iBool isInside = iFalse; |
@@ -1191,13 +1120,8 @@ const iTime *linkTime_GmDocument(const iGmDocument *d, iGmLinkId linkId) { | |||
1191 | return link ? &link->when : NULL; | 1120 | return link ? &link->when : NULL; |
1192 | } | 1121 | } |
1193 | 1122 | ||
1194 | |||
1195 | uint16_t linkImage_GmDocument(const iGmDocument *d, iGmLinkId linkId) { | 1123 | uint16_t linkImage_GmDocument(const iGmDocument *d, iGmLinkId linkId) { |
1196 | size_t index = findLinkImage_GmDocument_(d, linkId); | 1124 | return findLinkImage_Media(d->media, linkId); |
1197 | if (index != iInvalidPos) { | ||
1198 | return index + 1; | ||
1199 | } | ||
1200 | return 0; | ||
1201 | } | 1125 | } |
1202 | 1126 | ||
1203 | enum iColorId linkColor_GmDocument(const iGmDocument *d, iGmLinkId linkId, enum iGmLinkPart part) { | 1127 | enum iColorId linkColor_GmDocument(const iGmDocument *d, iGmLinkId linkId, enum iGmLinkPart part) { |
@@ -1256,26 +1180,6 @@ iBool isMediaLink_GmDocument(const iGmDocument *d, iGmLinkId linkId) { | |||
1256 | (imageFileExtension_GmLinkFlag | audioFileExtension_GmLinkFlag)) != 0; | 1180 | (imageFileExtension_GmLinkFlag | audioFileExtension_GmLinkFlag)) != 0; |
1257 | } | 1181 | } |
1258 | 1182 | ||
1259 | SDL_Texture *imageTexture_GmDocument(const iGmDocument *d, uint16_t imageId) { | ||
1260 | if (imageId > 0 && imageId <= size_PtrArray(&d->images)) { | ||
1261 | const iGmImage *img = constAt_PtrArray(&d->images, imageId - 1); | ||
1262 | return img->texture; | ||
1263 | } | ||
1264 | return NULL; | ||
1265 | } | ||
1266 | |||
1267 | void imageInfo_GmDocument(const iGmDocument *d, uint16_t imageId, iGmImageInfo *info_out) { | ||
1268 | if (imageId > 0 && imageId <= size_PtrArray(&d->images)) { | ||
1269 | const iGmImage *img = constAt_PtrArray(&d->images, imageId - 1); | ||
1270 | info_out->size = img->size; | ||
1271 | info_out->numBytes = img->numBytes; | ||
1272 | info_out->mime = cstr_String(&img->mime); | ||
1273 | } | ||
1274 | else { | ||
1275 | iZap(*info_out); | ||
1276 | } | ||
1277 | } | ||
1278 | |||
1279 | const iString *title_GmDocument(const iGmDocument *d) { | 1183 | const iString *title_GmDocument(const iGmDocument *d) { |
1280 | return &d->title; | 1184 | return &d->title; |
1281 | } | 1185 | } |
diff --git a/src/gmdocument.h b/src/gmdocument.h index 891ac6f2..a04398d9 100644 --- a/src/gmdocument.h +++ b/src/gmdocument.h | |||
@@ -23,15 +23,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
23 | #pragma once | 23 | #pragma once |
24 | 24 | ||
25 | #include "gmutil.h" | 25 | #include "gmutil.h" |
26 | #include "media.h" | ||
26 | 27 | ||
27 | #include <the_Foundation/array.h> | 28 | #include <the_Foundation/array.h> |
28 | #include <the_Foundation/object.h> | 29 | #include <the_Foundation/object.h> |
29 | #include <the_Foundation/rect.h> | 30 | #include <the_Foundation/rect.h> |
30 | #include <the_Foundation/string.h> | 31 | #include <the_Foundation/string.h> |
31 | #include <the_Foundation/time.h> | 32 | #include <the_Foundation/time.h> |
32 | #include <SDL_render.h> | ||
33 | 33 | ||
34 | iDeclareType(GmImageInfo) | ||
35 | iDeclareType(GmHeading) | 34 | iDeclareType(GmHeading) |
36 | iDeclareType(GmRun) | 35 | iDeclareType(GmRun) |
37 | 36 | ||
@@ -60,12 +59,6 @@ enum iGmLinkFlags { | |||
60 | permanent_GmLinkFlag = iBit(15), /* content cannot be dismissed; media link */ | 59 | permanent_GmLinkFlag = iBit(15), /* content cannot be dismissed; media link */ |
61 | }; | 60 | }; |
62 | 61 | ||
63 | struct Impl_GmImageInfo { | ||
64 | iInt2 size; | ||
65 | size_t numBytes; | ||
66 | const char *mime; | ||
67 | }; | ||
68 | |||
69 | struct Impl_GmHeading { | 62 | struct Impl_GmHeading { |
70 | iRangecc text; | 63 | iRangecc text; |
71 | int level; /* 0, 1, 2 */ | 64 | int level; /* 0, 1, 2 */ |
@@ -86,7 +79,8 @@ struct Impl_GmRun { | |||
86 | iRect bounds; /* used for hit testing, may extend to edges */ | 79 | iRect bounds; /* used for hit testing, may extend to edges */ |
87 | iRect visBounds; /* actual visual bounds */ | 80 | iRect visBounds; /* actual visual bounds */ |
88 | iGmLinkId linkId; /* zero for non-links */ | 81 | iGmLinkId linkId; /* zero for non-links */ |
89 | uint16_t imageId; /* zero for images */ | 82 | uint16_t imageId; /* zero if not an image */ |
83 | uint16_t audioId; /* zero if not audio */ | ||
90 | }; | 84 | }; |
91 | 85 | ||
92 | const char * findLoc_GmRun (const iGmRun *, iInt2 pos); | 86 | const char * findLoc_GmRun (const iGmRun *, iInt2 pos); |
@@ -103,15 +97,17 @@ enum iGmDocumentFormat { | |||
103 | void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed); | 97 | void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed); |
104 | void setFormat_GmDocument (iGmDocument *, enum iGmDocumentFormat format); | 98 | void setFormat_GmDocument (iGmDocument *, enum iGmDocumentFormat format); |
105 | void setWidth_GmDocument (iGmDocument *, int width, int forceBreakWidth); | 99 | void setWidth_GmDocument (iGmDocument *, int width, int forceBreakWidth); |
100 | void redoLayout_GmDocument (iGmDocument *); | ||
106 | void setUrl_GmDocument (iGmDocument *, const iString *url); | 101 | void setUrl_GmDocument (iGmDocument *, const iString *url); |
107 | void setSource_GmDocument (iGmDocument *, const iString *source, int width, int forceBreakWidth); | 102 | void setSource_GmDocument (iGmDocument *, const iString *source, int width, int forceBreakWidth); |
108 | void setImage_GmDocument (iGmDocument *, iGmLinkId linkId, const iString *mime, const iBlock *data, | ||
109 | iBool allowHide); | ||
110 | 103 | ||
111 | void reset_GmDocument (iGmDocument *); /* free images */ | 104 | void reset_GmDocument (iGmDocument *); /* free images */ |
112 | 105 | ||
113 | typedef void (*iGmDocumentRenderFunc)(void *, const iGmRun *); | 106 | typedef void (*iGmDocumentRenderFunc)(void *, const iGmRun *); |
114 | 107 | ||
108 | iMedia * media_GmDocument (iGmDocument *); | ||
109 | const iMedia * constMedia_GmDocument (const iGmDocument *); | ||
110 | |||
115 | void render_GmDocument (const iGmDocument *, iRangei visRangeY, | 111 | void render_GmDocument (const iGmDocument *, iRangei visRangeY, |
116 | iGmDocumentRenderFunc render, void *); | 112 | iGmDocumentRenderFunc render, void *); |
117 | iInt2 size_GmDocument (const iGmDocument *); | 113 | iInt2 size_GmDocument (const iGmDocument *); |
@@ -144,5 +140,3 @@ iBool isMediaLink_GmDocument (const iGmDocument *, iGmLinkId linkId); | |||
144 | const iString * title_GmDocument (const iGmDocument *); | 140 | const iString * title_GmDocument (const iGmDocument *); |
145 | iChar siteIcon_GmDocument (const iGmDocument *); | 141 | iChar siteIcon_GmDocument (const iGmDocument *); |
146 | 142 | ||
147 | SDL_Texture * imageTexture_GmDocument (const iGmDocument *, uint16_t imageId); | ||
148 | void imageInfo_GmDocument (const iGmDocument *, uint16_t imageId, iGmImageInfo *info_out); | ||
diff --git a/src/media.c b/src/media.c new file mode 100644 index 00000000..9f6acd19 --- /dev/null +++ b/src/media.c | |||
@@ -0,0 +1,155 @@ | |||
1 | /* Copyright 2020 Jaakko Keränen <jaakko.keranen@iki.fi> | ||
2 | |||
3 | Redistribution and use in source and binary forms, with or without | ||
4 | modification, are permitted provided that the following conditions are met: | ||
5 | |||
6 | 1. Redistributions of source code must retain the above copyright notice, this | ||
7 | list of conditions and the following disclaimer. | ||
8 | 2. Redistributions in binary form must reproduce the above copyright notice, | ||
9 | this list of conditions and the following disclaimer in the documentation | ||
10 | and/or other materials provided with the distribution. | ||
11 | |||
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | ||
22 | |||
23 | #include "media.h" | ||
24 | #include "gmdocument.h" | ||
25 | #include "ui/window.h" | ||
26 | |||
27 | #include <the_Foundation/ptrarray.h> | ||
28 | #include <stb_image.h> | ||
29 | #include <SDL_hints.h> | ||
30 | #include <SDL_render.h> | ||
31 | |||
32 | iDeclareType(GmImage) | ||
33 | |||
34 | struct Impl_GmImage { | ||
35 | iInt2 size; | ||
36 | size_t numBytes; | ||
37 | iString mime; | ||
38 | iGmLinkId linkId; | ||
39 | iBool isPermanent; | ||
40 | SDL_Texture *texture; | ||
41 | }; | ||
42 | |||
43 | void init_GmImage(iGmImage *d, const iBlock *data) { | ||
44 | init_String(&d->mime); | ||
45 | d->isPermanent = iFalse; | ||
46 | d->numBytes = size_Block(data); | ||
47 | uint8_t *imgData = stbi_load_from_memory( | ||
48 | constData_Block(data), size_Block(data), &d->size.x, &d->size.y, NULL, 4); | ||
49 | if (!imgData) { | ||
50 | d->size = zero_I2(); | ||
51 | d->texture = NULL; | ||
52 | } | ||
53 | else { | ||
54 | SDL_Surface *surface = SDL_CreateRGBSurfaceWithFormatFrom( | ||
55 | imgData, d->size.x, d->size.y, 32, d->size.x * 4, SDL_PIXELFORMAT_ABGR8888); | ||
56 | /* TODO: In multiwindow case, all windows must have the same shared renderer? | ||
57 | Or at least a shared context. */ | ||
58 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); /* linear scaling */ | ||
59 | d->texture = SDL_CreateTextureFromSurface(renderer_Window(get_Window()), surface); | ||
60 | SDL_FreeSurface(surface); | ||
61 | stbi_image_free(imgData); | ||
62 | } | ||
63 | d->linkId = 0; | ||
64 | } | ||
65 | |||
66 | void deinit_GmImage(iGmImage *d) { | ||
67 | SDL_DestroyTexture(d->texture); | ||
68 | deinit_String(&d->mime); | ||
69 | } | ||
70 | |||
71 | iDefineTypeConstructionArgs(GmImage, (const iBlock *data), data) | ||
72 | |||
73 | /*----------------------------------------------------------------------------------------------*/ | ||
74 | |||
75 | struct Impl_Media { | ||
76 | iPtrArray images; | ||
77 | }; | ||
78 | |||
79 | iDefineTypeConstruction(Media) | ||
80 | |||
81 | void init_Media(iMedia *d) { | ||
82 | init_PtrArray(&d->images); | ||
83 | } | ||
84 | |||
85 | void deinit_Media(iMedia *d) { | ||
86 | clear_Media(d); | ||
87 | deinit_PtrArray(&d->images); | ||
88 | } | ||
89 | |||
90 | void clear_Media(iMedia *d) { | ||
91 | iForEach(PtrArray, i, &d->images) { | ||
92 | deinit_GmImage(i.ptr); | ||
93 | } | ||
94 | clear_PtrArray(&d->images); | ||
95 | } | ||
96 | |||
97 | void setImage_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlock *data, | ||
98 | iBool allowHide) { | ||
99 | if (!mime || !data) { | ||
100 | iGmImage *img; | ||
101 | const iMediaId existing = findLinkImage_Media(d, linkId); | ||
102 | if (existing) { | ||
103 | take_PtrArray(&d->images, existing - 1, (void **) &img); | ||
104 | delete_GmImage(img); | ||
105 | } | ||
106 | } | ||
107 | else { | ||
108 | /* TODO: check if we know this MIME type */ | ||
109 | /* Upload the image. */ { | ||
110 | iGmImage *img = new_GmImage(data); | ||
111 | img->linkId = linkId; /* TODO: use a hash? */ | ||
112 | img->isPermanent = !allowHide; | ||
113 | set_String(&img->mime, mime); | ||
114 | if (img->texture) { | ||
115 | pushBack_PtrArray(&d->images, img); | ||
116 | } | ||
117 | else { | ||
118 | delete_GmImage(img); | ||
119 | } | ||
120 | } | ||
121 | } | ||
122 | // doLayout_GmDocument_(d); | ||
123 | } | ||
124 | |||
125 | iMediaId findLinkImage_Media(const iMedia *d, iGmLinkId linkId) { | ||
126 | /* TODO: use a hash */ | ||
127 | iConstForEach(PtrArray, i, &d->images) { | ||
128 | const iGmImage *img = i.ptr; | ||
129 | if (img->linkId == linkId) { | ||
130 | return index_PtrArrayConstIterator(&i) + 1; | ||
131 | } | ||
132 | } | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | SDL_Texture *imageTexture_Media(const iMedia *d, uint16_t imageId) { | ||
137 | if (imageId > 0 && imageId <= size_PtrArray(&d->images)) { | ||
138 | const iGmImage *img = constAt_PtrArray(&d->images, imageId - 1); | ||
139 | return img->texture; | ||
140 | } | ||
141 | return NULL; | ||
142 | } | ||
143 | |||
144 | void imageInfo_Media(const iMedia *d, uint16_t imageId, iGmImageInfo *info_out) { | ||
145 | if (imageId > 0 && imageId <= size_PtrArray(&d->images)) { | ||
146 | const iGmImage *img = constAt_PtrArray(&d->images, imageId - 1); | ||
147 | info_out->size = img->size; | ||
148 | info_out->numBytes = img->numBytes; | ||
149 | info_out->mime = cstr_String(&img->mime); | ||
150 | info_out->isPermanent = img->isPermanent; | ||
151 | } | ||
152 | else { | ||
153 | iZap(*info_out); | ||
154 | } | ||
155 | } | ||
diff --git a/src/media.h b/src/media.h new file mode 100644 index 00000000..0a6c1a81 --- /dev/null +++ b/src/media.h | |||
@@ -0,0 +1,48 @@ | |||
1 | /* Copyright 2020 Jaakko Keränen <jaakko.keranen@iki.fi> | ||
2 | |||
3 | Redistribution and use in source and binary forms, with or without | ||
4 | modification, are permitted provided that the following conditions are met: | ||
5 | |||
6 | 1. Redistributions of source code must retain the above copyright notice, this | ||
7 | list of conditions and the following disclaimer. | ||
8 | 2. Redistributions in binary form must reproduce the above copyright notice, | ||
9 | this list of conditions and the following disclaimer in the documentation | ||
10 | and/or other materials provided with the distribution. | ||
11 | |||
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | ||
22 | |||
23 | #pragma once | ||
24 | |||
25 | #include <the_Foundation/block.h> | ||
26 | #include <the_Foundation/string.h> | ||
27 | #include <the_Foundation/vec2.h> | ||
28 | #include <SDL_render.h> | ||
29 | |||
30 | typedef uint16_t iMediaId; | ||
31 | |||
32 | iDeclareType(GmImageInfo) | ||
33 | |||
34 | struct Impl_GmImageInfo { | ||
35 | iInt2 size; | ||
36 | size_t numBytes; | ||
37 | const char *mime; | ||
38 | iBool isPermanent; | ||
39 | }; | ||
40 | |||
41 | iDeclareType(Media) iDeclareTypeConstruction(Media) | ||
42 | |||
43 | void clear_Media (iMedia *); | ||
44 | void setImage_Media (iMedia *, uint16_t linkId, const iString *mime, const iBlock *data, iBool allowHide); | ||
45 | |||
46 | iMediaId findLinkImage_Media (const iMedia *, uint16_t linkId); | ||
47 | SDL_Texture * imageTexture_Media (const iMedia *, iMediaId imageId); | ||
48 | void imageInfo_Media (const iMedia *, iMediaId imageId, iGmImageInfo *info_out); | ||
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index eb248ceb..f63d93f4 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -31,9 +31,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
31 | #include "util.h" | 31 | #include "util.h" |
32 | #include "history.h" | 32 | #include "history.h" |
33 | #include "app.h" | 33 | #include "app.h" |
34 | #include "../gmdocument.h" | 34 | #include "gmdocument.h" |
35 | #include "../gmrequest.h" | 35 | #include "gmrequest.h" |
36 | #include "../gmutil.h" | 36 | #include "gmutil.h" |
37 | #include "media.h" | ||
37 | 38 | ||
38 | #include <the_Foundation/file.h> | 39 | #include <the_Foundation/file.h> |
39 | #include <the_Foundation/fileinfo.h> | 40 | #include <the_Foundation/fileinfo.h> |
@@ -735,8 +736,12 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse | |||
735 | baseName_Path(collect_String(newRange_String(parts.path))).start; | 736 | baseName_Path(collect_String(newRange_String(parts.path))).start; |
736 | } | 737 | } |
737 | format_String(&str, "=> %s %s\n", cstr_String(d->mod.url), imageTitle); | 738 | format_String(&str, "=> %s %s\n", cstr_String(d->mod.url), imageTitle); |
738 | setImage_GmDocument( | 739 | setImage_Media(media_GmDocument(d->doc), |
739 | d->doc, 1, mimeStr, &response->body, iFalse /* it's fixed */); | 740 | 1, |
741 | mimeStr, | ||
742 | &response->body, | ||
743 | iFalse /* it's fixed */); | ||
744 | redoLayout_GmDocument(d->doc); | ||
740 | } | 745 | } |
741 | else { | 746 | else { |
742 | clear_String(&str); | 747 | clear_String(&str); |
@@ -1136,13 +1141,13 @@ static iBool handleMediaCommand_DocumentWidget_(iDocumentWidget *d, const char * | |||
1136 | const enum iGmStatusCode code = status_GmRequest(req->req); | 1141 | const enum iGmStatusCode code = status_GmRequest(req->req); |
1137 | /* Give the media to the document for presentation. */ | 1142 | /* Give the media to the document for presentation. */ |
1138 | if (code == success_GmStatusCode) { | 1143 | if (code == success_GmStatusCode) { |
1139 | // printf("media finished: %s\n size: %zu\n type: %s\n", | ||
1140 | // cstr_String(url_GmRequest(req->req)), | ||
1141 | // size_Block(body_GmRequest(req->req)), | ||
1142 | // cstr_String(meta_GmRequest(req->req))); | ||
1143 | if (startsWith_String(meta_GmRequest(req->req), "image/")) { | 1144 | if (startsWith_String(meta_GmRequest(req->req), "image/")) { |
1144 | setImage_GmDocument(d->doc, req->linkId, meta_GmRequest(req->req), | 1145 | setImage_Media(media_GmDocument(d->doc), |
1145 | body_GmRequest(req->req), iTrue); | 1146 | req->linkId, |
1147 | meta_GmRequest(req->req), | ||
1148 | body_GmRequest(req->req), | ||
1149 | iTrue); | ||
1150 | redoLayout_GmDocument(d->doc); | ||
1146 | updateVisible_DocumentWidget_(d); | 1151 | updateVisible_DocumentWidget_(d); |
1147 | invalidate_DocumentWidget_(d); | 1152 | invalidate_DocumentWidget_(d); |
1148 | refresh_Widget(as_Widget(d)); | 1153 | refresh_Widget(as_Widget(d)); |
@@ -1792,10 +1797,11 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1792 | further to do. */ | 1797 | further to do. */ |
1793 | return iTrue; | 1798 | return iTrue; |
1794 | } | 1799 | } |
1795 | if (!requestMedia_DocumentWidget_(d, linkId)) { | 1800 | if (!requestMedia_DocumentWidget_(d, linkId)) { |
1796 | if (linkFlags & content_GmLinkFlag) { | 1801 | if (linkFlags & content_GmLinkFlag) { |
1797 | /* Dismiss shown content on click. */ | 1802 | /* Dismiss shown content on click. */ |
1798 | setImage_GmDocument(d->doc, linkId, NULL, NULL, iTrue); | 1803 | setImage_Media(media_GmDocument(d->doc), linkId, NULL, NULL, iTrue); |
1804 | redoLayout_GmDocument(d->doc); | ||
1799 | d->hoverLink = NULL; | 1805 | d->hoverLink = NULL; |
1800 | scroll_DocumentWidget_(d, 0); | 1806 | scroll_DocumentWidget_(d, 0); |
1801 | updateVisible_DocumentWidget_(d); | 1807 | updateVisible_DocumentWidget_(d); |
@@ -1807,8 +1813,12 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1807 | /* Show the existing content again if we have it. */ | 1813 | /* Show the existing content again if we have it. */ |
1808 | iMediaRequest *req = findMediaRequest_DocumentWidget_(d, linkId); | 1814 | iMediaRequest *req = findMediaRequest_DocumentWidget_(d, linkId); |
1809 | if (req) { | 1815 | if (req) { |
1810 | setImage_GmDocument(d->doc, linkId, meta_GmRequest(req->req), | 1816 | setImage_Media(media_GmDocument(d->doc), |
1811 | body_GmRequest(req->req), iTrue); | 1817 | linkId, |
1818 | meta_GmRequest(req->req), | ||
1819 | body_GmRequest(req->req), | ||
1820 | iTrue); | ||
1821 | redoLayout_GmDocument(d->doc); | ||
1812 | updateVisible_DocumentWidget_(d); | 1822 | updateVisible_DocumentWidget_(d); |
1813 | invalidate_DocumentWidget_(d); | 1823 | invalidate_DocumentWidget_(d); |
1814 | refresh_Widget(w); | 1824 | refresh_Widget(w); |
@@ -1894,7 +1904,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
1894 | iDrawContext *d = context; | 1904 | iDrawContext *d = context; |
1895 | const iInt2 origin = d->viewPos; | 1905 | const iInt2 origin = d->viewPos; |
1896 | if (run->imageId) { | 1906 | if (run->imageId) { |
1897 | SDL_Texture *tex = imageTexture_GmDocument(d->widget->doc, run->imageId); | 1907 | SDL_Texture *tex = imageTexture_Media(media_GmDocument(d->widget->doc), run->imageId); |
1898 | if (tex) { | 1908 | if (tex) { |
1899 | const iRect dst = moved_Rect(run->visBounds, origin); | 1909 | const iRect dst = moved_Rect(run->visBounds, origin); |
1900 | fillRect_Paint(&d->paint, dst, tmBackground_ColorId); /* in case the image has alpha */ | 1910 | fillRect_Paint(&d->paint, dst, tmBackground_ColorId); /* in case the image has alpha */ |
@@ -1981,7 +1991,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
1981 | fg = linkColor_GmDocument(doc, run->linkId, textHover_GmLinkPart); | 1991 | fg = linkColor_GmDocument(doc, run->linkId, textHover_GmLinkPart); |
1982 | iAssert(!isEmpty_Rect(run->bounds)); | 1992 | iAssert(!isEmpty_Rect(run->bounds)); |
1983 | iGmImageInfo info; | 1993 | iGmImageInfo info; |
1984 | imageInfo_GmDocument(doc, linkImage_GmDocument(doc, run->linkId), &info); | 1994 | imageInfo_Media(constMedia_GmDocument(doc), linkImage_GmDocument(doc, run->linkId), &info); |
1985 | iString text; | 1995 | iString text; |
1986 | init_String(&text); | 1996 | init_String(&text); |
1987 | format_String(&text, "%s \u2014 %d x %d \u2014 %.1fMB", | 1997 | format_String(&text, "%s \u2014 %d x %d \u2014 %.1fMB", |
@@ -2083,7 +2093,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
2083 | // drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, red_ColorId); | 2093 | // drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, red_ColorId); |
2084 | } | 2094 | } |
2085 | 2095 | ||
2086 | static int drawSideRect_(iPaint *p, iRect rect) { //}, int thickness) { | 2096 | static int drawSideRect_(iPaint *p, iRect rect) { |
2087 | int bg = tmBannerBackground_ColorId; | 2097 | int bg = tmBannerBackground_ColorId; |
2088 | int fg = tmBannerIcon_ColorId; | 2098 | int fg = tmBannerIcon_ColorId; |
2089 | if (equal_Color(get_Color(bg), get_Color(tmBackground_ColorId))) { | 2099 | if (equal_Color(get_Color(bg), get_Color(tmBackground_ColorId))) { |
@@ -2131,34 +2141,17 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) { | |||
2131 | const iChar icon = siteIcon_GmDocument(d->doc); | 2141 | const iChar icon = siteIcon_GmDocument(d->doc); |
2132 | iRect rect = { add_I2(topLeft_Rect(bounds), init1_I2(margin)), init1_I2(minBannerSize) }; | 2142 | iRect rect = { add_I2(topLeft_Rect(bounds), init1_I2(margin)), init1_I2(minBannerSize) }; |
2133 | p.alpha = opacity * 255; | 2143 | p.alpha = opacity * 255; |
2134 | //int offset = iMax(0, bottom_Rect(banner->visBounds) - d->scrollY); | 2144 | rect.pos.y += height_Rect(bounds) / 2 - rect.size.y / 2 - (banner ? banner->visBounds.size.y / 2 : 0); |
2135 | rect.pos.y += height_Rect(bounds) / 2 - rect.size.y / 2 - (banner ? banner->visBounds.size.y / 2 : 0); // offset; | 2145 | int fg = drawSideRect_(&p, rect); |
2136 | //rect.pos.y -= lineHeight_Text(heading3_FontId) / 2; /* the heading text underneath */ | ||
2137 | int fg = drawSideRect_(&p, rect); //, gap_UI / 2); | ||
2138 | iString str; | 2146 | iString str; |
2139 | initUnicodeN_String(&str, &icon, 1); | 2147 | initUnicodeN_String(&str, &icon, 1); |
2140 | drawCentered_Text(banner_FontId, rect, iTrue, fg, "%s", cstr_String(&str)); | 2148 | drawCentered_Text(banner_FontId, rect, iTrue, fg, "%s", cstr_String(&str)); |
2141 | #if 1 | ||
2142 | if (avail >= minBannerSize * 2.25f) { | 2149 | if (avail >= minBannerSize * 2.25f) { |
2143 | iRangecc text = currentHeading_DocumentWidget_(d);// bannerText_DocumentWidget_(d); | 2150 | iRangecc text = currentHeading_DocumentWidget_(d);// bannerText_DocumentWidget_(d); |
2144 | iInt2 pos = addY_I2(bottomLeft_Rect(rect), gap_Text); | 2151 | iInt2 pos = addY_I2(bottomLeft_Rect(rect), gap_Text); |
2145 | const int font = heading3_FontId; | 2152 | const int font = heading3_FontId; |
2146 | drawWrapRange_Text(font, pos, avail - margin, tmBannerSideTitle_ColorId, text); | 2153 | drawWrapRange_Text(font, pos, avail - margin, tmBannerSideTitle_ColorId, text); |
2147 | #if 0 | ||
2148 | while (!isEmpty_Range(&text)) { | ||
2149 | tryAdvance_Text(font, text, avail - 2 * margin, &endp); | ||
2150 | drawRange_Text( | ||
2151 | font, pos, tmBannerTitle_ColorId, (iRangecc){ text.start, endp }); | ||
2152 | // drawRange_Text(font, | ||
2153 | // add_I2(pos, init1_I2(-gap_UI / 4)), | ||
2154 | // tmBackground_ColorId, | ||
2155 | // (iRangecc){ text.start, endp }); | ||
2156 | text.start = endp; | ||
2157 | pos.y += lineHeight_Text(font); | ||
2158 | } | ||
2159 | #endif | ||
2160 | } | 2154 | } |
2161 | #endif | ||
2162 | setOpacity_Text(1.0f); | 2155 | setOpacity_Text(1.0f); |
2163 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); | 2156 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); |
2164 | } | 2157 | } |