summaryrefslogtreecommitdiff
path: root/src/gmdocument.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-07-28 23:41:35 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-07-28 23:41:35 +0300
commitd921021132367076cd2a5f120b3a49db6e29acf7 (patch)
treeb118977ae998023c71d5e2e2c69d3051dfb77e4a /src/gmdocument.c
parent1a8a0170168568d1c3f075cc2996aceda08e790d (diff)
Loading and rendering images in the document
There can be multiple inline media requests on the page. Images are always associated with a link.
Diffstat (limited to 'src/gmdocument.c')
-rw-r--r--src/gmdocument.c109
1 files changed, 109 insertions, 0 deletions
diff --git a/src/gmdocument.c b/src/gmdocument.c
index 3dbef514..f8998c17 100644
--- a/src/gmdocument.c
+++ b/src/gmdocument.c
@@ -3,10 +3,15 @@
3#include "ui/color.h" 3#include "ui/color.h"
4#include "ui/text.h" 4#include "ui/text.h"
5#include "ui/metrics.h" 5#include "ui/metrics.h"
6#include "ui/window.h"
6 7
7#include <the_Foundation/ptrarray.h> 8#include <the_Foundation/ptrarray.h>
8#include <the_Foundation/regexp.h> 9#include <the_Foundation/regexp.h>
9 10
11#include <SDL_hints.h>
12#include <SDL_render.h>
13#include <stb_image.h>
14
10iDeclareType(GmLink) 15iDeclareType(GmLink)
11 16
12struct Impl_GmLink { 17struct Impl_GmLink {
@@ -25,6 +30,40 @@ void deinit_GmLink(iGmLink *d) {
25 30
26iDefineTypeConstruction(GmLink) 31iDefineTypeConstruction(GmLink)
27 32
33iDeclareType(GmImage)
34
35struct Impl_GmImage {
36 iInt2 size;
37 SDL_Texture *texture;
38 iGmLinkId linkId;
39};
40
41void init_GmImage(iGmImage *d, const iBlock *data) {
42 uint8_t *imgData = stbi_load_from_memory(
43 constData_Block(data), size_Block(data), &d->size.x, &d->size.y, NULL, 4);
44 if (!imgData) {
45 d->size = zero_I2();
46 d->texture = NULL;
47 }
48 else {
49 SDL_Surface *surface = SDL_CreateRGBSurfaceWithFormatFrom(
50 imgData, d->size.x, d->size.y, 32, d->size.x * 4, SDL_PIXELFORMAT_ABGR8888);
51 /* TODO: In multiwindow case, all windows must have the same shared renderer?
52 Or at least a shared context. */
53 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); /* linear scaling */
54 d->texture = SDL_CreateTextureFromSurface(renderer_Window(get_Window()), surface);
55 SDL_FreeSurface(surface);
56 stbi_image_free(imgData);
57 }
58 d->linkId = 0;
59}
60
61void deinit_GmImage(iGmImage *d) {
62 SDL_DestroyTexture(d->texture);
63}
64
65iDefineTypeConstructionArgs(GmImage, (const iBlock *data), data)
66
28struct Impl_GmDocument { 67struct Impl_GmDocument {
29 iObject object; 68 iObject object;
30 enum iGmDocumentFormat format; 69 enum iGmDocumentFormat format;
@@ -34,6 +73,7 @@ struct Impl_GmDocument {
34 iArray layout; /* contents of source, laid out in document space */ 73 iArray layout; /* contents of source, laid out in document space */
35 iPtrArray links; 74 iPtrArray links;
36 iString title; /* the first top-level title */ 75 iString title; /* the first top-level title */
76 iPtrArray images; /* persistent across layouts, references links by ID */
37}; 77};
38 78
39iDefineObjectConstruction(GmDocument) 79iDefineObjectConstruction(GmDocument)
@@ -178,6 +218,17 @@ static void clearLinks_GmDocument_(iGmDocument *d) {
178 clear_PtrArray(&d->links); 218 clear_PtrArray(&d->links);
179} 219}
180 220
221static size_t findLinkImage_GmDocument_(const iGmDocument *d, iGmLinkId linkId) {
222 /* TODO: use a hash */
223 iConstForEach(PtrArray, i, &d->images) {
224 const iGmImage *img = i.ptr;
225 if (img->linkId == linkId) {
226 return index_PtrArrayConstIterator(&i);
227 }
228 }
229 return iInvalidPos;
230}
231
181static void doLayout_GmDocument_(iGmDocument *d) { 232static void doLayout_GmDocument_(iGmDocument *d) {
182 /* TODO: Collect these parameters into a GmTheme. */ 233 /* TODO: Collect these parameters into a GmTheme. */
183 static const int fonts[max_GmLineType] = { 234 static const int fonts[max_GmLineType] = {
@@ -236,6 +287,7 @@ static void doLayout_GmDocument_(iGmDocument *d) {
236 iGmRun run; 287 iGmRun run;
237 run.color = white_ColorId; 288 run.color = white_ColorId;
238 run.linkId = 0; 289 run.linkId = 0;
290 run.imageId = 0;
239 enum iGmLineType type; 291 enum iGmLineType type;
240 int indent = 0; 292 int indent = 0;
241 if (!isPreformat) { 293 if (!isPreformat) {
@@ -373,6 +425,24 @@ static void doLayout_GmDocument_(iGmDocument *d) {
373 trimStart_Rangecc(&runLine); 425 trimStart_Rangecc(&runLine);
374 pos.y += lineHeight_Text(run.font); 426 pos.y += lineHeight_Text(run.font);
375 } 427 }
428 /* Image content. */
429 if (type == link_GmLineType) {
430 const size_t imgIndex = findLinkImage_GmDocument_(d, run.linkId);
431 if (imgIndex != iInvalidPos) {
432 const iGmImage *img = constAt_PtrArray(&d->images, imgIndex);
433 run.bounds.pos = pos;
434 run.bounds.size.x = d->size.x;
435 const float aspect = (float) img->size.y / (float) img->size.x;
436 run.bounds.size.y = d->size.x * aspect;
437 run.visBounds = run.bounds; /* TODO: limit max height? */
438 run.text = iNullRange;
439 run.font = 0;
440 run.color = 0;
441 run.imageId = imgIndex + 1;
442 pushBack_Array(&d->layout, &run);
443 pos.y += run.bounds.size.y;
444 }
445 }
376 prevType = type; 446 prevType = type;
377 } 447 }
378 d->size.y = pos.y; 448 d->size.y = pos.y;
@@ -386,6 +456,7 @@ void init_GmDocument(iGmDocument *d) {
386 init_Array(&d->layout, sizeof(iGmRun)); 456 init_Array(&d->layout, sizeof(iGmRun));
387 init_PtrArray(&d->links); 457 init_PtrArray(&d->links);
388 init_String(&d->title); 458 init_String(&d->title);
459 init_PtrArray(&d->images);
389} 460}
390 461
391void deinit_GmDocument(iGmDocument *d) { 462void deinit_GmDocument(iGmDocument *d) {
@@ -397,6 +468,16 @@ void deinit_GmDocument(iGmDocument *d) {
397 deinit_String(&d->source); 468 deinit_String(&d->source);
398} 469}
399 470
471void reset_GmDocument(iGmDocument *d) {
472 /* Free the loaded images. */
473 iForEach(PtrArray, i, &d->images) {
474 deinit_GmImage(i.ptr);
475 }
476 clear_PtrArray(&d->images);
477 clearLinks_GmDocument_(d);
478 clear_Array(&d->layout);
479}
480
400void setFormat_GmDocument(iGmDocument *d, enum iGmDocumentFormat format) { 481void setFormat_GmDocument(iGmDocument *d, enum iGmDocumentFormat format) {
401 d->format = format; 482 d->format = format;
402} 483}
@@ -477,6 +558,21 @@ void setSource_GmDocument(iGmDocument *d, const iString *source, int width) {
477 /* TODO: just flag need-layout and do it later */ 558 /* TODO: just flag need-layout and do it later */
478} 559}
479 560
561void setImage_GmDocument(iGmDocument *d, iGmLinkId linkId, const iString *mime, const iBlock *data) {
562 /* TODO: check if we know this MIME type */
563 /* Load the image. */ {
564 iGmImage *img = new_GmImage(data);
565 img->linkId = linkId; /* TODO: use a hash? */
566 if (img->texture) {
567 pushBack_PtrArray(&d->images, img);
568 }
569 else {
570 delete_GmImage(img);
571 }
572 }
573 doLayout_GmDocument_(d);
574}
575
480void render_GmDocument(const iGmDocument *d, iRangei visRangeY, iGmDocumentRenderFunc render, 576void render_GmDocument(const iGmDocument *d, iRangei visRangeY, iGmDocumentRenderFunc render,
481 void *context) { 577 void *context) {
482 iBool isInside = iFalse; 578 iBool isInside = iFalse;
@@ -579,6 +675,19 @@ enum iColorId linkColor_GmDocument(const iGmDocument *d, iGmLinkId linkId) {
579 return white_ColorId; 675 return white_ColorId;
580} 676}
581 677
678iBool isMediaLink_GmDocument(const iGmDocument *d, iGmLinkId linkId) {
679 return (linkFlags_GmDocument(d, linkId) &
680 (imageFileExtension_GmLinkFlag | audioFileExtension_GmLinkFlag)) != 0;
681}
682
683SDL_Texture *imageTexture_GmDocument(const iGmDocument *d, uint16_t imageId) {
684 if (imageId > 0 && imageId <= size_PtrArray(&d->images)) {
685 const iGmImage *img = constAt_PtrArray(&d->images, imageId - 1);
686 return img->texture;
687 }
688 return NULL;
689}
690
582const iString *title_GmDocument(const iGmDocument *d) { 691const iString *title_GmDocument(const iGmDocument *d) {
583 return &d->title; 692 return &d->title;
584} 693}